File: interaction_sequence.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 (703 lines) | stat: -rw-r--r-- 30,701 bytes parent folder | download | duplicates (4)
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
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
// Copyright 2021 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_BASE_INTERACTION_INTERACTION_SEQUENCE_H_
#define UI_BASE_INTERACTION_INTERACTION_SEQUENCE_H_

#include <map>
#include <optional>
#include <string>
#include <string_view>
#include <variant>

#include "base/component_export.h"
#include "base/functional/callback_forward.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/base/interaction/element_tracker.h"

namespace ui {

// Follows an expected sequence of user-UI interactions and provides callbacks
// at each step. Useful for creating interaction tests and user tutorials.
//
// An interaction sequence consists of an ordered series of steps, each of which
// refers to an interface element tagged with a ElementIdentifier and each of
// which represents that element being either shown, activated, or hidden. Other
// unrelated events such as element hover or focus are ignored (but could be
// supported in the future).
//
// Each step has an optional callback that is triggered when the expected
// interaction happens, and an optional callback that is triggered when the step
// ends - either because the next step has started or because the user has
// aborted the sequence (typically by dismissing UI such as a dialog or menu,
// resulting in the element from the current step being hidden/destroyed). Once
// the first callback is called/the step starts, the second callback will always
// be called.
//
// Furthermore, when the last step in the sequence completes, in addition to its
// end callback, an optional sequence-completed callback will be called. If the
// user aborts the sequence or if this object is destroyed, then an optional
// sequence-aborted callback is called instead.
//
// To use a InteractionSequence, start with a builder:
//
//  sequence_ = InteractionSequence::Builder()
//      .SetCompletedCallback(base::BindOnce(...))
//      .AddStep(InteractionSequence::WithInitialElement(initial_element))
//      .AddStep(InteractionSequence::StepBuilder()
//          .SetElementID(kDialogElementID)
//          .SetType(StepType::kShown)
//          .SetStartCallback(...)
//          .Build())
//      .AddStep(...)
//      .Build();
//  sequence_->Start();
//
// For more detailed instructions on using the ui/base/interaction library, see
// README.md in this folder.
//
class COMPONENT_EXPORT(UI_BASE) InteractionSequence {
 public:
  // The type of event that is expected to happen next in the sequence.
  enum class StepType {
    // Represents the element with the specified ID becoming visible to the
    // user, or already being visible when the step starts.
    kShown,
    // Represents an element with the specified ID becoming activated by the
    // user (for buttons or menu items, being clicked). If the element goes away
    // before the step start callback can be called, null will be passed to the
    // step start callback (you can avoid this by setting step start callback
    // mode to immediate).
    kActivated,
    // Represents an element with the specified ID becoming hidden or destroyed,
    // or no elements with the specified ID being visible. If there is no
    // matching element or the element disappears before the start callback can
    // be called, null will be passed to the start callback.
    kHidden,
    // Represents a custom event with a specific custom event type. You may
    // further specify a required element name or ID to filter down which
    // events you actually want to step on vs. ignore.
    kCustomEvent,
    // Represents one or more nested, conditional subsequences. An element may
    // be provided for use in `SubsequenceCondition` checks. See
    // `SubsequenceMode` for more information on how subsequences work.
    //
    // Note that while a subsequence step can have an element name or ID, it is
    // not required. Furthermore, the element will be located at the start of
    // the step and if it is not present, null will be passed to the
    // SubsequenceCondition (unless must_be_visible is true, in which case the
    // step will fail). This allows subsequences to be conditional on the
    // presence of an element.
    //
    // Known limitations:
    // - If the triggering condition for the step following this one occurs
    //   during execution of one of the subsequences, it may be missed/lost.
    // - If there is no element specified, or the element does not exist, then
    //   the following step will not be able to effectively use
    //   ContextMode::kFromPreviousStep.
    kSubsequence,
    kMaxValue = kSubsequence
  };

