| OLD | NEW |
| 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/ui/website_settings/permission_bubble_request.h" | 9 #include "chrome/browser/ui/website_settings/permission_bubble_request.h" |
| 10 #include "chrome/common/chrome_switches.h" | 10 #include "chrome/common/chrome_switches.h" |
| 11 #include "content/public/browser/browser_thread.h" | 11 #include "content/public/browser/browser_thread.h" |
| 12 #include "content/public/browser/navigation_details.h" |
| 12 #include "content/public/browser/user_metrics.h" | 13 #include "content/public/browser/user_metrics.h" |
| 13 | 14 |
| 14 namespace { | 15 namespace { |
| 15 | 16 |
| 16 class CancelledRequest : public PermissionBubbleRequest { | 17 class CancelledRequest : public PermissionBubbleRequest { |
| 17 public: | 18 public: |
| 18 explicit CancelledRequest(PermissionBubbleRequest* cancelled) | 19 explicit CancelledRequest(PermissionBubbleRequest* cancelled) |
| 19 : icon_(cancelled->GetIconID()), | 20 : icon_(cancelled->GetIconID()), |
| 20 message_text_(cancelled->GetMessageText()), | 21 message_text_(cancelled->GetMessageText()), |
| 21 message_fragment_(cancelled->GetMessageTextFragment()), | 22 message_fragment_(cancelled->GetMessageTextFragment()), |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 94 | 95 |
| 95 void PermissionBubbleManager::AddRequest(PermissionBubbleRequest* request) { | 96 void PermissionBubbleManager::AddRequest(PermissionBubbleRequest* request) { |
| 96 content::RecordAction(base::UserMetricsAction("PermissionBubbleRequest")); | 97 content::RecordAction(base::UserMetricsAction("PermissionBubbleRequest")); |
| 97 // TODO(gbillock): is there a race between an early request on a | 98 // TODO(gbillock): is there a race between an early request on a |
| 98 // newly-navigated page and the to-be-cleaned-up requests on the previous | 99 // newly-navigated page and the to-be-cleaned-up requests on the previous |
| 99 // page? We should maybe listen to DidStartNavigationToPendingEntry (and | 100 // page? We should maybe listen to DidStartNavigationToPendingEntry (and |
| 100 // any other renderer-side nav initiations?). Double-check this for | 101 // any other renderer-side nav initiations?). Double-check this for |
| 101 // correct behavior on interstitials -- we probably want to basically queue | 102 // correct behavior on interstitials -- we probably want to basically queue |
| 102 // any request for which GetVisibleURL != GetLastCommittedURL. | 103 // any request for which GetVisibleURL != GetLastCommittedURL. |
| 103 request_url_ = web_contents()->GetLastCommittedURL(); | 104 request_url_ = web_contents()->GetLastCommittedURL(); |
| 105 bool is_main_frame = request->GetRequestingHostname() == request_url_; |
| 104 | 106 |
| 105 // Don't re-add an existing request or one with a duplicate text request. | 107 // Don't re-add an existing request or one with a duplicate text request. |
| 106 std::vector<PermissionBubbleRequest*>::iterator requests_iter; | 108 bool same_object = false; |
| 107 for (requests_iter = requests_.begin(); | 109 if (ExistingRequest(request, requests_, &same_object) || |
| 108 requests_iter != requests_.end(); | 110 ExistingRequest(request, queued_requests_, &same_object) || |
| 109 requests_iter++) { | 111 ExistingRequest(request, queued_frame_requests_, &same_object)) { |
| 110 if (*requests_iter == request) | 112 if (!same_object) |
| 111 return; | |
| 112 // TODO(gbillock): worry about the requesting host name as well. | |
| 113 if ((*requests_iter)->GetMessageTextFragment() == | |
| 114 request->GetMessageTextFragment()) { | |
| 115 request->RequestFinished(); | 113 request->RequestFinished(); |
| 116 return; | 114 return; |
| 117 } | |
| 118 } | |
| 119 for (requests_iter = queued_requests_.begin(); | |
| 120 requests_iter != queued_requests_.end(); | |
| 121 requests_iter++) { | |
| 122 if (*requests_iter == request) | |
| 123 return; | |
| 124 if ((*requests_iter)->GetMessageTextFragment() == | |
| 125 request->GetMessageTextFragment()) { | |
| 126 request->RequestFinished(); | |
| 127 return; | |
| 128 } | |
| 129 } | 115 } |
| 130 | 116 |
| 131 if (bubble_showing_) { | 117 if (bubble_showing_) { |
| 132 content::RecordAction( | 118 content::RecordAction( |
| 133 base::UserMetricsAction("PermissionBubbleRequestQueued")); | 119 base::UserMetricsAction("PermissionBubbleRequestQueued")); |
| 134 queued_requests_.push_back(request); | 120 if (is_main_frame) |
| 121 queued_requests_.push_back(request); |
| 122 else |
| 123 queued_frame_requests_.push_back(request); |
| 135 return; | 124 return; |
| 136 } | 125 } |
| 137 | 126 |
| 138 requests_.push_back(request); | 127 if (is_main_frame) { |
| 139 // TODO(gbillock): do we need to make default state a request property? | 128 requests_.push_back(request); |
| 140 accept_states_.push_back(true); | 129 // TODO(gbillock): do we need to make default state a request property? |
| 130 accept_states_.push_back(true); |
| 131 } else { |
| 132 queued_frame_requests_.push_back(request); |
| 133 } |
| 141 | 134 |
| 142 if (request->HasUserGesture()) | 135 if (request->HasUserGesture()) |
| 143 ShowBubble(); | 136 ScheduleShowBubble(); |
| 144 } | 137 } |
| 145 | 138 |
| 146 void PermissionBubbleManager::CancelRequest(PermissionBubbleRequest* request) { | 139 void PermissionBubbleManager::CancelRequest(PermissionBubbleRequest* request) { |
| 147 // First look in the queued requests, where we can simply delete the request | 140 // First look in the queued requests, where we can simply delete the request |
| 148 // and go on. | 141 // and go on. |
| 149 std::vector<PermissionBubbleRequest*>::iterator requests_iter; | 142 std::vector<PermissionBubbleRequest*>::iterator requests_iter; |
| 150 for (requests_iter = queued_requests_.begin(); | 143 for (requests_iter = queued_requests_.begin(); |
| 151 requests_iter != queued_requests_.end(); | 144 requests_iter != queued_requests_.end(); |
| 152 requests_iter++) { | 145 requests_iter++) { |
| 153 if (*requests_iter == request) { | 146 if (*requests_iter == request) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 165 continue; | 158 continue; |
| 166 | 159 |
| 167 // We can simply erase the current entry in the request table if we aren't | 160 // We can simply erase the current entry in the request table if we aren't |
| 168 // showing the dialog, or if we are showing it and it can accept the update. | 161 // showing the dialog, or if we are showing it and it can accept the update. |
| 169 bool can_erase = !bubble_showing_ || | 162 bool can_erase = !bubble_showing_ || |
| 170 !view_ || view_->CanAcceptRequestUpdate(); | 163 !view_ || view_->CanAcceptRequestUpdate(); |
| 171 if (can_erase) { | 164 if (can_erase) { |
| 172 (*requests_iter)->RequestFinished(); | 165 (*requests_iter)->RequestFinished(); |
| 173 requests_.erase(requests_iter); | 166 requests_.erase(requests_iter); |
| 174 accept_states_.erase(accepts_iter); | 167 accept_states_.erase(accepts_iter); |
| 175 ShowBubble(); // Will redraw the bubble if it is being shown. | 168 TriggerShowBubble(); // Will redraw the bubble if it is being shown. |
| 176 return; | 169 return; |
| 177 } | 170 } |
| 178 | 171 |
| 179 // Cancel the existing request and replace it with a dummy. | 172 // Cancel the existing request and replace it with a dummy. |
| 180 PermissionBubbleRequest* cancelled_request = | 173 PermissionBubbleRequest* cancelled_request = |
| 181 new CancelledRequest(*requests_iter); | 174 new CancelledRequest(*requests_iter); |
| 182 (*requests_iter)->RequestFinished(); | 175 (*requests_iter)->RequestFinished(); |
| 183 *requests_iter = cancelled_request; | 176 *requests_iter = cancelled_request; |
| 184 return; | 177 return; |
| 185 } | 178 } |
| (...skipping 10 matching lines...) Expand all Loading... |
| 196 view_->SetDelegate(NULL); | 189 view_->SetDelegate(NULL); |
| 197 view_->Hide(); | 190 view_->Hide(); |
| 198 bubble_showing_ = false; | 191 bubble_showing_ = false; |
| 199 } | 192 } |
| 200 | 193 |
| 201 view_ = view; | 194 view_ = view; |
| 202 if (!view) | 195 if (!view) |
| 203 return; | 196 return; |
| 204 | 197 |
| 205 view->SetDelegate(this); | 198 view->SetDelegate(this); |
| 206 ShowBubble(); | 199 TriggerShowBubble(); |
| 207 } | 200 } |
| 208 | 201 |
| 209 void PermissionBubbleManager::DocumentOnLoadCompletedInMainFrame() { | 202 void PermissionBubbleManager::DocumentOnLoadCompletedInMainFrame() { |
| 210 request_url_has_loaded_ = true; | 203 request_url_has_loaded_ = true; |
| 211 // This is scheduled because while all calls to the browser have been | 204 // This is scheduled because while all calls to the browser have been |
| 212 // issued at DOMContentLoaded, they may be bouncing around in scheduled | 205 // issued at DOMContentLoaded, they may be bouncing around in scheduled |
| 213 // callbacks finding the UI thread still. This makes sure we allow those | 206 // callbacks finding the UI thread still. This makes sure we allow those |
| 214 // scheduled calls to AddRequest to complete before we show the page-load | 207 // scheduled calls to AddRequest to complete before we show the page-load |
| 215 // permissions bubble. | 208 // permissions bubble. |
| 216 // TODO(gbillock): make this bind safe with a weak ptr. | 209 ScheduleShowBubble(); |
| 217 content::BrowserThread::PostTask( | 210 } |
| 218 content::BrowserThread::UI, FROM_HERE, | 211 |
| 219 base::Bind(&PermissionBubbleManager::ShowBubble, | 212 void PermissionBubbleManager::DocumentLoadedInFrame( |
| 220 weak_factory_.GetWeakPtr())); | 213 int64 frame_id, |
| 214 content::RenderViewHost* render_view_host) { |
| 215 if (request_url_has_loaded_) |
| 216 ScheduleShowBubble(); |
| 221 } | 217 } |
| 222 | 218 |
| 223 void PermissionBubbleManager::NavigationEntryCommitted( | 219 void PermissionBubbleManager::NavigationEntryCommitted( |
| 224 const content::LoadCommittedDetails& details) { | 220 const content::LoadCommittedDetails& details) { |
| 225 // No permissions requests pending. | 221 // No permissions requests pending. |
| 226 if (request_url_.is_empty()) | 222 if (request_url_.is_empty()) |
| 227 return; | 223 return; |
| 228 | 224 |
| 229 // If we have navigated to a new url... | 225 // If we have navigated to a new url or reloaded the page... |
| 230 if (request_url_ != web_contents()->GetLastCommittedURL()) { | 226 if (request_url_ != web_contents()->GetLastCommittedURL() || |
| 227 details.type == content::NAVIGATION_TYPE_EXISTING_PAGE) { |
| 231 // Kill off existing bubble and cancel any pending requests. | 228 // Kill off existing bubble and cancel any pending requests. |
| 232 CancelPendingQueue(); | 229 CancelPendingQueue(); |
| 233 FinalizeBubble(); | 230 FinalizeBubble(); |
| 234 } | 231 } |
| 235 } | 232 } |
| 236 | 233 |
| 237 void PermissionBubbleManager::WebContentsDestroyed() { | 234 void PermissionBubbleManager::WebContentsDestroyed() { |
| 238 // If the web contents has been destroyed, treat the bubble as cancelled. | 235 // If the web contents has been destroyed, treat the bubble as cancelled. |
| 239 CancelPendingQueue(); | 236 CancelPendingQueue(); |
| 240 FinalizeBubble(); | 237 FinalizeBubble(); |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 285 void PermissionBubbleManager::Closing() { | 282 void PermissionBubbleManager::Closing() { |
| 286 std::vector<PermissionBubbleRequest*>::iterator requests_iter; | 283 std::vector<PermissionBubbleRequest*>::iterator requests_iter; |
| 287 for (requests_iter = requests_.begin(); | 284 for (requests_iter = requests_.begin(); |
| 288 requests_iter != requests_.end(); | 285 requests_iter != requests_.end(); |
| 289 requests_iter++) { | 286 requests_iter++) { |
| 290 (*requests_iter)->Cancelled(); | 287 (*requests_iter)->Cancelled(); |
| 291 } | 288 } |
| 292 FinalizeBubble(); | 289 FinalizeBubble(); |
| 293 } | 290 } |
| 294 | 291 |
| 295 // TODO(gbillock): find a better name for this method. | 292 void PermissionBubbleManager::ScheduleShowBubble() { |
| 296 void PermissionBubbleManager::ShowBubble() { | 293 content::BrowserThread::PostTask( |
| 294 content::BrowserThread::UI, |
| 295 FROM_HERE, |
| 296 base::Bind(&PermissionBubbleManager::TriggerShowBubble, |
| 297 weak_factory_.GetWeakPtr())); |
| 298 } |
| 299 |
| 300 void PermissionBubbleManager::TriggerShowBubble() { |
| 297 if (!view_) | 301 if (!view_) |
| 298 return; | 302 return; |
| 299 if (requests_.empty()) | |
| 300 return; | |
| 301 if (bubble_showing_) | 303 if (bubble_showing_) |
| 302 return; | 304 return; |
| 303 if (!request_url_has_loaded_) | 305 if (!request_url_has_loaded_) |
| 304 return; | 306 return; |
| 307 if (requests_.empty() && queued_requests_.empty() && |
| 308 queued_frame_requests_.empty()) |
| 309 return; |
| 310 |
| 311 if (requests_.empty()) { |
| 312 // Queues containing a user-gesture-generated request have priority. |
| 313 if (HasUserGestureRequest(queued_requests_)) |
| 314 requests_.swap(queued_requests_); |
| 315 else if (HasUserGestureRequest(queued_frame_requests_)) |
| 316 requests_.swap(queued_frame_requests_); |
| 317 else if (queued_requests_.size()) |
| 318 requests_.swap(queued_requests_); |
| 319 else |
| 320 requests_.swap(queued_frame_requests_); |
| 321 |
| 322 // Sets the default value for each request to be 'accept'. |
| 323 // TODO(leng): Currently all requests default to true. If that changes: |
| 324 // a) Add additional accept_state queues to store default values. |
| 325 // b) Change the request API to provide the default value. |
| 326 accept_states_.resize(requests_.size(), true); |
| 327 } |
| 305 | 328 |
| 306 // Note: this should appear above Show() for testing, since in that | 329 // Note: this should appear above Show() for testing, since in that |
| 307 // case we may do in-line calling of finalization. | 330 // case we may do in-line calling of finalization. |
| 308 bubble_showing_ = true; | 331 bubble_showing_ = true; |
| 309 view_->Show(requests_, accept_states_, customization_mode_); | 332 view_->Show(requests_, accept_states_, customization_mode_); |
| 310 } | 333 } |
| 311 | 334 |
| 312 void PermissionBubbleManager::FinalizeBubble() { | 335 void PermissionBubbleManager::FinalizeBubble() { |
| 313 if (view_) | 336 if (view_) |
| 314 view_->Hide(); | 337 view_->Hide(); |
| 315 bubble_showing_ = false; | 338 bubble_showing_ = false; |
| 316 | 339 |
| 317 std::vector<PermissionBubbleRequest*>::iterator requests_iter; | 340 std::vector<PermissionBubbleRequest*>::iterator requests_iter; |
| 318 for (requests_iter = requests_.begin(); | 341 for (requests_iter = requests_.begin(); |
| 319 requests_iter != requests_.end(); | 342 requests_iter != requests_.end(); |
| 320 requests_iter++) { | 343 requests_iter++) { |
| 321 (*requests_iter)->RequestFinished(); | 344 (*requests_iter)->RequestFinished(); |
| 322 } | 345 } |
| 323 requests_.clear(); | 346 requests_.clear(); |
| 324 accept_states_.clear(); | 347 accept_states_.clear(); |
| 325 if (queued_requests_.size()) { | 348 if (queued_requests_.size() || queued_frame_requests_.size()) { |
| 326 requests_ = queued_requests_; | 349 TriggerShowBubble(); |
| 327 accept_states_.resize(requests_.size(), true); | |
| 328 queued_requests_.clear(); | |
| 329 ShowBubble(); | |
| 330 } else { | 350 } else { |
| 331 request_url_ = GURL(); | 351 request_url_ = GURL(); |
| 332 } | 352 } |
| 333 } | 353 } |
| 334 | 354 |
| 335 void PermissionBubbleManager::CancelPendingQueue() { | 355 void PermissionBubbleManager::CancelPendingQueue() { |
| 336 std::vector<PermissionBubbleRequest*>::iterator requests_iter; | 356 std::vector<PermissionBubbleRequest*>::iterator requests_iter; |
| 337 for (requests_iter = queued_requests_.begin(); | 357 for (requests_iter = queued_requests_.begin(); |
| 338 requests_iter != queued_requests_.end(); | 358 requests_iter != queued_requests_.end(); |
| 339 requests_iter++) { | 359 requests_iter++) { |
| 340 (*requests_iter)->RequestFinished(); | 360 (*requests_iter)->RequestFinished(); |
| 341 } | 361 } |
| 342 } | 362 } |
| 363 |
| 364 bool PermissionBubbleManager::ExistingRequest( |
| 365 PermissionBubbleRequest* request, |
| 366 const std::vector<PermissionBubbleRequest*>& queue, |
| 367 bool* same_object) { |
| 368 CHECK(same_object); |
| 369 *same_object = false; |
| 370 std::vector<PermissionBubbleRequest*>::const_iterator iter; |
| 371 for (iter = queue.begin(); iter != queue.end(); iter++) { |
| 372 if (*iter == request) { |
| 373 *same_object = true; |
| 374 return true; |
| 375 } |
| 376 if ((*iter)->GetMessageTextFragment() == |
| 377 request->GetMessageTextFragment() && |
| 378 (*iter)->GetRequestingHostname() == request->GetRequestingHostname()) { |
| 379 return true; |
| 380 } |
| 381 } |
| 382 return false; |
| 383 } |
| 384 |
| 385 bool PermissionBubbleManager::HasUserGestureRequest( |
| 386 const std::vector<PermissionBubbleRequest*>& queue) { |
| 387 std::vector<PermissionBubbleRequest*>::const_iterator iter; |
| 388 for (iter = queue.begin(); iter != queue.end(); iter++) { |
| 389 if ((*iter)->HasUserGesture()) |
| 390 return true; |
| 391 } |
| 392 return false; |
| 393 } |
| OLD | NEW |