// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/cookies_tree_model.h" #include #include #include #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "app/table_model_observer.h" #include "app/tree_node_model.h" #include "base/callback.h" #include "base/linked_ptr.h" #include "base/string_util.h" #include "chrome/browser/in_process_webkit/webkit_context.h" #include "chrome/browser/net/chrome_url_request_context.h" #include "chrome/browser/profile.h" #include "grit/app_resources.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "net/base/cookie_monster.h" #include "net/base/registry_controlled_domain.h" #include "net/url_request/url_request_context.h" #include "third_party/skia/include/core/SkBitmap.h" /////////////////////////////////////////////////////////////////////////////// // CookieTreeNode, public: void CookieTreeNode::DeleteStoredObjects() { std::for_each(children().begin(), children().end(), std::mem_fun(&CookieTreeNode::DeleteStoredObjects)); } CookiesTreeModel* CookieTreeNode::GetModel() const { if (GetParent()) return GetParent()->GetModel(); else return NULL; } /////////////////////////////////////////////////////////////////////////////// // CookieTreeCookieNode, public: CookieTreeCookieNode::CookieTreeCookieNode( net::CookieMonster::CookieListPair* cookie) : CookieTreeNode(UTF8ToWide(cookie->second.Name())), cookie_(cookie) { } void CookieTreeCookieNode::DeleteStoredObjects() { // notify CookieMonster that we should delete this cookie // Since we are running on the UI thread don't call GetURLRequestContext(). net::CookieMonster* monster = GetModel()->profile_-> GetRequestContext()->GetCookieStore()->GetCookieMonster(); // We have stored a copy of all the cookies in the model, and our model is // never re-calculated. Thus, we just need to delete the nodes from our // model, and tell CookieMonster to delete the cookies. We can keep the // vector storing the cookies in-tact and not delete from there (that would // invalidate our pointers), and the fact that it contains semi out-of-date // data is not problematic as we don't re-build the model based on that. monster->DeleteCookie(cookie_->first, cookie_->second, true); } namespace { // comparison functor, for use in CookieTreeRootNode class OriginNodeComparator { public: bool operator() (const CookieTreeNode* lhs, const CookieTreeNode* rhs) { // We want to order by registry controlled domain, so we would get // google.com, ad.google.com, www.google.com, // microsoft.com, ad.microsoft.com. CanonicalizeHost transforms the origins // into a form like google.com.www so that string comparisons work. return (CanonicalizeHost(lhs->GetTitle()) < CanonicalizeHost(rhs->GetTitle())); } private: static std::string CanonicalizeHost(const std::wstring& host_w) { // The canonicalized representation makes the registry controlled domain // come first, and then adds subdomains in reverse order, e.g. // 1.mail.google.com would become google.com.mail.1, and then a standard // string comparison works to order hosts by registry controlled domain // first. Leading dots are ignored, ".google.com" is the same as // "google.com". std::string host = WideToUTF8(host_w); std::string retval = net::RegistryControlledDomainService:: GetDomainAndRegistry(host); if (!retval.length()) // Is an IP address or other special origin. return host; std::string::size_type position = host.rfind(retval); // The host may be the registry controlled domain, in which case fail fast. if (position == 0 || position == std::string::npos) return host; // If host is www.google.com, retval will contain google.com at this point. // Start operating to the left of the registry controlled domain, e.g. in // the www.google.com example, start at index 3. --position; // If position == 0, that means it's a dot; this will be ignored to treat // ".google.com" the same as "google.com". while (position > 0) { retval += std::string("."); // Copy up to the next dot. host[position] is a dot so start after it. std::string::size_type next_dot = host.rfind(".", position - 1); if (next_dot == std::string::npos) { retval += host.substr(0, position); break; } retval += host.substr(next_dot + 1, position - (next_dot + 1)); position = next_dot; } return retval; } }; } // namespace /////////////////////////////////////////////////////////////////////////////// // CookieTreeDatabaseNode, public: CookieTreeDatabaseNode::CookieTreeDatabaseNode( BrowsingDataDatabaseHelper::DatabaseInfo* database_info) : CookieTreeNode(UTF8ToWide(database_info->database_name.empty() ? database_info->origin_identifier : database_info->database_name)), database_info_(database_info) { } void CookieTreeDatabaseNode::DeleteStoredObjects() { GetModel()->database_helper_->DeleteDatabase( database_info_->origin_identifier, database_info_->database_name); } /////////////////////////////////////////////////////////////////////////////// // CookieTreeLocalStorageNode, public: CookieTreeLocalStorageNode::CookieTreeLocalStorageNode( BrowsingDataLocalStorageHelper::LocalStorageInfo* local_storage_info) : CookieTreeNode(UTF8ToWide( local_storage_info->origin.empty() ? local_storage_info->database_identifier : local_storage_info->origin)), local_storage_info_(local_storage_info) { } void CookieTreeLocalStorageNode::DeleteStoredObjects() { GetModel()->local_storage_helper_->DeleteLocalStorageFile( local_storage_info_->file_path); } /////////////////////////////////////////////////////////////////////////////// // CookieTreeRootNode, public: CookieTreeOriginNode* CookieTreeRootNode::GetOrCreateOriginNode( const std::wstring& origin) { // Strip the trailing dot if it exists. std::wstring rewritten_origin = origin; if (origin.length() >= 1 && origin[0] == '.') rewritten_origin = origin.substr(1); CookieTreeOriginNode rewritten_origin_node(rewritten_origin); // First see if there is an existing match. std::vector::iterator origin_node_iterator = lower_bound(children().begin(), children().end(), &rewritten_origin_node, OriginNodeComparator()); if (origin_node_iterator != children().end() && rewritten_origin == (*origin_node_iterator)->GetTitle()) return static_cast(*origin_node_iterator); // Node doesn't exist, create a new one and insert it into the (ordered) // children. CookieTreeOriginNode* retval = new CookieTreeOriginNode(rewritten_origin); DCHECK(model_); model_->Add(this, (origin_node_iterator - children().begin()), retval); return retval; } /////////////////////////////////////////////////////////////////////////////// // CookieTreeOriginNode, public: CookieTreeCookiesNode* CookieTreeOriginNode::GetOrCreateCookiesNode() { if (cookies_child_) return cookies_child_; // need to make a Cookies node, add it to the tree, and return it CookieTreeCookiesNode* retval = new CookieTreeCookiesNode; GetModel()->Add(this, 0, retval); cookies_child_ = retval; return retval; } CookieTreeDatabasesNode* CookieTreeOriginNode::GetOrCreateDatabasesNode() { if (databases_child_) return databases_child_; // Need to make a Database node, add it to the tree, and return it. CookieTreeDatabasesNode* retval = new CookieTreeDatabasesNode; GetModel()->Add(this, cookies_child_ ? 1 : 0, retval); databases_child_ = retval; return retval; } CookieTreeLocalStoragesNode* CookieTreeOriginNode::GetOrCreateLocalStoragesNode() { if (local_storages_child_) return local_storages_child_; // Need to make a LocalStorages node, add it to the tree, and return it. CookieTreeLocalStoragesNode* retval = new CookieTreeLocalStoragesNode; int index = 0; if (cookies_child_) ++index; if (databases_child_) ++index; GetModel()->Add(this, index, retval); local_storages_child_ = retval; return retval; } /////////////////////////////////////////////////////////////////////////////// // CookieTreeCookiesNode, public: CookieTreeCookiesNode::CookieTreeCookiesNode() : CookieTreeNode(l10n_util::GetString(IDS_COOKIES_COOKIES)) {} void CookieTreeCookiesNode::AddCookieNode( CookieTreeCookieNode* new_child) { std::vector::iterator cookie_iterator = lower_bound(children().begin(), children().end(), new_child, CookieTreeCookieNode::CookieNodeComparator()); GetModel()->Add(this, (cookie_iterator - children().begin()), new_child); } /////////////////////////////////////////////////////////////////////////////// // CookieTreeDatabasesNode, public: CookieTreeDatabasesNode::CookieTreeDatabasesNode() : CookieTreeNode(l10n_util::GetString(IDS_COOKIES_WEB_DATABASES)) { } void CookieTreeDatabasesNode::AddDatabaseNode( CookieTreeDatabaseNode* new_child) { std::vector::iterator database_iterator = lower_bound(children().begin(), children().end(), new_child, CookieTreeDatabaseNode::CookieNodeComparator()); GetModel()->Add(this, (database_iterator - children().begin()), new_child); } /////////////////////////////////////////////////////////////////////////////// // CookieTreeLocalStoragesNode, public: CookieTreeLocalStoragesNode::CookieTreeLocalStoragesNode() : CookieTreeNode(l10n_util::GetString(IDS_COOKIES_LOCAL_STORAGE)) { } void CookieTreeLocalStoragesNode::AddLocalStorageNode( CookieTreeLocalStorageNode* new_child) { std::vector::iterator local_storage_iterator = lower_bound(children().begin(), children().end(), new_child, CookieTreeLocalStorageNode::CookieNodeComparator()); GetModel()->Add(this, (local_storage_iterator - children().begin()), new_child); } /////////////////////////////////////////////////////////////////////////////// // CookieTreeCookieNode, private bool CookieTreeCookieNode::CookieNodeComparator::operator() ( const CookieTreeNode* lhs, const CookieTreeNode* rhs) { const CookieTreeCookieNode* left = static_cast(lhs); const CookieTreeCookieNode* right = static_cast(rhs); return (left->cookie_->second.Name() < right->cookie_->second.Name()); } /////////////////////////////////////////////////////////////////////////////// // CookieTreeDatabaseNode, private bool CookieTreeDatabaseNode::CookieNodeComparator::operator() ( const CookieTreeNode* lhs, const CookieTreeNode* rhs) { const CookieTreeDatabaseNode* left = static_cast(lhs); const CookieTreeDatabaseNode* right = static_cast(rhs); return (left->database_info_->database_name < right->database_info_->database_name); } /////////////////////////////////////////////////////////////////////////////// // CookieTreeLocalStorageNode, private bool CookieTreeLocalStorageNode::CookieNodeComparator::operator() ( const CookieTreeNode* lhs, const CookieTreeNode* rhs) { const CookieTreeLocalStorageNode* left = static_cast(lhs); const CookieTreeLocalStorageNode* right = static_cast(rhs); return (left->local_storage_info_->origin < right->local_storage_info_->origin); } /////////////////////////////////////////////////////////////////////////////// // CookiesTreeModel, public: CookiesTreeModel::CookiesTreeModel( Profile* profile, BrowsingDataDatabaseHelper* database_helper, BrowsingDataLocalStorageHelper* local_storage_helper) : ALLOW_THIS_IN_INITIALIZER_LIST(TreeNodeModel( new CookieTreeRootNode(this))), profile_(profile), database_helper_(database_helper), local_storage_helper_(local_storage_helper) { LoadCookies(); DCHECK(database_helper_); database_helper_->StartFetching(NewCallback( this, &CookiesTreeModel::OnDatabaseModelInfoLoaded)); DCHECK(local_storage_helper_); local_storage_helper_->StartFetching(NewCallback( this, &CookiesTreeModel::OnStorageModelInfoLoaded)); } CookiesTreeModel::~CookiesTreeModel() { database_helper_->CancelNotification(); local_storage_helper_->CancelNotification(); } /////////////////////////////////////////////////////////////////////////////// // CookiesTreeModel, TreeModel methods (public): // TreeModel methods: // Returns the set of icons for the nodes in the tree. You only need override // this if you don't want to use the default folder icons. void CookiesTreeModel::GetIcons(std::vector* icons) { icons->push_back(*ResourceBundle::GetSharedInstance().GetBitmapNamed( IDR_DEFAULT_FAVICON)); icons->push_back(*ResourceBundle::GetSharedInstance().GetBitmapNamed( IDR_COOKIE_ICON)); icons->push_back(*ResourceBundle::GetSharedInstance().GetBitmapNamed( IDR_COOKIE_STORAGE_ICON)); } // Returns the index of the icon to use for |node|. Return -1 to use the // default icon. The index is relative to the list of icons returned from // GetIcons. int CookiesTreeModel::GetIconIndex(TreeModelNode* node) { CookieTreeNode* ct_node = static_cast(node); switch (ct_node->GetDetailedInfo().node_type) { case CookieTreeNode::DetailedInfo::TYPE_ORIGIN: return ORIGIN; break; case CookieTreeNode::DetailedInfo::TYPE_COOKIE: return COOKIE; break; case CookieTreeNode::DetailedInfo::TYPE_DATABASE: return DATABASE; break; case CookieTreeNode::DetailedInfo::TYPE_LOCAL_STORAGE: // The differences between local storage and HTML5 databases are semantic // enough that the user will not likely care if they share an icon. return DATABASE; break; default: return -1; } } void CookiesTreeModel::LoadCookies() { LoadCookiesWithFilter(std::wstring()); } void CookiesTreeModel::LoadCookiesWithFilter(const std::wstring& filter) { // mmargh mmargh mmargh! // Since we are running on the UI thread don't call GetURLRequestContext(). net::CookieMonster* cookie_monster = profile_->GetRequestContext()->GetCookieStore()->GetCookieMonster(); all_cookies_ = cookie_monster->GetAllCookies(); CookieTreeRootNode* root = static_cast(GetRoot()); for (CookieList::iterator it = all_cookies_.begin(); it != all_cookies_.end(); ++it) { // Get the origin cookie if (!filter.size() || (UTF8ToWide(it->first).find(filter) != std::wstring::npos)) { CookieTreeOriginNode* origin = root->GetOrCreateOriginNode(UTF8ToWide(it->first)); CookieTreeCookiesNode* cookies_node = origin->GetOrCreateCookiesNode(); CookieTreeCookieNode* new_cookie = new CookieTreeCookieNode(&*it); cookies_node->AddCookieNode(new_cookie); } } } void CookiesTreeModel::DeleteAllStoredObjects() { CookieTreeNode* root = GetRoot(); root->DeleteStoredObjects(); int num_children = root->GetChildCount(); for (int i = num_children - 1; i >= 0; --i) delete Remove(root, i); NotifyObserverTreeNodeChanged(root); } void CookiesTreeModel::DeleteCookieNode(CookieTreeNode* cookie_node) { cookie_node->DeleteStoredObjects(); // find the parent and index CookieTreeNode* parent_node = cookie_node->GetParent(); int cookie_node_index = parent_node->IndexOfChild(cookie_node); delete Remove(parent_node, cookie_node_index); } void CookiesTreeModel::UpdateSearchResults(const std::wstring& filter) { CookieTreeNode* root = GetRoot(); int num_children = root->GetChildCount(); for (int i = num_children - 1; i >= 0; --i) delete Remove(root, i); LoadCookiesWithFilter(filter); PopulateDatabaseInfoWithFilter(filter); PopulateLocalStorageInfoWithFilter(filter); NotifyObserverTreeNodeChanged(root); } void CookiesTreeModel::OnDatabaseModelInfoLoaded( const DatabaseInfoList& database_info) { database_info_list_ = database_info; PopulateDatabaseInfoWithFilter(std::wstring()); } void CookiesTreeModel::PopulateDatabaseInfoWithFilter( const std::wstring& filter) { CookieTreeRootNode* root = static_cast(GetRoot()); for (DatabaseInfoList::iterator database_info = database_info_list_.begin(); database_info != database_info_list_.end(); ++database_info) { std::string origin = database_info->host.empty() ? database_info->origin_identifier : database_info->host; if (!filter.size() || (UTF8ToWide(origin).find(filter) != std::wstring::npos)) { CookieTreeOriginNode* origin_node = root->GetOrCreateOriginNode( UTF8ToWide(database_info->host)); CookieTreeDatabasesNode* databases_node = origin_node->GetOrCreateDatabasesNode(); databases_node->AddDatabaseNode( new CookieTreeDatabaseNode(&(*database_info))); } } NotifyObserverTreeNodeChanged(root); } void CookiesTreeModel::OnStorageModelInfoLoaded( const LocalStorageInfoList& local_storage_info) { local_storage_info_list_ = local_storage_info; PopulateLocalStorageInfoWithFilter(std::wstring()); } void CookiesTreeModel::PopulateLocalStorageInfoWithFilter( const std::wstring& filter) { CookieTreeRootNode* root = static_cast(GetRoot()); for (LocalStorageInfoList::iterator local_storage_info = local_storage_info_list_.begin(); local_storage_info != local_storage_info_list_.end(); ++local_storage_info) { std::string origin = local_storage_info->host.empty() ? local_storage_info->database_identifier : local_storage_info->host; if (!filter.size() || (UTF8ToWide(origin).find(filter) != std::wstring::npos)) { CookieTreeOriginNode* origin_node = root->GetOrCreateOriginNode( UTF8ToWide(local_storage_info->host)); CookieTreeLocalStoragesNode* local_storages_node = origin_node->GetOrCreateLocalStoragesNode(); local_storages_node->AddLocalStorageNode( new CookieTreeLocalStorageNode(&(*local_storage_info))); } } NotifyObserverTreeNodeChanged(root); }