Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(277)

Side by Side Diff: chrome/browser/ui/website_settings/permission_bubble_manager.cc

Issue 1251633002: Add BubbleManager to manage bubbles and ChromeBubbleManager for events. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Apply Feedback Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/website_settings/permission_bubble_manager.h" 5 #include "chrome/browser/ui/website_settings/permission_bubble_manager.h"
6 6
7 #include "base/command_line.h" 7 #include "base/command_line.h"
8 #include "base/metrics/user_metrics_action.h" 8 #include "base/metrics/user_metrics_action.h"
9 #include "chrome/browser/profiles/profile.h"
msw 2015/08/13 03:37:21 nit: this probably isn't needed.
10 #include "chrome/browser/ui/browser.h"
11 #include "chrome/browser/ui/browser_finder.h"
12 #include "chrome/browser/ui/chrome_bubble_manager.h"
13 #include "chrome/browser/ui/chrome_bubble_manager_factory.h"
14 #include "chrome/browser/ui/website_settings/permission_bubble_delegate.h"
9 #include "chrome/browser/ui/website_settings/permission_bubble_request.h" 15 #include "chrome/browser/ui/website_settings/permission_bubble_request.h"
10 #include "chrome/common/chrome_switches.h" 16 #include "chrome/common/chrome_switches.h"
11 #include "content/public/browser/browser_thread.h" 17 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/navigation_details.h" 18 #include "content/public/browser/navigation_details.h"
13 #include "content/public/browser/user_metrics.h" 19 #include "content/public/browser/user_metrics.h"
14 20
15 namespace { 21 namespace {
16 22
17 class CancelledRequest : public PermissionBubbleRequest { 23 class CancelledRequest : public PermissionBubbleRequest {
18 public: 24 public:
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
70 switches::kDisablePermissionsBubbles)) 76 switches::kDisablePermissionsBubbles))
71 return false; 77 return false;
72 return true; 78 return true;
73 } 79 }
74 80
75 PermissionBubbleManager::PermissionBubbleManager( 81 PermissionBubbleManager::PermissionBubbleManager(
76 content::WebContents* web_contents) 82 content::WebContents* web_contents)
77 : content::WebContentsObserver(web_contents), 83 : content::WebContentsObserver(web_contents),
78 require_user_gesture_(false), 84 require_user_gesture_(false),
79 #if !defined(OS_ANDROID) // No bubbles in android tests. 85 #if !defined(OS_ANDROID) // No bubbles in android tests.
80 view_factory_(base::Bind(&PermissionBubbleView::Create)), 86 ui_factory_(base::Bind(&PermissionBubbleDelegate::CreateBubble)),
81 #endif 87 #endif
82 view_(nullptr),
83 main_frame_has_fully_loaded_(false), 88 main_frame_has_fully_loaded_(false),
84 auto_response_for_test_(NONE), 89 auto_response_for_test_(NONE),
85 weak_factory_(this) { 90 weak_factory_(this) {
86 } 91 }
87 92
88 PermissionBubbleManager::~PermissionBubbleManager() { 93 PermissionBubbleManager::~PermissionBubbleManager() {
89 if (view_ != NULL) 94 CancelPendingQueues();
90 view_->SetDelegate(NULL); 95 CloseBubble();
91
92 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
93 for (requests_iter = requests_.begin();
94 requests_iter != requests_.end();
95 requests_iter++) {
96 (*requests_iter)->RequestFinished();
97 }
98 for (requests_iter = queued_requests_.begin();
99 requests_iter != queued_requests_.end();
100 requests_iter++) {
101 (*requests_iter)->RequestFinished();
102 }
103 } 96 }
104 97
105 void PermissionBubbleManager::AddRequest(PermissionBubbleRequest* request) { 98 void PermissionBubbleManager::AddRequest(PermissionBubbleRequest* request) {
106 content::RecordAction(base::UserMetricsAction("PermissionBubbleRequest")); 99 content::RecordAction(base::UserMetricsAction("PermissionBubbleRequest"));
107 // TODO(gbillock): is there a race between an early request on a 100 // TODO(gbillock): is there a race between an early request on a
108 // newly-navigated page and the to-be-cleaned-up requests on the previous 101 // newly-navigated page and the to-be-cleaned-up requests on the previous
109 // page? We should maybe listen to DidStartNavigationToPendingEntry (and 102 // page? We should maybe listen to DidStartNavigationToPendingEntry (and
110 // any other renderer-side nav initiations?). Double-check this for 103 // any other renderer-side nav initiations?). Double-check this for
111 // correct behavior on interstitials -- we probably want to basically queue 104 // correct behavior on interstitials -- we probably want to basically queue
112 // any request for which GetVisibleURL != GetLastCommittedURL. 105 // any request for which GetVisibleURL != GetLastCommittedURL.
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
165 } 158 }
166 } 159 }
167 160
168 std::vector<bool>::iterator accepts_iter = accept_states_.begin(); 161 std::vector<bool>::iterator accepts_iter = accept_states_.begin();
169 for (requests_iter = requests_.begin(), accepts_iter = accept_states_.begin(); 162 for (requests_iter = requests_.begin(), accepts_iter = accept_states_.begin();
170 requests_iter != requests_.end(); 163 requests_iter != requests_.end();
171 requests_iter++, accepts_iter++) { 164 requests_iter++, accepts_iter++) {
172 if (*requests_iter != request) 165 if (*requests_iter != request)
173 continue; 166 continue;
174 167
168 BubbleManager* bubble_manager = GetBubbleManager();
169
175 // We can simply erase the current entry in the request table if we aren't 170 // We can simply erase the current entry in the request table if we aren't
176 // showing the dialog, or if we are showing it and it can accept the update. 171 // showing the dialog, or if we are showing it and it can accept the update.
177 bool can_erase = !IsBubbleVisible() || view_->CanAcceptRequestUpdate(); 172 bool can_erase =
173 !IsBubbleVisible() ||
174 (bubble_manager && bubble_manager->ShouldUpdateBubble(active_bubble_));
msw 2015/08/13 03:37:21 q: if the bubble_manager is null, that seems to in
hcarmona 2015/08/13 20:50:35 This happens when a tab is closed. The bubble mana
msw 2015/08/13 21:41:37 So if |bubble_manager| is null, should |can_erase|
175
178 if (can_erase) { 176 if (can_erase) {
179 (*requests_iter)->RequestFinished(); 177 (*requests_iter)->RequestFinished();
180 requests_.erase(requests_iter); 178 requests_.erase(requests_iter);
181 accept_states_.erase(accepts_iter); 179 accept_states_.erase(accepts_iter);
182 180
183 if (IsBubbleVisible()) { 181 if (bubble_manager && IsBubbleVisible()) {
184 view_->Hide(); 182 if (requests_.empty())
185 // Will redraw the bubble if it is being shown. 183 bubble_manager->CloseBubble(active_bubble_);
186 TriggerShowBubble(); 184 else
185 bubble_manager->UpdateBubble(active_bubble_);
187 } 186 }
188 return; 187 return;
189 } 188 }
190 189
191 // Cancel the existing request and replace it with a dummy. 190 // Cancel the existing request and replace it with a dummy.
192 PermissionBubbleRequest* cancelled_request = 191 PermissionBubbleRequest* cancelled_request =
193 new CancelledRequest(*requests_iter); 192 new CancelledRequest(*requests_iter);
194 (*requests_iter)->RequestFinished(); 193 (*requests_iter)->RequestFinished();
195 *requests_iter = cancelled_request; 194 *requests_iter = cancelled_request;
196 return; 195 return;
197 } 196 }
198 197
199 NOTREACHED(); // Callers should not cancel requests that are not pending. 198 NOTREACHED(); // Callers should not cancel requests that are not pending.
200 } 199 }
201 200
202 void PermissionBubbleManager::HideBubble() { 201 void PermissionBubbleManager::CloseBubble() {
203 // Disengage from the existing view if there is one. 202 if (!active_bubble_)
204 if (!view_)
205 return; 203 return;
206 204
207 view_->SetDelegate(nullptr); 205 if (BubbleManager* bubble_manager = GetBubbleManager())
msw 2015/08/13 03:37:21 Again, when could there be no bubble manager and w
hcarmona 2015/08/13 20:50:35 Yes, this seems like a weird state that we should
208 view_->Hide(); 206 bubble_manager->CloseBubble(active_bubble_);
209 view_.reset();
210 }
211
212 void PermissionBubbleManager::DisplayPendingRequests(Browser* browser) {
213 if (IsBubbleVisible())
214 return;
215
216 view_ = view_factory_.Run(browser);
217 view_->SetDelegate(this);
218
219 TriggerShowBubble();
220 }
221
222 void PermissionBubbleManager::UpdateAnchorPosition() {
223 if (view_)
224 view_->UpdateAnchorPosition();
225 } 207 }
226 208
227 bool PermissionBubbleManager::IsBubbleVisible() { 209 bool PermissionBubbleManager::IsBubbleVisible() {
228 return view_ && view_->IsVisible(); 210 return active_bubble_;
229 }
230
231 gfx::NativeWindow PermissionBubbleManager::GetBubbleWindow() {
232 if (view_)
233 return view_->GetNativeWindow();
234 return nullptr;
235 } 211 }
236 212
237 void PermissionBubbleManager::RequireUserGesture(bool required) { 213 void PermissionBubbleManager::RequireUserGesture(bool required) {
238 require_user_gesture_ = required; 214 require_user_gesture_ = required;
239 } 215 }
240 216
241 void PermissionBubbleManager::DidNavigateMainFrame( 217 void PermissionBubbleManager::DidNavigateMainFrame(
242 const content::LoadCommittedDetails& details, 218 const content::LoadCommittedDetails& details,
243 const content::FrameNavigateParams& params) { 219 const content::FrameNavigateParams& params) {
244 if (details.is_in_page) 220 if (details.is_in_page)
(...skipping 10 matching lines...) Expand all
255 // scheduled calls to AddRequest to complete before we show the page-load 231 // scheduled calls to AddRequest to complete before we show the page-load
256 // permissions bubble. 232 // permissions bubble.
257 ScheduleShowBubble(); 233 ScheduleShowBubble();
258 } 234 }
259 235
260 void PermissionBubbleManager::DocumentLoadedInFrame( 236 void PermissionBubbleManager::DocumentLoadedInFrame(
261 content::RenderFrameHost* render_frame_host) { 237 content::RenderFrameHost* render_frame_host) {
262 ScheduleShowBubble(); 238 ScheduleShowBubble();
263 } 239 }
264 240
265 void PermissionBubbleManager::NavigationEntryCommitted(
266 const content::LoadCommittedDetails& details) {
267 // No permissions requests pending.
268 if (request_url_.is_empty())
269 return;
270
271 // If we have navigated to a new url or reloaded the page...
272 // GetAsReferrer strips fragment and username/password, meaning
273 // the navigation is really to the same page.
274 if ((request_url_.GetAsReferrer() !=
275 web_contents()->GetLastCommittedURL().GetAsReferrer()) ||
276 (details.type == content::NAVIGATION_TYPE_EXISTING_PAGE &&
277 !details.is_in_page)) {
278 // Kill off existing bubble and cancel any pending requests.
279 CancelPendingQueues();
280 FinalizeBubble();
281 }
282 }
283
284 void PermissionBubbleManager::WebContentsDestroyed() { 241 void PermissionBubbleManager::WebContentsDestroyed() {
285 // If the web contents has been destroyed, treat the bubble as cancelled. 242 // If the web contents has been destroyed, treat the bubble as cancelled.
286 CancelPendingQueues(); 243 CancelPendingQueues();
287 FinalizeBubble(); 244 CloseBubble();
288 245
289 // The WebContents is going away; be aggressively paranoid and delete 246 // The WebContents is going away; be aggressively paranoid and delete
290 // ourselves lest other parts of the system attempt to add permission bubbles 247 // ourselves lest other parts of the system attempt to add permission bubbles
291 // or use us otherwise during the destruction. 248 // or use us otherwise during the destruction.
292 web_contents()->RemoveUserData(UserDataKey()); 249 web_contents()->RemoveUserData(UserDataKey());
293 // That was the equivalent of "delete this". This object is now destroyed; 250 // That was the equivalent of "delete this". This object is now destroyed;
294 // returning from this function is the only safe thing to do. 251 // returning from this function is the only safe thing to do.
295 } 252 }
296 253
297 void PermissionBubbleManager::ToggleAccept(int request_index, bool new_value) { 254 void PermissionBubbleManager::ToggleAccept(int request_index, bool new_value) {
298 DCHECK(request_index < static_cast<int>(accept_states_.size())); 255 DCHECK(request_index < static_cast<int>(accept_states_.size()));
299 accept_states_[request_index] = new_value; 256 accept_states_[request_index] = new_value;
300 } 257 }
301 258
302 void PermissionBubbleManager::Accept() { 259 void PermissionBubbleManager::Accept() {
303 std::vector<PermissionBubbleRequest*>::iterator requests_iter; 260 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
304 std::vector<bool>::iterator accepts_iter = accept_states_.begin(); 261 std::vector<bool>::iterator accepts_iter = accept_states_.begin();
305 for (requests_iter = requests_.begin(), accepts_iter = accept_states_.begin(); 262 for (requests_iter = requests_.begin(), accepts_iter = accept_states_.begin();
306 requests_iter != requests_.end(); 263 requests_iter != requests_.end();
307 requests_iter++, accepts_iter++) { 264 requests_iter++, accepts_iter++) {
308 if (*accepts_iter) 265 if (*accepts_iter)
309 (*requests_iter)->PermissionGranted(); 266 (*requests_iter)->PermissionGranted();
310 else 267 else
311 (*requests_iter)->PermissionDenied(); 268 (*requests_iter)->PermissionDenied();
312 } 269 }
313 FinalizeBubble(); 270 CloseBubble();
314 } 271 }
315 272
316 void PermissionBubbleManager::Deny() { 273 void PermissionBubbleManager::Deny() {
274 for (PermissionBubbleRequest* request : requests_)
msw 2015/08/13 03:37:21 I wonder... if the bubble UI wasn't updated for a
groby-ooo-7-16 2015/08/13 18:44:38 IIUC, these requests are stored in |queued_request
hcarmona 2015/08/13 20:50:35 Sounds good, we can have other CLs to refactor fir
275 request->PermissionDenied();
276 CloseBubble();
277 }
278
279 void PermissionBubbleManager::Closing() {
280 for (PermissionBubbleRequest* request : requests_)
281 request->Cancelled();
282 CloseBubble();
283 }
284
285 void PermissionBubbleManager::DidClose() {
317 std::vector<PermissionBubbleRequest*>::iterator requests_iter; 286 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
318 for (requests_iter = requests_.begin(); 287 for (requests_iter = requests_.begin();
msw 2015/08/13 03:37:21 nit: update loop to foreach like above.
groby-ooo-7-16 2015/08/13 18:44:38 Ugh. I'd rather not do that here. We've already go
msw 2015/08/13 19:18:58 It's a nit; definitely not required, and welcome t
hcarmona 2015/08/13 20:50:35 We can have a separate refactor CL. This particula
319 requests_iter != requests_.end(); 288 requests_iter != requests_.end();
320 requests_iter++) { 289 requests_iter++) {
321 (*requests_iter)->PermissionDenied(); 290 (*requests_iter)->RequestFinished();
322 } 291 }
323 FinalizeBubble();
324 }
325 292
326 void PermissionBubbleManager::Closing() { 293 requests_.clear();
327 std::vector<PermissionBubbleRequest*>::iterator requests_iter; 294 accept_states_.clear();
328 for (requests_iter = requests_.begin(); 295 if (queued_requests_.size() || queued_frame_requests_.size())
msw 2015/08/13 03:37:21 nit: ask if they're [not] empty, rather than impli
329 requests_iter != requests_.end(); 296 TriggerShowBubble();
330 requests_iter++) { 297 else
331 (*requests_iter)->Cancelled(); 298 request_url_ = GURL();
332 }
333 FinalizeBubble();
334 } 299 }
335 300
336 void PermissionBubbleManager::ScheduleShowBubble() { 301 void PermissionBubbleManager::ScheduleShowBubble() {
337 // ::ScheduleShowBubble() will be called again when the main frame will be 302 // ::ScheduleShowBubble() will be called again when the main frame will be
338 // loaded. 303 // loaded.
339 if (!main_frame_has_fully_loaded_) 304 if (!main_frame_has_fully_loaded_)
340 return; 305 return;
341 306
342 content::BrowserThread::PostTask( 307 content::BrowserThread::PostTask(
343 content::BrowserThread::UI, 308 content::BrowserThread::UI,
344 FROM_HERE, 309 FROM_HERE,
345 base::Bind(&PermissionBubbleManager::TriggerShowBubble, 310 base::Bind(&PermissionBubbleManager::TriggerShowBubble,
346 weak_factory_.GetWeakPtr())); 311 weak_factory_.GetWeakPtr()));
347 } 312 }
348 313
349 void PermissionBubbleManager::TriggerShowBubble() { 314 void PermissionBubbleManager::TriggerShowBubble() {
350 if (!view_)
351 return;
352 if (IsBubbleVisible()) 315 if (IsBubbleVisible())
353 return; 316 return;
354 if (!main_frame_has_fully_loaded_) 317 if (!main_frame_has_fully_loaded_)
355 return; 318 return;
356 if (requests_.empty() && queued_requests_.empty() && 319 if (requests_.empty() && queued_requests_.empty() &&
357 queued_frame_requests_.empty()) { 320 queued_frame_requests_.empty()) {
358 return; 321 return;
359 } 322 }
360 323
361 if (requests_.empty()) { 324 if (requests_.empty()) {
362 // Queues containing a user-gesture-generated request have priority. 325 // Queues containing a user-gesture-generated request have priority.
363 if (HasUserGestureRequest(queued_requests_)) 326 if (HasUserGestureRequest(queued_requests_))
364 requests_.swap(queued_requests_); 327 requests_.swap(queued_requests_);
365 else if (HasUserGestureRequest(queued_frame_requests_)) 328 else if (HasUserGestureRequest(queued_frame_requests_))
366 requests_.swap(queued_frame_requests_); 329 requests_.swap(queued_frame_requests_);
367 else if (queued_requests_.size()) 330 else if (queued_requests_.size())
368 requests_.swap(queued_requests_); 331 requests_.swap(queued_requests_);
369 else 332 else
370 requests_.swap(queued_frame_requests_); 333 requests_.swap(queued_frame_requests_);
371 334
372 // Sets the default value for each request to be 'accept'. 335 // Sets the default value for each request to be 'accept'.
373 // TODO(leng): Currently all requests default to true. If that changes: 336 // TODO(leng): Currently all requests default to true. If that changes:
374 // a) Add additional accept_state queues to store default values. 337 // a) Add additional accept_state queues to store default values.
375 // b) Change the request API to provide the default value. 338 // b) Change the request API to provide the default value.
376 accept_states_.resize(requests_.size(), true); 339 accept_states_.resize(requests_.size(), true);
377 } 340 }
378 341
379 view_->Show(requests_, accept_states_); 342 BubbleManager* bubble_manager = GetBubbleManager();
343 DCHECK(bubble_manager);
344
345 active_bubble_ = bubble_manager->ShowBubble(
346 make_scoped_ptr(new PermissionBubbleDelegate(ui_factory_, this)));
347
380 NotifyBubbleAdded(); 348 NotifyBubbleAdded();
381 349
382 // If in testing mode, automatically respond to the bubble that was shown. 350 // If in testing mode, automatically respond to the bubble that was shown.
383 if (auto_response_for_test_ != NONE) 351 if (auto_response_for_test_ != NONE)
384 DoAutoResponseForTesting(); 352 DoAutoResponseForTesting();
385 } 353 }
386 354
387 void PermissionBubbleManager::FinalizeBubble() {
388 if (view_)
389 view_->Hide();
390
391 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
392 for (requests_iter = requests_.begin();
393 requests_iter != requests_.end();
394 requests_iter++) {
395 (*requests_iter)->RequestFinished();
396 }
397 requests_.clear();
398 accept_states_.clear();
399 if (queued_requests_.size() || queued_frame_requests_.size())
400 TriggerShowBubble();
401 else
402 request_url_ = GURL();
403 }
404
405 void PermissionBubbleManager::CancelPendingQueues() { 355 void PermissionBubbleManager::CancelPendingQueues() {
406 std::vector<PermissionBubbleRequest*>::iterator requests_iter; 356 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
407 for (requests_iter = queued_requests_.begin(); 357 for (requests_iter = queued_requests_.begin();
408 requests_iter != queued_requests_.end(); 358 requests_iter != queued_requests_.end();
409 requests_iter++) { 359 requests_iter++) {
410 (*requests_iter)->RequestFinished(); 360 (*requests_iter)->RequestFinished();
411 } 361 }
412 for (requests_iter = queued_frame_requests_.begin(); 362 for (requests_iter = queued_frame_requests_.begin();
413 requests_iter != queued_frame_requests_.end(); 363 requests_iter != queued_frame_requests_.end();
414 requests_iter++) { 364 requests_iter++) {
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
450 } 400 }
451 401
452 void PermissionBubbleManager::AddObserver(Observer* observer) { 402 void PermissionBubbleManager::AddObserver(Observer* observer) {
453 observer_list_.AddObserver(observer); 403 observer_list_.AddObserver(observer);
454 } 404 }
455 405
456 void PermissionBubbleManager::RemoveObserver(Observer* observer) { 406 void PermissionBubbleManager::RemoveObserver(Observer* observer) {
457 observer_list_.RemoveObserver(observer); 407 observer_list_.RemoveObserver(observer);
458 } 408 }
459 409
410 BubbleManager* PermissionBubbleManager::GetBubbleManager() {
411 if (Browser* browser = chrome::FindBrowserWithWebContents(web_contents()))
412 return ChromeBubbleManagerFactory::GetForBrowserContext(browser->profile());
413 return nullptr;
414 }
415
460 void PermissionBubbleManager::NotifyBubbleAdded() { 416 void PermissionBubbleManager::NotifyBubbleAdded() {
461 FOR_EACH_OBSERVER(Observer, observer_list_, OnBubbleAdded()); 417 FOR_EACH_OBSERVER(Observer, observer_list_, OnBubbleAdded());
462 } 418 }
463 419
464 void PermissionBubbleManager::DoAutoResponseForTesting() { 420 void PermissionBubbleManager::DoAutoResponseForTesting() {
465 switch (auto_response_for_test_) { 421 switch (auto_response_for_test_) {
466 case ACCEPT_ALL: 422 case ACCEPT_ALL:
467 Accept(); 423 Accept();
468 break; 424 break;
469 case DENY_ALL: 425 case DENY_ALL:
470 Deny(); 426 Deny();
471 break; 427 break;
472 case DISMISS: 428 case DISMISS:
473 Closing(); 429 Closing();
474 break; 430 break;
475 case NONE: 431 case NONE:
476 NOTREACHED(); 432 NOTREACHED();
477 } 433 }
478 } 434 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698