1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #ifndef COMPONENTS_PERMISSIONS_PERMISSION_REQUEST_MANAGER_H_
6 #define COMPONENTS_PERMISSIONS_PERMISSION_REQUEST_MANAGER_H_
12 #include "base/check_is_test.h"
13 #include "base/gtest_prod_util.h"
14 #include "base/memory/weak_ptr.h"
15 #include "base/observer_list.h"
16 #include "base/time/time.h"
17 #include "base/timer/timer.h"
18 #include "components/permissions/permission_prompt.h"
19 #include "components/permissions/permission_request_queue.h"
20 #include "components/permissions/permission_ui_selector.h"
21 #include "components/permissions/permission_uma_util.h"
22 #include "components/permissions/request_type.h"
23 #include "content/public/browser/global_routing_id.h"
24 #include "content/public/browser/web_contents.h"
25 #include "content/public/browser/web_contents_observer.h"
26 #include "content/public/browser/web_contents_user_data.h"
27 #include "third_party/abseil-cpp/absl/types/optional.h"
28 #include "ui/gfx/geometry/rect.h"
33 class RenderFrameHost;
37 class PermissionRequestManagerTestApi;
40 namespace permissions {
41 class PermissionRequest;
42 enum class PermissionAction;
43 enum class PermissionPromptDisposition;
44 enum class PermissionPromptDispositionReason;
46 // The message to be printed in the Developer Tools console when the quiet
47 // notification permission prompt UI is shown on sites with abusive permission
49 extern const char kAbusiveNotificationRequestsEnforcementMessage[];
51 // The message to be printed in the Developer Tools console when the site is on
52 // the warning list for abusive permission request flows.
53 extern const char kAbusiveNotificationRequestsWarningMessage[];
55 // The message to be printed in the Developer Tools console when the site is on
56 // the blocking list for showing abusive notification content.
57 extern const char kAbusiveNotificationContentEnforcementMessage[];
59 // The message to be printed in the Developer Tools console when the site is on
60 // the warning list for showing abusive notification content.
61 extern const char kAbusiveNotificationContentWarningMessage[];
63 // Provides access to permissions bubbles. Allows clients to add a request
64 // callback interface to the existing permission bubble configuration.
65 // Depending on the situation and policy, that may add new UI to an existing
66 // permission bubble, create and show a new permission bubble, or provide no
67 // visible UI action at all. (In that case, the request will be immediately
68 // informed that the permission request failed.)
70 // A PermissionRequestManager is associated with a particular WebContents.
71 // Requests attached to a particular WebContents' PBM must outlive it.
73 // The PermissionRequestManager should be addressed on the UI thread.
74 class PermissionRequestManager
75 : public content::WebContentsObserver,
76 public content::WebContentsUserData<PermissionRequestManager>,
77 public PermissionPrompt::Delegate {
79 class Observer : public base::CheckedObserver {
81 virtual void OnTabVisibilityChanged(content::Visibility visibility) {}
82 virtual void OnPromptAdded() {}
83 virtual void OnPromptRemoved() {}
84 // Called when recreation of the permission prompt is not possible. It means
85 // that `PermissionRequestManager` is ready to display a prompt but the UI
86 // layer was not able to display it.
87 virtual void OnPromptRecreateViewFailed() {}
88 // Called when permission prompt creation was aborted because the current
89 // tab is no longer visible, hance it is not possible to display a prompt.
90 virtual void OnPromptCreationFailedHiddenTab() {}
91 // Called when the current batch of requests have been handled and the
92 // prompt is no longer visible. Note that there might be some queued
93 // permission requests that will get shown after this. This differs from
94 // OnPromptRemoved() in that the prompt may disappear while there are
95 // still in-flight requests (e.g. when switching tabs while the prompt is
97 virtual void OnRequestsFinalized() {}
99 virtual void OnPermissionRequestManagerDestructed() {}
100 virtual void OnNavigation(content::NavigationHandle* navigation_handle) {}
102 virtual void OnRequestDecided(permissions::PermissionAction action) {}
105 enum AutoResponseType { NONE, ACCEPT_ONCE, ACCEPT_ALL, DENY_ALL, DISMISS };
107 using UiDecision = PermissionUiSelector::Decision;
108 using QuietUiReason = PermissionUiSelector::QuietUiReason;
109 using WarningReason = PermissionUiSelector::WarningReason;
111 ~PermissionRequestManager() override;
113 // Adds a new request to the permission bubble. Ownership of the request
114 // remains with the caller. The caller must arrange for the request to
115 // outlive the PermissionRequestManager. If a bubble is visible when this
116 // call is made, the request will be queued up and shown after the current
117 // bubble closes. A request with message text identical to an outstanding
118 // request will be merged with the outstanding request, and will have the same
119 // callbacks called as the outstanding request.
120 void AddRequest(content::RenderFrameHost* source_frame,
121 PermissionRequest* request);
123 // Will reposition the bubble (may change parent if necessary).
126 // For observing the status of the permission bubble manager.
127 void AddObserver(Observer* observer);
128 void RemoveObserver(Observer* observer);
130 bool IsRequestInProgress() const;
132 // Returns `true` if a permission request is in progress but a prompt view is
134 bool CanRestorePrompt();
136 // Recreates a permission prompt.
137 void RestorePrompt();
139 // Do NOT use this methods in production code. Use this methods in browser
140 // tests that need to accept or deny permissions when requested in
141 // JavaScript. Your test needs to set this appropriately, and then the bubble
142 // will proceed as desired as soon as Show() is called.
143 void set_auto_response_for_test(AutoResponseType response) {
144 auto_response_for_test_ = response;
147 // WebContentsObserver:
148 void DidStartNavigation(
149 content::NavigationHandle* navigation_handle) override;
150 void DidFinishNavigation(
151 content::NavigationHandle* navigation_handle) override;
152 void DocumentOnLoadCompletedInPrimaryMainFrame() override;
153 void DOMContentLoaded(content::RenderFrameHost* render_frame_host) override;
154 void WebContentsDestroyed() override;
155 void OnVisibilityChanged(content::Visibility visibility) override;
157 // PermissionPrompt::Delegate:
158 const std::vector<PermissionRequest*>& Requests() override;
159 GURL GetRequestingOrigin() const override;
160 GURL GetEmbeddingOrigin() const override;
161 void Accept() override;
162 void AcceptThisTime() override;
163 void Deny() override;
164 void Dismiss() override;
165 void Ignore() override;
166 void FinalizeCurrentRequests() override;
167 void OpenHelpCenterLink(const ui::Event& event) override;
168 void PreIgnoreQuietPrompt() override;
169 bool WasCurrentRequestAlreadyDisplayed() override;
170 bool ShouldDropCurrentRequestIfCannotShowQuietly() const override;
171 bool ShouldCurrentRequestUseQuietUI() const override;
172 absl::optional<PermissionUiSelector::QuietUiReason> ReasonForUsingQuietUi()
174 void SetDismissOnTabClose() override;
175 void SetPromptShown() override;
176 void SetDecisionTime() override;
177 void SetManageClicked() override;
178 void SetLearnMoreClicked() override;
179 base::WeakPtr<PermissionPrompt::Delegate> GetWeakPtr() override;
180 content::WebContents* GetAssociatedWebContents() override;
181 bool RecreateView() override;
183 // Returns the bounds of the active permission prompt view if we're
185 absl::optional<gfx::Rect> GetPromptBubbleViewBoundsInScreen() const;
187 void set_manage_clicked() { did_click_manage_ = true; }
188 void set_learn_more_clicked() { did_click_learn_more_ = true; }
190 void set_web_contents_supports_permission_requests(
191 bool web_contents_supports_permission_requests) {
192 web_contents_supports_permission_requests_ =
193 web_contents_supports_permission_requests;
196 // For testing only, used to override the default UI selectors and instead use
198 void set_permission_ui_selector_for_testing(
199 std::unique_ptr<PermissionUiSelector> selector) {
200 clear_permission_ui_selector_for_testing();
201 add_permission_ui_selector_for_testing(std::move(selector));
204 // For testing only, used to add a new selector without overriding the
206 void add_permission_ui_selector_for_testing(
207 std::unique_ptr<PermissionUiSelector> selector) {
208 permission_ui_selectors_.emplace_back(std::move(selector));
211 // For testing only, clear the existing ui selectors.
212 void clear_permission_ui_selector_for_testing() {
213 permission_ui_selectors_.clear();
216 // Getter for testing.
217 const std::vector<std::unique_ptr<PermissionUiSelector>>&
218 get_permission_ui_selectors_for_testing() {
219 return permission_ui_selectors_;
222 void set_view_factory_for_testing(PermissionPrompt::Factory view_factory) {
223 view_factory_ = std::move(view_factory);
226 PermissionPrompt* view_for_testing() const { return view_.get(); }
228 void set_current_request_first_display_time_for_testing(base::Time time) {
229 current_request_first_display_time_ = time;
232 absl::optional<PermissionUmaUtil::PredictionGrantLikelihood>
233 prediction_grant_likelihood_for_testing() const {
234 return prediction_grant_likelihood_;
237 absl::optional<permissions::PermissionPromptDisposition>
238 current_request_prompt_disposition_for_testing() const {
239 return current_request_prompt_disposition_;
242 void set_time_to_decision_for_test(base::TimeDelta time_to_decision) {
243 time_to_decision_for_test_ = time_to_decision;
246 void set_enabled_app_level_notification_permission_for_testing(bool enabled) {
247 enabled_app_level_notification_permission_for_testing_ = enabled;
250 void set_embedding_origin_for_testing(const GURL& embedding_origin) {
251 embedding_origin_for_testing_ = embedding_origin;
254 base::ObserverList<Observer>* get_observer_list_for_testing() {
256 return &observer_list_;
259 bool has_pending_requests() {
260 return !pending_permission_requests_.IsEmpty();
263 void SetHatsShownCallback(base::OnceCallback<void()> callback) override;
266 friend class test::PermissionRequestManagerTestApi;
267 friend class content::WebContentsUserData<PermissionRequestManager>;
268 FRIEND_TEST_ALL_PREFIXES(PermissionRequestManagerTest, WeakDuplicateRequests);
269 FRIEND_TEST_ALL_PREFIXES(PermissionRequestManagerTest,
270 WeakDuplicateRequestsAccept);
272 explicit PermissionRequestManager(content::WebContents* web_contents);
274 // Defines how to handle the current request, when new requests arrive
275 enum class CurrentRequestFate {
276 // Keep showing the current request. The incoming requests should not take
277 // priority over the current request and will be pushed to pending requests
281 // Put the current request back to the pending requests queue for displaying
282 // later when it returns to the front of the queue.
285 // Finalize/ignore the current request and show the new request.
289 // Reprioritize the current requests (preempting, finalizing) based on what
290 // type of UI has been shown for `requests_` and current pending requests
292 // Determine the next request candidate would be prompted later and push the
293 // candidate to front of the pending queue.
294 // Return true if we keep showing the current request, otherwise return false
295 bool ReprioritizeCurrentRequestIfNeeded();
297 // Validate the input request. If the request is invalid and |should_finalize|
298 // is set, cancel and remove it from *_map_ and *_set_.
299 // Return true if the request is valid, otherwise false.
300 bool ValidateRequest(PermissionRequest* request, bool should_finalize = true);
302 // Adds `request` into `pending_permission_requests_`, and request's
303 // `source_frame` into `request_sources_map_`.
304 void QueueRequest(content::RenderFrameHost* source_frame,
305 PermissionRequest* request);
307 // Because the requests are shown in a different order for Normal and Quiet
308 // Chip, pending requests are returned back to pending_permission_requests_ to
309 // process them after the new requests.
310 void PreemptAndRequeueCurrentRequest();
312 // If a request isn't already in progress, dequeue and show the request
314 void DequeueRequestIfNeeded();
316 // Schedule a call to |DequeueRequestIfNeeded|. Is needed to ensure requests
317 // that can be grouped together have time to all be added to the queue.
318 void ScheduleDequeueRequestIfNeeded();
320 // Shows the prompt for a request that has just been dequeued, or re-show a
321 // prompt after switching tabs away and back.
324 // Delete the view object
328 void ResetViewStateForCurrentRequest();
330 // Records metrics and informs embargo and autoblocker about the requests
331 // being decided. Based on |view_->ShouldFinalizeRequestAfterDecided()| it
332 // will also call |FinalizeCurrentRequests()|. Otherwise a separate
333 // |FinalizeCurrentRequests()| call must be made to release the |view_|.
334 void CurrentRequestsDecided(PermissionAction permission_action);
336 // Cancel all pending or active requests and destroy the PermissionPrompt if
337 // one exists. This is called if the WebContents is destroyed or navigates its
339 void CleanUpRequests();
341 // Searches |requests_| and |pending_permission_requests_| - but *not*
342 // |duplicate_requests_| - for a request matching |request|, and returns the
343 // matching request, or |nullptr| if no match. Note that the matching request
344 // may or may not be the same object as |request|.
345 PermissionRequest* GetExistingRequest(PermissionRequest* request) const;
347 // Returns an iterator into |duplicate_requests_|, points the matching list,
348 // or duplicate_requests_.end() if no match. The matching list contains all
349 // the weak requests which are duplicate of the given |request| (see
351 using WeakPermissionRequestList =
352 std::list<std::list<base::WeakPtr<PermissionRequest>>>;
353 WeakPermissionRequestList::iterator FindDuplicateRequestList(
354 PermissionRequest* request);
356 // Trigger |visitor| for each live weak request which matches the given
357 // request (see |IsDuplicateOf|) in the |duplicate_requests_|. Returns an
358 // iterator into |duplicate_requests_|, points the matching list, or
359 // duplicate_requests_.end() if no match.
360 using DuplicateRequestVisitor =
361 base::RepeatingCallback<void(const base::WeakPtr<PermissionRequest>&)>;
362 WeakPermissionRequestList::iterator VisitDuplicateRequests(
363 DuplicateRequestVisitor visitor,
364 PermissionRequest* request);
366 // Calls PermissionGranted on a request and all its duplicates.
367 void PermissionGrantedIncludingDuplicates(PermissionRequest* request,
369 // Calls PermissionDenied on a request and all its duplicates.
370 void PermissionDeniedIncludingDuplicates(PermissionRequest* request);
371 // Calls Cancelled on a request and all its duplicates.
372 void CancelledIncludingDuplicates(PermissionRequest* request,
373 bool is_final_decision = true);
374 // Calls RequestFinished on a request and all its duplicates.
375 void RequestFinishedIncludingDuplicates(PermissionRequest* request);
377 void NotifyTabVisibilityChanged(content::Visibility visibility);
378 void NotifyPromptAdded();
379 void NotifyPromptRemoved();
380 void NotifyPromptRecreateFailed();
381 void NotifyPromptCreationFailedHiddenTab();
382 void NotifyRequestDecided(permissions::PermissionAction permission_action);
384 void StorePermissionActionForUMA(const GURL& origin,
385 RequestType request_type,
386 PermissionAction permission_action);
388 void OnPermissionUiSelectorDone(size_t selector_index,
389 const UiDecision& decision);
391 PermissionPromptDisposition DetermineCurrentRequestUIDisposition();
392 PermissionPromptDispositionReason
393 DetermineCurrentRequestUIDispositionReasonForUMA();
395 void LogWarningToConsole(const char* message);
397 void DoAutoResponseForTesting();
399 void PreIgnoreQuietPromptInternal();
401 // Returns true if there is a request in progress that is initiated by an
402 // embedded permission element.
403 bool IsCurrentRequestEmbeddedPermissionElementInitiated() const;
405 // Returns true when the current request should be finalized together with the
406 // permission decision.
407 bool ShouldFinalizeRequestAfterDecided(PermissionAction action) const;
409 // Factory to be used to create views when needed.
410 PermissionPrompt::Factory view_factory_;
412 // The UI surface for an active permission prompt if we're displaying one.
413 // On Desktop, we destroy this upon tab switching, while on Android we keep
414 // the object alive. The infobar system hides the actual infobar UI and modals
415 // prevent tab switching.
416 std::unique_ptr<PermissionPrompt> view_;
418 // The disposition for the currently active permission prompt, if any.
419 // Recorded separately because the `view_` might not be available at prompt
420 // resolution in order to determine the disposition.
421 absl::optional<permissions::PermissionPromptDisposition>
422 current_request_prompt_disposition_;
424 // We only show new prompts when |tab_is_hidden_| is false.
427 // The request (or requests) that the user is currently being prompted for.
428 // When this is non-empty, the |view_| is generally non-null as long as the
430 std::vector<PermissionRequest*> requests_;
432 struct PermissionRequestSource {
433 content::GlobalRenderFrameHostId requesting_frame_id;
435 bool IsSourceFrameInactiveAndDisallowActivation() const;
438 PermissionRequestQueue pending_permission_requests_;
440 // Stores the weak pointers of duplicated requests in a 2D list.
441 WeakPermissionRequestList duplicate_requests_;
443 // Maps each PermissionRequest currently in |requests_| or
444 // |pending_permission_requests_| to which RenderFrameHost it originated from.
445 // Note that no date is stored for |duplicate_requests_|.
446 std::map<PermissionRequest*, PermissionRequestSource> request_sources_map_;
448 // Sequence of requests from pending queue will be marked as validated, when
449 // we are extracting a group of requests from the queue to show to user. This
450 // is an immature solution to avoid an infinitive loop of preempting, we would
451 // not prempt a request if the incoming request is already validated.
452 std::set<PermissionRequest*> validated_requests_set_;
454 base::ObserverList<Observer> observer_list_;
455 AutoResponseType auto_response_for_test_ = NONE;
457 // Suppress notification permission prompts in this tab, regardless of the
458 // origin requesting the permission.
459 bool is_notification_prompt_cooldown_active_ = false;
461 // A vector of selectors which decide if the quiet prompt UI should be used
462 // to display permission requests. Sorted from the highest priority to the
463 // lowest priority selector.
464 std::vector<std::unique_ptr<PermissionUiSelector>> permission_ui_selectors_;
466 // Holds the decisions returned by selectors. Needed in case a lower priority
467 // selector returns a decision first and we need to wait for the decisions of
468 // higher priority selectors before making use of it.
469 std::vector<absl::optional<PermissionUiSelector::Decision>>
472 // Whether the view for the current |requests_| has been shown to the user at
474 bool current_request_already_displayed_ = false;
476 // When the view for the current |requests_| has been first shown to the user,
477 // or zero if not at all.
478 base::Time current_request_first_display_time_;
480 // Whether to use the normal or quiet UI to display the current permission
481 // |requests_|, and whether to show warnings. This will be nullopt if we are
482 // still waiting on the result from |permission_ui_selectors_|.
483 absl::optional<UiDecision> current_request_ui_to_use_;
485 // The likelihood value returned by the Web Permission Predictions Service,
486 // to be recoreded in UKM.
487 absl::optional<PermissionUmaUtil::PredictionGrantLikelihood>
488 prediction_grant_likelihood_;
490 // Status of the decision made by the Web Permission Prediction Service, if
491 // it was held back or not.
492 absl::optional<bool> was_decision_held_back_;
494 // True when the prompt is being temporary destroyed to be recreated for the
495 // correct browser or when the tab is hidden. In those cases, callbacks from
496 // the bubble itself should be ignored.
497 bool ignore_callbacks_from_prompt_ = false;
499 // Whether the web contents associated with this request manager supports
500 // permission prompts.
501 bool web_contents_supports_permission_requests_ = true;
503 // Whether the current request should be dismissed if the current tab is
505 bool should_dismiss_current_request_ = false;
507 // Whether the permission prompt was shown for the current request.
508 bool did_show_prompt_ = false;
510 // When the user made any decision for the current |requests_|, or zero if not
512 base::Time current_request_decision_time_;
514 bool did_click_manage_ = false;
516 bool did_click_learn_more_ = false;
518 absl::optional<base::TimeDelta> time_to_decision_for_test_;
520 absl::optional<bool> enabled_app_level_notification_permission_for_testing_;
522 absl::optional<GURL> embedding_origin_for_testing_;
524 // A timer is used to pre-ignore the permission request if it's been displayed
526 base::OneShotTimer preignore_timer_;
528 absl::optional<base::OnceCallback<void()>> hats_shown_callback_;
530 base::WeakPtrFactory<PermissionRequestManager> weak_factory_{this};
531 WEB_CONTENTS_USER_DATA_KEY_DECL();
534 } // namespace permissions
536 #endif // COMPONENTS_PERMISSIONS_PERMISSION_REQUEST_MANAGER_H_