blob: 1a01bed98532a03c500d579fef4140209d242825 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_TRIGGER_SCRIPTS_TRIGGER_SCRIPT_COORDINATOR_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_TRIGGER_SCRIPTS_TRIGGER_SCRIPT_COORDINATOR_H_
#include <memory>
#include <string>
#include <vector>
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/time/time.h"
#include "components/autofill_assistant/browser/client.h"
#include "components/autofill_assistant/browser/metrics.h"
#include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/service/service_request_sender.h"
#include "components/autofill_assistant/browser/trigger_context.h"
#include "components/autofill_assistant/browser/trigger_scripts/dynamic_trigger_conditions.h"
#include "components/autofill_assistant/browser/trigger_scripts/static_trigger_conditions.h"
#include "components/autofill_assistant/browser/trigger_scripts/trigger_script.h"
#include "components/autofill_assistant/browser/web/web_controller.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "url/gurl.h"
namespace autofill_assistant {
// Fetches and coordinates trigger scripts for a specific url. Similar in scope
// and responsibility to a regular |controller|, but for trigger scripts instead
// of regular scripts.
class TriggerScriptCoordinator : public content::WebContentsObserver {
public:
// Observer interface for listeners interested in status updates of this
// coordinator.
class Observer : public base::CheckedObserver {
public:
Observer() = default;
~Observer() override = default;
virtual void OnTriggerScriptShown(const TriggerScriptUIProto& proto) = 0;
virtual void OnTriggerScriptHidden() = 0;
virtual void OnTriggerScriptFinished(
Metrics::LiteScriptFinishedState state) = 0;
virtual void OnVisibilityChanged(bool visible) = 0;
};
// |client| and |web_contents| must outlive this instance.
TriggerScriptCoordinator(
Client* client,
std::unique_ptr<WebController> web_controller,
std::unique_ptr<ServiceRequestSender> request_sender,
const GURL& get_trigger_scripts_server,
std::unique_ptr<StaticTriggerConditions> static_trigger_conditions,
std::unique_ptr<DynamicTriggerConditions> dynamic_trigger_conditions,
ukm::UkmRecorder* ukm_recorder);
~TriggerScriptCoordinator() override;
TriggerScriptCoordinator(const TriggerScriptCoordinator&) = delete;
TriggerScriptCoordinator& operator=(const TriggerScriptCoordinator&) = delete;
// Retrieves all trigger scripts for |deeplink_url| and starts evaluating
// their trigger conditions. Observers will be notified of all relevant status
// updates.
void Start(const GURL& deeplink_url,
std::unique_ptr<TriggerContext> trigger_context);
// Performs |action|. This is usually invoked by the UI as a result of user
// interactions.
void PerformTriggerScriptAction(
TriggerScriptProto::TriggerScriptAction action);
// Called when the user swipe-dismisses the bottom sheet.
void OnBottomSheetClosedWithSwipe();
// Called when the back button is pressed. Returns whether the event was
// handled or not.
bool OnBackButtonPressed();
// Called when the keyboard was shown or hidden.
void OnKeyboardVisibilityChanged(bool visible);
// Called when a trigger script was attempted to be shown on screen. This may
// have failed, for example when trying to show a trigger script after
// switching from CCT to regular tab.
void OnTriggerScriptShown(bool success);
// Called when the tab's interactability has changed. A tab may be
// non-interactable while invisible and/or while the user is in the
// tab-switcher.
void OnTabInteractabilityChanged(bool interactable);
// Called when the proactive help Chrome setting has changed.
void OnProactiveHelpSettingChanged(bool proactive_help_enabled);
void AddObserver(Observer* observer);
void RemoveObserver(const Observer* observer);
private:
friend class TriggerScriptCoordinatorTest;
// From content::WebContentsObserver.
void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override;
void OnVisibilityChanged(content::Visibility visibility) override;
void WebContentsDestroyed() override;
void StartCheckingTriggerConditions();
void StopCheckingTriggerConditions();
void ShowTriggerScript(int index);
void HideTriggerScript();
void CheckDynamicTriggerConditions();
void OnDynamicTriggerConditionsEvaluated(bool is_out_of_schedule);
void OnGetTriggerScripts(int http_status, const std::string& response);
void Stop(Metrics::LiteScriptFinishedState state);
GURL GetCurrentURL() const;
void OnEffectiveVisibilityChanged();
// Can be invoked to trigger an immediate check of the trigger condition,
// reusing the dynamic results of the last time. Does nothing if there are no
// previous results to reuse.
void RunOutOfScheduleTriggerConditionCheck();
void NotifyOnTriggerScriptFinished(Metrics::LiteScriptFinishedState state);
// Used to retrieve deps and also to request shutdown and, if applicable,
// start of the regular script.
Client* client_;
// The original deeplink to request trigger scripts for.
GURL deeplink_url_;
// List of additional domains. If the user leaves the (sub)domain of
// |deeplink_url_| or |additional_allowed_domains_|, the session stops.
std::vector<std::string> additional_allowed_domains_;
// The trigger context for the most recent |Start|. This is stored as a member
// to allow pausing and resuming the same trigger flow.
std::unique_ptr<TriggerContext> trigger_context_;
// Whether the tab is currently visible or not. While invisible or
// non-interactable (e.g., during tab switching), trigger scripts are hidden
// and condition evaluation is suspended (see also
// |web_contents_interactable_|).
bool web_contents_visible_ = true;
// Whether the tab is currently interactable or not. While invisible or
// non-interactable (e.g., during tab switching), trigger scripts are hidden
// and condition evaluation is suspended (see also |web_contents_visible_|).
bool web_contents_interactable_ = true;
// Whether the coordinator is currently checking trigger conditions.
bool is_checking_trigger_conditions_ = false;
// Index of the trigger script that is currently being shown. -1 if no script
// is being shown.
int visible_trigger_script_ = -1;
// Used to request trigger scripts from the backend.
std::unique_ptr<ServiceRequestSender> request_sender_;
// The URL of the server that should be contacted by |request_sender_|.
GURL get_trigger_scripts_server_;
// The web controller to evaluate element conditions.
std::unique_ptr<WebController> web_controller_;
// The list of currently registered observers.
base::ObserverList<Observer> observers_;
// The list of trigger scripts that were fetched from the backend.
std::vector<std::unique_ptr<TriggerScript>> trigger_scripts_;
// Evaluate and cache the results for static and dynamic trigger conditions.
std::unique_ptr<StaticTriggerConditions> static_trigger_conditions_;
std::unique_ptr<DynamicTriggerConditions> dynamic_trigger_conditions_;
// The time between consecutive evaluations of dynamic trigger conditions.
base::TimeDelta trigger_condition_check_interval_ =
base::TimeDelta::FromMilliseconds(1000);
// The number of times the trigger condition may be evaluated. If this reaches
// 0, the trigger script stops with |LITE_SCRIPT_TRIGGER_CONDITION_TIMEOUT|.
// -1 means no limit.
//
// This number is defined by the timeout (specified in proto) divided by
// |trigger_condition_check_interval_|. It resets on tab resume and on UI
// hidden. While the UI is visible, the number does not decrease.
int64_t remaining_trigger_condition_evaluations_ = -1;
// The initial number of evaluations. Used to store the reset value. See
// |remaining_trigger_condition_evaluations_|.
int64_t initial_trigger_condition_evaluations_ = -1;
// The UKM recorder used for metrics.
ukm::UkmRecorder* const ukm_recorder_;
// Flag to ensure that we only get one LiteScriptFinished event per run.
bool finished_state_recorded_ = false;
base::WeakPtrFactory<TriggerScriptCoordinator> weak_ptr_factory_{this};
};
} // namespace autofill_assistant
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_TRIGGER_SCRIPTS_TRIGGER_SCRIPT_COORDINATOR_H_