Skip to content

Commit

Permalink
[Paint Preview] Add Accessibility Support
Browse files Browse the repository at this point in the history
This adds a11y supoprt to the paint preview component. A follow-up CL
will add hooks to WebContentsAccessibility logic.

On capture, the AX tree is serialized to file. When the compositor is
initialzied, the AX tree is deserialized.

Bug: 1179326
Change-Id: Ib995dc3eaadeacec256daf6541e624d3fbf6f9e6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2698929
Commit-Queue: Mehran Mahmoudi <mahmoudi@chromium.org>
Reviewed-by: Yaron Friedman <yfriedman@chromium.org>
Reviewed-by: Tanya Gupta <tgupta@chromium.org>
Reviewed-by: Dominic Mazzoni <dmazzoni@chromium.org>
Reviewed-by: Henrique Nakashima <hnakashima@chromium.org>
Reviewed-by: Calder Kitagawa <ckitagawa@chromium.org>
Cr-Commit-Position: refs/heads/master@{#857905}
  • Loading branch information
Mehran Mahmoudi authored and Chromium LUCI CQ committed Feb 25, 2021
1 parent 1ce826c commit 21f8df5
Show file tree
Hide file tree
Showing 40 changed files with 563 additions and 123 deletions.
Expand Up @@ -14,6 +14,7 @@
import org.chromium.chrome.browser.flags.CachedFieldTrialParameter;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.page_annotations.PageAnnotationsServiceConfig;
import org.chromium.chrome.browser.paint_preview.StartupPaintPreviewHelper;
import org.chromium.chrome.browser.tab.state.ShoppingPersistedTabData;
import org.chromium.chrome.browser.tasks.ConditionalTabStripUtils;
import org.chromium.chrome.browser.tasks.ReturnToChromeExperimentsUtil;
Expand Down Expand Up @@ -98,6 +99,9 @@ public void cacheNativeFlags() {
ConditionalTabStripUtils.CONDITIONAL_TAB_STRIP_SESSION_TIME_MS,
PageAnnotationsServiceConfig.PAGE_ANNOTATIONS_BASE_URL,
ReturnToChromeExperimentsUtil.TAB_SWITCHER_ON_RETURN_MS,
ShoppingPersistedTabData.TIME_TO_LIVE_MS,
ShoppingPersistedTabData.DISPLAY_TIME_MS,
ShoppingPersistedTabData.STALE_TAB_THRESHOLD_SECONDS,
StartSurfaceConfiguration.HOME_BUTTON_ON_GRID_TAB_SWITCHER,
StartSurfaceConfiguration.NEW_SURFACE_FROM_HOME_BUTTON,
StartSurfaceConfiguration.OMNIBOX_FOCUSED_ON_NEW_TAB,
Expand All @@ -111,13 +115,11 @@ public void cacheNativeFlags() {
StartSurfaceConfiguration.TRENDY_ENDPOINT,
StartSurfaceConfiguration.TRENDY_FAILURE_MIN_PERIOD_MS,
StartSurfaceConfiguration.TRENDY_SUCCESS_MIN_PERIOD_MS,
StartupPaintPreviewHelper.ACCESSIBILITY_SUPPORT_PARAM,
TabContentManager.ALLOW_TO_REFETCH_TAB_THUMBNAIL_VARIATION,
TabUiFeatureUtilities.ENABLE_LAUNCH_BUG_FIX,
TabUiFeatureUtilities.ENABLE_LAUNCH_POLISH,
TabUiFeatureUtilities.ENABLE_SEARCH_CHIP,
ShoppingPersistedTabData.TIME_TO_LIVE_MS,
ShoppingPersistedTabData.DISPLAY_TIME_MS,
ShoppingPersistedTabData.STALE_TAB_THRESHOLD_SECONDS,
TabUiFeatureUtilities.ENABLE_PRICE_TRACKING,
TabUiFeatureUtilities.ENABLE_SEARCH_CHIP_ADAPTIVE,
TabUiFeatureUtilities.ZOOMING_MIN_MEMORY,
Expand Down
Expand Up @@ -12,6 +12,7 @@
import org.chromium.base.ApplicationStatus;
import org.chromium.base.Callback;
import org.chromium.base.supplier.Supplier;
import org.chromium.chrome.browser.flags.BooleanCachedFieldTrialParameter;
import org.chromium.chrome.browser.flags.CachedFeatureFlags;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.fullscreen.BrowserControlsManager;
Expand All @@ -35,6 +36,9 @@
* Glue code for the Paint Preview show-on-startup feature.
*/
public class StartupPaintPreviewHelper {
public static final BooleanCachedFieldTrialParameter ACCESSIBILITY_SUPPORT_PARAM =
new BooleanCachedFieldTrialParameter(ChromeFeatureList.PAINT_PREVIEW_SHOW_ON_STARTUP,
"has_accessibility_support", false);
/**
* Tracks whether a paint preview should be shown on tab restore. We use this to only attempt
* to display a paint preview on the first tab restoration that happens on Chrome startup when
Expand Down Expand Up @@ -126,8 +130,12 @@ private boolean preventShowOnRestore(Tab tab) {
*/
public static void showPaintPreviewOnRestore(Tab tab) {
if (!CachedFeatureFlags.isEnabled(ChromeFeatureList.PAINT_PREVIEW_SHOW_ON_STARTUP)
|| !sShouldShowOnRestore
|| ChromeAccessibilityUtil.get().isAccessibilityEnabled()) {
|| !sShouldShowOnRestore) {
return;
}

if (ChromeAccessibilityUtil.get().isAccessibilityEnabled()
&& !ACCESSIBILITY_SUPPORT_PARAM.getValue()) {
return;
}

Expand Down
1 change: 1 addition & 0 deletions chrome/browser/paint_preview/android/BUILD.gn
Expand Up @@ -35,6 +35,7 @@ android_library("java") {
"//chrome/browser/tab:java",
"//chrome/browser/tabmodel:java",
"//chrome/browser/ui/messages/android:java",
"//chrome/browser/util:java",
"//components/browser_ui/styles/android:java",
"//components/paint_preview/browser/android:java",
"//components/paint_preview/common/proto:proto_java",
Expand Down
Expand Up @@ -7,6 +7,7 @@
import org.chromium.base.task.PostTask;
import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
import org.chromium.components.paintpreview.player.PlayerManager;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.NavigationHandle;
Expand Down Expand Up @@ -105,6 +106,11 @@ public void onLinkClick(GURL url) {
removePaintPreviewDemo();
}

@Override
public boolean isAccessibilityEnabled() {
return ChromeAccessibilityUtil.get().isAccessibilityEnabled();
}

private class DemoPaintPreviewTabObserver extends EmptyTabObserver {
@Override
public void onDidStartNavigation(Tab tab, NavigationHandle navigationHandle) {
Expand Down
Expand Up @@ -23,6 +23,7 @@
import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManagerProvider;
import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
import org.chromium.components.paintpreview.player.PlayerManager;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.NavigationHandle;
Expand Down Expand Up @@ -284,6 +285,11 @@ public void onLinkClick(GURL url) {
remove(ExitCause.LINK_CLICKED);
}

@Override
public boolean isAccessibilityEnabled() {
return ChromeAccessibilityUtil.get().isAccessibilityEnabled();
}

@VisibleForTesting
TabObserver getTabObserverForTesting() {
return mStartupTabObserver;
Expand Down
Expand Up @@ -336,7 +336,7 @@ public static class TestCompositorDelegate implements PlayerCompositorDelegate {
parcel.setDataPosition(0);
UnguessableToken token = UnguessableToken.CREATOR.createFromParcel(parcel);
compositorListener.onCompositorReady(token, new UnguessableToken[] {token},
new int[] {500, 500}, new int[] {0, 0}, new int[] {0}, null, null);
new int[] {500, 500}, new int[] {0, 0}, new int[] {0}, null, null, 0);
}, 250);
}

Expand Down Expand Up @@ -408,5 +408,10 @@ public void onPullToRefresh() {}

@Override
public void onLinkClick(GURL url) {}

@Override
public boolean isAccessibilityEnabled() {
return false;
}
}
}
124 changes: 89 additions & 35 deletions chrome/browser/paint_preview/services/paint_preview_tab_service.cc
Expand Up @@ -17,7 +17,9 @@
#include "chrome/browser/paint_preview/services/paint_preview_tab_service_file_mixin.h"
#include "components/paint_preview/browser/file_manager.h"
#include "components/paint_preview/browser/warm_compositor.h"
#include "content/public/browser/browser_accessibility_state.h"
#include "content/public/browser/render_process_host.h"
#include "ui/accessibility/ax_mode.h"
#include "ui/gfx/geometry/rect.h"

#if defined(OS_ANDROID)
Expand Down Expand Up @@ -54,6 +56,18 @@ int TabIdFromDirectoryKey(const DirectoryKey& key) {

} // namespace

PaintPreviewTabService::TabServiceTask::TabServiceTask(
int tab_id,
const DirectoryKey& key,
int frame_tree_node_id,
content::GlobalFrameRoutingId frame_routing_id)
: tab_id_(tab_id),
key_(key),
frame_tree_node_id_(frame_tree_node_id),
frame_routing_id_(frame_routing_id) {}

PaintPreviewTabService::TabServiceTask::~TabServiceTask() = default;

PaintPreviewTabService::PaintPreviewTabService(
std::unique_ptr<PaintPreviewFileMixin> file_mixin,
std::unique_ptr<PaintPreviewPolicy> policy,
Expand Down Expand Up @@ -112,16 +126,27 @@ void PaintPreviewTabService::CaptureTab(int tab_id,
contents->IncrementCapturerCount(gfx::Size(), true);

auto file_manager = GetFileMixin()->GetFileManager();

auto key = file_manager->CreateKey(tab_id);
auto it = tasks_.emplace(
tab_id, std::make_unique<TabServiceTask>(
tab_id, key, contents->GetMainFrame()->GetFrameTreeNodeId(),
contents->GetMainFrame()->GetGlobalFrameRoutingId()));
if (!it.second) {
std::move(callback).Run(Status::kCaptureInProgress);
return;
}
it.first->second->SetCallback(std::move(callback).Then(
base::BindOnce(&PaintPreviewTabService::DeleteTask,
weak_ptr_factory_.GetWeakPtr(), tab_id)));

GetFileMixin()->GetTaskRunner()->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&FileManager::CreateOrGetDirectory,
GetFileMixin()->GetFileManager(), key, true),
base::BindOnce(&PaintPreviewTabService::CaptureTabInternal,
weak_ptr_factory_.GetWeakPtr(), tab_id, key,
contents->GetMainFrame()->GetFrameTreeNodeId(),
contents->GetMainFrame()->GetGlobalFrameRoutingId(),
std::move(callback)));
weak_ptr_factory_.GetWeakPtr(),
it.first->second->GetWeakPtr()));
}

void PaintPreviewTabService::TabClosed(int tab_id) {
Expand Down Expand Up @@ -214,6 +239,10 @@ PaintPreviewTabService::GetPathAndroid(JNIEnv* env) {
}
#endif // defined(OS_ANDROID)

void PaintPreviewTabService::DeleteTask(int tab_id) {
tasks_.erase(tab_id);
}

void PaintPreviewTabService::InitializeCache(
const base::flat_set<DirectoryKey>& in_use_keys) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Expand All @@ -227,26 +256,40 @@ void PaintPreviewTabService::InitializeCache(
}

void PaintPreviewTabService::CaptureTabInternal(
int tab_id,
const DirectoryKey& key,
int frame_tree_node_id,
content::GlobalFrameRoutingId frame_routing_id,
FinishedCallback callback,
base::WeakPtr<TabServiceTask> task,
const base::Optional<base::FilePath>& file_path) {
if (!task) {
return;
}

DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!file_path.has_value()) {
std::move(callback).Run(Status::kDirectoryCreationFailed);
task->OnCaptured(Status::kDirectoryCreationFailed);
return;
}
auto* contents =
content::WebContents::FromFrameTreeNodeId(frame_tree_node_id);
auto* rfh = content::RenderFrameHost::FromID(frame_routing_id);
content::WebContents::FromFrameTreeNodeId(task->frame_tree_node_id());
auto* rfh = content::RenderFrameHost::FromID(task->frame_routing_id());
if (!contents || !rfh || contents->IsBeingDestroyed() ||
contents->GetMainFrame() != rfh || !rfh->IsCurrent() ||
!rfh->IsRenderFrameCreated() || !rfh->IsRenderFrameLive()) {
std::move(callback).Run(Status::kWebContentsGone);
task->OnCaptured(Status::kWebContentsGone);
return;
}
if (content::BrowserAccessibilityState::GetInstance()
->IsAccessibleBrowser()) {
task->SetWaitForAccessibility();
contents->RequestAXTreeSnapshot(
base::BindOnce(&PaintPreviewFileMixin::WriteAXTreeUpdate,
GetFileMixin()->GetWeakPtr(), task->key(),
base::BindOnce(&PaintPreviewTabService::OnAXTreeWritten,
weak_ptr_factory_.GetWeakPtr(), task)),
ui::kAXModeWebContentsOnly,
/* exclude_offscreen= */ false,
/* max_nodes= */ 5000,
/* timeout= */ {});
}

CaptureParams capture_params;
capture_params.web_contents = contents;
capture_params.render_frame_host = rfh;
Expand All @@ -255,50 +298,61 @@ void PaintPreviewTabService::CaptureTabInternal(
capture_params.clip_rect = gfx::Rect();
capture_params.capture_links = true;
capture_params.max_per_capture_size = kMaxPerCaptureSizeBytes;
CapturePaintPreview(
capture_params,
base::BindOnce(&PaintPreviewTabService::OnCaptured,
weak_ptr_factory_.GetWeakPtr(), tab_id, key,
frame_tree_node_id, std::move(callback)));
CapturePaintPreview(capture_params,
base::BindOnce(&PaintPreviewTabService::OnCaptured,
weak_ptr_factory_.GetWeakPtr(), task));
}

void PaintPreviewTabService::OnAXTreeWritten(base::WeakPtr<TabServiceTask> task,
bool result) {
if (task) {
task->OnAXTreeWritten(result);
}
}

void PaintPreviewTabService::OnCaptured(
int tab_id,
const DirectoryKey& key,
int frame_tree_node_id,
FinishedCallback callback,
base::WeakPtr<TabServiceTask> task,
PaintPreviewBaseService::CaptureStatus status,
std::unique_ptr<CaptureResult> result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!task) {
return;
}

auto* web_contents =
content::WebContents::FromFrameTreeNodeId(frame_tree_node_id);
content::WebContents::FromFrameTreeNodeId(task->frame_tree_node_id());
if (web_contents)
web_contents->DecrementCapturerCount(true);

if (status != PaintPreviewBaseService::CaptureStatus::kOk ||
!result->capture_success) {
std::move(callback).Run(Status::kCaptureFailed);
task->OnCaptured(Status::kCaptureFailed);
return;
}
auto file_manager = GetFileMixin()->GetFileManager();
GetFileMixin()->GetTaskRunner()->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&FileManager::SerializePaintPreviewProto,
GetFileMixin()->GetFileManager(), key, result->proto,
true),
GetFileMixin()->GetFileManager(), task->key(),
result->proto, true),
base::BindOnce(&PaintPreviewTabService::OnFinished,
weak_ptr_factory_.GetWeakPtr(), tab_id,
std::move(callback)));
weak_ptr_factory_.GetWeakPtr(), task));
}

