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 |