Exemple #1
0
void
LinkableAccessible::BindToParent(Accessible* aParent,
                                 uint32_t aIndexInParent)
{
    AccessibleWrap::BindToParent(aParent, aIndexInParent);

    // Cache action content.
    mActionAcc = nullptr;
    mIsLink = false;
    mIsOnclick = false;

    if (nsCoreUtils::HasClickListener(mContent)) {
        mIsOnclick = true;
        return;
    }

    // XXX: The logic looks broken since the click listener may be registered
    // on non accessible node in parent chain but this node is skipped when tree
    // is traversed.
    Accessible* walkUpAcc = this;
    while ((walkUpAcc = walkUpAcc->Parent()) && !walkUpAcc->IsDoc()) {
        if (walkUpAcc->LinkState() & states::LINKED) {
            mIsLink = true;
            mActionAcc = walkUpAcc;
            return;
        }

        if (nsCoreUtils::HasClickListener(walkUpAcc->GetContent())) {
            mActionAcc = walkUpAcc;
            mIsOnclick = true;
            return;
        }
    }
}
STDMETHODIMP
ia2Accessible::get_accessibleWithCaret(IUnknown** aAccessible,
                                       long* aCaretOffset)
{
  if (!aAccessible || !aCaretOffset)
    return E_INVALIDARG;

  *aAccessible = nullptr;
  *aCaretOffset = -1;

  AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
  if (acc->IsDefunct())
    return CO_E_OBJNOTCONNECTED;

  int32_t caretOffset = -1;
  Accessible* accWithCaret = SelectionMgr()->AccessibleWithCaret(&caretOffset);
  if (!accWithCaret || acc->Document() != accWithCaret->Document())
    return S_FALSE;

  Accessible* child = accWithCaret;
  while (!child->IsDoc() && child != acc)
    child = child->Parent();

  if (child != acc)
    return S_FALSE;

  *aAccessible =  static_cast<IAccessible2*>(
    static_cast<AccessibleWrap*>(accWithCaret));
  (*aAccessible)->AddRef();
  *aCaretOffset = caretOffset;
  return S_OK;
}
Exemple #3
0
Accessible* HTMLLabelIterator::Next() {
  // Get either <label for="[id]"> element which explicitly points to given
  // element, or <label> ancestor which implicitly point to it.
  Accessible* label = nullptr;
  while ((label = mRelIter.Next())) {
    if (IsLabel(label)) {
      return label;
    }
  }

  // Ignore ancestor label on not widget accessible.
  if (mLabelFilter == eSkipAncestorLabel || !mAcc->IsWidget()) return nullptr;

  // Go up tree to get a name of ancestor label if there is one (an ancestor
  // <label> implicitly points to us). Don't go up farther than form or
  // document.
  Accessible* walkUp = mAcc->Parent();
  while (walkUp && !walkUp->IsDoc()) {
    nsIContent* walkUpEl = walkUp->GetContent();
    if (IsLabel(walkUp) &&
        !walkUpEl->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::_for)) {
      mLabelFilter = eSkipAncestorLabel;  // prevent infinite loop
      return walkUp;
    }

    if (walkUpEl->IsHTMLElement(nsGkAtoms::form)) break;

    walkUp = walkUp->Parent();
  }

  return nullptr;
}
void DocAccessibleChildBase::ShowEvent(AccShowEvent* aShowEvent) {
  Accessible* parent = aShowEvent->Parent();
  uint64_t parentID =
      parent->IsDoc() ? 0 : reinterpret_cast<uint64_t>(parent->UniqueID());
  uint32_t idxInParent = aShowEvent->GetAccessible()->IndexInParent();
  nsTArray<AccessibleData> shownTree;
  ShowEventData data(parentID, idxInParent, shownTree, false);
  SerializeTree(aShowEvent->GetAccessible(), data.NewTree());
  MaybeSendShowEvent(data, aShowEvent->IsFromUserInput());
}
xpcAccessibleGeneric::~xpcAccessibleGeneric()
{
  if (mIntl.IsNull()) {
    return;
  }

  xpcAccessibleDocument* xpcDoc = nullptr;
  if (mIntl.IsAccessible()) {
    Accessible* acc = mIntl.AsAccessible();
    if (!acc->IsDoc() && !acc->IsApplication()) {
      xpcDoc = GetAccService()->GetXPCDocument(acc->Document());
      xpcDoc->NotifyOfShutdown(acc);
    }
  } else {
    ProxyAccessible* proxy = mIntl.AsProxy();
    if (!proxy->IsDoc()) {
      xpcDoc = GetAccService()->GetXPCDocument(proxy->Document());
      xpcDoc->NotifyOfShutdown(proxy);
    }
  }
}
Exemple #6
0
void DocAccessibleWrap::UpdateFocusPathBounds() {
  if (!mFocusPath.Count()) {
    return;
  }

  if (IPCAccessibilityActive()) {
    DocAccessibleChild* ipcDoc = IPCDoc();
    nsTArray<BatchData> boundsData(mFocusPath.Count());
    for (auto iter = mFocusPath.Iter(); !iter.Done(); iter.Next()) {
      Accessible* accessible = iter.Data();
      if (!accessible || accessible->IsDefunct()) {
        MOZ_ASSERT_UNREACHABLE("Focus path cached accessible is gone.");
        continue;
      }

      auto uid = accessible->IsDoc() && accessible->AsDoc()->IPCDoc()
                     ? 0
                     : reinterpret_cast<uint64_t>(accessible->UniqueID());
      boundsData.AppendElement(BatchData(
          accessible->Document()->IPCDoc(), uid, 0, accessible->Bounds(), 0,
          nsString(), nsString(), nsString(), UnspecifiedNaN<double>(),
          UnspecifiedNaN<double>(), UnspecifiedNaN<double>(),
          UnspecifiedNaN<double>(), nsTArray<Attribute>()));
    }

    ipcDoc->SendBatch(eBatch_BoundsUpdate, boundsData);
  } else if (SessionAccessibility* sessionAcc =
                 SessionAccessibility::GetInstanceFor(this)) {
    nsTArray<AccessibleWrap*> accessibles(mFocusPath.Count());
    for (auto iter = mFocusPath.Iter(); !iter.Done(); iter.Next()) {
      accessibles.AppendElement(
          static_cast<AccessibleWrap*>(iter.Data().get()));
    }

    sessionAcc->UpdateCachedBounds(accessibles);
  }
}
Exemple #7
0
EventTree*
EventTree::FindOrInsert(Accessible* aContainer)
{
  if (!mFirst) {
    mFirst.reset(new EventTree(aContainer, true));
    return mFirst.get();
  }

  EventTree* prevNode = nullptr;
  EventTree* node = mFirst.get();
  do {
    MOZ_ASSERT(!node->mContainer->IsApplication(),
               "No event for application accessible is expected here");
    MOZ_ASSERT(!node->mContainer->IsDefunct(), "An event target has to be alive");

    // Case of same target.
    if (node->mContainer == aContainer) {
      return node;
    }

    // Check if the given container is contained by a current node
    Accessible* top = mContainer ? mContainer : aContainer->Document();
    Accessible* parent = aContainer;
    while (parent) {
      // Reached a top, no match for a current event.
      if (parent == top) {
        break;
      }

      // We got a match.
      if (parent->Parent() == node->mContainer) {
        return node->FindOrInsert(aContainer);
      }

      parent = parent->Parent();
      MOZ_ASSERT(parent, "Wrong tree");
    }

    // If the given container contains a current node
    // then
    //   if show or hide of the given node contains a grand parent of the current node
    //   then ignore the current node and its show and hide events
    //   otherwise ignore the current node, but not its show and hide events
    Accessible* curParent = node->mContainer;
    while (curParent && !curParent->IsDoc()) {
      if (curParent->Parent() != aContainer) {
        curParent = curParent->Parent();
        continue;
      }

      // Insert the tail node into the hierarchy between the current node and
      // its parent.
      node->mFireReorder = false;
      UniquePtr<EventTree>& nodeOwnerRef = prevNode ? prevNode->mNext : mFirst;
      UniquePtr<EventTree> newNode(new EventTree(aContainer, mDependentEvents.IsEmpty()));
      newNode->mFirst = Move(nodeOwnerRef);
      nodeOwnerRef = Move(newNode);
      nodeOwnerRef->mNext = Move(node->mNext);

      // Check if a next node is contained by the given node too, and move them
      // under the given node if so.
      prevNode = nodeOwnerRef.get();
      node = nodeOwnerRef->mNext.get();
      UniquePtr<EventTree>* nodeRef = &nodeOwnerRef->mNext;
      EventTree* insNode = nodeOwnerRef->mFirst.get();
      while (node) {
        Accessible* curParent = node->mContainer;
        while (curParent && !curParent->IsDoc()) {
          if (curParent->Parent() != aContainer) {
            curParent = curParent->Parent();
            continue;
          }

          MOZ_ASSERT(!insNode->mNext);

          node->mFireReorder = false;
          insNode->mNext = Move(*nodeRef);
          insNode = insNode->mNext.get();

          prevNode->mNext = Move(node->mNext);
          node = prevNode;
          break;
        }

        prevNode = node;
        nodeRef = &node->mNext;
        node = node->mNext.get();
      }

      return nodeOwnerRef.get();
    }

    prevNode = node;
  } while ((node = node->mNext.get()));

  MOZ_ASSERT(prevNode, "Nowhere to insert");
  MOZ_ASSERT(!prevNode->mNext, "Taken by another node");

  // If 'this' node contains the given container accessible, then
  //   do not emit a reorder event for the container
  //   if a dependent show event target contains the given container then do not
  //   emit show / hide events (see Process() method)

  prevNode->mNext.reset(new EventTree(aContainer, mDependentEvents.IsEmpty()));
  return prevNode->mNext.get();
}
EventTree*
EventTree::FindOrInsert(Accessible* aContainer)
{
  if (!mFirst) {
    mFirst.reset(new EventTree(aContainer, mDependentEvents.IsEmpty()));
    return mFirst.get();
  }

  EventTree* prevNode = nullptr;
  EventTree* node = mFirst.get();
  do {
    MOZ_ASSERT(!node->mContainer->IsApplication(),
               "No event for application accessible is expected here");
    MOZ_ASSERT(!node->mContainer->IsDefunct(), "An event target has to be alive");

    // Case of same target.
    if (node->mContainer == aContainer) {
      return node;
    }

    // Check if the given container is contained by a current node
    Accessible* top = mContainer ? mContainer : aContainer->Document();
    Accessible* parent = aContainer;
    while (parent) {
      // Reached a top, no match for a current event.
      if (parent == top) {
        break;
      }

      // We got a match.
      if (parent->Parent() == node->mContainer) {
        // Reject the node if it's contained by a show/hide event target
        uint32_t evCount = node->mDependentEvents.Length();
        for (uint32_t idx = 0; idx < evCount; idx++) {
          AccMutationEvent* ev = node->mDependentEvents[idx];
          if (ev->GetAccessible() == parent) {
#ifdef A11Y_LOG
            if (logging::IsEnabled(logging::eEventTree)) {
              logging::MsgBegin("EVENTS_TREE",
                "Rejecting node contained by show/hide");
              logging::AccessibleInfo("Node", aContainer);
              logging::MsgEnd();
            }
#endif
            // If the node is rejected, then check if it has related hide event
            // on stack, and if so, then connect it to the parent show event.
            if (ev->IsShow()) {
              AccShowEvent* showEv = downcast_accEvent(ev);
              Controller(aContainer)->
                WithdrawPrecedingEvents(&showEv->mPrecedingEvents);
            }
            return nullptr;
          }
        }

        return node->FindOrInsert(aContainer);
      }

      parent = parent->Parent();
      MOZ_ASSERT(parent, "Wrong tree");
    }

    // If the given container contains a current node
    // then
    //   if show or hide of the given node contains a grand parent of the current node
    //   then ignore the current node and its show and hide events
    //   otherwise ignore the current node, but not its show and hide events
    Accessible* curParent = node->mContainer;
    while (curParent && !curParent->IsDoc()) {
      if (curParent->Parent() != aContainer) {
        curParent = curParent->Parent();
        continue;
      }

      // Insert the tail node into the hierarchy between the current node and
      // its parent.
      node->mFireReorder = false;
      UniquePtr<EventTree>& nodeOwnerRef = prevNode ? prevNode->mNext : mFirst;
      UniquePtr<EventTree> newNode(new EventTree(aContainer, mDependentEvents.IsEmpty()));
      newNode->mFirst = Move(nodeOwnerRef);
      nodeOwnerRef = Move(newNode);
      nodeOwnerRef->mNext = Move(node->mNext);

      // Check if a next node is contained by the given node too, and move them
      // under the given node if so.
      prevNode = nodeOwnerRef.get();
      node = nodeOwnerRef->mNext.get();
      UniquePtr<EventTree>* nodeRef = &nodeOwnerRef->mNext;
      EventTree* insNode = nodeOwnerRef->mFirst.get();
      while (node) {
        Accessible* curParent = node->mContainer;
        while (curParent && !curParent->IsDoc()) {
          if (curParent->Parent() != aContainer) {
            curParent = curParent->Parent();
            continue;
          }

          MOZ_ASSERT(!insNode->mNext);

          node->mFireReorder = false;
          insNode->mNext = Move(*nodeRef);
          insNode = insNode->mNext.get();

          prevNode->mNext = Move(node->mNext);
          node = prevNode;
          break;
        }

        prevNode = node;
        nodeRef = &node->mNext;
        node = node->mNext.get();
      }

      return nodeOwnerRef.get();
    }

    prevNode = node;
  } while ((node = node->mNext.get()));

  MOZ_ASSERT(prevNode, "Nowhere to insert");
  MOZ_ASSERT(!prevNode->mNext, "Taken by another node");

  // If 'this' node contains the given container accessible, then
  //   do not emit a reorder event for the container
  //   if a dependent show event target contains the given container then do not
  //   emit show / hide events (see Process() method)

  prevNode->mNext.reset(new EventTree(aContainer, mDependentEvents.IsEmpty()));
  return prevNode->mNext.get();
}
Exemple #9
0
void DocAccessibleWrap::CacheViewportCallback(nsITimer* aTimer,
                                              void* aDocAccParam) {
  RefPtr<DocAccessibleWrap> docAcc(
      dont_AddRef(reinterpret_cast<DocAccessibleWrap*>(aDocAccParam)));
  if (!docAcc) {
    return;
  }

  nsIPresShell* presShell = docAcc->PresShell();
  if (!presShell) {
    return;
  }
  nsIFrame* rootFrame = presShell->GetRootFrame();
  if (!rootFrame) {
    return;
  }

  nsTArray<nsIFrame*> frames;
  nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
  nsRect scrollPort = sf ? sf->GetScrollPortRect() : rootFrame->GetRect();

  nsLayoutUtils::GetFramesForArea(
      presShell->GetRootFrame(), scrollPort, frames,
      nsLayoutUtils::FrameForPointFlags::ONLY_VISIBLE);
  AccessibleHashtable inViewAccs;
  for (size_t i = 0; i < frames.Length(); i++) {
    nsIContent* content = frames.ElementAt(i)->GetContent();
    if (!content) {
      continue;
    }

    Accessible* visibleAcc = docAcc->GetAccessibleOrContainer(content);
    if (!visibleAcc) {
      continue;
    }

    for (Accessible* acc = visibleAcc; acc && acc != docAcc->Parent();
         acc = acc->Parent()) {
      if (inViewAccs.Contains(acc->UniqueID())) {
        break;
      }
      inViewAccs.Put(acc->UniqueID(), acc);
    }
  }

  if (IPCAccessibilityActive()) {
    DocAccessibleChild* ipcDoc = docAcc->IPCDoc();
    nsTArray<BatchData> cacheData(inViewAccs.Count());
    for (auto iter = inViewAccs.Iter(); !iter.Done(); iter.Next()) {
      Accessible* accessible = iter.Data();
      auto uid = accessible->IsDoc() && accessible->AsDoc()->IPCDoc()
                     ? 0
                     : reinterpret_cast<uint64_t>(accessible->UniqueID());
      cacheData.AppendElement(
          BatchData(accessible->Document()->IPCDoc(), uid, accessible->State(),
                    accessible->Bounds(), accessible->ActionCount(), nsString(),
                    nsString(), nsString(), UnspecifiedNaN<double>(),
                    UnspecifiedNaN<double>(), UnspecifiedNaN<double>(),
                    UnspecifiedNaN<double>(), nsTArray<Attribute>()));
    }

    ipcDoc->SendBatch(eBatch_Viewport, cacheData);
  } else if (SessionAccessibility* sessionAcc =
                 SessionAccessibility::GetInstanceFor(docAcc)) {
    nsTArray<AccessibleWrap*> accessibles(inViewAccs.Count());
    for (auto iter = inViewAccs.Iter(); !iter.Done(); iter.Next()) {
      accessibles.AppendElement(
          static_cast<AccessibleWrap*>(iter.Data().get()));
    }

    sessionAcc->ReplaceViewportCache(accessibles);
  }

  if (docAcc->mCacheRefreshTimer) {
    docAcc->mCacheRefreshTimer = nullptr;
  }
}
Exemple #10
0
TreeMutation::TreeMutation(Accessible* aParent, bool aNoEvents) :
  mParent(aParent), mStartIdx(UINT32_MAX),
  mStateFlagsCopy(mParent->mStateFlags),
  mEventTree(aNoEvents ? kNoEventTree : nullptr)
{
#ifdef DEBUG
  mIsDone = false;
#endif

#ifdef A11Y_LOG
  if (mEventTree != kNoEventTree && logging::IsEnabled(logging::eEventTree)) {
    logging::MsgBegin("EVENTS_TREE", "reordering tree before");
    logging::AccessibleInfo("reordering for", mParent);
    Controller()->RootEventTree().Log();
    logging::MsgEnd();

    logging::MsgBegin("EVENTS_TREE", "Container tree");
    if (logging::IsEnabled(logging::eVerbose)) {
      nsAutoString level;
      Accessible* root = mParent->Document();
      do {
        const char* prefix = "";
        if (mParent == root) {
          prefix = "_X_";
        }
        else {
          const EventTree& ret = Controller()->RootEventTree();
          if (ret.Find(root)) {
            prefix = "_с_";
          }
        }

        printf("%s", NS_ConvertUTF16toUTF8(level).get());
        logging::AccessibleInfo(prefix, root);
        if (root->FirstChild() && !root->FirstChild()->IsDoc()) {
          level.Append(NS_LITERAL_STRING("  "));
          root = root->FirstChild();
          continue;
        }
        int32_t idxInParent = root->mParent ?
          root->mParent->mChildren.IndexOf(root) : -1;
        if (idxInParent != -1 &&
            idxInParent < static_cast<int32_t>(root->mParent->mChildren.Length() - 1)) {
          root = root->mParent->mChildren.ElementAt(idxInParent + 1);
          continue;
        }

        while ((root = root->Parent()) && !root->IsDoc()) {
          level.Cut(0, 2);

          int32_t idxInParent = root->mParent ?
          root->mParent->mChildren.IndexOf(root) : -1;
          if (idxInParent != -1 &&
              idxInParent < static_cast<int32_t>(root->mParent->mChildren.Length() - 1)) {
            root = root->mParent->mChildren.ElementAt(idxInParent + 1);
            break;
          }
        }
      }
      while (root && !root->IsDoc());
    }
    logging::MsgEnd();
  }
#endif

  mParent->mStateFlags |= Accessible::eKidsMutating;
}