File: ui_test_utils.h

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (654 lines) | stat: -rw-r--r-- 24,468 bytes parent folder | download | duplicates (6)
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
// 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 CHROME_TEST_BASE_UI_TEST_UTILS_H_
#define CHROME_TEST_BASE_UI_TEST_UTILS_H_

#include <set>
#include <string>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "chrome/browser/ui/browser_list_observer.h"
#include "chrome/browser/ui/exclusive_access/fullscreen_observer.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
#include "chrome/browser/ui/view_ids.h"
#include "components/history/core/browser/history_service.h"
#include "content/public/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/window_open_disposition.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/views/view_observer.h"
#include "url/gurl.h"

#if defined(TOOLKIT_VIEWS)
#include "ui/views/test/widget_test_api.h"
#endif

class Browser;
class FullscreenController;
class Profile;

namespace javascript_dialogs {
class AppModalDialogController;
}

namespace base {
class FilePath;
}

struct NavigateParams;

namespace content {
class RenderFrameHost;
class WebContents;
}

namespace gfx {
class Rect;
}

namespace views {
class View;
}

// A collections of functions designed for use with InProcessBrowserTest.
namespace ui_test_utils {

// Flags to indicate what to wait for in a navigation test.
// They can be ORed together.
// The order in which the waits happen when more than one is selected, is:
//    Browser
//    Tab
//    Navigation
enum BrowserTestWaitFlags {
  // Don't wait for anything.
  BROWSER_TEST_NO_WAIT = 0,
  // Wait for a new browser.
  BROWSER_TEST_WAIT_FOR_BROWSER = 1 << 0,
  // Wait for a new tab.
  BROWSER_TEST_WAIT_FOR_TAB = 1 << 1,
  // Wait for loading to stop. Loading stops when either
  // a document and its subresources are completely loaded
  // (i.e. the spinner has stopped) or no document can be
  // loaded due to an e.g. an error or crash.
  BROWSER_TEST_WAIT_FOR_LOAD_STOP = 1 << 2,

