blob: ae5c43badcf88bc9c9bfa720756095658e1b36dd [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_STATE_IMPL_H_
#define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_STATE_IMPL_H_
#include <list>
#include <memory>
#include <vector>
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "components/metrics/metrics_provider.h"
#include "content/browser/accessibility/scoped_mode_collection.h"
#include "content/common/content_export.h"
#include "content/public/browser/browser_accessibility_state.h"
#include "content/public/browser/render_widget_host.h"
#include "ui/accessibility/ax_mode.h"
#include "ui/accessibility/platform/assistive_tech.h"
#include "ui/accessibility/platform/ax_platform.h"
namespace content {
struct FocusedNodeDetails;
class WebContentsImpl;
// The BrowserAccessibilityState class is used to determine if Chrome should be
// customized for users with assistive technology, such as screen readers. We
// modify the behavior of certain user interfaces to provide a better experience
// for screen reader users. The way we detect a screen reader program is
// different for each platform.
//
// Screen Reader Detection
// (1) On Windows, many screen reader detection mechanisms will give false
// positives, such as relying on the SPI_GETSCREENREADER system parameter.
// In Chrome, we attempt to dynamically detect a MSAA client screen reader
// by calling NotifyWinEvent in NativeWidgetWin with a custom ID and wait
// to see if the ID is requested by a subsequent call to WM_GETOBJECT.
// (2) On macOS, we dynamically detect if VoiceOver is running by Key-Value
// Observing changes to the "voiceOverEnabled" property in NSWorkspace. We
// also monitor the undocumented accessibility attribute
// @"AXEnhancedUserInterface", which is set by other assistive
// technologies.
class CONTENT_EXPORT BrowserAccessibilityStateImpl
: public BrowserAccessibilityState,
public ui::AXPlatform::Delegate,
public content::RenderWidgetHost::InputEventObserver,
public ScopedModeCollection::Delegate {
public:
BrowserAccessibilityStateImpl(const BrowserAccessibilityStateImpl&) = delete;
BrowserAccessibilityStateImpl& operator=(
const BrowserAccessibilityStateImpl&) = delete;
~BrowserAccessibilityStateImpl() override;
// Returns the single process-wide instance.
static BrowserAccessibilityStateImpl* GetInstance();
// Returns a new instance. Only one instance may be live in the process at any
// time.
static std::unique_ptr<BrowserAccessibilityStateImpl> Create();
// BrowserAccessibilityState implementation.
ui::AXMode GetAccessibilityMode() override;
ui::AXMode GetAccessibilityModeForBrowserContext(
BrowserContext* browser_context) override;
// Any currently running assistive tech that should prevent accessibility from
// being auto-disabled.
ui::AssistiveTech ActiveAssistiveTech() const override;
void SetPerformanceFilteringAllowed(bool enabled) override;
bool IsPerformanceFilteringAllowed() override;
base::CallbackListSubscription RegisterFocusChangedCallback(
FocusChangedCallback callback) override;
std::unique_ptr<ScopedAccessibilityMode> CreateScopedModeForProcess(
ui::AXMode mode) override;
std::unique_ptr<ScopedAccessibilityMode> CreateScopedModeForBrowserContext(
BrowserContext* browser_context,
ui::AXMode mode) override;
std::unique_ptr<ScopedAccessibilityMode> CreateScopedModeForWebContents(
WebContents* web_contents,
ui::AXMode mode) override;
void SetAXModeChangeAllowed(bool allowed) override;
bool IsAXModeChangeAllowed() const override;
void SetActivationFromPlatformEnabled(bool enabled) override;
bool IsActivationFromPlatformEnabled() override;
bool IsAccessibilityPerformanceMeasurementExperimentActive() const override;
void NotifyWebContentsPreferencesChanged() const override;
// ui::AXPlatform::Delegate:
void OnMinimalPropertiesUsed() override;
void OnPropertiesUsedInBrowserUI() override;
void OnPropertiesUsedInWebContent() override;
void OnInlineTextBoxesUsedInWebContent() override;
void OnExtendedPropertiesUsedInWebContent() override;
void OnHTMLAttributesUsed() override;
void OnActionFromAssistiveTech() override;
// content::RenderWidgetHost::InputEventObserver:
void OnInputEvent(const RenderWidgetHost& widget,
const blink::WebInputEvent& event) override;
// The global accessibility mode is automatically enabled based on
// usage of accessibility APIs. When we detect a significant amount
// of user inputs within a certain time period, but no accessibility
// API usage, we automatically disable accessibility.
void OnUserInputEvent();
// Notifies listeners that the focused element changed inside a WebContents.
void OnFocusChangedInPage(const FocusedNodeDetails& details);
// Return true if auto-disable should be blocked.
bool ShouldBlockAutoDisable();
// Signal to BrowserAccessibilityState that a page navigation has occurred.
void OnPageNavigationComplete();
// Sets the initial accessibility mode for `web_contents` if it is not
// hidden or if ProgressiveAccessibility is not enabled.
void OnWebContentsInitialized(WebContentsImpl* web_contents);
// Applies the effective accessibility mode for `web_contents` if
// ProgressiveAccessibility is enabled.
void OnWebContentsRevealed(WebContentsImpl* web_contents);
// Tracks `web_contents` for the sake of disabling accessibility later if
// ProgressiveAccessibility is enabled and disable_on_hide is selected. A
// previously-hidden WebContents becomes eligible for disablement if it is
// not among the last five to be hidden once it has been hidden for at least
// five minutes.
void OnWebContentsHidden(WebContentsImpl* web_contents);
// Notifies the instance that `assistive_tech` is the most significant of any
// assistive technologies discovered. AXPlatform observers are notified if
// `assistive_tech` differs from the most recent discovery. Called by
// subclasses or accessibility managers when they detect the presence of
// assistive tech via platform-specific means.
void OnAssistiveTechFound(ui::AssistiveTech assistive_tech);
void SetDiscoverAssistiveTechnologyCallbackForTesting(
base::RepeatingClosure callback) {
discover_at_callback_for_testing_ = std::move(callback);
}
// A hidden WebContents is guaranteed to retain its accessibility state when
// the ProgressiveAccessibility feature is in disable_on_hide mode for at
// least five minutes, plus or minus twenty seconds.
static base::TimeDelta GetRandomizedDisableDelay();
static base::TimeDelta GetMaxDisableDelay();
// The number of recently-hidden WebContents that will not have accessibility
// disabled if the ProgressiveAccessibility feature is on in disable_on_hide
// mode.
static constexpr int kMaxPreservedWebContents = 5;
protected:
BrowserAccessibilityStateImpl();
// Refreshes the assistive tech if an AXMode change indicates that the
// presence of an active screen reader may have changed.
// * Platforms that have a perfect signal for the presence of a screen reader
// should not override this method: the default implementation treats the
// screen reader flag as a deterministic indicator.
// * Platforms such as Windows and Linux that require a slow computation
// to determine the presence of a screen reader should begin the computation
// when the presence of AXMode::kExtendedProperties is inconsistent with the
// current known screen reader state.
virtual void RefreshAssistiveTechIfNecessary(ui::AXMode new_mode);
ui::AXPlatform& ax_platform() { return ax_platform_; }
private:
void UpdateAccessibilityActivityTask();
// Stops tracking `web_contents` for disabling accessibility while it is
// hidden.
void OnDisablerDestroyedForWebContents(WebContentsImpl* web_contents);
// Combines the effective accessibility mode for the process, for
// `web_contents`'s BrowserContext, and for `web_contents` and applies it
// to `web_contents` if ProgressiveAccessibility is disabled or if
// `web_contents` is not hidden.
void ApplyAccessibilityModeToWebContents(WebContentsImpl* web_contents,
ui::AXMode process_mode,
ui::AXMode browser_context_mode,
ui::AXMode web_contents_mode);
// ScopedModeCollection::Delegate:
// Handles a change to the effective accessibility mode for the process.
void OnModeChanged(ui::AXMode old_mode, ui::AXMode new_mode) override;
// Filters out `kFromPlatform` from `mode` if activation from platform
// integration is enabled; otherwise, filters all mode flags from `mode` if
// `kFromPlatform` is present in it.
ui::AXMode FilterModeFlags(ui::AXMode mode) override;
// Handles a change to the effective accessibility mode for `browser_context`.
void OnModeChangedForBrowserContext(BrowserContext* browser_context,
ui::AXMode old_mode,
ui::AXMode new_mode);
// Handles a change to the effective accessibility mode for `web_contents`.
void OnModeChangedForWebContents(WebContents* web_contents,
ui::AXMode old_mode,
ui::AXMode new_mode);
// Add the AXModes + AXMode::kFromPlatform, when corresponding platform APIs
// are used.
void EnableAXModeFromPlatform(ui::AXMode modes_to_add);
// Refreshes the instance's notion of active assistive technologies.
// Implementations must call `OnAssistiveTechFound()` with the results of any
// discovery.
virtual void RefreshAssistiveTech();
// Helper function to configure the accessibility performance experiment.
std::unique_ptr<ScopedAccessibilityMode>
ConfigureAccessibilityPerformanceExperiment();
// Helper to disable and clean-up the accessibility performance experiment.
void ExitPerformanceExperiment();
// The process's single AXPlatform instance.
ui::AXPlatform ax_platform_{*this};
// Whether there is a pending task to run UpdateAccessibilityActivityTask.
bool accessibility_update_task_pending_ = false;
// Whether changes to the AXMode are allowed.
// Changes are disallowed while running tests or when
// --force-renderer-accessibility is used on the command line.
bool allow_ax_mode_changes_ = true;
// Keeps track of whether performance filtering is allowed for the device.
// Default is true to defer to feature flag. Value may be set to false by
// prefs.
bool performance_filtering_allowed_ = true;
// Tracks whether the accessibility engine has been used in any form during
// the current session. Toggled to true when accessibility is first enabled,
// and never toggled back to false.
bool has_enabled_accessibility_in_session_ = false;
// True if activation of accessibility from interactions with the platform's
// accessibility integration is enabled.
bool activation_from_platform_enabled_ = true;
// Timer used to track the time between start-up and engine first-use.
base::ElapsedTimer first_use_timer_;
// Counter used to track the number of page navigations between start-up
// and engine first-use.
uint32_t num_page_navs_before_first_use_ = 0;
// The time of the first user input event; if we receive multiple
// user input events within a 30-second period and no
base::TimeTicks first_user_input_event_time_;
int user_input_event_count_ = 0;
// The time accessibility became active, used to calculate active time.
base::TimeTicks accessibility_active_start_time_;
// The time accessibility became inactive, used to calculate inactive time.
base::TimeTicks accessibility_inactive_start_time_;
// The last time accessibility was active, used to calculate active time.
base::TimeTicks accessibility_last_usage_time_;
// The time accessibility was enabled, for statistics.
base::TimeTicks accessibility_enabled_time_;
// The time accessibility was auto-disabled, for statistics.
base::TimeTicks accessibility_disabled_time_;
base::RepeatingCallbackList<void(const FocusedNodeDetails&)>
focus_changed_callbacks_;
// The collection of active ScopedAccessibilityMode instances targeting all
// WebContentses in the process.
ScopedModeCollection scoped_modes_for_process_{*this};
// A ScopedAccessibilityMode that holds the process-wide mode flags modified
// via --force-renderer-accessibility on the command line.
std::unique_ptr<ScopedAccessibilityMode> forced_accessibility_mode_;
// A ScopedAccessibilityMode that holds process-wide mode flags required to
// support the platform API calls being used.
std::unique_ptr<ScopedAccessibilityMode> platform_ax_mode_;
// Keeps track of whether the Accessibility Performance Measurement Experiment
// is currently active. This is necessary because there are cases where we
// don't want to make the experiment active, and checking the state of the
// feature flag causes the study to be active. In this case, if the conditions
// are met, this will contain the mode of the current experiment group,
// nullptr otherwise.
std::unique_ptr<ScopedAccessibilityMode> experiment_accessibility_mode_;
// The most recently hidden WebContentses; used only when the disable-on-hide
// feature of ProgressiveAccessibility is enabled. This container holds the
// most-recently hidden WebContentses. Accessibility is disabled for each one
// that is pushed out of this list when a sixth element is added.
std::list<raw_ptr<WebContentsImpl>> last_hidden_;
// Keeps track of whether screen reader presence was checked. Resets with
// every new page load. A new check only occurs if kAXModeComplete is active
// and a screen reader isn't running.
bool has_recently_checked_for_screen_reader_ = false;
base::RepeatingClosure discover_at_callback_for_testing_;
friend class ui::AXPlatform;
};
} // namespace content
#endif // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_STATE_IMPL_H_