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