Rename GetIconID to GetIconId
[chromium-blink-merge.git] / chrome / browser / ui / website_settings / permission_bubble_manager.cc
blob201d22fc81e4dfc337550f5228d583b7819400d0
1 // Copyright 2014 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.
5 #include "chrome/browser/ui/website_settings/permission_bubble_manager.h"
7 #include "base/command_line.h"
8 #include "base/metrics/user_metrics_action.h"
9 #include "chrome/browser/ui/website_settings/permission_bubble_request.h"
10 #include "chrome/common/chrome_switches.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/navigation_details.h"
13 #include "content/public/browser/user_metrics.h"
15 #if !defined(OS_ANDROID)
16 #include "chrome/browser/ui/browser_finder.h"
17 #endif
19 namespace {
21 class CancelledRequest : public PermissionBubbleRequest {
22 public:
23 explicit CancelledRequest(PermissionBubbleRequest* cancelled)
24 : icon_(cancelled->GetIconId()),
25 message_text_(cancelled->GetMessageText()),
26 message_fragment_(cancelled->GetMessageTextFragment()),
27 user_gesture_(cancelled->HasUserGesture()),
28 hostname_(cancelled->GetRequestingHostname()) {}
29 ~CancelledRequest() override {}
31 int GetIconId() const override { return icon_; }
32 base::string16 GetMessageText() const override { return message_text_; }
33 base::string16 GetMessageTextFragment() const override {
34 return message_fragment_;
36 bool HasUserGesture() const override { return user_gesture_; }
37 GURL GetRequestingHostname() const override { return hostname_; }
39 // These are all no-ops since the placeholder is non-forwarding.
40 void PermissionGranted() override {}
41 void PermissionDenied() override {}
42 void Cancelled() override {}
44 void RequestFinished() override { delete this; }
46 private:
47 int icon_;
48 base::string16 message_text_;
49 base::string16 message_fragment_;
50 bool user_gesture_;
51 GURL hostname_;
54 } // namespace
56 // PermissionBubbleManager::Observer -------------------------------------------
58 PermissionBubbleManager::Observer::~Observer() {
61 void PermissionBubbleManager::Observer::OnBubbleAdded() {
64 // PermissionBubbleManager -----------------------------------------------------
66 DEFINE_WEB_CONTENTS_USER_DATA_KEY(PermissionBubbleManager);
68 // static
69 bool PermissionBubbleManager::Enabled() {
70 #if defined(OS_ANDROID) || defined(OS_IOS)
71 return false;
72 #endif
73 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
74 switches::kDisablePermissionsBubbles))
75 return false;
76 return true;
79 PermissionBubbleManager::PermissionBubbleManager(
80 content::WebContents* web_contents)
81 : content::WebContentsObserver(web_contents),
82 require_user_gesture_(false),
83 #if !defined(OS_ANDROID) // No bubbles in android tests.
84 view_factory_(base::Bind(&PermissionBubbleView::Create)),
85 #endif
86 view_(nullptr),
87 main_frame_has_fully_loaded_(false),
88 auto_response_for_test_(NONE),
89 weak_factory_(this) {
92 PermissionBubbleManager::~PermissionBubbleManager() {
93 if (view_ != NULL)
94 view_->SetDelegate(NULL);
96 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
97 for (requests_iter = requests_.begin();
98 requests_iter != requests_.end();
99 requests_iter++) {
100 (*requests_iter)->RequestFinished();
102 for (requests_iter = queued_requests_.begin();
103 requests_iter != queued_requests_.end();
104 requests_iter++) {
105 (*requests_iter)->RequestFinished();
109 void PermissionBubbleManager::AddRequest(PermissionBubbleRequest* request) {
110 content::RecordAction(base::UserMetricsAction("PermissionBubbleRequest"));
111 // TODO(gbillock): is there a race between an early request on a
112 // newly-navigated page and the to-be-cleaned-up requests on the previous
113 // page? We should maybe listen to DidStartNavigationToPendingEntry (and
114 // any other renderer-side nav initiations?). Double-check this for
115 // correct behavior on interstitials -- we probably want to basically queue
116 // any request for which GetVisibleURL != GetLastCommittedURL.
117 request_url_ = web_contents()->GetLastCommittedURL();
118 bool is_main_frame =
119 request->GetRequestingHostname().GetOrigin() == request_url_.GetOrigin();
121 // Don't re-add an existing request or one with a duplicate text request.
122 bool same_object = false;
123 if (ExistingRequest(request, requests_, &same_object) ||
124 ExistingRequest(request, queued_requests_, &same_object) ||
125 ExistingRequest(request, queued_frame_requests_, &same_object)) {
126 if (!same_object)
127 request->RequestFinished();
128 return;
131 if (IsBubbleVisible()) {
132 if (is_main_frame) {
133 content::RecordAction(
134 base::UserMetricsAction("PermissionBubbleRequestQueued"));
135 queued_requests_.push_back(request);
136 } else {
137 content::RecordAction(
138 base::UserMetricsAction("PermissionBubbleIFrameRequestQueued"));
139 queued_frame_requests_.push_back(request);
141 return;
144 if (is_main_frame) {
145 requests_.push_back(request);
146 // TODO(gbillock): do we need to make default state a request property?
147 accept_states_.push_back(true);
148 } else {
149 content::RecordAction(
150 base::UserMetricsAction("PermissionBubbleIFrameRequestQueued"));
151 queued_frame_requests_.push_back(request);
154 if (!require_user_gesture_ || request->HasUserGesture())
155 ScheduleShowBubble();
158 void PermissionBubbleManager::CancelRequest(PermissionBubbleRequest* request) {
159 // First look in the queued requests, where we can simply delete the request
160 // and go on.
161 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
162 for (requests_iter = queued_requests_.begin();
163 requests_iter != queued_requests_.end();
164 requests_iter++) {
165 if (*requests_iter == request) {
166 (*requests_iter)->RequestFinished();
167 queued_requests_.erase(requests_iter);
168 return;
172 std::vector<bool>::iterator accepts_iter = accept_states_.begin();
173 for (requests_iter = requests_.begin(), accepts_iter = accept_states_.begin();
174 requests_iter != requests_.end();
175 requests_iter++, accepts_iter++) {
176 if (*requests_iter != request)
177 continue;
179 // We can simply erase the current entry in the request table if we aren't
180 // showing the dialog, or if we are showing it and it can accept the update.
181 bool can_erase = !IsBubbleVisible() || view_->CanAcceptRequestUpdate();
182 if (can_erase) {
183 (*requests_iter)->RequestFinished();
184 requests_.erase(requests_iter);
185 accept_states_.erase(accepts_iter);
187 if (IsBubbleVisible()) {
188 view_->Hide();
189 // Will redraw the bubble if it is being shown.
190 TriggerShowBubble();
192 return;
195 // Cancel the existing request and replace it with a dummy.
196 PermissionBubbleRequest* cancelled_request =
197 new CancelledRequest(*requests_iter);
198 (*requests_iter)->RequestFinished();
199 *requests_iter = cancelled_request;
200 return;
203 NOTREACHED(); // Callers should not cancel requests that are not pending.
206 void PermissionBubbleManager::HideBubble() {
207 // Disengage from the existing view if there is one.
208 if (!view_)
209 return;
211 view_->SetDelegate(nullptr);
212 view_->Hide();
213 view_.reset();
216 void PermissionBubbleManager::DisplayPendingRequests() {
217 if (IsBubbleVisible())
218 return;
220 #if defined(OS_ANDROID)
221 NOTREACHED();
222 return;
223 #else
224 view_ = view_factory_.Run(chrome::FindBrowserWithWebContents(web_contents()));
225 view_->SetDelegate(this);
226 #endif
228 TriggerShowBubble();
231 void PermissionBubbleManager::UpdateAnchorPosition() {
232 if (view_)
233 view_->UpdateAnchorPosition();
236 bool PermissionBubbleManager::IsBubbleVisible() {
237 return view_ && view_->IsVisible();
240 gfx::NativeWindow PermissionBubbleManager::GetBubbleWindow() {
241 if (view_)
242 return view_->GetNativeWindow();
243 return nullptr;
246 void PermissionBubbleManager::RequireUserGesture(bool required) {
247 require_user_gesture_ = required;
250 void PermissionBubbleManager::DidNavigateMainFrame(
251 const content::LoadCommittedDetails& details,
252 const content::FrameNavigateParams& params) {
253 if (details.is_in_page)
254 return;
256 CancelPendingQueues();
257 FinalizeBubble();
258 main_frame_has_fully_loaded_ = false;
261 void PermissionBubbleManager::DocumentOnLoadCompletedInMainFrame() {
262 main_frame_has_fully_loaded_ = true;
263 // This is scheduled because while all calls to the browser have been
264 // issued at DOMContentLoaded, they may be bouncing around in scheduled
265 // callbacks finding the UI thread still. This makes sure we allow those
266 // scheduled calls to AddRequest to complete before we show the page-load
267 // permissions bubble.
268 ScheduleShowBubble();
271 void PermissionBubbleManager::DocumentLoadedInFrame(
272 content::RenderFrameHost* render_frame_host) {
273 ScheduleShowBubble();
276 void PermissionBubbleManager::WebContentsDestroyed() {
277 // If the web contents has been destroyed, treat the bubble as cancelled.
278 CancelPendingQueues();
279 FinalizeBubble();
281 // The WebContents is going away; be aggressively paranoid and delete
282 // ourselves lest other parts of the system attempt to add permission bubbles
283 // or use us otherwise during the destruction.
284 web_contents()->RemoveUserData(UserDataKey());
285 // That was the equivalent of "delete this". This object is now destroyed;
286 // returning from this function is the only safe thing to do.
289 void PermissionBubbleManager::ToggleAccept(int request_index, bool new_value) {
290 DCHECK(request_index < static_cast<int>(accept_states_.size()));
291 accept_states_[request_index] = new_value;
294 void PermissionBubbleManager::Accept() {
295 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
296 std::vector<bool>::iterator accepts_iter = accept_states_.begin();
297 for (requests_iter = requests_.begin(), accepts_iter = accept_states_.begin();
298 requests_iter != requests_.end();
299 requests_iter++, accepts_iter++) {
300 if (*accepts_iter)
301 (*requests_iter)->PermissionGranted();
302 else
303 (*requests_iter)->PermissionDenied();
305 FinalizeBubble();
308 void PermissionBubbleManager::Deny() {
309 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
310 for (requests_iter = requests_.begin();
311 requests_iter != requests_.end();
312 requests_iter++) {
313 (*requests_iter)->PermissionDenied();
315 FinalizeBubble();
318 void PermissionBubbleManager::Closing() {
319 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
320 for (requests_iter = requests_.begin();
321 requests_iter != requests_.end();
322 requests_iter++) {
323 (*requests_iter)->Cancelled();
325 FinalizeBubble();
328 void PermissionBubbleManager::ScheduleShowBubble() {
329 // ::ScheduleShowBubble() will be called again when the main frame will be
330 // loaded.
331 if (!main_frame_has_fully_loaded_)
332 return;
334 content::BrowserThread::PostTask(
335 content::BrowserThread::UI,
336 FROM_HERE,
337 base::Bind(&PermissionBubbleManager::TriggerShowBubble,
338 weak_factory_.GetWeakPtr()));
341 void PermissionBubbleManager::TriggerShowBubble() {
342 if (!view_)
343 return;
344 if (IsBubbleVisible())
345 return;
346 if (!main_frame_has_fully_loaded_)
347 return;
348 if (requests_.empty() && queued_requests_.empty() &&
349 queued_frame_requests_.empty()) {
350 return;
353 if (requests_.empty()) {
354 // Queues containing a user-gesture-generated request have priority.
355 if (HasUserGestureRequest(queued_requests_))
356 requests_.swap(queued_requests_);
357 else if (HasUserGestureRequest(queued_frame_requests_))
358 requests_.swap(queued_frame_requests_);
359 else if (queued_requests_.size())
360 requests_.swap(queued_requests_);
361 else
362 requests_.swap(queued_frame_requests_);
364 // Sets the default value for each request to be 'accept'.
365 // TODO(leng): Currently all requests default to true. If that changes:
366 // a) Add additional accept_state queues to store default values.
367 // b) Change the request API to provide the default value.
368 accept_states_.resize(requests_.size(), true);
371 view_->Show(requests_, accept_states_);
372 NotifyBubbleAdded();
374 // If in testing mode, automatically respond to the bubble that was shown.
375 if (auto_response_for_test_ != NONE)
376 DoAutoResponseForTesting();
379 void PermissionBubbleManager::FinalizeBubble() {
380 if (view_)
381 view_->Hide();
383 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
384 for (requests_iter = requests_.begin();
385 requests_iter != requests_.end();
386 requests_iter++) {
387 (*requests_iter)->RequestFinished();
389 requests_.clear();
390 accept_states_.clear();
391 if (queued_requests_.size() || queued_frame_requests_.size())
392 TriggerShowBubble();
393 else
394 request_url_ = GURL();
397 void PermissionBubbleManager::CancelPendingQueues() {
398 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
399 for (requests_iter = queued_requests_.begin();
400 requests_iter != queued_requests_.end();
401 requests_iter++) {
402 (*requests_iter)->RequestFinished();
404 for (requests_iter = queued_frame_requests_.begin();
405 requests_iter != queued_frame_requests_.end();
406 requests_iter++) {
407 (*requests_iter)->RequestFinished();
409 queued_requests_.clear();
410 queued_frame_requests_.clear();
413 bool PermissionBubbleManager::ExistingRequest(
414 PermissionBubbleRequest* request,
415 const std::vector<PermissionBubbleRequest*>& queue,
416 bool* same_object) {
417 CHECK(same_object);
418 *same_object = false;
419 std::vector<PermissionBubbleRequest*>::const_iterator iter;
420 for (iter = queue.begin(); iter != queue.end(); iter++) {
421 if (*iter == request) {
422 *same_object = true;
423 return true;
425 if ((*iter)->GetMessageTextFragment() ==
426 request->GetMessageTextFragment() &&
427 (*iter)->GetRequestingHostname() == request->GetRequestingHostname()) {
428 return true;
431 return false;
434 bool PermissionBubbleManager::HasUserGestureRequest(
435 const std::vector<PermissionBubbleRequest*>& queue) {
436 std::vector<PermissionBubbleRequest*>::const_iterator iter;
437 for (iter = queue.begin(); iter != queue.end(); iter++) {
438 if ((*iter)->HasUserGesture())
439 return true;
441 return false;
444 void PermissionBubbleManager::AddObserver(Observer* observer) {
445 observer_list_.AddObserver(observer);
448 void PermissionBubbleManager::RemoveObserver(Observer* observer) {
449 observer_list_.RemoveObserver(observer);
452 void PermissionBubbleManager::NotifyBubbleAdded() {
453 FOR_EACH_OBSERVER(Observer, observer_list_, OnBubbleAdded());
456 void PermissionBubbleManager::DoAutoResponseForTesting() {
457 switch (auto_response_for_test_) {
458 case ACCEPT_ALL:
459 Accept();
460 break;
461 case DENY_ALL:
462 Deny();
463 break;
464 case DISMISS:
465 Closing();
466 break;
467 case NONE:
468 NOTREACHED();