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 { 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. 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 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 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 226 void BrowsingDataRemoverFunction::OnBrowsingDataRemoverDone() { 227 DCHECK_CURRENTLY_ON(BrowserThread::UI); 228 this->SendResponse(true); 229 230 Release(); // Balanced in RunAsync. 231 } 232 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 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 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 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. 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 384 int BrowsingDataRemoveAppcacheFunction::GetRemovalMask() { 385 return BrowsingDataRemover::REMOVE_APPCACHE; 386 } 387 388 int BrowsingDataRemoveCacheFunction::GetRemovalMask() { 389 return BrowsingDataRemover::REMOVE_CACHE; 390 } 391 392 int BrowsingDataRemoveCookiesFunction::GetRemovalMask() { 393 return BrowsingDataRemover::REMOVE_COOKIES | 394 BrowsingDataRemover::REMOVE_CHANNEL_IDS; 395 } 396 397 int BrowsingDataRemoveDownloadsFunction::GetRemovalMask() { 398 return BrowsingDataRemover::REMOVE_DOWNLOADS; 399 } 400 401 int BrowsingDataRemoveFileSystemsFunction::GetRemovalMask() { 402 return BrowsingDataRemover::REMOVE_FILE_SYSTEMS; 403 } 404 405 int BrowsingDataRemoveFormDataFunction::GetRemovalMask() { 406 return BrowsingDataRemover::REMOVE_FORM_DATA; 407 } 408 409 int BrowsingDataRemoveHistoryFunction::GetRemovalMask() { 410 return BrowsingDataRemover::REMOVE_HISTORY; 411 } 412 413 int BrowsingDataRemoveIndexedDBFunction::GetRemovalMask() { 414 return BrowsingDataRemover::REMOVE_INDEXEDDB; 415 } 416 417 int BrowsingDataRemoveLocalStorageFunction::GetRemovalMask() { 418 return BrowsingDataRemover::REMOVE_LOCAL_STORAGE; 419 } 420 421 int BrowsingDataRemovePluginDataFunction::GetRemovalMask() { 422 return BrowsingDataRemover::REMOVE_PLUGIN_DATA; 423 } 424 425 int BrowsingDataRemovePasswordsFunction::GetRemovalMask() { 426 return BrowsingDataRemover::REMOVE_PASSWORDS; 427 } 428 429 int BrowsingDataRemoveServiceWorkersFunction::GetRemovalMask() { 430 return BrowsingDataRemover::REMOVE_SERVICE_WORKERS; 431 } 432 433 int BrowsingDataRemoveWebSQLFunction::GetRemovalMask() { 434 return BrowsingDataRemover::REMOVE_WEBSQL; 435 } 436