Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/accessible/base/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 12 kB image not shown  

Quelle  TextRange.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "TextRange-inl.h"

#include "LocalAccessible-inl.h"
#include "HyperTextAccessible-inl.h"
#include "mozilla/IntegerRange.h"
#include "mozilla/dom/Selection.h"
#include "nsAccUtils.h"

namespace mozilla {
namespace a11y {

/**
 * Returns a text point for aAcc within aContainer.
 */

static void ToTextPoint(Accessible* aAcc, Accessible** aContainer,
                        int32_t* aOffset, bool aIsBefore = true) {
  if (aAcc->IsHyperText()) {
    *aContainer = aAcc;
    *aOffset =
        aIsBefore
            ? 0
            : static_cast<int32_t>(aAcc->AsHyperTextBase()->CharacterCount());
    return;
  }

  Accessible* child = nullptr;
  Accessible* parent = aAcc;
  do {
    child = parent;
    parent = parent->Parent();
  } while (parent && !parent->IsHyperText());

  if (parent) {
    *aContainer = parent;
    *aOffset = parent->AsHyperTextBase()->GetChildOffset(
        child->IndexInParent() + static_cast<int32_t>(!aIsBefore));
  }
}

////////////////////////////////////////////////////////////////////////////////
// TextPoint

bool TextPoint::operator<(const TextPoint& aPoint) const {
  if (mContainer == aPoint.mContainer) return mOffset < aPoint.mOffset;

  // Build the chain of parents
  Accessible* p1 = mContainer;
  Accessible* p2 = aPoint.mContainer;
  AutoTArray<Accessible*, 30> parents1, parents2;
  do {
    parents1.AppendElement(p1);
    p1 = p1->Parent();
  } while (p1);
  do {
    parents2.AppendElement(p2);
    p2 = p2->Parent();
  } while (p2);

  // Find where the parent chain differs
  uint32_t pos1 = parents1.Length(), pos2 = parents2.Length();
  for (uint32_t len = std::min(pos1, pos2); len > 0; --len) {
    Accessible* child1 = parents1.ElementAt(--pos1);
    Accessible* child2 = parents2.ElementAt(--pos2);
    if (child1 != child2) {
      return child1->IndexInParent() < child2->IndexInParent();
    }
  }

  if (pos1 != 0) {
    // If parents1 is a superset of parents2 then mContainer is a
    // descendant of aPoint.mContainer. The next element down in parents1
    // is mContainer's ancestor that is the child of aPoint.mContainer.
    // We compare its end offset in aPoint.mContainer with aPoint.mOffset.
    Accessible* child = parents1.ElementAt(pos1 - 1);
    MOZ_ASSERT(child->Parent() == aPoint.mContainer);
    return child->EndOffset() < static_cast<uint32_t>(aPoint.mOffset);
  }

  if (pos2 != 0) {
    // If parents2 is a superset of parents1 then aPoint.mContainer is a
    // descendant of mContainer. The next element down in parents2
    // is aPoint.mContainer's ancestor that is the child of mContainer.
    // We compare its start offset in mContainer with mOffset.
    Accessible* child = parents2.ElementAt(pos2 - 1);
    MOZ_ASSERT(child->Parent() == mContainer);
    return static_cast<uint32_t>(mOffset) < child->StartOffset();
  }

  NS_ERROR("Broken tree?!");
  return false;
}

////////////////////////////////////////////////////////////////////////////////
// TextRange

TextRange::TextRange(Accessible* aRoot, Accessible* aStartContainer,
                     int32_t aStartOffset, Accessible* aEndContainer,
                     int32_t aEndOffset)
    : mRoot(aRoot),
      mStartContainer(aStartContainer),
      mEndContainer(aEndContainer),
      mStartOffset(aStartOffset),
      mEndOffset(aEndOffset) {}

bool TextRange::Crop(Accessible* aContainer) {
  uint32_t boundaryPos = 0, containerPos = 0;
  AutoTArray<Accessible*, 30> boundaryParents, containerParents;

  // Crop the start boundary.
  Accessible* container = nullptr;
  HyperTextAccessibleBase* startHyper = mStartContainer->AsHyperTextBase();
  Accessible* boundary = startHyper->GetChildAtOffset(mStartOffset);
  if (boundary != aContainer) {
    CommonParent(boundary, aContainer, &boundaryParents, &boundaryPos,
                 &containerParents, &containerPos);

    if (boundaryPos == 0) {
      if (containerPos != 0) {
        // The container is contained by the start boundary, reduce the range to
        // the point starting at the container.
        ToTextPoint(aContainer, &mStartContainer, &mStartOffset);
      } else {
        // The start boundary and the container are siblings.
        container = aContainer;
      }
    } else {
      // The container does not contain the start boundary.
      boundary = boundaryParents[boundaryPos];
      container = containerParents[containerPos];
    }

    if (container) {
      // If the range start is after the container, then make the range invalid.
      if (boundary->IndexInParent() > container->IndexInParent()) {
        return !!(mRoot = nullptr);
      }

      // If the range starts before the container, then reduce the range to
      // the point starting at the container.
      if (boundary->IndexInParent() < container->IndexInParent()) {
        ToTextPoint(container, &mStartContainer, &mStartOffset);
      }
    }

    boundaryParents.SetLengthAndRetainStorage(0);
    containerParents.SetLengthAndRetainStorage(0);
  }

  HyperTextAccessibleBase* endHyper = mEndContainer->AsHyperTextBase();
  boundary = endHyper->GetChildAtOffset(mEndOffset);
  if (boundary == aContainer) {
    return true;
  }

  // Crop the end boundary.
  container = nullptr;
  CommonParent(boundary, aContainer, &boundaryParents, &boundaryPos,
               &containerParents, &containerPos);

  if (boundaryPos == 0) {
    if (containerPos != 0) {
      ToTextPoint(aContainer, &mEndContainer, &mEndOffset, false);
    } else {
      container = aContainer;
    }
  } else {
    boundary = boundaryParents[boundaryPos];
    container = containerParents[containerPos];
  }

  if (!container) {
    return true;
  }

  if (boundary->IndexInParent() < container->IndexInParent()) {
    return !!(mRoot = nullptr);
  }

  if (boundary->IndexInParent() > container->IndexInParent()) {
    ToTextPoint(container, &mEndContainer, &mEndOffset, false);
  }

  return true;
}

/**
 * Convert the given DOM point to a DOM point in non-generated contents.
 *
 * If aDOMPoint is in ::before, the result is immediately after it.
 * If aDOMPoint is in ::after, the result is immediately before it.
 */

static DOMPoint ClosestNotGeneratedDOMPoint(const DOMPoint& aDOMPoint,
                                            nsIContent* aElementContent) {
  MOZ_ASSERT(aDOMPoint.node, "The node must not be null");

  // ::before pseudo element
  if (aElementContent &&
      aElementContent->IsGeneratedContentContainerForBefore()) {
    MOZ_ASSERT(aElementContent->GetParent(),
               "::before must have parent element");
    // The first child of its parent (i.e., immediately after the ::before) is
    // good point for a DOM range.
    return DOMPoint(aElementContent->GetParent(), 0);
  }

  // ::after pseudo element
  if (aElementContent &&
      aElementContent->IsGeneratedContentContainerForAfter()) {
    MOZ_ASSERT(aElementContent->GetParent(),
               "::after must have parent element");
    // The end of its parent (i.e., immediately before the ::after) is good
    // point for a DOM range.
    return DOMPoint(aElementContent->GetParent(),
                    aElementContent->GetParent()->GetChildCount());
  }

  return aDOMPoint;
}

/**
 * GetElementAsContentOf() returns a content representing an element which is
 * or includes aNode.
 *
 * XXX This method is enough to retrieve ::before or ::after pseudo element.
 *     So, if you want to use this for other purpose, you might need to check
 *     ancestors too.
 */

static nsIContent* GetElementAsContentOf(nsINode* aNode) {
  if (auto* element = dom::Element::FromNode(aNode)) {
    return element;
  }
  return aNode->GetParentElement();
}

bool TextRange::AssignDOMRange(nsRange* aRange, bool* aReversed) const {
  MOZ_ASSERT(mRoot->IsLocal(), "Not supported for RemoteAccessible");
  bool reversed = EndPoint() < StartPoint();
  if (aReversed) {
    *aReversed = reversed;
  }

  HyperTextAccessible* startHyper = mStartContainer->AsLocal()->AsHyperText();
  HyperTextAccessible* endHyper = mEndContainer->AsLocal()->AsHyperText();
  DOMPoint startPoint = reversed ? endHyper->OffsetToDOMPoint(mEndOffset)
                                 : startHyper->OffsetToDOMPoint(mStartOffset);
  if (!startPoint.node) {
    return false;
  }

  // HyperTextAccessible manages pseudo elements generated by ::before or
  // ::after.  However, contents of them are not in the DOM tree normally.
  // Therefore, they are not selectable and editable.  So, when this creates
  // a DOM range, it should not start from nor end in any pseudo contents.

  nsIContent* container = GetElementAsContentOf(startPoint.node);
  DOMPoint startPointForDOMRange =
      ClosestNotGeneratedDOMPoint(startPoint, container);
  aRange->SetStart(startPointForDOMRange.node, startPointForDOMRange.idx);

  // If the caller wants collapsed range, let's collapse the range to its start.
  if (mEndContainer == mStartContainer && mEndOffset == mStartOffset) {
    aRange->Collapse(true);
    return true;
  }

  DOMPoint endPoint = reversed ? startHyper->OffsetToDOMPoint(mStartOffset)
                               : endHyper->OffsetToDOMPoint(mEndOffset);
  if (!endPoint.node) {
    return false;
  }

  if (startPoint.node != endPoint.node) {
    container = GetElementAsContentOf(endPoint.node);
  }

  DOMPoint endPointForDOMRange =
      ClosestNotGeneratedDOMPoint(endPoint, container);
  aRange->SetEnd(endPointForDOMRange.node, endPointForDOMRange.idx);
  return true;
}

void TextRange::TextRangesFromSelection(dom::Selection* aSelection,
                                        nsTArray<TextRange>* aRanges) {
  MOZ_ASSERT(aRanges->Length() == 0, "TextRange array supposed to be empty");

  aRanges->SetCapacity(aSelection->RangeCount());

  const uint32_t rangeCount = aSelection->RangeCount();
  for (const uint32_t idx : IntegerRange(rangeCount)) {
    MOZ_ASSERT(aSelection->RangeCount() == rangeCount);
    const nsRange* DOMRange = aSelection->GetRangeAt(idx);
    MOZ_ASSERT(DOMRange);
    HyperTextAccessible* startContainer =
        nsAccUtils::GetTextContainer(DOMRange->GetStartContainer());
    HyperTextAccessible* endContainer =
        nsAccUtils::GetTextContainer(DOMRange->GetEndContainer());
    HyperTextAccessible* commonAncestor = nsAccUtils::GetTextContainer(
        DOMRange->GetClosestCommonInclusiveAncestor());
    if (!startContainer || !endContainer) {
      continue;
    }

    int32_t startOffset = startContainer->DOMPointToOffset(
        DOMRange->GetStartContainer(), DOMRange->StartOffset(), false);
    int32_t endOffset = endContainer->DOMPointToOffset(
        DOMRange->GetEndContainer(), DOMRange->EndOffset(), true);

    TextRange tr(commonAncestor && commonAncestor->IsTextField()
                     ? commonAncestor
                     : startContainer->Document(),
                 startContainer, startOffset, endContainer, endOffset);
    *(aRanges->AppendElement()) = std::move(tr);
  }
}

////////////////////////////////////////////////////////////////////////////////
// pivate

void TextRange::Set(Accessible* aRoot, Accessible* aStartContainer,
                    int32_t aStartOffset, Accessible* aEndContainer,
                    int32_t aEndOffset) {
  mRoot = aRoot;
  mStartContainer = aStartContainer;
  mEndContainer = aEndContainer;
  mStartOffset = aStartOffset;
  mEndOffset = aEndOffset;
}

Accessible* TextRange::CommonParent(Accessible* aAcc1, Accessible* aAcc2,
                                    nsTArray<Accessible*>* aParents1,
                                    uint32_t* aPos1,
                                    nsTArray<Accessible*>* aParents2,
                                    uint32_t* aPos2) const {
  if (aAcc1 == aAcc2) {
    return aAcc1;
  }

  MOZ_ASSERT(aParents1->Length() == 0 || aParents2->Length() == 0,
             "Wrong arguments");

  // Build the chain of parents.
  Accessible* p1 = aAcc1;
  Accessible* p2 = aAcc2;
  do {
    aParents1->AppendElement(p1);
    p1 = p1->Parent();
  } while (p1);
  do {
    aParents2->AppendElement(p2);
    p2 = p2->Parent();
  } while (p2);

  // Find where the parent chain differs
  *aPos1 = aParents1->Length();
  *aPos2 = aParents2->Length();
  Accessible* parent = nullptr;
  uint32_t len = 0;
  for (len = std::min(*aPos1, *aPos2); len > 0; --len) {
    Accessible* child1 = aParents1->ElementAt(--(*aPos1));
    Accessible* child2 = aParents2->ElementAt(--(*aPos2));
    if (child1 != child2) break;

    parent = child1;
  }

  return parent;
}

}  // namespace a11y
}  // namespace mozilla

77%


¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.