• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
26 using content::BrowserThread;
27 
28 namespace extension_browsing_data_api_constants {
29 
30 // Parameter name keys.
31 const char kDataRemovalPermittedKey[] = "dataRemovalPermitted";
32 const char kDataToRemoveKey[] = "dataToRemove";
33 const char kOptionsKey[] = "options";
34 
35 // Type keys.
36 const char kAppCacheKey[] = "appcache";
37 const char kCacheKey[] = "cache";
38 const char kChannelIDsKey[] = "serverBoundCertificates";
39 const char kCookiesKey[] = "cookies";
40 const char kDownloadsKey[] = "downloads";
41 const char kFileSystemsKey[] = "fileSystems";
42 const char kFormDataKey[] = "formData";
43 const char kHistoryKey[] = "history";
44 const char kIndexedDBKey[] = "indexedDB";
45 const char kLocalStorageKey[] = "localStorage";
46 const char kPasswordsKey[] = "passwords";
47 const char kPluginDataKey[] = "pluginData";
48 const char kServiceWorkersKey[] = "serviceWorkers";
49 const char kWebSQLKey[] = "webSQL";
50 
51 // Option keys.
52 const char kExtensionsKey[] = "extension";
53 const char kOriginTypesKey[] = "originTypes";
54 const char kProtectedWebKey[] = "protectedWeb";
55 const char kSinceKey[] = "since";
56 const char kUnprotectedWebKey[] = "unprotectedWeb";
57 
58 // Errors!
59 // The placeholder will be filled by the name of the affected data type (e.g.,
60 // "history").
61 const char kBadDataTypeDetails[] = "Invalid value for data type '%s'.";
62 const char kDeleteProhibitedError[] = "Browsing history and downloads are not "
63                                       "permitted to be removed.";
64 const char kOneAtATimeError[] = "Only one 'browsingData' API call can run at "
65                                 "a time.";
66 
67 }  // namespace extension_browsing_data_api_constants
68 
69 namespace {
MaskForKey(const char * key)70 int 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.
IsRemovalPermitted(int removal_mask,PrefService * prefs)107 bool 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 
RunSync()119 bool 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 
SetDetails(base::DictionaryValue * selected_dict,base::DictionaryValue * permitted_dict,const char * data_type,bool is_selected)215 void 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 
OnBrowsingDataRemoverDone()226 void BrowsingDataRemoverFunction::OnBrowsingDataRemoverDone() {
227   DCHECK_CURRENTLY_ON(BrowserThread::UI);
228   this->SendResponse(true);
229 
230   Release();  // Balanced in RunAsync.
231 }
232 
RunAsync()233 bool 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 
CheckRemovingPluginDataSupported(scoped_refptr<PluginPrefs> plugin_prefs)286 void 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 
StartRemoving()296 void 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 
ParseOriginSetMask(const base::DictionaryValue & options)316 int 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.
GetRemovalMask()360 int 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 
GetRemovalMask()384 int BrowsingDataRemoveAppcacheFunction::GetRemovalMask() {
385   return BrowsingDataRemover::REMOVE_APPCACHE;
386 }
387 
GetRemovalMask()388 int BrowsingDataRemoveCacheFunction::GetRemovalMask() {
389   return BrowsingDataRemover::REMOVE_CACHE;
390 }
391 
GetRemovalMask()392 int BrowsingDataRemoveCookiesFunction::GetRemovalMask() {
393   return BrowsingDataRemover::REMOVE_COOKIES |
394          BrowsingDataRemover::REMOVE_CHANNEL_IDS;
395 }
396 
GetRemovalMask()397 int BrowsingDataRemoveDownloadsFunction::GetRemovalMask() {
398   return BrowsingDataRemover::REMOVE_DOWNLOADS;
399 }
400 
GetRemovalMask()401 int BrowsingDataRemoveFileSystemsFunction::GetRemovalMask() {
402   return BrowsingDataRemover::REMOVE_FILE_SYSTEMS;
403 }
404 
GetRemovalMask()405 int BrowsingDataRemoveFormDataFunction::GetRemovalMask() {
406   return BrowsingDataRemover::REMOVE_FORM_DATA;
407 }
408 
GetRemovalMask()409 int BrowsingDataRemoveHistoryFunction::GetRemovalMask() {
410   return BrowsingDataRemover::REMOVE_HISTORY;
411 }
412 
GetRemovalMask()413 int BrowsingDataRemoveIndexedDBFunction::GetRemovalMask() {
414   return BrowsingDataRemover::REMOVE_INDEXEDDB;
415 }
416 
GetRemovalMask()417 int BrowsingDataRemoveLocalStorageFunction::GetRemovalMask() {
418   return BrowsingDataRemover::REMOVE_LOCAL_STORAGE;
419 }
420 
GetRemovalMask()421 int BrowsingDataRemovePluginDataFunction::GetRemovalMask() {
422   return BrowsingDataRemover::REMOVE_PLUGIN_DATA;
423 }
424 
GetRemovalMask()425 int BrowsingDataRemovePasswordsFunction::GetRemovalMask() {
426   return BrowsingDataRemover::REMOVE_PASSWORDS;
427 }
428 
GetRemovalMask()429 int BrowsingDataRemoveServiceWorkersFunction::GetRemovalMask() {
430   return BrowsingDataRemover::REMOVE_SERVICE_WORKERS;
431 }
432 
GetRemovalMask()433 int BrowsingDataRemoveWebSQLFunction::GetRemovalMask() {
434   return BrowsingDataRemover::REMOVE_WEBSQL;
435 }
436