Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / website_settings / permission_bubble_manager.cc
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 "base/command_line.h"
8 #include "base/metrics/user_metrics_action.h"
9 #include "chrome/browser/ui/website_settings/permission_bubble_request.h"
10 #include "chrome/common/chrome_switches.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/navigation_details.h"
13 #include "content/public/browser/user_metrics.h"
14
15 namespace {
16
17 class CancelledRequest : public PermissionBubbleRequest {
18  public:
19   explicit CancelledRequest(PermissionBubbleRequest* cancelled)
20       : icon_(cancelled->GetIconID()),
21         message_text_(cancelled->GetMessageText()),
22         message_fragment_(cancelled->GetMessageTextFragment()),
23         user_gesture_(cancelled->HasUserGesture()),
24         hostname_(cancelled->GetRequestingHostname()) {}
25   ~CancelledRequest() override {}
26
27   int GetIconID() const override { return icon_; }
28   base::string16 GetMessageText() const override { return message_text_; }
29   base::string16 GetMessageTextFragment() const override {
30     return message_fragment_;
31   }
32   bool HasUserGesture() const override { return user_gesture_; }
33   GURL GetRequestingHostname() const override { return hostname_; }
34
35   // These are all no-ops since the placeholder is non-forwarding.
36   void PermissionGranted() override {}
37   void PermissionDenied() override {}
38   void Cancelled() override {}
39
40   void RequestFinished() override { delete this; }
41
42  private:
43   int icon_;
44   base::string16 message_text_;
45   base::string16 message_fragment_;
46   bool user_gesture_;
47   GURL hostname_;
48 };
49
50 }  // namespace
51
52 DEFINE_WEB_CONTENTS_USER_DATA_KEY(PermissionBubbleManager);
53
54 // static
55 bool PermissionBubbleManager::Enabled() {
56   if (CommandLine::ForCurrentProcess()->HasSwitch(
57           switches::kEnablePermissionsBubbles))
58     return true;
59
60   if (CommandLine::ForCurrentProcess()->HasSwitch(
61           switches::kDisablePermissionsBubbles))
62     return false;
63
64   return false;
65 }
66
67 PermissionBubbleManager::PermissionBubbleManager(
68     content::WebContents* web_contents)
69   : content::WebContentsObserver(web_contents),
70     bubble_showing_(false),
71     view_(NULL),
72     request_url_has_loaded_(false),
73     customization_mode_(false),
74     weak_factory_(this) {}
75
76 PermissionBubbleManager::~PermissionBubbleManager() {
77   if (view_ != NULL)
78     view_->SetDelegate(NULL);
79
80   std::vector<PermissionBubbleRequest*>::iterator requests_iter;
81   for (requests_iter = requests_.begin();
82        requests_iter != requests_.end();
83        requests_iter++) {
84     (*requests_iter)->RequestFinished();
85   }
86   for (requests_iter = queued_requests_.begin();
87        requests_iter != queued_requests_.end();
88        requests_iter++) {
89     (*requests_iter)->RequestFinished();
90   }
91 }
92
93 void PermissionBubbleManager::AddRequest(PermissionBubbleRequest* request) {
94   content::RecordAction(base::UserMetricsAction("PermissionBubbleRequest"));
95   // TODO(gbillock): is there a race between an early request on a
96   // newly-navigated page and the to-be-cleaned-up requests on the previous
97   // page? We should maybe listen to DidStartNavigationToPendingEntry (and
98   // any other renderer-side nav initiations?). Double-check this for
99   // correct behavior on interstitials -- we probably want to basically queue
100   // any request for which GetVisibleURL != GetLastCommittedURL.
101   request_url_ = web_contents()->GetLastCommittedURL();
102   bool is_main_frame =
103       request->GetRequestingHostname().GetOrigin() == request_url_.GetOrigin();
104
105   // Don't re-add an existing request or one with a duplicate text request.
106   bool same_object = false;
107   if (ExistingRequest(request, requests_, &same_object) ||
108       ExistingRequest(request, queued_requests_, &same_object) ||
109       ExistingRequest(request, queued_frame_requests_, &same_object)) {
110     if (!same_object)
111       request->RequestFinished();
112     return;
113   }
114
115   if (bubble_showing_) {
116     if (is_main_frame) {
117       content::RecordAction(
118           base::UserMetricsAction("PermissionBubbleRequestQueued"));
119       queued_requests_.push_back(request);
120     } else {
121       content::RecordAction(
122           base::UserMetricsAction("PermissionBubbleIFrameRequestQueued"));
123       queued_frame_requests_.push_back(request);
124     }
125     return;
126   }
127
128   if (is_main_frame) {
129     requests_.push_back(request);
130     // TODO(gbillock): do we need to make default state a request property?
131     accept_states_.push_back(true);
132   } else {
133     content::RecordAction(
134         base::UserMetricsAction("PermissionBubbleIFrameRequestQueued"));
135     queued_frame_requests_.push_back(request);
136   }
137
138   if (request->HasUserGesture())
139     ScheduleShowBubble();
140 }
141
142 void PermissionBubbleManager::CancelRequest(PermissionBubbleRequest* request) {
143   // First look in the queued requests, where we can simply delete the request
144   // and go on.
145   std::vector<PermissionBubbleRequest*>::iterator requests_iter;
146   for (requests_iter = queued_requests_.begin();
147        requests_iter != queued_requests_.end();
148        requests_iter++) {
149     if (*requests_iter == request) {
150       (*requests_iter)->RequestFinished();
151       queued_requests_.erase(requests_iter);
152       return;
153     }
154   }
155
156   std::vector<bool>::iterator accepts_iter = accept_states_.begin();
157   for (requests_iter = requests_.begin(), accepts_iter = accept_states_.begin();
158        requests_iter != requests_.end();
159        requests_iter++, accepts_iter++) {
160     if (*requests_iter != request)
161       continue;
162
163     // We can simply erase the current entry in the request table if we aren't
164     // showing the dialog, or if we are showing it and it can accept the update.
165     bool can_erase = !bubble_showing_ ||
166                      !view_ || view_->CanAcceptRequestUpdate();
167     if (can_erase) {
168       (*requests_iter)->RequestFinished();
169       requests_.erase(requests_iter);
170       accept_states_.erase(accepts_iter);
171       TriggerShowBubble();  // Will redraw the bubble if it is being shown.
172       return;
173     }
174
175     // Cancel the existing request and replace it with a dummy.
176     PermissionBubbleRequest* cancelled_request =
177         new CancelledRequest(*requests_iter);
178     (*requests_iter)->RequestFinished();
179     *requests_iter = cancelled_request;
180     return;
181   }
182
183   NOTREACHED();  // Callers should not cancel requests that are not pending.
184 }
185
186 void PermissionBubbleManager::SetView(PermissionBubbleView* view) {
187   if (view == view_)
188     return;
189
190   // Disengage from the existing view if there is one.
191   if (view_ != NULL) {
192     view_->SetDelegate(NULL);
193     view_->Hide();
194     bubble_showing_ = false;
195   }
196
197   view_ = view;
198   if (!view)
199     return;
200
201   view->SetDelegate(this);
202   TriggerShowBubble();
203 }
204
205 void PermissionBubbleManager::DocumentOnLoadCompletedInMainFrame() {
206   request_url_has_loaded_ = true;
207   // This is scheduled because while all calls to the browser have been
208   // issued at DOMContentLoaded, they may be bouncing around in scheduled
209   // callbacks finding the UI thread still. This makes sure we allow those
210   // scheduled calls to AddRequest to complete before we show the page-load
211   // permissions bubble.
212   ScheduleShowBubble();
213 }
214
215 void PermissionBubbleManager::DocumentLoadedInFrame(
216     content::RenderFrameHost* render_frame_host) {
217   if (request_url_has_loaded_)
218     ScheduleShowBubble();
219 }
220
221 void PermissionBubbleManager::NavigationEntryCommitted(
222     const content::LoadCommittedDetails& details) {
223   // No permissions requests pending.
224   if (request_url_.is_empty())
225     return;
226
227   // If we have navigated to a new url or reloaded the page...
228   // GetAsReferrer strips fragment and username/password, meaning
229   // the navigation is really to the same page.
230   if ((request_url_.GetAsReferrer() !=
231        web_contents()->GetLastCommittedURL().GetAsReferrer()) ||
232       details.type == content::NAVIGATION_TYPE_EXISTING_PAGE) {
233     // Kill off existing bubble and cancel any pending requests.
234     CancelPendingQueues();
235     FinalizeBubble();
236   }
237 }
238
239 void PermissionBubbleManager::WebContentsDestroyed() {
240   // If the web contents has been destroyed, treat the bubble as cancelled.
241   CancelPendingQueues();
242   FinalizeBubble();
243
244   // The WebContents is going away; be aggressively paranoid and delete
245   // ourselves lest other parts of the system attempt to add permission bubbles
246   // or use us otherwise during the destruction.
247   web_contents()->RemoveUserData(UserDataKey());
248   // That was the equivalent of "delete this". This object is now destroyed;
249   // returning from this function is the only safe thing to do.
250 }
251
252 void PermissionBubbleManager::ToggleAccept(int request_index, bool new_value) {
253   DCHECK(request_index < static_cast<int>(accept_states_.size()));
254   accept_states_[request_index] = new_value;
255 }
256
257 void PermissionBubbleManager::SetCustomizationMode() {
258   customization_mode_ = true;
259   if (view_)
260     view_->Show(requests_, accept_states_, customization_mode_);
261 }
262
263 void PermissionBubbleManager::Accept() {
264   std::vector<PermissionBubbleRequest*>::iterator requests_iter;
265   std::vector<bool>::iterator accepts_iter = accept_states_.begin();
266   for (requests_iter = requests_.begin(), accepts_iter = accept_states_.begin();
267        requests_iter != requests_.end();
268        requests_iter++, accepts_iter++) {
269     if (*accepts_iter)
270       (*requests_iter)->PermissionGranted();
271     else
272       (*requests_iter)->PermissionDenied();
273   }
274   FinalizeBubble();
275 }
276
277 void PermissionBubbleManager::Deny() {
278   std::vector<PermissionBubbleRequest*>::iterator requests_iter;
279   for (requests_iter = requests_.begin();
280        requests_iter != requests_.end();
281        requests_iter++) {
282     (*requests_iter)->PermissionDenied();
283   }
284   FinalizeBubble();
285 }
286
287 void PermissionBubbleManager::Closing() {
288   std::vector<PermissionBubbleRequest*>::iterator requests_iter;
289   for (requests_iter = requests_.begin();
290        requests_iter != requests_.end();
291        requests_iter++) {
292     (*requests_iter)->Cancelled();
293   }
294   FinalizeBubble();
295 }
296
297 void PermissionBubbleManager::ScheduleShowBubble() {
298   content::BrowserThread::PostTask(
299       content::BrowserThread::UI,
300       FROM_HERE,
301       base::Bind(&PermissionBubbleManager::TriggerShowBubble,
302                  weak_factory_.GetWeakPtr()));
303 }
304
305 void PermissionBubbleManager::TriggerShowBubble() {
306   if (!view_)
307     return;
308   if (bubble_showing_)
309     return;
310   if (!request_url_has_loaded_)
311     return;
312   if (requests_.empty() && queued_requests_.empty() &&
313       queued_frame_requests_.empty()) {
314     return;
315   }
316
317   if (requests_.empty()) {
318     // Queues containing a user-gesture-generated request have priority.
319     if (HasUserGestureRequest(queued_requests_))
320       requests_.swap(queued_requests_);
321     else if (HasUserGestureRequest(queued_frame_requests_))
322       requests_.swap(queued_frame_requests_);
323     else if (queued_requests_.size())
324       requests_.swap(queued_requests_);
325     else
326       requests_.swap(queued_frame_requests_);
327
328     // Sets the default value for each request to be 'accept'.
329     // TODO(leng):  Currently all requests default to true.  If that changes:
330     // a) Add additional accept_state queues to store default values.
331     // b) Change the request API to provide the default value.
332     accept_states_.resize(requests_.size(), true);
333   }
334
335   // Note: this should appear above Show() for testing, since in that
336   // case we may do in-line calling of finalization.
337   bubble_showing_ = true;
338   view_->Show(requests_, accept_states_, customization_mode_);
339 }
340
341 void PermissionBubbleManager::FinalizeBubble() {
342   if (view_)
343     view_->Hide();
344   bubble_showing_ = false;
345
346   std::vector<PermissionBubbleRequest*>::iterator requests_iter;
347   for (requests_iter = requests_.begin();
348        requests_iter != requests_.end();
349        requests_iter++) {
350     (*requests_iter)->RequestFinished();
351   }
352   requests_.clear();
353   accept_states_.clear();
354   if (queued_requests_.size() || queued_frame_requests_.size())
355     TriggerShowBubble();
356   else
357     request_url_ = GURL();
358 }
359
360 void PermissionBubbleManager::CancelPendingQueues() {
361   std::vector<PermissionBubbleRequest*>::iterator requests_iter;
362   for (requests_iter = queued_requests_.begin();
363        requests_iter != queued_requests_.end();
364        requests_iter++) {
365     (*requests_iter)->RequestFinished();
366   }
367   for (requests_iter = queued_frame_requests_.begin();
368        requests_iter != queued_frame_requests_.end();
369        requests_iter++) {
370     (*requests_iter)->RequestFinished();
371   }
372   queued_requests_.clear();
373   queued_frame_requests_.clear();
374 }
375
376 bool PermissionBubbleManager::ExistingRequest(
377     PermissionBubbleRequest* request,
378     const std::vector<PermissionBubbleRequest*>& queue,
379     bool* same_object) {
380   CHECK(same_object);
381   *same_object = false;
382   std::vector<PermissionBubbleRequest*>::const_iterator iter;
383   for (iter = queue.begin(); iter != queue.end(); iter++) {
384     if (*iter == request) {
385       *same_object = true;
386       return true;
387     }
388     if ((*iter)->GetMessageTextFragment() ==
389             request->GetMessageTextFragment() &&
390         (*iter)->GetRequestingHostname() == request->GetRequestingHostname()) {
391       return true;
392     }
393   }
394   return false;
395 }
396
397 bool PermissionBubbleManager::HasUserGestureRequest(
398     const std::vector<PermissionBubbleRequest*>& queue) {
399   std::vector<PermissionBubbleRequest*>::const_iterator iter;
400   for (iter = queue.begin(); iter != queue.end(); iter++) {
401     if ((*iter)->HasUserGesture())
402       return true;
403   }
404   return false;
405 }
406