cookies_tree_model.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
1// Copyright (c) 2010 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#include "chrome/browser/cookies_tree_model.h" 6 7#include <algorithm> 8#include <functional> 9#include <vector> 10 11#include "app/l10n_util.h" 12#include "app/resource_bundle.h" 13#include "app/table_model_observer.h" 14#include "app/tree_node_model.h" 15#include "base/callback.h" 16#include "base/i18n/rtl.h" 17#include "base/linked_ptr.h" 18#include "base/string_util.h" 19#include "chrome/browser/extensions/extensions_service.h" 20#include "chrome/browser/in_process_webkit/webkit_context.h" 21#include "grit/app_resources.h" 22#include "grit/generated_resources.h" 23#include "grit/theme_resources.h" 24#include "net/base/cookie_monster.h" 25#include "net/base/registry_controlled_domain.h" 26#include "net/url_request/url_request_context.h" 27#include "third_party/skia/include/core/SkBitmap.h" 28 29static const char kFileOriginNodeName[] = "file://"; 30 31/////////////////////////////////////////////////////////////////////////////// 32// CookieTreeNode, public: 33 34void CookieTreeNode::DeleteStoredObjects() { 35 std::for_each(children().begin(), 36 children().end(), 37 std::mem_fun(&CookieTreeNode::DeleteStoredObjects)); 38} 39 40CookiesTreeModel* CookieTreeNode::GetModel() const { 41 if (GetParent()) 42 return GetParent()->GetModel(); 43 else 44 return NULL; 45} 46 47/////////////////////////////////////////////////////////////////////////////// 48// CookieTreeCookieNode, public: 49 50CookieTreeCookieNode::CookieTreeCookieNode( 51 net::CookieMonster::CanonicalCookie* cookie) 52 : CookieTreeNode(UTF8ToWide(cookie->Name())), 53 cookie_(cookie) { 54} 55 56void CookieTreeCookieNode::DeleteStoredObjects() { 57 // notify CookieMonster that we should delete this cookie 58 // We have stored a copy of all the cookies in the model, and our model is 59 // never re-calculated. Thus, we just need to delete the nodes from our 60 // model, and tell CookieMonster to delete the cookies. We can keep the 61 // vector storing the cookies in-tact and not delete from there (that would 62 // invalidate our pointers), and the fact that it contains semi out-of-date 63 // data is not problematic as we don't re-build the model based on that. 64 GetModel()->cookie_monster_-> 65 DeleteCookie(cookie_->Domain(), *cookie_, true); 66} 67 68namespace { 69// comparison functor, for use in CookieTreeRootNode 70class OriginNodeComparator { 71 public: 72 bool operator() (const CookieTreeNode* lhs, 73 const CookieTreeNode* rhs) { 74 // We want to order by registry controlled domain, so we would get 75 // google.com, ad.google.com, www.google.com, 76 // microsoft.com, ad.microsoft.com. CanonicalizeHost transforms the origins 77 // into a form like google.com.www so that string comparisons work. 78 return (CanonicalizeHost(lhs->GetTitleAsString16()) < 79 CanonicalizeHost(rhs->GetTitleAsString16())); 80 } 81 82 private: 83 static std::string CanonicalizeHost(const string16& host16) { 84 // The canonicalized representation makes the registry controlled domain 85 // come first, and then adds subdomains in reverse order, e.g. 86 // 1.mail.google.com would become google.com.mail.1, and then a standard 87 // string comparison works to order hosts by registry controlled domain 88 // first. Leading dots are ignored, ".google.com" is the same as 89 // "google.com". 90 91 std::string host = UTF16ToUTF8(host16); 92 std::string retval = net::RegistryControlledDomainService:: 93 GetDomainAndRegistry(host); 94 if (!retval.length()) // Is an IP address or other special origin. 95 return host; 96 97 std::string::size_type position = host.rfind(retval); 98 99 // The host may be the registry controlled domain, in which case fail fast. 100 if (position == 0 || position == std::string::npos) 101 return host; 102 103 // If host is www.google.com, retval will contain google.com at this point. 104 // Start operating to the left of the registry controlled domain, e.g. in 105 // the www.google.com example, start at index 3. 106 --position; 107 108 // If position == 0, that means it's a dot; this will be ignored to treat 109 // ".google.com" the same as "google.com". 110 while (position > 0) { 111 retval += std::string("."); 112 // Copy up to the next dot. host[position] is a dot so start after it. 113 std::string::size_type next_dot = host.rfind(".", position - 1); 114 if (next_dot == std::string::npos) { 115 retval += host.substr(0, position); 116 break; 117 } 118 retval += host.substr(next_dot + 1, position - (next_dot + 1)); 119 position = next_dot; 120 } 121 return retval; 122 } 123}; 124 125} // namespace 126 127/////////////////////////////////////////////////////////////////////////////// 128// CookieTreeAppCacheNode, public: 129 130CookieTreeAppCacheNode::CookieTreeAppCacheNode( 131 const appcache::AppCacheInfo* appcache_info) 132 : CookieTreeNode(UTF8ToWide(appcache_info->manifest_url.spec())), 133 appcache_info_(appcache_info) { 134} 135 136void CookieTreeAppCacheNode::DeleteStoredObjects() { 137 DCHECK(GetModel()->appcache_helper_); 138 GetModel()->appcache_helper_->DeleteAppCacheGroup( 139 appcache_info_->manifest_url); 140} 141 142/////////////////////////////////////////////////////////////////////////////// 143// CookieTreeDatabaseNode, public: 144 145CookieTreeDatabaseNode::CookieTreeDatabaseNode( 146 BrowsingDataDatabaseHelper::DatabaseInfo* database_info) 147 : CookieTreeNode(database_info->database_name.empty() ? 148 l10n_util::GetString(IDS_COOKIES_WEB_DATABASE_UNNAMED_NAME) : 149 UTF8ToWide(database_info->database_name)), 150 database_info_(database_info) { 151} 152 153void CookieTreeDatabaseNode::DeleteStoredObjects() { 154 GetModel()->database_helper_->DeleteDatabase( 155 database_info_->origin_identifier, database_info_->database_name); 156} 157 158/////////////////////////////////////////////////////////////////////////////// 159// CookieTreeLocalStorageNode, public: 160 161CookieTreeLocalStorageNode::CookieTreeLocalStorageNode( 162 BrowsingDataLocalStorageHelper::LocalStorageInfo* local_storage_info) 163 : CookieTreeNode(UTF8ToWide( 164 local_storage_info->origin.empty() ? 165 local_storage_info->database_identifier : 166 local_storage_info->origin)), 167 local_storage_info_(local_storage_info) { 168} 169 170void CookieTreeLocalStorageNode::DeleteStoredObjects() { 171 GetModel()->local_storage_helper_->DeleteLocalStorageFile( 172 local_storage_info_->file_path); 173} 174 175/////////////////////////////////////////////////////////////////////////////// 176// CookieTreeRootNode, public: 177 178CookieTreeOriginNode* CookieTreeRootNode::GetOrCreateOriginNode( 179 const GURL& url) { 180 CookieTreeOriginNode origin_node(url); 181 182 // First see if there is an existing match. 183 std::vector<CookieTreeNode*>::iterator origin_node_iterator = 184 lower_bound(children().begin(), 185 children().end(), 186 &origin_node, 187 OriginNodeComparator()); 188 189 if (origin_node_iterator != children().end() && 190 CookieTreeOriginNode::TitleForUrl(url) == 191 (*origin_node_iterator)->GetTitle()) 192 return static_cast<CookieTreeOriginNode*>(*origin_node_iterator); 193 // Node doesn't exist, create a new one and insert it into the (ordered) 194 // children. 195 CookieTreeOriginNode* retval = new CookieTreeOriginNode(url); 196 DCHECK(model_); 197 model_->Add(this, (origin_node_iterator - children().begin()), retval); 198 return retval; 199} 200 201/////////////////////////////////////////////////////////////////////////////// 202// CookieTreeOriginNode, public: 203 204// static 205std::wstring CookieTreeOriginNode::TitleForUrl( 206 const GURL& url) { 207 return UTF8ToWide(url.SchemeIsFile() ? kFileOriginNodeName : url.host()); 208} 209 210CookieTreeOriginNode::CookieTreeOriginNode(const GURL& url) 211 : CookieTreeNode(TitleForUrl(url)), 212 cookies_child_(NULL), 213 databases_child_(NULL), 214 local_storages_child_(NULL), 215 appcaches_child_(NULL), 216 url_(url) {} 217 218 219CookieTreeCookiesNode* CookieTreeOriginNode::GetOrCreateCookiesNode() { 220 if (cookies_child_) 221 return cookies_child_; 222 cookies_child_ = new CookieTreeCookiesNode; 223 AddChildSortedByTitle(cookies_child_); 224 return cookies_child_; 225} 226 227CookieTreeDatabasesNode* CookieTreeOriginNode::GetOrCreateDatabasesNode() { 228 if (databases_child_) 229 return databases_child_; 230 databases_child_ = new CookieTreeDatabasesNode; 231 AddChildSortedByTitle(databases_child_); 232 return databases_child_; 233} 234 235CookieTreeLocalStoragesNode* 236 CookieTreeOriginNode::GetOrCreateLocalStoragesNode() { 237 if (local_storages_child_) 238 return local_storages_child_; 239 local_storages_child_ = new CookieTreeLocalStoragesNode; 240 AddChildSortedByTitle(local_storages_child_); 241 return local_storages_child_; 242} 243 244CookieTreeAppCachesNode* CookieTreeOriginNode::GetOrCreateAppCachesNode() { 245 if (appcaches_child_) 246 return appcaches_child_; 247 appcaches_child_ = new CookieTreeAppCachesNode; 248 AddChildSortedByTitle(appcaches_child_); 249 return appcaches_child_; 250} 251 252void CookieTreeOriginNode::CreateContentException( 253 HostContentSettingsMap* content_settings, ContentSetting setting) const { 254 if (CanCreateContentException()) { 255 content_settings->AddExceptionForURL(url_, 256 CONTENT_SETTINGS_TYPE_COOKIES, 257 setting); 258 } 259} 260 261bool CookieTreeOriginNode::CanCreateContentException() const { 262 return !url_.SchemeIsFile(); 263} 264 265/////////////////////////////////////////////////////////////////////////////// 266// CookieTreeCookiesNode, public: 267 268CookieTreeCookiesNode::CookieTreeCookiesNode() 269 : CookieTreeNode(l10n_util::GetString(IDS_COOKIES_COOKIES)) { 270} 271 272/////////////////////////////////////////////////////////////////////////////// 273// CookieTreeAppCachesNode, public: 274 275CookieTreeAppCachesNode::CookieTreeAppCachesNode() 276 : CookieTreeNode(l10n_util::GetString(IDS_COOKIES_APPLICATION_CACHES)) { 277} 278 279/////////////////////////////////////////////////////////////////////////////// 280// CookieTreeDatabasesNode, public: 281 282CookieTreeDatabasesNode::CookieTreeDatabasesNode() 283 : CookieTreeNode(l10n_util::GetString(IDS_COOKIES_WEB_DATABASES)) { 284} 285 286/////////////////////////////////////////////////////////////////////////////// 287// CookieTreeLocalStoragesNode, public: 288 289CookieTreeLocalStoragesNode::CookieTreeLocalStoragesNode() 290 : CookieTreeNode(l10n_util::GetString(IDS_COOKIES_LOCAL_STORAGE)) { 291} 292 293/////////////////////////////////////////////////////////////////////////////// 294// CookieTreeNode, protected 295 296bool CookieTreeNode::NodeTitleComparator::operator() ( 297 const CookieTreeNode* lhs, const CookieTreeNode* rhs) { 298 const CookieTreeNode* left = 299 static_cast<const CookieTreeNode*>(lhs); 300 const CookieTreeNode* right = 301 static_cast<const CookieTreeNode*>(rhs); 302 return (left->GetTitleAsString16() < right->GetTitleAsString16()); 303} 304 305void CookieTreeNode::AddChildSortedByTitle(CookieTreeNode* new_child) { 306 std::vector<CookieTreeNode*>::iterator iter = 307 lower_bound(children().begin(), 308 children().end(), 309 new_child, 310 NodeTitleComparator()); 311 GetModel()->Add(this, iter - children().begin(), new_child); 312} 313 314/////////////////////////////////////////////////////////////////////////////// 315// CookiesTreeModel, public: 316 317CookiesTreeModel::CookiesTreeModel( 318 net::CookieMonster* cookie_monster, 319 BrowsingDataDatabaseHelper* database_helper, 320 BrowsingDataLocalStorageHelper* local_storage_helper, 321 BrowsingDataAppCacheHelper* appcache_helper) 322 : ALLOW_THIS_IN_INITIALIZER_LIST(TreeNodeModel<CookieTreeNode>( 323 new CookieTreeRootNode(this))), 324 cookie_monster_(cookie_monster), 325 appcache_helper_(appcache_helper), 326 database_helper_(database_helper), 327 local_storage_helper_(local_storage_helper), 328 batch_update_(0) { 329 LoadCookies(); 330 DCHECK(database_helper_); 331 database_helper_->StartFetching(NewCallback( 332 this, &CookiesTreeModel::OnDatabaseModelInfoLoaded)); 333 DCHECK(local_storage_helper_); 334 local_storage_helper_->StartFetching(NewCallback( 335 this, &CookiesTreeModel::OnStorageModelInfoLoaded)); 336 337 // TODO(michaeln): when all of the ui impls have been updated, 338 // make this a required parameter. 339 if (appcache_helper_) { 340 appcache_helper_->StartFetching(NewCallback( 341 this, &CookiesTreeModel::OnAppCacheModelInfoLoaded)); 342 } 343} 344 345CookiesTreeModel::~CookiesTreeModel() { 346 database_helper_->CancelNotification(); 347 local_storage_helper_->CancelNotification(); 348 if (appcache_helper_) 349 appcache_helper_->CancelNotification(); 350} 351 352/////////////////////////////////////////////////////////////////////////////// 353// CookiesTreeModel, TreeModel methods (public): 354 355// TreeModel methods: 356// Returns the set of icons for the nodes in the tree. You only need override 357// this if you don't want to use the default folder icons. 358void CookiesTreeModel::GetIcons(std::vector<SkBitmap>* icons) { 359 icons->push_back(*ResourceBundle::GetSharedInstance().GetBitmapNamed( 360 IDR_DEFAULT_FAVICON)); 361 icons->push_back(*ResourceBundle::GetSharedInstance().GetBitmapNamed( 362 IDR_COOKIE_ICON)); 363 icons->push_back(*ResourceBundle::GetSharedInstance().GetBitmapNamed( 364 IDR_COOKIE_STORAGE_ICON)); 365} 366 367// Returns the index of the icon to use for |node|. Return -1 to use the 368// default icon. The index is relative to the list of icons returned from 369// GetIcons. 370int CookiesTreeModel::GetIconIndex(TreeModelNode* node) { 371 CookieTreeNode* ct_node = static_cast<CookieTreeNode*>(node); 372 switch (ct_node->GetDetailedInfo().node_type) { 373 case CookieTreeNode::DetailedInfo::TYPE_ORIGIN: 374 return ORIGIN; 375 break; 376 case CookieTreeNode::DetailedInfo::TYPE_COOKIE: 377 return COOKIE; 378 break; 379 case CookieTreeNode::DetailedInfo::TYPE_DATABASE: 380 return DATABASE; 381 break; 382 case CookieTreeNode::DetailedInfo::TYPE_LOCAL_STORAGE: 383 return DATABASE; // close enough 384 break; 385 case CookieTreeNode::DetailedInfo::TYPE_APPCACHE: 386 return DATABASE; // ditto 387 break; 388 default: 389 return -1; 390 } 391} 392 393void CookiesTreeModel::LoadCookies() { 394 LoadCookiesWithFilter(std::wstring()); 395} 396 397void CookiesTreeModel::LoadCookiesWithFilter(const std::wstring& filter) { 398 // mmargh mmargh mmargh! 399 400 all_cookies_ = cookie_monster_->GetAllCookies(); 401 CookieTreeRootNode* root = static_cast<CookieTreeRootNode*>(GetRoot()); 402 for (CookieList::iterator it = all_cookies_.begin(); 403 it != all_cookies_.end(); ++it) { 404 std::string origin_host = it->Domain(); 405 if (origin_host.length() > 1 && origin_host[0] == '.') 406 origin_host = it->Domain().substr(1); 407 // We treat secure cookies just the same as normal ones. 408 GURL origin(std::string(chrome::kHttpScheme) + 409 chrome::kStandardSchemeSeparator + origin_host + "/"); 410 if (!filter.size() || 411 (CookieTreeOriginNode::TitleForUrl(origin).find(filter) != 412 std::string::npos)) { 413 CookieTreeOriginNode* origin_node = 414 root->GetOrCreateOriginNode(origin); 415 CookieTreeCookiesNode* cookies_node = 416 origin_node->GetOrCreateCookiesNode(); 417 CookieTreeCookieNode* new_cookie = new CookieTreeCookieNode(&*it); 418 cookies_node->AddCookieNode(new_cookie); 419 } 420 } 421} 422 423void CookiesTreeModel::DeleteAllStoredObjects() { 424 NotifyObserverBeginBatch(); 425 CookieTreeNode* root = GetRoot(); 426 root->DeleteStoredObjects(); 427 int num_children = root->GetChildCount(); 428 for (int i = num_children - 1; i >= 0; --i) 429 delete Remove(root, i); 430 NotifyObserverTreeNodeChanged(root); 431 NotifyObserverEndBatch(); 432} 433 434void CookiesTreeModel::DeleteCookieNode(CookieTreeNode* cookie_node) { 435 if (cookie_node == GetRoot()) 436 return; 437 cookie_node->DeleteStoredObjects(); 438 // find the parent and index 439 CookieTreeNode* parent_node = cookie_node->GetParent(); 440 int cookie_node_index = parent_node->IndexOfChild(cookie_node); 441 delete Remove(parent_node, cookie_node_index); 442 if (parent_node->GetChildCount() == 0) 443 DeleteCookieNode(parent_node); 444} 445 446void CookiesTreeModel::UpdateSearchResults(const std::wstring& filter) { 447 CookieTreeNode* root = GetRoot(); 448 int num_children = root->GetChildCount(); 449 NotifyObserverBeginBatch(); 450 for (int i = num_children - 1; i >= 0; --i) 451 delete Remove(root, i); 452 LoadCookiesWithFilter(filter); 453 PopulateDatabaseInfoWithFilter(filter); 454 PopulateLocalStorageInfoWithFilter(filter); 455 PopulateAppCacheInfoWithFilter(filter); 456 NotifyObserverTreeNodeChanged(root); 457 NotifyObserverEndBatch(); 458} 459 460void CookiesTreeModel::AddObserver(Observer* observer) { 461 cookies_observer_list_.AddObserver(observer); 462 // Call super so that TreeNodeModel can notify, too. 463 TreeNodeModel<CookieTreeNode>::AddObserver(observer); 464} 465 466void CookiesTreeModel::RemoveObserver(Observer* observer) { 467 cookies_observer_list_.RemoveObserver(observer); 468 // Call super so that TreeNodeModel doesn't have dead pointers. 469 TreeNodeModel<CookieTreeNode>::RemoveObserver(observer); 470} 471 472void CookiesTreeModel::OnAppCacheModelInfoLoaded() { 473 appcache_info_ = appcache_helper_->info_collection(); 474 PopulateAppCacheInfoWithFilter(std::wstring()); 475} 476 477void CookiesTreeModel::PopulateAppCacheInfoWithFilter( 478 const std::wstring& filter) { 479 using appcache::AppCacheInfo; 480 using appcache::AppCacheInfoVector; 481 typedef std::map<GURL, AppCacheInfoVector> InfoByOrigin; 482 483 if (!appcache_info_ || appcache_info_->infos_by_origin.empty()) 484 return; 485 486 CookieTreeRootNode* root = static_cast<CookieTreeRootNode*>(GetRoot()); 487 NotifyObserverBeginBatch(); 488 for (InfoByOrigin::const_iterator origin = 489 appcache_info_->infos_by_origin.begin(); 490 origin != appcache_info_->infos_by_origin.end(); ++origin) { 491 std::wstring origin_node_name = UTF8ToWide(origin->first.host()); 492 if (filter.empty() || 493 (origin_node_name.find(filter) != std::wstring::npos)) { 494 CookieTreeOriginNode* origin_node = 495 root->GetOrCreateOriginNode(origin->first); 496 CookieTreeAppCachesNode* appcaches_node = 497 origin_node->GetOrCreateAppCachesNode(); 498 499 for (AppCacheInfoVector::const_iterator info = origin->second.begin(); 500 info != origin->second.end(); ++info) { 501 appcaches_node->AddAppCacheNode( 502 new CookieTreeAppCacheNode(&(*info))); 503 } 504 } 505 } 506 NotifyObserverTreeNodeChanged(root); 507 NotifyObserverEndBatch(); 508} 509 510void CookiesTreeModel::OnDatabaseModelInfoLoaded( 511 const DatabaseInfoList& database_info) { 512 database_info_list_ = database_info; 513 PopulateDatabaseInfoWithFilter(std::wstring()); 514} 515 516void CookiesTreeModel::PopulateDatabaseInfoWithFilter( 517 const std::wstring& filter) { 518 if (database_info_list_.empty()) 519 return; 520 CookieTreeRootNode* root = static_cast<CookieTreeRootNode*>(GetRoot()); 521 NotifyObserverBeginBatch(); 522 for (DatabaseInfoList::iterator database_info = database_info_list_.begin(); 523 database_info != database_info_list_.end(); 524 ++database_info) { 525 GURL origin(database_info->origin); 526 527 if (!filter.size() || 528 (CookieTreeOriginNode::TitleForUrl(origin).find(filter) != 529 std::wstring::npos)) { 530 CookieTreeOriginNode* origin_node = 531 root->GetOrCreateOriginNode(origin); 532 CookieTreeDatabasesNode* databases_node = 533 origin_node->GetOrCreateDatabasesNode(); 534 databases_node->AddDatabaseNode( 535 new CookieTreeDatabaseNode(&(*database_info))); 536 } 537 } 538 NotifyObserverTreeNodeChanged(root); 539 NotifyObserverEndBatch(); 540} 541 542void CookiesTreeModel::OnStorageModelInfoLoaded( 543 const LocalStorageInfoList& local_storage_info) { 544 local_storage_info_list_ = local_storage_info; 545 PopulateLocalStorageInfoWithFilter(std::wstring()); 546} 547 548void CookiesTreeModel::PopulateLocalStorageInfoWithFilter( 549 const std::wstring& filter) { 550 if (local_storage_info_list_.empty()) 551 return; 552 CookieTreeRootNode* root = static_cast<CookieTreeRootNode*>(GetRoot()); 553 NotifyObserverBeginBatch(); 554 for (LocalStorageInfoList::iterator local_storage_info = 555 local_storage_info_list_.begin(); 556 local_storage_info != local_storage_info_list_.end(); 557 ++local_storage_info) { 558 GURL origin(local_storage_info->origin); 559 560 if (!filter.size() || 561 (CookieTreeOriginNode::TitleForUrl(origin).find(filter) != 562 std::wstring::npos)) { 563 CookieTreeOriginNode* origin_node = 564 root->GetOrCreateOriginNode(origin); 565 CookieTreeLocalStoragesNode* local_storages_node = 566 origin_node->GetOrCreateLocalStoragesNode(); 567 local_storages_node->AddLocalStorageNode( 568 new CookieTreeLocalStorageNode(&(*local_storage_info))); 569 } 570 } 571 NotifyObserverTreeNodeChanged(root); 572 NotifyObserverEndBatch(); 573} 574 575void CookiesTreeModel::NotifyObserverBeginBatch() { 576 // Only notify the model once if we're batching in a nested manner. 577 if (batch_update_++ == 0) { 578 FOR_EACH_OBSERVER(Observer, 579 cookies_observer_list_, 580 TreeModelBeginBatch(this)); 581 } 582} 583 584void CookiesTreeModel::NotifyObserverEndBatch() { 585 // Only notify the observers if this is the outermost call to EndBatch() if 586 // called in a nested manner. 587 if (--batch_update_ == 0) { 588 FOR_EACH_OBSERVER(Observer, 589 cookies_observer_list_, 590 TreeModelEndBatch(this)); 591 } 592} 593