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 { 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. 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 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 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 219 void BrowsingDataRemoverFunction::OnBrowsingDataRemoverDone() { 220 DCHECK_CURRENTLY_ON(BrowserThread::UI); 221 this->SendResponse(true); 222 223 Release(); // Balanced in RunAsync. 224 } 225 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 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 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 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. 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 377 int BrowsingDataRemoveAppcacheFunction::GetRemovalMask() { 378 return BrowsingDataRemover::REMOVE_APPCACHE; 379 } 380 381 int BrowsingDataRemoveCacheFunction::GetRemovalMask() { 382 return BrowsingDataRemover::REMOVE_CACHE; 383 } 384 385 int BrowsingDataRemoveCookiesFunction::GetRemovalMask() { 386 return BrowsingDataRemover::REMOVE_COOKIES | 387 BrowsingDataRemover::REMOVE_SERVER_BOUND_CERTS; 388 } 389 390 int BrowsingDataRemoveDownloadsFunction::GetRemovalMask() { 391 return BrowsingDataRemover::REMOVE_DOWNLOADS; 392 } 393 394 int BrowsingDataRemoveFileSystemsFunction::GetRemovalMask() { 395 return BrowsingDataRemover::REMOVE_FILE_SYSTEMS; 396 } 397 398 int BrowsingDataRemoveFormDataFunction::GetRemovalMask() { 399 return BrowsingDataRemover::REMOVE_FORM_DATA; 400 } 401 402 int BrowsingDataRemoveHistoryFunction::GetRemovalMask() { 403 return BrowsingDataRemover::REMOVE_HISTORY; 404 } 405 406 int BrowsingDataRemoveIndexedDBFunction::GetRemovalMask() { 407 return BrowsingDataRemover::REMOVE_INDEXEDDB; 408 } 409 410 int BrowsingDataRemoveLocalStorageFunction::GetRemovalMask() { 411 return BrowsingDataRemover::REMOVE_LOCAL_STORAGE; 412 } 413 414 int BrowsingDataRemovePluginDataFunction::GetRemovalMask() { 415 return BrowsingDataRemover::REMOVE_PLUGIN_DATA; 416 } 417 418 int BrowsingDataRemovePasswordsFunction::GetRemovalMask() { 419 return BrowsingDataRemover::REMOVE_PASSWORDS; 420 } 421 422 int BrowsingDataRemoveWebSQLFunction::GetRemovalMask() { 423 return BrowsingDataRemover::REMOVE_WEBSQL; 424 } 425