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

Side by Side Diff: chrome/browser/ui/website_settings/permission_bubble_manager.cc

Issue 2081103002: Rename PermissionBubbleManager to PermissionRequestManager (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Review comments Created 4 years, 5 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/website_settings/permission_bubble_manager.h"
6
7 #include <algorithm>
8
9 #include "base/command_line.h"
10 #include "base/metrics/user_metrics_action.h"
11 #include "build/build_config.h"
12 #include "chrome/browser/permissions/permission_uma_util.h"
13 #include "chrome/browser/ui/website_settings/permission_bubble_request.h"
14 #include "chrome/common/chrome_switches.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/navigation_details.h"
17 #include "content/public/browser/user_metrics.h"
18 #include "url/origin.h"
19
20 #if !defined(OS_ANDROID)
21 #include "chrome/browser/ui/browser_finder.h"
22 #endif
23
24 namespace {
25
26 class CancelledRequest : public PermissionBubbleRequest {
27 public:
28 explicit CancelledRequest(PermissionBubbleRequest* cancelled)
29 : icon_(cancelled->GetIconId()),
30 message_fragment_(cancelled->GetMessageTextFragment()),
31 origin_(cancelled->GetOrigin()) {}
32 ~CancelledRequest() override {}
33
34 int GetIconId() const override { return icon_; }
35 base::string16 GetMessageTextFragment() const override {
36 return message_fragment_;
37 }
38 GURL GetOrigin() const override { return origin_; }
39
40 // These are all no-ops since the placeholder is non-forwarding.
41 void PermissionGranted() override {}
42 void PermissionDenied() override {}
43 void Cancelled() override {}
44
45 void RequestFinished() override { delete this; }
46
47 private:
48 int icon_;
49 base::string16 message_fragment_;
50 GURL origin_;
51 };
52
53 bool IsMessageTextEqual(PermissionBubbleRequest* a,
54 PermissionBubbleRequest* b) {
55 if (a == b)
56 return true;
57 if (a->GetMessageTextFragment() == b->GetMessageTextFragment() &&
58 a->GetOrigin() == b->GetOrigin()) {
59 return true;
60 }
61 return false;
62 }
63
64 } // namespace
65
66 // PermissionBubbleManager::Observer -------------------------------------------
67
68 PermissionBubbleManager::Observer::~Observer() {
69 }
70
71 void PermissionBubbleManager::Observer::OnBubbleAdded() {
72 }
73
74 // PermissionBubbleManager -----------------------------------------------------
75
76 DEFINE_WEB_CONTENTS_USER_DATA_KEY(PermissionBubbleManager);
77
78 PermissionBubbleManager::PermissionBubbleManager(
79 content::WebContents* web_contents)
80 : content::WebContentsObserver(web_contents),
81 #if !defined(OS_ANDROID) // No bubbles in android tests.
82 view_factory_(base::Bind(&PermissionBubbleView::Create)),
83 #endif
84 view_(nullptr),
85 main_frame_has_fully_loaded_(false),
86 auto_response_for_test_(NONE),
87 weak_factory_(this) {
88 }
89
90 PermissionBubbleManager::~PermissionBubbleManager() {
91 if (view_ != NULL)
92 view_->SetDelegate(NULL);
93
94 for (PermissionBubbleRequest* request : requests_)
95 request->RequestFinished();
96 for (PermissionBubbleRequest* request : queued_requests_)
97 request->RequestFinished();
98 for (PermissionBubbleRequest* request : queued_frame_requests_)
99 request->RequestFinished();
100 for (const auto& entry : duplicate_requests_)
101 entry.second->RequestFinished();
102 }
103
104 void PermissionBubbleManager::AddRequest(PermissionBubbleRequest* request) {
105 content::RecordAction(base::UserMetricsAction("PermissionBubbleRequest"));
106 // TODO(gbillock): is there a race between an early request on a
107 // newly-navigated page and the to-be-cleaned-up requests on the previous
108 // page? We should maybe listen to DidStartNavigationToPendingEntry (and
109 // any other renderer-side nav initiations?). Double-check this for
110 // correct behavior on interstitials -- we probably want to basically queue
111 // any request for which GetVisibleURL != GetLastCommittedURL.
112 request_url_ = web_contents()->GetLastCommittedURL();
113 bool is_main_frame = url::Origin(request_url_)
114 .IsSameOriginWith(url::Origin(request->GetOrigin()));
115
116 // Don't re-add an existing request or one with a duplicate text request.
117 PermissionBubbleRequest* existing_request = GetExistingRequest(request);
118 if (existing_request) {
119 // |request| is a duplicate. Add it to |duplicate_requests_| unless it's the
120 // same object as |existing_request| or an existing duplicate.
121 if (request == existing_request)
122 return;
123 auto range = duplicate_requests_.equal_range(existing_request);
124 for (auto it = range.first; it != range.second; ++it) {
125 if (request == it->second)
126 return;
127 }
128 duplicate_requests_.insert(std::make_pair(existing_request, request));
129 return;
130 }
131
132 if (IsBubbleVisible()) {
133 if (is_main_frame) {
134 content::RecordAction(
135 base::UserMetricsAction("PermissionBubbleRequestQueued"));
136 queued_requests_.push_back(request);
137 } else {
138 content::RecordAction(
139 base::UserMetricsAction("PermissionBubbleIFrameRequestQueued"));
140 queued_frame_requests_.push_back(request);
141 }
142 return;
143 }
144
145 if (is_main_frame) {
146 requests_.push_back(request);
147 accept_states_.push_back(true);
148 } else {
149 content::RecordAction(
150 base::UserMetricsAction("PermissionBubbleIFrameRequestQueued"));
151 queued_frame_requests_.push_back(request);
152 }
153
154 ScheduleShowBubble();
155 }
156
157 void PermissionBubbleManager::CancelRequest(PermissionBubbleRequest* request) {
158 // First look in the queued requests, where we can simply finish the request
159 // and go on.
160 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
161 for (requests_iter = queued_requests_.begin();
162 requests_iter != queued_requests_.end();
163 requests_iter++) {
164 if (*requests_iter == request) {
165 RequestFinishedIncludingDuplicates(*requests_iter);
166 queued_requests_.erase(requests_iter);
167 return;
168 }
169 }
170 for (requests_iter = queued_frame_requests_.begin();
171 requests_iter != queued_frame_requests_.end(); requests_iter++) {
172 if (*requests_iter == request) {
173 RequestFinishedIncludingDuplicates(*requests_iter);
174 queued_frame_requests_.erase(requests_iter);
175 return;
176 }
177 }
178
179 std::vector<bool>::iterator accepts_iter = accept_states_.begin();
180 for (requests_iter = requests_.begin(), accepts_iter = accept_states_.begin();
181 requests_iter != requests_.end();
182 requests_iter++, accepts_iter++) {
183 if (*requests_iter != request)
184 continue;
185
186 // We can simply erase the current entry in the request table if we aren't
187 // showing the dialog, or if we are showing it and it can accept the update.
188 bool can_erase = !IsBubbleVisible() || view_->CanAcceptRequestUpdate();
189 if (can_erase) {
190 RequestFinishedIncludingDuplicates(*requests_iter);
191 requests_.erase(requests_iter);
192 accept_states_.erase(accepts_iter);
193
194 if (IsBubbleVisible()) {
195 view_->Hide();
196 // Will redraw the bubble if it is being shown.
197 TriggerShowBubble();
198 }
199 return;
200 }
201
202 // Cancel the existing request and replace it with a dummy.
203 PermissionBubbleRequest* cancelled_request =
204 new CancelledRequest(*requests_iter);
205 RequestFinishedIncludingDuplicates(*requests_iter);
206 *requests_iter = cancelled_request;
207 return;
208 }
209
210 // Since |request| wasn't found in queued_requests_, queued_frame_requests_ or
211 // requests_ it must have been marked as a duplicate. We can't search
212 // duplicate_requests_ by value, so instead use GetExistingRequest to find the
213 // key (request it was duped against), and iterate through duplicates of that.
214 PermissionBubbleRequest* existing_request = GetExistingRequest(request);
215 auto range = duplicate_requests_.equal_range(existing_request);
216 for (auto it = range.first; it != range.second; ++it) {
217 if (request == it->second) {
218 it->second->RequestFinished();
219 duplicate_requests_.erase(it);
220 return;
221 }
222 }
223
224 NOTREACHED(); // Callers should not cancel requests that are not pending.
225 }
226
227 void PermissionBubbleManager::HideBubble() {
228 // Disengage from the existing view if there is one.
229 if (!view_)
230 return;
231
232 view_->SetDelegate(nullptr);
233 view_->Hide();
234 view_.reset();
235 }
236
237 void PermissionBubbleManager::DisplayPendingRequests() {
238 if (IsBubbleVisible())
239 return;
240
241 #if defined(OS_ANDROID)
242 NOTREACHED();
243 return;
244 #else
245 view_ = view_factory_.Run(chrome::FindBrowserWithWebContents(web_contents()));
246 view_->SetDelegate(this);
247 #endif
248
249 TriggerShowBubble();
250 }
251
252 void PermissionBubbleManager::UpdateAnchorPosition() {
253 if (view_)
254 view_->UpdateAnchorPosition();
255 }
256
257 bool PermissionBubbleManager::IsBubbleVisible() {
258 return view_ && view_->IsVisible();
259 }
260
261 gfx::NativeWindow PermissionBubbleManager::GetBubbleWindow() {
262 if (view_)
263 return view_->GetNativeWindow();
264 return nullptr;
265 }
266
267 void PermissionBubbleManager::DidNavigateMainFrame(
268 const content::LoadCommittedDetails& details,
269 const content::FrameNavigateParams& params) {
270 if (details.is_in_page)
271 return;
272
273 CancelPendingQueues();
274 FinalizeBubble();
275 main_frame_has_fully_loaded_ = false;
276 }
277
278 void PermissionBubbleManager::DocumentOnLoadCompletedInMainFrame() {
279 main_frame_has_fully_loaded_ = true;
280 // This is scheduled because while all calls to the browser have been
281 // issued at DOMContentLoaded, they may be bouncing around in scheduled
282 // callbacks finding the UI thread still. This makes sure we allow those
283 // scheduled calls to AddRequest to complete before we show the page-load
284 // permissions bubble.
285 ScheduleShowBubble();
286 }
287
288 void PermissionBubbleManager::DocumentLoadedInFrame(
289 content::RenderFrameHost* render_frame_host) {
290 ScheduleShowBubble();
291 }
292
293 void PermissionBubbleManager::WebContentsDestroyed() {
294 // If the web contents has been destroyed, treat the bubble as cancelled.
295 CancelPendingQueues();
296 FinalizeBubble();
297
298 // The WebContents is going away; be aggressively paranoid and delete
299 // ourselves lest other parts of the system attempt to add permission bubbles
300 // or use us otherwise during the destruction.
301 web_contents()->RemoveUserData(UserDataKey());
302 // That was the equivalent of "delete this". This object is now destroyed;
303 // returning from this function is the only safe thing to do.
304 }
305
306 void PermissionBubbleManager::ToggleAccept(int request_index, bool new_value) {
307 DCHECK(request_index < static_cast<int>(accept_states_.size()));
308 accept_states_[request_index] = new_value;
309 }
310
311 void PermissionBubbleManager::Accept() {
312 PermissionUmaUtil::PermissionPromptAccepted(requests_, accept_states_);
313
314 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
315 std::vector<bool>::iterator accepts_iter = accept_states_.begin();
316 for (requests_iter = requests_.begin(), accepts_iter = accept_states_.begin();
317 requests_iter != requests_.end();
318 requests_iter++, accepts_iter++) {
319 if (*accepts_iter) {
320 PermissionGrantedIncludingDuplicates(*requests_iter);
321 } else {
322 PermissionDeniedIncludingDuplicates(*requests_iter);
323 }
324 }
325 FinalizeBubble();
326 }
327
328 void PermissionBubbleManager::Deny() {
329 PermissionUmaUtil::PermissionPromptDenied(requests_);
330
331 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
332 for (requests_iter = requests_.begin();
333 requests_iter != requests_.end();
334 requests_iter++) {
335 PermissionDeniedIncludingDuplicates(*requests_iter);
336 }
337 FinalizeBubble();
338 }
339
340 void PermissionBubbleManager::Closing() {
341 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
342 for (requests_iter = requests_.begin();
343 requests_iter != requests_.end();
344 requests_iter++) {
345 CancelledIncludingDuplicates(*requests_iter);
346 }
347 FinalizeBubble();
348 }
349
350 void PermissionBubbleManager::ScheduleShowBubble() {
351 // ::ScheduleShowBubble() will be called again when the main frame will be
352 // loaded.
353 if (!main_frame_has_fully_loaded_)
354 return;
355
356 content::BrowserThread::PostTask(
357 content::BrowserThread::UI,
358 FROM_HERE,
359 base::Bind(&PermissionBubbleManager::TriggerShowBubble,
360 weak_factory_.GetWeakPtr()));
361 }
362
363 void PermissionBubbleManager::TriggerShowBubble() {
364 if (!view_)
365 return;
366 if (IsBubbleVisible())
367 return;
368 if (!main_frame_has_fully_loaded_)
369 return;
370 if (requests_.empty() && queued_requests_.empty() &&
371 queued_frame_requests_.empty()) {
372 return;
373 }
374
375 if (requests_.empty()) {
376 if (queued_requests_.size())
377 requests_.swap(queued_requests_);
378 else
379 requests_.swap(queued_frame_requests_);
380
381 // Sets the default value for each request to be 'accept'.
382 accept_states_.resize(requests_.size(), true);
383 }
384
385 view_->Show(requests_, accept_states_);
386 PermissionUmaUtil::PermissionPromptShown(requests_);
387 NotifyBubbleAdded();
388
389 // If in testing mode, automatically respond to the bubble that was shown.
390 if (auto_response_for_test_ != NONE)
391 DoAutoResponseForTesting();
392 }
393
394 void PermissionBubbleManager::FinalizeBubble() {
395 if (view_)
396 view_->Hide();
397
398 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
399 for (requests_iter = requests_.begin();
400 requests_iter != requests_.end();
401 requests_iter++) {
402 RequestFinishedIncludingDuplicates(*requests_iter);
403 }
404 requests_.clear();
405 accept_states_.clear();
406 if (queued_requests_.size() || queued_frame_requests_.size())
407 TriggerShowBubble();
408 else
409 request_url_ = GURL();
410 }
411
412 void PermissionBubbleManager::CancelPendingQueues() {
413 std::vector<PermissionBubbleRequest*>::iterator requests_iter;
414 for (requests_iter = queued_requests_.begin();
415 requests_iter != queued_requests_.end();
416 requests_iter++) {
417 RequestFinishedIncludingDuplicates(*requests_iter);
418 }
419 for (requests_iter = queued_frame_requests_.begin();
420 requests_iter != queued_frame_requests_.end();
421 requests_iter++) {
422 RequestFinishedIncludingDuplicates(*requests_iter);
423 }
424 queued_requests_.clear();
425 queued_frame_requests_.clear();
426 }
427
428 PermissionBubbleRequest* PermissionBubbleManager::GetExistingRequest(
429 PermissionBubbleRequest* request) {
430 for (PermissionBubbleRequest* existing_request : requests_)
431 if (IsMessageTextEqual(existing_request, request))
432 return existing_request;
433 for (PermissionBubbleRequest* existing_request : queued_requests_)
434 if (IsMessageTextEqual(existing_request, request))
435 return existing_request;
436 for (PermissionBubbleRequest* existing_request : queued_frame_requests_)
437 if (IsMessageTextEqual(existing_request, request))
438 return existing_request;
439 return nullptr;
440 }
441
442 void PermissionBubbleManager::PermissionGrantedIncludingDuplicates(
443 PermissionBubbleRequest* request) {
444 DCHECK_EQ(request, GetExistingRequest(request))
445 << "Only requests in [queued_[frame_]]requests_ can have duplicates";
446 request->PermissionGranted();
447 auto range = duplicate_requests_.equal_range(request);
448 for (auto it = range.first; it != range.second; ++it)
449 it->second->PermissionGranted();
450 }
451 void PermissionBubbleManager::PermissionDeniedIncludingDuplicates(
452 PermissionBubbleRequest* request) {
453 DCHECK_EQ(request, GetExistingRequest(request))
454 << "Only requests in [queued_[frame_]]requests_ can have duplicates";
455 request->PermissionDenied();
456 auto range = duplicate_requests_.equal_range(request);
457 for (auto it = range.first; it != range.second; ++it)
458 it->second->PermissionDenied();
459 }
460 void PermissionBubbleManager::CancelledIncludingDuplicates(
461 PermissionBubbleRequest* request) {
462 DCHECK_EQ(request, GetExistingRequest(request))
463 << "Only requests in [queued_[frame_]]requests_ can have duplicates";
464 request->Cancelled();
465 auto range = duplicate_requests_.equal_range(request);
466 for (auto it = range.first; it != range.second; ++it)
467 it->second->Cancelled();
468 }
469 void PermissionBubbleManager::RequestFinishedIncludingDuplicates(
470 PermissionBubbleRequest* request) {
471 // We can't call GetExistingRequest here, because other entries in requests_,
472 // queued_requests_ or queued_frame_requests_ might already have been deleted.
473 DCHECK_EQ(1, std::count(requests_.begin(), requests_.end(), request) +
474 std::count(queued_requests_.begin(), queued_requests_.end(),
475 request) +
476 std::count(queued_frame_requests_.begin(),
477 queued_frame_requests_.end(), request))
478 << "Only requests in [queued_[frame_]]requests_ can have duplicates";
479 request->RequestFinished();
480 // Beyond this point, |request| has probably been deleted.
481 auto range = duplicate_requests_.equal_range(request);
482 for (auto it = range.first; it != range.second; ++it)
483 it->second->RequestFinished();
484 // Additionally, we can now remove the duplicates.
485 duplicate_requests_.erase(request);
486 }
487
488 void PermissionBubbleManager::AddObserver(Observer* observer) {
489 observer_list_.AddObserver(observer);
490 }
491
492 void PermissionBubbleManager::RemoveObserver(Observer* observer) {
493 observer_list_.RemoveObserver(observer);
494 }
495
496 void PermissionBubbleManager::NotifyBubbleAdded() {
497 FOR_EACH_OBSERVER(Observer, observer_list_, OnBubbleAdded());
498 }
499
500 void PermissionBubbleManager::DoAutoResponseForTesting() {
501 switch (auto_response_for_test_) {
502 case ACCEPT_ALL:
503 Accept();
504 break;
505 case DENY_ALL:
506 Deny();
507 break;
508 case DISMISS:
509 Closing();
510 break;
511 case NONE:
512 NOTREACHED();
513 }
514 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698