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