void
EventQueue::CreateTextChangeEventFor(AccMutationEvent* aEvent)
{
    Accessible* container = aEvent->mAccessible->Parent();
    if (!container)
        return;

    HyperTextAccessible* textAccessible = container->AsHyperText();
    if (!textAccessible)
        return;

    // Don't fire event for the first html:br in an editor.
    if (aEvent->mAccessible->Role() == roles::WHITESPACE) {
        nsCOMPtr<nsIEditor> editor = textAccessible->GetEditor();
        if (editor) {
            bool isEmpty = false;
            editor->GetDocumentIsEmpty(&isEmpty);
            if (isEmpty)
                return;
        }
    }

    int32_t offset = textAccessible->GetChildOffset(aEvent->mAccessible);

    nsAutoString text;
    aEvent->mAccessible->AppendTextTo(text);
    if (text.IsEmpty())
        return;

    aEvent->mTextChangeEvent =
        new AccTextChangeEvent(textAccessible, offset, text, aEvent->IsShow(),
                               aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput);
}
HyperTextAccessible*
DocAccessibleChild::IdToHyperTextAccessible(const uint64_t& aID)
{
  Accessible* acc = IdToAccessible(aID);
  MOZ_ASSERT(!acc || acc->IsHyperText());
  return acc ? acc->AsHyperText() : nullptr;
}
HyperTextAccessible*
nsAccessiblePivot::SearchForText(Accessible* aAccessible, bool aBackward)
{
  Accessible* root = GetActiveRoot();
  Accessible* accessible = aAccessible;
  while (true) {
    Accessible* child = nullptr;

    while ((child = (aBackward ? accessible->LastChild() :
                                 accessible->FirstChild()))) {
      accessible = child;
      if (child->IsHyperText())
        return child->AsHyperText();
    }

    Accessible* sibling = nullptr;
    Accessible* temp = accessible;
    do {
      if (temp == root)
        break;

      if (temp != aAccessible && temp->IsHyperText())
        return temp->AsHyperText();

      sibling = aBackward ? temp->PrevSibling() : temp->NextSibling();

      if (sibling)
        break;
    } while ((temp = temp->Parent()));

    if (!sibling)
      break;

    accessible = sibling;
    if (accessible->IsHyperText())
      return accessible->AsHyperText();
  }

  return nullptr;
}
Beispiel #4
0
HyperTextAccessible*
nsAccUtils::GetTextContainer(nsINode* aNode)
{
  // Get text accessible containing the result node.
  DocAccessible* doc =
    GetAccService()->GetDocAccessible(aNode->OwnerDoc());
  Accessible* accessible =
    doc ? doc->GetAccessibleOrContainer(aNode) : nullptr;
  if (!accessible)
    return nullptr;

  do {
    HyperTextAccessible* textAcc = accessible->AsHyperText();
    if (textAcc)
      return textAcc;

    accessible = accessible->Parent();
  } while (accessible);

  return nullptr;
}
nsresult
HyperTextAccessibleWrap::HandleAccEvent(AccEvent* aEvent)
{
  uint32_t eventType = aEvent->GetEventType();

  if (eventType == nsIAccessibleEvent::EVENT_TEXT_REMOVED ||
      eventType == nsIAccessibleEvent::EVENT_TEXT_INSERTED) {
    Accessible* accessible = aEvent->GetAccessible();
    if (accessible && accessible->IsHyperText()) {
      AccTextChangeEvent* event = downcast_accEvent(aEvent);
        HyperTextAccessibleWrap* text =
          static_cast<HyperTextAccessibleWrap*>(accessible->AsHyperText());
      ia2AccessibleText::UpdateTextChangeData(text, event->IsTextInserted(),
                                              event->ModifiedText(),
                                              event->GetStartOffset(),
                                              event->GetLength());
    }
  }

  return HyperTextAccessible::HandleAccEvent(aEvent);
}
HyperTextAccessible*
nsAccUtils::GetTextAccessibleFromSelection(nsISelection* aSelection)
{
  // Get accessible from selection's focus DOM point (the DOM point where
  // selection is ended).

  nsCOMPtr<nsIDOMNode> focusDOMNode;
  aSelection->GetFocusNode(getter_AddRefs(focusDOMNode));
  if (!focusDOMNode)
    return nullptr;

  int32_t focusOffset = 0;
  aSelection->GetFocusOffset(&focusOffset);

  nsCOMPtr<nsINode> focusNode(do_QueryInterface(focusDOMNode));
  nsCOMPtr<nsINode> resultNode =
    nsCoreUtils::GetDOMNodeFromDOMPoint(focusNode, focusOffset);

  // Get text accessible containing the result node.
  DocAccessible* doc = 
    GetAccService()->GetDocAccessible(resultNode->OwnerDoc());
  Accessible* accessible = doc ? 
    doc->GetAccessibleOrContainer(resultNode) : nullptr;
  if (!accessible) {
    NS_NOTREACHED("No nsIAccessibleText for selection change event!");
    return nullptr;
  }

  do {
    HyperTextAccessible* textAcc = accessible->AsHyperText();
    if (textAcc)
      return textAcc;

    accessible = accessible->Parent();
  } while (accessible);

  NS_NOTREACHED("We must reach document accessible implementing nsIAccessibleText!");
  return nullptr;
}
NS_IMETHODIMP
nsAccessiblePivot::MovePreviousByText(TextBoundaryType aBoundary, bool* aResult)
{
  NS_ENSURE_ARG(aResult);

  *aResult = false;

  int32_t tempStart = mStartOffset, tempEnd = mEndOffset;
  Accessible* tempPosition = mPosition;
  Accessible* root = GetActiveRoot();
  while (true) {
    Accessible* curPosition = tempPosition;
    HyperTextAccessible* text;
    // Find the nearest text node using a reverse preorder traversal starting
    // from the current node.
    if (!(text = tempPosition->AsHyperText())) {
      text = SearchForText(tempPosition, true);
      if (!text)
        return NS_OK;
      if (text != curPosition)
        tempStart = tempEnd = -1;
      tempPosition = text;
    }

    // If the search led to the parent of the node we started on (e.g. when
    // starting on a text leaf), start the text movement from the end of that
    // node, otherwise we just default to 0.
    if (tempStart == -1) {
      if (tempPosition != curPosition)
        tempStart = text == curPosition->Parent() ?
                    text->GetChildOffset(curPosition) : text->CharacterCount();
      else
        tempStart = 0;
    }

    // If there's no more text on the current node, try to find the previous
    // text node; if there isn't one, bail out.
    if (tempStart == 0) {
      if (tempPosition == root)
        return NS_OK;

      // If we're currently sitting on a link, try move to either the previous
      // sibling or the parent, whichever is closer to the current end
      // offset. Otherwise, do a forward search for the next node to land on
      // (we don't do this in the first case because we don't want to go to the
      // subtree).
      Accessible* sibling = tempPosition->PrevSibling();
      if (tempPosition->IsLink()) {
        if (sibling && sibling->IsLink()) {
          HyperTextAccessible* siblingText = sibling->AsHyperText();
          tempStart = tempEnd = siblingText ?
                                siblingText->CharacterCount() : -1;
          tempPosition = sibling;
        } else {
          tempStart = tempPosition->StartOffset();
          tempEnd = tempPosition->EndOffset();
          tempPosition = tempPosition->Parent();
        }
      } else {
        HyperTextAccessible* tempText = SearchForText(tempPosition, true);
        if (!tempText)
          return NS_OK;
        tempPosition = tempText;
        tempStart = tempEnd = tempText->CharacterCount();
      }
      continue;
    }

    AccessibleTextBoundary startBoundary, endBoundary;
    switch (aBoundary) {
      case CHAR_BOUNDARY:
        startBoundary = nsIAccessibleText::BOUNDARY_CHAR;
        endBoundary = nsIAccessibleText::BOUNDARY_CHAR;
        break;
      case WORD_BOUNDARY:
        startBoundary = nsIAccessibleText::BOUNDARY_WORD_START;
        endBoundary = nsIAccessibleText::BOUNDARY_WORD_END;
        break;
      default:
        return NS_ERROR_INVALID_ARG;
    }

    nsAutoString unusedText;
    int32_t newStart = 0, newEnd = 0, currentStart = tempStart, potentialEnd = 0;
    text->TextBeforeOffset(tempStart, startBoundary, &newStart, &newEnd, unusedText);
    if (newStart < tempStart)
      tempStart = newEnd >= currentStart ? newStart : newEnd;
    else // XXX: In certain odd cases newStart is equal to tempStart
      text->TextBeforeOffset(tempStart - 1, startBoundary, &newStart,
                             &tempStart, unusedText);
    text->TextAtOffset(tempStart, endBoundary, &newStart, &potentialEnd,
                       unusedText);
    tempEnd = potentialEnd < tempEnd ? potentialEnd : currentStart;

    // The offset range we've obtained might have embedded characters in it,
    // limit the range to the start of the last occurrence of an embedded
    // character.
    Accessible* childAtOffset = nullptr;
    for (int32_t i = tempEnd - 1; i >= tempStart; i--) {
      childAtOffset = text->GetChildAtOffset(i);
      if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset)) {
        tempStart = childAtOffset->EndOffset();
        break;
      }
    }
    // If there's an embedded character at the very end of the range, we
    // instead want to traverse into it. So restart the movement with
    // the child as the starting point.
    if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset) &&
        tempEnd == static_cast<int32_t>(childAtOffset->EndOffset())) {
      tempPosition = childAtOffset;
      tempStart = tempEnd = childAtOffset->AsHyperText()->CharacterCount();
      continue;
    }

    *aResult = true;

    Accessible* startPosition = mPosition;
    int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
    mPosition = tempPosition;
    mStartOffset = tempStart;
    mEndOffset = tempEnd;

    NotifyOfPivotChange(startPosition, oldStart, oldEnd,
                        nsIAccessiblePivot::REASON_TEXT);
    return NS_OK;
  }
}
void
EventQueue::ProcessEventQueue()
{
    // Process only currently queued events.
    nsTArray<nsRefPtr<AccEvent> > events;
    events.SwapElements(mEvents);

    uint32_t eventCount = events.Length();
#ifdef A11Y_LOG
    if (eventCount > 0 && logging::IsEnabled(logging::eEvents)) {
        logging::MsgBegin("EVENTS", "events processing");
        logging::Address("document", mDocument);
        logging::MsgEnd();
    }
#endif

    for (uint32_t idx = 0; idx < eventCount; idx++) {
        AccEvent* event = events[idx];
        if (event->mEventRule != AccEvent::eDoNotEmit) {
            Accessible* target = event->GetAccessible();
            if (!target || target->IsDefunct())
                continue;

            // Dispatch the focus event if target is still focused.
            if (event->mEventType == nsIAccessibleEvent::EVENT_FOCUS) {
                FocusMgr()->ProcessFocusEvent(event);
                continue;
            }

            // Dispatch caret moved and text selection change events.
            if (event->mEventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED) {
                AccCaretMoveEvent* caretMoveEvent = downcast_accEvent(event);
                HyperTextAccessible* hyperText = target->AsHyperText();
                if (hyperText &&
                        NS_SUCCEEDED(hyperText->GetCaretOffset(&caretMoveEvent->mCaretOffset))) {

                    nsEventShell::FireEvent(caretMoveEvent);

                    // There's a selection so fire selection change as well.
                    int32_t selectionCount;
                    hyperText->GetSelectionCount(&selectionCount);
                    if (selectionCount)
                        nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED,
                                                hyperText);
                }
                continue;
            }

            // Fire selected state change events in support to selection events.
            if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_ADD) {
                nsEventShell::FireEvent(event->mAccessible, states::SELECTED,
                                        true, event->mIsFromUserInput);

            } else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_REMOVE) {
                nsEventShell::FireEvent(event->mAccessible, states::SELECTED,
                                        false, event->mIsFromUserInput);

            } else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION) {
                AccSelChangeEvent* selChangeEvent = downcast_accEvent(event);
                nsEventShell::FireEvent(event->mAccessible, states::SELECTED,
                                        (selChangeEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd),
                                        event->mIsFromUserInput);

                if (selChangeEvent->mPackedEvent) {
                    nsEventShell::FireEvent(selChangeEvent->mPackedEvent->mAccessible,
                                            states::SELECTED,
                                            (selChangeEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd),
                                            selChangeEvent->mPackedEvent->mIsFromUserInput);
                }
            }

            nsEventShell::FireEvent(event);

            // Fire text change events.
            AccMutationEvent* mutationEvent = downcast_accEvent(event);
            if (mutationEvent) {
                if (mutationEvent->mTextChangeEvent)
                    nsEventShell::FireEvent(mutationEvent->mTextChangeEvent);
            }
        }

        if (event->mEventType == nsIAccessibleEvent::EVENT_HIDE)
            mDocument->ShutdownChildrenInSubtree(event->mAccessible);

        if (!mDocument)
            return;
    }
}