| // 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_specifier.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() |
| // .SetElement(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_INTERACTION) 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_INTERACTION) 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_INTERACTION) 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_INTERACTION) 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_INTERACTION) StepBuilder { |
| public: |
| StepBuilder(); |
| ~StepBuilder(); |
| StepBuilder(StepBuilder&& other); |
| StepBuilder& operator=(StepBuilder&& other); |
| |
| // Sets the identifier or name of the element to use for this step. |
| // `SetElement()` or equivalent is required for all step types except |
| // `kCustomEvent`. |
| StepBuilder& SetElement(ElementSpecifier element_specifier); |
| |
| // Sets the unique identifier for this step. Either this or |
| // SetElementName() is required for all step types except kCustomEvent. |
| // DEPRECATED: use `SetElement()`. |
| 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. |
| // DEPRECATED: use `SetElement()`. |
| 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_INTERACTION) |
| extern void PrintTo(InteractionSequence::StepType step_type, std::ostream* os); |
| |
| COMPONENT_EXPORT(UI_BASE_INTERACTION) |
| extern void PrintTo(InteractionSequence::AbortedReason reason, |
| std::ostream* os); |
| |
| COMPONENT_EXPORT(UI_BASE_INTERACTION) |
| extern void PrintTo(InteractionSequence::SubsequenceMode mode, |
| std::ostream* os); |
| |
| COMPONENT_EXPORT(UI_BASE_INTERACTION) |
| extern void PrintTo(InteractionSequence::StepStartMode mode, std::ostream* os); |
| |
| COMPONENT_EXPORT(UI_BASE_INTERACTION) |
| extern void PrintTo(const InteractionSequence::AbortedData& aborted_data, |
| std::ostream* os); |
| |
| COMPONENT_EXPORT(UI_BASE_INTERACTION) |
| extern std::ostream& operator<<(std::ostream& os, |
| InteractionSequence::StepType step_type); |
| |
| COMPONENT_EXPORT(UI_BASE_INTERACTION) |
| extern std::ostream& operator<<(std::ostream& os, |
| InteractionSequence::AbortedReason reason); |
| |
| COMPONENT_EXPORT(UI_BASE_INTERACTION) |
| extern std::ostream& operator<<(std::ostream& os, |
| InteractionSequence::StepStartMode mode); |
| |
| COMPONENT_EXPORT(UI_BASE_INTERACTION) |
| extern std::ostream& operator<<(std::ostream& os, |
| InteractionSequence::SubsequenceMode mode); |
| |
| COMPONENT_EXPORT(UI_BASE_INTERACTION) |
| extern std::ostream& operator<<( |
| std::ostream& os, |
| const InteractionSequence::AbortedData& aborted_data); |
| |
| } // namespace ui |
| |
| #endif // UI_BASE_INTERACTION_INTERACTION_SEQUENCE_H_ |