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// Defines the Chrome Extensions BrowsingData API functions, which entail
6// clearing browsing data, and clearing the browser's cache (which, let's be
7// honest, are the same thing), as specified in the extension API JSON.
8
9#include "chrome/browser/extensions/api/browsing_data/browsing_data_api.h"
10
11#include <string>
12
13#include "base/strings/stringprintf.h"
14#include "base/values.h"
15#include "chrome/browser/browsing_data/browsing_data_helper.h"
16#include "chrome/browser/browsing_data/browsing_data_remover.h"
17#include "chrome/browser/plugins/plugin_data_remover_helper.h"
18#include "chrome/browser/plugins/plugin_prefs.h"
19#include "chrome/browser/profiles/profile.h"
20#include "chrome/browser/ui/browser.h"
21#include "chrome/common/pref_names.h"
22#include "content/public/browser/browser_thread.h"
23#include "extensions/common/error_utils.h"
24#include "extensions/common/extension.h"
25
26using content::BrowserThread;
27
28namespace extension_browsing_data_api_constants {
29
30// Parameter name keys.
31const char kDataRemovalPermittedKey[] = "dataRemovalPermitted";
32const char kDataToRemoveKey[] = "dataToRemove";
33const char kOptionsKey[] = "options";
34
35// Type keys.
36const char kAppCacheKey[] = "appcache";
37const char kCacheKey[] = "cache";
38const char kChannelIDsKey[] = "serverBoundCertificates";
39const char kCookiesKey[] = "cookies";
40const char kDownloadsKey[] = "downloads";
41const char kFileSystemsKey[] = "fileSystems";
42const char kFormDataKey[] = "formData";
43const char kHistoryKey[] = "history";
44const char kIndexedDBKey[] = "indexedDB";
45const char kLocalStorageKey[] = "localStorage";
46const char kPasswordsKey[] = "passwords";
47const char kPluginDataKey[] = "pluginData";
48const char kServiceWorkersKey[] = "serviceWorkers";
49const char kWebSQLKey[] = "webSQL";
50
51// Option keys.
52const char kExtensionsKey[] = "extension";
53const char kOriginTypesKey[] = "originTypes";
54const char kProtectedWebKey[] = "protectedWeb";
55const char kSinceKey[] = "since";
56const char kUnprotectedWebKey[] = "unprotectedWeb";
57
58// Errors!
59// The placeholder will be filled by the name of the affected data type (e.g.,
60// "history").
61const char kBadDataTypeDetails[] = "Invalid value for data type '%s'.";
62const char kDeleteProhibitedError[] = "Browsing history and downloads are not "
63                                      "permitted to be removed.";
64const char kOneAtATimeError[] = "Only one 'browsingData' API call can run at "
65                                "a time.";
66
67}  // namespace extension_browsing_data_api_constants
68
69namespace {
70int MaskForKey(const char* key) {
71  if (strcmp(key, extension_browsing_data_api_constants::kAppCacheKey) == 0)
72    return BrowsingDataRemover::REMOVE_APPCACHE;
73  if (strcmp(key, extension_browsing_data_api_constants::kCacheKey) == 0)
74    return BrowsingDataRemover::REMOVE_CACHE;
75  if (strcmp(key, extension_browsing_data_api_constants::kCookiesKey) == 0)
76    return BrowsingDataRemover::REMOVE_COOKIES;
77  if (strcmp(key, extension_browsing_data_api_constants::kDownloadsKey) == 0)
78    return BrowsingDataRemover::REMOVE_DOWNLOADS;
79  if (strcmp(key, extension_browsing_data_api_constants::kFileSystemsKey) == 0)
80    return BrowsingDataRemover::REMOVE_FILE_SYSTEMS;
81  if (strcmp(key, extension_browsing_data_api_constants::kFormDataKey) == 0)
82    return BrowsingDataRemover::REMOVE_FORM_DATA;
83  if (strcmp(key, extension_browsing_data_api_constants::kHistoryKey) == 0)
84    return BrowsingDataRemover::REMOVE_HISTORY;
85  if (strcmp(key, extension_browsing_data_api_constants::kIndexedDBKey) == 0)
86    return BrowsingDataRemover::REMOVE_INDEXEDDB;
87  if (strcmp(key, extension_browsing_data_api_constants::kLocalStorageKey) == 0)
88    return BrowsingDataRemover::REMOVE_LOCAL_STORAGE;
89  if (strcmp(key,
90             extension_browsing_data_api_constants::kChannelIDsKey) == 0)
91    return BrowsingDataRemover::REMOVE_CHANNEL_IDS;
92  if (strcmp(key, extension_browsing_data_api_constants::kPasswordsKey) == 0)
93    return BrowsingDataRemover::REMOVE_PASSWORDS;
94  if (strcmp(key, extension_browsing_data_api_constants::kPluginDataKey) == 0)
95    return BrowsingDataRemover::REMOVE_PLUGIN_DATA;
96  if (strcmp(key, extension_browsing_data_api_constants::kServiceWorkersKey) ==
97      0)
98    return BrowsingDataRemover::REMOVE_SERVICE_WORKERS;
99  if (strcmp(key, extension_browsing_data_api_constants::kWebSQLKey) == 0)
100    return BrowsingDataRemover::REMOVE_WEBSQL;
101
102  return 0;
103}
104
105// Returns false if any of the selected data types are not allowed to be
106// deleted.
107bool IsRemovalPermitted(int removal_mask, PrefService* prefs) {
108  // Enterprise policy or user preference might prohibit deleting browser or
109  // download history.
110  if ((removal_mask & BrowsingDataRemover::REMOVE_HISTORY) ||
111      (removal_mask & BrowsingDataRemover::REMOVE_DOWNLOADS)) {
112    return prefs->GetBoolean(prefs::kAllowDeletingBrowserHistory);
113  }
114  return true;
115}
116
117}  // namespace
118
119bool BrowsingDataSettingsFunction::RunSync() {
120  PrefService* prefs = GetProfile()->GetPrefs();
121
122  // Fill origin types.
123  // The "cookies" and "hosted apps" UI checkboxes both map to
124  // REMOVE_SITE_DATA in browsing_data_remover.h, the former for the unprotected
125  // web, the latter for  protected web data. There is no UI control for
126  // extension data.
127  scoped_ptr<base::DictionaryValue> origin_types(new base::DictionaryValue);
128  origin_types->SetBoolean(
129      extension_browsing_data_api_constants::kUnprotectedWebKey,
130      prefs->GetBoolean(prefs::kDeleteCookies));
131  origin_types->SetBoolean(
132      extension_browsing_data_api_constants::kProtectedWebKey,
133      prefs->GetBoolean(prefs::kDeleteHostedAppsData));
134  origin_types->SetBoolean(
135      extension_browsing_data_api_constants::kExtensionsKey, false);
136
137  // Fill deletion time period.
138  int period_pref = prefs->GetInteger(prefs::kDeleteTimePeriod);
139  BrowsingDataRemover::TimePeriod period =
140      static_cast<BrowsingDataRemover::TimePeriod>(period_pref);
141  double since = 0;
142  if (period != BrowsingDataRemover::EVERYTHING) {
143    base::Time time = BrowsingDataRemover::CalculateBeginDeleteTime(period);
144    since = time.ToJsTime();
145  }
146
147  scoped_ptr<base::DictionaryValue> options(new base::DictionaryValue);
148  options->Set(extension_browsing_data_api_constants::kOriginTypesKey,
149               origin_types.release());
150  options->SetDouble(extension_browsing_data_api_constants::kSinceKey, since);
151
152  // Fill dataToRemove and dataRemovalPermitted.
153  scoped_ptr<base::DictionaryValue> selected(new base::DictionaryValue);
154  scoped_ptr<base::DictionaryValue> permitted(new base::DictionaryValue);
155
156  bool delete_site_data = prefs->GetBoolean(prefs::kDeleteCookies) ||
157                          prefs->GetBoolean(prefs::kDeleteHostedAppsData);
158
159  SetDetails(selected.get(), permitted.get(),
160             extension_browsing_data_api_constants::kAppCacheKey,
161             delete_site_data);
162  SetDetails(selected.get(), permitted.get(),
163             extension_browsing_data_api_constants::kCookiesKey,
164             delete_site_data);
165  SetDetails(selected.get(), permitted.get(),
166             extension_browsing_data_api_constants::kFileSystemsKey,
167             delete_site_data);
168  SetDetails(selected.get(), permitted.get(),
169             extension_browsing_data_api_constants::kIndexedDBKey,
170             delete_site_data);
171  SetDetails(selected.get(), permitted.get(),
172      extension_browsing_data_api_constants::kLocalStorageKey,
173      delete_site_data);
174  SetDetails(selected.get(), permitted.get(),
175             extension_browsing_data_api_constants::kWebSQLKey,
176             delete_site_data);
177  SetDetails(selected.get(), permitted.get(),
178      extension_browsing_data_api_constants::kChannelIDsKey,
179      delete_site_data);
180  SetDetails(selected.get(), permitted.get(),
181             extension_browsing_data_api_constants::kServiceWorkersKey,
182             delete_site_data);
183
184  SetDetails(selected.get(), permitted.get(),
185      extension_browsing_data_api_constants::kPluginDataKey,
186      delete_site_data && prefs->GetBoolean(prefs::kClearPluginLSODataEnabled));
187
188  SetDetails(selected.get(), permitted.get(),
189             extension_browsing_data_api_constants::kHistoryKey,
190             prefs->GetBoolean(prefs::kDeleteBrowsingHistory));
191  SetDetails(selected.get(), permitted.get(),
192             extension_browsing_data_api_constants::kDownloadsKey,
193             prefs->GetBoolean(prefs::kDeleteDownloadHistory));
194  SetDetails(selected.get(), permitted.get(),
195             extension_browsing_data_api_constants::kCacheKey,
196             prefs->GetBoolean(prefs::kDeleteCache));
197  SetDetails(selected.get(), permitted.get(),
198             extension_browsing_data_api_constants::kFormDataKey,
199             prefs->GetBoolean(prefs::kDeleteFormData));
200  SetDetails(selected.get(), permitted.get(),
201             extension_browsing_data_api_constants::kPasswordsKey,
202             prefs->GetBoolean(prefs::kDeletePasswords));
203
204  scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue);
205  result->Set(extension_browsing_data_api_constants::kOptionsKey,
206              options.release());
207  result->Set(extension_browsing_data_api_constants::kDataToRemoveKey,
208              selected.release());
209  result->Set(extension_browsing_data_api_constants::kDataRemovalPermittedKey,
210              permitted.release());
211  SetResult(result.release());
212  return true;
213}
214
215void BrowsingDataSettingsFunction::SetDetails(
216    base::DictionaryValue* selected_dict,
217    base::DictionaryValue* permitted_dict,
218    const char* data_type,
219    bool is_selected) {
220  bool is_permitted =
221      IsRemovalPermitted(MaskForKey(data_type), GetProfile()->GetPrefs());
222  selected_dict->SetBoolean(data_type, is_selected && is_permitted);
223  permitted_dict->SetBoolean(data_type, is_permitted);
224}
225
226void BrowsingDataRemoverFunction::OnBrowsingDataRemoverDone() {
227  DCHECK_CURRENTLY_ON(BrowserThread::UI);
228  this->SendResponse(true);
229
230  Release();  // Balanced in RunAsync.
231}
232
233bool BrowsingDataRemoverFunction::RunAsync() {
234  // If we don't have a profile, something's pretty wrong.
235  DCHECK(GetProfile());
236
237  // Grab the initial |options| parameter, and parse out the arguments.
238  base::DictionaryValue* options;
239  EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &options));
240  DCHECK(options);
241
242  origin_set_mask_ = ParseOriginSetMask(*options);
243
244  // If |ms_since_epoch| isn't set, default it to 0.
245  double ms_since_epoch;
246  if (!options->GetDouble(extension_browsing_data_api_constants::kSinceKey,
247                          &ms_since_epoch))
248    ms_since_epoch = 0;
249
250  // base::Time takes a double that represents seconds since epoch. JavaScript
251  // gives developers milliseconds, so do a quick conversion before populating
252  // the object. Also, Time::FromDoubleT converts double time 0 to empty Time
253  // object. So we need to do special handling here.
254  remove_since_ = (ms_since_epoch == 0) ?
255      base::Time::UnixEpoch() :
256      base::Time::FromDoubleT(ms_since_epoch / 1000.0);
257
258  removal_mask_ = GetRemovalMask();
259  if (bad_message_)
260    return false;
261
262  // Check for prohibited data types.
263  if (!IsRemovalPermitted(removal_mask_, GetProfile()->GetPrefs())) {
264    error_ = extension_browsing_data_api_constants::kDeleteProhibitedError;
265    return false;
266  }
267
268  if (removal_mask_ & BrowsingDataRemover::REMOVE_PLUGIN_DATA) {
269    // If we're being asked to remove plugin data, check whether it's actually
270    // supported.
271    BrowserThread::PostTask(
272        BrowserThread::FILE,
273        FROM_HERE,
274        base::Bind(
275            &BrowsingDataRemoverFunction::CheckRemovingPluginDataSupported,
276            this,
277            PluginPrefs::GetForProfile(GetProfile())));
278  } else {
279    StartRemoving();
280  }
281
282  // Will finish asynchronously.
283  return true;
284}
285
286void BrowsingDataRemoverFunction::CheckRemovingPluginDataSupported(
287    scoped_refptr<PluginPrefs> plugin_prefs) {
288  if (!PluginDataRemoverHelper::IsSupported(plugin_prefs.get()))
289    removal_mask_ &= ~BrowsingDataRemover::REMOVE_PLUGIN_DATA;
290
291  BrowserThread::PostTask(
292      BrowserThread::UI, FROM_HERE,
293      base::Bind(&BrowsingDataRemoverFunction::StartRemoving, this));
294}
295
296void BrowsingDataRemoverFunction::StartRemoving() {
297  if (BrowsingDataRemover::is_removing()) {
298    error_ = extension_browsing_data_api_constants::kOneAtATimeError;
299    SendResponse(false);
300    return;
301  }
302
303  // If we're good to go, add a ref (Balanced in OnBrowsingDataRemoverDone)
304  AddRef();
305
306  // Create a BrowsingDataRemover, set the current object as an observer (so
307  // that we're notified after removal) and call remove() with the arguments
308  // we've generated above. We can use a raw pointer here, as the browsing data
309  // remover is responsible for deleting itself once data removal is complete.
310  BrowsingDataRemover* remover = BrowsingDataRemover::CreateForRange(
311      GetProfile(), remove_since_, base::Time::Max());
312  remover->AddObserver(this);
313  remover->Remove(removal_mask_, origin_set_mask_);
314}
315
316int BrowsingDataRemoverFunction::ParseOriginSetMask(
317    const base::DictionaryValue& options) {
318  // Parse the |options| dictionary to generate the origin set mask. Default to
319  // UNPROTECTED_WEB if the developer doesn't specify anything.
320  int mask = BrowsingDataHelper::UNPROTECTED_WEB;
321
322  const base::DictionaryValue* d = NULL;
323  if (options.HasKey(extension_browsing_data_api_constants::kOriginTypesKey)) {
324    EXTENSION_FUNCTION_VALIDATE(options.GetDictionary(
325        extension_browsing_data_api_constants::kOriginTypesKey, &d));
326    bool value;
327
328    // The developer specified something! Reset to 0 and parse the dictionary.
329    mask = 0;
330
331    // Unprotected web.
332    if (d->HasKey(extension_browsing_data_api_constants::kUnprotectedWebKey)) {
333      EXTENSION_FUNCTION_VALIDATE(d->GetBoolean(
334          extension_browsing_data_api_constants::kUnprotectedWebKey, &value));
335      mask |= value ? BrowsingDataHelper::UNPROTECTED_WEB : 0;
336    }
337
338    // Protected web.
339    if (d->HasKey(extension_browsing_data_api_constants::kProtectedWebKey)) {
340      EXTENSION_FUNCTION_VALIDATE(d->GetBoolean(
341          extension_browsing_data_api_constants::kProtectedWebKey, &value));
342      mask |= value ? BrowsingDataHelper::PROTECTED_WEB : 0;
343    }
344
345    // Extensions.
346    if (d->HasKey(extension_browsing_data_api_constants::kExtensionsKey)) {
347      EXTENSION_FUNCTION_VALIDATE(d->GetBoolean(
348          extension_browsing_data_api_constants::kExtensionsKey, &value));
349      mask |= value ? BrowsingDataHelper::EXTENSION : 0;
350    }
351  }
352
353  return mask;
354}
355
356// Parses the |dataToRemove| argument to generate the removal mask. Sets
357// |bad_message_| (like EXTENSION_FUNCTION_VALIDATE would if this were a bool
358// method) if 'dataToRemove' is not present or any data-type keys don't have
359// supported (boolean) values.
360int BrowsingDataRemoveFunction::GetRemovalMask() {
361  base::DictionaryValue* data_to_remove;
362  if (!args_->GetDictionary(1, &data_to_remove)) {
363    bad_message_ = true;
364    return 0;
365  }
366
367  int removal_mask = 0;
368
369  for (base::DictionaryValue::Iterator i(*data_to_remove);
370       !i.IsAtEnd();
371       i.Advance()) {
372    bool selected = false;
373    if (!i.value().GetAsBoolean(&selected)) {
374      bad_message_ = true;
375      return 0;
376    }
377    if (selected)
378      removal_mask |= MaskForKey(i.key().c_str());
379  }
380
381  return removal_mask;
382}
383
384int BrowsingDataRemoveAppcacheFunction::GetRemovalMask() {
385  return BrowsingDataRemover::REMOVE_APPCACHE;
386}
387
388int BrowsingDataRemoveCacheFunction::GetRemovalMask() {
389  return BrowsingDataRemover::REMOVE_CACHE;
390}
391
392int BrowsingDataRemoveCookiesFunction::GetRemovalMask() {
393  return BrowsingDataRemover::REMOVE_COOKIES |
394         BrowsingDataRemover::REMOVE_CHANNEL_IDS;
395}
396
397int BrowsingDataRemoveDownloadsFunction::GetRemovalMask() {
398  return BrowsingDataRemover::REMOVE_DOWNLOADS;
399}
400
401int BrowsingDataRemoveFileSystemsFunction::GetRemovalMask() {
402  return BrowsingDataRemover::REMOVE_FILE_SYSTEMS;
403}
404
405int BrowsingDataRemoveFormDataFunction::GetRemovalMask() {
406  return BrowsingDataRemover::REMOVE_FORM_DATA;
407}
408
409int BrowsingDataRemoveHistoryFunction::GetRemovalMask() {
410  return BrowsingDataRemover::REMOVE_HISTORY;
411}
412
413int BrowsingDataRemoveIndexedDBFunction::GetRemovalMask() {
414  return BrowsingDataRemover::REMOVE_INDEXEDDB;
415}
416
417int BrowsingDataRemoveLocalStorageFunction::GetRemovalMask() {
418  return BrowsingDataRemover::REMOVE_LOCAL_STORAGE;
419}
420
421int BrowsingDataRemovePluginDataFunction::GetRemovalMask() {
422  return BrowsingDataRemover::REMOVE_PLUGIN_DATA;
423}
424
425int BrowsingDataRemovePasswordsFunction::GetRemovalMask() {
426  return BrowsingDataRemover::REMOVE_PASSWORDS;
427}
428
429int BrowsingDataRemoveServiceWorkersFunction::GetRemovalMask() {
430  return BrowsingDataRemover::REMOVE_SERVICE_WORKERS;
431}
432
433int BrowsingDataRemoveWebSQLFunction::GetRemovalMask() {
434  return BrowsingDataRemover::REMOVE_WEBSQL;
435}
436