1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157

content / public / test / accessibility_notification_waiter.h [blame]

// Copyright 2013 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_PUBLIC_TEST_ACCESSIBILITY_NOTIFICATION_WAITER_H_
#define CONTENT_PUBLIC_TEST_ACCESSIBILITY_NOTIFICATION_WAITER_H_

#include <memory>

#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "content/public/browser/web_contents_observer.h"
#include "ui/accessibility/ax_event_generator.h"
#include "ui/accessibility/ax_mode.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/accessibility/ax_node_id_forward.h"
#include "ui/accessibility/ax_tree.h"

namespace base {
class RunLoop;
}

namespace ui {
class BrowserAccessibilityManager;
}

namespace content {

class RenderFrameHost;
class RenderFrameHostImpl;
class WebContents;

// Create an instance of this class *before* doing any operation that
// might generate an accessibility event (like a page navigation or
// clicking on a button). Then call WaitForNotification
// afterwards to block until the specified accessibility notification has been
// received.
class AccessibilityNotificationWaiter : public WebContentsObserver {
 public:
  // Will wait for any event across all ways including scroll or location
  // changes as well normal and generated events.
  explicit AccessibilityNotificationWaiter(WebContents* web_contents);

  // Wait for a specific Blink event.
  AccessibilityNotificationWaiter(WebContents* web_contents,
                                  ui::AXMode accessibility_mode,
                                  ax::mojom::Event event);

  // Wait for a specific AXEventGenerator event.
  AccessibilityNotificationWaiter(WebContents* web_contents,
                                  ui::AXMode accessibility_mode,
                                  ui::AXEventGenerator::Event event);

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

  ~AccessibilityNotificationWaiter() override;

  // Blocks until the specific accessibility notification registered in
  // AccessibilityNotificationWaiter is received. Returns true if an event was
  // received, false if waiting ended for some other reason.
  // Pass true for |all_frames| to wait for a notification on all frames
  // before returning, rather than waiting for only a single notification
  // from any frame.
  [[nodiscard]] bool WaitForNotification(bool all_frames = false);

  // Blocks until the notification is received, or the given timeout passes.
  // Returns true if an event was received, false if waiting ended for some
  // other reason.
  [[nodiscard]] bool WaitForNotificationWithTimeout(base::TimeDelta timeout);

  // After WaitForNotification has returned, this will retrieve
  // the tree of accessibility nodes received from the renderer process for
  // the observed WebContents (not including the trees of inner WebContents).
  const ui::AXTree& GetAXTree() const;

  // After WaitForNotification returns, use this to retrieve the id of the
  // node that was the target of the event.
  int event_target_id() const { return event_target_id_; }

  bool notification_received() const { return notification_received_; }

  // After WaitForNotification returns, use this to retrieve the
  // `BrowserAccessibilityManager` that was the target of the event.
  ui::BrowserAccessibilityManager* event_browser_accessibility_manager() const {
    return event_browser_accessibility_manager_;
  }

  // WebContentsObserver override:
  void RenderFrameHostChanged(RenderFrameHost* old_host,
                              RenderFrameHost* new_host) override;

  // Quits listening and unblocks WaitForNotification* calls.
  void Quit();

 private:
  // Listen to all frames within the frame tree of this WebContents.
  void ListenToAllFrames(WebContents* web_contents);

  // Bind either the OnAccessibilityEvent or OnGeneratedEvent callback
  // for a given frame within the WebContent's frame tree.
  void ListenToFrame(RenderFrameHostImpl* frame_host);

  // Helper to bind the OnAccessibilityEvent callback.
  void BindOnAccessibilityEvent(RenderFrameHostImpl* frame_host);

  // Helper to bind the OnGeneratedEvent callback.
  void BindOnGeneratedEvent(RenderFrameHostImpl* frame_host);

  // Helper to bind the OnLocationsChanged callback.
  void BindOnLocationsChanged(RenderFrameHostImpl* frame_host);

  // Callback from RenderViewHostImpl.
  void OnAccessibilityEvent(RenderFrameHostImpl* rfhi,
                            ax::mojom::Event event,
                            int event_target_id);

  // Callback from BrowserAccessibilityManager for all generated events.
  void OnGeneratedEvent(ui::BrowserAccessibilityManager* manager,
                        ui::AXEventGenerator::Event event,
                        ui::AXNodeID event_target_id);

  // Callback from BrowserAccessibilityManager when locations / bounding
  // boxes change.
  void OnLocationsChanged();

  // Callback from BrowserAccessibilityManager for the focus changed event.
  //
  // TODO(crbug.com/41470112): Remove this method once we migrate to using
  // AXEventGenerator for focus changed events.
  void OnFocusChanged();

  // Returns the tree of accessibility nodes received from renderer processes
  // for the WebContents that owns `render_frame`. This may not be the observed
  // WebContents, but rather an inner WebContents (e.g., for a guest view).
  const ui::AXTree& GetAXTreeForFrame(RenderFrameHostImpl* render_frame) const;

  std::optional<ax::mojom::Event> event_to_wait_for_;
  std::optional<ui::AXEventGenerator::Event> generated_event_to_wait_for_;
  std::unique_ptr<base::RunLoop> loop_runner_;
  base::RepeatingClosure loop_runner_quit_closure_;
  int event_target_id_ = 0;
  raw_ptr<ui::BrowserAccessibilityManager, AcrossTasksDanglingUntriaged>
      event_browser_accessibility_manager_ = nullptr;
  bool notification_received_ = false;
  int frame_count_ = 0;
  int notification_count_ = 0;
  bool wait_for_any_event_ = false;

  base::WeakPtrFactory<AccessibilityNotificationWaiter> weak_factory_{this};
};

}  // namespace content

#endif  // CONTENT_PUBLIC_TEST_ACCESSIBILITY_NOTIFICATION_WAITER_H_