1// Copyright (c) 2012 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/chrome_quota_permission_context.h"
6
7#include <string>
8
9#include "base/bind.h"
10#include "base/prefs/pref_service.h"
11#include "base/strings/utf_string_conversions.h"
12#include "chrome/browser/infobars/infobar_service.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/browser/tab_contents/tab_util.h"
15#include "chrome/browser/ui/website_settings/permission_bubble_manager.h"
16#include "chrome/browser/ui/website_settings/permission_bubble_request.h"
17#include "chrome/common/pref_names.h"
18#include "chrome/grit/generated_resources.h"
19#include "chrome/grit/locale_settings.h"
20#include "components/infobars/core/confirm_infobar_delegate.h"
21#include "components/infobars/core/infobar.h"
22#include "content/public/browser/browser_thread.h"
23#include "content/public/browser/navigation_details.h"
24#include "content/public/browser/web_contents.h"
25#include "grit/theme_resources.h"
26#include "net/base/net_util.h"
27#include "storage/common/quota/quota_types.h"
28#include "ui/base/l10n/l10n_util.h"
29#include "url/gurl.h"
30
31namespace {
32
33// If the site requested larger quota than this threshold, show a different
34// message to the user.
35const int64 kRequestLargeQuotaThreshold = 5 * 1024 * 1024;
36
37// QuotaPermissionRequest ---------------------------------------------
38
39class QuotaPermissionRequest : public PermissionBubbleRequest {
40 public:
41  QuotaPermissionRequest(
42      ChromeQuotaPermissionContext* context,
43      const GURL& origin_url,
44      int64 requested_quota,
45      bool user_gesture,
46      const std::string& display_languages,
47      const content::QuotaPermissionContext::PermissionCallback& callback);
48
49  virtual ~QuotaPermissionRequest();
50
51  // PermissionBubbleRequest:
52  virtual int GetIconID() const OVERRIDE;
53  virtual base::string16 GetMessageText() const OVERRIDE;
54  virtual base::string16 GetMessageTextFragment() const OVERRIDE;
55  virtual bool HasUserGesture() const OVERRIDE;
56  virtual GURL GetRequestingHostname() const OVERRIDE;
57  virtual void PermissionGranted() OVERRIDE;
58  virtual void PermissionDenied() OVERRIDE;
59  virtual void Cancelled() OVERRIDE;
60  virtual void RequestFinished() OVERRIDE;
61
62 private:
63  scoped_refptr<ChromeQuotaPermissionContext> context_;
64  GURL origin_url_;
65  std::string display_languages_;
66  int64 requested_quota_;
67  bool user_gesture_;
68  content::QuotaPermissionContext::PermissionCallback callback_;
69
70  DISALLOW_COPY_AND_ASSIGN(QuotaPermissionRequest);
71};
72
73QuotaPermissionRequest::QuotaPermissionRequest(
74    ChromeQuotaPermissionContext* context,
75    const GURL& origin_url,
76    int64 requested_quota,
77    bool user_gesture,
78    const std::string& display_languages,
79    const content::QuotaPermissionContext::PermissionCallback& callback)
80    : context_(context),
81      origin_url_(origin_url),
82      display_languages_(display_languages),
83      requested_quota_(requested_quota),
84      user_gesture_(user_gesture),
85      callback_(callback) {}
86
87QuotaPermissionRequest::~QuotaPermissionRequest() {}
88
89int QuotaPermissionRequest::GetIconID() const {
90  // TODO(gbillock): get the proper image here
91  return IDR_INFOBAR_WARNING;
92}
93
94base::string16 QuotaPermissionRequest::GetMessageText() const {
95  return l10n_util::GetStringFUTF16(
96      (requested_quota_ > kRequestLargeQuotaThreshold ?
97          IDS_REQUEST_LARGE_QUOTA_INFOBAR_QUESTION :
98          IDS_REQUEST_QUOTA_INFOBAR_QUESTION),
99      net::FormatUrl(origin_url_, display_languages_,
100                     net::kFormatUrlOmitUsernamePassword |
101                     net::kFormatUrlOmitTrailingSlashOnBareHostname,
102                     net::UnescapeRule::SPACES, NULL, NULL, NULL)
103  );
104}
105
106base::string16 QuotaPermissionRequest::GetMessageTextFragment() const {
107  return l10n_util::GetStringUTF16(IDS_REQUEST_QUOTA_PERMISSION_FRAGMENT);
108}
109
110bool QuotaPermissionRequest::HasUserGesture() const {
111  return user_gesture_;
112}
113
114GURL QuotaPermissionRequest::GetRequestingHostname() const {
115  return origin_url_;
116}
117
118void QuotaPermissionRequest::PermissionGranted() {
119  context_->DispatchCallbackOnIOThread(
120      callback_,
121      content::QuotaPermissionContext::QUOTA_PERMISSION_RESPONSE_ALLOW);
122  callback_ = content::QuotaPermissionContext::PermissionCallback();
123}
124
125void QuotaPermissionRequest::PermissionDenied() {
126  context_->DispatchCallbackOnIOThread(
127      callback_,
128      content::QuotaPermissionContext::QUOTA_PERMISSION_RESPONSE_DISALLOW);
129  callback_ = content::QuotaPermissionContext::PermissionCallback();
130}
131
132void QuotaPermissionRequest::Cancelled() {
133}
134
135void QuotaPermissionRequest::RequestFinished() {
136  if (!callback_.is_null()) {
137    context_->DispatchCallbackOnIOThread(
138        callback_,
139        content::QuotaPermissionContext::QUOTA_PERMISSION_RESPONSE_CANCELLED);
140  }
141
142  delete this;
143}
144
145
146// RequestQuotaInfoBarDelegate ------------------------------------------------
147
148class RequestQuotaInfoBarDelegate : public ConfirmInfoBarDelegate {
149 public:
150  // Creates a request quota infobar and delegate and adds the infobar to
151  // |infobar_service|.
152  static void Create(
153      InfoBarService* infobar_service,
154      ChromeQuotaPermissionContext* context,
155      const GURL& origin_url,
156      int64 requested_quota,
157      const std::string& display_languages,
158      const content::QuotaPermissionContext::PermissionCallback& callback);
159
160 private:
161  RequestQuotaInfoBarDelegate(
162      ChromeQuotaPermissionContext* context,
163      const GURL& origin_url,
164      int64 requested_quota,
165      const std::string& display_languages,
166      const content::QuotaPermissionContext::PermissionCallback& callback);
167  virtual ~RequestQuotaInfoBarDelegate();
168
169  // ConfirmInfoBarDelegate:
170  virtual base::string16 GetMessageText() const OVERRIDE;
171  virtual bool Accept() OVERRIDE;
172  virtual bool Cancel() OVERRIDE;
173
174  scoped_refptr<ChromeQuotaPermissionContext> context_;
175  GURL origin_url_;
176  std::string display_languages_;
177  int64 requested_quota_;
178  content::QuotaPermissionContext::PermissionCallback callback_;
179
180  DISALLOW_COPY_AND_ASSIGN(RequestQuotaInfoBarDelegate);
181};
182
183// static
184void RequestQuotaInfoBarDelegate::Create(
185    InfoBarService* infobar_service,
186    ChromeQuotaPermissionContext* context,
187    const GURL& origin_url,
188    int64 requested_quota,
189    const std::string& display_languages,
190    const content::QuotaPermissionContext::PermissionCallback& callback) {
191  infobar_service->AddInfoBar(ConfirmInfoBarDelegate::CreateInfoBar(
192      scoped_ptr<ConfirmInfoBarDelegate>(new RequestQuotaInfoBarDelegate(
193          context, origin_url, requested_quota, display_languages, callback))));
194}
195
196RequestQuotaInfoBarDelegate::RequestQuotaInfoBarDelegate(
197    ChromeQuotaPermissionContext* context,
198    const GURL& origin_url,
199    int64 requested_quota,
200    const std::string& display_languages,
201    const content::QuotaPermissionContext::PermissionCallback& callback)
202    : ConfirmInfoBarDelegate(),
203      context_(context),
204      origin_url_(origin_url),
205      display_languages_(display_languages),
206      requested_quota_(requested_quota),
207      callback_(callback) {
208}
209
210RequestQuotaInfoBarDelegate::~RequestQuotaInfoBarDelegate() {
211  if (!callback_.is_null()) {
212    context_->DispatchCallbackOnIOThread(
213        callback_,
214        content::QuotaPermissionContext::QUOTA_PERMISSION_RESPONSE_CANCELLED);
215  }
216}
217
218base::string16 RequestQuotaInfoBarDelegate::GetMessageText() const {
219  // If the site requested larger quota than this threshold, show a different
220  // message to the user.
221  return l10n_util::GetStringFUTF16(
222      (requested_quota_ > kRequestLargeQuotaThreshold ?
223          IDS_REQUEST_LARGE_QUOTA_INFOBAR_QUESTION :
224          IDS_REQUEST_QUOTA_INFOBAR_QUESTION),
225      net::FormatUrl(origin_url_, display_languages_,
226                     net::kFormatUrlOmitUsernamePassword |
227                     net::kFormatUrlOmitTrailingSlashOnBareHostname,
228                     net::UnescapeRule::SPACES, NULL, NULL, NULL)
229      );
230}
231
232bool RequestQuotaInfoBarDelegate::Accept() {
233  context_->DispatchCallbackOnIOThread(
234      callback_,
235      content::QuotaPermissionContext::QUOTA_PERMISSION_RESPONSE_ALLOW);
236  return true;
237}
238
239bool RequestQuotaInfoBarDelegate::Cancel() {
240  context_->DispatchCallbackOnIOThread(
241      callback_,
242      content::QuotaPermissionContext::QUOTA_PERMISSION_RESPONSE_CANCELLED);
243  return true;
244}
245
246}  // namespace
247
248
249// ChromeQuotaPermissionContext -----------------------------------------------
250
251ChromeQuotaPermissionContext::ChromeQuotaPermissionContext() {
252}
253
254void ChromeQuotaPermissionContext::RequestQuotaPermission(
255    const content::StorageQuotaParams& params,
256    int render_process_id,
257    const PermissionCallback& callback) {
258  if (params.storage_type != storage::kStorageTypePersistent) {
259    // For now we only support requesting quota with this interface
260    // for Persistent storage type.
261    callback.Run(QUOTA_PERMISSION_RESPONSE_DISALLOW);
262    return;
263  }
264
265  if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
266    content::BrowserThread::PostTask(
267        content::BrowserThread::UI, FROM_HERE,
268        base::Bind(&ChromeQuotaPermissionContext::RequestQuotaPermission, this,
269                   params, render_process_id, callback));
270    return;
271  }
272
273  content::WebContents* web_contents =
274      tab_util::GetWebContentsByID(render_process_id,
275                                   params.render_view_id);
276  if (!web_contents) {
277    // The tab may have gone away or the request may not be from a tab.
278    LOG(WARNING) << "Attempt to request quota tabless renderer: "
279                 << render_process_id << "," << params.render_view_id;
280    DispatchCallbackOnIOThread(callback, QUOTA_PERMISSION_RESPONSE_CANCELLED);
281    return;
282  }
283
284  if (PermissionBubbleManager::Enabled()) {
285    PermissionBubbleManager* bubble_manager =
286        PermissionBubbleManager::FromWebContents(web_contents);
287    if (bubble_manager) {
288      bubble_manager->AddRequest(new QuotaPermissionRequest(this,
289              params.origin_url, params.requested_size, params.user_gesture,
290              Profile::FromBrowserContext(web_contents->GetBrowserContext())->
291                  GetPrefs()->GetString(prefs::kAcceptLanguages),
292              callback));
293    }
294    return;
295  }
296
297  InfoBarService* infobar_service =
298      InfoBarService::FromWebContents(web_contents);
299  if (!infobar_service) {
300    // The tab has no infobar service.
301    LOG(WARNING) << "Attempt to request quota from a background page: "
302                 << render_process_id << "," << params.render_view_id;
303    DispatchCallbackOnIOThread(callback, QUOTA_PERMISSION_RESPONSE_CANCELLED);
304    return;
305  }
306  RequestQuotaInfoBarDelegate::Create(
307      infobar_service, this, params.origin_url, params.requested_size,
308      Profile::FromBrowserContext(web_contents->GetBrowserContext())->
309          GetPrefs()->GetString(prefs::kAcceptLanguages),
310      callback);
311}
312
313void ChromeQuotaPermissionContext::DispatchCallbackOnIOThread(
314    const PermissionCallback& callback,
315    QuotaPermissionResponse response) {
316  DCHECK_EQ(false, callback.is_null());
317
318  if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)) {
319    content::BrowserThread::PostTask(
320        content::BrowserThread::IO, FROM_HERE,
321        base::Bind(&ChromeQuotaPermissionContext::DispatchCallbackOnIOThread,
322                   this, callback, response));
323    return;
324  }
325
326  callback.Run(response);
327}
328
329ChromeQuotaPermissionContext::~ChromeQuotaPermissionContext() {}
330