  // Describes how the subsequences in a `StepType::kSubsequence` step are
  // executed.
  enum class SubsequenceMode {
    // The first subsequence whose condition is met is executed, and the step
    // finishes if the subsequence completes. If no subsequences run, the step
    // succeeds.
    kAtMostOne,
    // The first subsequence whose condition is met is executed, and the step
    // finishes if the subsequence completes. If no subsequences run, the step
    // fails.
    kExactlyOne,
    // All subsequences whose conditions are met are executed, and the step
    // finishes if any of the subsequences completes successfully. The others
    // may fail, and are destroyed immediately as soon as the first succeeds. If
    // no sequences run, the step fails.
    kAtLeastOne,
    // All subsequences whose conditions are met are executed, and the step
    // finishes if all of the subsequences complete. If no sequences run, the
    // step succeeds.
    //
    // This is the default behavior.
    kAll,
    kMaxValue = kAll
  };

  // Details why a sequence was aborted.
  enum class AbortedReason {
    // External code destructed this object before the sequence could complete.
    kSequenceDestroyed,
    // The starting element was hidden before the sequence started.
    kElementHiddenBeforeSequenceStart,
    // An element should have been visible at the start of a step but was not.
    kElementNotVisibleAtStartOfStep,
    // An element should have remained visible during a step but did not.
    kElementHiddenDuringStep,
    // An element was hidden during an asynchronous step between trigger and
    // step start.
    kElementHiddenBetweenTriggerAndStepStart,
    // One or more subsequences were expected to run, but none could due to
    // failed preconditions.
    kNoSubsequenceRun,
    // One or more subsequences needed to succeed, but one or more unexpectedly
    // failed. Details will be the failed step from the first subsequence that
    // should have completed but did not.
    kSubsequenceFailed,
    // The sequence was explicitly failed as part of a test.
    kFailedForTesting,
    // A timeout was reached during execution. This might not be fatal, but the
    // current state can be dumped regardless.
    kSequenceTimedOut,
    // A discrete triggering condition for the following step happened before
    // the start callback for the current callback was even run.
    kSubsequentStepTriggeredTooEarly,
    // A triggering condition for the following step happened while waiting for
    // the previous step to complete, but then the state changed in such a way
    // that the trigger became invalid.
    kSubsequentStepTriggerInvalidated,
    // Update this if values are added to the enumeration.
    kMaxValue = kSubsequentStepTriggerInvalidated
  };

  // Specifies how the context for a step is determined.
  enum class ContextMode {
    // Use the initial context for the sequence.
    kInitial,
    // Search for the element in any context.
    //
    // Note that these events are sent after events for specific contexts, if
    // you have two steps in succession that are both `StepType::kActivated`
    // with the second one having `ContextMode::kAny`, then the same activation
    // could conceivably trigger both steps. Fortunately, this sort of thing
    // doesn't happen in any of the real-world use cases; if it does it will be
    // fixed with special case code.
    kAny,
    // Inherits the context from the previous step. Cannot be used on the first
    // step in a sequence.
    kFromPreviousStep
  };

  // Determines how each step's start callback executes - and by extension, when
  // the following step is staged for execution.
  enum class StepStartMode {
    // The start callback will be posted and execute on a fresh call stack,
    // after all immediately-pending tasks. Even if there is no start callback,
    // staging of the following step will be done on that fresh call stack,
    // preventing multiple steps from cascading on the same triggering callback.
    // This is the default.
    kAsynchronous,
    // The start callback will execute immediately as soon as the step trigger
    // is detected. This could be in the middle of an operation, such as a set
    // of UI elements being shown together. Use this only when waiting even a
    // small amount of time would cause a problem (which is almost never).
    kImmediate
  };