  BROWSER_TEST_MASK = BROWSER_TEST_WAIT_FOR_BROWSER |
                      BROWSER_TEST_WAIT_FOR_TAB |
                      BROWSER_TEST_WAIT_FOR_LOAD_STOP
};

// Puts the current tab title in |title|. Returns true on success.
bool GetCurrentTabTitle(const Browser* browser, std::u16string* title);

// NavigateToURL* functions navigate the given |browser| to |url| according the
// provided parameters and block until ready (by default - until loading stops,
// see BROWSER_TEST_WAIT_FOR_LOAD_STOP for more details. Note that this is
// different from content::NavigateToURL, which block only until navigation
// succeeds or fails, which generally happens earlier).
//
// Some of these functions return RenderFrameHost* where the navigation was
// committed or nullptr if the navigation failed. The caller should inspect the
// return value - typically with: ASSERT_TRUE(NavigateToURL(...)).
//
// Note: if the navigation has committed, this doesn't mean that the old
// RenderFrameHost was destroyed:
// - it either can wait for the renderer process to finish running unload
//   handlers and acknowledge that.
// - it can be stored in BackForwardCache to be reused for subsequent
//   back/forward navigation.
//
// If the test needs to test RenderFrameHost cleanup, use
// BackForwardCache::DisableForTesting to ensure that RenderFrameHost isn't
// preserved in BackForwardCache and
// RenderFrameDeletedObserver::WaitUntilDeleted to wait for deletion.

// Navigate according to |params|.
void NavigateToURL(NavigateParams* params);

// Navigate current tab of the |browser| to |url| using POST request, simulating
// form submission.
void NavigateToURLWithPost(Browser* browser, const GURL& url);

// Navigate current tab of the |browser| to |url|, simulating a user typing
// |url| into the omnibox.
[[nodiscard]] content::RenderFrameHost* NavigateToURL(Browser* browser,
                                                      const GURL& url);

// Same as |NavigateToURL|, but:
// - |disposition| allows to specify in which tab navigation should happen
// - |browser_test_flags| allows to specify a different condition this function
//   would wait until, see BrowserTestWaitFlags for details.
content::RenderFrameHost* NavigateToURLWithDisposition(
    Browser* browser,
    const GURL& url,
    WindowOpenDisposition disposition,
    int browser_test_flags);

// Same as |NavigateToURL|, but wait for a given number of navigations to
// complete instead of the tab to finish loading.
content::RenderFrameHost* NavigateToURLBlockUntilNavigationsComplete(
    Browser* browser,
    const GURL& url,
    int number_of_navigations);

// See |NavigateToURLWithDisposition| and
// |NavigateToURLBlockUntilNavigationsComplete|.
content::RenderFrameHost*
NavigateToURLWithDispositionBlockUntilNavigationsComplete(
    Browser* browser,
    const GURL& url,
    int number_of_navigations,
    WindowOpenDisposition disposition,
    int browser_test_flags);

// Generate the file path for testing a particular test.
// The file for the tests is all located in
// test_root_directory/dir/<file>
// The returned path is base::FilePath format.
base::FilePath GetTestFilePath(const base::FilePath& dir,
                               const base::FilePath& file);

// Generate the URL for testing a particular test.
// HTML for the tests is all located in
// test_root_directory/dir/<file>
// The returned path is GURL format.
GURL GetTestUrl(const base::FilePath& dir, const base::FilePath& file);

// Generate the path of the build directory, relative to the source root.
bool GetRelativeBuildDirectory(base::FilePath* build_dir);

// Blocks until an application modal dialog is shown and returns it.
javascript_dialogs::AppModalDialogController* WaitForAppModalDialog();

// Blocks until the web contents shows a web modal dialog.
void WaitForWebModalDialog(content::WebContents* web_contents);

#if defined(TOOLKIT_VIEWS)
// Blocks until the given view attains the given visibility state.
void WaitForViewVisibility(Browser* browser, ViewID vid, bool visible);
#endif

// Performs a find in the page of the specified tab. Returns the number of
// matches found.  |ordinal| is an optional parameter which is set to the index
// of the current match. |selection_rect| is an optional parameter which is set
// to the location of the current match.
int FindInPage(content::WebContents* tab,
               const std::u16string& search_string,
               bool forward,
               bool case_sensitive,
               int* ordinal,
               gfx::Rect* selection_rect);

// Blocks until the |history_service|'s history finishes loading.
void WaitForHistoryToLoad(history::HistoryService* history_service);

// Blocks until a Browser is added to the BrowserList.
Browser* WaitForBrowserToOpen();

// Blocks until a Browser is removed from the BrowserList. If |browser| is null,
// the removal of any browser will suffice; otherwise the removed browser must
// match |browser|.
void WaitForBrowserToClose(Browser* browser = nullptr);

// Download the given file and waits for the download to complete.
void DownloadURL(Browser* browser, const GURL& download_url);

// Waits until the autocomplete controller reaches its done state.
void WaitForAutocompleteDone(Browser* browser);

// Waits until the window gets minimized.
// Returns success or not.
bool WaitForMinimized(Browser* browser);

// Waits until the window gets maximized.
// Returns success or not.
bool WaitForMaximized(Browser* browser);

// See comment on views::AsyncWidgetRequestWaiter.
[[nodiscard]] views::AsyncWidgetRequestWaiter CreateAsyncWidgetRequestWaiter(
    Browser& browser);

// SetAndWaitForBounds sets the given `bounds` on `browser` and waits until the
// bounds update will be observable from all parts of the client (on Wayland).
// This does not verify the resulting bounds.
void SetAndWaitForBounds(Browser& browser, const gfx::Rect& bounds);

// Maximizes the browser window and wait until the window is maximized and all
// related visible UI effects are applied and observable from chrome.
// Returns true if succeeded.
bool MaximizeAndWaitUntilUIUpdateDone(Browser& browser);

// Waits for fullscreen state to be updated.
// There're two variation of fullscreen concepts, browser fullscreen and
// tab fullscreen. Due to fullscreen implementation, fullscreen state may
// be updated synchronously, while observer invocations and some other
// following tasks are done asynchronously.
// This class checks the condition on instance creation, then every
// OnFullscreenStateChanged invocation to deal with the situation.
// Once the condition is met, this class remembers the state, so following
// Wait() will do nothing, even if the condition is changed once again.
class FullscreenWaiter : public FullscreenObserver {
 public:
  // The conditions to be satisfied. std::nullopt means to ignore the
  // value.
  struct Expectation {
    // Condition for IsFullscreenForBrowser() to satisfy.
    std::optional<bool> browser_fullscreen;
    // Condition for IsTabFullscreen() to satisfy.
    std::optional<bool> tab_fullscreen;
    // ID of the display to be used for the fullscreen.
    std::optional<int64_t> display_id;
  };
  // Shortcut constant representing no fullscreen is enabled.
  inline static constexpr Expectation kNoFullscreen = {
      .browser_fullscreen = false,
      .tab_fullscreen = false,
  };

