TextAttrs.cpp (22209B)
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 "AccAttributes.h" 9 #include "nsAccUtils.h" 10 #include "nsCoreUtils.h" 11 #include "StyleInfo.h" 12 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" 19 20 using namespace mozilla; 21 using namespace mozilla::a11y; 22 23 //////////////////////////////////////////////////////////////////////////////// 24 // TextAttrsMgr 25 //////////////////////////////////////////////////////////////////////////////// 26 27 void TextAttrsMgr::GetAttributes(AccAttributes* aAttributes) { 28 MOZ_ASSERT( 29 // 1. Hyper text accessible and attributes list must always be specified. 30 mHyperTextAcc && aAttributes && 31 ( 32 // 2. If text attributes for a child of a container are being 33 // requested, the offset accessible must be specified and it must 34 // be text. 35 (mOffsetAcc && mOffsetAcc->IsText()) || 36 // 3. If only default text attributes for a container are being 37 // requested, the offset accessible must not be specified, but the 38 // include default text attributes flag must be specified. 39 (!mOffsetAcc && mIncludeDefAttrs)), 40 "Wrong usage of TextAttrsMgr!"); 41 42 // Get the content and frame of the accessible. In the case of document 43 // accessible it's role content and root frame. 44 nsIContent* hyperTextElm = mHyperTextAcc->GetContent(); 45 if (!hyperTextElm) { 46 return; // XXX: we don't support text attrs on document with no body 47 } 48 49 nsIFrame* rootFrame = mHyperTextAcc->GetFrame(); 50 if (!rootFrame) { 51 return; 52 } 53 54 nsIContent *offsetNode = nullptr, *offsetElm = nullptr; 55 nsIFrame* frame = nullptr; 56 if (mOffsetAcc) { 57 offsetNode = mOffsetAcc->GetContent(); 58 offsetElm = nsCoreUtils::GetDOMElementFor(offsetNode); 59 MOZ_ASSERT(offsetElm, "No element for offset accessible!"); 60 if (!offsetElm) return; 61 62 frame = offsetElm->GetPrimaryFrame(); 63 } 64 65 // "language" text attribute 66 LangTextAttr langTextAttr(mHyperTextAcc, hyperTextElm, offsetNode); 67 68 // "aria-invalid" text attribute 69 InvalidTextAttr invalidTextAttr(hyperTextElm, offsetNode); 70 71 // "background-color" text attribute 72 BGColorTextAttr bgColorTextAttr(rootFrame, frame); 73 74 // "color" text attribute 75 ColorTextAttr colorTextAttr(rootFrame, frame); 76 77 // "font-family" text attribute 78 FontFamilyTextAttr fontFamilyTextAttr(rootFrame, frame); 79 80 // "font-size" text attribute 81 FontSizeTextAttr fontSizeTextAttr(rootFrame, frame); 82 83 // "font-style" text attribute 84 FontStyleTextAttr fontStyleTextAttr(rootFrame, frame); 85 86 // "font-weight" text attribute 87 FontWeightTextAttr fontWeightTextAttr(rootFrame, frame); 88 89 // "auto-generated" text attribute 90 AutoGeneratedTextAttr autoGenTextAttr(mHyperTextAcc, mOffsetAcc); 91 92 // "text-underline(line-through)-style(color)" text attributes 93 TextDecorTextAttr textDecorTextAttr(rootFrame, frame); 94 95 // "text-position" text attribute 96 TextPosTextAttr textPosTextAttr(rootFrame, frame, hyperTextElm, offsetNode); 97 98 TextAttr* attrArray[] = { 99 &langTextAttr, &invalidTextAttr, &bgColorTextAttr, 100 &colorTextAttr, &fontFamilyTextAttr, &fontSizeTextAttr, 101 &fontStyleTextAttr, &fontWeightTextAttr, &autoGenTextAttr, 102 &textDecorTextAttr, &textPosTextAttr}; 103 104 // Expose text attributes. 105 for (TextAttr* attr : attrArray) { 106 attr->Expose(aAttributes, mIncludeDefAttrs); 107 } 108 } 109 110 //////////////////////////////////////////////////////////////////////////////// 111 // LangTextAttr 112 //////////////////////////////////////////////////////////////////////////////// 113 114 TextAttrsMgr::LangTextAttr::LangTextAttr(HyperTextAccessible* aRoot, 115 nsIContent* aRootElm, nsIContent* aElm) 116 : TTextAttr<nsString>(!aElm), mRootContent(aRootElm) { 117 aRoot->Language(mRootNativeValue); 118 mIsRootDefined = !mRootNativeValue.IsEmpty(); 119 120 if (aElm) { 121 nsCoreUtils::GetLanguageFor(aElm, mRootContent, mNativeValue); 122 mIsDefined = !mNativeValue.IsEmpty(); 123 } 124 } 125 126 TextAttrsMgr::LangTextAttr::~LangTextAttr() {} 127 128 void TextAttrsMgr::LangTextAttr::ExposeValue(AccAttributes* aAttributes, 129 const nsString& aValue) { 130 RefPtr<nsAtom> lang = NS_Atomize(aValue); 131 aAttributes->SetAttribute(nsGkAtoms::language, lang); 132 } 133 134 //////////////////////////////////////////////////////////////////////////////// 135 // InvalidTextAttr 136 //////////////////////////////////////////////////////////////////////////////// 137 138 TextAttrsMgr::InvalidTextAttr::InvalidTextAttr(nsIContent* aRootElm, 139 nsIContent* aElm) 140 : TTextAttr<uint32_t>(!aElm), mRootElm(aRootElm) { 141 mIsRootDefined = GetValue(mRootElm, &mRootNativeValue); 142 if (aElm) mIsDefined = GetValue(aElm, &mNativeValue); 143 } 144 145 void TextAttrsMgr::InvalidTextAttr::ExposeValue(AccAttributes* aAttributes, 146 const uint32_t& aValue) { 147 switch (aValue) { 148 case eFalse: 149 aAttributes->SetAttribute(nsGkAtoms::invalid, nsGkAtoms::_false); 150 break; 151 152 case eGrammar: 153 aAttributes->SetAttribute(nsGkAtoms::invalid, nsGkAtoms::grammar); 154 break; 155 156 case eSpelling: 157 aAttributes->SetAttribute(nsGkAtoms::invalid, nsGkAtoms::spelling); 158 break; 159 160 case eTrue: 161 aAttributes->SetAttribute(nsGkAtoms::invalid, nsGkAtoms::_true); 162 break; 163 } 164 } 165 166 bool TextAttrsMgr::InvalidTextAttr::GetValue(nsIContent* aElm, 167 uint32_t* aValue) { 168 nsIContent* elm = aElm; 169 do { 170 if (nsAccUtils::HasDefinedARIAToken(elm, nsGkAtoms::aria_invalid)) { 171 static dom::Element::AttrValuesArray tokens[] = { 172 nsGkAtoms::_false, nsGkAtoms::grammar, nsGkAtoms::spelling, nullptr}; 173 174 int32_t idx = nsAccUtils::FindARIAAttrValueIn( 175 elm->AsElement(), nsGkAtoms::aria_invalid, tokens, eCaseMatters); 176 switch (idx) { 177 case 0: 178 *aValue = eFalse; 179 return true; 180 case 1: 181 *aValue = eGrammar; 182 return true; 183 case 2: 184 *aValue = eSpelling; 185 return true; 186 default: 187 *aValue = eTrue; 188 return true; 189 } 190 } 191 } while ((elm = elm->GetParent()) && elm != mRootElm); 192 193 return false; 194 } 195 196 //////////////////////////////////////////////////////////////////////////////// 197 // BGColorTextAttr 198 //////////////////////////////////////////////////////////////////////////////// 199 200 TextAttrsMgr::BGColorTextAttr::BGColorTextAttr(nsIFrame* aRootFrame, 201 nsIFrame* aFrame) 202 : TTextAttr<nscolor>(!aFrame), mRootFrame(aRootFrame) { 203 mIsRootDefined = GetColor(mRootFrame, &mRootNativeValue); 204 if (aFrame) mIsDefined = GetColor(aFrame, &mNativeValue); 205 } 206 207 void TextAttrsMgr::BGColorTextAttr::ExposeValue(AccAttributes* aAttributes, 208 const nscolor& aValue) { 209 aAttributes->SetAttribute(nsGkAtoms::background_color, Color{aValue}); 210 } 211 212 bool TextAttrsMgr::BGColorTextAttr::GetColor(nsIFrame* aFrame, 213 nscolor* aColor) { 214 nscolor backgroundColor = aFrame->StyleBackground()->BackgroundColor(aFrame); 215 if (NS_GET_A(backgroundColor) > 0) { 216 *aColor = backgroundColor; 217 return true; 218 } 219 220 nsContainerFrame* parentFrame = aFrame->GetParent(); 221 if (!parentFrame) { 222 *aColor = aFrame->PresContext()->DefaultBackgroundColor(); 223 return true; 224 } 225 226 // Each frame of parents chain for the initially passed 'aFrame' has 227 // transparent background color. So background color isn't changed from 228 // 'mRootFrame' to initially passed 'aFrame'. 229 if (parentFrame == mRootFrame) return false; 230 231 return GetColor(parentFrame, aColor); 232 } 233 234 //////////////////////////////////////////////////////////////////////////////// 235 // ColorTextAttr 236 //////////////////////////////////////////////////////////////////////////////// 237 238 TextAttrsMgr::ColorTextAttr::ColorTextAttr(nsIFrame* aRootFrame, 239 nsIFrame* aFrame) 240 : TTextAttr<nscolor>(!aFrame) { 241 mRootNativeValue = aRootFrame->StyleText()->mColor.ToColor(); 242 mIsRootDefined = true; 243 244 if (aFrame) { 245 mNativeValue = aFrame->StyleText()->mColor.ToColor(); 246 mIsDefined = true; 247 } 248 } 249 250 void TextAttrsMgr::ColorTextAttr::ExposeValue(AccAttributes* aAttributes, 251 const nscolor& aValue) { 252 aAttributes->SetAttribute(nsGkAtoms::color, Color{aValue}); 253 } 254 255 //////////////////////////////////////////////////////////////////////////////// 256 // FontFamilyTextAttr 257 //////////////////////////////////////////////////////////////////////////////// 258 259 TextAttrsMgr::FontFamilyTextAttr::FontFamilyTextAttr(nsIFrame* aRootFrame, 260 nsIFrame* aFrame) 261 : TTextAttr<nsString>(!aFrame) { 262 mIsRootDefined = GetFontFamily(aRootFrame, mRootNativeValue); 263 264 if (aFrame) mIsDefined = GetFontFamily(aFrame, mNativeValue); 265 } 266 267 void TextAttrsMgr::FontFamilyTextAttr::ExposeValue(AccAttributes* aAttributes, 268 const nsString& aValue) { 269 RefPtr<nsAtom> family = NS_Atomize(aValue); 270 aAttributes->SetAttribute(nsGkAtoms::font_family, family); 271 } 272 273 bool TextAttrsMgr::FontFamilyTextAttr::GetFontFamily(nsIFrame* aFrame, 274 nsString& aFamily) { 275 RefPtr<nsFontMetrics> fm = 276 nsLayoutUtils::GetFontMetricsForFrame(aFrame, 1.0f); 277 278 gfxFontGroup* fontGroup = fm->GetThebesFontGroup(); 279 RefPtr<gfxFont> font = fontGroup->GetFirstValidFont(); 280 gfxFontEntry* fontEntry = font->GetFontEntry(); 281 aFamily.Append(NS_ConvertUTF8toUTF16(fontEntry->FamilyName())); 282 return true; 283 } 284 285 //////////////////////////////////////////////////////////////////////////////// 286 // FontSizeTextAttr 287 //////////////////////////////////////////////////////////////////////////////// 288 289 TextAttrsMgr::FontSizeTextAttr::FontSizeTextAttr(nsIFrame* aRootFrame, 290 nsIFrame* aFrame) 291 : TTextAttr<nscoord>(!aFrame) { 292 mDC = aRootFrame->PresContext()->DeviceContext(); 293 294 mRootNativeValue = aRootFrame->StyleFont()->mSize.ToAppUnits(); 295 mIsRootDefined = true; 296 297 if (aFrame) { 298 mNativeValue = aFrame->StyleFont()->mSize.ToAppUnits(); 299 mIsDefined = true; 300 } 301 } 302 303 void TextAttrsMgr::FontSizeTextAttr::ExposeValue(AccAttributes* aAttributes, 304 const nscoord& aValue) { 305 // Convert from nscoord to pt. 306 // 307 // Note: according to IA2, "The conversion doesn't have to be exact. 308 // The intent is to give the user a feel for the size of the text." 309 // 310 // ATK does not specify a unit and will likely follow IA2 here. 311 // 312 // XXX todo: consider sharing this code with layout module? (bug 474621) 313 float px = NSAppUnitsToFloatPixels(aValue, mozilla::AppUnitsPerCSSPixel()); 314 // Each pt is 4/3 of a CSS pixel. 315 FontSize fontSize{NS_lround(px * 3 / 4)}; 316 317 aAttributes->SetAttribute(nsGkAtoms::font_size, fontSize); 318 } 319 320 //////////////////////////////////////////////////////////////////////////////// 321 // FontStyleTextAttr 322 //////////////////////////////////////////////////////////////////////////////// 323 324 TextAttrsMgr::FontStyleTextAttr::FontStyleTextAttr(nsIFrame* aRootFrame, 325 nsIFrame* aFrame) 326 : TTextAttr<FontSlantStyle>(!aFrame) { 327 mRootNativeValue = aRootFrame->StyleFont()->mFont.style; 328 mIsRootDefined = true; 329 330 if (aFrame) { 331 mNativeValue = aFrame->StyleFont()->mFont.style; 332 mIsDefined = true; 333 } 334 } 335 336 void TextAttrsMgr::FontStyleTextAttr::ExposeValue( 337 AccAttributes* aAttributes, const FontSlantStyle& aValue) { 338 if (aValue.IsNormal()) { 339 aAttributes->SetAttribute(nsGkAtoms::font_style, nsGkAtoms::normal); 340 } else if (aValue.IsItalic()) { 341 RefPtr<nsAtom> atom = NS_Atomize("italic"); 342 aAttributes->SetAttribute(nsGkAtoms::font_style, atom); 343 } else { 344 nsAutoCString s; 345 aValue.ToString(s); 346 nsString wide; 347 CopyUTF8toUTF16(s, wide); 348 aAttributes->SetAttribute(nsGkAtoms::font_style, std::move(wide)); 349 } 350 } 351 352 //////////////////////////////////////////////////////////////////////////////// 353 // FontWeightTextAttr 354 //////////////////////////////////////////////////////////////////////////////// 355 356 TextAttrsMgr::FontWeightTextAttr::FontWeightTextAttr(nsIFrame* aRootFrame, 357 nsIFrame* aFrame) 358 : TTextAttr<FontWeight>(!aFrame) { 359 mRootNativeValue = GetFontWeight(aRootFrame); 360 mIsRootDefined = true; 361 362 if (aFrame) { 363 mNativeValue = GetFontWeight(aFrame); 364 mIsDefined = true; 365 } 366 } 367 368 void TextAttrsMgr::FontWeightTextAttr::ExposeValue(AccAttributes* aAttributes, 369 const FontWeight& aValue) { 370 int value = aValue.ToIntRounded(); 371 aAttributes->SetAttribute(nsGkAtoms::font_weight, value); 372 } 373 374 FontWeight TextAttrsMgr::FontWeightTextAttr::GetFontWeight(nsIFrame* aFrame) { 375 // nsFont::width isn't suitable here because it's necessary to expose real 376 // value of font weight (used font might not have some font weight values). 377 RefPtr<nsFontMetrics> fm = 378 nsLayoutUtils::GetFontMetricsForFrame(aFrame, 1.0f); 379 380 gfxFontGroup* fontGroup = fm->GetThebesFontGroup(); 381 RefPtr<gfxFont> font = fontGroup->GetFirstValidFont(); 382 383 // When there doesn't exist a bold font in the family and so the rendering of 384 // a non-bold font face is changed so that the user sees what looks like a 385 // bold font, i.e. synthetic bolding is used. (Simply returns false on any 386 // platforms that don't use the multi-strike synthetic bolding.) 387 if (font->ApplySyntheticBold()) { 388 return FontWeight::BOLD; 389 } 390 391 // On Windows, font->GetStyle()->weight will give the same weight as 392 // fontEntry->Weight(), the weight of the first font in the font group, 393 // which may not be the weight of the font face used to render the 394 // characters. On Mac, font->GetStyle()->weight will just give the same 395 // number as getComputedStyle(). fontEntry->Weight() will give the weight 396 // range supported by the font face used, so we clamp the weight that was 397 // requested by style to what is actually supported by the font. 398 gfxFontEntry* fontEntry = font->GetFontEntry(); 399 return fontEntry->Weight().Clamp(font->GetStyle()->weight); 400 } 401 402 //////////////////////////////////////////////////////////////////////////////// 403 // AutoGeneratedTextAttr 404 //////////////////////////////////////////////////////////////////////////////// 405 TextAttrsMgr::AutoGeneratedTextAttr::AutoGeneratedTextAttr( 406 HyperTextAccessible* aHyperTextAcc, LocalAccessible* aAccessible) 407 : TTextAttr<bool>(!aAccessible) { 408 mRootNativeValue = false; 409 mIsRootDefined = false; 410 411 if (aAccessible) { 412 mIsDefined = mNativeValue = 413 ((aAccessible->NativeRole() == roles::STATICTEXT) || 414 (aAccessible->NativeRole() == roles::LISTITEM_MARKER)); 415 } 416 } 417 418 void TextAttrsMgr::AutoGeneratedTextAttr::ExposeValue( 419 AccAttributes* aAttributes, const bool& aValue) { 420 aAttributes->SetAttribute(nsGkAtoms::auto_generated, aValue); 421 } 422 423 //////////////////////////////////////////////////////////////////////////////// 424 // TextDecorTextAttr 425 //////////////////////////////////////////////////////////////////////////////// 426 427 TextAttrsMgr::TextDecorValue::TextDecorValue(nsIFrame* aFrame) { 428 const nsStyleTextReset* textReset = aFrame->StyleTextReset(); 429 mStyle = textReset->mTextDecorationStyle; 430 mColor = textReset->mTextDecorationColor.CalcColor(aFrame); 431 mLine = 432 textReset->mTextDecorationLine & (StyleTextDecorationLine::UNDERLINE | 433 StyleTextDecorationLine::LINE_THROUGH); 434 } 435 436 TextAttrsMgr::TextDecorTextAttr::TextDecorTextAttr(nsIFrame* aRootFrame, 437 nsIFrame* aFrame) 438 : TTextAttr<TextDecorValue>(!aFrame) { 439 mRootNativeValue = TextDecorValue(aRootFrame); 440 mIsRootDefined = mRootNativeValue.IsDefined(); 441 442 if (aFrame) { 443 mNativeValue = TextDecorValue(aFrame); 444 mIsDefined = mNativeValue.IsDefined(); 445 } 446 } 447 448 void TextAttrsMgr::TextDecorTextAttr::ExposeValue( 449 AccAttributes* aAttributes, const TextDecorValue& aValue) { 450 if (aValue.IsUnderline()) { 451 RefPtr<nsAtom> underlineStyle = 452 StyleInfo::TextDecorationStyleToAtom(aValue.Style()); 453 aAttributes->SetAttribute(nsGkAtoms::textUnderlineStyle, underlineStyle); 454 455 aAttributes->SetAttribute(nsGkAtoms::textUnderlineColor, 456 Color{aValue.Color()}); 457 return; 458 } 459 460 if (aValue.IsLineThrough()) { 461 RefPtr<nsAtom> lineThroughStyle = 462 StyleInfo::TextDecorationStyleToAtom(aValue.Style()); 463 aAttributes->SetAttribute(nsGkAtoms::textLineThroughStyle, 464 lineThroughStyle); 465 466 aAttributes->SetAttribute(nsGkAtoms::textLineThroughColor, 467 Color{aValue.Color()}); 468 } 469 } 470 471 //////////////////////////////////////////////////////////////////////////////// 472 // TextPosTextAttr 473 //////////////////////////////////////////////////////////////////////////////// 474 475 TextAttrsMgr::TextPosTextAttr::TextPosTextAttr(nsIFrame* aRootFrame, 476 nsIFrame* aFrame, 477 nsIContent* aRootElm, 478 nsIContent* aElm) 479 : TTextAttr<Maybe<TextPosValue>>(!aFrame && !aElm), mRootElm(aRootElm) { 480 // Get the text-position values for the roots and children. 481 // If we find an ARIA text-position value on a DOM element - searching up 482 // from the supplied root DOM element - use the associated frame as the root 483 // frame. This ensures that we're using the proper root frame for comparison. 484 nsIFrame* ariaFrame = nullptr; 485 Maybe<TextPosValue> rootAria = GetAriaTextPosValue(aRootElm, ariaFrame); 486 if (rootAria && ariaFrame) { 487 aRootFrame = ariaFrame; 488 } 489 Maybe<TextPosValue> rootLayout = GetLayoutTextPosValue(aRootFrame); 490 Maybe<TextPosValue> childLayout; 491 Maybe<TextPosValue> childAria; 492 if (aFrame) { 493 childLayout = GetLayoutTextPosValue(aFrame); 494 } 495 if (aElm) { 496 childAria = GetAriaTextPosValue(aElm); 497 } 498 499 // Aria values take precedence over layout values. 500 mIsRootDefined = rootAria || rootLayout; 501 mRootNativeValue = rootAria ? rootAria : rootLayout; 502 mIsDefined = childAria || childLayout; 503 mNativeValue = childAria ? childAria : childLayout; 504 505 // If there's no child text-position information from ARIA, and the child 506 // layout info is equivalent to the root layout info (i.e., it's inherited), 507 // then we should prefer the root information. 508 if (!childAria && childLayout == rootLayout) { 509 mIsDefined = false; 510 } 511 } 512 513 void TextAttrsMgr::TextPosTextAttr::ExposeValue( 514 AccAttributes* aAttributes, const Maybe<TextPosValue>& aValue) { 515 if (aValue.isNothing()) { 516 return; 517 } 518 519 RefPtr<nsAtom> atom = nullptr; 520 switch (*aValue) { 521 case eTextPosBaseline: 522 atom = nsGkAtoms::baseline; 523 break; 524 525 case eTextPosSub: 526 atom = nsGkAtoms::sub; 527 break; 528 529 case eTextPosSuper: 530 atom = NS_Atomize("super"); 531 break; 532 } 533 534 if (atom) { 535 aAttributes->SetAttribute(nsGkAtoms::textPosition, atom); 536 } 537 } 538 539 Maybe<TextAttrsMgr::TextPosValue> 540 TextAttrsMgr::TextPosTextAttr::GetAriaTextPosValue(nsIContent* aElm) const { 541 nsIFrame* ariaFrame = nullptr; 542 return GetAriaTextPosValue(aElm, ariaFrame); 543 } 544 545 Maybe<TextAttrsMgr::TextPosValue> 546 TextAttrsMgr::TextPosTextAttr::GetAriaTextPosValue(nsIContent* aElm, 547 nsIFrame*& ariaFrame) const { 548 // Search for the superscript and subscript roles that imply text-position. 549 const nsIContent* elm = aElm; 550 do { 551 if (elm->IsElement()) { 552 const mozilla::dom::Element* domElm = elm->AsElement(); 553 static const dom::Element::AttrValuesArray tokens[] = { 554 nsGkAtoms::subscript, nsGkAtoms::superscript, nullptr}; 555 const int32_t valueIdx = domElm->FindAttrValueIn( 556 kNameSpaceID_None, nsGkAtoms::role, tokens, eCaseMatters); 557 ariaFrame = domElm->GetPrimaryFrame(); 558 if (valueIdx == 0) { 559 return Some(eTextPosSub); 560 } 561 if (valueIdx == 1) { 562 return Some(eTextPosSuper); 563 } 564 } 565 } while ((elm = elm->GetParent()) && elm != mRootElm); 566 567 ariaFrame = nullptr; 568 return Nothing{}; 569 } 570 571 Maybe<TextAttrsMgr::TextPosValue> 572 TextAttrsMgr::TextPosTextAttr::GetLayoutTextPosValue(nsIFrame* aFrame) const { 573 const auto& verticalAlign = aFrame->StyleDisplay()->mVerticalAlign; 574 if (verticalAlign.IsKeyword()) { 575 switch (verticalAlign.AsKeyword()) { 576 case StyleVerticalAlignKeyword::Baseline: 577 return Some(eTextPosBaseline); 578 case StyleVerticalAlignKeyword::Sub: 579 return Some(eTextPosSub); 580 case StyleVerticalAlignKeyword::Super: 581 return Some(eTextPosSuper); 582 // No good guess for the rest, so do not expose value of text-position 583 // attribute. 584 default: 585 return Nothing{}; 586 } 587 } 588 589 const auto& length = verticalAlign.AsLength(); 590 if (length.ConvertsToPercentage()) { 591 const float percentValue = length.ToPercentage(); 592 return percentValue > 0 ? Some(eTextPosSuper) 593 : (percentValue < 0 ? Some(eTextPosSub) 594 : Some(eTextPosBaseline)); 595 } 596 597 if (length.ConvertsToLength()) { 598 const nscoord coordValue = length.ToLength(); 599 return coordValue > 0 600 ? Some(eTextPosSuper) 601 : (coordValue < 0 ? Some(eTextPosSub) : Some(eTextPosBaseline)); 602 } 603 604 if (const nsIContent* content = aFrame->GetContent()) { 605 if (content->IsHTMLElement(nsGkAtoms::sup)) return Some(eTextPosSuper); 606 if (content->IsHTMLElement(nsGkAtoms::sub)) return Some(eTextPosSub); 607 } 608 609 return Nothing{}; 610 }