  // Determines whether a subsequence will run. `seq` is the parent sequence,
  // and `el` is the reference element, and may be null if the element is not
  // specified or if there is no matching element. This is unlike other steps
  // where an element is typically required to be present before the step can
  // proceed.
  using SubsequenceCondition =
      base::OnceCallback<bool(const InteractionSequence* seq,
                              const TrackedElement* el)>;

  // Returns a callback that causes the subsequence to always run.
  static SubsequenceCondition AlwaysRun();

  // A step context is either an explicit context or a ContextMode.
  using StepContext = std::variant<ElementContext, ContextMode>;

  // Callback when a step in the sequence starts. If |element| is no longer
  // available, it will be null.
  using StepStartCallback =
      base::OnceCallback<void(InteractionSequence* sequence,
                              TrackedElement* element)>;

  // Callback when a step in the sequence ends. If |element| is no longer
  // available, it will be null.
  using StepEndCallback = base::OnceCallback<void(TrackedElement* element)>;

  // Information passed when a sequence fails or is aborted.
  struct COMPONENT_EXPORT(UI_BASE) AbortedData {
    AbortedData();
    ~AbortedData();
    AbortedData(const AbortedData& other);
    AbortedData& operator=(const AbortedData& other);

    // The index of the step where the failure occurred. 0 before the sequence
    // starts, and is incremented on each step transition after the previous
    // step's end callback is called, or if the next step's precondition fails
    // (so that it refers to the correct step).
    int step_index = 0;

    // The description of the failed step.
    std::string step_description;

    // The step type of the failed step.
    StepType step_type = StepType::kShown;

    // A reference to the element used by the failed step. This is a weak
    // reference and may be null if the element was hidden or destroyed.
    SafeElementReference element;

    // The identifier of the element used by the failed step.
    ElementIdentifier element_id;

    // The context of the element expected by the failed step, or null if
    // unknown/unspecified.
    ui::ElementContext context;

    // The reason the step failed/the sequence was aborted.
    AbortedReason aborted_reason = AbortedReason::kSequenceDestroyed;

    // If this failure was due to a subsequence failing, the failure information
    // for the subsequences will be stored here.
    //
    // This also stores the next step when a step fails due to e.g. an element
    // losing visibility.
    std::vector<std::optional<AbortedData>> subsequence_failures;
  };

  // Callback for when the user aborts the sequence by failing to follow the
  // sequence of steps, or if this object is deleted after the sequence starts,
  // or when the sequence fails for some other reason.
  //
  // The most recent step is described by the `AbortedData` block.
  using AbortedCallback = base::OnceCallback<void(const AbortedData&)>;

  using CompletedCallback = base::OnceClosure;

  struct Configuration;
  class StepBuilder;
  struct SubsequenceData;

  struct COMPONENT_EXPORT(UI_BASE) Step {
    Step();
    Step(const Step& other) = delete;
    void operator=(const Step& other) = delete;
    ~Step();

    bool uses_named_element() const { return !element_name.empty(); }

    StepType type = StepType::kShown;
    ElementIdentifier id;
    CustomElementEventType custom_event_type;
    std::string element_name;
    StepContext context = ContextMode::kInitial;

    // This is used for testing; while `context` can be updated as part of
    // sequence execution, this will never change.
    bool in_any_context = false;

    // These will always have values when the sequence is built, but can be
    // unspecified during construction. If unspecified, they will be set to
    // appropriate defaults for `type`.
    std::optional<bool> must_be_visible;
    std::optional<bool> must_remain_visible;
    bool transition_only_on_event = false;

    StepStartCallback start_callback;
    std::optional<StepStartMode> step_start_mode;
    StepEndCallback end_callback;
    ElementTracker::Subscription subscription;

    // Tracks the element associated with the step, if known. We could use a
    // SafeElementReference here, but there are cases where we want to do
    // additional processing if this element goes away, so we'll add the
    // listeners manually instead.
    raw_ptr<TrackedElement, DanglingUntriaged> element = nullptr;

