nsCoreUtils.cpp (24271B)
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 "nsCoreUtils.h" 7 8 #include "nsAttrValue.h" 9 #include "nsIAccessibleTypes.h" 10 11 #include "mozilla/dom/Document.h" 12 #include "nsAccUtils.h" 13 #include "nsRange.h" 14 #include "nsXULElement.h" 15 #include "nsIDocShell.h" 16 #include "nsIObserverService.h" 17 #include "nsPresContext.h" 18 #include "nsISelectionController.h" 19 #include "nsISimpleEnumerator.h" 20 #include "mozilla/dom/TouchEvent.h" 21 #include "mozilla/ErrorResult.h" 22 #include "mozilla/EventListenerManager.h" 23 #include "mozilla/EventStateManager.h" 24 #include "mozilla/MouseEvents.h" 25 #include "mozilla/PresShell.h" 26 #include "mozilla/ScrollContainerFrame.h" 27 #include "mozilla/StaticPrefs_dom.h" 28 #include "mozilla/TouchEvents.h" 29 #include "nsGkAtoms.h" 30 31 #include "AnchorPositioningUtils.h" 32 #include "nsComponentManagerUtils.h" 33 34 #include "XULTreeElement.h" 35 #include "nsIContentInlines.h" 36 #include "nsTreeColumns.h" 37 #include "mozilla/dom/DocumentInlines.h" 38 #include "mozilla/dom/Element.h" 39 #include "mozilla/dom/ElementInternals.h" 40 #include "mozilla/dom/HTMLLabelElement.h" 41 #include "mozilla/dom/MouseEventBinding.h" 42 #include "mozilla/dom/Selection.h" 43 44 using namespace mozilla; 45 46 using mozilla::dom::DOMRect; 47 using mozilla::dom::Element; 48 using mozilla::dom::Selection; 49 using mozilla::dom::XULTreeElement; 50 51 using mozilla::a11y::nsAccUtils; 52 53 //////////////////////////////////////////////////////////////////////////////// 54 // nsCoreUtils 55 //////////////////////////////////////////////////////////////////////////////// 56 57 bool nsCoreUtils::IsLabelWithControl(nsIContent* aContent) { 58 dom::HTMLLabelElement* label = dom::HTMLLabelElement::FromNode(aContent); 59 if (label && label->GetControl()) return true; 60 61 return false; 62 } 63 64 bool nsCoreUtils::HasClickListener(nsIContent* aContent) { 65 NS_ENSURE_TRUE(aContent, false); 66 EventListenerManager* listenerManager = 67 aContent->GetExistingListenerManager(); 68 69 return listenerManager && 70 (listenerManager->HasListenersFor(nsGkAtoms::onclick) || 71 listenerManager->HasListenersFor(nsGkAtoms::onmousedown) || 72 listenerManager->HasListenersFor(nsGkAtoms::onmouseup)); 73 } 74 75 void nsCoreUtils::DispatchClickEvent(XULTreeElement* aTree, int32_t aRowIndex, 76 nsTreeColumn* aColumn, 77 const nsAString& aPseudoElt) { 78 RefPtr<dom::Element> tcElm = aTree->GetTreeBody(); 79 if (!tcElm) return; 80 81 Document* document = tcElm->GetUncomposedDoc(); 82 if (!document) return; 83 84 RefPtr<PresShell> presShell = document->GetPresShell(); 85 if (!presShell) { 86 return; 87 } 88 89 // Ensure row is visible. 90 aTree->EnsureRowIsVisible(aRowIndex); 91 92 // Calculate x and y coordinates. 93 nsresult rv; 94 nsIntRect rect = 95 aTree->GetCoordsForCellItem(aRowIndex, aColumn, aPseudoElt, rv); 96 if (NS_FAILED(rv)) { 97 return; 98 } 99 100 RefPtr<DOMRect> treeBodyRect = tcElm->GetBoundingClientRect(); 101 int32_t tcX = (int32_t)treeBodyRect->X(); 102 int32_t tcY = (int32_t)treeBodyRect->Y(); 103 104 // Dispatch mouse events. 105 AutoWeakFrame tcFrame = tcElm->GetPrimaryFrame(); 106 nsIFrame* rootFrame = presShell->GetRootFrame(); 107 108 nsPoint offset; 109 nsCOMPtr<nsIWidget> rootWidget = rootFrame->GetNearestWidget(offset); 110 RefPtr<nsPresContext> presContext = presShell->GetPresContext(); 111 112 int32_t cnvdX = presContext->CSSPixelsToDevPixels(tcX + int32_t(rect.x) + 1) + 113 presContext->AppUnitsToDevPixels(offset.x); 114 int32_t cnvdY = presContext->CSSPixelsToDevPixels(tcY + int32_t(rect.y) + 1) + 115 presContext->AppUnitsToDevPixels(offset.y); 116 117 // This isn't needed once bug 1924790 is fixed. 118 tcElm->OwnerDoc()->NotifyUserGestureActivation(); 119 120 // XUL is just desktop, so there is no real reason for senfing touch events. 121 DispatchMouseEvent(eMouseDown, cnvdX, cnvdY, tcElm, tcFrame, presShell, 122 rootWidget); 123 124 DispatchMouseEvent(eMouseUp, cnvdX, cnvdY, tcElm, tcFrame, presShell, 125 rootWidget); 126 } 127 128 void nsCoreUtils::DispatchMouseEvent(EventMessage aMessage, int32_t aX, 129 int32_t aY, nsIContent* aContent, 130 nsIFrame* aFrame, PresShell* aPresShell, 131 nsIWidget* aRootWidget) { 132 MOZ_ASSERT(!IsPointerEventMessage(aMessage)); 133 WidgetMouseEvent event(true, aMessage, aRootWidget, WidgetMouseEvent::eReal); 134 135 event.mRefPoint = LayoutDeviceIntPoint(aX, aY); 136 137 event.mClickCount = 1; 138 event.mButton = MouseButton::ePrimary; 139 event.mInputSource = dom::MouseEvent_Binding::MOZ_SOURCE_UNKNOWN; 140 141 nsEventStatus status = nsEventStatus_eIgnore; 142 aPresShell->HandleEventWithTarget(&event, aFrame, aContent, &status); 143 } 144 145 void nsCoreUtils::DispatchTouchEvent(EventMessage aMessage, int32_t aX, 146 int32_t aY, nsIContent* aContent, 147 nsIFrame* aFrame, PresShell* aPresShell, 148 nsIWidget* aRootWidget) { 149 nsIDocShell* docShell = nullptr; 150 if (aPresShell->GetDocument()) { 151 docShell = aPresShell->GetDocument()->GetDocShell(); 152 } 153 if (!dom::TouchEvent::PrefEnabled(docShell)) { 154 return; 155 } 156 157 WidgetTouchEvent event(true, aMessage, aRootWidget); 158 159 // XXX: Touch has an identifier of -1 to hint that it is synthesized. 160 RefPtr<dom::Touch> t = new dom::Touch(-1, LayoutDeviceIntPoint(aX, aY), 161 LayoutDeviceIntPoint(1, 1), 0.0f, 1.0f); 162 t->SetTouchTarget(aContent); 163 event.mTouches.AppendElement(t); 164 nsEventStatus status = nsEventStatus_eIgnore; 165 aPresShell->HandleEventWithTarget(&event, aFrame, aContent, &status); 166 } 167 168 uint32_t nsCoreUtils::GetAccessKeyFor(nsIContent* aContent) { 169 // Accesskeys are registered by @accesskey attribute only. At first check 170 // whether it is presented on the given element to avoid the slow 171 // EventStateManager::GetRegisteredAccessKey() method. 172 if (!aContent->IsElement() || !aContent->AsElement()->HasAttr( 173 kNameSpaceID_None, nsGkAtoms::accesskey)) { 174 return 0; 175 } 176 177 nsPresContext* presContext = aContent->OwnerDoc()->GetPresContext(); 178 if (!presContext) return 0; 179 180 EventStateManager* esm = presContext->EventStateManager(); 181 if (!esm) return 0; 182 183 return esm->GetRegisteredAccessKey(aContent->AsElement()); 184 } 185 186 nsIContent* nsCoreUtils::GetDOMElementFor(nsIContent* aContent) { 187 if (aContent->IsElement()) return aContent; 188 189 if (aContent->IsText()) return aContent->GetFlattenedTreeParent(); 190 191 return nullptr; 192 } 193 194 nsINode* nsCoreUtils::GetDOMNodeFromDOMPoint(nsINode* aNode, uint32_t aOffset) { 195 if (aNode && aNode->IsElement()) { 196 uint32_t childCount = aNode->GetChildCount(); 197 NS_ASSERTION(aOffset <= childCount, "Wrong offset of the DOM point!"); 198 199 // The offset can be after last child of container node that means DOM point 200 // is placed immediately after the last child. In this case use the DOM node 201 // from the given DOM point is used as result node. 202 if (aOffset != childCount) return aNode->GetChildAt_Deprecated(aOffset); 203 } 204 205 return aNode; 206 } 207 208 bool nsCoreUtils::IsAncestorOf(nsINode* aPossibleAncestorNode, 209 nsINode* aPossibleDescendantNode, 210 nsINode* aRootNode) { 211 NS_ENSURE_TRUE(aPossibleAncestorNode && aPossibleDescendantNode, false); 212 213 nsINode* parentNode = aPossibleDescendantNode; 214 while ((parentNode = parentNode->GetParentNode()) && 215 parentNode != aRootNode) { 216 if (parentNode == aPossibleAncestorNode) return true; 217 } 218 219 return false; 220 } 221 222 nsresult nsCoreUtils::ScrollSubstringTo(nsIFrame* aFrame, nsRange* aRange, 223 uint32_t aScrollType) { 224 ScrollAxis vertical, horizontal; 225 ConvertScrollTypeToPercents(aScrollType, &vertical, &horizontal); 226 227 return ScrollSubstringTo(aFrame, aRange, vertical, horizontal); 228 } 229 230 nsresult nsCoreUtils::ScrollSubstringTo(nsIFrame* aFrame, nsRange* aRange, 231 ScrollAxis aVertical, 232 ScrollAxis aHorizontal) { 233 if (!aFrame || !aRange) { 234 return NS_ERROR_FAILURE; 235 } 236 237 const RefPtr<dom::Selection> selection = [&]() -> dom::Selection* { 238 nsISelectionController* const selCon = aFrame->GetSelectionController(); 239 NS_ENSURE_TRUE(selCon, nullptr); 240 return selCon->GetSelection( 241 nsISelectionController::SELECTION_ACCESSIBILITY); 242 }(); 243 if (MOZ_UNLIKELY(!selection)) { 244 return NS_ERROR_FAILURE; 245 } 246 247 selection->RemoveAllRanges(IgnoreErrors()); 248 selection->AddRangeAndSelectFramesAndNotifyListeners(*aRange, IgnoreErrors()); 249 250 selection->ScrollIntoView(nsISelectionController::SELECTION_ANCHOR_REGION, 251 aVertical, aHorizontal, ScrollFlags::None, 252 SelectionScrollMode::SyncNoFlush); 253 254 selection->CollapseToStart(IgnoreErrors()); 255 256 return NS_OK; 257 } 258 259 void nsCoreUtils::ScrollFrameToPoint(nsIFrame* aScrollContainerFrame, 260 nsIFrame* aFrame, 261 const LayoutDeviceIntPoint& aPoint) { 262 ScrollContainerFrame* scrollContainerFrame = 263 do_QueryFrame(aScrollContainerFrame); 264 if (!scrollContainerFrame) { 265 return; 266 } 267 268 nsPoint point = LayoutDeviceIntPoint::ToAppUnits( 269 aPoint, aFrame->PresContext()->AppUnitsPerDevPixel()); 270 nsRect frameRect = aFrame->GetScreenRectInAppUnits(); 271 nsPoint deltaPoint = point - frameRect.TopLeft(); 272 273 nsPoint scrollPoint = scrollContainerFrame->GetScrollPosition(); 274 scrollPoint -= deltaPoint; 275 276 scrollContainerFrame->ScrollTo(scrollPoint, ScrollMode::Instant); 277 } 278 279 void nsCoreUtils::ConvertScrollTypeToPercents(uint32_t aScrollType, 280 ScrollAxis* aVertical, 281 ScrollAxis* aHorizontal) { 282 WhereToScroll whereY, whereX; 283 WhenToScroll whenY, whenX; 284 switch (aScrollType) { 285 case nsIAccessibleScrollType::SCROLL_TYPE_TOP_LEFT: 286 whereY = WhereToScroll::Start; 287 whenY = WhenToScroll::Always; 288 whereX = WhereToScroll::Start; 289 whenX = WhenToScroll::Always; 290 break; 291 case nsIAccessibleScrollType::SCROLL_TYPE_BOTTOM_RIGHT: 292 whereY = WhereToScroll::End; 293 whenY = WhenToScroll::Always; 294 whereX = WhereToScroll::End; 295 whenX = WhenToScroll::Always; 296 break; 297 case nsIAccessibleScrollType::SCROLL_TYPE_TOP_EDGE: 298 whereY = WhereToScroll::Start; 299 whenY = WhenToScroll::Always; 300 whereX = WhereToScroll::Nearest; 301 whenX = WhenToScroll::IfNotFullyVisible; 302 break; 303 case nsIAccessibleScrollType::SCROLL_TYPE_BOTTOM_EDGE: 304 whereY = WhereToScroll::End; 305 whenY = WhenToScroll::Always; 306 whereX = WhereToScroll::Nearest; 307 whenX = WhenToScroll::IfNotFullyVisible; 308 break; 309 case nsIAccessibleScrollType::SCROLL_TYPE_LEFT_EDGE: 310 whereY = WhereToScroll::Nearest; 311 whenY = WhenToScroll::IfNotFullyVisible; 312 whereX = WhereToScroll::Start; 313 whenX = WhenToScroll::Always; 314 break; 315 case nsIAccessibleScrollType::SCROLL_TYPE_RIGHT_EDGE: 316 whereY = WhereToScroll::Nearest; 317 whenY = WhenToScroll::IfNotFullyVisible; 318 whereX = WhereToScroll::End; 319 whenX = WhenToScroll::Always; 320 break; 321 default: 322 whereY = WhereToScroll::Center; 323 whenY = WhenToScroll::IfNotFullyVisible; 324 whereX = WhereToScroll::Center; 325 whenX = WhenToScroll::IfNotFullyVisible; 326 } 327 *aVertical = ScrollAxis(whereY, whenY); 328 *aHorizontal = ScrollAxis(whereX, whenX); 329 } 330 331 already_AddRefed<nsIDocShell> nsCoreUtils::GetDocShellFor(nsINode* aNode) { 332 if (!aNode) return nullptr; 333 334 nsCOMPtr<nsIDocShell> docShell = aNode->OwnerDoc()->GetDocShell(); 335 return docShell.forget(); 336 } 337 338 bool nsCoreUtils::IsRootDocument(Document* aDocument) { 339 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = aDocument->GetDocShell(); 340 NS_ASSERTION(docShellTreeItem, "No document shell for document!"); 341 342 nsCOMPtr<nsIDocShellTreeItem> parentTreeItem; 343 docShellTreeItem->GetInProcessParent(getter_AddRefs(parentTreeItem)); 344 345 return !parentTreeItem; 346 } 347 348 bool nsCoreUtils::IsTopLevelContentDocInProcess(Document* aDocumentNode) { 349 mozilla::dom::BrowsingContext* bc = aDocumentNode->GetBrowsingContext(); 350 return bc->IsContent() && ( 351 // Tab document. 352 bc->IsTop() || 353 // Out-of-process iframe. 354 !bc->GetParent()->IsInProcess()); 355 } 356 357 bool nsCoreUtils::IsErrorPage(Document* aDocument) { 358 nsIURI* uri = aDocument->GetDocumentURI(); 359 if (!uri->SchemeIs("about")) { 360 return false; 361 } 362 363 nsAutoCString path; 364 uri->GetPathQueryRef(path); 365 366 constexpr auto neterror = "neterror"_ns; 367 constexpr auto certerror = "certerror"_ns; 368 369 return StringBeginsWith(path, neterror) || StringBeginsWith(path, certerror); 370 } 371 372 PresShell* nsCoreUtils::GetPresShellFor(nsINode* aNode) { 373 return aNode->OwnerDoc()->GetPresShell(); 374 } 375 376 bool nsCoreUtils::GetID(nsIContent* aContent, nsAString& aID) { 377 return aContent->IsElement() && 378 aContent->AsElement()->GetAttr(nsGkAtoms::id, aID); 379 } 380 381 bool nsCoreUtils::GetUIntAttr(nsIContent* aContent, nsAtom* aAttr, 382 int32_t* aUInt) { 383 if (!aContent->IsElement()) { 384 return false; 385 } 386 return GetUIntAttrValue(nsAccUtils::GetARIAAttr(aContent->AsElement(), aAttr), 387 aUInt); 388 } 389 390 bool nsCoreUtils::GetUIntAttrValue(const nsAttrValue* aVal, int32_t* aUInt) { 391 if (!aVal) { 392 return false; 393 } 394 nsAutoString value; 395 aVal->ToString(value); 396 if (!value.IsEmpty()) { 397 nsresult error = NS_OK; 398 int32_t integer = value.ToInteger(&error); 399 if (NS_SUCCEEDED(error) && integer > 0) { 400 *aUInt = integer; 401 return true; 402 } 403 } 404 405 return false; 406 } 407 408 void nsCoreUtils::GetLanguageFor(nsIContent* aContent, nsIContent* aRootContent, 409 nsAString& aLanguage) { 410 aLanguage.Truncate(); 411 412 nsIContent* walkUp = aContent; 413 while (walkUp && walkUp != aRootContent && 414 (!walkUp->IsElement() || 415 !walkUp->AsElement()->GetAttr(nsGkAtoms::lang, aLanguage))) { 416 walkUp = walkUp->GetParent(); 417 } 418 } 419 420 XULTreeElement* nsCoreUtils::GetTree(nsIContent* aContent) { 421 // Find DOMNode's parents recursively until reach the <tree> tag 422 nsIContent* currentContent = aContent; 423 while (currentContent) { 424 if (currentContent->NodeInfo()->Equals(nsGkAtoms::tree, kNameSpaceID_XUL)) { 425 return XULTreeElement::FromNode(currentContent); 426 } 427 currentContent = currentContent->GetFlattenedTreeParent(); 428 } 429 430 return nullptr; 431 } 432 433 already_AddRefed<nsTreeColumn> nsCoreUtils::GetFirstSensibleColumn( 434 XULTreeElement* aTree, FlushType aFlushType) { 435 if (!aTree) { 436 return nullptr; 437 } 438 439 RefPtr<nsTreeColumns> cols = aTree->GetColumns(aFlushType); 440 if (!cols) { 441 return nullptr; 442 } 443 444 RefPtr<nsTreeColumn> column = cols->GetFirstColumn(); 445 if (column && IsColumnHidden(column)) return GetNextSensibleColumn(column); 446 447 return column.forget(); 448 } 449 450 uint32_t nsCoreUtils::GetSensibleColumnCount(XULTreeElement* aTree) { 451 uint32_t count = 0; 452 if (!aTree) { 453 return count; 454 } 455 456 RefPtr<nsTreeColumns> cols = aTree->GetColumns(); 457 if (!cols) { 458 return count; 459 } 460 461 nsTreeColumn* column = cols->GetFirstColumn(); 462 463 while (column) { 464 if (!IsColumnHidden(column)) count++; 465 466 column = column->GetNext(); 467 } 468 469 return count; 470 } 471 472 already_AddRefed<nsTreeColumn> nsCoreUtils::GetSensibleColumnAt( 473 XULTreeElement* aTree, uint32_t aIndex) { 474 if (!aTree) { 475 return nullptr; 476 } 477 478 uint32_t idx = aIndex; 479 480 nsCOMPtr<nsTreeColumn> column = GetFirstSensibleColumn(aTree); 481 while (column) { 482 if (idx == 0) return column.forget(); 483 484 idx--; 485 column = GetNextSensibleColumn(column); 486 } 487 488 return nullptr; 489 } 490 491 already_AddRefed<nsTreeColumn> nsCoreUtils::GetNextSensibleColumn( 492 nsTreeColumn* aColumn) { 493 if (!aColumn) { 494 return nullptr; 495 } 496 497 RefPtr<nsTreeColumn> nextColumn = aColumn->GetNext(); 498 499 while (nextColumn && IsColumnHidden(nextColumn)) { 500 nextColumn = nextColumn->GetNext(); 501 } 502 503 return nextColumn.forget(); 504 } 505 506 already_AddRefed<nsTreeColumn> nsCoreUtils::GetPreviousSensibleColumn( 507 nsTreeColumn* aColumn) { 508 if (!aColumn) { 509 return nullptr; 510 } 511 512 RefPtr<nsTreeColumn> prevColumn = aColumn->GetPrevious(); 513 514 while (prevColumn && IsColumnHidden(prevColumn)) { 515 prevColumn = prevColumn->GetPrevious(); 516 } 517 518 return prevColumn.forget(); 519 } 520 521 bool nsCoreUtils::IsColumnHidden(nsTreeColumn* aColumn) { 522 if (!aColumn) { 523 return false; 524 } 525 526 Element* element = aColumn->Element(); 527 return element->GetBoolAttr(nsGkAtoms::hidden); 528 } 529 530 void nsCoreUtils::ScrollTo(PresShell* aPresShell, nsIContent* aContent, 531 uint32_t aScrollType) { 532 ScrollAxis vertical, horizontal; 533 ConvertScrollTypeToPercents(aScrollType, &vertical, &horizontal); 534 aPresShell->ScrollContentIntoView(aContent, vertical, horizontal, 535 ScrollFlags::ScrollOverflowHidden); 536 } 537 538 bool nsCoreUtils::IsHTMLTableHeader(nsIContent* aContent) { 539 return aContent->NodeInfo()->Equals(nsGkAtoms::th) || 540 (aContent->IsElement() && 541 aContent->AsElement()->HasAttr(nsGkAtoms::scope)); 542 } 543 544 bool nsCoreUtils::IsWhitespaceString(const nsAString& aString) { 545 nsAString::const_char_iterator iterBegin, iterEnd; 546 547 aString.BeginReading(iterBegin); 548 aString.EndReading(iterEnd); 549 550 while (iterBegin != iterEnd && IsWhitespace(*iterBegin)) ++iterBegin; 551 552 return iterBegin == iterEnd; 553 } 554 555 bool nsCoreUtils::AccEventObserversExist() { 556 nsCOMPtr<nsIObserverService> obsService = services::GetObserverService(); 557 NS_ENSURE_TRUE(obsService, false); 558 559 nsCOMPtr<nsISimpleEnumerator> observers; 560 obsService->EnumerateObservers(NS_ACCESSIBLE_EVENT_TOPIC, 561 getter_AddRefs(observers)); 562 NS_ENSURE_TRUE(observers, false); 563 564 bool hasObservers = false; 565 observers->HasMoreElements(&hasObservers); 566 567 return hasObservers; 568 } 569 570 void nsCoreUtils::DispatchAccEvent(RefPtr<nsIAccessibleEvent> event) { 571 nsCOMPtr<nsIObserverService> obsService = services::GetObserverService(); 572 NS_ENSURE_TRUE_VOID(obsService); 573 574 obsService->NotifyObservers(event, NS_ACCESSIBLE_EVENT_TOPIC, nullptr); 575 } 576 577 bool nsCoreUtils::IsDisplayContents(nsIContent* aContent) { 578 auto* element = Element::FromNodeOrNull(aContent); 579 return element && element->IsDisplayContents(); 580 } 581 582 bool nsCoreUtils::CanCreateAccessibleWithoutFrame(nsIContent* aContent) { 583 auto* element = Element::FromNodeOrNull(aContent); 584 if (!element) { 585 return false; 586 } 587 if (!element->HasServoData() || Servo_Element_IsDisplayNone(element)) { 588 // Out of the flat tree or in a display: none subtree. 589 return false; 590 } 591 592 // If we aren't display: contents or option/optgroup we can't create an 593 // accessible without frame. Our select combobox code relies on the latter. 594 if (!element->IsDisplayContents() && 595 !element->IsAnyOfHTMLElements(nsGkAtoms::option, nsGkAtoms::optgroup)) { 596 return false; 597 } 598 599 // Even if we're display: contents or optgroups, we might not be able to 600 // create an accessible if we're in a content-visibility: hidden subtree. 601 // 602 // To check that, find the closest ancestor element with a frame. 603 for (nsINode* ancestor = element->GetFlattenedTreeParentNode(); 604 ancestor && ancestor->IsContent(); 605 ancestor = ancestor->GetFlattenedTreeParentNode()) { 606 if (nsIFrame* f = ancestor->AsContent()->GetPrimaryFrame()) { 607 if (f->HidesContent(nsIFrame::IncludeContentVisibility::Hidden) || 608 f->IsHiddenByContentVisibilityOnAnyAncestor( 609 nsIFrame::IncludeContentVisibility::Hidden)) { 610 return false; 611 } 612 break; 613 } 614 } 615 616 return true; 617 } 618 619 bool nsCoreUtils::IsDocumentVisibleConsideringInProcessAncestors( 620 const Document* aDocument) { 621 const Document* parent = aDocument; 622 do { 623 if (!parent->IsVisible()) { 624 return false; 625 } 626 } while ((parent = parent->GetInProcessParentDocument())); 627 return true; 628 } 629 630 bool nsCoreUtils::IsDescendantOfAnyShadowIncludingAncestor( 631 nsINode* aDescendant, nsINode* aStartAncestor) { 632 const nsINode* descRoot = aDescendant->SubtreeRoot(); 633 nsINode* ancRoot = aStartAncestor->SubtreeRoot(); 634 for (;;) { 635 if (ancRoot == descRoot) { 636 return true; 637 } 638 auto* shadow = mozilla::dom::ShadowRoot::FromNode(ancRoot); 639 if (!shadow || !shadow->GetHost()) { 640 break; 641 } 642 ancRoot = shadow->GetHost()->SubtreeRoot(); 643 } 644 return false; 645 } 646 647 Element* nsCoreUtils::GetAriaActiveDescendantElement(Element* aElement) { 648 if (Element* activeDescendant = aElement->GetAriaActiveDescendantElement()) { 649 return activeDescendant; 650 } 651 652 if (auto* element = nsGenericHTMLElement::FromNode(aElement)) { 653 if (auto* internals = element->GetInternals()) { 654 return internals->GetAriaActiveDescendantElement(); 655 } 656 } 657 658 return nullptr; 659 } 660 661 bool nsCoreUtils::IsTrimmedWhitespaceBeforeHardLineBreak(nsIFrame* aFrame) { 662 if (!aFrame->GetRect().IsEmpty() || 663 !aFrame->HasAnyStateBits(TEXT_END_OF_LINE)) { 664 return false; 665 } 666 // Normally, accessibility calls nsIFrame::GetRenderedText with 667 // TrailingWhitespace::NoTrim. Using TrailingWhitespace::Trim instead trims 0 668 // width whitespace before a hard line break, resulting in an empty string if 669 // that is all the frame contains. Note that TrailingWhitespace::Trim does 670 // *not* trim whitespace before a soft line break (wrapped line). 671 nsIFrame::RenderedText text = aFrame->GetRenderedText( 672 0, UINT32_MAX, nsIFrame::TextOffsetType::OffsetsInContentText, 673 nsIFrame::TrailingWhitespace::Trim); 674 return text.mString.IsEmpty(); 675 } 676 677 const nsIFrame* nsCoreUtils::GetAnchorForPositionedFrame( 678 const PresShell* aPresShell, const nsIFrame* aPositionedFrame) { 679 if (!aPositionedFrame || 680 !aPositionedFrame->Style()->HasAnchorPosReference()) { 681 return nullptr; 682 } 683 684 const nsAtom* anchorName = nullptr; 685 AnchorPosReferenceData* referencedAnchors = 686 aPositionedFrame->GetProperty(nsIFrame::AnchorPosReferences()); 687 688 if (!referencedAnchors) { 689 return nullptr; 690 } 691 692 for (const auto& entry : *referencedAnchors) { 693 if (entry.GetData().isNothing()) { 694 continue; 695 } 696 697 if (anchorName && entry.GetKey() != anchorName) { 698 // Multiple anchors referenced. 699 return nullptr; 700 } 701 702 anchorName = entry.GetKey(); 703 } 704 705 return anchorName 706 ? aPresShell->GetAnchorPosAnchor(anchorName, aPositionedFrame) 707 : nullptr; 708 } 709 710 nsIFrame* nsCoreUtils::GetPositionedFrameForAnchor( 711 const PresShell* aPresShell, const nsIFrame* aAnchorFrame) { 712 if (!aAnchorFrame) { 713 return nullptr; 714 } 715 716 nsIFrame* positionedFrame = nullptr; 717 const auto* styleDisp = aAnchorFrame->StyleDisplay(); 718 if (styleDisp->HasAnchorName()) { 719 for (auto& name : styleDisp->mAnchorName.AsSpan()) { 720 for (nsIFrame* frame : aPresShell->GetAnchorPosPositioned()) { 721 // Bug 1990069: We need to iterate over all positioned frames in doc and 722 // check their referenced anchors because we don't store reverse mapping 723 // from anchor to positioned frame. 724 const auto* referencedAnchors = 725 frame->GetProperty(nsIFrame::AnchorPosReferences()); 726 if (!referencedAnchors) { 727 // Depending on where we are in the reflow, this property may or may 728 // not be set. If it isn't set, a future reflow will set it, so we can 729 // just skip this frame for now. 730 continue; 731 } 732 const auto* data = referencedAnchors->Lookup(name.AsAtom()); 733 if (data && *data && data->ref().mOffsetData) { 734 if (aAnchorFrame == 735 aPresShell->GetAnchorPosAnchor(name.AsAtom(), frame)) { 736 if (positionedFrame) { 737 // Multiple positioned frames reference this anchor. 738 return nullptr; 739 } 740 positionedFrame = frame; 741 } 742 } 743 } 744 } 745 } 746 747 return positionedFrame; 748 }