NS_IMETHODIMP
nsAccessiblePivot::MovePrevious(nsIAccessibleTraversalRule* aRule,
                                nsIAccessible* aAnchor,
                                bool aIncludeStart, bool aIsFromUserInput,
                                uint8_t aArgc, bool* aResult)
{
  NS_ENSURE_ARG(aResult);
  NS_ENSURE_ARG(aRule);
  *aResult = false;

  Accessible* anchor = mPosition;
  if (aArgc > 0 && aAnchor)
    anchor = aAnchor->ToInternalAccessible();

  if (anchor && (anchor->IsDefunct() || !IsDescendantOf(anchor, GetActiveRoot())))
    return NS_ERROR_NOT_IN_TREE;

  nsresult rv = NS_OK;
  Accessible* accessible =
    SearchBackward(anchor, aRule, (aArgc > 1) ? aIncludeStart : false, &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  if (accessible)
    *aResult = MovePivotInternal(accessible, nsIAccessiblePivot::REASON_PREV,
                                 (aArgc > 2) ? aIsFromUserInput : true);

  return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::MoveLast(nsIAccessibleTraversalRule* aRule,
                            bool* aResult)
{
  NS_ENSURE_ARG(aResult);
  NS_ENSURE_ARG(aRule);

  Accessible* root = GetActiveRoot();
  NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);

  *aResult = false;
  nsresult rv = NS_OK;
  Accessible* lastAccessible = root;
  Accessible* accessible = nullptr;

  // First go to the last accessible in pre-order
  while (lastAccessible->HasChildren())
    lastAccessible = lastAccessible->LastChild();

  // Search backwards from last accessible and find the last occurrence in the doc
  accessible = SearchBackward(lastAccessible, aRule, true, &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  if (accessible)
    *aResult = MovePivotInternal(accessible, nsAccessiblePivot::REASON_LAST);

  return NS_OK;
}
STDMETHODIMP
ia2AccessibleHyperlink::get_anchor(long aIndex, VARIANT* aAnchor)
{
  A11Y_TRYBLOCK_BEGIN

  VariantInit(aAnchor);

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

  if (aIndex < 0 || aIndex >= static_cast<long>(thisObj->AnchorCount()))
    return E_INVALIDARG;

  if (!thisObj->IsLink())
    return S_FALSE;

  AccessibleWrap* anchor =
    static_cast<AccessibleWrap*>(thisObj->AnchorAt(aIndex));
  if (!anchor)
    return S_FALSE;

  void* instancePtr = nullptr;
  HRESULT result = anchor->QueryInterface(IID_IUnknown, &instancePtr);
  if (FAILED(result))
    return result;

  IUnknown* unknownPtr = static_cast<IUnknown*>(instancePtr);
  aAnchor->ppunkVal = &unknownPtr;
  aAnchor->vt = VT_UNKNOWN;
  return S_OK;

  A11Y_TRYBLOCK_END
}
Beispiel #4
0
NS_IMETHODIMP
nsAccessiblePivot::MoveToPoint(nsIAccessibleTraversalRule* aRule,
                               int32_t aX, int32_t aY, bool aIgnoreNoMatch,
                               bool aIsFromUserInput, uint8_t aArgc,
                               bool* aResult)
{
  NS_ENSURE_ARG_POINTER(aResult);
  NS_ENSURE_ARG_POINTER(aRule);

  *aResult = false;

  Accessible* root = GetActiveRoot();
  NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);

  RuleCache cache(aRule);
  Accessible* match = nullptr;
  Accessible* child = root->ChildAtPoint(aX, aY, Accessible::eDeepestChild);
  while (child && root != child) {
    uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
    nsresult rv = cache.ApplyFilter(child, &filtered);
    NS_ENSURE_SUCCESS(rv, rv);

    // Ignore any matching nodes that were below this one
    if (filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE)
      match = nullptr;

    // Match if no node below this is a match
    if ((filtered & nsIAccessibleTraversalRule::FILTER_MATCH) && !match) {
      int32_t childX, childY, childWidth, childHeight;
      child->GetBounds(&childX, &childY, &childWidth, &childHeight);
      // Double-check child's bounds since the deepest child may have been out
      // of bounds. This assures we don't return a false positive.
      if (aX >= childX && aX < childX + childWidth &&
          aY >= childY && aY < childY + childHeight)
        match = child;
    }

    child = child->Parent();
  }

  if (match || !aIgnoreNoMatch)
    *aResult = MovePivotInternal(match, nsIAccessiblePivot::REASON_POINT,
                                 (aArgc > 0) ? aIsFromUserInput : true);

  return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::MoveFirst(nsIAccessibleTraversalRule* aRule, bool* aResult)
{
  NS_ENSURE_ARG(aResult);
  NS_ENSURE_ARG(aRule);

  Accessible* root = GetActiveRoot();
  NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);

  nsresult rv = NS_OK;
  Accessible* accessible = SearchForward(root, aRule, true, &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  if (accessible)
    *aResult = MovePivotInternal(accessible, nsIAccessiblePivot::REASON_FIRST);

  return NS_OK;
}
STDMETHODIMP
ia2AccessibleHyperlink::get_valid(boolean* aValid)
{
  A11Y_TRYBLOCK_BEGIN

  *aValid = false;

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

  if (!thisObj->IsLink())
    return S_FALSE;

  *aValid = thisObj->IsLinkValid();
  return S_OK;

  A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleHyperlink::get_endIndex(long* aIndex)
{
  A11Y_TRYBLOCK_BEGIN

  *aIndex = 0;

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

  if (!thisObj->IsLink())
    return S_FALSE;

  *aIndex = thisObj->EndOffset();
  return S_OK;

  A11Y_TRYBLOCK_END
}
STDMETHODIMP
ia2AccessibleHyperlink::get_anchorTarget(long aIndex, VARIANT* aAnchorTarget)
{
  A11Y_TRYBLOCK_BEGIN

  VariantInit(aAnchorTarget);

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

  if (aIndex < 0 || aIndex >= static_cast<long>(thisObj->AnchorCount()))
    return E_INVALIDARG;

  if (!thisObj->IsLink())
    return S_FALSE;

  nsCOMPtr<nsIURI> uri = thisObj->AnchorURIAt(aIndex);
  if (!uri)
    return S_FALSE;

  nsAutoCString prePath;
  nsresult rv = uri->GetPrePath(prePath);
  if (NS_FAILED(rv))
    return GetHRESULT(rv);

  nsAutoCString path;
  rv = uri->GetPath(path);
  if (NS_FAILED(rv))
    return GetHRESULT(rv);

  nsAutoString stringURI;
  AppendUTF8toUTF16(prePath, stringURI);
  AppendUTF8toUTF16(path, stringURI);

  aAnchorTarget->vt = VT_BSTR;
  aAnchorTarget->bstrVal = ::SysAllocStringLen(stringURI.get(),
                                               stringURI.Length());
  return aAnchorTarget->bstrVal ? S_OK : E_OUTOFMEMORY;

  A11Y_TRYBLOCK_END
}
Beispiel #9
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);
  }
}
Beispiel #10
0
nsresult
AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
{
  nsresult rv = Accessible::HandleAccEvent(aEvent);
  NS_ENSURE_SUCCESS(rv, rv);

    Accessible* accessible = aEvent->GetAccessible();
    NS_ENSURE_TRUE(accessible, NS_ERROR_FAILURE);

    // The accessible can become defunct if we have an xpcom event listener
    // which decides it would be fun to change the DOM and flush layout.
    if (accessible->IsDefunct())
        return NS_OK;

    uint32_t type = aEvent->GetEventType();

    AtkObject* atkObj = AccessibleWrap::GetAtkObject(accessible);

  // We don't create ATK objects for plain text leaves, just return NS_OK in
  // such case.
    if (!atkObj) {
        NS_ASSERTION(type == nsIAccessibleEvent::EVENT_SHOW ||
                     type == nsIAccessibleEvent::EVENT_HIDE,
                     "Event other than SHOW and HIDE fired for plain text leaves");
        return NS_OK;
    }

    AccessibleWrap* accWrap = GetAccessibleWrap(atkObj);
    if (!accWrap) {
        return NS_OK; // Node is shut down
    }

    switch (type) {
    case nsIAccessibleEvent::EVENT_STATE_CHANGE:
      {
        AccStateChangeEvent* event = downcast_accEvent(aEvent);
        MAI_ATK_OBJECT(atkObj)->FireStateChangeEvent(event->GetState(),
                                                     event->IsStateEnabled());
        break;
      }

    case nsIAccessibleEvent::EVENT_TEXT_REMOVED:
    case nsIAccessibleEvent::EVENT_TEXT_INSERTED:
        return FireAtkTextChangedEvent(aEvent, atkObj);

    case nsIAccessibleEvent::EVENT_FOCUS:
      {
        a11y::RootAccessible* rootAccWrap = accWrap->RootAccessible();
        if (rootAccWrap && rootAccWrap->mActivated) {
            atk_focus_tracker_notify(atkObj);
            // Fire state change event for focus
            atk_object_notify_state_change(atkObj, ATK_STATE_FOCUSED, true);
            return NS_OK;
        }
      } break;

    case nsIAccessibleEvent::EVENT_NAME_CHANGE:
      {
        nsAutoString newName;
        accessible->Name(newName);

        MaybeFireNameChange(atkObj, newName);

        break;
      }

  case nsIAccessibleEvent::EVENT_VALUE_CHANGE:
    if (accessible->HasNumericValue()) {
      // Make sure this is a numeric value. Don't fire for string value changes
      // (e.g. text editing) ATK values are always numeric.
      g_object_notify((GObject*)atkObj, "accessible-value");
    }
    break;

    case nsIAccessibleEvent::EVENT_SELECTION:
    case nsIAccessibleEvent::EVENT_SELECTION_ADD:
    case nsIAccessibleEvent::EVENT_SELECTION_REMOVE:
    {
      // XXX: dupe events may be fired
      AccSelChangeEvent* selChangeEvent = downcast_accEvent(aEvent);
      g_signal_emit_by_name(AccessibleWrap::GetAtkObject(selChangeEvent->Widget()),
                            "selection_changed");
      break;
    }

    case nsIAccessibleEvent::EVENT_SELECTION_WITHIN:
    {
      g_signal_emit_by_name(atkObj, "selection_changed");
      break;
    }

    case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED:
        g_signal_emit_by_name(atkObj, "text_selection_changed");
        break;

    case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED:
      {
        AccCaretMoveEvent* caretMoveEvent = downcast_accEvent(aEvent);
        NS_ASSERTION(caretMoveEvent, "Event needs event data");
        if (!caretMoveEvent)
            break;

        int32_t caretOffset = caretMoveEvent->GetCaretOffset();
        g_signal_emit_by_name(atkObj, "text_caret_moved", caretOffset);
      } break;

    case nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED:
        g_signal_emit_by_name(atkObj, "text-attributes-changed");
        break;

    case nsIAccessibleEvent::EVENT_TABLE_MODEL_CHANGED:
        g_signal_emit_by_name(atkObj, "model_changed");
        break;

    case nsIAccessibleEvent::EVENT_TABLE_ROW_INSERT:
      {
        AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent);
        NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE);

        int32_t rowIndex = tableEvent->GetIndex();
        int32_t numRows = tableEvent->GetCount();

        g_signal_emit_by_name(atkObj, "row_inserted", rowIndex, numRows);
     } break;

   case nsIAccessibleEvent::EVENT_TABLE_ROW_DELETE:
     {
        AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent);
        NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE);

        int32_t rowIndex = tableEvent->GetIndex();
        int32_t numRows = tableEvent->GetCount();

        g_signal_emit_by_name(atkObj, "row_deleted", rowIndex, numRows);
      } break;

    case nsIAccessibleEvent::EVENT_TABLE_ROW_REORDER:
      {
        g_signal_emit_by_name(atkObj, "row_reordered");
        break;
      }

    case nsIAccessibleEvent::EVENT_TABLE_COLUMN_INSERT:
      {
        AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent);
        NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE);

        int32_t colIndex = tableEvent->GetIndex();
        int32_t numCols = tableEvent->GetCount();
        g_signal_emit_by_name(atkObj, "column_inserted", colIndex, numCols);
      } break;

    case nsIAccessibleEvent::EVENT_TABLE_COLUMN_DELETE:
      {
        AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent);
        NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE);

        int32_t colIndex = tableEvent->GetIndex();
        int32_t numCols = tableEvent->GetCount();
        g_signal_emit_by_name(atkObj, "column_deleted", colIndex, numCols);
      } break;

    case nsIAccessibleEvent::EVENT_TABLE_COLUMN_REORDER:
        g_signal_emit_by_name(atkObj, "column_reordered");
        break;

    case nsIAccessibleEvent::EVENT_SECTION_CHANGED:
        g_signal_emit_by_name(atkObj, "visible_data_changed");
        break;

    case nsIAccessibleEvent::EVENT_SHOW:
        return FireAtkShowHideEvent(aEvent, atkObj, true);

    case nsIAccessibleEvent::EVENT_HIDE:
        // XXX - Handle native dialog accessibles.
        if (!accessible->IsRoot() && accessible->HasARIARole() &&
            accessible->ARIARole() == roles::DIALOG) {
          guint id = g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT);
          g_signal_emit(atkObj, id, 0);
        }
        return FireAtkShowHideEvent(aEvent, atkObj, false);

        /*
         * Because dealing with menu is very different between nsIAccessible
         * and ATK, and the menu activity is important, specially transfer the
         * following two event.
         * Need more verification by AT test.
         */
    case nsIAccessibleEvent::EVENT_MENU_START:
    case nsIAccessibleEvent::EVENT_MENU_END:
        break;

    case nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE:
      {
        accessible->AsRoot()->mActivated = true;
        guint id = g_signal_lookup("activate", MAI_TYPE_ATK_OBJECT);
        g_signal_emit(atkObj, id, 0);

        // Always fire a current focus event after activation.
        FocusMgr()->ForceFocusEvent();
      } break;

    case nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE:
      {
        accessible->AsRoot()->mActivated = false;
        guint id = g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT);
        g_signal_emit(atkObj, id, 0);
      } break;

    case nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE:
      {
        guint id = g_signal_lookup("maximize", MAI_TYPE_ATK_OBJECT);
        g_signal_emit(atkObj, id, 0);
      } break;

    case nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE:
      {
        guint id = g_signal_lookup("minimize", MAI_TYPE_ATK_OBJECT);
        g_signal_emit(atkObj, id, 0);
      } break;

    case nsIAccessibleEvent::EVENT_WINDOW_RESTORE:
      {
        guint id = g_signal_lookup("restore", MAI_TYPE_ATK_OBJECT);
        g_signal_emit(atkObj, id, 0);
      } break;

    case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE:
        g_signal_emit_by_name (atkObj, "load_complete");
        // XXX - Handle native dialog accessibles.
        if (!accessible->IsRoot() && accessible->HasARIARole() &&
            accessible->ARIARole() == roles::DIALOG) {
          guint id = g_signal_lookup("activate", MAI_TYPE_ATK_OBJECT);
          g_signal_emit(atkObj, id, 0);
        }
      break;

    case nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD:
        g_signal_emit_by_name (atkObj, "reload");
      break;

    case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED:
        g_signal_emit_by_name (atkObj, "load_stopped");
      break;

    case nsIAccessibleEvent::EVENT_MENUPOPUP_START:
        atk_focus_tracker_notify(atkObj); // fire extra focus event
        atk_object_notify_state_change(atkObj, ATK_STATE_VISIBLE, true);
        atk_object_notify_state_change(atkObj, ATK_STATE_SHOWING, true);
        break;

    case nsIAccessibleEvent::EVENT_MENUPOPUP_END:
        atk_object_notify_state_change(atkObj, ATK_STATE_VISIBLE, false);
        atk_object_notify_state_change(atkObj, ATK_STATE_SHOWING, false);
        break;
    }

    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;
    }
}
Beispiel #12
0
void
EventQueue::ProcessEventQueue()
{
  // Process only currently queued events.
  nsTArray<RefPtr<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_SELECTION_CHANGED) {
        SelectionMgr()->ProcessTextSelChangeEvent(event);
        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);
    }

    if (!mDocument)
      return;
  }
}
Beispiel #13
0
void
NotificationController::WillRefresh(mozilla::TimeStamp aTime)
{
  // If the document accessible that notification collector was created for is
  // now shut down, don't process notifications anymore.
  NS_ASSERTION(mDocument,
               "The document was shut down while refresh observer is attached!");
  if (!mDocument)
    return;

  // Any generic notifications should be queued if we're processing content
  // insertions or generic notifications.
  mObservingState = eRefreshProcessingForUpdate;

  // Initial accessible tree construction.
  if (!mDocument->HasLoadState(DocAccessible::eTreeConstructed)) {
    // If document is not bound to parent at this point then the document is not
    // ready yet (process notifications later).
    if (!mDocument->IsBoundToParent()) {
      mObservingState = eRefreshObserving;
      return;
    }

#ifdef DEBUG_NOTIFICATIONS
    printf("\ninitial tree created, document: %p, document node: %p\n",
           mDocument.get(), mDocument->GetDocumentNode());
#endif

    mDocument->DoInitialUpdate();

    NS_ASSERTION(mContentInsertions.Length() == 0,
                 "Pending content insertions while initial accessible tree isn't created!");
  }

  // Process content inserted notifications to update the tree. Process other
  // notifications like DOM events and then flush event queue. If any new
  // notifications are queued during this processing then they will be processed
  // on next refresh. If notification processing queues up new events then they
  // are processed in this refresh. If events processing queues up new events
  // then new events are processed on next refresh.
  // Note: notification processing or event handling may shut down the owning
  // document accessible.

  // Process only currently queued content inserted notifications.
  nsTArray<nsRefPtr<ContentInsertion> > contentInsertions;
  contentInsertions.SwapElements(mContentInsertions);

  PRUint32 insertionCount = contentInsertions.Length();
  for (PRUint32 idx = 0; idx < insertionCount; idx++) {
    contentInsertions[idx]->Process();
    if (!mDocument)
      return;
  }

  // Process rendered text change notifications.
  mTextHash.EnumerateEntries(TextEnumerator, mDocument);
  mTextHash.Clear();

  // Bind hanging child documents.
  PRUint32 hangingDocCnt = mHangingChildDocuments.Length();
  for (PRUint32 idx = 0; idx < hangingDocCnt; idx++) {
    DocAccessible* childDoc = mHangingChildDocuments[idx];
    if (childDoc->IsDefunct())
      continue;

    nsIContent* ownerContent = mDocument->GetDocumentNode()->
      FindContentForSubDocument(childDoc->GetDocumentNode());
    if (ownerContent) {
      Accessible* outerDocAcc = mDocument->GetAccessible(ownerContent);
      if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) {
        if (mDocument->AppendChildDocument(childDoc))
          continue;

        outerDocAcc->RemoveChild(childDoc);
      }

      // Failed to bind the child document, destroy it.
      childDoc->Shutdown();
    }
  }
  mHangingChildDocuments.Clear();

  // If the document is ready and all its subdocuments are completely loaded
  // then process the document load.
  if (mDocument->HasLoadState(DocAccessible::eReady) &&
      !mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) &&
      hangingDocCnt == 0) {
    PRUint32 childDocCnt = mDocument->ChildDocumentCount(), childDocIdx = 0;
    for (; childDocIdx < childDocCnt; childDocIdx++) {
      DocAccessible* childDoc = mDocument->GetChildDocumentAt(childDocIdx);
      if (!childDoc->HasLoadState(DocAccessible::eCompletelyLoaded))
        break;
    }

    if (childDocIdx == childDocCnt) {
      mDocument->ProcessLoad();
      if (!mDocument)
        return;
    }
  }

  // Process only currently queued generic notifications.
  nsTArray < nsRefPtr<Notification> > notifications;
  notifications.SwapElements(mNotifications);

  PRUint32 notificationCount = notifications.Length();
  for (PRUint32 idx = 0; idx < notificationCount; idx++) {
    notifications[idx]->Process();
    if (!mDocument)
      return;
  }

  // Process invalidation list of the document after all accessible tree
  // modification are done.
  mDocument->ProcessInvalidationList();

  // If a generic notification occurs after this point then we may be allowed to
  // process it synchronously.
  mObservingState = eRefreshObserving;

  // Process only currently queued events.
  nsTArray<nsRefPtr<AccEvent> > events;
  events.SwapElements(mEvents);

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

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

      mDocument->ProcessPendingEvent(accEvent);

      // Fire text change event caused by tree mutation.
      AccMutationEvent* showOrHideEvent = downcast_accEvent(accEvent);
      if (showOrHideEvent) {
        if (showOrHideEvent->mTextChangeEvent)
          mDocument->ProcessPendingEvent(showOrHideEvent->mTextChangeEvent);
      }
    }
    if (!mDocument)
      return;
  }

  // Stop further processing if there are no new notifications of any kind or
  // events and document load is processed.
  if (mContentInsertions.Length() == 0 && mNotifications.Length() == 0 &&
      mEvents.Length() == 0 && mTextHash.Count() == 0 &&
      mHangingChildDocuments.Length() == 0 &&
      mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) &&
      mPresShell->RemoveRefreshObserver(this, Flush_Display)) {
    mObservingState = eNotObservingRefresh;
  }
}