  FullscreenWaiter(Browser* browser, Expectation expecation);

  FullscreenWaiter(const FullscreenWaiter&) = delete;
  FullscreenWaiter& operator=(const FullscreenWaiter&) = delete;
  ~FullscreenWaiter() override;

  // Waits for the fullscreen state(s) to be satisfied.
  // Once it is satisfied after creation, this will do nothing,
  // even if the state is changed once again, and does not satisfy
  // the condition on calling Wait().
  void Wait();

  // FullscreenObserver:
  void OnFullscreenStateChanged() override;

 private:
  // Checks whether the condition is satisfied now.
  bool IsSatisfied() const;

  const Expectation expectation_;
  const raw_ptr<FullscreenController> controller_;
  base::ScopedObservation<FullscreenController, FullscreenObserver>
      observation_{this};
  base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed};

  // Caches if the condition is satisfied even once.
  bool satisfied_;
};

// This waiter waits for the specified |browser| becoming the last active
// browser in BrowserList. In Lacros, BrowserList::SetLastActive is triggered by
// OnWidgetActivationChanged when wayland notify the UI change asynchronously.
// Many testing code needs to wait until the expected browser to be set as
// the last active browser, and some testing code needs to wait until
// BrowserList::OnSetLastActive() is observed.
class BrowserSetLastActiveWaiter : public BrowserListObserver {
 public:
  // By default, the waiting will be satisfied if the expected |browser| is the
  // last active browser in BrowserList. In most cases, the testing code
  // depending on chrome::FindLastActive() should be good.
  // In some cases, for example, when there is only one browser in the
  // BrowserList, |browser| can be returned as the last active browser even if
  // the asynchronous Wayland UI event has not arrived yet (i.e.
  // BrowserList::SetLastActive() is not triggered and the code observing
  // BrowserList::OnSetLastActive() will not be called). If the test case
  // depends on the code observing BrowserList::OnSetLastActive() being executed
  // first, we can configure the waiter to be satisfied upon
  // OnBrowserSetLastActive is observed by passing
  // |wait_for_set_last_active_observed| being true.
  explicit BrowserSetLastActiveWaiter(
      Browser* browser,
      bool wait_for_set_last_active_observed = false);
  BrowserSetLastActiveWaiter(const BrowserSetLastActiveWaiter&) = delete;
  BrowserSetLastActiveWaiter& operator=(const BrowserSetLastActiveWaiter&) =
      delete;

  ~BrowserSetLastActiveWaiter() override;

  // Runs a loop until |browser_| becomes the last active browser.
  void Wait();

  // BrowserListObserver:
  void OnBrowserSetLastActive(Browser* browser) override;

 private:
  const raw_ptr<Browser> browser_;  // not_owned
  bool satisfied_ = false;
  bool wait_for_set_last_active_observed_ = false;
  base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed};
};

