Commit 284e8a92 authored by James Teh's avatar James Teh
Browse files

Bug 1779578 part 2: Split serialization of a11y subtrees across multiple IPDL...

Bug 1779578 part 2: Split serialization of a11y subtrees across multiple IPDL calls if we are likely to exceed the IPDL maximum message size. (ESR)  a=RyanVM

In the content process, we simply split into multiple calls when the number of Accessibles exceeds our maximum.
The maximum is calculated to allow for every Accessible to consume 2 KB in the IPDL message.
Currently, this means we split every 131072 Accessibles.
Of course, we could still exceed this IPDL message size if one or more  Accessibles consumed a lot more than this; e.g. many labels longer than 2 KB.
However, this seems unlikely in the real world.
If this turns out to be a problem, we'll need to count the actual size of the serialized data for each Accessible.
For example, we could use AccAttributes::SizeOfExcludingThis, though that isn't exactly the serialized size.
I worry though that such data structure traversal could get expensive at scale.

In the parent process, we defer attaching the root of the new subtree to its parent until the final call.
This is achieved by saving the root during the first call and using that to attach and fire events in the final call.

Original Revision: https://phabricator.services.mozilla.com/D184367

Depends on D192439

Differential Revision: https://phabricator.services.mozilla.com/D192440
parent 8ca15e78
Loading
Loading
Loading
Loading
+19 −2
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "chrome/common/ipc_channel.h"
#include "mozilla/a11y/DocAccessibleChildBase.h"
#include "mozilla/a11y/CacheConstants.h"
#include "mozilla/a11y/RemoteAccessible.h"
@@ -76,14 +77,30 @@ void DocAccessibleChildBase::InsertIntoIpcTree(LocalAccessible* aChild,
                                               bool aSuppressShowEvent) {
  nsTArray<LocalAccessible*> shownTree;
  FlattenTree(aChild, shownTree);
  nsTArray<AccessibleData> data(shownTree.Length());
  uint32_t totalAccs = shownTree.Length();
  // Exceeding the IPDL maximum message size will cause a crash. Try to avoid
  // this by only including kMaxAccsPerMessage Accessibels in a single IPDL
  // call. If there are Accessibles beyond this, they will be split across
  // multiple calls.
  constexpr uint32_t kMaxAccsPerMessage =
      IPC::Channel::kMaximumMessageSize / (2 * 1024);
  nsTArray<AccessibleData> data(std::min(kMaxAccsPerMessage, totalAccs));
  for (LocalAccessible* child : shownTree) {
    if (data.Length() == kMaxAccsPerMessage) {
      if (ipc::ProcessChild::ExpectingShutdown()) {
        return;
      }
      SendShowEvent(data, aSuppressShowEvent, false, false);
      data.ClearAndRetainStorage();
    }
    data.AppendElement(SerializeAcc(child));
  }
  if (ipc::ProcessChild::ExpectingShutdown()) {
    return;
  }
  SendShowEvent(data, aSuppressShowEvent, false);
  if (!data.IsEmpty()) {
    SendShowEvent(data, aSuppressShowEvent, true, false);
  }
}