    // Provides a useful description for debugging that can be read or passed
    // to the abort callback on failure.
    std::string description;

    // These only apply if the type of the step is kSubsequence.
    SubsequenceMode subsequence_mode = SubsequenceMode::kAll;
    std::vector<SubsequenceData> subsequence_data;
  };

  // Use a Builder to specify parameters when creating an InteractionSequence.
  class COMPONENT_EXPORT(UI_BASE) Builder {
   public:
    Builder();
    Builder(Builder&& other);
    Builder& operator=(Builder&& other);
    ~Builder();

    // Sets the callback if the user exits the sequence early.
    Builder& SetAbortedCallback(AbortedCallback callback);

    // Sets the callback if the user completes the sequence.
    // Convenience method so that the last step's end callback doesn't need to
    // have special logic in it.
    Builder& SetCompletedCallback(CompletedCallback callback);

    // Adds an expected step in the sequence. All sequences must have at least
    // one step.
    Builder& AddStep(std::unique_ptr<Step> step);

    // Convenience methods to add a step when using a StepBuilder.
    Builder& AddStep(StepBuilder& step_builder);

    // Convenience method for cases where we don't have an lvalue.
    Builder& AddStep(StepBuilder&& step_builder);

    // Sets the context for this sequence. Must be called if no step is added
    // by element or has had SetContext() called. Typically the initial step of
    // a sequence will use WithInitialElement() so it won't be necessary to call
    // this method.
    Builder& SetContext(ElementContext context);

    // Sets the default step start mode for this sequence. This will acquire
    // a default value if not set; for subsequences, the value of the parent
    // sequence is inherited instead if no value is set.
    Builder& SetDefaultStepStartMode(StepStartMode step_start_mode);

    // Creates the InteractionSequence. You must call Start() to initiate the
    // sequence; sequences cannot be re-used, and a Builder is no longer valid
    // after Build() is called.
    std::unique_ptr<InteractionSequence> Build();

   private:
    friend class InteractionSequence;

    std::unique_ptr<InteractionSequence> BuildSubsequence(
        const Configuration* owner_config,
        const Step* owning_step);

    std::unique_ptr<Configuration> configuration_;
  };

  // Used inline in calls to Builder::AddStep to specify step parameters.
  //
  // Methods intended to be used in Kombucha test bodies have rvalue versions
  // to reduce the need for std::move().
  class COMPONENT_EXPORT(UI_BASE) StepBuilder {
   public:
    StepBuilder();
    ~StepBuilder();
    StepBuilder(StepBuilder&& other);
    StepBuilder& operator=(StepBuilder&& other);

    // Sets the unique identifier for this step. Either this or
    // SetElementName() is required for all step types except kCustomEvent.
    StepBuilder& SetElementID(ElementIdentifier element_id);

    // Sets the step to refer to a named element instead of an
    // ElementIdentifier. Either this or SetElementID() is required for all
    // step types other than kCustomEvent.
    StepBuilder& SetElementName(std::string_view name);

    // Sets the context for the step; useful for setting up the initial
    // element of the sequence if you do not know the context ahead of time, or
    // to specify that a step should not use the default context.
    StepBuilder& SetContext(StepContext context) &;
    StepBuilder&& SetContext(StepContext context) &&;

    // Sets the type of step. Required. You must set `event_type` if and only
    // if `step_type` is kCustomEvent.
    StepBuilder& SetType(
        StepType step_type,
        CustomElementEventType event_type = CustomElementEventType());

    // Changes the subsequence mode from the default. See `SubsequenceMode` for
    // details. Implicitly sets the step type to kSubsequence.
    StepBuilder& SetSubsequenceMode(SubsequenceMode subsequence_mode);

    // Adds a subsequence to the step. The subsequence will run if `condition`
    // returns true. Implicitly changes the step type to kSubsequence.
    //
    // The subsequence will not actually be built until it is needed. It will
    // inherit the named elements of its parent unless otherwise specified.
    StepBuilder& AddSubsequence(Builder subsequence,
                                SubsequenceCondition condition = AlwaysRun());