// Toggles browser fullscreen mode, then wait for its completion.
void ToggleFullscreenModeAndWait(Browser* browser);

// Waits until |browser| becomes active.
void WaitUntilBrowserBecomeActive(Browser* browser);

// Returns true if |browser| is active.
bool IsBrowserActive(Browser* browser);

// Opens a new browser window with chrome::NewEmptyWindow() and wait until it
// becomes active.
// Returns newly created browser.
Browser* OpenNewEmptyWindowAndWaitUntilActivated(
    Profile* profile,
    bool should_trigger_session_restore = false);

// Waits for |browser| becomes the last active browser.
// By default, the waiting will be satisfied if the expected |browser| is the
// last active browser in BrowserList. In most cases, this is enough for the
// testing code depending on chrome::FindLastActive(). In some cases, for
// example, when there is only one browser in the BrowserList, |browser| can be
// returned as the last active browser even if the asynchronous Wayland UI event
// has not arrived yet (i.e. BrowserList::SetLastActive() is not triggered and
// the code observing BrowserList::OnSetLastActive() will not be called). If the
// test case depends on the code observing BrowserList::OnSetLastActive() being
// executed first, we can configure the waiter to be satisfied upon
// OnBrowserSetLastActive is observed by passing
// |wait_for_set_last_active_observed| being true.
// Note: The last active browser is not necessarily the current active browser.
// A browser could be de-activated and still the last active browser. In many
// tests, BrowserList::GetLastActive() is incorrectly used to verify the
// expected browser being the active browser, see b/345848530.
void WaitForBrowserSetLastActive(
    Browser* browser,
    bool wait_for_set_last_active_observed = false);

// Send the given text to the omnibox and wait until it's updated.
void SendToOmniboxAndSubmit(
    Browser* browser,
    const std::string& input,
    base::TimeTicks match_selection_timestamp = base::TimeTicks());

// Gets the first browser that is not in the specified set.
Browser* GetBrowserNotInSet(const std::set<Browser*>& excluded_browsers);

// Gets the size and value of the cookie string for |url| in the given tab.
// Can be called from any thread.
void GetCookies(const GURL& url,
                content::WebContents* contents,
                int* value_size,
                std::string* value);

