tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

DocAccessibleParent.cpp (43967B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "ARIAMap.h"
      8 #include "CachedTableAccessible.h"
      9 #include "DocAccessibleParent.h"
     10 #include "mozilla/a11y/Platform.h"
     11 #include "mozilla/Components.h"  // for mozilla::components
     12 #include "mozilla/dom/BrowserBridgeParent.h"
     13 #include "mozilla/dom/BrowserParent.h"
     14 #include "mozilla/dom/CanonicalBrowsingContext.h"
     15 #include "mozilla/PerfStats.h"
     16 #include "mozilla/ProfilerMarkers.h"
     17 #include "nsAccessibilityService.h"
     18 #include "xpcAccessibleDocument.h"
     19 #include "xpcAccEvents.h"
     20 #include "nsAccUtils.h"
     21 #include "nsIIOService.h"
     22 #include "TextRange.h"
     23 #include "Relation.h"
     24 #include "RootAccessible.h"
     25 
     26 #if defined(XP_WIN)
     27 #  include "Compatibility.h"
     28 #  include "nsWinUtils.h"
     29 #endif
     30 
     31 #if defined(ANDROID)
     32 #  define ACQUIRE_ANDROID_LOCK \
     33    MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor());
     34 #else
     35 #  define ACQUIRE_ANDROID_LOCK \
     36    do {                       \
     37    } while (0);
     38 #endif
     39 
     40 namespace mozilla {
     41 
     42 namespace a11y {
     43 uint64_t DocAccessibleParent::sMaxDocID = 0;
     44 
     45 DocAccessibleParent::DocAccessibleParent()
     46    : RemoteAccessible(this),
     47 #if defined(XP_WIN)
     48      mEmulatedWindowHandle(nullptr),
     49 #endif  // defined(XP_WIN)
     50      mTopLevel(false),
     51      mTopLevelInContentProcess(false),
     52      mShutdown(false),
     53      mFocus(0),
     54      mCaretId(0),
     55      mCaretOffset(-1),
     56      mIsCaretAtEndOfLine(false) {
     57  sMaxDocID++;
     58  mActorID = sMaxDocID;
     59  MOZ_ASSERT(!LiveDocs().Get(mActorID));
     60  LiveDocs().InsertOrUpdate(mActorID, this);
     61 }
     62 
     63 DocAccessibleParent::~DocAccessibleParent() {
     64  UnregisterWeakMemoryReporter(this);
     65  LiveDocs().Remove(mActorID);
     66  MOZ_ASSERT(mChildDocs.Length() == 0);
     67  MOZ_ASSERT(!ParentDoc());
     68 }
     69 
     70 already_AddRefed<DocAccessibleParent> DocAccessibleParent::New() {
     71  RefPtr<DocAccessibleParent> dap(new DocAccessibleParent());
     72  // We need to do this with a non-zero reference count.  The easiest way is to
     73  // do it in this static method and hide the constructor.
     74  RegisterWeakMemoryReporter(dap);
     75  return dap.forget();
     76 }
     77 
     78 void DocAccessibleParent::SetBrowsingContext(
     79    dom::CanonicalBrowsingContext* aBrowsingContext) {
     80  mBrowsingContext = aBrowsingContext;
     81 }
     82 
     83 mozilla::ipc::IPCResult DocAccessibleParent::ProcessShowEvent(
     84    nsTArray<AccessibleData>&& aNewTree, const bool& aEventSuppressed,
     85    const bool& aComplete, const bool& aFromUser) {
     86  AUTO_PROFILER_MARKER_TEXT("DocAccessibleParent::ProcessShowEvent", A11Y, {},
     87                            ""_ns);
     88  PerfStats::AutoMetricRecording<PerfStats::Metric::A11Y_ProcessShowEvent>
     89      autoRecording;
     90  // DO NOT ADD CODE ABOVE THIS BLOCK: THIS CODE IS MEASURING TIMINGS.
     91 
     92  ACQUIRE_ANDROID_LOCK
     93 
     94  MOZ_ASSERT(CheckDocTree());
     95 
     96  if (aNewTree.IsEmpty()) {
     97    return IPC_FAIL(this, "No children being added");
     98  }
     99 
    100  RemoteAccessible* root = nullptr;
    101  RemoteAccessible* rootParent = nullptr;
    102  RemoteAccessible* lastParent = this;
    103  uint64_t lastParentID = 0;
    104  for (const auto& accData : aNewTree) {
    105    // Avoid repeated hash lookups when there are multiple children of the same
    106    // parent.
    107    RemoteAccessible* parent = accData.ParentID() == lastParentID
    108                                   ? lastParent
    109                                   : GetAccessible(accData.ParentID());
    110    // XXX This should really never happen, but sometimes we fail to fire the
    111    // required show events.
    112    if (!parent) {
    113      NS_ERROR("adding child to unknown accessible");
    114 #ifdef DEBUG
    115      return IPC_FAIL(this, "unknown parent accessible");
    116 #else
    117      return IPC_OK();
    118 #endif
    119    }
    120    lastParent = parent;
    121    lastParentID = accData.ParentID();
    122 
    123    uint32_t childIdx = accData.IndexInParent();
    124    if (childIdx > parent->ChildCount()) {
    125      NS_ERROR("invalid index to add child at");
    126 #ifdef DEBUG
    127      return IPC_FAIL(this, "invalid index");
    128 #else
    129      return IPC_OK();
    130 #endif
    131    }
    132 
    133    RemoteAccessible* child = CreateAcc(accData);
    134    if (!child) {
    135      // This shouldn't happen.
    136      return IPC_FAIL(this, "failed to add children");
    137    }
    138    if (!root && !mPendingShowChild) {
    139      // This is the first Accessible, which is the root of the shown subtree.
    140      root = child;
    141      rootParent = parent;
    142    }
    143    // If this show event has been split across multiple messages and this is
    144    // not the last message, don't attach the shown root to the tree yet.
    145    // Otherwise, clients might crawl the incomplete subtree and they won't get
    146    // mutation events for the remaining pieces.
    147    if (aComplete || root != child) {
    148      AttachChild(parent, childIdx, child);
    149    }
    150  }
    151 
    152  MOZ_ASSERT(CheckDocTree());
    153 
    154  if (!aComplete && !mPendingShowChild) {
    155    // This is the first message for a show event split across multiple
    156    // messages. Save the show target for subsequent messages and return.
    157    const auto& accData = aNewTree[0];
    158    mPendingShowChild = accData.ID();
    159    mPendingShowParent = accData.ParentID();
    160    mPendingShowIndex = accData.IndexInParent();
    161    return IPC_OK();
    162  }
    163  if (!aComplete) {
    164    // This show event has been split into multiple messages, but this is
    165    // neither the first nor the last message. There's nothing more to do here.
    166    return IPC_OK();
    167  }
    168  MOZ_ASSERT(aComplete);
    169  if (mPendingShowChild) {
    170    // This is the last message for a show event split across multiple
    171    // messages. Retrieve the saved show target, attach it to the tree and fire
    172    // an event if appropriate.
    173    rootParent = GetAccessible(mPendingShowParent);
    174    MOZ_ASSERT(rootParent);
    175    root = GetAccessible(mPendingShowChild);
    176    MOZ_ASSERT(root);
    177    AttachChild(rootParent, mPendingShowIndex, root);
    178    mPendingShowChild = 0;
    179    mPendingShowParent = 0;
    180    mPendingShowIndex = 0;
    181  }
    182 
    183  // Just update, no events.
    184  if (aEventSuppressed) {
    185    return IPC_OK();
    186  }
    187 
    188  {
    189    // Scope for PerfStats
    190    AUTO_PROFILER_MARKER_TEXT("a11y::PlatformShowHideEvent", A11Y, {}, ""_ns);
    191    PerfStats::AutoMetricRecording<
    192        PerfStats::Metric::A11Y_PlatformShowHideEvent>
    193        autoRecording;
    194    // WITHIN THIS SCOPE, DO NOT ADD CODE ABOVE THIS BLOCK:
    195    // THIS CODE IS MEASURING TIMINGS.
    196    PlatformShowHideEvent(root, rootParent, true, aFromUser);
    197  }
    198 
    199  if (nsCOMPtr<nsIObserverService> obsService =
    200          services::GetObserverService()) {
    201    obsService->NotifyObservers(nullptr, NS_ACCESSIBLE_CACHE_TOPIC, nullptr);
    202  }
    203 
    204  if (!nsCoreUtils::AccEventObserversExist()) {
    205    return IPC_OK();
    206  }
    207 
    208  uint32_t type = nsIAccessibleEvent::EVENT_SHOW;
    209  xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(root);
    210  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
    211  nsINode* node = nullptr;
    212  RefPtr<xpcAccEvent> event =
    213      new xpcAccEvent(type, xpcAcc, doc, node, aFromUser);
    214  nsCoreUtils::DispatchAccEvent(std::move(event));
    215 
    216  return IPC_OK();
    217 }
    218 
    219 RemoteAccessible* DocAccessibleParent::CreateAcc(
    220    const AccessibleData& aAccData) {
    221  RemoteAccessible* newProxy;
    222  if ((newProxy = GetAccessible(aAccData.ID()))) {
    223    // This is a move. Reuse the Accessible; don't destroy it.
    224    if (newProxy->RemoteParent()) {
    225      MOZ_ASSERT_UNREACHABLE(
    226          "Attempt to move RemoteAccessible which still has a parent!");
    227      return nullptr;
    228    }
    229    return newProxy;
    230  }
    231 
    232  if (!aria::IsRoleMapIndexValid(aAccData.RoleMapEntryIndex())) {
    233    MOZ_ASSERT_UNREACHABLE("Invalid role map entry index");
    234    return nullptr;
    235  }
    236 
    237  newProxy = new RemoteAccessible(aAccData.ID(), this, aAccData.Role(),
    238                                  aAccData.Type(), aAccData.GenericTypes(),
    239                                  aAccData.RoleMapEntryIndex());
    240  mAccessibles.PutEntry(aAccData.ID())->mProxy = newProxy;
    241 
    242  if (RefPtr<AccAttributes> fields = aAccData.CacheFields()) {
    243    newProxy->ApplyCache(CacheUpdateType::Initial, fields);
    244  }
    245 
    246  return newProxy;
    247 }
    248 
    249 void DocAccessibleParent::AttachChild(RemoteAccessible* aParent,
    250                                      uint32_t aIndex,
    251                                      RemoteAccessible* aChild) {
    252  aParent->AddChildAt(aIndex, aChild);
    253  aChild->SetParent(aParent);
    254  // ProxyCreated might have already been called if aChild is being moved.
    255  if (!aChild->GetWrapper()) {
    256    ProxyCreated(aChild);
    257  }
    258  if (aChild->IsTableCell()) {
    259    CachedTableAccessible::Invalidate(aChild);
    260  }
    261  if (aChild->IsOuterDoc()) {
    262    // We can only do this after ProxyCreated is called because it will fire an
    263    // event on aChild.
    264    mPendingOOPChildDocs.RemoveIf([&](dom::BrowserBridgeParent* bridge) {
    265      MOZ_ASSERT(bridge->GetBrowserParent(),
    266                 "Pending BrowserBridgeParent should be alive");
    267      if (bridge->GetEmbedderAccessibleId() != aChild->ID()) {
    268        return false;
    269      }
    270      MOZ_ASSERT(bridge->GetEmbedderAccessibleDoc() == this);
    271      if (DocAccessibleParent* childDoc = bridge->GetDocAccessibleParent()) {
    272        AddChildDoc(childDoc, aChild->ID(), false);
    273      }
    274      return true;
    275    });
    276  }
    277 }
    278 
    279 void DocAccessibleParent::ShutdownOrPrepareForMove(RemoteAccessible* aAcc) {
    280  // Children might be removed or moved. Handle them the same way. We do this
    281  // before checking the moving IDs set in order to ensure that we handle moved
    282  // descendants properly. Avoid descending into the children of outer documents
    283  // for moves since they are added and removed differently to normal children.
    284  if (!aAcc->IsOuterDoc()) {
    285    // Even if some children are kept, those will be re-attached when we handle
    286    // the show event. For now, clear all of them by moving them to a temporary.
    287    auto children{std::move(aAcc->mChildren)};
    288    for (RemoteAccessible* child : children) {
    289      ShutdownOrPrepareForMove(child);
    290    }
    291  }
    292 
    293  const uint64_t id = aAcc->ID();
    294  if (!mMovingIDs.Contains(id)) {
    295    // This Accessible is being removed.
    296    aAcc->Shutdown();
    297    return;
    298  }
    299  // This is a move. Moves are sent as a hide and then a show, but for a move,
    300  // we want to keep the Accessible alive for reuse later.
    301  if (aAcc->IsTable() || aAcc->IsTableCell()) {
    302    // For table cells, it's important that we do this before the parent is
    303    // cleared because CachedTableAccessible::Invalidate needs the ancestry.
    304    CachedTableAccessible::Invalidate(aAcc);
    305  }
    306  if (aAcc->IsHyperText()) {
    307    aAcc->InvalidateCachedHyperTextOffsets();
    308  }
    309  aAcc->SetParent(nullptr);
    310  mMovingIDs.EnsureRemoved(id);
    311 }
    312 
    313 mozilla::ipc::IPCResult DocAccessibleParent::ProcessHideEvent(
    314    const uint64_t& aRootID, const bool& aFromUser) {
    315  AUTO_PROFILER_MARKER_TEXT("DocAccessibleParent::ProcessHideEvent", A11Y, {},
    316                            ""_ns);
    317  PerfStats::AutoMetricRecording<PerfStats::Metric::A11Y_ProcessHideEvent>
    318      autoRecording;
    319  // DO NOT ADD CODE ABOVE THIS BLOCK: THIS CODE IS MEASURING TIMINGS.
    320  ACQUIRE_ANDROID_LOCK
    321 
    322  MOZ_ASSERT(CheckDocTree());
    323 
    324  // We shouldn't actually need this because mAccessibles shouldn't have an
    325  // entry for the document itself, but it doesn't hurt to be explicit.
    326  if (!aRootID) {
    327    return IPC_FAIL(this, "Trying to hide entire document?");
    328  }
    329 
    330  ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID);
    331  if (!rootEntry) {
    332    NS_ERROR("invalid root being removed!");
    333    return IPC_OK();
    334  }
    335 
    336  RemoteAccessible* root = rootEntry->mProxy;
    337  if (!root) {
    338    NS_ERROR("invalid root being removed!");
    339    return IPC_OK();
    340  }
    341 
    342  RemoteAccessible* parent = root->RemoteParent();
    343  {
    344    // Scope for PerfStats
    345    AUTO_PROFILER_MARKER_TEXT("a11y::PlatformShowHideEvent", A11Y, {}, ""_ns);
    346    PerfStats::AutoMetricRecording<
    347        PerfStats::Metric::A11Y_PlatformShowHideEvent>
    348        autoRecording;
    349    // WITHIN THIS SCOPE, DO NOT ADD CODE ABOVE THIS BLOCK:
    350    // THIS CODE IS MEASURING TIMINGS.
    351    PlatformShowHideEvent(root, parent, false, aFromUser);
    352  }
    353 
    354  RefPtr<xpcAccHideEvent> event = nullptr;
    355  if (nsCoreUtils::AccEventObserversExist()) {
    356    uint32_t type = nsIAccessibleEvent::EVENT_HIDE;
    357    xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(root);
    358    xpcAccessibleGeneric* xpcParent = GetXPCAccessible(parent);
    359    RemoteAccessible* next = root->RemoteNextSibling();
    360    xpcAccessibleGeneric* xpcNext = next ? GetXPCAccessible(next) : nullptr;
    361    RemoteAccessible* prev = root->RemotePrevSibling();
    362    xpcAccessibleGeneric* xpcPrev = prev ? GetXPCAccessible(prev) : nullptr;
    363    xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
    364    nsINode* node = nullptr;
    365    event = new xpcAccHideEvent(type, xpcAcc, doc, node, aFromUser, xpcParent,
    366                                xpcNext, xpcPrev);
    367  }
    368 
    369  parent->RemoveChild(root);
    370  ShutdownOrPrepareForMove(root);
    371 
    372  MOZ_ASSERT(CheckDocTree());
    373 
    374  if (event) {
    375    nsCoreUtils::DispatchAccEvent(std::move(event));
    376  }
    377 
    378  return IPC_OK();
    379 }
    380 
    381 mozilla::ipc::IPCResult DocAccessibleParent::RecvEvent(
    382    const uint64_t& aID, const uint32_t& aEventType) {
    383  ACQUIRE_ANDROID_LOCK
    384  if (mShutdown) {
    385    return IPC_OK();
    386  }
    387  if (aEventType == 0 || aEventType >= nsIAccessibleEvent::EVENT_LAST_ENTRY) {
    388    MOZ_ASSERT_UNREACHABLE("Invalid event");
    389    return IPC_FAIL(this, "Invalid event");
    390  }
    391 
    392  RemoteAccessible* remote = GetAccessible(aID);
    393  if (!remote) {
    394    NS_ERROR("no proxy for event!");
    395    return IPC_OK();
    396  }
    397 
    398  FireEvent(remote, aEventType);
    399  return IPC_OK();
    400 }
    401 
    402 void DocAccessibleParent::FireEvent(RemoteAccessible* aAcc,
    403                                    const uint32_t& aEventType) {
    404  if (aEventType == nsIAccessibleEvent::EVENT_REORDER ||
    405      aEventType == nsIAccessibleEvent::EVENT_INNER_REORDER) {
    406    uint32_t count = aAcc->ChildCount();
    407    for (uint32_t c = 0; c < count; ++c) {
    408      aAcc->RemoteChildAt(c)->InvalidateGroupInfo();
    409    }
    410  } else if (aEventType == nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE &&
    411             aAcc == this) {
    412    // A DocAccessible gets the STALE state while it is still loading, but we
    413    // don't fire a state change for that. That state might have been
    414    // included in the initial cache push, so clear it here.
    415    // We also clear the BUSY state here. Although we do fire a state change
    416    // for that, we fire it after doc load complete. It doesn't make sense
    417    // for the document to report BUSY after doc load complete and doing so
    418    // confuses JAWS.
    419    UpdateStateCache(states::STALE | states::BUSY, false);
    420  }
    421 
    422  PlatformEvent(aAcc, aEventType);
    423 
    424  if (!nsCoreUtils::AccEventObserversExist()) {
    425    return;
    426  }
    427 
    428  xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(aAcc);
    429  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
    430  nsINode* node = nullptr;
    431  bool fromUser = true;  // XXX fix me
    432  RefPtr<xpcAccEvent> event =
    433      new xpcAccEvent(aEventType, xpcAcc, doc, node, fromUser);
    434  nsCoreUtils::DispatchAccEvent(std::move(event));
    435 }
    436 
    437 mozilla::ipc::IPCResult DocAccessibleParent::RecvStateChangeEvent(
    438    const uint64_t& aID, const uint64_t& aState, const bool& aEnabled) {
    439  ACQUIRE_ANDROID_LOCK
    440  if (mShutdown) {
    441    return IPC_OK();
    442  }
    443 
    444  RemoteAccessible* target = GetAccessible(aID);
    445  if (!target) {
    446    NS_ERROR("we don't know about the target of a state change event!");
    447    return IPC_OK();
    448  }
    449 
    450  target->UpdateStateCache(aState, aEnabled);
    451  if (nsCOMPtr<nsIObserverService> obsService =
    452          services::GetObserverService()) {
    453    obsService->NotifyObservers(nullptr, NS_ACCESSIBLE_CACHE_TOPIC, nullptr);
    454  }
    455  PlatformStateChangeEvent(target, aState, aEnabled);
    456 
    457  if (!nsCoreUtils::AccEventObserversExist()) {
    458    return IPC_OK();
    459  }
    460 
    461  xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
    462  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
    463  uint32_t type = nsIAccessibleEvent::EVENT_STATE_CHANGE;
    464  bool extra;
    465  uint32_t state = nsAccUtils::To32States(aState, &extra);
    466  bool fromUser = true;     // XXX fix this
    467  nsINode* node = nullptr;  // XXX can we do better?
    468  RefPtr<xpcAccStateChangeEvent> event = new xpcAccStateChangeEvent(
    469      type, xpcAcc, doc, node, fromUser, state, extra, aEnabled);
    470  nsCoreUtils::DispatchAccEvent(std::move(event));
    471 
    472  return IPC_OK();
    473 }
    474 
    475 mozilla::ipc::IPCResult DocAccessibleParent::RecvCaretMoveEvent(
    476    const uint64_t& aID, const LayoutDeviceIntRect& aCaretRect,
    477    const int32_t& aOffset, const bool& aIsSelectionCollapsed,
    478    const bool& aIsAtEndOfLine, const int32_t& aGranularity,
    479    const bool& aFromUser) {
    480  ACQUIRE_ANDROID_LOCK
    481  if (mShutdown) {
    482    return IPC_OK();
    483  }
    484 
    485  RemoteAccessible* proxy = GetAccessible(aID);
    486  if (!proxy) {
    487    NS_ERROR("unknown caret move event target!");
    488    return IPC_OK();
    489  }
    490 
    491  mCaretId = aID;
    492  mCaretOffset = aOffset;
    493  mIsCaretAtEndOfLine = aIsAtEndOfLine;
    494  mCaretRect = aCaretRect;
    495  if (aIsSelectionCollapsed) {
    496    // We don't fire selection events for collapsed selections, but we need to
    497    // ensure we don't have a stale cached selection; e.g. when selecting
    498    // forward and then unselecting backward.
    499    mTextSelections.ClearAndRetainStorage();
    500    mTextSelections.AppendElement(TextRangeData(aID, aID, aOffset, aOffset));
    501  }
    502 
    503  PlatformCaretMoveEvent(proxy, aOffset, aIsSelectionCollapsed, aGranularity,
    504                         aFromUser);
    505 
    506  if (!nsCoreUtils::AccEventObserversExist()) {
    507    return IPC_OK();
    508  }
    509 
    510  xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
    511  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
    512  nsINode* node = nullptr;
    513  bool fromUser = true;  // XXX fix me
    514  uint32_t type = nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED;
    515  RefPtr<xpcAccCaretMoveEvent> event = new xpcAccCaretMoveEvent(
    516      type, xpcAcc, doc, node, fromUser, aOffset, aIsSelectionCollapsed,
    517      aIsAtEndOfLine, aGranularity);
    518  nsCoreUtils::DispatchAccEvent(std::move(event));
    519 
    520  return IPC_OK();
    521 }
    522 
    523 mozilla::ipc::IPCResult DocAccessibleParent::ProcessTextChangeEvent(
    524    const uint64_t& aID, const nsAString& aStr, const int32_t& aStart,
    525    const uint32_t& aLen, const bool& aIsInsert, const bool& aFromUser) {
    526  ACQUIRE_ANDROID_LOCK
    527 
    528  RemoteAccessible* target = GetAccessible(aID);
    529  if (!target) {
    530    NS_ERROR("text change event target is unknown!");
    531    return IPC_OK();
    532  }
    533 
    534  PlatformTextChangeEvent(target, aStr, aStart, aLen, aIsInsert, aFromUser);
    535 
    536  if (!nsCoreUtils::AccEventObserversExist()) {
    537    return IPC_OK();
    538  }
    539 
    540  xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
    541  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
    542  uint32_t type = aIsInsert ? nsIAccessibleEvent::EVENT_TEXT_INSERTED
    543                            : nsIAccessibleEvent::EVENT_TEXT_REMOVED;
    544  nsINode* node = nullptr;
    545  RefPtr<xpcAccTextChangeEvent> event = new xpcAccTextChangeEvent(
    546      type, xpcAcc, doc, node, aFromUser, aStart, aLen, aIsInsert, aStr);
    547  nsCoreUtils::DispatchAccEvent(std::move(event));
    548 
    549  return IPC_OK();
    550 }
    551 
    552 mozilla::ipc::IPCResult DocAccessibleParent::RecvMutationEvents(
    553    nsTArray<MutationEventData>&& aData) {
    554  // We do not use ACQUIRE_ANDROID_LOCK here since we call functions that do
    555  // that for us. The lock is not re-entrant.
    556  mozilla::ipc::IPCResult result = IPC_OK();
    557  if (mShutdown) {
    558    return result;
    559  }
    560  for (MutationEventData& data : aData) {
    561    switch (data.type()) {
    562      case MutationEventData::Type::TCacheEventData: {
    563        CacheEventData& cacheEventData = data;
    564        result = RecvCache(cacheEventData.UpdateType(),
    565                           std::move(cacheEventData.aData()));
    566        break;
    567      }
    568      case MutationEventData::Type::TReorderEventData: {
    569        ReorderEventData& reorderEventData = data;
    570        result = RecvEvent(reorderEventData.ID(), reorderEventData.Type());
    571        break;
    572      }
    573      case MutationEventData::Type::THideEventData: {
    574        HideEventData& hideEventData = data;
    575        result = ProcessHideEvent(hideEventData.ID(),
    576                                  hideEventData.IsFromUserInput());
    577        break;
    578      }
    579      case MutationEventData::Type::TShowEventData: {
    580        ShowEventData& showEventData = data;
    581        result = ProcessShowEvent(
    582            std::move(showEventData.NewTree()), showEventData.EventSuppressed(),
    583            showEventData.Complete(), showEventData.FromUser());
    584        break;
    585      }
    586      case MutationEventData::Type::TTextChangeEventData: {
    587        TextChangeEventData& textChangeEventData = data;
    588        result = ProcessTextChangeEvent(
    589            textChangeEventData.ID(), textChangeEventData.Str(),
    590            textChangeEventData.Start(), textChangeEventData.Len(),
    591            textChangeEventData.IsInsert(), textChangeEventData.FromUser());
    592        break;
    593      }
    594      default:
    595        break;
    596    }
    597    if (!result) {
    598      return result;
    599    }
    600  }
    601 
    602  return IPC_OK();
    603 }
    604 
    605 mozilla::ipc::IPCResult DocAccessibleParent::RecvRequestAckMutationEvents() {
    606  if (!mShutdown) {
    607    (void)SendAckMutationEvents();
    608  }
    609  return IPC_OK();
    610 }
    611 
    612 mozilla::ipc::IPCResult DocAccessibleParent::RecvSelectionEvent(
    613    const uint64_t& aID, const uint64_t& aWidgetID, const uint32_t& aType) {
    614  ACQUIRE_ANDROID_LOCK
    615  if (mShutdown) {
    616    return IPC_OK();
    617  }
    618  if (aType == 0 || aType >= nsIAccessibleEvent::EVENT_LAST_ENTRY) {
    619    MOZ_ASSERT_UNREACHABLE("Invalid event");
    620    return IPC_FAIL(this, "Invalid event");
    621  }
    622 
    623  RemoteAccessible* target = GetAccessible(aID);
    624  RemoteAccessible* widget = GetAccessible(aWidgetID);
    625  if (!target || !widget) {
    626    NS_ERROR("invalid id in selection event");
    627    return IPC_OK();
    628  }
    629 
    630  PlatformSelectionEvent(target, widget, aType);
    631  if (!nsCoreUtils::AccEventObserversExist()) {
    632    return IPC_OK();
    633  }
    634  xpcAccessibleGeneric* xpcTarget = GetXPCAccessible(target);
    635  xpcAccessibleDocument* xpcDoc = GetAccService()->GetXPCDocument(this);
    636  RefPtr<xpcAccEvent> event =
    637      new xpcAccEvent(aType, xpcTarget, xpcDoc, nullptr, false);
    638  nsCoreUtils::DispatchAccEvent(std::move(event));
    639 
    640  return IPC_OK();
    641 }
    642 
    643 mozilla::ipc::IPCResult DocAccessibleParent::RecvScrollingEvent(
    644    const uint64_t& aID, const uint64_t& aType, const uint32_t& aScrollX,
    645    const uint32_t& aScrollY, const uint32_t& aMaxScrollX,
    646    const uint32_t& aMaxScrollY) {
    647  ACQUIRE_ANDROID_LOCK
    648  if (mShutdown) {
    649    return IPC_OK();
    650  }
    651  if (aType == 0 || aType >= nsIAccessibleEvent::EVENT_LAST_ENTRY) {
    652    MOZ_ASSERT_UNREACHABLE("Invalid event");
    653    return IPC_FAIL(this, "Invalid event");
    654  }
    655 
    656  RemoteAccessible* target = GetAccessible(aID);
    657  if (!target) {
    658    NS_ERROR("no proxy for event!");
    659    return IPC_OK();
    660  }
    661 
    662 #if defined(ANDROID)
    663  PlatformScrollingEvent(target, aType, aScrollX, aScrollY, aMaxScrollX,
    664                         aMaxScrollY);
    665 #else
    666  PlatformEvent(target, aType);
    667 #endif
    668 
    669  if (!nsCoreUtils::AccEventObserversExist()) {
    670    return IPC_OK();
    671  }
    672 
    673  xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
    674  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
    675  nsINode* node = nullptr;
    676  bool fromUser = true;  // XXX: Determine if this was from user input.
    677  RefPtr<xpcAccScrollingEvent> event =
    678      new xpcAccScrollingEvent(aType, xpcAcc, doc, node, fromUser, aScrollX,
    679                               aScrollY, aMaxScrollX, aMaxScrollY);
    680  nsCoreUtils::DispatchAccEvent(std::move(event));
    681 
    682  return IPC_OK();
    683 }
    684 
    685 mozilla::ipc::IPCResult DocAccessibleParent::RecvCache(
    686    const mozilla::a11y::CacheUpdateType& aUpdateType,
    687    nsTArray<CacheData>&& aData) {
    688  AUTO_PROFILER_MARKER_TEXT("DocAccessibleParent::RecvCache", A11Y, {}, ""_ns);
    689  PerfStats::AutoMetricRecording<PerfStats::Metric::A11Y_RecvCache>
    690      autoRecording;
    691  // DO NOT ADD CODE ABOVE THIS BLOCK: THIS CODE IS MEASURING TIMINGS.
    692 
    693  ACQUIRE_ANDROID_LOCK
    694  if (mShutdown) {
    695    return IPC_OK();
    696  }
    697 
    698  for (auto& entry : aData) {
    699    RemoteAccessible* remote = GetAccessible(entry.ID());
    700    if (!remote) {
    701      MOZ_ASSERT_UNREACHABLE("No remote found!");
    702      continue;
    703    }
    704 
    705    remote->ApplyCache(aUpdateType, entry.Fields());
    706  }
    707 
    708  if (nsCOMPtr<nsIObserverService> obsService =
    709          services::GetObserverService()) {
    710    obsService->NotifyObservers(nullptr, NS_ACCESSIBLE_CACHE_TOPIC, nullptr);
    711  }
    712 
    713  return IPC_OK();
    714 }
    715 
    716 mozilla::ipc::IPCResult DocAccessibleParent::RecvSelectedAccessiblesChanged(
    717    nsTArray<uint64_t>&& aSelectedIDs, nsTArray<uint64_t>&& aUnselectedIDs) {
    718  ACQUIRE_ANDROID_LOCK
    719  if (mShutdown) {
    720    return IPC_OK();
    721  }
    722 
    723  for (auto& id : aSelectedIDs) {
    724    RemoteAccessible* remote = GetAccessible(id);
    725    if (!remote) {
    726      MOZ_ASSERT_UNREACHABLE("No remote found!");
    727      continue;
    728    }
    729 
    730    remote->UpdateStateCache(states::SELECTED, true);
    731  }
    732 
    733  for (auto& id : aUnselectedIDs) {
    734    RemoteAccessible* remote = GetAccessible(id);
    735    if (!remote) {
    736      MOZ_ASSERT_UNREACHABLE("No remote found!");
    737      continue;
    738    }
    739 
    740    remote->UpdateStateCache(states::SELECTED, false);
    741  }
    742 
    743  if (nsCOMPtr<nsIObserverService> obsService =
    744          services::GetObserverService()) {
    745    obsService->NotifyObservers(nullptr, NS_ACCESSIBLE_CACHE_TOPIC, nullptr);
    746  }
    747 
    748  return IPC_OK();
    749 }
    750 
    751 mozilla::ipc::IPCResult DocAccessibleParent::RecvAccessiblesWillMove(
    752    nsTArray<uint64_t>&& aIDs) {
    753  for (uint64_t id : aIDs) {
    754    mMovingIDs.EnsureInserted(id);
    755  }
    756  return IPC_OK();
    757 }
    758 
    759 #if !defined(XP_WIN)
    760 mozilla::ipc::IPCResult DocAccessibleParent::RecvAnnouncementEvent(
    761    const uint64_t& aID, const nsAString& aAnnouncement,
    762    const uint16_t& aPriority) {
    763  ACQUIRE_ANDROID_LOCK
    764  if (mShutdown) {
    765    return IPC_OK();
    766  }
    767 
    768  RemoteAccessible* target = GetAccessible(aID);
    769  if (!target) {
    770    NS_ERROR("no proxy for event!");
    771    return IPC_OK();
    772  }
    773 
    774 #  if defined(ANDROID)
    775  PlatformAnnouncementEvent(target, aAnnouncement, aPriority);
    776 #  endif
    777 
    778  if (!nsCoreUtils::AccEventObserversExist()) {
    779    return IPC_OK();
    780  }
    781 
    782  xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
    783  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
    784  RefPtr<xpcAccAnnouncementEvent> event = new xpcAccAnnouncementEvent(
    785      nsIAccessibleEvent::EVENT_ANNOUNCEMENT, xpcAcc, doc, nullptr, false,
    786      aAnnouncement, aPriority);
    787  nsCoreUtils::DispatchAccEvent(std::move(event));
    788 
    789  return IPC_OK();
    790 }
    791 #endif  // !defined(XP_WIN)
    792 
    793 mozilla::ipc::IPCResult DocAccessibleParent::RecvTextSelectionChangeEvent(
    794    const uint64_t& aID, nsTArray<TextRangeData>&& aSelection) {
    795  ACQUIRE_ANDROID_LOCK
    796  if (mShutdown) {
    797    return IPC_OK();
    798  }
    799 
    800  RemoteAccessible* target = GetAccessible(aID);
    801  if (!target) {
    802    NS_ERROR("no proxy for event!");
    803    return IPC_OK();
    804  }
    805 
    806  mTextSelections.ClearAndRetainStorage();
    807  mTextSelections.AppendElements(aSelection);
    808 
    809 #ifdef MOZ_WIDGET_COCOA
    810  AutoTArray<TextRange, 1> ranges;
    811  SelectionRanges(&ranges);
    812  PlatformTextSelectionChangeEvent(target, ranges);
    813 #else
    814  PlatformEvent(target, nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED);
    815 #endif
    816 
    817  if (!nsCoreUtils::AccEventObserversExist()) {
    818    return IPC_OK();
    819  }
    820  xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(target);
    821  xpcAccessibleDocument* doc = nsAccessibilityService::GetXPCDocument(this);
    822  nsINode* node = nullptr;
    823  bool fromUser = true;  // XXX fix me
    824  RefPtr<xpcAccEvent> event =
    825      new xpcAccEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED, xpcAcc,
    826                      doc, node, fromUser);
    827  nsCoreUtils::DispatchAccEvent(std::move(event));
    828 
    829  return IPC_OK();
    830 }
    831 
    832 mozilla::ipc::IPCResult DocAccessibleParent::RecvRoleChangedEvent(
    833    const a11y::role& aRole, const uint8_t& aRoleMapEntryIndex) {
    834  ACQUIRE_ANDROID_LOCK
    835  if (mShutdown) {
    836    return IPC_OK();
    837  }
    838  if (!aria::IsRoleMapIndexValid(aRoleMapEntryIndex)) {
    839    MOZ_ASSERT_UNREACHABLE("Invalid role map entry index");
    840    return IPC_FAIL(this, "Invalid role map entry index");
    841  }
    842 
    843  mNativeRole = aRole;
    844  mRoleMapEntryIndex = aRoleMapEntryIndex;
    845 
    846 #ifdef MOZ_WIDGET_COCOA
    847  PlatformRoleChangedEvent(this, aRole, aRoleMapEntryIndex);
    848 #endif
    849 
    850  return IPC_OK();
    851 }
    852 
    853 mozilla::ipc::IPCResult DocAccessibleParent::RecvBindChildDoc(
    854    NotNull<PDocAccessibleParent*> aChildDoc, const uint64_t& aID) {
    855  ACQUIRE_ANDROID_LOCK
    856  // One document should never directly be the child of another.
    857  // We should always have at least an outer doc accessible in between.
    858  MOZ_ASSERT(aID);
    859  if (!aID) return IPC_FAIL(this, "ID is 0!");
    860 
    861  if (mShutdown) {
    862    return IPC_OK();
    863  }
    864 
    865  MOZ_ASSERT(CheckDocTree());
    866 
    867  auto childDoc = static_cast<DocAccessibleParent*>(aChildDoc.get());
    868  childDoc->Unbind();
    869  ipc::IPCResult result = AddChildDoc(childDoc, aID, false);
    870  MOZ_ASSERT(result);
    871  MOZ_ASSERT(CheckDocTree());
    872 #ifdef DEBUG
    873  if (!result) {
    874    return result;
    875  }
    876 #else
    877  result = IPC_OK();
    878 #endif
    879 
    880  return result;
    881 }
    882 
    883 ipc::IPCResult DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
    884                                                uint64_t aParentID,
    885                                                bool aCreating) {
    886  // We do not use GetAccessible here because we want to be sure to not get the
    887  // document it self.
    888  ProxyEntry* e = mAccessibles.GetEntry(aParentID);
    889  if (!e) {
    890 #ifndef FUZZING_SNAPSHOT
    891    // This diagnostic assert and the one down below expect a well-behaved
    892    // child process. In IPC fuzzing, we directly fuzz parameters of each
    893    // method over IPDL and the asserts are not valid under these conditions.
    894    MOZ_DIAGNOSTIC_CRASH("Binding to nonexistent proxy!");
    895 #endif
    896    return IPC_FAIL(this, "binding to nonexistant proxy!");
    897  }
    898 
    899  RemoteAccessible* outerDoc = e->mProxy;
    900  MOZ_ASSERT(outerDoc);
    901 
    902  // OuterDocAccessibles are expected to only have a document as a child.
    903  // However for compatibility we tolerate replacing one document with another
    904  // here.
    905  if (!outerDoc->IsOuterDoc() || outerDoc->ChildCount() > 1 ||
    906      (outerDoc->ChildCount() == 1 && !outerDoc->RemoteChildAt(0)->IsDoc())) {
    907 #ifndef FUZZING_SNAPSHOT
    908    MOZ_DIAGNOSTIC_CRASH("Binding to parent that isn't a valid OuterDoc!");
    909 #endif
    910    return IPC_FAIL(this, "Binding to parent that isn't a valid OuterDoc!");
    911  }
    912 
    913  if (outerDoc->ChildCount() == 1) {
    914    MOZ_ASSERT(outerDoc->RemoteChildAt(0)->AsDoc());
    915    outerDoc->RemoteChildAt(0)->AsDoc()->Unbind();
    916  }
    917 
    918  aChildDoc->SetParent(outerDoc);
    919  outerDoc->SetChildDoc(aChildDoc);
    920  mChildDocs.AppendElement(aChildDoc->mActorID);
    921 
    922  if (aCreating) {
    923    ProxyCreated(aChildDoc);
    924  }
    925 
    926  if (aChildDoc->IsTopLevelInContentProcess()) {
    927    // aChildDoc is an embedded document in a different content process to
    928    // this document.
    929 #if defined(XP_WIN)
    930    if (nsWinUtils::IsWindowEmulationStarted()) {
    931      aChildDoc->SetEmulatedWindowHandle(mEmulatedWindowHandle);
    932    }
    933 #endif  // defined(XP_WIN)
    934    // We need to fire a reorder event on the outer doc accessible.
    935    // For same-process documents, this is fired by the content process, but
    936    // this isn't possible when the document is in a different process to its
    937    // embedder.
    938    // FireEvent fires both OS and XPCOM events.
    939    FireEvent(outerDoc, nsIAccessibleEvent::EVENT_REORDER);
    940  }
    941 
    942  return IPC_OK();
    943 }
    944 
    945 ipc::IPCResult DocAccessibleParent::AddChildDoc(
    946    dom::BrowserBridgeParent* aBridge) {
    947  MOZ_ASSERT(aBridge->GetEmbedderAccessibleDoc() == this);
    948  uint64_t parentId = aBridge->GetEmbedderAccessibleId();
    949  MOZ_ASSERT(parentId);
    950  if (!mAccessibles.GetEntry(parentId)) {
    951    // Sometimes, this gets called before the embedder sends us the
    952    // OuterDocAccessible. We must add the child when the OuterDocAccessible
    953    // gets created later.
    954    mPendingOOPChildDocs.Insert(aBridge);
    955    return IPC_OK();
    956  }
    957  return AddChildDoc(aBridge->GetDocAccessibleParent(), parentId,
    958                     /* aCreating */ false);
    959 }
    960 
    961 mozilla::ipc::IPCResult DocAccessibleParent::RecvShutdown() {
    962  ACQUIRE_ANDROID_LOCK
    963  Destroy();
    964 
    965  auto mgr = static_cast<dom::BrowserParent*>(Manager());
    966  if (!mgr->IsDestroyed()) {
    967    if (!PDocAccessibleParent::Send__delete__(this)) {
    968      return IPC_FAIL_NO_REASON(mgr);
    969    }
    970  }
    971 
    972  return IPC_OK();
    973 }
    974 
    975 void DocAccessibleParent::Destroy() {
    976  // If we are already shutdown that is because our containing tab parent is
    977  // shutting down in which case we don't need to do anything.
    978  if (mShutdown) {
    979    return;
    980  }
    981 
    982  mShutdown = true;
    983  mBrowsingContext = nullptr;
    984 
    985 #ifdef ANDROID
    986  if (FocusMgr() && FocusMgr()->IsFocusedRemoteDoc(this)) {
    987    FocusMgr()->SetFocusedRemoteDoc(nullptr);
    988  }
    989 #endif
    990 
    991  MOZ_DIAGNOSTIC_ASSERT(LiveDocs().Contains(mActorID));
    992  uint32_t childDocCount = mChildDocs.Length();
    993  for (uint32_t i = 0; i < childDocCount; i++) {
    994    for (uint32_t j = i + 1; j < childDocCount; j++) {
    995      MOZ_DIAGNOSTIC_ASSERT(mChildDocs[i] != mChildDocs[j]);
    996    }
    997  }
    998 
    999  // XXX This indirection through the hash map of live documents shouldn't be
   1000  // needed, but be paranoid for now.
   1001  int32_t actorID = mActorID;
   1002  for (uint32_t i = childDocCount - 1; i < childDocCount; i--) {
   1003    DocAccessibleParent* thisDoc = LiveDocs().Get(actorID);
   1004    MOZ_ASSERT(thisDoc);
   1005    if (!thisDoc) {
   1006      return;
   1007    }
   1008 
   1009    thisDoc->ChildDocAt(i)->Destroy();
   1010  }
   1011 
   1012  for (auto iter = mAccessibles.Iter(); !iter.Done(); iter.Next()) {
   1013    RemoteAccessible* acc = iter.Get()->mProxy;
   1014    MOZ_ASSERT(acc != this);
   1015    if (acc->IsTable()) {
   1016      CachedTableAccessible::Invalidate(acc);
   1017    }
   1018    ProxyDestroyed(acc);
   1019    // mAccessibles owns acc, so removing it deletes acc.
   1020    iter.Remove();
   1021  }
   1022 
   1023  DocAccessibleParent* thisDoc = LiveDocs().Get(actorID);
   1024  MOZ_ASSERT(thisDoc);
   1025  if (!thisDoc) {
   1026    return;
   1027  }
   1028 
   1029  mChildren.Clear();
   1030  // The code above should have already completely cleared these, but to be
   1031  // extra safe make sure they are cleared here.
   1032  thisDoc->mAccessibles.Clear();
   1033  thisDoc->mChildDocs.Clear();
   1034 
   1035  DocManager::NotifyOfRemoteDocShutdown(thisDoc);
   1036  thisDoc = LiveDocs().Get(actorID);
   1037  MOZ_ASSERT(thisDoc);
   1038  if (!thisDoc) {
   1039    return;
   1040  }
   1041 
   1042  ProxyDestroyed(thisDoc);
   1043  thisDoc = LiveDocs().Get(actorID);
   1044  MOZ_ASSERT(thisDoc);
   1045  if (!thisDoc) {
   1046    return;
   1047  }
   1048 
   1049  if (IsTopLevel()) {
   1050    GetAccService()->RemoteDocShutdown(this);
   1051  } else {
   1052    Unbind();
   1053  }
   1054 }
   1055 
   1056 void DocAccessibleParent::ActorDestroy(ActorDestroyReason aWhy) {
   1057  MOZ_ASSERT(CheckDocTree());
   1058  if (!mShutdown) {
   1059    ACQUIRE_ANDROID_LOCK
   1060    Destroy();
   1061  }
   1062 }
   1063 
   1064 DocAccessibleParent* DocAccessibleParent::ParentDoc() const {
   1065  if (RemoteAccessible* parent = RemoteParent()) {
   1066    return parent->Document();
   1067  }
   1068  return nullptr;
   1069 }
   1070 
   1071 bool DocAccessibleParent::CheckDocTree() const {
   1072  size_t childDocs = mChildDocs.Length();
   1073  for (size_t i = 0; i < childDocs; i++) {
   1074    const DocAccessibleParent* childDoc = ChildDocAt(i);
   1075    if (!childDoc || childDoc->ParentDoc() != this) return false;
   1076 
   1077    if (!childDoc->CheckDocTree()) {
   1078      return false;
   1079    }
   1080  }
   1081 
   1082  return true;
   1083 }
   1084 
   1085 xpcAccessibleGeneric* DocAccessibleParent::GetXPCAccessible(
   1086    RemoteAccessible* aProxy) {
   1087  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
   1088  MOZ_ASSERT(doc);
   1089 
   1090  return doc->GetAccessible(aProxy);
   1091 }
   1092 
   1093 #if defined(XP_WIN)
   1094 void DocAccessibleParent::MaybeInitWindowEmulation() {
   1095  if (!nsWinUtils::IsWindowEmulationStarted()) {
   1096    return;
   1097  }
   1098 
   1099  // XXX get the bounds from the browserParent instead of poking at accessibles
   1100  // which might not exist yet.
   1101  LocalAccessible* outerDoc = OuterDocOfRemoteBrowser();
   1102  if (!outerDoc) {
   1103    return;
   1104  }
   1105 
   1106  RootAccessible* rootDocument = outerDoc->RootAccessible();
   1107  MOZ_ASSERT(rootDocument);
   1108 
   1109  bool isActive = true;
   1110  LayoutDeviceIntRect rect(CW_USEDEFAULT, CW_USEDEFAULT, 0, 0);
   1111  if (Compatibility::IsDolphin()) {
   1112    rect = Bounds();
   1113    LayoutDeviceIntRect rootRect = rootDocument->Bounds();
   1114    rect.MoveToX(rootRect.X() - rect.X());
   1115    rect.MoveToY(rect.Y() - rootRect.Y());
   1116 
   1117    auto browserParent = static_cast<dom::BrowserParent*>(Manager());
   1118    isActive = browserParent->GetDocShellIsActive();
   1119  }
   1120 
   1121  // onCreate is guaranteed to be called synchronously by
   1122  // nsWinUtils::CreateNativeWindow, so this reference isn't really necessary.
   1123  // However, static analysis complains without it.
   1124  RefPtr<DocAccessibleParent> thisRef = this;
   1125  nsWinUtils::NativeWindowCreateProc onCreate([thisRef](HWND aHwnd) -> void {
   1126    ::SetPropW(aHwnd, kPropNameDocAccParent,
   1127               reinterpret_cast<HANDLE>(thisRef.get()));
   1128    thisRef->SetEmulatedWindowHandle(aHwnd);
   1129  });
   1130 
   1131  HWND parentWnd = reinterpret_cast<HWND>(rootDocument->GetNativeWindow());
   1132  DebugOnly<HWND> hWnd = nsWinUtils::CreateNativeWindow(
   1133      kClassNameTabContent, parentWnd, rect.X(), rect.Y(), rect.Width(),
   1134      rect.Height(), isActive, &onCreate);
   1135  MOZ_ASSERT(hWnd);
   1136 }
   1137 
   1138 void DocAccessibleParent::SetEmulatedWindowHandle(HWND aWindowHandle) {
   1139  if (!aWindowHandle && mEmulatedWindowHandle && IsTopLevel()) {
   1140    ::DestroyWindow(mEmulatedWindowHandle);
   1141  }
   1142  mEmulatedWindowHandle = aWindowHandle;
   1143 }
   1144 #endif  // defined(XP_WIN)
   1145 
   1146 mozilla::ipc::IPCResult DocAccessibleParent::RecvFocusEvent(
   1147    const uint64_t& aID, const LayoutDeviceIntRect& aCaretRect) {
   1148  ACQUIRE_ANDROID_LOCK
   1149  if (mShutdown) {
   1150    return IPC_OK();
   1151  }
   1152 
   1153  RemoteAccessible* proxy = GetAccessible(aID);
   1154  if (!proxy) {
   1155    NS_ERROR("no proxy for event!");
   1156    return IPC_OK();
   1157  }
   1158 
   1159 #ifdef ANDROID
   1160  if (FocusMgr()) {
   1161    FocusMgr()->SetFocusedRemoteDoc(this);
   1162  }
   1163 #endif
   1164 
   1165  mFocus = aID;
   1166  mCaretRect = aCaretRect;
   1167  PlatformFocusEvent(proxy);
   1168 
   1169  if (!nsCoreUtils::AccEventObserversExist()) {
   1170    return IPC_OK();
   1171  }
   1172 
   1173  xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
   1174  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
   1175  nsINode* node = nullptr;
   1176  bool fromUser = true;  // XXX fix me
   1177  RefPtr<xpcAccEvent> event = new xpcAccEvent(nsIAccessibleEvent::EVENT_FOCUS,
   1178                                              xpcAcc, doc, node, fromUser);
   1179  nsCoreUtils::DispatchAccEvent(std::move(event));
   1180 
   1181  return IPC_OK();
   1182 }
   1183 
   1184 LayoutDeviceIntRect DocAccessibleParent::GetCachedCaretRect() {
   1185  LayoutDeviceIntRect caretRect = mCaretRect;
   1186  if (!caretRect.IsEmpty()) {
   1187    // Reapply doc offset to the caret rect.
   1188    LayoutDeviceIntRect docRect = Bounds();
   1189    caretRect.MoveBy(docRect.X(), docRect.Y());
   1190  }
   1191 
   1192  return caretRect;
   1193 }
   1194 
   1195 void DocAccessibleParent::SelectionRanges(nsTArray<TextRange>* aRanges) const {
   1196  aRanges->SetCapacity(mTextSelections.Length());
   1197  for (const auto& data : mTextSelections) {
   1198    // Selection ranges should usually be in sync with the tree. However, tree
   1199    // and selection updates happen using separate IPDL calls, so it's possible
   1200    // for a client selection query to arrive between them. Thus, we validate
   1201    // the Accessibles and offsets here.
   1202    auto* startAcc =
   1203        const_cast<RemoteAccessible*>(GetAccessible(data.StartID()));
   1204    auto* endAcc = const_cast<RemoteAccessible*>(GetAccessible(data.EndID()));
   1205    if (!startAcc || !endAcc) {
   1206      continue;
   1207    }
   1208    // Offset 0 is always valid, even if the container is empty.
   1209    if (data.StartOffset() > 0) {
   1210      uint32_t startCount = startAcc->CharacterCount();
   1211      if (data.StartOffset() > static_cast<int32_t>(startCount)) {
   1212        continue;
   1213      }
   1214    }
   1215    if (data.EndOffset() > 0) {
   1216      uint32_t endCount = endAcc->CharacterCount();
   1217      if (data.EndOffset() > static_cast<int32_t>(endCount)) {
   1218        continue;
   1219      }
   1220    }
   1221    aRanges->AppendElement(TextRange(const_cast<DocAccessibleParent*>(this),
   1222                                     startAcc, data.StartOffset(), endAcc,
   1223                                     data.EndOffset()));
   1224  }
   1225 }
   1226 
   1227 Accessible* DocAccessibleParent::FocusedChild() {
   1228  LocalAccessible* outerDoc = OuterDocOfRemoteBrowser();
   1229  if (!outerDoc) {
   1230    return nullptr;
   1231  }
   1232 
   1233  RootAccessible* rootDocument = outerDoc->RootAccessible();
   1234  return rootDocument->FocusedChild();
   1235 }
   1236 
   1237 void DocAccessibleParent::URL(nsACString& aURL) const {
   1238  if (!mBrowsingContext) {
   1239    return;
   1240  }
   1241  nsCOMPtr<nsIURI> uri = mBrowsingContext->GetCurrentURI();
   1242  if (!uri) {
   1243    return;
   1244  }
   1245  // Let's avoid treating too long URI in the main process for avoiding
   1246  // memory fragmentation as far as possible.
   1247  if (uri->SchemeIs("data") || uri->SchemeIs("blob")) {
   1248    return;
   1249  }
   1250  nsCOMPtr<nsIIOService> io = mozilla::components::IO::Service();
   1251  if (NS_WARN_IF(!io)) {
   1252    return;
   1253  }
   1254  nsCOMPtr<nsIURI> exposableURI;
   1255  if (NS_FAILED(io->CreateExposableURI(uri, getter_AddRefs(exposableURI))) ||
   1256      MOZ_UNLIKELY(!exposableURI)) {
   1257    return;
   1258  }
   1259  exposableURI->GetSpec(aURL);
   1260 }
   1261 
   1262 void DocAccessibleParent::URL(nsAString& aURL) const {
   1263  nsAutoCString url;
   1264  URL(url);
   1265  CopyUTF8toUTF16(url, aURL);
   1266 }
   1267 
   1268 void DocAccessibleParent::MimeType(nsAString& aMime) const {
   1269  if (mCachedFields) {
   1270    mCachedFields->GetAttribute(CacheKey::MimeType, aMime);
   1271  }
   1272 }
   1273 
   1274 Relation DocAccessibleParent::RelationByType(RelationType aType) const {
   1275  // If the accessible is top-level, provide the NODE_CHILD_OF relation so that
   1276  // MSAA clients can easily get to true parent instead of getting to oleacc's
   1277  // ROLE_WINDOW accessible when window emulation is enabled which will prevent
   1278  // us from going up further (because it is system generated and has no idea
   1279  // about the hierarchy above it).
   1280  if (aType == RelationType::NODE_CHILD_OF && IsTopLevel()) {
   1281    return Relation(Parent());
   1282  }
   1283 
   1284  return RemoteAccessible::RelationByType(aType);
   1285 }
   1286 
   1287 DocAccessibleParent* DocAccessibleParent::GetFrom(
   1288    dom::BrowsingContext* aBrowsingContext) {
   1289  if (!aBrowsingContext) {
   1290    return nullptr;
   1291  }
   1292 
   1293  dom::BrowserParent* bp = aBrowsingContext->Canonical()->GetBrowserParent();
   1294  if (!bp) {
   1295    return nullptr;
   1296  }
   1297 
   1298  const ManagedContainer<PDocAccessibleParent>& docs =
   1299      bp->ManagedPDocAccessibleParent();
   1300  for (auto* key : docs) {
   1301    // Iterate over our docs until we find one with a browsing
   1302    // context that matches the one we passed in. Return that
   1303    // document.
   1304    auto* doc = static_cast<a11y::DocAccessibleParent*>(key);
   1305    if (doc->GetBrowsingContext() == aBrowsingContext) {
   1306      return doc;
   1307    }
   1308  }
   1309 
   1310  return nullptr;
   1311 }
   1312 
   1313 size_t DocAccessibleParent::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) {
   1314  size_t size = 0;
   1315 
   1316  size += RemoteAccessible::SizeOfExcludingThis(aMallocSizeOf);
   1317 
   1318  size += mReverseRelations.ShallowSizeOfExcludingThis(aMallocSizeOf);
   1319  for (auto i = mReverseRelations.Iter(); !i.Done(); i.Next()) {
   1320    size += i.Data().ShallowSizeOfExcludingThis(aMallocSizeOf);
   1321    for (auto j = i.Data().Iter(); !j.Done(); j.Next()) {
   1322      size += j.Data().ShallowSizeOfExcludingThis(aMallocSizeOf);
   1323    }
   1324  }
   1325 
   1326  size += mOnScreenAccessibles.ShallowSizeOfExcludingThis(aMallocSizeOf);
   1327 
   1328  size += mChildDocs.ShallowSizeOfExcludingThis(aMallocSizeOf);
   1329 
   1330  size += mAccessibles.ShallowSizeOfExcludingThis(aMallocSizeOf);
   1331  for (auto i = mAccessibles.Iter(); !i.Done(); i.Next()) {
   1332    size += i.Get()->mProxy->SizeOfIncludingThis(aMallocSizeOf);
   1333  }
   1334 
   1335  size += mPendingOOPChildDocs.ShallowSizeOfExcludingThis(aMallocSizeOf);
   1336 
   1337  // The mTextSelections array contains structs of integers.  We can count them
   1338  // by counting the size of the array - there's no deep structure here.
   1339  size += mTextSelections.ShallowSizeOfExcludingThis(aMallocSizeOf);
   1340 
   1341  return size;
   1342 }
   1343 
   1344 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOfAccessibilityCache);
   1345 
   1346 NS_IMETHODIMP
   1347 DocAccessibleParent::CollectReports(nsIHandleReportCallback* aHandleReport,
   1348                                    nsISupports* aData, bool aAnon) {
   1349  nsAutoCString path;
   1350 
   1351  if (aAnon) {
   1352    path = nsPrintfCString("explicit/a11y/cache(%" PRIu64 ")", mActorID);
   1353  } else {
   1354    nsCString url;
   1355    URL(url);
   1356    url.ReplaceChar(
   1357        '/', '\\');  // Tell the memory reporter this is not a path seperator.
   1358    path = nsPrintfCString("explicit/a11y/cache(%s)", url.get());
   1359  }
   1360 
   1361  aHandleReport->Callback(
   1362      /* process */ ""_ns, path, KIND_HEAP, UNITS_BYTES,
   1363      SizeOfIncludingThis(MallocSizeOfAccessibilityCache),
   1364      nsLiteralCString("Size of the accessability cache for this document."),
   1365      aData);
   1366 
   1367  return NS_OK;
   1368 }
   1369 
   1370 NS_IMPL_ISUPPORTS(DocAccessibleParent, nsIMemoryReporter);
   1371 
   1372 }  // namespace a11y
   1373 }  // namespace mozilla