    // Indicates that the specified element must be visible at the start of the
    // step. Defaults to true for StepType::kActivated, false otherwise. Failure
    // To meet this condition will abort the sequence.
    StepBuilder& SetMustBeVisibleAtStart(bool must_be_visible) &;
    StepBuilder&& SetMustBeVisibleAtStart(bool must_be_visible) &&;

    // Indicates that the specified element must remain visible throughout the
    // step once it has been shown. Defaults to true for StepType::kShown, false
    // otherwise (and incompatible with StepType::kHidden). Failure to meet this
    // condition will abort the sequence.
    StepBuilder& SetMustRemainVisible(bool must_remain_visible) &;
    StepBuilder&& SetMustRemainVisible(bool must_remain_visible) &&;

    // For kShown and kHidden events, if set to true, only allows a step
    // transition to happen when a "shown" or "hidden" event is received, and
    // not if an element is already visible (in the case of kShown steps) or no
    // elements are visible (in the case of kHidden steps).
    //
    // Default is false. Has no effect on kActiated events which are discrete
    // rather than stateful.
    //
    // Note: Does not track events fired during previous step's start callback,
    // so should not be used in automated interaction testing. The default
    // behavior should be fine for these cases.
    //
    // Note: Be careful when setting this value to true, as it increases the
    // likelihood of ending up in a state where a failure cannot be detected;
    // that is, waiting for an element to appear and then it... never does. In
    // this case, you will need an external way to terminate the sequence (a
    // timeout, user interaction, etc.)
    StepBuilder& SetTransitionOnlyOnEvent(bool transition_only_on_event) &;
    StepBuilder&& SetTransitionOnlyOnEvent(bool transition_only_on_event) &&;

    // Sets the callback called at the start of the step.
    StepBuilder& SetStartCallback(StepStartCallback start_callback);

    // Sets the callback called at the start of the step. Convenience method
    // that eliminates the InteractionSequence argument if you do not need it.
    StepBuilder& SetStartCallback(
        base::OnceCallback<void(TrackedElement*)> start_callback);

    // Sets the callback called at the start of the step. Convenience method
    // that eliminates both arguments if you do not need them.
    StepBuilder& SetStartCallback(base::OnceClosure start_callback);

    // Sets the step start mode for this step. If not set, inherits the default
    // mode from its sequence.
    StepBuilder& SetStepStartMode(StepStartMode step_start_mode) &;
    StepBuilder&& SetStepStartMode(StepStartMode step_start_mode) &&;

    // Sets the callback called at the end of the step. Guaranteed to be called
    // if the start callback is called, before the start callback of the next
    // step or the sequence aborted or completed callback. Also called if this
    // object is destroyed while the step is still in-process.
    StepBuilder& SetEndCallback(StepEndCallback end_callback);

    // Sets the callback called at the end of the step. Convenience method if
    // you don't need the parameter.
    StepBuilder& SetEndCallback(base::OnceClosure end_callback);

    // Sets the description of the step.
    StepBuilder& SetDescription(std::string_view description) &;
    StepBuilder&& SetDescription(std::string_view description) &&;

    // Prepends `prefix`, along with a colon and space, to this step's
    // description.
    StepBuilder& AddDescriptionPrefix(std::string_view prefix) &;
    StepBuilder&& AddDescriptionPrefix(std::string_view prefix) &&;

    // Builds the step. The builder will not be valid after calling Build().
    std::unique_ptr<Step> Build();

   private:
    friend class InteractionSequence;
    std::unique_ptr<Step> step_;
  };