// Utility class to watch all existing and added tabs, until some interesting
// thing has happened.  Subclasses get to decide what they consider to be
// interesting.  In practice, usage is like this:
//
// - Subclass `AllTabsObserver`
// - Override `ProcessOneWebContents()` to check for the interesting thing.
// - Optionally return a `WebContentsObserver` that will watch for the
//   interesting thing for this WebContents.
// - Eventually call `ConditionMet()` to indicate that the interesting thing has
//   happened, and no further waiting is needed.
//
// Users of this class just call `Wait()` at most once.
class AllTabsObserver : public TabStripModelObserver,
                        public BrowserListObserver {
 public:
  AllTabsObserver(const AllTabsObserver&) = delete;
  AllTabsObserver& operator=(const AllTabsObserver&) = delete;

  ~AllTabsObserver() override;

  // Waits until whatever interesting thing we're waiting for has happened.
  // Will return immediately if it's already happened.
  void Wait();

 protected:
  AllTabsObserver();

  // Will be called for every tab's WebContents, including ones that exist when
  // this class is constructed and any that are created afterwards until
  // destruction or until `ConditionMet()` is called.
  //
  // This may choose not to return an observer if there's no need to watch this
  // contents.  It may also call `ConditionMet()` before returning, presumably
  // because the new contents already matches whatever condition our subclass is
  // checking for.  In that case, it will presumably not bother to return an
  // observer for the new contents.
  //
  // These will be deleted before `this` is deleted, so it's okay to have the
  // observers hold raw_ptrs back to `this`.
  virtual std::unique_ptr<base::CheckedObserver> ProcessOneContents(
      content::WebContents* web_contents) = 0;

  // Add all tabs from all browsers.  Must be called by the subclass ctor.
  void AddAllBrowsers();

  // Called by our subclass to let us know that whatever it's trying to wait for
  // has happened.  May be called at any time, including during a call to
  // `CreateObserverIfNeeded()`.  May be called more than once, though
  // calls will be ignored.
  void ConditionMet();

 private:
  // Record for every tab we're watching.
  struct TabNavigationMapEntry {
    TabNavigationMapEntry();
    ~TabNavigationMapEntry();

    // Provided by the subclass to do whatever it does.
    std::unique_ptr<base::CheckedObserver> subclass_observer;
    // Provided by us to clean up properly.
    std::unique_ptr<base::CheckedObserver> destruction_observer;
  };
  using TabNavigationMap =
      std::map<const content::WebContents*, TabNavigationMapEntry>;

  // Add all tabs from `browser`, and start watching for changes.
  void AddBrowser(const Browser* browser);

  // TabStripModelObserver:
  void OnTabStripModelChanged(
      TabStripModel* tab_strip_model,
      const TabStripModelChange& change,
      const TabStripSelectionChange& selection) override;

  // BrowserListObserver
  void OnBrowserAdded(Browser* browser) override;

  // Called for every WebContents.  Notifies the subclass, and sets up observers
  // if needed.
  void AddWebContents(content::WebContents* web_contents);

  // Called by our destruction observers.
  void OnWebContentsDestroyed(content::WebContents* web_contents);

  // Map of how many times each tab has navigated since |this| was created.
  TabNavigationMap tab_navigation_map_;

  // True if WaitForNavigations has been called, until
  // |num_navigations_to_wait_for_| have been observed.
  bool condition_met_ = false;

  // Flag to make sure that subclasses call `AddAllBrowsers()`.
  bool added_all_browsers_ = false;

  std::unique_ptr<base::RunLoop> run_loop_;
};

// Observer which waits for navigation events and blocks until a specific URL is
// loaded. The URL must be an exact match.
class UrlLoadObserver : public AllTabsObserver {
 public:
  // `url` is the URL to look for.
  explicit UrlLoadObserver(const GURL& url);
  ~UrlLoadObserver() override;

  // Returns the WebContents which navigated to `url`.
  content::WebContents* web_contents() const { return web_contents_; }

 protected:
  // Helper class to watch for DidStopLoading on one WebContents and relay it to
  // the UrlLoadObserver that created us.
  class LoadStopObserver : public content::WebContentsObserver {
   public:
    LoadStopObserver(UrlLoadObserver* owner,
                     content::WebContents* web_contents);
    ~LoadStopObserver() override;

    // WebContentsObserver
    void DidStopLoading() override;

   private:
    raw_ptr<UrlLoadObserver> owner_ = nullptr;
  };

  // AllTabsObserver
  std::unique_ptr<base::CheckedObserver> ProcessOneContents(
      content::WebContents* web_contents) override;

  // Called by `LoadStopObserver` when a WebContents DidStopLoading().
  void OnDidStopLoading(content::WebContents* web_contents);

 private:
  friend class LoadStopObserver;

  GURL url_;
  raw_ptr<content::WebContents> web_contents_ = nullptr;
};

// A helper that will wait until a tab is added to a specific Browser.
class TabAddedWaiter : public TabStripModelObserver {
 public:
  explicit TabAddedWaiter(Browser* browser);
  TabAddedWaiter(const TabAddedWaiter&) = delete;
  TabAddedWaiter& operator=(const TabAddedWaiter&) = delete;
  ~TabAddedWaiter() override = default;

  content::WebContents* Wait();

  // TabStripModelObserver:
  void OnTabStripModelChanged(
      TabStripModel* tab_strip_model,
      const TabStripModelChange& change,
      const TabStripSelectionChange& selection) override;

 private:
  base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed};
  raw_ptr<content::WebContents, AcrossTasksDanglingUntriaged> web_contents_ =
      nullptr;
};

