blob: 33315d27d34305ef23971b1b3f1e95542b4d141f [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 CHROME_BROWSER_UI_VIEWS_USER_EDUCATION_FEATURE_PROMO_CONTROLLER_VIEWS_H_
#define CHROME_BROWSER_UI_VIEWS_USER_EDUCATION_FEATURE_PROMO_CONTROLLER_VIEWS_H_
#include <memory>
#include "base/cancelable_callback.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/token.h"
#include "chrome/browser/ui/user_education/feature_promo_controller.h"
#include "chrome/browser/ui/views/user_education/feature_promo_bubble_owner.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/views/view_tracker.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"
class BrowserView;
class FeaturePromoSnoozeService;
struct FeaturePromoBubbleParams;
namespace base {
struct Feature;
class Token;
} // namespace base
namespace feature_engagement {
class Tracker;
}
// Views implementation of FeaturePromoController. There is one instance
// per window.
class FeaturePromoControllerViews : public FeaturePromoController {
public:
// Create the instance for the given |browser_view|.
explicit FeaturePromoControllerViews(BrowserView* browser_view,
FeaturePromoBubbleOwner* bubble_owner);
~FeaturePromoControllerViews() override;
// Get the appropriate instance for |view|. This finds the BrowserView
// that contains |view| and returns its instance. May return nullptr,
// but if |view| is in a BrowserView's hierarchy it shouldn't.
static FeaturePromoControllerViews* GetForView(views::View* view);
// Repositions the bubble (if showing) relative to the anchor view.
// This should be called whenever the anchor view is potentially
// moved. It is safe to call this if a bubble is not showing.
void UpdateBubbleForAnchorBoundsChange();
// For IPH not registered with |FeaturePromoRegistry|. Only use this
// if it is infeasible to pre-register your IPH.
bool MaybeShowPromoWithParams(
const base::Feature& iph_feature,
const FeaturePromoBubbleParams& params,
views::View* anchor_view,
BubbleCloseCallback close_callback = BubbleCloseCallback());
// Builds the CreateParams from the BubbleParams.
FeaturePromoBubbleView::CreateParams GetBaseCreateParams(
const FeaturePromoBubbleParams& params,
views::View* anchor_view);
// Only for security or privacy critical promos. Immedialy shows a
// promo with |params|, cancelling any normal promo and blocking any
// further promos until it's done.
//
// Returns an ID that can be passed to CloseBubbleForCriticalPromo()
// if successful. This can fail if another critical promo is showing.
absl::optional<base::Token> ShowCriticalPromo(
const FeaturePromoBubbleParams& params,
views::View* anchor_view);
// Ends a promo started by ShowCriticalPromo() if it's still showing.
void CloseBubbleForCriticalPromo(const base::Token& critical_promo_id);
// Returns whether a critical promo is showing for the given `Token`.
bool CriticalPromoIsShowing(const base::Token& critical_promo_id) const;
// For systems where there are rendering issues of e.g. displaying the
// omnibox and a bubble in the same region on the screen, dismisses a non-
// critical promo bubble which overlaps a given screen region. Returns true
// if a bubble is closed as a result.
bool DismissNonCriticalBubbleInRegion(const gfx::Rect& screen_bounds);
// FeaturePromoController:
bool MaybeShowPromo(
const base::Feature& iph_feature,
BubbleCloseCallback close_callback = BubbleCloseCallback()) override;
bool MaybeShowPromoWithTextReplacements(
const base::Feature& iph_feature,
FeaturePromoTextReplacements text_replacements,
BubbleCloseCallback close_callback = BubbleCloseCallback()) override;
bool BubbleIsShowing(const base::Feature& iph_feature) const override;
bool CloseBubble(const base::Feature& iph_feature) override;
PromoHandle CloseBubbleAndContinuePromo(
const base::Feature& iph_feature) override;
// Gets the IPH backend. Provided for convenience.
feature_engagement::Tracker* feature_engagement_tracker() { return tracker_; }
// Blocks a check that the anchor view for the IPH is in an active window
// before showing the IPH. Intended for browser and unit tests.
static void BlockActiveWindowCheckForTesting();
// Returns true if the IPH should be allowed to show in an inactive window.
// False by default, but browser and unit tests may modify this behavior.
static bool IsActiveWindowCheckBlockedForTesting();
// Blocks any further promos from showing. Additionally cancels the
// current promo unless an outstanding PromoHandle from
// CloseBubbleAndContinuePromo exists. Intended for browser tests.
void BlockPromosForTesting();
FeaturePromoSnoozeService* snooze_service_for_testing() {
return snooze_service_.get();
}
private:
bool MaybeShowPromoImpl(const base::Feature& iph_feature,
const FeaturePromoBubbleParams& params,
views::View* anchor_view,
BubbleCloseCallback close_callback);
// Called when PromoHandle is destroyed to finish the promo.
void FinishContinuedPromo() override;
bool ShowPromoBubbleImpl(const FeaturePromoBubbleParams& params,
views::View* anchor_view);
void HandleBubbleClosed();
// Call these methods when the user actively snooze or dismiss the IPH.
void OnUserSnooze(const base::Feature& iph_feature);
void OnUserDismiss(const base::Feature& iph_feature);
// The browser window this instance is responsible for.
BrowserView* const browser_view_;
// The delegate responsible for creating and owning a bubble.
FeaturePromoBubbleOwner* const bubble_owner_;
// Snooze service that is notified when a user snoozes or dismisses the promo.
// Ask this service for display permission before |tracker_|.
std::unique_ptr<FeaturePromoSnoozeService> snooze_service_;
// IPH backend that is notified of user events and decides whether to
// trigger IPH.
feature_engagement::Tracker* const tracker_;
// Non-null as long as a promo is showing. Corresponds to an IPH
// feature registered with |tracker_|.
const base::Feature* current_iph_feature_ = nullptr;
// Bubble ID from `bubble_owner_`, if a bubble is showing.
absl::optional<base::Token> bubble_id_;
// Has a value if a critical promo is showing. If this has a value,
// |current_iph_feature_| will usually be null. There is one edge case
// where this may not be true: when a critical promo is requested
// between a normal promo's CloseBubbleAndContinuePromo() call and its
// end.
absl::optional<base::Token> current_critical_promo_;
// If present, called when |current_iph_feature_|'s bubble stops
// showing. Only valid if |current_iph_feature_| and |promo_bubble_|
// are both non-null.
BubbleCloseCallback close_callback_;
// Stores the bubble anchor view so we can set/unset a highlight on
// it.
views::ViewTracker anchor_view_tracker_;
static bool active_window_check_blocked_for_testing;
bool promos_blocked_for_testing_ = false;
base::WeakPtrFactory<FeaturePromoControllerViews> weak_ptr_factory_{this};
};
#endif // CHROME_BROWSER_UI_VIEWS_USER_EDUCATION_FEATURE_PROMO_CONTROLLER_VIEWS_H_