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

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: Partial 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"
10 #include "chrome/browser/ui/browser.h"
11 #include "chrome/browser/ui/chrome_bubble_manager.h"
12 #include "chrome/browser/ui/chrome_bubble_manager_factory.h"
13 #include "chrome/browser/ui/website_settings/permission_bubble_delegate.h"
9 #include "chrome/browser/ui/website_settings/permission_bubble_request.h" 14 #include "chrome/browser/ui/website_settings/permission_bubble_request.h"
10 #include "chrome/common/chrome_switches.h" 15 #include "chrome/common/chrome_switches.h"
11 #include "content/public/browser/browser_thread.h" 16 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/navigation_details.h" 17 #include "content/public/browser/navigation_details.h"
13 #include "content/public/browser/user_metrics.h" 18 #include "content/public/browser/user_metrics.h"
14 19
15 namespace { 20 namespace {
16 21
17 class CancelledRequest : public PermissionBubbleRequest { 22 class CancelledRequest : public PermissionBubbleRequest {
18 public: 23 public:
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
72 return true; 77 return true;
73 } 78 }
74 79
75 PermissionBubbleManager::PermissionBubbleManager( 80 PermissionBubbleManager::PermissionBubbleManager(
76 content::WebContents* web_contents) 81 content::WebContents* web_contents)
77 : content::WebContentsObserver(web_contents), 82 : content::WebContentsObserver(web_contents),
78 require_user_gesture_(false), 83 require_user_gesture_(false),
79 #if !defined(OS_ANDROID) // No bubbles in android tests. 84 #if !defined(OS_ANDROID) // No bubbles in android tests.
80 view_factory_(base::Bind(&PermissionBubbleView::Create)), 85 view_factory_(base::Bind(&PermissionBubbleView::Create)),
81 #endif 86 #endif
82 view_(nullptr),
83 main_frame_has_fully_loaded_(false), 87 main_frame_has_fully_loaded_(false),
84 auto_response_for_test_(NONE), 88 auto_response_for_test_(NONE),
85 weak_factory_(this) { 89 weak_factory_(this) {
86 } 90 }
87 91
88 PermissionBubbleManager::~PermissionBubbleManager() { 92 PermissionBubbleManager::~PermissionBubbleManager() {
89 if (view_ != NULL) 93 CancelPendingQueues();
90 view_->SetDelegate(NULL); 94 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 } 95 }
104 96
105 void PermissionBubbleManager::AddRequest(PermissionBubbleRequest* request) { 97 void PermissionBubbleManager::AddRequest(PermissionBubbleRequest* request) {
106 content::RecordAction(base::UserMetricsAction("PermissionBubbleRequest")); 98 content::RecordAction(base::UserMetricsAction("PermissionBubbleRequest"));
107 // TODO(gbillock): is there a race between an early request on a 99 // 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 100 // newly-navigated page and the to-be-cleaned-up requests on the previous
109 // page? We should maybe listen to DidStartNavigationToPendingEntry (and 101 // page? We should maybe listen to DidStartNavigationToPendingEntry (and
110 // any other renderer-side nav initiations?). Double-check this for 102 // any other renderer-side nav initiations?). Double-check this for
111 // correct behavior on interstitials -- we probably want to basically queue 103 // correct behavior on interstitials -- we probably want to basically queue
112 // any request for which GetVisibleURL != GetLastCommittedURL. 104 // any request for which GetVisibleURL != GetLastCommittedURL.
(...skipping 28 matching lines...) Expand all
141 requests_.push_back(request); 133 requests_.push_back(request);
142 // TODO(gbillock): do we need to make default state a request property? 134 // TODO(gbillock): do we need to make default state a request property?
143 accept_states_.push_back(true); 135 accept_states_.push_back(true);
144 } else { 136 } else {
145 content::RecordAction( 137 content::RecordAction(
146 base::UserMetricsAction("PermissionBubbleIFrameRequestQueued")); 138 base::UserMetricsAction("PermissionBubbleIFrameRequestQueued"));
147 queued_frame_requests_.push_back(request); 139 queued_frame_requests_.push_back(request);
148 } 140 }
149 141
150 if (!require_user_gesture_ || request->HasUserGesture()) 142 if (!require_user_gesture_ || request->HasUserGesture())
151 ScheduleShowBubble(); 143 TriggerShowBubble();
152 } 144 }
153 145
154 void PermissionBubbleManager::CancelRequest(PermissionBubbleRequest* request) { 146 void PermissionBubbleManager::CancelRequest(PermissionBubbleRequest* request) {
155 // First look in the queued requests, where we can simply delete the request 147 // First look in the queued requests, where we can simply delete the request
156 // and go on. 148 // and go on.
157 std::vector<PermissionBubbleRequest*>::iterator requests_iter; 149 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
158 for (requests_iter = queued_requests_.begin(); 150 for (requests_iter = queued_requests_.begin();
159 requests_iter != queued_requests_.end(); 151 requests_iter != queued_requests_.end();
160 requests_iter++) { 152 requests_iter++) {
161 if (*requests_iter == request) { 153 if (*requests_iter == request) {
162 (*requests_iter)->RequestFinished(); 154 (*requests_iter)->RequestFinished();
163 queued_requests_.erase(requests_iter); 155 queued_requests_.erase(requests_iter);
164 return; 156 return;
165 } 157 }
166 } 158 }
167 159
168 std::vector<bool>::iterator accepts_iter = accept_states_.begin(); 160 std::vector<bool>::iterator accepts_iter = accept_states_.begin();
169 for (requests_iter = requests_.begin(), accepts_iter = accept_states_.begin(); 161 for (requests_iter = requests_.begin(), accepts_iter = accept_states_.begin();
170 requests_iter != requests_.end(); 162 requests_iter != requests_.end();
171 requests_iter++, accepts_iter++) { 163 requests_iter++, accepts_iter++) {
172 if (*requests_iter != request) 164 if (*requests_iter != request)
173 continue; 165 continue;
174 166
175 // We can simply erase the current entry in the request table if we aren't 167 // 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. 168 // showing the dialog, or if we are showing it and it can accept the update.
177 bool can_erase = !IsBubbleVisible() || view_->CanAcceptRequestUpdate(); 169 bool can_erase = !IsBubbleVisible();
170
171 // TODO(hcarmona): Don't forget this logic.
172 // Maybe move it into the BubbleManager?
173 // bool can_erase = !IsBubbleVisible() || view_->CanAcceptRequestUpdate();
please use gerrit instead 2015/08/07 23:02:27 Move more on the delegate. Including CanAcceptRequ
hcarmona 2015/08/11 02:35:46 Done.
174
178 if (can_erase) { 175 if (can_erase) {
179 (*requests_iter)->RequestFinished(); 176 (*requests_iter)->RequestFinished();
180 requests_.erase(requests_iter); 177 requests_.erase(requests_iter);
181 accept_states_.erase(accepts_iter); 178 accept_states_.erase(accepts_iter);
182 179
180 /*
183 if (IsBubbleVisible()) { 181 if (IsBubbleVisible()) {
184 view_->Hide(); 182 UpdateBubble(); // TODO(hcarmona): HOW?
185 // Will redraw the bubble if it is being shown. 183 // Will redraw the bubble if it is being shown.
186 TriggerShowBubble(); 184 TriggerShowBubble();
187 } 185 }
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 ChromeBubbleManagerFactory::GetForBrowserContext(browser_->profile())
207 view_->SetDelegate(nullptr); 205 ->CloseBubble(active_bubble_);
208 view_->Hide();
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 } 206 }
226 207
227 bool PermissionBubbleManager::IsBubbleVisible() { 208 bool PermissionBubbleManager::IsBubbleVisible() {
228 return view_ && view_->IsVisible(); 209 return active_bubble_;
229 }
230
231 gfx::NativeWindow PermissionBubbleManager::GetBubbleWindow() {
232 if (view_)
233 return view_->GetNativeWindow();
234 return nullptr;
235 } 210 }
236 211
237 void PermissionBubbleManager::RequireUserGesture(bool required) { 212 void PermissionBubbleManager::RequireUserGesture(bool required) {
238 require_user_gesture_ = required; 213 require_user_gesture_ = required;
239 } 214 }
240 215
241 void PermissionBubbleManager::DidNavigateMainFrame( 216 void PermissionBubbleManager::DidNavigateMainFrame(
242 const content::LoadCommittedDetails& details, 217 const content::LoadCommittedDetails& details,
243 const content::FrameNavigateParams& params) { 218 const content::FrameNavigateParams& params) {
244 if (details.is_in_page) 219 if (details.is_in_page)
245 return; 220 return;
246 221
247 main_frame_has_fully_loaded_ = false; 222 main_frame_has_fully_loaded_ = false;
248 } 223 }
249 224
250 void PermissionBubbleManager::DocumentOnLoadCompletedInMainFrame() { 225 void PermissionBubbleManager::DocumentOnLoadCompletedInMainFrame() {
251 main_frame_has_fully_loaded_ = true; 226 main_frame_has_fully_loaded_ = true;
252 // This is scheduled because while all calls to the browser have been 227 // This is scheduled because while all calls to the browser have been
253 // issued at DOMContentLoaded, they may be bouncing around in scheduled 228 // issued at DOMContentLoaded, they may be bouncing around in scheduled
254 // callbacks finding the UI thread still. This makes sure we allow those 229 // callbacks finding the UI thread still. This makes sure we allow those
255 // scheduled calls to AddRequest to complete before we show the page-load 230 // scheduled calls to AddRequest to complete before we show the page-load
256 // permissions bubble. 231 // permissions bubble.
257 ScheduleShowBubble(); 232 TriggerShowBubble();
258 } 233 }
259 234
260 void PermissionBubbleManager::DocumentLoadedInFrame( 235 void PermissionBubbleManager::DocumentLoadedInFrame(
261 content::RenderFrameHost* render_frame_host) { 236 content::RenderFrameHost* render_frame_host) {
262 ScheduleShowBubble(); 237 TriggerShowBubble();
263 } 238 }
264 239
265 void PermissionBubbleManager::NavigationEntryCommitted( 240 void PermissionBubbleManager::NavigationEntryCommitted(
266 const content::LoadCommittedDetails& details) { 241 const content::LoadCommittedDetails& details) {
267 // No permissions requests pending. 242 // No permissions requests pending.
268 if (request_url_.is_empty()) 243 if (request_url_.is_empty())
269 return; 244 return;
270 245
271 // If we have navigated to a new url or reloaded the page... 246 // If we have navigated to a new url or reloaded the page...
272 // GetAsReferrer strips fragment and username/password, meaning 247 // GetAsReferrer strips fragment and username/password, meaning
273 // the navigation is really to the same page. 248 // the navigation is really to the same page.
274 if ((request_url_.GetAsReferrer() != 249 if ((request_url_.GetAsReferrer() !=
275 web_contents()->GetLastCommittedURL().GetAsReferrer()) || 250 web_contents()->GetLastCommittedURL().GetAsReferrer()) ||
276 (details.type == content::NAVIGATION_TYPE_EXISTING_PAGE && 251 (details.type == content::NAVIGATION_TYPE_EXISTING_PAGE &&
277 !details.is_in_page)) { 252 !details.is_in_page)) {
278 // Kill off existing bubble and cancel any pending requests. 253 // Kill off existing bubble and cancel any pending requests.
279 CancelPendingQueues(); 254 CancelPendingQueues();
280 FinalizeBubble(); 255 CloseBubble();
281 } 256 }
282 } 257 }
283 258
284 void PermissionBubbleManager::WebContentsDestroyed() { 259 void PermissionBubbleManager::WebContentsDestroyed() {
285 // If the web contents has been destroyed, treat the bubble as cancelled. 260 // If the web contents has been destroyed, treat the bubble as cancelled.
286 CancelPendingQueues(); 261 CancelPendingQueues();
287 FinalizeBubble(); 262 CloseBubble();
288 263
289 // The WebContents is going away; be aggressively paranoid and delete 264 // The WebContents is going away; be aggressively paranoid and delete
290 // ourselves lest other parts of the system attempt to add permission bubbles 265 // ourselves lest other parts of the system attempt to add permission bubbles
291 // or use us otherwise during the destruction. 266 // or use us otherwise during the destruction.
292 web_contents()->RemoveUserData(UserDataKey()); 267 web_contents()->RemoveUserData(UserDataKey());
293 // That was the equivalent of "delete this". This object is now destroyed; 268 // That was the equivalent of "delete this". This object is now destroyed;
294 // returning from this function is the only safe thing to do. 269 // returning from this function is the only safe thing to do.
295 } 270 }
296 271
297 void PermissionBubbleManager::ToggleAccept(int request_index, bool new_value) { 272 void PermissionBubbleManager::ToggleAccept(int request_index, bool new_value) {
298 DCHECK(request_index < static_cast<int>(accept_states_.size())); 273 DCHECK(request_index < static_cast<int>(accept_states_.size()));
299 accept_states_[request_index] = new_value; 274 accept_states_[request_index] = new_value;
300 } 275 }
301 276
302 void PermissionBubbleManager::Accept() { 277 void PermissionBubbleManager::Accept() {
303 std::vector<PermissionBubbleRequest*>::iterator requests_iter; 278 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
304 std::vector<bool>::iterator accepts_iter = accept_states_.begin(); 279 std::vector<bool>::iterator accepts_iter = accept_states_.begin();
305 for (requests_iter = requests_.begin(), accepts_iter = accept_states_.begin(); 280 for (requests_iter = requests_.begin(), accepts_iter = accept_states_.begin();
306 requests_iter != requests_.end(); 281 requests_iter != requests_.end();
307 requests_iter++, accepts_iter++) { 282 requests_iter++, accepts_iter++) {
308 if (*accepts_iter) 283 if (*accepts_iter)
309 (*requests_iter)->PermissionGranted(); 284 (*requests_iter)->PermissionGranted();
310 else 285 else
311 (*requests_iter)->PermissionDenied(); 286 (*requests_iter)->PermissionDenied();
312 } 287 }
313 FinalizeBubble(); 288 CloseBubble();
314 } 289 }
315 290
316 void PermissionBubbleManager::Deny() { 291 void PermissionBubbleManager::Deny() {
317 std::vector<PermissionBubbleRequest*>::iterator requests_iter; 292 for (PermissionBubbleRequest* request : requests_)
318 for (requests_iter = requests_.begin(); 293 request->PermissionDenied();
319 requests_iter != requests_.end(); 294 CloseBubble();
320 requests_iter++) {
321 (*requests_iter)->PermissionDenied();
322 }
323 FinalizeBubble();
324 } 295 }
325 296
326 void PermissionBubbleManager::Closing() { 297 void PermissionBubbleManager::Closing() {
327 std::vector<PermissionBubbleRequest*>::iterator requests_iter; 298 for (PermissionBubbleRequest* request : requests_)
328 for (requests_iter = requests_.begin(); 299 request->Cancelled();
329 requests_iter != requests_.end(); 300 CloseBubble();
330 requests_iter++) {
331 (*requests_iter)->Cancelled();
332 }
333 FinalizeBubble();
334 } 301 }
335 302
336 void PermissionBubbleManager::ScheduleShowBubble() { 303 void PermissionBubbleManager::Finalize() {
337 // ::ScheduleShowBubble() will be called again when the main frame will be 304 for (PermissionBubbleRequest* request : requests_)
338 // loaded. 305 request->RequestFinished();
339 if (!main_frame_has_fully_loaded_)
340 return;
341 306
342 content::BrowserThread::PostTask( 307 requests_.clear();
343 content::BrowserThread::UI, 308 accept_states_.clear();
344 FROM_HERE, 309 if (queued_requests_.size() || queued_frame_requests_.size())
345 base::Bind(&PermissionBubbleManager::TriggerShowBubble, 310 TriggerShowBubble();
346 weak_factory_.GetWeakPtr())); 311 else
312 request_url_ = GURL();
347 } 313 }
348 314
349 void PermissionBubbleManager::TriggerShowBubble() { 315 void PermissionBubbleManager::TriggerShowBubble() {
350 if (!view_)
351 return;
352 if (IsBubbleVisible()) 316 if (IsBubbleVisible())
353 return; 317 return;
354 if (!main_frame_has_fully_loaded_) 318 if (!main_frame_has_fully_loaded_)
355 return; 319 return;
356 if (requests_.empty() && queued_requests_.empty() && 320 if (requests_.empty() && queued_requests_.empty() &&
357 queued_frame_requests_.empty()) { 321 queued_frame_requests_.empty()) {
358 return; 322 return;
359 } 323 }
360 324
361 if (requests_.empty()) { 325 if (requests_.empty()) {
362 // Queues containing a user-gesture-generated request have priority. 326 // Queues containing a user-gesture-generated request have priority.
363 if (HasUserGestureRequest(queued_requests_)) 327 if (HasUserGestureRequest(queued_requests_))
364 requests_.swap(queued_requests_); 328 requests_.swap(queued_requests_);
365 else if (HasUserGestureRequest(queued_frame_requests_)) 329 else if (HasUserGestureRequest(queued_frame_requests_))
366 requests_.swap(queued_frame_requests_); 330 requests_.swap(queued_frame_requests_);
367 else if (queued_requests_.size()) 331 else if (queued_requests_.size())
368 requests_.swap(queued_requests_); 332 requests_.swap(queued_requests_);
369 else 333 else
370 requests_.swap(queued_frame_requests_); 334 requests_.swap(queued_frame_requests_);
371 335
372 // Sets the default value for each request to be 'accept'. 336 // Sets the default value for each request to be 'accept'.
373 // TODO(leng): Currently all requests default to true. If that changes: 337 // TODO(leng): Currently all requests default to true. If that changes:
374 // a) Add additional accept_state queues to store default values. 338 // a) Add additional accept_state queues to store default values.
375 // b) Change the request API to provide the default value. 339 // b) Change the request API to provide the default value.
376 accept_states_.resize(requests_.size(), true); 340 accept_states_.resize(requests_.size(), true);
377 } 341 }
378 342
379 view_->Show(requests_, accept_states_); 343 active_bubble_ =
344 ChromeBubbleManagerFactory::GetForBrowserContext(browser_->profile())
345 ->ShowBubble(
346 make_scoped_ptr(new PermissionBubbleDelegate(this, view_factory_)));
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() { 355 void PermissionBubbleManager::CancelPendingQueues() {
388 if (view_) 356 for (PermissionBubbleRequest* request : requests_)
389 view_->Hide(); 357 request->RequestFinished();
358 for (PermissionBubbleRequest* queued_request : queued_frame_requests_)
359 queued_request->RequestFinished();
390 360
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() {
406 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
407 for (requests_iter = queued_requests_.begin();
408 requests_iter != queued_requests_.end();
409 requests_iter++) {
410 (*requests_iter)->RequestFinished();
411 }
412 for (requests_iter = queued_frame_requests_.begin();
413 requests_iter != queued_frame_requests_.end();
414 requests_iter++) {
415 (*requests_iter)->RequestFinished();
416 }
417 queued_requests_.clear(); 361 queued_requests_.clear();
418 queued_frame_requests_.clear(); 362 queued_frame_requests_.clear();
419 } 363 }
420 364
421 bool PermissionBubbleManager::ExistingRequest( 365 bool PermissionBubbleManager::ExistingRequest(
422 PermissionBubbleRequest* request, 366 PermissionBubbleRequest* request,
423 const std::vector<PermissionBubbleRequest*>& queue, 367 const std::vector<PermissionBubbleRequest*>& queue,
424 bool* same_object) { 368 bool* same_object) {
425 CHECK(same_object); 369 CHECK(same_object);
426 *same_object = false; 370 *same_object = false;
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
469 case DENY_ALL: 413 case DENY_ALL:
470 Deny(); 414 Deny();
471 break; 415 break;
472 case DISMISS: 416 case DISMISS:
473 Closing(); 417 Closing();
474 break; 418 break;
475 case NONE: 419 case NONE:
476 NOTREACHED(); 420 NOTREACHED();
477 } 421 }
478 } 422 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698