void DocAccessibleChildBase::ShowEvent(AccShowEvent* aShowEvent) {
+38 −3
Original line number Diff line number Diff line
@@ -84,7 +84,7 @@ void DocAccessibleParent::SetBrowsingContext(

mozilla::ipc::IPCResult DocAccessibleParent::RecvShowEvent(
    nsTArray<AccessibleData>&& aNewTree, const bool& aEventSuppressed,
    const bool& aFromUser) {
    const bool& aComplete, const bool& aFromUser) {
  ACQUIRE_ANDROID_LOCK
  if (mShutdown) return IPC_OK();

@@ -128,16 +128,51 @@ mozilla::ipc::IPCResult DocAccessibleParent::RecvShowEvent(
      // This shouldn't happen.
      return IPC_FAIL(this, "failed to add children");
    }
    if (!root) {
    if (!root && !mPendingShowChild) {
      // This is the first Accessible, which is the root of the shown subtree.
      root = child;
      rootParent = parent;
    }
    // If this show event has been split across multiple messages and this is
    // not the last message, don't attach the shown root to the tree yet.
    // Otherwise, clients might crawl the incomplete subtree and they won't get
    // mutation events for the remaining pieces.
    if (aComplete || root != child) {
      AttachChild(parent, childIdx, child);
    }
  }

  MOZ_ASSERT(CheckDocTree());

  if (!aComplete && !mPendingShowChild) {
    // This is the first message for a show event split across multiple
    // messages. Save the show target for subsequent messages and return.
    const auto& accData = aNewTree[0];
    mPendingShowChild = accData.ID();
    mPendingShowParent = accData.ParentID();
    mPendingShowIndex = accData.IndexInParent();
    return IPC_OK();
  }
  if (!aComplete) {
    // This show event has been split into multiple messages, but this is
    // neither the first nor the last message. There's nothing more to do here.
    return IPC_OK();
  }
  MOZ_ASSERT(aComplete);
  if (mPendingShowChild) {
    // This is the last message for a show event split across multiple
    // messages. Retrieve the saved show target, attach it to the tree and fire
    // an event if appropriate.
    rootParent = GetAccessible(mPendingShowParent);
    MOZ_ASSERT(rootParent);
    root = GetAccessible(mPendingShowChild);
    MOZ_ASSERT(root);
    AttachChild(rootParent, mPendingShowIndex, root);
    mPendingShowChild = 0;
    mPendingShowParent = 0;
    mPendingShowIndex = 0;
  }

  // Just update, no events.
  if (aEventSuppressed) {
    return IPC_OK();
+4 −1
Original line number Diff line number Diff line
@@ -108,7 +108,7 @@ class DocAccessibleParent : public RemoteAccessible,

  virtual mozilla::ipc::IPCResult RecvShowEvent(
      nsTArray<AccessibleData>&& aNewTree, const bool& aEventSuppressed,
      const bool& aFromUser) override;
      const bool& aComplete, const bool& aFromUser) override;
  virtual mozilla::ipc::IPCResult RecvHideEvent(const uint64_t& aRootID,
                                                const bool& aFromUser) override;
  mozilla::ipc::IPCResult RecvStateChangeEvent(const uint64_t& aID,
@@ -407,6 +407,9 @@ class DocAccessibleParent : public RemoteAccessible,
   * proxy object so we can't use a real map.
   */
  nsTHashtable<ProxyEntry> mAccessibles;
  uint64_t mPendingShowChild = 0;
  uint64_t mPendingShowParent = 0;
  uint32_t mPendingShowIndex = 0;
  nsTHashSet<uint64_t> mMovingIDs;
  uint64_t mActorID;
  bool mTopLevel;
+1 −1
Original line number Diff line number Diff line
@@ -71,7 +71,7 @@ parent:
   */
  async Event(uint64_t aID, uint32_t type);
  async ShowEvent(AccessibleData[] aNewTree, bool aEventSuppressed,
                  bool aFromuser);
                  bool aComplete, bool aFromuser);
  async HideEvent(uint64_t aRootID, bool aFromUser);
  async StateChangeEvent(uint64_t aID, uint64_t aState, bool aEnabled);
  async CaretMoveEvent(uint64_t aID, int32_t aOffset,
+1 −1
Original line number Diff line number Diff line
@@ -56,7 +56,7 @@ parent:
   */
  async Event(uint64_t aID, uint32_t type);
  async ShowEvent(AccessibleData[] aNewTree, bool aEventSuppressed,
                  bool aFromuser);
                  bool aComplete, bool aFromuser);
  async HideEvent(uint64_t aRootID, bool aFromUser);
  async StateChangeEvent(uint64_t aID, uint64_t aState, bool aEnabled);
  async CaretMoveEvent(uint64_t aID, LayoutDeviceIntRect aCaretRect,
Loading