// Similar to TabAddedWaiter, but will observe tabs added to all Browser
// objects, and can return the last tab that was added.
class AllBrowserTabAddedWaiter : public TabStripModelObserver,
                                 public BrowserListObserver {
 public:
  AllBrowserTabAddedWaiter();
  AllBrowserTabAddedWaiter(const AllBrowserTabAddedWaiter&) = delete;
  AllBrowserTabAddedWaiter& operator=(const AllBrowserTabAddedWaiter&) = delete;
  ~AllBrowserTabAddedWaiter() override;

  content::WebContents* Wait();

  // TabStripModelObserver:
  void OnTabStripModelChanged(
      TabStripModel* tab_strip_model,
      const TabStripModelChange& change,
      const TabStripSelectionChange& selection) override;

  // BrowserListObserver:
  void OnBrowserAdded(Browser* browser) override;

 private:
  base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed};

  // The last tab that was added.
  raw_ptr<content::WebContents, AcrossTasksDanglingUntriaged> web_contents_ =
      nullptr;
};

// Enumerates all history contents on the backend thread. Returns them in
// descending order by time.
class HistoryEnumerator {
 public:
  explicit HistoryEnumerator(Profile* profile);
  HistoryEnumerator(const HistoryEnumerator&) = delete;
  HistoryEnumerator& operator=(const HistoryEnumerator&) = delete;
  ~HistoryEnumerator();

  std::vector<GURL>& urls() { return urls_; }

 private:
  std::vector<GURL> urls_;
};

// In general, tests should use WaitForBrowserToClose() and
// WaitForBrowserToOpen() rather than instantiating this class directly.
class BrowserChangeObserver : public BrowserListObserver {
 public:
  enum class ChangeType {
    kAdded,
    kRemoved,
  };

  BrowserChangeObserver(Browser* browser, ChangeType type);
  BrowserChangeObserver(const BrowserChangeObserver&) = delete;
  BrowserChangeObserver& operator=(const BrowserChangeObserver&) = delete;
  ~BrowserChangeObserver() override;

  Browser* Wait();

  // BrowserListObserver:
  void OnBrowserAdded(Browser* browser) override;

  void OnBrowserRemoved(Browser* browser) override;

 private:
  raw_ptr<Browser, AcrossTasksDanglingUntriaged> browser_;
  ChangeType type_;
  base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed};
};

// Encapsulates waiting for the browser window to change state. This is
// needed for example on Chrome desktop linux, where window state change is done
// asynchronously as an event received from a different process.
class CheckWaiter {
 public:
  CheckWaiter(base::RepeatingCallback<bool()> callback,
              bool expected,
              const base::TimeDelta& timeout);
  CheckWaiter(const CheckWaiter&) = delete;
  CheckWaiter& operator=(const CheckWaiter&) = delete;
  ~CheckWaiter();

  // Blocks until the browser window becomes maximized.
  void Wait();

 private:
  bool Check();

  base::RepeatingCallback<bool()> callback_;
  bool expected_;
  const base::TimeTicks timeout_;
  // The waiter's RunLoop quit closure.
  base::RepeatingClosure quit_;
};

// Used to wait for the view to contain non-empty bounds.
class ViewBoundsWaiter : public views::ViewObserver {
 public:
  explicit ViewBoundsWaiter(views::View* observed_view);
  ViewBoundsWaiter(const ViewBoundsWaiter&) = delete;
  ViewBoundsWaiter& operator=(const ViewBoundsWaiter&) = delete;
  ~ViewBoundsWaiter() override;

  // Blocks until the view has non-empty bounds.
  void WaitForNonEmptyBounds();

 private:
  // views::ViewObserver:
  void OnViewBoundsChanged(views::View* observed_view) override;

  const raw_ptr<views::View> observed_view_;
  base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed};
};

}  // namespace ui_test_utils

#endif  // CHROME_TEST_BASE_UI_TEST_UTILS_H_