diff options
-rw-r--r-- | app/tree_node_model.h | 4 | ||||
-rw-r--r-- | chrome/app/generated_resources.grd | 50 | ||||
-rw-r--r-- | chrome/browser/cookies_tree_model.cc | 231 | ||||
-rw-r--r-- | chrome/browser/cookies_tree_model.h | 207 | ||||
-rw-r--r-- | chrome/browser/cookies_tree_model_unittest.cc | 290 | ||||
-rw-r--r-- | chrome/browser/views/options/advanced_contents_view.cc | 3 | ||||
-rw-r--r-- | chrome/browser/views/options/cookies_view.cc | 263 | ||||
-rw-r--r-- | chrome/browser/views/options/cookies_view.h | 103 | ||||
-rwxr-xr-x | chrome/chrome.gyp | 3 | ||||
-rw-r--r-- | views/controls/tree/tree_view.cc | 21 |
10 files changed, 933 insertions, 242 deletions
diff --git a/app/tree_node_model.h b/app/tree_node_model.h index 8d8b713..2d84050 100644 --- a/app/tree_node_model.h +++ b/app/tree_node_model.h @@ -109,7 +109,7 @@ class TreeNode : public TreeModelNode { int GetTotalNodeCount() const { int count = 1; // Start with one to include the node itself. for (size_t i = 0; i < children_->size(); ++i) { - TreeNode<NodeType>* child = children_[i]; + const TreeNode<NodeType>* child = children_[i]; count += child->GetTotalNodeCount(); } return count; @@ -245,7 +245,7 @@ class TreeNodeModel : public TreeModel { } NodeType* AsNode(TreeModelNode* model_node) { - return reinterpret_cast<NodeType*>(model_node); + return static_cast<NodeType*>(model_node); } // Sets the title of the specified node. diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index be48c58..84d56ad 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -4305,6 +4305,9 @@ Keep your key file in a safe place. You will need it to create new versions of y <message name="IDS_OPTIONS_COOKIES_SHOWCOOKIES" desc="The label of the 'Show Cookies' button"> Show cookies </message> + <message name="IDS_OPTIONS_COOKIES_SHOWCOOKIES_WEBSITE_PERMISSIONS" desc="The label of the 'Show Cookies and Website Permissions' button. Website Permissions means you can set permissions for individual websites, such as google.com can do this, yahoo.com can do that."> + Show cookies and website permissions + </message> <message name="IDS_OPTIONS_PLUGINS_IN_SANDBOX" desc="The label for the 'run plugins in sandbox' option"> Runs plug-ins in a sandbox with no privileges. Will cause some plug-ins to not work properly. </message> @@ -4395,6 +4398,9 @@ Keep your key file in a safe place. You will need it to create new versions of y <message name="IDS_COOKIES_WINDOW_TITLE" desc="The title of the Cookies Window"> Cookies </message> + <message name="IDS_COOKIES_WEBSITE_PERMISSIONS_WINDOW_TITLE" desc="The title of the redesigned Cookies Window that lets you manage cookies, storage quota for websites and other permissions per-website"> + Cookies and Website Permissions + </message> <message name="IDS_COOKIES_SEARCH_LABEL" desc="The label of the 'Search:' label in the Cookies window"> Search: </message> @@ -4449,7 +4455,49 @@ Keep your key file in a safe place. You will need it to create new versions of y <message name="IDS_COOKIES_NAME_COLUMN_HEADER" desc="The label of the Cookie Name header in the Cookies table"> Cookie Name </message> - + <message name="IDS_COOKIES_SIZE_TAKEN" desc="Amount of storage space taken up by something (database, application cache, etc) in megabytes"> + <ph name="SIZE_TAKEN">$1<ex>2.5</ex></ph>MB + </message> + <message name="IDS_COOKIES_SIZE_SUFFIX_MEGABYTES" desc="Abbreviation for megabytes, shown after a dialog box in which user can type in a size"> + MB + </message> + <message name="IDS_COOKIES_COOKIES" desc="Label for folder under which a list of cookies appear"> + Cookies + </message> + <message name="IDS_COOKIES_APPLICATION_CACHES" desc="Label for folder under which a list of application caches (that's the name of a HTML standard, have yet to see it translated) are stored"> + Application Caches + </message> + <message name="IDS_COOKIES_APPLICATION_CACHE" desc="Label to denote an application cache in the list of things being stored on your computer (application cache is the name of a HTML standard I have yet to see translated)"> + Application Cache + </message> + <message name="IDS_COOKIES_APPLICATION_CACHE_MANIFEST_LABEL" desc="The Manifest label (manifest is a URL specified by the application cache)"> + Manifest: + </message> + <message name="IDS_COOKIES_SIZE_LABEL" desc="The Size label, to indicate how much space is taken by a database or application cache"> + Size: + </message> + <message name="IDS_COOKIES_QUOTA_LABEL" desc="The Quota label (how much storage space a site is allowed to use)"> + Quota: + </message> + <message name="IDS_COOKIES_QUOTA_SET_BUTTON" desc="The Set button, to set the storage quota allowed for a site"> + Set + </message> + <message name="IDS_COOKIES_OVERALL_LABEL" desc="Displayed to set settings for a site overall, as opposed to a particular database in the site"> + Overall for this site: + </message> + <message name="IDS_COOKIES_WEB_DATABASES" desc="Label for the folder under which a list of web databases (name of a HTML standard) are displayed"> + Web Databases + </message> + <message name="IDS_COOKIES_WEB_DATABASE" desc="Label for an individual web databases (name of a HTML standard) are displayed"> + Web Database + </message> + <message name="IDS_COOKIES_WEB_DATABASE_NAME" desc="Label for the name of an individual web databases (name of a HTML standard) are displayed"> + Database Name: + </message> + <message name="IDS_COOKIES_LOCAL_STORAGE" desc="Label for local storage (name of a HTML standard)"> + Local Storage + </message> + <!-- New Tab --> <message name="IDS_NEW_TAB_TITLE" desc="Title of the new tab page, this is only shown while loading, then the title comes from the page"> diff --git a/chrome/browser/cookies_tree_model.cc b/chrome/browser/cookies_tree_model.cc new file mode 100644 index 0000000..1ec6e3f --- /dev/null +++ b/chrome/browser/cookies_tree_model.cc @@ -0,0 +1,231 @@ +// Copyright (c) 2009 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 <algorithm> +#include <functional> +#include <vector> + +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "app/table_model_observer.h" +#include "app/tree_node_model.h" +#include "base/linked_ptr.h" +#include "base/string_util.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/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() { + GetModel()->DeleteCookie(*cookie_); +} + +namespace { +// comparison functor, for use in CookieTreeRootNode +class OriginNodeComparator { + public: + bool operator() (const CookieTreeNode* lhs, + const CookieTreeNode* rhs) { + return (lhs->GetTitle() < rhs->GetTitle()); + } +}; + +} // namespace + +/////////////////////////////////////////////////////////////////////////////// +// 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<CookieTreeNode*>::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<CookieTreeOriginNode*>(*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; +} + +/////////////////////////////////////////////////////////////////////////////// +// CookieTreeCookiesNode, public: + +CookieTreeCookiesNode::CookieTreeCookiesNode() + : CookieTreeNode(l10n_util::GetString(IDS_COOKIES_COOKIES)) {} + + +void CookieTreeCookiesNode::AddCookieNode( + CookieTreeCookieNode* new_child) { + std::vector<CookieTreeNode*>::iterator cookie_iterator = + lower_bound(children().begin(), + children().end(), + new_child, + CookieTreeCookieNode::CookieNodeComparator()); + GetModel()->Add(this, (cookie_iterator - children().begin()), new_child); +} + +/////////////////////////////////////////////////////////////////////////////// +// CookieTreeCookieNode, private + +bool CookieTreeCookieNode::CookieNodeComparator::operator() ( + const CookieTreeNode* lhs, const CookieTreeNode* rhs) { + return (static_cast<const CookieTreeCookieNode*>(lhs)-> + cookie_->second.Name() < + static_cast<const CookieTreeCookieNode*>(rhs)-> + cookie_->second.Name()); +} + +/////////////////////////////////////////////////////////////////////////////// +// CookiesTreeModel, public: + +CookiesTreeModel::CookiesTreeModel(Profile* profile) + : ALLOW_THIS_IN_INITIALIZER_LIST(TreeNodeModel<CookieTreeNode>( + new CookieTreeRootNode(this))), + profile_(profile) { + LoadCookies(); +} + +/////////////////////////////////////////////////////////////////////////////// +// 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<SkBitmap>* icons) { + icons->push_back(*ResourceBundle::GetSharedInstance().GetBitmapNamed( + IDR_DEFAULT_FAVICON)); + icons->push_back(*ResourceBundle::GetSharedInstance().GetBitmapNamed( + IDR_COOKIE_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<CookieTreeNode*>(node); + switch (ct_node->GetDetailedInfo().node_type) { + case CookieTreeNode::DetailedInfo::TYPE_ORIGIN: + return ORIGIN; + break; + case CookieTreeNode::DetailedInfo::TYPE_COOKIE: + return COOKIE; + break; + default: + return -1; + } +} + +void CookiesTreeModel::LoadCookies() { + // 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<CookieTreeRootNode*>(GetRoot()); + for (CookieList::iterator it = all_cookies_.begin(); + it != all_cookies_.end(); + ++it) { + // Get the origin cookie + 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::DeleteCookie( + const net::CookieMonster::CookieListPair& cookie) { + // notify CookieMonster that we should delete this cookie + // Since we are running on the UI thread don't call GetURLRequestContext(). + net::CookieMonster* monster = + 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); +} + +void CookiesTreeModel::DeleteAllCookies() { + CookieTreeNode* root = GetRoot(); + root->DeleteStoredObjects(); + int num_children = root->GetChildCount(); + for (int i = num_children - 1; i >= 0; --i) { + delete Remove(root, i); + } + LoadCookies(); + NotifyObserverTreeNodeChanged(root); +} + +void CookiesTreeModel::DeleteCookieNode(CookieTreeCookieNode* 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); +} diff --git a/chrome/browser/cookies_tree_model.h b/chrome/browser/cookies_tree_model.h new file mode 100644 index 0000000..c1f26308 --- /dev/null +++ b/chrome/browser/cookies_tree_model.h @@ -0,0 +1,207 @@ +// Copyright (c) 2009 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. + +#ifndef CHROME_BROWSER_COOKIES_TREE_MODEL_H_ +#define CHROME_BROWSER_COOKIES_TREE_MODEL_H_ + +#include <string> +#include <vector> + +#include "app/tree_node_model.h" +#include "base/scoped_ptr.h" +#include "net/base/cookie_monster.h" + +class CookiesTreeModel; +class CookieTreeCookieNode; +class CookieTreeCookiesNode; +class CookieTreeOriginNode; +class Profile; + +// CookieTreeNode ------------------------------------------------------------- +// The base node type in the Cookies + Local Storage options view, from which +// all other types are derived. Specialized from TreeNode in that it has a +// notion of deleting objects stored in the profile, and being able to have +// its children do the same. +class CookieTreeNode : public TreeNode<CookieTreeNode> { + public: + // Used to pull out information for the InfoView (the details display below + // the tree control.) + struct DetailedInfo { + // NodeType corresponds to the various CookieTreeNode types. + enum NodeType { + TYPE_ROOT, // This is used for CookieTreeRootNode nodes. + TYPE_ORIGIN, // This is used for CookieTreeOriginNode nodes. + TYPE_COOKIES, // This is used for CookieTreeCookiesNode nodes. + TYPE_COOKIE // This is used for CookieTreeCookieNode nodes. + }; + + DetailedInfo(const std::wstring& origin, NodeType node_type, + const net::CookieMonster::CookieListPair* cookie) + : origin(origin), + node_type(node_type), + cookie(cookie) {} + + std::wstring origin; + NodeType node_type; + const net::CookieMonster::CookieListPair* cookie; + }; + + CookieTreeNode() {} + explicit CookieTreeNode(const std::wstring& title) + : TreeNode<CookieTreeNode>(title) {} + + // Delete backend storage for this node, and any children nodes. (E.g. delete + // the cookie from CookieMonster, clear the database, and so forth.) + virtual void DeleteStoredObjects(); + + // Gets a pointer back to the associated model for the tree we are in. + virtual CookiesTreeModel* GetModel() const; + + // Returns a struct with detailed information used to populate the details + // part of the view. + virtual DetailedInfo GetDetailedInfo() const = 0; + + private: + + DISALLOW_COPY_AND_ASSIGN(CookieTreeNode); +}; + +// CookieTreeRootNode --------------------------------------------------------- +// The node at the root of the CookieTree that gets inserted into the view. +class CookieTreeRootNode : public CookieTreeNode { + public: + explicit CookieTreeRootNode(CookiesTreeModel* model) : model_(model) {} + virtual ~CookieTreeRootNode() {} + + CookieTreeOriginNode* GetOrCreateOriginNode(const std::wstring& origin); + + // CookieTreeNode methods: + virtual CookiesTreeModel* GetModel() const { return model_; } + virtual DetailedInfo GetDetailedInfo() const { + return DetailedInfo(std::wstring(), DetailedInfo::TYPE_ROOT, NULL); + } + private: + + CookiesTreeModel* model_; + + DISALLOW_COPY_AND_ASSIGN(CookieTreeRootNode); +}; + +// CookieTreeOriginNode ------------------------------------------------------- +class CookieTreeOriginNode : public CookieTreeNode { + public: + explicit CookieTreeOriginNode(const std::wstring& origin) + : CookieTreeNode(origin), cookies_child_(NULL) {} + virtual ~CookieTreeOriginNode() {} + + // CookieTreeNode methods: + virtual DetailedInfo GetDetailedInfo() const { + return DetailedInfo(GetTitle(), DetailedInfo::TYPE_ORIGIN, NULL); + } + + // CookieTreeOriginNode methods: + CookieTreeCookiesNode* GetOrCreateCookiesNode(); + private: + + // A pointer to the COOKIES node. Eventually we will also have database, + // appcache, local storage, ..., and when we build up the tree we need to + // quickly get a reference to the COOKIES node to add children. Checking each + // child and interrogating them to see if they are a COOKIES, APPCACHES, + // DATABASES etc node seems less preferable than storing an extra pointer per + // origin. + CookieTreeCookiesNode* cookies_child_; + + DISALLOW_COPY_AND_ASSIGN(CookieTreeOriginNode); +}; + +// CookieTreeCookiesNode ------------------------------------------------------ +class CookieTreeCookiesNode : public CookieTreeNode { + public: + CookieTreeCookiesNode(); + virtual ~CookieTreeCookiesNode() {} + + // CookieTreeNode methods: + virtual DetailedInfo GetDetailedInfo() const { + return DetailedInfo(GetParent()->GetTitle(), DetailedInfo::TYPE_COOKIES, + NULL); + } + + // CookieTreeCookiesNode methods: + void AddCookieNode(CookieTreeCookieNode* child); + + private: + DISALLOW_COPY_AND_ASSIGN(CookieTreeCookiesNode); +}; + +class CookieTreeCookieNode : public CookieTreeNode { + public: + friend class CookieTreeCookiesNode; + + // Does not take ownership of cookie, and cookie should remain valid at least + // as long as the CookieTreeCookieNode is valid. + explicit CookieTreeCookieNode(net::CookieMonster::CookieListPair* cookie); + virtual ~CookieTreeCookieNode() {} + + // CookieTreeNode methods: + virtual void DeleteStoredObjects(); + virtual DetailedInfo GetDetailedInfo() const { + return DetailedInfo(GetParent()->GetParent()->GetTitle(), + DetailedInfo::TYPE_COOKIE, cookie_); + } + + private: + // Comparator functor, takes CookieTreeNode so that we can use it in + // lower_bound using children()'s iterators, which are CookieTreeNode*. + class CookieNodeComparator { + public: + bool operator() (const CookieTreeNode* lhs, const CookieTreeNode* rhs); + }; + + // Cookie_ is not owned by the node, and is expected to remain valid as long + // as the CookieTreeCookieNode is valid. + net::CookieMonster::CookieListPair* cookie_; + + DISALLOW_COPY_AND_ASSIGN(CookieTreeCookieNode); +}; + + +class CookiesTreeModel : public TreeNodeModel<CookieTreeNode> { + public: + explicit CookiesTreeModel(Profile* profile); + virtual ~CookiesTreeModel() {} + + // 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. + virtual void GetIcons(std::vector<SkBitmap>* icons); + + // 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. + virtual int GetIconIndex(TreeModelNode* node); + + // CookiesTreeModel methods: + void DeleteCookie(const net::CookieMonster::CookieListPair& cookie); + void DeleteAllCookies(); + void DeleteCookieNode(CookieTreeCookieNode* cookie_node); + + private: + enum CookieIconIndex { + ORIGIN = 0, + COOKIE = 1 + }; + typedef net::CookieMonster::CookieList CookieList; + typedef std::vector<net::CookieMonster::CookieListPair*> CookiePtrList; + + void LoadCookies(); + + // The profile from which this model sources cookies. + Profile* profile_; + CookieList all_cookies_; + + DISALLOW_COPY_AND_ASSIGN(CookiesTreeModel); +}; + +#endif // CHROME_BROWSER_COOKIES_TREE_MODEL_H_ + diff --git a/chrome/browser/cookies_tree_model_unittest.cc b/chrome/browser/cookies_tree_model_unittest.cc new file mode 100644 index 0000000..43575f5 --- /dev/null +++ b/chrome/browser/cookies_tree_model_unittest.cc @@ -0,0 +1,290 @@ +// Copyright (c) 2009 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 <string> + +#include "app/l10n_util.h" +#include "chrome/browser/net/url_request_context_getter.h" +#include "chrome/test/testing_profile.h" +#include "net/url_request/url_request_context.h" +#include "testing/gtest/include/gtest/gtest.h" + + +namespace { + +class TestURLRequestContext : public URLRequestContext { + public: + TestURLRequestContext() { + cookie_store_ = new net::CookieMonster(); + } +}; + +class TestURLRequestContextGetter : public URLRequestContextGetter { + public: + virtual URLRequestContext* GetURLRequestContext() { + if (!context_) + context_ = new TestURLRequestContext(); + return context_.get(); + } + private: + scoped_refptr<URLRequestContext> context_; +}; + +class CookieTestingProfile : public TestingProfile { + public: + virtual URLRequestContextGetter* GetRequestContext() { + if (!url_request_context_getter_.get()) + url_request_context_getter_ = new TestURLRequestContextGetter; + return url_request_context_getter_.get(); + } + virtual ~CookieTestingProfile() {} + + net::CookieMonster* GetCookieMonster() { + return GetRequestContext()->GetCookieStore()->GetCookieMonster(); + } + + private: + scoped_refptr<URLRequestContextGetter> url_request_context_getter_; +}; + + + +class CookiesTreeModelTest : public testing::Test { + public: + virtual void SetUp() { + profile_.reset(new CookieTestingProfile()); + } + + // Get the cookie names in the cookie list, as a comma seperated string. + // (Note that the CookieMonster cookie list is sorted by domain.) + // Ex: + // monster->SetCookie(GURL("http://b"), "X=1") + // monster->SetCookie(GURL("http://a"), "Y=1") + // EXPECT_STREQ("Y,X", GetMonsterCookies(monster).c_str()); + std::string GetMonsterCookies(net::CookieMonster* monster) { + std::vector<std::string> parts; + net::CookieMonster::CookieList cookie_list = monster->GetAllCookies(); + for (size_t i = 0; i < cookie_list.size(); ++i) + parts.push_back(cookie_list[i].second.Name()); + return JoinString(parts, ','); + } + + std::string GetCookiesOfChildren(const CookieTreeNode* node) { + if (node->GetChildCount()) { + std::string retval; + for (int i = 0; i < node->GetChildCount(); ++i) { + retval += GetCookiesOfChildren(node->GetChild(i)); + } + return retval; + } else { + if (node->GetDetailedInfo().node_type == + CookieTreeNode::DetailedInfo::TYPE_COOKIE) + return node->GetDetailedInfo().cookie->second.Name() + ","; + else + return ""; + } + } + // Get the cookie names displayed in the view (if we had one) in the order + // they are displayed, as a comma seperated string. + // Ex: EXPECT_STREQ("X,Y", GetDisplayedCookies(cookies_view).c_str()); + std::string GetDisplayedCookies(CookiesTreeModel* cookies_model) { + CookieTreeRootNode* root = static_cast<CookieTreeRootNode*>( + cookies_model->GetRoot()); + std::string retval = GetCookiesOfChildren(root); + if (retval.length() && retval[retval.length() - 1] == ',') + retval.erase(retval.length() - 1); + return retval; + } + + // do not call on the root + void DeleteCookie(CookieTreeNode* node) { + node->DeleteStoredObjects(); + // find the parent and index + CookieTreeNode* parent_node = node->GetParent(); + DCHECK(parent_node); + int ct_node_index = parent_node->IndexOfChild(node); + delete parent_node->GetModel()->Remove(parent_node, ct_node_index); + } + protected: + MessageLoopForUI message_loop_; + scoped_ptr<CookieTestingProfile> profile_; +}; + + +TEST_F(CookiesTreeModelTest, RemoveAll) { + net::CookieMonster* monster = profile_->GetCookieMonster(); + monster->SetCookie(GURL("http://foo"), "A=1"); + monster->SetCookie(GURL("http://foo2"), "B=1"); + CookiesTreeModel cookies_model(profile_.get()); + + // Reset the selection of the first row. + { + SCOPED_TRACE("Before removing"); + EXPECT_EQ(GetMonsterCookies(monster), GetDisplayedCookies(&cookies_model)); + } + + cookies_model.DeleteAllCookies(); + { + SCOPED_TRACE("After removing"); + EXPECT_EQ(1, cookies_model.GetRoot()->GetTotalNodeCount()); + EXPECT_EQ(0, cookies_model.GetRoot()->GetChildCount()); + EXPECT_EQ(std::string(""), GetMonsterCookies(monster)); + EXPECT_EQ(GetMonsterCookies(monster), GetDisplayedCookies(&cookies_model)); + } +} + +TEST_F(CookiesTreeModelTest, Remove) { + net::CookieMonster* monster = profile_->GetCookieMonster(); + monster->SetCookie(GURL("http://foo1"), "A=1"); + monster->SetCookie(GURL("http://foo2"), "B=1"); + monster->SetCookie(GURL("http://foo3"), "C=1"); + CookiesTreeModel cookies_model(profile_.get()); + + { + SCOPED_TRACE("Initial State 3 cookies"); + // 10 because there's the root, then foo1 -> cookies -> a, + // foo2 -> cookies -> b, foo3 -> cookies -> c + EXPECT_EQ(10, cookies_model.GetRoot()->GetTotalNodeCount()); + } + DeleteCookie(cookies_model.GetRoot()->GetChild(0)); + { + SCOPED_TRACE("First origin removed"); + EXPECT_STREQ("B,C", GetMonsterCookies(monster).c_str()); + EXPECT_STREQ("B,C", GetDisplayedCookies(&cookies_model).c_str()); + EXPECT_EQ(7, cookies_model.GetRoot()->GetTotalNodeCount()); + } +} + +TEST_F(CookiesTreeModelTest, RemoveCookiesNode) { + net::CookieMonster* monster = profile_->GetCookieMonster(); + monster->SetCookie(GURL("http://foo1"), "A=1"); + monster->SetCookie(GURL("http://foo2"), "B=1"); + monster->SetCookie(GURL("http://foo3"), "C=1"); + CookiesTreeModel cookies_model(profile_.get()); + + { + SCOPED_TRACE("Initial State 3 cookies"); + // 10 because there's the root, then foo1 -> cookies -> a, + // foo2 -> cookies -> b, foo3 -> cookies -> c + EXPECT_EQ(10, cookies_model.GetRoot()->GetTotalNodeCount()); + } + DeleteCookie(cookies_model.GetRoot()->GetChild(0)->GetChild(0)); + { + SCOPED_TRACE("First origin removed"); + EXPECT_STREQ("B,C", GetMonsterCookies(monster).c_str()); + EXPECT_STREQ("B,C", GetDisplayedCookies(&cookies_model).c_str()); + // 8 because in this case, the origin remains, although the COOKIES + // node beneath it has been deleted. So, we have + // root -> foo1 -> cookies -> a, foo2, foo3 -> cookies -> c + EXPECT_EQ(8, cookies_model.GetRoot()->GetTotalNodeCount()); + } +} + +TEST_F(CookiesTreeModelTest, RemoveCookieNode) { + net::CookieMonster* monster = profile_->GetCookieMonster(); + monster->SetCookie(GURL("http://foo1"), "A=1"); + monster->SetCookie(GURL("http://foo2"), "B=1"); + monster->SetCookie(GURL("http://foo3"), "C=1"); + CookiesTreeModel cookies_model(profile_.get()); + + { + SCOPED_TRACE("Initial State 3 cookies"); + // 10 because there's the root, then foo1 -> cookies -> a, + // foo2 -> cookies -> b, foo3 -> cookies -> c + EXPECT_EQ(10, cookies_model.GetRoot()->GetTotalNodeCount()); + } + DeleteCookie(cookies_model.GetRoot()->GetChild(1)->GetChild(0)); + { + SCOPED_TRACE("Second origin COOKIES node removed"); + EXPECT_STREQ("A,C", GetMonsterCookies(monster).c_str()); + EXPECT_STREQ("A,C", GetDisplayedCookies(&cookies_model).c_str()); + // 8 because in this case, the origin remains, although the COOKIES + // node beneath it has been deleted. So, we have + // root -> foo1 -> cookies -> a, foo2, foo3 -> cookies -> c + EXPECT_EQ(8, cookies_model.GetRoot()->GetTotalNodeCount()); + } +} + +TEST_F(CookiesTreeModelTest, RemoveSingleCookieNode) { + net::CookieMonster* monster = profile_->GetCookieMonster(); + monster->SetCookie(GURL("http://foo1"), "A=1"); + monster->SetCookie(GURL("http://foo2"), "B=1"); + monster->SetCookie(GURL("http://foo3"), "C=1"); + monster->SetCookie(GURL("http://foo3"), "D=1"); + CookiesTreeModel cookies_model(profile_.get()); + + { + SCOPED_TRACE("Initial State 4 cookies"); + // 11 because there's the root, then foo1 -> cookies -> a, + // foo2 -> cookies -> b, foo3 -> cookies -> c,d + EXPECT_EQ(11, cookies_model.GetRoot()->GetTotalNodeCount()); + EXPECT_STREQ("A,B,C,D", GetMonsterCookies(monster).c_str()); + EXPECT_STREQ("A,B,C,D", GetDisplayedCookies(&cookies_model).c_str()); + } + DeleteCookie(cookies_model.GetRoot()->GetChild(2)); + { + SCOPED_TRACE("Third origin removed"); + EXPECT_STREQ("A,B", GetMonsterCookies(monster).c_str()); + EXPECT_STREQ("A,B", GetDisplayedCookies(&cookies_model).c_str()); + EXPECT_EQ(7, cookies_model.GetRoot()->GetTotalNodeCount()); + } +} + +TEST_F(CookiesTreeModelTest, RemoveSingleCookieNodeOf3) { + net::CookieMonster* monster = profile_->GetCookieMonster(); + monster->SetCookie(GURL("http://foo1"), "A=1"); + monster->SetCookie(GURL("http://foo2"), "B=1"); + monster->SetCookie(GURL("http://foo3"), "C=1"); + monster->SetCookie(GURL("http://foo3"), "D=1"); + monster->SetCookie(GURL("http://foo3"), "E=1"); + CookiesTreeModel cookies_model(profile_.get()); + + { + SCOPED_TRACE("Initial State 5 cookies"); + // 11 because there's the root, then foo1 -> cookies -> a, + // foo2 -> cookies -> b, foo3 -> cookies -> c,d,e + EXPECT_EQ(12, cookies_model.GetRoot()->GetTotalNodeCount()); + EXPECT_STREQ("A,B,C,D,E", GetMonsterCookies(monster).c_str()); + EXPECT_STREQ("A,B,C,D,E", GetDisplayedCookies(&cookies_model).c_str()); + } + DeleteCookie(cookies_model.GetRoot()->GetChild(2)->GetChild(0)-> + GetChild(1)); + { + SCOPED_TRACE("Middle cookie in third origin removed"); + EXPECT_STREQ("A,B,C,E", GetMonsterCookies(monster).c_str()); + EXPECT_STREQ("A,B,C,E", GetDisplayedCookies(&cookies_model).c_str()); + EXPECT_EQ(11, cookies_model.GetRoot()->GetTotalNodeCount()); + } +} + +TEST_F(CookiesTreeModelTest, RemoveSecondOrigin) { + net::CookieMonster* monster = profile_->GetCookieMonster(); + monster->SetCookie(GURL("http://foo1"), "A=1"); + monster->SetCookie(GURL("http://foo2"), "B=1"); + monster->SetCookie(GURL("http://foo3"), "C=1"); + monster->SetCookie(GURL("http://foo3"), "D=1"); + monster->SetCookie(GURL("http://foo3"), "E=1"); + CookiesTreeModel cookies_model(profile_.get()); + + { + SCOPED_TRACE("Initial State 5 cookies"); + // 11 because there's the root, then foo1 -> cookies -> a, + // foo2 -> cookies -> b, foo3 -> cookies -> c,d,e + EXPECT_EQ(12, cookies_model.GetRoot()->GetTotalNodeCount()); + EXPECT_STREQ("A,B,C,D,E", GetMonsterCookies(monster).c_str()); + EXPECT_STREQ("A,B,C,D,E", GetDisplayedCookies(&cookies_model).c_str()); + } + DeleteCookie(cookies_model.GetRoot()->GetChild(1)); + { + SCOPED_TRACE("Second origin removed"); + EXPECT_STREQ("A,C,D,E", GetMonsterCookies(monster).c_str()); + EXPECT_STREQ("A,C,D,E", GetDisplayedCookies(&cookies_model).c_str()); + // Left with root -> foo1 -> cookies -> a, foo3 -> cookies -> c,d,e + EXPECT_EQ(9, cookies_model.GetRoot()->GetTotalNodeCount()); + } +} + +} // namespace diff --git a/chrome/browser/views/options/advanced_contents_view.cc b/chrome/browser/views/options/advanced_contents_view.cc index 3359783..a6d62a1 100644 --- a/chrome/browser/views/options/advanced_contents_view.cc +++ b/chrome/browser/views/options/advanced_contents_view.cc @@ -656,7 +656,8 @@ void PrivacySection::InitControlLayout() { allow_cookies_model_.get()); cookie_behavior_combobox_->set_listener(this); show_cookies_button_ = new views::NativeButton( - this, l10n_util::GetString(IDS_OPTIONS_COOKIES_SHOWCOOKIES)); + this, l10n_util::GetString( + IDS_OPTIONS_COOKIES_SHOWCOOKIES_WEBSITE_PERMISSIONS)); GridLayout* layout = new GridLayout(contents_); contents_->SetLayoutManager(layout); diff --git a/chrome/browser/views/options/cookies_view.cc b/chrome/browser/views/options/cookies_view.cc index 924480c..2fe5ac7 100644 --- a/chrome/browser/views/options/cookies_view.cc +++ b/chrome/browser/views/options/cookies_view.cc @@ -12,7 +12,7 @@ #include "base/i18n/time_formatting.h" #include "base/message_loop.h" #include "base/string_util.h" -#include "chrome/browser/cookies_table_model.h" +#include "chrome/browser/cookies_tree_model.h" #include "chrome/browser/profile.h" #include "grit/generated_resources.h" #include "grit/locale_settings.h" @@ -21,7 +21,7 @@ #include "views/grid_layout.h" #include "views/controls/label.h" #include "views/controls/button/native_button.h" -#include "views/controls/table/table_view.h" +#include "views/controls/tree/tree_view.h" #include "views/controls/textfield/textfield.h" #include "views/standard_layout.h" @@ -29,116 +29,42 @@ views::Window* CookiesView::instance_ = NULL; static const int kCookieInfoViewBorderSize = 1; static const int kCookieInfoViewInsetSize = 3; -static const int kSearchFilterDelayMs = 500; + /////////////////////////////////////////////////////////////////////////////// -// CookiesTableView +// CookiesTreeView // Overridden to handle Delete key presses -class CookiesTableView : public views::TableView { +class CookiesTreeView : public views::TreeView { public: - CookiesTableView(CookiesTableModel* cookies_model, - std::vector<TableColumn> columns); - virtual ~CookiesTableView() {} + explicit CookiesTreeView(CookiesTreeModel* cookies_model); + virtual ~CookiesTreeView() {} - // Removes the cookies associated with the selected rows in the TableView. - void RemoveSelectedCookies(); + // Removes the items associated with the selected node in the TreeView + void RemoveSelectedItems(); private: - // Our model, as a CookiesTableModel. - CookiesTableModel* cookies_model_; + // Our model, as a CookiesTreeModel. + CookiesTreeModel* cookies_model_; - DISALLOW_COPY_AND_ASSIGN(CookiesTableView); + DISALLOW_COPY_AND_ASSIGN(CookiesTreeView); }; -CookiesTableView::CookiesTableView( - CookiesTableModel* cookies_model, - std::vector<TableColumn> columns) - : views::TableView(cookies_model, columns, views::ICON_AND_TEXT, false, - true, true), - cookies_model_(cookies_model) { +CookiesTreeView::CookiesTreeView(CookiesTreeModel* cookies_model) + : cookies_model_(cookies_model) { + SetModel(cookies_model_); + SetRootShown(false); + SetEditable(false); } -void CookiesTableView::RemoveSelectedCookies() { - // It's possible that we don't have anything selected. - if (SelectedRowCount() <= 0) - return; - - if (SelectedRowCount() == cookies_model_->RowCount()) { - cookies_model_->RemoveAllShownCookies(); - return; - } - - // Remove the selected cookies. This iterates over the rows backwards, which - // is required when calling RemoveCookies, see bug 2994. - int last_selected_view_row = -1; - int remove_count = 0; - for (views::TableView::iterator i = SelectionBegin(); - i != SelectionEnd(); ++i) { - int selected_model_row = *i; - ++remove_count; - if (last_selected_view_row == -1) { - // Store the view row since the view to model mapping changes when - // we delete. - last_selected_view_row = model_to_view(selected_model_row); - } - cookies_model_->RemoveCookies(selected_model_row, 1); +void CookiesTreeView::RemoveSelectedItems() { + TreeModelNode* selected_node = GetSelectedNode(); + if (selected_node) { + cookies_model_->DeleteCookieNode(static_cast<CookieTreeCookieNode*>( + GetSelectedNode())); } - - // Select the next row after the last row deleted (unless removing last row). - DCHECK(RowCount() > 0 && last_selected_view_row != -1); - Select(view_to_model(std::min(RowCount() - 1, - last_selected_view_row - remove_count + 1))); } -/////////////////////////////////////////////////////////////////////////////// -// CookieInfoView -// -// Responsible for displaying a tabular grid of Cookie information. -class CookieInfoView : public views::View { - public: - CookieInfoView(); - virtual ~CookieInfoView(); - - // Update the display from the specified CookieNode. - void SetCookie(const std::string& domain, - const net::CookieMonster::CanonicalCookie& cookie_node); - - // Clears the cookie display to indicate that no or multiple cookies are - // selected. - void ClearCookieDisplay(); - - // Enables or disables the cookie proerty text fields. - void EnableCookieDisplay(bool enabled); - - protected: - // views::View overrides: - virtual void ViewHierarchyChanged(bool is_add, - views::View* parent, - views::View* child); - - private: - // Set up the view layout - void Init(); - - // Individual property labels - views::Label* name_label_; - views::Textfield* name_value_field_; - views::Label* content_label_; - views::Textfield* content_value_field_; - views::Label* domain_label_; - views::Textfield* domain_value_field_; - views::Label* path_label_; - views::Textfield* path_value_field_; - views::Label* send_for_label_; - views::Textfield* send_for_value_field_; - views::Label* created_label_; - views::Textfield* created_value_field_; - views::Label* expires_label_; - views::Textfield* expires_value_field_; - - DISALLOW_COPY_AND_ASSIGN(CookieInfoView); -}; /////////////////////////////////////////////////////////////////////////////// // CookieInfoView, public: @@ -347,12 +273,7 @@ void CookiesView::ShowCookiesWindow(Profile* profile) { } CookiesView::~CookiesView() { - cookies_table_->SetModel(NULL); -} - -void CookiesView::UpdateSearchResults() { - cookies_table_model_->UpdateSearchResults(search_field_->text()); - remove_all_button_->SetEnabled(cookies_table_model_->RowCount() > 0); + cookies_tree_->SetModel(NULL); } /////////////////////////////////////////////////////////////////////////////// @@ -361,67 +282,18 @@ void CookiesView::UpdateSearchResults() { void CookiesView::ButtonPressed( views::Button* sender, const views::Event& event) { if (sender == remove_button_) { - cookies_table_->RemoveSelectedCookies(); + cookies_tree_->RemoveSelectedItems(); } else if (sender == remove_all_button_) { - // Delete all the Cookies shown. - cookies_table_model_->RemoveAllShownCookies(); - UpdateForEmptyState(); - } else if (sender == clear_search_button_) { - ResetSearchQuery(); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// CookiesView, views::TableViewObserver implementation: -void CookiesView::OnSelectionChanged() { - int selected_row_count = cookies_table_->SelectedRowCount(); - if (selected_row_count == 1) { - int selected_index = cookies_table_->FirstSelectedRow(); - if (selected_index >= 0 && - selected_index < cookies_table_model_->RowCount()) { - info_view_->SetCookie(cookies_table_model_->GetDomainAt(selected_index), - cookies_table_model_->GetCookieAt(selected_index)); - } - } else { - info_view_->ClearCookieDisplay(); - } - remove_button_->SetEnabled(selected_row_count != 0); - if (cookies_table_->RowCount() == 0) + cookies_tree_model_->DeleteAllCookies(); UpdateForEmptyState(); -} - -void CookiesView::OnTableViewDelete(views::TableView* table_view) { - cookies_table_->RemoveSelectedCookies(); -} - -/////////////////////////////////////////////////////////////////////////////// -// CookiesView, views::Textfield::Controller implementation: - -void CookiesView::ContentsChanged(views::Textfield* sender, - const std::wstring& new_contents) { - clear_search_button_->SetEnabled(!search_field_->text().empty()); - search_update_factory_.RevokeAll(); - MessageLoop::current()->PostDelayedTask(FROM_HERE, - search_update_factory_.NewRunnableMethod( - &CookiesView::UpdateSearchResults), kSearchFilterDelayMs); -} - -bool CookiesView::HandleKeystroke(views::Textfield* sender, - const views::Textfield::Keystroke& key) { - if (key.GetKeyboardCode() == base::VKEY_ESCAPE) { - ResetSearchQuery(); - } else if (key.GetKeyboardCode() == base::VKEY_RETURN) { - search_update_factory_.RevokeAll(); - UpdateSearchResults(); } - return false; } /////////////////////////////////////////////////////////////////////////////// // CookiesView, views::DialogDelegate implementation: std::wstring CookiesView::GetWindowTitle() const { - return l10n_util::GetString(IDS_COOKIES_WINDOW_TITLE); + return l10n_util::GetString(IDS_COOKIES_WEBSITE_PERMISSIONS_WINDOW_TITLE); } void CookiesView::WindowClosing() { @@ -468,50 +340,49 @@ void CookiesView::ViewHierarchyChanged(bool is_add, } /////////////////////////////////////////////////////////////////////////////// +// CookiesView, views::TreeViewController overrides: + +void CookiesView::OnTreeViewSelectionChanged(views::TreeView* tree_view) { + CookieTreeNode::DetailedInfo detailed_info = + static_cast<CookieTreeNode*>(tree_view->GetSelectedNode())-> + GetDetailedInfo(); + if (detailed_info.node_type == CookieTreeNode::DetailedInfo::TYPE_COOKIE) { + info_view_->SetCookie(detailed_info.cookie->first, + detailed_info.cookie->second); + } else { + info_view_->ClearCookieDisplay(); + } +} + +void CookiesView::OnTreeViewKeyDown(base::KeyboardCode keycode) { + if (keycode == base::VKEY_DELETE) + cookies_tree_->RemoveSelectedItems(); +} + +/////////////////////////////////////////////////////////////////////////////// // CookiesView, private: CookiesView::CookiesView(Profile* profile) - : search_label_(NULL), - search_field_(NULL), - clear_search_button_(NULL), + : description_label_(NULL), - cookies_table_(NULL), + cookies_tree_(NULL), info_view_(NULL), remove_button_(NULL), remove_all_button_(NULL), - profile_(profile), - ALLOW_THIS_IN_INITIALIZER_LIST(search_update_factory_(this)) { + profile_(profile) { +} + +views::View* CookiesView::GetInitiallyFocusedView() { + return cookies_tree_; } void CookiesView::Init() { - search_label_ = new views::Label( - l10n_util::GetString(IDS_COOKIES_SEARCH_LABEL)); - search_field_ = new views::Textfield; - search_field_->SetController(this); - clear_search_button_ = new views::NativeButton( - this, l10n_util::GetString(IDS_COOKIES_CLEAR_SEARCH_LABEL)); - clear_search_button_->SetEnabled(false); description_label_ = new views::Label( l10n_util::GetString(IDS_COOKIES_INFO_LABEL)); description_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); - - cookies_table_model_.reset(new CookiesTableModel(profile_)); + cookies_tree_model_.reset(new CookiesTreeModel(profile_)); info_view_ = new CookieInfoView; - std::vector<TableColumn> columns; - columns.push_back(TableColumn(IDS_COOKIES_DOMAIN_COLUMN_HEADER, - TableColumn::LEFT, 200, 0.5f)); - columns.back().sortable = true; - columns.push_back(TableColumn(IDS_COOKIES_NAME_COLUMN_HEADER, - TableColumn::LEFT, 150, 0.5f)); - columns.back().sortable = true; - cookies_table_ = new CookiesTableView(cookies_table_model_.get(), columns); - cookies_table_->SetObserver(this); - // Make the table initially sorted by domain. - views::TableView::SortDescriptors sort; - sort.push_back( - views::TableView::SortDescriptor(IDS_COOKIES_DOMAIN_COLUMN_HEADER, - true)); - cookies_table_->SetSortDescriptors(sort); + cookies_tree_ = new CookiesTreeView(cookies_tree_model_.get()); remove_button_ = new views::NativeButton( this, l10n_util::GetString(IDS_COOKIES_REMOVE_LABEL)); remove_all_button_ = new views::NativeButton( @@ -539,16 +410,16 @@ void CookiesView::Init() { column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, GridLayout::USE_PREF, 0, 0); - layout->StartRow(0, five_column_layout_id); - layout->AddView(search_label_); - layout->AddView(search_field_); - layout->AddView(clear_search_button_); - layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing); layout->StartRow(0, single_column_layout_id); layout->AddView(description_label_); + layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); layout->StartRow(1, single_column_layout_id); - layout->AddView(cookies_table_); + layout->AddView(cookies_tree_); + cookies_tree_->ExpandAll(); + + cookies_tree_->SetController(this); + layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); layout->StartRow(0, single_column_layout_id); layout->AddView(info_view_); @@ -557,18 +428,8 @@ void CookiesView::Init() { View* parent = GetParent(); parent->AddChildView(remove_button_); parent->AddChildView(remove_all_button_); - - if (cookies_table_->RowCount() > 0) { - cookies_table_->Select(0); - } else { + if (!cookies_tree_model_.get()->GetRoot()->GetChildCount()) UpdateForEmptyState(); - } -} - -void CookiesView::ResetSearchQuery() { - search_field_->SetText(EmptyWString()); - clear_search_button_->SetEnabled(false); - UpdateSearchResults(); } void CookiesView::UpdateForEmptyState() { diff --git a/chrome/browser/views/options/cookies_view.h b/chrome/browser/views/options/cookies_view.h index 23e28ba..7932b45 100644 --- a/chrome/browser/views/options/cookies_view.h +++ b/chrome/browser/views/options/cookies_view.h @@ -5,9 +5,12 @@ #ifndef CHROME_BROWSER_VIEWS_OPTIONS_COOKIES_VIEW_H_ #define CHROME_BROWSER_VIEWS_OPTIONS_COOKIES_VIEW_H_ +#include <string> + #include "base/task.h" +#include "net/base/cookie_monster.h" #include "views/controls/button/button.h" -#include "views/controls/table/table_view_observer.h" +#include "views/controls/tree/tree_view.h" #include "views/controls/textfield/textfield.h" #include "views/view.h" #include "views/window/dialog_delegate.h" @@ -17,53 +20,42 @@ namespace views { class Label; class NativeButton; -class TableView; } // namespace views + class CookieInfoView; -class CookiesTableModel; -class CookiesTableView; +class CookiesTreeModel; +class CookiesTreeView; class Profile; class Timer; + class CookiesView : public views::View, public views::DialogDelegate, public views::ButtonListener, - public views::TableViewObserver, - public views::Textfield::Controller { + public views::TreeViewController { public: // Show the Cookies Window, creating one if necessary. static void ShowCookiesWindow(Profile* profile); virtual ~CookiesView(); - // Updates the display to show only the search results. - void UpdateSearchResults(); - // views::ButtonListener implementation. virtual void ButtonPressed(views::Button* sender, const views::Event& event); - // views::TableViewObserver implementation. - virtual void OnSelectionChanged(); + // views::TreeViewController implementation. + virtual void OnTreeViewSelectionChanged(views::TreeView* tree_view); - // Invoked when the user presses the delete key. Deletes the selected - // cookies. - virtual void OnTableViewDelete(views::TableView* table_view); - - // views::Textfield::Controller implementation. - virtual void ContentsChanged(views::Textfield* sender, - const std::wstring& new_contents); - virtual bool HandleKeystroke(views::Textfield* sender, - const views::Textfield::Keystroke& key); + // views::TreeViewController implementation. + virtual void OnTreeViewKeyDown(base::KeyboardCode keycode); // views::WindowDelegate implementation. virtual int GetDialogButtons() const { return MessageBoxFlags::DIALOGBUTTON_CANCEL; } - virtual views::View* GetInitiallyFocusedView() { - return search_field_; - } + virtual views::View* GetInitiallyFocusedView(); + virtual bool CanResize() const { return true; } virtual std::wstring GetWindowTitle() const; virtual void WindowClosing(); @@ -86,32 +78,22 @@ class CookiesView : public views::View, // Initialize the dialog contents and layout. void Init(); - // Resets the display to what it would be if there were no search query. - void ResetSearchQuery(); - // Update the UI when there are no cookies. void UpdateForEmptyState(); // Assorted dialog controls - views::Label* search_label_; - views::Textfield* search_field_; - views::NativeButton* clear_search_button_; views::Label* description_label_; - CookiesTableView* cookies_table_; + CookiesTreeView* cookies_tree_; CookieInfoView* info_view_; views::NativeButton* remove_button_; views::NativeButton* remove_all_button_; - // The Cookies Table model - scoped_ptr<CookiesTableModel> cookies_table_model_; + // The Cookies Tree model + scoped_ptr<CookiesTreeModel> cookies_tree_model_; // The Profile for which Cookies are displayed Profile* profile_; - // A factory to construct Runnable Methods so that we can be called back to - // re-evaluate the model after the search query string changes. - ScopedRunnableMethodFactory<CookiesView> search_update_factory_; - // Our containing window. If this is non-NULL there is a visible Cookies // window somewhere. static views::Window* instance_; @@ -119,4 +101,53 @@ class CookiesView : public views::View, DISALLOW_COPY_AND_ASSIGN(CookiesView); }; +/////////////////////////////////////////////////////////////////////////////// +// CookieInfoView +// +// Responsible for displaying a tabular grid of Cookie information. +class CookieInfoView : public views::View { + public: + CookieInfoView(); + virtual ~CookieInfoView(); + + // Update the display from the specified CookieNode. + void SetCookie(const std::string& domain, + const net::CookieMonster::CanonicalCookie& cookie_node); + + // Clears the cookie display to indicate that no or multiple cookies are + // selected. + void ClearCookieDisplay(); + + // Enables or disables the cookie proerty text fields. + void EnableCookieDisplay(bool enabled); + + protected: + // views::View overrides: + virtual void ViewHierarchyChanged(bool is_add, + views::View* parent, + views::View* child); + + private: + // Set up the view layout + void Init(); + + // Individual property labels + views::Label* name_label_; + views::Textfield* name_value_field_; + views::Label* content_label_; + views::Textfield* content_value_field_; + views::Label* domain_label_; + views::Textfield* domain_value_field_; + views::Label* path_label_; + views::Textfield* path_value_field_; + views::Label* send_for_label_; + views::Textfield* send_for_value_field_; + views::Label* created_label_; + views::Textfield* created_value_field_; + views::Label* expires_label_; + views::Textfield* expires_value_field_; + + DISALLOW_COPY_AND_ASSIGN(CookieInfoView); +}; + #endif // CHROME_BROWSER_VIEWS_OPTIONS_COOKIES_VIEW_H_ diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 3ab5e1e..90193bb 100755 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -1262,6 +1262,8 @@ 'browser/command_updater.h', 'browser/cookies_table_model.cc', 'browser/cookies_table_model.h', + 'browser/cookies_tree_model.cc', + 'browser/cookies_tree_model.h', 'browser/cross_site_request_manager.cc', 'browser/cross_site_request_manager.h', 'browser/defaults.cc', @@ -4605,6 +4607,7 @@ 'browser/cocoa/view_resizer_pong.mm', 'browser/cocoa/web_drop_target_unittest.mm', 'browser/command_updater_unittest.cc', + 'browser/cookies_tree_model_unittest.cc', 'browser/debugger/devtools_manager_unittest.cc', 'browser/dom_ui/dom_ui_theme_source_unittest.cc', 'browser/dom_ui/dom_ui_unittest.cc', diff --git a/views/controls/tree/tree_view.cc b/views/controls/tree/tree_view.cc index 5d39241..957007c 100644 --- a/views/controls/tree/tree_view.cc +++ b/views/controls/tree/tree_view.cc @@ -6,7 +6,9 @@ #include <vector> +#include "app/gfx/canvas.h" #include "app/gfx/canvas_paint.h" +#include "app/gfx/favicon_size.h" #include "app/gfx/icon_util.h" #include "app/l10n_util.h" #include "app/l10n_util_win.h" @@ -653,7 +655,24 @@ HIMAGELIST TreeView::CreateImageList() { DestroyIcon(h_closed_icon); DestroyIcon(h_opened_icon); for (size_t i = 0; i < model_images.size(); ++i) { - HICON model_icon = IconUtil::CreateHICONFromSkBitmap(model_images[i]); + HICON model_icon; + + // Need to resize the provided icons to be the same size as + // IDR_FOLDER_CLOSED if they aren't already. + if (model_images[i].width() != width || + model_images[i].height() != height) { + gfx::Canvas canvas(width, height, false); + // Make the background completely transparent. + canvas.drawColor(SK_ColorBLACK, SkXfermode::kClear_Mode); + + // Draw our icons into this canvas. + int height_offset = (height - model_images[i].height()) / 2; + int width_offset = (width - model_images[i].width()) / 2; + canvas.DrawBitmapInt(model_images[i], width_offset, height_offset); + model_icon = IconUtil::CreateHICONFromSkBitmap(canvas.ExtractBitmap()); + } else { + model_icon = IconUtil::CreateHICONFromSkBitmap(model_images[i]); + } ImageList_AddIcon(image_list, model_icon); DestroyIcon(model_icon); } |