910e62b5创建于 1月15日历史提交
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_H_
#define UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_H_

#include "base/component_export.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ref.h"
#include "base/observer_list.h"
#include "base/scoped_observation_traits.h"
#include "base/threading/thread_checker.h"
#include "build/build_config.h"
#include "ui/accessibility/ax_mode.h"
#include "ui/accessibility/platform/assistive_tech.h"

namespace ui {

class AXModeObserver;
class AXPlatformNode;

// Process-wide accessibility platform state.
class COMPONENT_EXPORT(AX_PLATFORM) AXPlatform {
 public:
#if BUILDFLAG(IS_WIN)
  // These strings are only needed for IA2 support.
  struct ProductStrings {
    // Product name, e.g. "Chrome".
    std::string product_name;
    // Version number, e.g. "aa.bb.cc.dd".
    std::string product_version;
    // Toolkit version of the product, for example, the User Agent string.
    std::string toolkit_version;
  };
#endif

  class COMPONENT_EXPORT(AX_PLATFORM) Delegate {
   public:
    Delegate(const Delegate&) = delete;
    Delegate& operator=(const Delegate&) = delete;
    virtual ~Delegate() = default;

    // Returns the effective process-wide accessibility mode.
    virtual AXMode GetAccessibilityMode() = 0;

#if BUILDFLAG(IS_WIN)
    // Used to retrieve the product name, version, and toolkit version for IA2.
    // Only called the first time the data is needed to fill in the
    // product_strings_ member of AXPlatform.
    virtual ProductStrings GetProductStrings() = 0;
#endif

    // A very basic accessible property was used, such as role, name or
    // location. Only enables AXMode::kNativeAPIs unless the screen reader
    // honeypot is used.
    virtual void OnMinimalPropertiesUsed() {}
    // An a11y property was used in the browser UI. Enable AXMode::kNativeAPIs.
    virtual void OnPropertiesUsedInBrowserUI() {}
    // A basic property was used in web content. Enable AXMode::kWebContents.
    virtual void OnPropertiesUsedInWebContent() {}
    // Inline textboxes were used. Enable AXMode::kInlineTextBoxes.
    virtual void OnInlineTextBoxesUsedInWebContent() {}
    // Extended properties were used. Enable AXMode::kExtendedProperties.
    virtual void OnExtendedPropertiesUsedInWebContent() {}
    // HTML properties were used. Enable AXMode::kHTML.
    virtual void OnHTMLAttributesUsed() {}
    // An a11y action was used in web content.
    virtual void OnActionFromAssistiveTech() {}

   protected:
    Delegate() = default;
  };

  // Returns the single process-wide instance.
  static AXPlatform& GetInstance();

  // Constructs a new instance. Only one instance may be alive in a process at
  // any given time. Typically, the embedder creates one during process startup
  // and ensures that it is kept alive throughout the process's UX.
  explicit AXPlatform(Delegate& delegate);
  AXPlatform(const AXPlatform&) = delete;
  AXPlatform& operator=(const AXPlatform&) = delete;
  ~AXPlatform();

  // Returns the process-wide accessibility mode.
  AXMode GetMode();

  void AddModeObserver(AXModeObserver* observer);
  void RemoveModeObserver(AXModeObserver* observer);

  // Notifies observers that the mode flags in `mode` have been added to the
  // process-wide accessibility mode.
  void NotifyModeAdded(AXMode mode);

  // Notify observers that an assistive technology was launched or exited.
  // Note: in some cases we do not yet have a perfect signal when the user
  // quits their assistive tech, so in that case the tool will continue to
  // appear to be present.
  // The only known assistive tech that this affects currently is JAWS.
  // TODO(crbug.com/402069423) Improve JAWS exit detection.
  void NotifyAssistiveTechChanged(AssistiveTech assistive_tech);

  // The current active assistive tech, such as a screen reader, where a
  // detection algorithm has been implemented.
  AssistiveTech active_assistive_tech() const {
    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
    return active_assistive_tech_;
  }

  // Is the current active assistive tech a screen reader.
  bool IsScreenReaderActive();

  // Returns whether caret browsing is enabled. When caret browsing is enabled,
  // we need to ensure that we keep ATs aware of caret movement.
  bool IsCaretBrowsingEnabled();
  void SetCaretBrowsingState(bool enabled);

#if BUILDFLAG(IS_WIN)
  // Returns the product name, e.g. "Chrome".
  const std::string& GetProductName() const;

  // Returns the version number, e.g. "aa.bb.cc.dd".
  const std::string& GetProductVersion() const;

  // Returns the toolkit version of the product, for example, the User Agent
  // string.
  const std::string& GetToolkitVersion() const;

  // Enables or disables use of the UI Automation Provider on Windows. If this
  // function is not called, the provider is enabled or disabled on the basis of
  // the "UiaProvider" base::Feature. In such cases, the `--enable-features` or
  // `--disable-features` switches on the browser's command line may be used to
  // enable or disable use of the provider, respectively. This function may only
  // be called during browser process startup before any UI is presented.
  void SetUiaProviderEnabled(bool is_enabled);