  // Returns a step with the following values already set, typically used as the
  // first step in a sequence (because the first element is usually present):
  //   ElementID: element->identifier()
  //   MustBeVisibleAtStart: true
  //   MustRemainVisible: true
  //
  // This is a convenience method and also removes the need to call
  // Builder::SetContext(). Specific framework implementations may provide
  // wrappers around this method that allow direct conversion from framework UI
  // elements (e.g. a views::View) to the target element.
  static std::unique_ptr<Step> WithInitialElement(
      TrackedElement* element,
      StepStartCallback start_callback = StepStartCallback(),
      StepEndCallback end_callback = StepEndCallback());

  ~InteractionSequence();

  // Starts the sequence. All of the elements in the sequence must belong to the
  // same top-level application window (which includes menus, bubbles, etc.
  // associated with that window).
  void Start();

  // Starts the sequence and does not return until the sequence either
  // completes or aborts. Events on the current thread continue to be processed
  // while the method is waiting, so this will not e.g. block the browser UI
  // thread from handling inputs.
  //
  // This is a test-only method since production code applications should
  // always run asynchronously.
  void RunSynchronouslyForTesting();

  // Returns whether the current step uses ContextMode::kAny.
  bool IsCurrentStepInAnyContextForTesting() const;

  // Returns whether the current step is using "immediate" execution mode.
  bool IsCurrentStepImmediateForTesting() const;

  // Explicitly fails the sequence.
  void FailForTesting();

  // Assigns an element to a given name. The name is local to this interaction
  // sequence. It is valid for `element` to be null; in this case, we are
  // explicitly saying "there is no element with this name [yet]".
  //
  // It is safe to call this method from a step start callback, but not a step
  // end or aborted callback, as in the latter case the sequence might be in
  // the process of being destructed.
  void NameElement(TrackedElement* element, std::string_view name);

  // Retrieves a named element, which may be null if we specified "no element"
  // or if the element has gone away.
  //
  // It is safe to call this method from a step start callback, but not a step
  // end or aborted callback, as in the latter case the sequence might be in
  // the process of being destructed.
  TrackedElement* GetNamedElement(const std::string& name);
  const TrackedElement* GetNamedElement(const std::string& name) const;

  // Builds aborted data for the current step and the given reason.
  AbortedData BuildAbortedData(AbortedReason reason) const;

  // Gets a weak pointer to this object.
  base::WeakPtr<InteractionSequence> AsWeakPtr();

 private:
  FRIEND_TEST_ALL_PREFIXES(InteractionSequenceSubsequenceTest, NamedElements);

  // Describes the state of the sequence.
  enum State {
    // [Sub]sequence waiting to be started.
    kNotStarted,
    // No transition is currently in progress.
    kIdle,
    // The end callback of the previous step is running.
    kInEndCallback,
    // The next step has been loaded, and the sequence is preparing to run the
    // new step's start callback.
    kWaitingForStartCallback,
    // The start callback for the new step is running.
    kInStartCallback,
  };

  explicit InteractionSequence(std::unique_ptr<Configuration> configuration,
                               const Step* reference_step);

  // Callbacks from the ElementTracker.
  void OnElementShown(TrackedElement* element);
  void OnElementActivated(TrackedElement* element);
  void OnElementHidden(TrackedElement* element);
  void OnCustomEvent(TrackedElement* element);

  // Callbacks used only during step transitions to cache certain events.
  void OnTriggerDuringStepTransition(TrackedElement* element);
  void OnElementHiddenDuringStepTransition(TrackedElement* element);
  void OnElementHiddenWaitingForEvent(TrackedElement* element);

  // While we're transitioning steps or staging a subsequence, it's possible for
  // an activation that would trigger the following step to come in. This method
  // adds a callback that's valid only during the step transition to watch for
  // this event.
  void MaybeWatchForEarlyTrigger(const Step* current_step);

  // A note on the next three methods - DoStepTransition(), StageNextStep(), and
  // Abort(): To prevent re-entrancy issues, they must always be the final call
  // in any method before it returns. This greatly simplifies the consistency
  // checks and safeguards that need to be put into place to make sure we aren't
  // making contradictory changes to state or calling callbacks in the wrong
  // order.

