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_
|