1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef CHROME_BROWSER_UI_VIEWS_USER_EDUCATION_FEATURE_PROMO_CONTROLLER_VIEWS_H_
6 #define CHROME_BROWSER_UI_VIEWS_USER_EDUCATION_FEATURE_PROMO_CONTROLLER_VIEWS_H_
7 
8 #include <memory>
9 
10 #include "base/memory/weak_ptr.h"
11 #include "base/optional.h"
12 #include "base/scoped_observer.h"
13 #include "base/token.h"
14 #include "chrome/browser/ui/user_education/feature_promo_controller.h"
15 #include "ui/views/view_tracker.h"
16 #include "ui/views/widget/widget.h"
17 #include "ui/views/widget/widget_observer.h"
18 
19 class BrowserView;
20 class FeaturePromoBubbleView;
21 struct FeaturePromoBubbleParams;
22 class FeaturePromoSnoozeService;
23 
24 namespace base {
25 struct Feature;
26 class Token;
27 }  // namespace base
28 
29 namespace feature_engagement {
30 class Tracker;
31 }
32 
33 // Views implementation of FeaturePromoController. There is one instance
34 // per window.
35 class FeaturePromoControllerViews : public FeaturePromoController,
36                                     public views::WidgetObserver {
37  public:
38   // Create the instance for the given |browser_view|.
39   explicit FeaturePromoControllerViews(BrowserView* browser_view);
40   ~FeaturePromoControllerViews() override;
41 
42   // Get the appropriate instance for |view|. This finds the BrowserView
43   // that contains |view| and returns its instance. May return nullptr,
44   // but if |view| is in a BrowserView's hierarchy it shouldn't.
45   static FeaturePromoControllerViews* GetForView(views::View* view);
46 
47   // Repositions the bubble (if showing) relative to the anchor view.
48   // This should be called whenever the anchor view is potentially
49   // moved. It is safe to call this if a bubble is not showing.
50   void UpdateBubbleForAnchorBoundsChange();
51 
52   // For IPH not registered with |FeaturePromoRegistry|. Only use this
53   // if it is infeasible to pre-register your IPH.
54   bool MaybeShowPromoWithParams(
55       const base::Feature& iph_feature,
56       const FeaturePromoBubbleParams& params,
57       BubbleCloseCallback close_callback = BubbleCloseCallback());
58 
59   // Only for security or privacy critical promos. Immedialy shows a
60   // promo with |params|, cancelling any normal promo and blocking any
61   // further promos until it's done.
62   //
63   // Returns an ID that can be passed to CloseBubbleForCriticalPromo()
64   // if successful. This can fail if another critical promo is showing.
65   base::Optional<base::Token> ShowCriticalPromo(
66       const FeaturePromoBubbleParams& params);
67 
68   // Ends a promo started by ShowCriticalPromo() if it's still showing.
69   void CloseBubbleForCriticalPromo(const base::Token& critical_promo_id);
70 
71   // FeaturePromoController:
72   bool MaybeShowPromo(
73       const base::Feature& iph_feature,
74       BubbleCloseCallback close_callback = BubbleCloseCallback()) override;
75   bool BubbleIsShowing(const base::Feature& iph_feature) const override;
76   bool CloseBubble(const base::Feature& iph_feature) override;
77   PromoHandle CloseBubbleAndContinuePromo(
78       const base::Feature& iph_feature) override;
79 
80   // views::WidgetObserver:
81   void OnWidgetClosing(views::Widget* widget) override;
82   void OnWidgetDestroying(views::Widget* widget) override;
83 
84   // Gets the IPH backend. Provided for convenience.
feature_engagement_tracker()85   feature_engagement::Tracker* feature_engagement_tracker() { return tracker_; }
86 
87   // Blocks any further promos from showing. Additionally cancels the
88   // current promo unless an outstanding PromoHandle from
89   // CloseBubbleAndContinuePromo exists. Intended for browser tests.
90   void BlockPromosForTesting();
91 
promo_bubble_for_testing()92   FeaturePromoBubbleView* promo_bubble_for_testing() { return promo_bubble_; }
promo_bubble_for_testing()93   const FeaturePromoBubbleView* promo_bubble_for_testing() const {
94     return promo_bubble_;
95   }
96 
snooze_service_for_testing()97   FeaturePromoSnoozeService* snooze_service_for_testing() {
98     return snooze_service_.get();
99   }
100 
101  private:
102   // Called when PromoHandle is destroyed to finish the promo.
103   void FinishContinuedPromo() override;
104 
105   void ShowPromoBubbleImpl(const FeaturePromoBubbleParams& params);
106 
107   void HandleBubbleClosed();
108 
109   // Call these methods when the user actively snooze or dismiss the IPH.
110   void OnUserSnooze(const base::Feature& iph_feature);
111   void OnUserDismiss(const base::Feature& iph_feature);
112 
113   // The browser window this instance is responsible for.
114   BrowserView* const browser_view_;
115 
116   // Snooze service that is notified when a user snoozes or dismisses the promo.
117   // Ask this service for display permission before |tracker_|.
118   std::unique_ptr<FeaturePromoSnoozeService> snooze_service_;
119 
120   // IPH backend that is notified of user events and decides whether to
121   // trigger IPH.
122   feature_engagement::Tracker* const tracker_;
123 
124   // Non-null as long as a promo is showing. Corresponds to an IPH
125   // feature registered with |tracker_|.
126   const base::Feature* current_iph_feature_ = nullptr;
127 
128   // Has a value if a critical promo is showing. If this has a value,
129   // |current_iph_feature_| will usually be null. There is one edge case
130   // where this may not be true: when a critical promo is requested
131   // between a normal promo's CloseBubbleAndContinuePromo() call and its
132   // end.
133   base::Optional<base::Token> current_critical_promo_;
134 
135   // The bubble currently showing, if any.
136   FeaturePromoBubbleView* promo_bubble_ = nullptr;
137 
138   // If present, called when |current_iph_feature_|'s bubble stops
139   // showing. Only valid if |current_iph_feature_| and |promo_bubble_|
140   // are both non-null.
141   BubbleCloseCallback close_callback_;
142 
143   // Stores the bubble anchor view so we can set/unset a highlight on
144   // it.
145   views::ViewTracker anchor_view_tracker_;
146 
147   bool promos_blocked_for_testing_ = false;
148 
149   ScopedObserver<views::Widget, views::WidgetObserver> widget_observer_{this};
150 
151   base::WeakPtrFactory<FeaturePromoController> weak_ptr_factory_{this};
152 };
153 
154 #endif  // CHROME_BROWSER_UI_VIEWS_USER_EDUCATION_FEATURE_PROMO_CONTROLLER_VIEWS_H_
155