  // Disables the UI Automation Provider on Windows, and signals to UIA that the
  // previous providers that might have been returned are no longer valid.
  void DisableActiveUiaProvider();

  // Returns true if the UI Automation Provider for Windows is enabled.
  bool IsUiaProviderEnabled() const;

  // Notes that an inbound request from a UIA client has been serviced; for
  // example, by calling UiaReturnRawElementProvider to give it a window's
  // root object.
  void SetUiaClientServiced(bool uia_client_serviced);

  // Returns true if a UIA client has been serviced; see above.
  bool HasServicedUiaClients();
#endif

  // A very basic accessible property was used, such as role, name or location.
  // Always enables AXMode::kNativeAPIs by calling OnMinimalPropertiesUsed() on
  // the delegate. If the screen reader honeypot is used (currently windows
  // only), OnPropertiesUsedInWebContent() will also be called, enabling web
  // content accessibility via AXMode::kWebContents.
  void OnMinimalPropertiesUsed(bool is_name_used = false);
  // An a11y property was used in the browser UI. Enable AXMode::kNativeAPIs.
  void OnPropertiesUsedInBrowserUI();
  // A basic property was used in web content. Enable AXMode::kWebContents.
  void OnPropertiesUsedInWebContent();
  // Inline textboxes were used. Enable AXMode::kInlineTextBoxes.
  void OnInlineTextBoxesUsedInWebContent();
  // Extended properties were used. Enable AXMode::kExtendedProperties.
  void OnExtendedPropertiesUsedInWebContent();
  // HTML properties were used. Enable AXMode::kHTML.
  void OnHTMLAttributesUsed();
  // An a11y action was used in web content.
  void OnActionFromAssistiveTech();
#if BUILDFLAG(IS_WIN)
  // The honeypot is a fake alert object that is created, with a fake alert
  // event fired on it. It is considered unlikely that anything but a screen
  // reader would be query that object, and also query other minimal properties.
  // TODO(accessibility): We may no longer need this method because we
  // detect all screen readers directly, although this may happen earlier.
  void OnScreenReaderHoneyPotQueried();
#endif

  void DetachFromThreadForTesting();

 private:
  friend class ::ui::AXPlatformNode;
  FRIEND_TEST_ALL_PREFIXES(AXPlatformTest, Observer);

#if BUILDFLAG(IS_WIN)
  // Retrieves the product name, version, and toolkit version from the delegate
  // if they have not already been retrieved.
  void RetrieveProductStringsIfNeeded() const
      VALID_CONTEXT_REQUIRED(thread_checker_);
#endif

  // The embedder's delegate.
  const raw_ref<Delegate> delegate_ GUARDED_BY_CONTEXT(thread_checker_);

  base::ObserverList<AXModeObserver,
                     /*check_empty=*/true,
                     /*allow_reentrancy=*/false>
      observers_ GUARDED_BY_CONTEXT(thread_checker_);

#if BUILDFLAG(IS_WIN)
  // See product_name() product_version(), and toolkit_version().
  // These are lazily cached upon first use. Mutable to allow caching.
  mutable std::optional<ProductStrings> product_strings_
      GUARDED_BY_CONTEXT(thread_checker_);

  enum class UiaProviderEnablement {
    // Enabled or disabled via Chrome Variations (base::FeatureList).
    kVariations,
    // Explicitly enabled at runtime.
    kEnabled,
    // Explicitly disabled at runtime.
    kDisabled,
  };
  UiaProviderEnablement uia_provider_enablement_
      GUARDED_BY_CONTEXT(thread_checker_) = UiaProviderEnablement::kVariations;
#endif  // BUILDFLAG(IS_WIN)

  // Keeps track of the active AssistiveTech.
  AssistiveTech active_assistive_tech_ GUARDED_BY_CONTEXT(thread_checker_) =
      AssistiveTech::kUninitialized;

  // Keeps track of whether caret browsing is enabled.
  bool caret_browsing_enabled_ GUARDED_BY_CONTEXT(thread_checker_) = false;

#if BUILDFLAG(IS_WIN)
  bool screen_reader_honeypot_queried_ GUARDED_BY_CONTEXT(thread_checker_) =
      false;
  bool is_name_used_ GUARDED_BY_CONTEXT(thread_checker_) = false;
  bool has_serviced_uia_clients_ GUARDED_BY_CONTEXT(thread_checker_) = false;
#endif

  THREAD_CHECKER(thread_checker_);
};

}  // namespace ui

namespace base {

// Traits type in support of base::ScopedObservation.
template <>
struct ScopedObservationTraits<ui::AXPlatform, ui::AXModeObserver> {
  static void AddObserver(ui::AXPlatform* source,
                          ui::AXModeObserver* observer) {
    source->AddModeObserver(observer);
  }
  static void RemoveObserver(ui::AXPlatform* source,
                             ui::AXModeObserver* observer) {
    source->RemoveModeObserver(observer);
  }
};

}  // namespace base

#endif  // UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_H_