  // Start the transition from the current step to the next step.
  void StartStepTransition(TrackedElement* element);

  // Finish the transition from the current step to the next step.
  void CompleteStepTransition();

  // Looks at the next step to determine what needs to be done. Called at the
  // start of the sequence and after each subsequent step starts.
  void StageNextStep();

  // Cancels the sequence and cleans up.
  void Abort(AbortedReason reason);

  // Returns true if `name` is non-empty and `element` matches the element
  // with the specified name, or if `name` is empty (indicating we don't care
  // about it being a named element). Otherwise returns false.
  bool MatchesNameIfSpecified(const TrackedElement* element,
                              const std::string& name) const;

  // Returns the next step, or null if none.
  Step* next_step();
  const Step* next_step() const;

  // Returns the context for the current sequence.
  ElementContext context() const;

  // Updates the next step context from the current based on its StepContext.
  // Returns an element context if one is determined; null context if the step
  // allows any context.
  // Do not call for named elements.
  ElementContext UpdateNextStepContext(const Step* current_step);

  // Callbacks for when subsequences terminate.
  using SubsequenceHandle = const void*;
  void OnSubsequenceCompleted(SubsequenceHandle subsequence);
  void OnSubsequenceAborted(SubsequenceHandle subsequence,
                            const AbortedData& aborted_data);
  void BuildSubsequences(const Step* current_step);
  SubsequenceData* FindSubsequenceData(SubsequenceHandle subsequence);

  State state_ = State::kNotStarted;
  int active_step_index_ = 0;
  bool missing_first_element_ = false;
  bool trigger_during_callback_ = false;
  std::unique_ptr<Step> current_step_;
  ElementTracker::Subscription next_step_hidden_subscription_;
  std::unique_ptr<Configuration> configuration_;
  std::map<std::string, SafeElementReference> named_elements_;
  base::OnceClosure quit_run_loop_closure_for_testing_;

  // This is necessary because this object could be deleted during any callback,
  // and we don't want to risk a UAF if that happens.
  base::WeakPtrFactory<InteractionSequence> weak_factory_{this};
};

COMPONENT_EXPORT(UI_BASE)
extern void PrintTo(InteractionSequence::StepType step_type, std::ostream* os);

COMPONENT_EXPORT(UI_BASE)
extern void PrintTo(InteractionSequence::AbortedReason reason,
                    std::ostream* os);

COMPONENT_EXPORT(UI_BASE)
extern void PrintTo(InteractionSequence::SubsequenceMode mode,
                    std::ostream* os);

COMPONENT_EXPORT(UI_BASE)
extern void PrintTo(InteractionSequence::StepStartMode mode, std::ostream* os);

COMPONENT_EXPORT(UI_BASE)
extern void PrintTo(const InteractionSequence::AbortedData& aborted_data,
                    std::ostream* os);

COMPONENT_EXPORT(UI_BASE)
extern std::ostream& operator<<(std::ostream& os,
                                InteractionSequence::StepType step_type);

COMPONENT_EXPORT(UI_BASE)
extern std::ostream& operator<<(std::ostream& os,
                                InteractionSequence::AbortedReason reason);

COMPONENT_EXPORT(UI_BASE)
extern std::ostream& operator<<(std::ostream& os,
                                InteractionSequence::StepStartMode mode);

COMPONENT_EXPORT(UI_BASE)
extern std::ostream& operator<<(std::ostream& os,
                                InteractionSequence::SubsequenceMode mode);

COMPONENT_EXPORT(UI_BASE)
extern std::ostream& operator<<(
    std::ostream& os,
    const InteractionSequence::AbortedData& aborted_data);

}  // namespace ui

#endif  // UI_BASE_INTERACTION_INTERACTION_SEQUENCE_H_