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

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

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

Powered by Google App Engine
This is Rietveld 408576698