Bug 1758689: Create and maintain a viewport cache for hittesting r=Jamie,emilio

This hittesting approach is borrowed from our Android implementation.
We maintain a cache of ID's for accessibles which are visible in the viewport.
This cache is created with a call to `nsLayoutUtils::GetFramesForArea`, which
returns frames in the viewport, in hittesting order.

In the parent process, we walk through the cache, keeping track of accs whose
bounds contain the hittesting point. Depending on if we're searching for the
direct child, or the deepest child, we walk the entire cache or return the
first match.

Each document (in the content process) maintains a dirty bit, which gets set
any time an acc it contains bundles either a text update, or an update that
affects bounds. We check whether this bit is set in `DidRefresh` after getting
a notification from our post-refresh observer. If that bit is set, we
queue a cache update for the `::Viewport` domain on the current document.

Because this cache depends on the viewport being painted, we include the
`IgnorePaintSuppression` flag in our `GetFramesForArea` call. This ensures
the display lists are built before the page finishes loading.

Differential Revision: https://phabricator.services.mozilla.com/D147225
This commit is contained in:
Morgan Rae Reschenberg
2022-06-13 22:28:36 +00:00
parent 232e79554b
commit b6ecba1f9d
13 changed files with 379 additions and 6 deletions

View File

@@ -83,7 +83,8 @@ NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(NotificationController, Release)
void NotificationController::Shutdown() {
if (mObservingState != eNotObservingRefresh &&
mPresShell->RemoveRefreshObserver(this, FlushType::Display)) {
mPresShell->RemoveRefreshObserver(this, FlushType::Display) &&
mPresShell->RemovePostRefreshObserver(this)) {
mObservingState = eNotObservingRefresh;
}
@@ -453,7 +454,8 @@ void NotificationController::ScheduleProcessing() {
// asynchronously (after style and layout).
if (mObservingState == eNotObservingRefresh) {
if (mPresShell->AddRefreshObserver(this, FlushType::Display,
"Accessibility notifications")) {
"Accessibility notifications") &&
mPresShell->AddPostRefreshObserver(this)) {
mObservingState = eRefreshObserving;
}
}
@@ -981,11 +983,30 @@ void NotificationController::WillRefresh(mozilla::TimeStamp aTime) {
!mFocusEvent && mEvents.IsEmpty() && mTextHash.Count() == 0 &&
mHangingChildDocuments.IsEmpty() &&
mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) &&
mPresShell->RemoveRefreshObserver(this, FlushType::Display)) {
mPresShell->RemoveRefreshObserver(this, FlushType::Display) &&
mPresShell->RemovePostRefreshObserver(this)) {
mObservingState = eNotObservingRefresh;
}
}
void NotificationController::DidRefresh() {
if (IPCAccessibilityActive() && mDocument->IsViewportCacheDirty()) {
// It is now safe to send the viewport cache, because
// we know painting has finished.
RefPtr<AccAttributes> fields = mDocument->BundleFieldsForCache(
CacheDomain::Viewport, CacheUpdateType::Update);
if (fields->Count()) {
nsTArray<CacheData> data(1);
data.AppendElement(CacheData(0, fields));
MOZ_ASSERT(mDocument->IPCDoc());
mDocument->IPCDoc()->SendCache(CacheUpdateType::Update, data, true);
}
mDocument->SetViewportCacheDirty(false);
}
}
void NotificationController::EventMap::PutEvent(AccTreeMutationEvent* aEvent) {
EventType type = GetEventType(aEvent);
uint64_t addr = reinterpret_cast<uintptr_t>(aEvent->GetAccessible());