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