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