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 |