void PaintPreviewTabService::OnFinished(int tab_id,
FinishedCallback callback,
void PaintPreviewTabService::OnFinished(base::WeakPtr<TabServiceTask> task,
bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (success)
if (!task) {
return;
}
int tab_id = task->tab_id();

if (success) {
captured_tab_ids_.insert(tab_id);
std::move(callback).Run(success ? Status::kOk
: Status::kProtoSerializationFailed);
}
// WARNING: `task` may be invalidated by this call.
task->OnCaptured(success ? Status::kOk : Status::kProtoSerializationFailed);

auto file_manager = GetFileMixin()->GetFileManager();
GetFileMixin()->GetTaskRunner()->PostTaskAndReplyWithResult(
FROM_HERE,
Expand Down Expand Up @@ -350,8 +404,8 @@ void PaintPreviewTabService::RunAudit(
keys_to_delete.resize(it - keys_to_delete.begin());

// The performance of this is poor (O(n) per removal). However,
// |keys_to_delete| should normally be 0 or small and this is only run once at
// startup.
// |keys_to_delete| should normally be 0 or small and this is only run once
// at startup.
for (const auto& key : keys_to_delete)
captured_tab_ids_.erase(TabIdFromDirectoryKey(key));

Expand Down

0 comments on commit 21f8df5

Please sign in to comment.