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/. */
5 
6 #include "TextAttrs.h"
7 
8 #include "Accessible-inl.h"
9 #include "nsAccUtils.h"
10 #include "nsCoreUtils.h"
11 #include "StyleInfo.h"
12 
13 #include "gfxFont.h"
14 #include "nsFontMetrics.h"
15 #include "nsLayoutUtils.h"
16 #include "nsContainerFrame.h"
17 #include "HyperTextAccessible.h"
18 #include "mozilla/AppUnits.h"
19 #include "mozilla/gfx/2D.h"
20 
21 #if defined(MOZ_WIDGET_GTK)
22 #include "gfxPlatformGtk.h" // xxx - for UseFcFontList
23 #endif
24 
25 using namespace mozilla;
26 using namespace mozilla::a11y;
27 
28 ////////////////////////////////////////////////////////////////////////////////
29 // TextAttrsMgr
30 ////////////////////////////////////////////////////////////////////////////////
31 
32 void
GetAttributes(nsIPersistentProperties * aAttributes,uint32_t * aStartOffset,uint32_t * aEndOffset)33 TextAttrsMgr::GetAttributes(nsIPersistentProperties* aAttributes,
34                             uint32_t* aStartOffset,
35                             uint32_t* aEndOffset)
36 {
37   // 1. Hyper text accessible must be specified always.
38   // 2. Offset accessible and result hyper text offsets must be specified in
39   // the case of text attributes.
40   // 3. Offset accessible and result hyper text offsets must not be specified
41   // but include default text attributes flag and attributes list must be
42   // specified in the case of default text attributes.
43   NS_PRECONDITION(mHyperTextAcc &&
44                   ((mOffsetAcc && mOffsetAccIdx != -1 &&
45                     aStartOffset && aEndOffset) ||
46                   (!mOffsetAcc && mOffsetAccIdx == -1 &&
47                     !aStartOffset && !aEndOffset &&
48                    mIncludeDefAttrs && aAttributes)),
49                   "Wrong usage of TextAttrsMgr!");
50 
51   // Embedded objects are combined into own range with empty attributes set.
52   if (mOffsetAcc && !mOffsetAcc->IsText()) {
53     for (int32_t childIdx = mOffsetAccIdx - 1; childIdx >= 0; childIdx--) {
54       Accessible* currAcc = mHyperTextAcc->GetChildAt(childIdx);
55       if (currAcc->IsText())
56         break;
57 
58       (*aStartOffset)--;
59     }
60 
61     uint32_t childCount = mHyperTextAcc->ChildCount();
62     for (uint32_t childIdx = mOffsetAccIdx + 1; childIdx < childCount;
63          childIdx++) {
64       Accessible* currAcc = mHyperTextAcc->GetChildAt(childIdx);
65       if (currAcc->IsText())
66         break;
67 
68       (*aEndOffset)++;
69     }
70 
71     return;
72   }
73 
74   // Get the content and frame of the accessible. In the case of document
75   // accessible it's role content and root frame.
76   nsIContent* hyperTextElm = mHyperTextAcc->GetContent();
77   if (!hyperTextElm)
78     return; // XXX: we don't support text attrs on document with no body
79 
80   nsIFrame* rootFrame = mHyperTextAcc->GetFrame();
81   MOZ_ASSERT(rootFrame, "No frame for accessible!");
82   if (!rootFrame)
83     return;
84 
85   nsIContent *offsetNode = nullptr, *offsetElm = nullptr;
86   nsIFrame *frame = nullptr;
87   if (mOffsetAcc) {
88     offsetNode = mOffsetAcc->GetContent();
89     offsetElm = nsCoreUtils::GetDOMElementFor(offsetNode);
90     MOZ_ASSERT(offsetElm, "No element for offset accessible!");
91     if (!offsetElm)
92       return;
93 
94     frame = offsetElm->GetPrimaryFrame();
95   }
96 
97   // "language" text attribute
98   LangTextAttr langTextAttr(mHyperTextAcc, hyperTextElm, offsetNode);
99 
100   // "aria-invalid" text attribute
101   InvalidTextAttr invalidTextAttr(hyperTextElm, offsetNode);
102 
103   // "background-color" text attribute
104   BGColorTextAttr bgColorTextAttr(rootFrame, frame);
105 
106   // "color" text attribute
107   ColorTextAttr colorTextAttr(rootFrame, frame);
108 
109   // "font-family" text attribute
110   FontFamilyTextAttr fontFamilyTextAttr(rootFrame, frame);
111 
112   // "font-size" text attribute
113   FontSizeTextAttr fontSizeTextAttr(rootFrame, frame);
114 
115   // "font-style" text attribute
116   FontStyleTextAttr fontStyleTextAttr(rootFrame, frame);
117 
118   // "font-weight" text attribute
119   FontWeightTextAttr fontWeightTextAttr(rootFrame, frame);
120 
121   // "auto-generated" text attribute
122   AutoGeneratedTextAttr autoGenTextAttr(mHyperTextAcc, mOffsetAcc);
123 
124   // "text-underline(line-through)-style(color)" text attributes
125   TextDecorTextAttr textDecorTextAttr(rootFrame, frame);
126 
127   // "text-position" text attribute
128   TextPosTextAttr textPosTextAttr(rootFrame, frame);
129 
130   TextAttr* attrArray[] =
131   {
132     &langTextAttr,
133     &invalidTextAttr,
134     &bgColorTextAttr,
135     &colorTextAttr,
136     &fontFamilyTextAttr,
137     &fontSizeTextAttr,
138     &fontStyleTextAttr,
139     &fontWeightTextAttr,
140     &autoGenTextAttr,
141     &textDecorTextAttr,
142     &textPosTextAttr
143   };
144 
145   // Expose text attributes if applicable.
146   if (aAttributes) {
147     for (uint32_t idx = 0; idx < ArrayLength(attrArray); idx++)
148       attrArray[idx]->Expose(aAttributes, mIncludeDefAttrs);
149   }
150 
151   // Expose text attributes range where they are applied if applicable.
152   if (mOffsetAcc)
153     GetRange(attrArray, ArrayLength(attrArray), aStartOffset, aEndOffset);
154 }
155 
156 void
GetRange(TextAttr * aAttrArray[],uint32_t aAttrArrayLen,uint32_t * aStartOffset,uint32_t * aEndOffset)157 TextAttrsMgr::GetRange(TextAttr* aAttrArray[], uint32_t aAttrArrayLen,
158                        uint32_t* aStartOffset, uint32_t* aEndOffset)
159 {
160   // Navigate backward from anchor accessible to find start offset.
161   for (int32_t childIdx = mOffsetAccIdx - 1; childIdx >= 0; childIdx--) {
162     Accessible* currAcc = mHyperTextAcc->GetChildAt(childIdx);
163 
164     // Stop on embedded accessible since embedded accessibles are combined into
165     // own range.
166     if (!currAcc->IsText())
167       break;
168 
169     MOZ_ASSERT(nsCoreUtils::GetDOMElementFor(currAcc->GetContent()),
170                "Text accessible has to have an associated DOM element");
171 
172     bool offsetFound = false;
173     for (uint32_t attrIdx = 0; attrIdx < aAttrArrayLen; attrIdx++) {
174       TextAttr* textAttr = aAttrArray[attrIdx];
175       if (!textAttr->Equal(currAcc)) {
176         offsetFound = true;
177         break;
178       }
179     }
180 
181     if (offsetFound)
182       break;
183 
184     *(aStartOffset) -= nsAccUtils::TextLength(currAcc);
185   }
186 
187   // Navigate forward from anchor accessible to find end offset.
188   uint32_t childLen = mHyperTextAcc->ChildCount();
189   for (uint32_t childIdx = mOffsetAccIdx + 1; childIdx < childLen; childIdx++) {
190     Accessible* currAcc = mHyperTextAcc->GetChildAt(childIdx);
191     if (!currAcc->IsText())
192       break;
193 
194     MOZ_ASSERT(nsCoreUtils::GetDOMElementFor(currAcc->GetContent()),
195                "Text accessible has to have an associated DOM element");
196 
197     bool offsetFound = false;
198     for (uint32_t attrIdx = 0; attrIdx < aAttrArrayLen; attrIdx++) {
199       TextAttr* textAttr = aAttrArray[attrIdx];
200 
201       // Alter the end offset when text attribute changes its value and stop
202       // the search.
203       if (!textAttr->Equal(currAcc)) {
204         offsetFound = true;
205         break;
206       }
207     }
208 
209     if (offsetFound)
210       break;
211 
212     (*aEndOffset) += nsAccUtils::TextLength(currAcc);
213   }
214 }
215 
216 
217 ////////////////////////////////////////////////////////////////////////////////
218 // LangTextAttr
219 ////////////////////////////////////////////////////////////////////////////////
220 
221 TextAttrsMgr::LangTextAttr::
LangTextAttr(HyperTextAccessible * aRoot,nsIContent * aRootElm,nsIContent * aElm)222   LangTextAttr(HyperTextAccessible* aRoot,
223                nsIContent* aRootElm, nsIContent* aElm) :
224   TTextAttr<nsString>(!aElm), mRootContent(aRootElm)
225 {
226   aRoot->Language(mRootNativeValue);
227   mIsRootDefined =  !mRootNativeValue.IsEmpty();
228 
229   if (aElm) {
230     nsCoreUtils::GetLanguageFor(aElm, mRootContent, mNativeValue);
231     mIsDefined = !mNativeValue.IsEmpty();
232   }
233 }
234 
235 TextAttrsMgr::LangTextAttr::
~LangTextAttr()236   ~LangTextAttr() {}
237 
238 bool
239 TextAttrsMgr::LangTextAttr::
GetValueFor(Accessible * aAccessible,nsString * aValue)240   GetValueFor(Accessible* aAccessible, nsString* aValue)
241 {
242   nsCoreUtils::GetLanguageFor(aAccessible->GetContent(), mRootContent, *aValue);
243   return !aValue->IsEmpty();
244 }
245 
246 void
247 TextAttrsMgr::LangTextAttr::
ExposeValue(nsIPersistentProperties * aAttributes,const nsString & aValue)248   ExposeValue(nsIPersistentProperties* aAttributes, const nsString& aValue)
249 {
250   nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::language, aValue);
251 }
252 
253 ////////////////////////////////////////////////////////////////////////////////
254 // InvalidTextAttr
255 ////////////////////////////////////////////////////////////////////////////////
256 
257 TextAttrsMgr::InvalidTextAttr::
InvalidTextAttr(nsIContent * aRootElm,nsIContent * aElm)258   InvalidTextAttr(nsIContent* aRootElm, nsIContent* aElm) :
259   TTextAttr<uint32_t>(!aElm), mRootElm(aRootElm)
260 {
261   mIsRootDefined = GetValue(mRootElm, &mRootNativeValue);
262   if (aElm)
263     mIsDefined = GetValue(aElm, &mNativeValue);
264 }
265 
266 bool
267 TextAttrsMgr::InvalidTextAttr::
GetValueFor(Accessible * aAccessible,uint32_t * aValue)268   GetValueFor(Accessible* aAccessible, uint32_t* aValue)
269 {
270   nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
271   return elm ? GetValue(elm, aValue) : false;
272 }
273 
274 void
275 TextAttrsMgr::InvalidTextAttr::
ExposeValue(nsIPersistentProperties * aAttributes,const uint32_t & aValue)276   ExposeValue(nsIPersistentProperties* aAttributes, const uint32_t& aValue)
277 {
278   switch (aValue) {
279     case eFalse:
280       nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::invalid,
281                              NS_LITERAL_STRING("false"));
282       break;
283 
284     case eGrammar:
285       nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::invalid,
286                              NS_LITERAL_STRING("grammar"));
287       break;
288 
289     case eSpelling:
290       nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::invalid,
291                              NS_LITERAL_STRING("spelling"));
292       break;
293 
294     case eTrue:
295       nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::invalid,
296                              NS_LITERAL_STRING("true"));
297       break;
298   }
299 }
300 
301 bool
302 TextAttrsMgr::InvalidTextAttr::
GetValue(nsIContent * aElm,uint32_t * aValue)303   GetValue(nsIContent* aElm, uint32_t* aValue)
304 {
305   nsIContent* elm = aElm;
306   do {
307     if (nsAccUtils::HasDefinedARIAToken(elm, nsGkAtoms::aria_invalid)) {
308       static nsIContent::AttrValuesArray tokens[] =
309         { &nsGkAtoms::_false, &nsGkAtoms::grammar, &nsGkAtoms::spelling,
310           nullptr };
311 
312       int32_t idx = elm->FindAttrValueIn(kNameSpaceID_None,
313                                          nsGkAtoms::aria_invalid, tokens,
314                                          eCaseMatters);
315       switch (idx) {
316         case 0:
317           *aValue = eFalse;
318           return true;
319         case 1:
320           *aValue = eGrammar;
321           return true;
322         case 2:
323           *aValue = eSpelling;
324           return true;
325         default:
326           *aValue = eTrue;
327           return true;
328       }
329     }
330   } while ((elm = elm->GetParent()) && elm != mRootElm);
331 
332   return false;
333 }
334 
335 
336 ////////////////////////////////////////////////////////////////////////////////
337 // BGColorTextAttr
338 ////////////////////////////////////////////////////////////////////////////////
339 
340 TextAttrsMgr::BGColorTextAttr::
BGColorTextAttr(nsIFrame * aRootFrame,nsIFrame * aFrame)341   BGColorTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) :
342   TTextAttr<nscolor>(!aFrame), mRootFrame(aRootFrame)
343 {
344   mIsRootDefined = GetColor(mRootFrame, &mRootNativeValue);
345   if (aFrame)
346     mIsDefined = GetColor(aFrame, &mNativeValue);
347 }
348 
349 bool
350 TextAttrsMgr::BGColorTextAttr::
GetValueFor(Accessible * aAccessible,nscolor * aValue)351   GetValueFor(Accessible* aAccessible, nscolor* aValue)
352 {
353   nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
354   if (elm) {
355     nsIFrame* frame = elm->GetPrimaryFrame();
356     if (frame) {
357       return GetColor(frame, aValue);
358     }
359   }
360   return false;
361 }
362 
363 void
364 TextAttrsMgr::BGColorTextAttr::
ExposeValue(nsIPersistentProperties * aAttributes,const nscolor & aValue)365   ExposeValue(nsIPersistentProperties* aAttributes, const nscolor& aValue)
366 {
367   nsAutoString formattedValue;
368   StyleInfo::FormatColor(aValue, formattedValue);
369   nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::backgroundColor,
370                          formattedValue);
371 }
372 
373 bool
374 TextAttrsMgr::BGColorTextAttr::
GetColor(nsIFrame * aFrame,nscolor * aColor)375   GetColor(nsIFrame* aFrame, nscolor* aColor)
376 {
377   const nsStyleBackground* styleBackground = aFrame->StyleBackground();
378 
379   if (NS_GET_A(styleBackground->mBackgroundColor) > 0) {
380     *aColor = styleBackground->mBackgroundColor;
381     return true;
382   }
383 
384   nsContainerFrame *parentFrame = aFrame->GetParent();
385   if (!parentFrame) {
386     *aColor = aFrame->PresContext()->DefaultBackgroundColor();
387     return true;
388   }
389 
390   // Each frame of parents chain for the initially passed 'aFrame' has
391   // transparent background color. So background color isn't changed from
392   // 'mRootFrame' to initially passed 'aFrame'.
393   if (parentFrame == mRootFrame)
394     return false;
395 
396   return GetColor(parentFrame, aColor);
397 }
398 
399 
400 ////////////////////////////////////////////////////////////////////////////////
401 // ColorTextAttr
402 ////////////////////////////////////////////////////////////////////////////////
403 
404 TextAttrsMgr::ColorTextAttr::
ColorTextAttr(nsIFrame * aRootFrame,nsIFrame * aFrame)405   ColorTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) :
406   TTextAttr<nscolor>(!aFrame)
407 {
408   mRootNativeValue = aRootFrame->StyleColor()->mColor;
409   mIsRootDefined = true;
410 
411   if (aFrame) {
412     mNativeValue = aFrame->StyleColor()->mColor;
413     mIsDefined = true;
414   }
415 }
416 
417 bool
418 TextAttrsMgr::ColorTextAttr::
GetValueFor(Accessible * aAccessible,nscolor * aValue)419   GetValueFor(Accessible* aAccessible, nscolor* aValue)
420 {
421   nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
422   if (elm) {
423     nsIFrame* frame = elm->GetPrimaryFrame();
424     if (frame) {
425       *aValue = frame->StyleColor()->mColor;
426       return true;
427     }
428   }
429   return false;
430 }
431 
432 void
433 TextAttrsMgr::ColorTextAttr::
ExposeValue(nsIPersistentProperties * aAttributes,const nscolor & aValue)434   ExposeValue(nsIPersistentProperties* aAttributes, const nscolor& aValue)
435 {
436   nsAutoString formattedValue;
437   StyleInfo::FormatColor(aValue, formattedValue);
438   nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::color, formattedValue);
439 }
440 
441 
442 ////////////////////////////////////////////////////////////////////////////////
443 // FontFamilyTextAttr
444 ////////////////////////////////////////////////////////////////////////////////
445 
446 TextAttrsMgr::FontFamilyTextAttr::
FontFamilyTextAttr(nsIFrame * aRootFrame,nsIFrame * aFrame)447   FontFamilyTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) :
448   TTextAttr<nsString>(!aFrame)
449 {
450   mIsRootDefined = GetFontFamily(aRootFrame, mRootNativeValue);
451 
452   if (aFrame)
453     mIsDefined = GetFontFamily(aFrame, mNativeValue);
454 }
455 
456 bool
457 TextAttrsMgr::FontFamilyTextAttr::
GetValueFor(Accessible * aAccessible,nsString * aValue)458   GetValueFor(Accessible* aAccessible, nsString* aValue)
459 {
460   nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
461   if (elm) {
462     nsIFrame* frame = elm->GetPrimaryFrame();
463     if (frame) {
464       return GetFontFamily(frame, *aValue);
465     }
466   }
467   return false;
468 }
469 
470 void
471 TextAttrsMgr::FontFamilyTextAttr::
ExposeValue(nsIPersistentProperties * aAttributes,const nsString & aValue)472   ExposeValue(nsIPersistentProperties* aAttributes, const nsString& aValue)
473 {
474   nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::font_family, aValue);
475 }
476 
477 bool
478 TextAttrsMgr::FontFamilyTextAttr::
GetFontFamily(nsIFrame * aFrame,nsString & aFamily)479   GetFontFamily(nsIFrame* aFrame, nsString& aFamily)
480 {
481   RefPtr<nsFontMetrics> fm =
482     nsLayoutUtils::GetFontMetricsForFrame(aFrame, 1.0f);
483 
484   gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
485   gfxFont* font = fontGroup->GetFirstValidFont();
486   gfxFontEntry* fontEntry = font->GetFontEntry();
487   aFamily = fontEntry->FamilyName();
488   return true;
489 }
490 
491 
492 ////////////////////////////////////////////////////////////////////////////////
493 // FontSizeTextAttr
494 ////////////////////////////////////////////////////////////////////////////////
495 
496 TextAttrsMgr::FontSizeTextAttr::
FontSizeTextAttr(nsIFrame * aRootFrame,nsIFrame * aFrame)497   FontSizeTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) :
498   TTextAttr<nscoord>(!aFrame)
499 {
500   mDC = aRootFrame->PresContext()->DeviceContext();
501 
502   mRootNativeValue = aRootFrame->StyleFont()->mSize;
503   mIsRootDefined = true;
504 
505   if (aFrame) {
506     mNativeValue = aFrame->StyleFont()->mSize;
507     mIsDefined = true;
508   }
509 }
510 
511 bool
512 TextAttrsMgr::FontSizeTextAttr::
GetValueFor(Accessible * aAccessible,nscoord * aValue)513   GetValueFor(Accessible* aAccessible, nscoord* aValue)
514 {
515   nsIContent* el = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
516   if (el) {
517     nsIFrame* frame = el->GetPrimaryFrame();
518     if (frame) {
519       *aValue = frame->StyleFont()->mSize;
520       return true;
521     }
522   }
523   return false;
524 }
525 
526 void
527 TextAttrsMgr::FontSizeTextAttr::
ExposeValue(nsIPersistentProperties * aAttributes,const nscoord & aValue)528   ExposeValue(nsIPersistentProperties* aAttributes, const nscoord& aValue)
529 {
530   // Convert from nscoord to pt.
531   //
532   // Note: according to IA2, "The conversion doesn't have to be exact.
533   // The intent is to give the user a feel for the size of the text."
534   //
535   // ATK does not specify a unit and will likely follow IA2 here.
536   //
537   // XXX todo: consider sharing this code with layout module? (bug 474621)
538   float px =
539     NSAppUnitsToFloatPixels(aValue, mozilla::AppUnitsPerCSSPixel());
540   // Each pt is 4/3 of a CSS pixel.
541   int pts = NS_lround(px*3/4);
542 
543   nsAutoString value;
544   value.AppendInt(pts);
545   value.AppendLiteral("pt");
546 
547   nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::font_size, value);
548 }
549 
550 
551 ////////////////////////////////////////////////////////////////////////////////
552 // FontStyleTextAttr
553 ////////////////////////////////////////////////////////////////////////////////
554 
555 TextAttrsMgr::FontStyleTextAttr::
FontStyleTextAttr(nsIFrame * aRootFrame,nsIFrame * aFrame)556   FontStyleTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) :
557   TTextAttr<nscoord>(!aFrame)
558 {
559   mRootNativeValue = aRootFrame->StyleFont()->mFont.style;
560   mIsRootDefined = true;
561 
562   if (aFrame) {
563     mNativeValue = aFrame->StyleFont()->mFont.style;
564     mIsDefined = true;
565   }
566 }
567 
568 bool
569 TextAttrsMgr::FontStyleTextAttr::
GetValueFor(Accessible * aAccessible,nscoord * aValue)570   GetValueFor(Accessible* aAccessible, nscoord* aValue)
571 {
572   nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
573   if (elm) {
574     nsIFrame* frame = elm->GetPrimaryFrame();
575     if (frame) {
576       *aValue = frame->StyleFont()->mFont.style;
577       return true;
578     }
579   }
580   return false;
581 }
582 
583 void
584 TextAttrsMgr::FontStyleTextAttr::
ExposeValue(nsIPersistentProperties * aAttributes,const nscoord & aValue)585   ExposeValue(nsIPersistentProperties* aAttributes, const nscoord& aValue)
586 {
587   nsAutoString formattedValue;
588   StyleInfo::FormatFontStyle(aValue, formattedValue);
589 
590   nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::font_style, formattedValue);
591 }
592 
593 
594 ////////////////////////////////////////////////////////////////////////////////
595 // FontWeightTextAttr
596 ////////////////////////////////////////////////////////////////////////////////
597 
598 TextAttrsMgr::FontWeightTextAttr::
FontWeightTextAttr(nsIFrame * aRootFrame,nsIFrame * aFrame)599   FontWeightTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) :
600   TTextAttr<int32_t>(!aFrame)
601 {
602   mRootNativeValue = GetFontWeight(aRootFrame);
603   mIsRootDefined = true;
604 
605   if (aFrame) {
606     mNativeValue = GetFontWeight(aFrame);
607     mIsDefined = true;
608   }
609 }
610 
611 bool
612 TextAttrsMgr::FontWeightTextAttr::
GetValueFor(Accessible * aAccessible,int32_t * aValue)613   GetValueFor(Accessible* aAccessible, int32_t* aValue)
614 {
615   nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
616   if (elm) {
617     nsIFrame* frame = elm->GetPrimaryFrame();
618     if (frame) {
619       *aValue = GetFontWeight(frame);
620       return true;
621     }
622   }
623   return false;
624 }
625 
626 void
627 TextAttrsMgr::FontWeightTextAttr::
ExposeValue(nsIPersistentProperties * aAttributes,const int32_t & aValue)628   ExposeValue(nsIPersistentProperties* aAttributes, const int32_t& aValue)
629 {
630   nsAutoString formattedValue;
631   formattedValue.AppendInt(aValue);
632 
633   nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::fontWeight, formattedValue);
634 }
635 
636 int32_t
637 TextAttrsMgr::FontWeightTextAttr::
GetFontWeight(nsIFrame * aFrame)638   GetFontWeight(nsIFrame* aFrame)
639 {
640   // nsFont::width isn't suitable here because it's necessary to expose real
641   // value of font weight (used font might not have some font weight values).
642   RefPtr<nsFontMetrics> fm =
643     nsLayoutUtils::GetFontMetricsForFrame(aFrame, 1.0f);
644 
645   gfxFontGroup *fontGroup = fm->GetThebesFontGroup();
646   gfxFont *font = fontGroup->GetFirstValidFont();
647 
648   // When there doesn't exist a bold font in the family and so the rendering of
649   // a non-bold font face is changed so that the user sees what looks like a
650   // bold font, i.e. synthetic bolding is used. IsSyntheticBold method is only
651   // needed on Mac, but it is "safe" to use on all platforms.  (For non-Mac
652   // platforms it always return false.)
653   if (font->IsSyntheticBold())
654     return 700;
655 
656   bool useFontEntryWeight = true;
657 
658   // Under Linux, when gfxPangoFontGroup code is used,
659   // font->GetStyle()->weight will give the absolute weight requested of the
660   // font face. The gfxPangoFontGroup code uses the gfxFontEntry constructor
661   // which doesn't initialize the weight field.
662 #if defined(MOZ_WIDGET_GTK)
663   useFontEntryWeight = gfxPlatformGtk::UseFcFontList();
664 #endif
665 
666   if (useFontEntryWeight) {
667     // On Windows, font->GetStyle()->weight will give the same weight as
668     // fontEntry->Weight(), the weight of the first font in the font group,
669     // which may not be the weight of the font face used to render the
670     // characters. On Mac, font->GetStyle()->weight will just give the same
671     // number as getComputedStyle(). fontEntry->Weight() will give the weight
672     // of the font face used.
673     gfxFontEntry *fontEntry = font->GetFontEntry();
674     return fontEntry->Weight();
675   } else {
676     return font->GetStyle()->weight;
677   }
678 }
679 
680 ////////////////////////////////////////////////////////////////////////////////
681 // AutoGeneratedTextAttr
682 ////////////////////////////////////////////////////////////////////////////////
683 TextAttrsMgr::AutoGeneratedTextAttr::
AutoGeneratedTextAttr(HyperTextAccessible * aHyperTextAcc,Accessible * aAccessible)684   AutoGeneratedTextAttr(HyperTextAccessible* aHyperTextAcc,
685                         Accessible* aAccessible) :
686   TTextAttr<bool>(!aAccessible)
687 {
688   mRootNativeValue = false;
689   mIsRootDefined = false;
690 
691   if (aAccessible)
692     mIsDefined = mNativeValue = (aAccessible->NativeRole() == roles::STATICTEXT);
693 }
694 
695 bool
696 TextAttrsMgr::AutoGeneratedTextAttr::
GetValueFor(Accessible * aAccessible,bool * aValue)697   GetValueFor(Accessible* aAccessible, bool* aValue)
698 {
699   return *aValue = (aAccessible->NativeRole() == roles::STATICTEXT);
700 }
701 
702 void
703 TextAttrsMgr::AutoGeneratedTextAttr::
ExposeValue(nsIPersistentProperties * aAttributes,const bool & aValue)704   ExposeValue(nsIPersistentProperties* aAttributes, const bool& aValue)
705 {
706   nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::auto_generated,
707                          aValue ? NS_LITERAL_STRING("true") : NS_LITERAL_STRING("false"));
708 }
709 
710 
711 ////////////////////////////////////////////////////////////////////////////////
712 // TextDecorTextAttr
713 ////////////////////////////////////////////////////////////////////////////////
714 
715 TextAttrsMgr::TextDecorValue::
TextDecorValue(nsIFrame * aFrame)716   TextDecorValue(nsIFrame* aFrame)
717 {
718   const nsStyleTextReset* textReset = aFrame->StyleTextReset();
719   mStyle = textReset->mTextDecorationStyle;
720   mColor = aFrame->StyleColor()->
721     CalcComplexColor(textReset->mTextDecorationColor);
722   mLine = textReset->mTextDecorationLine &
723     (NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE |
724      NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH);
725 }
726 
727 TextAttrsMgr::TextDecorTextAttr::
TextDecorTextAttr(nsIFrame * aRootFrame,nsIFrame * aFrame)728   TextDecorTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) :
729   TTextAttr<TextDecorValue>(!aFrame)
730 {
731   mRootNativeValue = TextDecorValue(aRootFrame);
732   mIsRootDefined = mRootNativeValue.IsDefined();
733 
734   if (aFrame) {
735     mNativeValue = TextDecorValue(aFrame);
736     mIsDefined = mNativeValue.IsDefined();
737   }
738 }
739 
740 bool
741 TextAttrsMgr::TextDecorTextAttr::
GetValueFor(Accessible * aAccessible,TextDecorValue * aValue)742   GetValueFor(Accessible* aAccessible, TextDecorValue* aValue)
743 {
744   nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
745   if (elm) {
746     nsIFrame* frame = elm->GetPrimaryFrame();
747     if (frame) {
748       *aValue = TextDecorValue(frame);
749       return aValue->IsDefined();
750     }
751   }
752   return false;
753 }
754 
755 void
756 TextAttrsMgr::TextDecorTextAttr::
ExposeValue(nsIPersistentProperties * aAttributes,const TextDecorValue & aValue)757   ExposeValue(nsIPersistentProperties* aAttributes, const TextDecorValue& aValue)
758 {
759   if (aValue.IsUnderline()) {
760     nsAutoString formattedStyle;
761     StyleInfo::FormatTextDecorationStyle(aValue.Style(), formattedStyle);
762     nsAccUtils::SetAccAttr(aAttributes,
763                            nsGkAtoms::textUnderlineStyle,
764                            formattedStyle);
765 
766     nsAutoString formattedColor;
767     StyleInfo::FormatColor(aValue.Color(), formattedColor);
768     nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textUnderlineColor,
769                            formattedColor);
770     return;
771   }
772 
773   if (aValue.IsLineThrough()) {
774     nsAutoString formattedStyle;
775     StyleInfo::FormatTextDecorationStyle(aValue.Style(), formattedStyle);
776     nsAccUtils::SetAccAttr(aAttributes,
777                            nsGkAtoms::textLineThroughStyle,
778                            formattedStyle);
779 
780     nsAutoString formattedColor;
781     StyleInfo::FormatColor(aValue.Color(), formattedColor);
782     nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textLineThroughColor,
783                            formattedColor);
784   }
785 }
786 
787 ////////////////////////////////////////////////////////////////////////////////
788 // TextPosTextAttr
789 ////////////////////////////////////////////////////////////////////////////////
790 
791 TextAttrsMgr::TextPosTextAttr::
TextPosTextAttr(nsIFrame * aRootFrame,nsIFrame * aFrame)792   TextPosTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) :
793   TTextAttr<TextPosValue>(!aFrame)
794 {
795   mRootNativeValue = GetTextPosValue(aRootFrame);
796   mIsRootDefined = mRootNativeValue != eTextPosNone;
797 
798   if (aFrame) {
799     mNativeValue = GetTextPosValue(aFrame);
800     mIsDefined = mNativeValue != eTextPosNone;
801   }
802 }
803 
804 bool
805 TextAttrsMgr::TextPosTextAttr::
GetValueFor(Accessible * aAccessible,TextPosValue * aValue)806   GetValueFor(Accessible* aAccessible, TextPosValue* aValue)
807 {
808   nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
809   if (elm) {
810     nsIFrame* frame = elm->GetPrimaryFrame();
811     if (frame) {
812       *aValue = GetTextPosValue(frame);
813       return *aValue != eTextPosNone;
814     }
815   }
816   return false;
817 }
818 
819 void
820 TextAttrsMgr::TextPosTextAttr::
ExposeValue(nsIPersistentProperties * aAttributes,const TextPosValue & aValue)821   ExposeValue(nsIPersistentProperties* aAttributes, const TextPosValue& aValue)
822 {
823   switch (aValue) {
824     case eTextPosBaseline:
825       nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textPosition,
826                              NS_LITERAL_STRING("baseline"));
827       break;
828 
829     case eTextPosSub:
830       nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textPosition,
831                              NS_LITERAL_STRING("sub"));
832       break;
833 
834     case eTextPosSuper:
835       nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textPosition,
836                              NS_LITERAL_STRING("super"));
837       break;
838 
839     case eTextPosNone:
840       break;
841   }
842 }
843 
844 TextAttrsMgr::TextPosValue
845 TextAttrsMgr::TextPosTextAttr::
GetTextPosValue(nsIFrame * aFrame) const846   GetTextPosValue(nsIFrame* aFrame) const
847 {
848   const nsStyleCoord& styleCoord = aFrame->StyleDisplay()->mVerticalAlign;
849   switch (styleCoord.GetUnit()) {
850     case eStyleUnit_Enumerated:
851       switch (styleCoord.GetIntValue()) {
852         case NS_STYLE_VERTICAL_ALIGN_BASELINE:
853           return eTextPosBaseline;
854         case NS_STYLE_VERTICAL_ALIGN_SUB:
855           return eTextPosSub;
856         case NS_STYLE_VERTICAL_ALIGN_SUPER:
857           return eTextPosSuper;
858 
859         // No good guess for these:
860         //   NS_STYLE_VERTICAL_ALIGN_TOP
861         //   NS_STYLE_VERTICAL_ALIGN_TEXT_TOP
862         //   NS_STYLE_VERTICAL_ALIGN_MIDDLE
863         //   NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM
864         //   NS_STYLE_VERTICAL_ALIGN_BOTTOM
865         //   NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE
866         // Do not expose value of text-position attribute.
867 
868         default:
869           break;
870       }
871       return eTextPosNone;
872 
873     case eStyleUnit_Percent:
874     {
875       float percentValue = styleCoord.GetPercentValue();
876       return percentValue > 0 ?
877         eTextPosSuper :
878         (percentValue < 0 ? eTextPosSub : eTextPosBaseline);
879     }
880 
881     case eStyleUnit_Coord:
882     {
883        nscoord coordValue = styleCoord.GetCoordValue();
884        return coordValue > 0 ?
885          eTextPosSuper :
886          (coordValue < 0 ? eTextPosSub : eTextPosBaseline);
887     }
888 
889     case eStyleUnit_Null:
890     case eStyleUnit_Normal:
891     case eStyleUnit_Auto:
892     case eStyleUnit_None:
893     case eStyleUnit_Factor:
894     case eStyleUnit_Degree:
895     case eStyleUnit_Grad:
896     case eStyleUnit_Radian:
897     case eStyleUnit_Turn:
898     case eStyleUnit_FlexFraction:
899     case eStyleUnit_Integer:
900     case eStyleUnit_Calc:
901       break;
902   }
903 
904   const nsIContent* content = aFrame->GetContent();
905   if (content) {
906     if (content->IsHTMLElement(nsGkAtoms::sup))
907       return eTextPosSuper;
908     if (content->IsHTMLElement(nsGkAtoms::sub))
909       return eTextPosSub;
910   }
911 
912   return eTextPosNone;
913 }
914