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

#ifndef EXTENSIONS_BROWSER_API_AUTOMATION_INTERNAL_AUTOMATION_EVENT_ROUTER_H_
#define EXTENSIONS_BROWSER_API_AUTOMATION_INTERNAL_AUTOMATION_EVENT_ROUTER_H_

#include <set>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/memory/singleton.h"
#include "base/observer_list.h"
#include "base/scoped_multi_source_observation.h"
#include "base/uuid.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_process_host_observer.h"
#include "content/public/browser/web_contents_observer.h"
#include "extensions/browser/api/automation_internal/automation_event_router_interface.h"
#include "extensions/common/api/automation_internal.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/mojom/automation_registry.mojom.h"
#include "mojo/public/cpp/bindings/associated_receiver_set.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
#include "mojo/public/cpp/bindings/remote_set.h"
#include "services/accessibility/public/mojom/automation.mojom.h"
#include "ui/accessibility/ax_action_handler_registry.h"
#include "ui/accessibility/ax_location_and_scroll_updates.h"
#include "ui/accessibility/ax_tree_id.h"
#include "ui/accessibility/ax_updates_and_events.h"

namespace content {
class BrowserContext;
}  // namespace content

namespace ui {
struct AXActionData;
}  // namespace ui

namespace extensions {
struct AutomationListener;
struct WorkerId;

class AutomationEventRouterObserver {
 public:
  virtual void AllAutomationExtensionsGone() = 0;
  virtual void ExtensionListenerAdded() = 0;
};

// Routes accessibility events from the browser process to the extension's
// renderer process.
class AutomationEventRouter
    : public content::RenderProcessHostObserver,
      public AutomationEventRouterInterface,
      public ui::AXActionHandlerObserver,
      public extensions::mojom::RendererAutomationRegistry {
 public:
  using RenderProcessHostId = int;

  static AutomationEventRouter* GetInstance();

  // Indicates that the listener at |listener_rph_id| wants to receive
  // automation events from the accessibility tree indicated by
  // |source_ax_tree_id|. Automation events are forwarded from now on until the
  // listener process dies.
  void RegisterListenerForOneTree(const ExtensionId& extension_id,
                                  const RenderProcessHostId& listener_rph_id,
                                  content::WebContents* web_contents,
                                  ui::AXTreeID source_ax_tree_id);

  // Indicates that the listener at |listener_rph_id| wants to receive
  // automation events from all accessibility trees because it has Desktop
  // permission.
  void RegisterListenerWithDesktopPermission(
      const ExtensionId& extension_id,
      const RenderProcessHostId& listener_rph_id,
      content::WebContents* web_contents);

  // Undoes the Register call above. May result in disabling of automation.
  void UnregisterListenerWithDesktopPermission(
      const RenderProcessHostId& listener_rph_id);

  // Like the above function, but for all listeners. Definitely results in
  // disabling of automation.
  void UnregisterAllListenersWithDesktopPermission();

  // The following two methods should only be called by Lacros.
  void NotifyAllAutomationExtensionsGone();
  void NotifyExtensionListenerAdded();

  void AddObserver(AutomationEventRouterObserver* observer);
  void RemoveObserver(AutomationEventRouterObserver* observer);
  bool HasObserver(AutomationEventRouterObserver* observer);

  // AutomationEventRouterInterface:
  void DispatchAccessibilityEvents(
      const ui::AXTreeID& tree_id,
      const std::vector<ui::AXTreeUpdate>& updates,
      const gfx::Point& mouse_location,
      const std::vector<ui::AXEvent>& events) override;
  void DispatchAccessibilityLocationChange(
      const ui::AXTreeID& tree_id,
      const ui::AXLocationChange& details) override;
  void DispatchAccessibilityScrollChange(
      const ui::AXTreeID& tree_id,
      const ui::AXScrollChange& details) override;
  void DispatchTreeDestroyedEvent(ui::AXTreeID tree_id) override;
  void DispatchActionResult(
      const ui::AXActionData& data,
      bool result,
      content::BrowserContext* browser_context = nullptr) override;
  void DispatchGetTextLocationDataResult(
      const ui::AXActionData& data,
      const std::optional<gfx::Rect>& rect) override;

  // If a remote router is registered, then all events are directly forwarded to
  // it. The caller of this method is responsible for calling it again with
  // |nullptr| before the remote router is destroyed to prevent UaF.
  void RegisterRemoteRouter(AutomationEventRouterInterface* router);

  static void BindForRenderer(
      RenderProcessHostId render_process_id,
      mojo::PendingAssociatedReceiver<
          extensions::mojom::RendererAutomationRegistry> receiver);

 private:
  class AutomationListener : public content::WebContentsObserver {
   public:
    explicit AutomationListener(content::WebContents* web_contents);
    AutomationListener(const AutomationListener& other) = delete;
    AutomationListener& operator=(const AutomationListener&) = delete;
    ~AutomationListener() override;

    // content:WebContentsObserver:
    void PrimaryPageChanged(content::Page& page) override;

    raw_ptr<AutomationEventRouter> router;
    ExtensionId extension_id;
    RenderProcessHostId render_process_host_id;
    bool desktop;
    std::set<ui::AXTreeID> tree_ids;
  };

  AutomationEventRouter();

  AutomationEventRouter(const AutomationEventRouter&) = delete;
  AutomationEventRouter& operator=(const AutomationEventRouter&) = delete;

  ~AutomationEventRouter() override;

  void Register(const ExtensionId& extension_id,
                const RenderProcessHostId& listener_rph_id,
                content::WebContents* web_contents,
                ui::AXTreeID source_ax_tree_id,
                bool desktop);

  // RenderProcessHostObserver:
  void RenderProcessExited(
      content::RenderProcessHost* host,
      const content::ChildProcessTerminationInfo& info) override;
  void RenderProcessHostDestroyed(content::RenderProcessHost* host) override;

  // ui::AXActionHandlerObserver:
  void TreeRemoved(ui::AXTreeID ax_tree_id) override;

  void RemoveAutomationListener(content::RenderProcessHost* host);

  // Returns the listener for the provided ID, or `nullptr` if none is found.
  AutomationListener* GetListenerByRenderProcessID(
      const RenderProcessHostId& listener_rph_id) const;

  // ax::mojom::AutomationClient:
  void BindAutomation(
      mojo::PendingAssociatedRemote<ax::mojom::Automation> automation) override;

  std::vector<std::unique_ptr<AutomationListener>> listeners_;

  std::map<WorkerId, base::Uuid> keepalive_request_uuid_for_worker_;

  // The caller of RegisterRemoteRouter is responsible for ensuring that this
  // pointer is valid. The remote router must be unregistered with
  // RegisterRemoteRouter(nullptr) before it is destroyed.
  raw_ptr<AutomationEventRouterInterface> remote_router_ = nullptr;

  base::ScopedMultiSourceObservation<content::RenderProcessHost,
                                     content::RenderProcessHostObserver>
      rph_observers_{this};

  base::ObserverList<AutomationEventRouterObserver>::Unchecked observers_;

  mojo::AssociatedReceiverSet<extensions::mojom::RendererAutomationRegistry,
                              RenderProcessHostId>
      receivers_;

  mojo::AssociatedRemoteSet<ax::mojom::Automation> automation_remote_set_;

  base::WeakPtrFactory<AutomationEventRouter> weak_ptr_factory_{this};
  friend struct base::DefaultSingletonTraits<AutomationEventRouter>;
  friend class AutomationEventRouterExtensionBrowserTest;
};

}  // namespace extensions

#endif  // EXTENSIONS_BROWSER_API_AUTOMATION_INTERNAL_AUTOMATION_EVENT_ROUTER_H_