diff options
author | bulach@chromium.org <bulach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-25 13:50:48 +0000 |
---|---|---|
committer | bulach@chromium.org <bulach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-25 13:50:48 +0000 |
commit | 8f9aefdd9324680937c83b2137ecba8584322245 (patch) | |
tree | 7ace3e1ab52d41b24f8f268c8e29592253019ac9 /chrome | |
parent | 099fdcd5ff79ae6815f052060a605ad9a3454ad5 (diff) | |
download | chromium_src-8f9aefdd9324680937c83b2137ecba8584322245.zip chromium_src-8f9aefdd9324680937c83b2137ecba8584322245.tar.gz chromium_src-8f9aefdd9324680937c83b2137ecba8584322245.tar.bz2 |
Adds local storage nodes to cookie tree model and cookies view.
BUG=none
TEST=The show cookie dialog box should have a new node "local storage" when appropriate. When selected, it should display details of local storage (name, size on disk, last modified) in the details frame.
Review URL: http://codereview.chromium.org/523139
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@37001 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
23 files changed, 1610 insertions, 232 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 3ca3859..0f7dca4 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -5193,6 +5193,15 @@ Keep your key file in a safe place. You will need it to create new versions of y <message name="IDS_COOKIES_DOMAIN_COLUMN_HEADER" desc="The label of the Domain header in the Cookies table"> Site </message> + <message name="IDS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL" desc="The Local Storage Origin label"> + Origin: + </message> + <message name="IDS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL" desc="The Local Storage Size on disk label"> + Size on disk: + </message> + <message name="IDS_COOKIES_LOCAL_STORAGE_LAST_MODIFIED_LABEL" desc="The Local Storage Last modified label"> + Last modified: + </message> <message name="IDS_COOKIES_NAME_COLUMN_HEADER" desc="The label of the Cookie Name header in the Cookies table"> Cookie Name </message> diff --git a/chrome/browser/browsing_data_local_storage_helper.cc b/chrome/browser/browsing_data_local_storage_helper.cc new file mode 100644 index 0000000..d203563 --- /dev/null +++ b/chrome/browser/browsing_data_local_storage_helper.cc @@ -0,0 +1,125 @@ +// 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/browsing_data_local_storage_helper.h" + +#include "base/file_util.h" +#include "base/message_loop.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/in_process_webkit/webkit_context.h" +#include "chrome/browser/profile.h" +#include "third_party/WebKit/WebKit/chromium/public/WebSecurityOrigin.h" +#include "third_party/WebKit/WebKit/chromium/public/WebString.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webkit_glue.h" + +BrowsingDataLocalStorageHelper::BrowsingDataLocalStorageHelper( + Profile* profile) + : profile_(profile), + completion_callback_(NULL), + is_fetching_(false) { + DCHECK(profile_); +} + +void BrowsingDataLocalStorageHelper::StartFetching( + Callback1<const std::vector<LocalStorageInfo>& >::Type* callback) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + DCHECK(!is_fetching_); + DCHECK(callback); + is_fetching_ = true; + completion_callback_.reset(callback); + ChromeThread::PostTask( + ChromeThread::WEBKIT, FROM_HERE, + NewRunnableMethod( + this, + &BrowsingDataLocalStorageHelper:: + FetchLocalStorageInfoInWebKitThread)); +} + +void BrowsingDataLocalStorageHelper::CancelNotification() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + completion_callback_.reset(NULL); +} + +void BrowsingDataLocalStorageHelper::DeleteLocalStorageFile( + const FilePath& file_path) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + ChromeThread::PostTask( + ChromeThread::WEBKIT, FROM_HERE, + NewRunnableMethod( + this, + &BrowsingDataLocalStorageHelper:: + DeleteLocalStorageFileInWebKitThread, + file_path)); +} + +void BrowsingDataLocalStorageHelper::DeleteAllLocalStorageFiles() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + ChromeThread::PostTask( + ChromeThread::WEBKIT, FROM_HERE, + NewRunnableMethod( + this, + &BrowsingDataLocalStorageHelper:: + DeleteAllLocalStorageFilesInWebKitThread)); +} + +void BrowsingDataLocalStorageHelper::FetchLocalStorageInfoInWebKitThread() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::WEBKIT)); + file_util::FileEnumerator file_enumerator( + profile_->GetWebKitContext()->data_path().Append( + DOMStorageContext::kLocalStorageDirectory), + false, file_util::FileEnumerator::FILES); + for (FilePath file_path = file_enumerator.Next(); !file_path.empty(); + file_path = file_enumerator.Next()) { + if (file_path.Extension() == DOMStorageContext::kLocalStorageExtension) { + scoped_ptr<WebKit::WebSecurityOrigin> web_security_origin( + WebKit::WebSecurityOrigin::createFromDatabaseIdentifier( + webkit_glue::FilePathToWebString(file_path.BaseName()))); + file_util::FileInfo file_info; + bool ret = file_util::GetFileInfo(file_path, &file_info); + if (ret) { + local_storage_info_.push_back(LocalStorageInfo( + webkit_glue::WebStringToStdString(web_security_origin->protocol()), + webkit_glue::WebStringToStdString(web_security_origin->host()), + web_security_origin->port(), + webkit_glue::WebStringToStdString( + web_security_origin->databaseIdentifier()), + webkit_glue::WebStringToStdString( + web_security_origin->toString()), + file_path, + file_info.size, + file_info.last_modified)); + } + } + } + + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod( + this, &BrowsingDataLocalStorageHelper::NotifyInUIThread)); +} + +void BrowsingDataLocalStorageHelper::NotifyInUIThread() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + DCHECK(is_fetching_); + // Note: completion_callback_ mutates only in the UI thread, so it's safe to + // test it here. + if (completion_callback_ != NULL) + completion_callback_->Run(local_storage_info_); + is_fetching_ = false; +} + +void BrowsingDataLocalStorageHelper::DeleteLocalStorageFileInWebKitThread( + const FilePath& file_path) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::WEBKIT)); + profile_->GetWebKitContext()->dom_storage_context()->DeleteLocalStorageFile( + file_path); +} + +void + BrowsingDataLocalStorageHelper::DeleteAllLocalStorageFilesInWebKitThread() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::WEBKIT)); + profile_->GetWebKitContext()->dom_storage_context() + ->DeleteAllLocalStorageFiles(); +} diff --git a/chrome/browser/browsing_data_local_storage_helper.h b/chrome/browser/browsing_data_local_storage_helper.h new file mode 100644 index 0000000..f9d9398d --- /dev/null +++ b/chrome/browser/browsing_data_local_storage_helper.h @@ -0,0 +1,98 @@ +// 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_BROWSING_DATA_LOCAL_STORAGE_HELPER_H_ +#define CHROME_BROWSER_BROWSING_DATA_LOCAL_STORAGE_HELPER_H_ + +#include <string> + +#include "base/file_path.h" +#include "base/scoped_ptr.h" +#include "base/task.h" + +class Profile; + +// This class fetches local storage information in the WebKit thread, and +// notifies the UI thread upon completion. +// A client of this class need to call StartFetching from the UI thread to +// initiate the flow, and it'll be notified by the callback in its UI +// thread at some later point. +// The client must call CancelNotification() if it's destroyed before the +// callback is notified. +class BrowsingDataLocalStorageHelper + : public base::RefCountedThreadSafe<BrowsingDataLocalStorageHelper> { + public: + // Contains detailed information about local storage. + struct LocalStorageInfo { + LocalStorageInfo( + const std::string& protocol, + const std::string& host, + unsigned short port, + const std::string& database_identifier, + const std::string& origin, + const FilePath& file_path, + int64 size, + base::Time last_modified) + : protocol(protocol), + host(host), + port(port), + database_identifier(database_identifier), + origin(origin), + file_path(file_path), + size(size), + last_modified(last_modified) { + } + + std::string protocol; + std::string host; + unsigned short port; + std::string database_identifier; + std::string origin; + FilePath file_path; + int64 size; + base::Time last_modified; + }; + + explicit BrowsingDataLocalStorageHelper(Profile* profile); + + // Starts the fetching process, which will notify its completion via + // callback. + // This must be called only in the UI thread. + virtual void StartFetching( + Callback1<const std::vector<LocalStorageInfo>& >::Type* callback); + // Cancels the notification callback (i.e., the window that created it no + // longer exists). + // This must be called only in the UI thread. + virtual void CancelNotification(); + // Requests a single local storage file to be deleted in the WEBKIT thread. + virtual void DeleteLocalStorageFile(const FilePath& file_path); + // Requests all local storage files to be deleted in the WEBKIT thread. + virtual void DeleteAllLocalStorageFiles(); + + private: + // Enumerates all local storage files in the WEBKIT thread. + void FetchLocalStorageInfoInWebKitThread(); + // Notifies the completion callback in the UI thread. + void NotifyInUIThread(); + // Delete a single local storage file in the WEBKIT thread. + void DeleteLocalStorageFileInWebKitThread(const FilePath& file_path); + // Delete all local storage files in the WEBKIT thread. + void DeleteAllLocalStorageFilesInWebKitThread(); + + Profile* profile_; + // This only mutates on the UI thread. + scoped_ptr<Callback1<const std::vector<LocalStorageInfo>& >::Type > + completion_callback_; + // Indicates whether or not we're currently fetching information: + // it's true when StartFetching() is called in the UI thread, and it's reset + // after we notified the callback in the UI thread. + // This only mutates on the UI thread. + bool is_fetching_; + // This only mutates in the WEBKIT thread. + std::vector<LocalStorageInfo> local_storage_info_; + + DISALLOW_COPY_AND_ASSIGN(BrowsingDataLocalStorageHelper); +}; + +#endif // CHROME_BROWSER_BROWSING_DATA_LOCAL_STORAGE_HELPER_H_ diff --git a/chrome/browser/browsing_data_local_storage_helper_unittest.cc b/chrome/browser/browsing_data_local_storage_helper_unittest.cc new file mode 100644 index 0000000..f36b724 --- /dev/null +++ b/chrome/browser/browsing_data_local_storage_helper_unittest.cc @@ -0,0 +1,170 @@ +// 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 <string> + +#include "base/basictypes.h" +#include "base/file_path.h" +#include "base/ref_counted.h" +#include "chrome/browser/in_process_webkit/webkit_context.h" +#include "chrome/browser/in_process_webkit/webkit_thread.h" +#include "chrome/browser/browsing_data_local_storage_helper.h" +#include "chrome/test/in_process_browser_test.h" +#include "chrome/test/testing_profile.h" +#include "chrome/test/ui_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +static const FilePath::CharType kTestFile0[] = + FILE_PATH_LITERAL("http_www.chromium.org_0.localstorage"); + +static const FilePath::CharType kTestFile1[] = + FILE_PATH_LITERAL("http_www.google.com_0.localstorage"); + +static const FilePath::CharType kTestFileInvalid[] = + FILE_PATH_LITERAL("http_www.google.com_localstorage_0.foo"); + +class BrowsingDataLocalStorageHelperTest : public InProcessBrowserTest { + protected: + void CreateLocalStorageFilesForTest() { + FilePath storage_path = GetLocalStoragePathForTestingProfile(); + file_util::CreateDirectory(storage_path); + const FilePath::CharType* kFilesToCreate[] = { + kTestFile0, kTestFile1, kTestFileInvalid, + }; + for (size_t i = 0; i < arraysize(kFilesToCreate); ++i) { + FilePath file_path = storage_path.Append(kFilesToCreate[i]); + file_util::WriteFile(file_path, NULL, 0); + } + } + + FilePath GetLocalStoragePathForTestingProfile() { + FilePath storage_path(testing_profile_.GetPath()); + storage_path = storage_path.Append( + DOMStorageContext::kLocalStorageDirectory); + return storage_path; + } + TestingProfile testing_profile_; +}; + +// This class is notified by BrowsingDataLocalStorageHelper on the UI thread +// once it finishes fetching the local storage data. +class StopTestOnCallback { + public: + explicit StopTestOnCallback( + BrowsingDataLocalStorageHelper* local_storage_helper) + : local_storage_helper_(local_storage_helper) { + DCHECK(local_storage_helper_); + } + + void Callback( + const std::vector<BrowsingDataLocalStorageHelper::LocalStorageInfo>& + local_storage_info) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + // There's no guarantee on the order, ensure these files are there. + const char* const kTestHosts[] = {"www.chromium.org", "www.google.com"}; + bool test_hosts_found[arraysize(kTestHosts)] = {false, false}; + ASSERT_EQ(arraysize(kTestHosts), local_storage_info.size()); + for (size_t i = 0; i < arraysize(kTestHosts); ++i) { + for (size_t j = 0; j < local_storage_info.size(); ++j) { + BrowsingDataLocalStorageHelper::LocalStorageInfo info = + local_storage_info.at(j); + ASSERT_EQ("http", info.protocol); + if (info.host == kTestHosts[i]) { + ASSERT_FALSE(test_hosts_found[i]); + test_hosts_found[i] = true; + } + } + } + for (size_t i = 0; i < arraysize(kTestHosts); ++i) { + ASSERT_TRUE(test_hosts_found[i]) << kTestHosts[i]; + } + MessageLoop::current()->Quit(); + } + + private: + BrowsingDataLocalStorageHelper* local_storage_helper_; +}; + +IN_PROC_BROWSER_TEST_F(BrowsingDataLocalStorageHelperTest, CallbackCompletes) { + scoped_refptr<BrowsingDataLocalStorageHelper> local_storage_helper( + new BrowsingDataLocalStorageHelper(&testing_profile_)); + CreateLocalStorageFilesForTest(); + StopTestOnCallback stop_test_on_callback(local_storage_helper); + local_storage_helper->StartFetching( + NewCallback(&stop_test_on_callback, &StopTestOnCallback::Callback)); + // Blocks until StopTestOnCallback::Callback is notified. + ui_test_utils::RunMessageLoop(); +} + +class WaitForWebKitThread + : public base::RefCountedThreadSafe<WaitForWebKitThread> { + public: + void QuitUiMessageLoopAfterWebKitThreadNotified() { + ChromeThread::PostTask(ChromeThread::WEBKIT, + FROM_HERE, + NewRunnableMethod( + this, &WaitForWebKitThread::RunInWebKitThread)); + } + + private: + void RunInWebKitThread() { + ChromeThread::PostTask(ChromeThread::UI, + FROM_HERE, + NewRunnableMethod( + this, &WaitForWebKitThread::RunInUiThread)); + } + + void RunInUiThread() { + MessageLoop::current()->Quit(); + } +}; + +IN_PROC_BROWSER_TEST_F(BrowsingDataLocalStorageHelperTest, DeleteSingleFile) { + scoped_refptr<BrowsingDataLocalStorageHelper> local_storage_helper( + new BrowsingDataLocalStorageHelper(&testing_profile_)); + CreateLocalStorageFilesForTest(); + local_storage_helper->DeleteLocalStorageFile( + GetLocalStoragePathForTestingProfile().Append(FilePath(kTestFile0))); + scoped_refptr<WaitForWebKitThread> wait_for_webkit_thread( + new WaitForWebKitThread); + wait_for_webkit_thread->QuitUiMessageLoopAfterWebKitThreadNotified(); + // Blocks until WaitForWebKitThread is notified. + ui_test_utils::RunMessageLoop(); + // Ensure the file has been deleted. + file_util::FileEnumerator file_enumerator( + GetLocalStoragePathForTestingProfile(), + false, + file_util::FileEnumerator::FILES); + int num_files = 0; + for (FilePath file_path = file_enumerator.Next(); + !file_path.empty(); + file_path = file_enumerator.Next()) { + ASSERT_FALSE(FilePath(kTestFile0) == file_path.BaseName()); + ++num_files; + } + ASSERT_EQ(2, num_files); +} + +IN_PROC_BROWSER_TEST_F(BrowsingDataLocalStorageHelperTest, DeleteAllFiles) { + scoped_refptr<BrowsingDataLocalStorageHelper> local_storage_helper( + new BrowsingDataLocalStorageHelper(&testing_profile_)); + CreateLocalStorageFilesForTest(); + local_storage_helper->DeleteAllLocalStorageFiles(); + scoped_refptr<WaitForWebKitThread> wait_for_webkit_thread( + new WaitForWebKitThread); + wait_for_webkit_thread->QuitUiMessageLoopAfterWebKitThreadNotified(); + // Blocks until WaitForWebKitThread is notified. + ui_test_utils::RunMessageLoop(); + // Ensure the alls files but the one without local storage extension have been + // deleted. + file_util::FileEnumerator file_enumerator( + GetLocalStoragePathForTestingProfile(), + false, + file_util::FileEnumerator::FILES); + for (FilePath file_path = file_enumerator.Next(); + !file_path.empty(); + file_path = file_enumerator.Next()) { + ASSERT_TRUE(FilePath(kTestFileInvalid) == file_path.BaseName()); + } +} diff --git a/chrome/browser/cocoa/cookies_window_controller.h b/chrome/browser/cocoa/cookies_window_controller.h index fac226a..0ddd6c6 100644 --- a/chrome/browser/cocoa/cookies_window_controller.h +++ b/chrome/browser/cocoa/cookies_window_controller.h @@ -87,12 +87,14 @@ class CookiesTreeModelObserverBridge : public TreeModelObserver { IBOutlet NSTreeController* treeController_; Profile* profile_; // weak + BrowsingDataLocalStorageHelper* storageHelper_; // weak } @property (assign, nonatomic) BOOL removeButtonEnabled; @property (readonly, nonatomic) NSTreeController* treeController; // Designated initializer. Profile cannot be NULL. -- (id)initWithProfile:(Profile*)profile; +- (id)initWithProfile:(Profile*)profile + storageHelper:(BrowsingDataLocalStorageHelper*)storageHelper; // Shows the cookies window as a modal sheet attached to |window|. - (void)attachSheetTo:(NSWindow*)window; diff --git a/chrome/browser/cocoa/cookies_window_controller.mm b/chrome/browser/cocoa/cookies_window_controller.mm index 227c0b3..998edf4 100644 --- a/chrome/browser/cocoa/cookies_window_controller.mm +++ b/chrome/browser/cocoa/cookies_window_controller.mm @@ -131,12 +131,14 @@ CocoaCookieTreeNode* CookiesTreeModelObserverBridge::FindCocoaNode( @synthesize removeButtonEnabled = removeButtonEnabled_; @synthesize treeController = treeController_; -- (id)initWithProfile:(Profile*)profile { +- (id)initWithProfile:(Profile*)profile + storageHelper:(BrowsingDataLocalStorageHelper*)storageHelper { DCHECK(profile); NSString* nibpath = [mac_util::MainAppBundle() pathForResource:@"Cookies" ofType:@"nib"]; if ((self = [super initWithWindowNibPath:nibpath owner:self])) { profile_ = profile; + storageHelper_ = storageHelper; [self loadTreeModelFromProfile]; @@ -305,7 +307,7 @@ CocoaCookieTreeNode* CookiesTreeModelObserverBridge::FindCocoaNode( // to rebuild after the user clears browsing data. Because the models get // clobbered, we rebuild the icon cache for safety (though they do not change). - (void)loadTreeModelFromProfile { - treeModel_.reset(new CookiesTreeModel(profile_)); + treeModel_.reset(new CookiesTreeModel(profile_, storageHelper_)); modelObserver_.reset(new CookiesTreeModelObserverBridge(self)); treeModel_->SetObserver(modelObserver_.get()); diff --git a/chrome/browser/cocoa/cookies_window_controller_unittest.mm b/chrome/browser/cocoa/cookies_window_controller_unittest.mm index e6fc929..de009c4 100644 --- a/chrome/browser/cocoa/cookies_window_controller_unittest.mm +++ b/chrome/browser/cocoa/cookies_window_controller_unittest.mm @@ -12,6 +12,7 @@ #include "chrome/browser/cocoa/clear_browsing_data_controller.h" #import "chrome/browser/cocoa/cookies_window_controller.h" #include "chrome/browser/cocoa/cocoa_test_helper.h" +#include "chrome/browser/mock_browsing_data_local_storage_helper.h" #include "chrome/browser/net/url_request_context_getter.h" #include "chrome/browser/cookies_tree_model.h" #include "chrome/test/testing_profile.h" @@ -65,8 +66,11 @@ class CookiesWindowControllerTest : public CocoaTest { CocoaTest::SetUp(); TestingProfile* profile = browser_helper_.profile(); profile->CreateRequestContext(); + local_storage_helper_ = new MockBrowsingDataLocalStorageHelper(profile); controller_.reset( - [[CookiesWindowController alloc] initWithProfile:profile]); + [[CookiesWindowController alloc] initWithProfile:profile + storageHelper:local_storage_helper_] + ); } virtual void TearDown() { @@ -86,6 +90,7 @@ class CookiesWindowControllerTest : public CocoaTest { protected: BrowserTestHelper browser_helper_; scoped_nsobject<CookiesWindowController> controller_; + BrowsingDataLocalStorageHelper* local_storage_helper_; }; TEST_F(CookiesWindowControllerTest, Construction) { @@ -137,7 +142,7 @@ TEST_F(CookiesWindowControllerTest, FindCocoaNodeRecursive) { TEST_F(CookiesWindowControllerTest, CocoaNodeFromTreeNodeCookie) { net::CookieMonster* cm = browser_helper_.profile()->GetCookieMonster(); cm->SetCookie(GURL("http://foo.com"), "A=B"); - CookiesTreeModel model(browser_helper_.profile()); + CookiesTreeModel model(browser_helper_.profile(), local_storage_helper_); // Root --> foo.com --> Cookies --> A. Create node for 'A'. TreeModelNode* node = model.GetRoot()->GetChild(0)->GetChild(0)->GetChild(0); @@ -158,7 +163,7 @@ TEST_F(CookiesWindowControllerTest, CocoaNodeFromTreeNodeCookie) { TEST_F(CookiesWindowControllerTest, CocoaNodeFromTreeNodeRecursive) { net::CookieMonster* cm = browser_helper_.profile()->GetCookieMonster(); cm->SetCookie(GURL("http://foo.com"), "A=B"); - CookiesTreeModel model(browser_helper_.profile()); + CookiesTreeModel model(browser_helper_.profile(), local_storage_helper_); // Root --> foo.com --> Cookies --> A. Create node for 'foo.com'. CookieTreeNode* node = model.GetRoot()->GetChild(0); @@ -200,7 +205,8 @@ TEST_F(CookiesWindowControllerTest, TreeNodesAdded) { cm->SetCookie(url, "A=B"); controller_.reset( - [[CookiesWindowController alloc] initWithProfile:profile]); + [[CookiesWindowController alloc] initWithProfile:profile + storageHelper:local_storage_helper_]); // Root --> foo.com --> Cookies. NSMutableArray* cocoa_children = @@ -241,7 +247,8 @@ TEST_F(CookiesWindowControllerTest, TreeNodesRemoved) { cm->SetCookie(url, "E=F"); controller_.reset( - [[CookiesWindowController alloc] initWithProfile:profile]); + [[CookiesWindowController alloc] initWithProfile:profile + storageHelper:local_storage_helper_]); // Root --> foo.com --> Cookies. NSMutableArray* cocoa_children = @@ -271,7 +278,8 @@ TEST_F(CookiesWindowControllerTest, TreeNodeChildrenReordered) { cm->SetCookie(url, "E=F"); controller_.reset( - [[CookiesWindowController alloc] initWithProfile:profile]); + [[CookiesWindowController alloc] initWithProfile:profile + storageHelper:local_storage_helper_]); // Root --> foo.com --> Cookies. NSMutableArray* cocoa_children = @@ -316,7 +324,8 @@ TEST_F(CookiesWindowControllerTest, TreeNodeChanged) { cm->SetCookie(url, "A=B"); controller_.reset( - [[CookiesWindowController alloc] initWithProfile:profile]); + [[CookiesWindowController alloc] initWithProfile:profile + storageHelper:local_storage_helper_]); CookiesTreeModel* model = [controller_ treeModel]; // Root --> foo.com --> Cookies. @@ -348,7 +357,8 @@ TEST_F(CookiesWindowControllerTest, TestDeleteCookie) { // This will clean itself up when we call |-closeSheet:|. If we reset the // scoper, we'd get a double-free. CookiesWindowController* controller = - [[CookiesWindowController alloc] initWithProfile:profile]; + [[CookiesWindowController alloc] initWithProfile:profile + storageHelper:local_storage_helper_]; [controller attachSheetTo:test_window()]; NSTreeController* treeController = [controller treeController]; @@ -378,7 +388,8 @@ TEST_F(CookiesWindowControllerTest, TestDidExpandItem) { cm->SetCookie(url, "C=D"); controller_.reset( - [[CookiesWindowController alloc] initWithProfile:profile]); + [[CookiesWindowController alloc] initWithProfile:profile + storageHelper:local_storage_helper_]); // Root --> foo.com. CocoaCookieTreeNode* foo = @@ -444,7 +455,8 @@ TEST_F(CookiesWindowControllerTest, RemoveButtonEnabled) { // This will clean itself up when we call |-closeSheet:|. If we reset the // scoper, we'd get a double-free. CookiesWindowController* controller = - [[CookiesWindowController alloc] initWithProfile:profile]; + [[CookiesWindowController alloc] initWithProfile:profile + storageHelper:local_storage_helper_]; [controller attachSheetTo:test_window()]; // Nothing should be selected right now. diff --git a/chrome/browser/cocoa/preferences_window_controller.mm b/chrome/browser/cocoa/preferences_window_controller.mm index 50f49cf..c4ad9ac 100644 --- a/chrome/browser/cocoa/preferences_window_controller.mm +++ b/chrome/browser/cocoa/preferences_window_controller.mm @@ -1315,8 +1315,11 @@ const int kDisabledIndex = 1; // Shows the cookies controller. - (IBAction)showCookies:(id)sender { // The controller will clean itself up. + BrowsingDataLocalStorageHelper* storageHelper = + new BrowsingDataLocalStorageHelper(profile_); CookiesWindowController* controller = - [[CookiesWindowController alloc] initWithProfile:profile_]; + [[CookiesWindowController alloc] initWithProfile:profile_ + storageHelper:storageHelper]; [controller attachSheetTo:[self window]]; } diff --git a/chrome/browser/cookies_tree_model.cc b/chrome/browser/cookies_tree_model.cc index 71932e8..aa699bf 100644 --- a/chrome/browser/cookies_tree_model.cc +++ b/chrome/browser/cookies_tree_model.cc @@ -14,6 +14,7 @@ #include "app/tree_node_model.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" @@ -114,6 +115,22 @@ class OriginNodeComparator { } // namespace /////////////////////////////////////////////////////////////////////////////// +// CookieTreeLocalStorageNode, public: + +CookieTreeLocalStorageNode::CookieTreeLocalStorageNode( + BrowsingDataLocalStorageHelper::LocalStorageInfo* local_storage_info) + : CookieTreeNode(UTF8ToWide( + !local_storage_info->origin.empty() ? + local_storage_info->origin : + local_storage_info->database_identifier)), + local_storage_info_(local_storage_info) { +} + +void CookieTreeLocalStorageNode::DeleteStoredObjects() { + GetModel()->DeleteLocalStorage(local_storage_info_->file_path); +} + +/////////////////////////////////////////////////////////////////////////////// // CookieTreeRootNode, public: CookieTreeOriginNode* CookieTreeRootNode::GetOrCreateOriginNode( const std::wstring& origin) { @@ -155,6 +172,17 @@ CookieTreeCookiesNode* CookieTreeOriginNode::GetOrCreateCookiesNode() { 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; + GetModel()->Add(this, cookies_child_ ? 1 : 0, retval); + local_storages_child_ = retval; + return retval; +} + /////////////////////////////////////////////////////////////////////////////// // CookieTreeCookiesNode, public: @@ -173,24 +201,67 @@ void CookieTreeCookiesNode::AddCookieNode( } /////////////////////////////////////////////////////////////////////////////// +// CookieTreeLocalStoragesNode, public: + +CookieTreeLocalStoragesNode::CookieTreeLocalStoragesNode() + : CookieTreeNode(l10n_util::GetString(IDS_COOKIES_LOCAL_STORAGE)) { +} + +void CookieTreeLocalStoragesNode::AddLocalStorageNode( + CookieTreeLocalStorageNode* new_child) { + std::vector<CookieTreeNode*>::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) { - return (static_cast<const CookieTreeCookieNode*>(lhs)-> - cookie_->second.Name() < - static_cast<const CookieTreeCookieNode*>(rhs)-> - cookie_->second.Name()); + const CookieTreeCookieNode* left = + static_cast<const CookieTreeCookieNode*>(lhs); + const CookieTreeCookieNode* right = + static_cast<const CookieTreeCookieNode*>(rhs); + return (left->cookie_->second.Name() < right->cookie_->second.Name()); +} + +/////////////////////////////////////////////////////////////////////////////// +// CookieTreeLocalStorageNode, private + +bool CookieTreeLocalStorageNode::CookieNodeComparator::operator() ( + const CookieTreeNode* lhs, const CookieTreeNode* rhs) { + const CookieTreeLocalStorageNode* left = + static_cast<const CookieTreeLocalStorageNode*>(lhs); + const CookieTreeLocalStorageNode* right = + static_cast<const CookieTreeLocalStorageNode*>(rhs); + return (left->local_storage_info_->origin < + right->local_storage_info_->origin); } /////////////////////////////////////////////////////////////////////////////// // CookiesTreeModel, public: -CookiesTreeModel::CookiesTreeModel(Profile* profile) +CookiesTreeModel::CookiesTreeModel( + Profile* profile, + BrowsingDataLocalStorageHelper* local_storage_helper) : ALLOW_THIS_IN_INITIALIZER_LIST(TreeNodeModel<CookieTreeNode>( new CookieTreeRootNode(this))), - profile_(profile) { + profile_(profile), + local_storage_helper_(local_storage_helper) { LoadCookies(); + DCHECK(local_storage_helper_); + local_storage_helper_->StartFetching(NewCallback( + this, &CookiesTreeModel::OnStorageModelInfoLoaded)); +} + +CookiesTreeModel::~CookiesTreeModel() { + local_storage_helper_->CancelNotification(); } /////////////////////////////////////////////////////////////////////////////// @@ -218,6 +289,8 @@ int CookiesTreeModel::GetIconIndex(TreeModelNode* node) { case CookieTreeNode::DetailedInfo::TYPE_COOKIE: return COOKIE; break; + case CookieTreeNode::DetailedInfo::TYPE_LOCAL_STORAGE: + // TODO(bulach): add an icon for local storage. default: return -1; } @@ -283,11 +356,50 @@ void CookiesTreeModel::DeleteCookieNode(CookieTreeNode* cookie_node) { delete Remove(parent_node, cookie_node_index); } +void CookiesTreeModel::DeleteLocalStorage(const FilePath& file_path) { + local_storage_helper_->DeleteLocalStorageFile(file_path); +} + +void CookiesTreeModel::DeleteAllLocalStorage() { + local_storage_helper_->DeleteAllLocalStorageFiles(); +} + 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); + PopulateLocalStorageInfoWithFilter(filter); + NotifyObserverTreeNodeChanged(root); +} + +void CookiesTreeModel::OnStorageModelInfoLoaded( + const LocalStorageInfoList& local_storage_info) { + local_storage_info_list_ = local_storage_info; + PopulateLocalStorageInfoWithFilter(L""); +} + +void CookiesTreeModel::PopulateLocalStorageInfoWithFilter( + const std::wstring& filter) { + CookieTreeRootNode* root = static_cast<CookieTreeRootNode*>(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->host : + local_storage_info->database_identifier; + 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); } diff --git a/chrome/browser/cookies_tree_model.h b/chrome/browser/cookies_tree_model.h index 11b7f8a..8a4bd60 100644 --- a/chrome/browser/cookies_tree_model.h +++ b/chrome/browser/cookies_tree_model.h @@ -10,9 +10,12 @@ #include "app/tree_node_model.h" #include "base/scoped_ptr.h" +#include "chrome/browser/browsing_data_local_storage_helper.h" #include "net/base/cookie_monster.h" class CookiesTreeModel; +class CookieTreeLocalStorageNode; +class CookieTreeLocalStoragesNode; class CookieTreeCookieNode; class CookieTreeCookiesNode; class CookieTreeOriginNode; @@ -33,18 +36,27 @@ class CookieTreeNode : public TreeNode<CookieTreeNode> { 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. + TYPE_COOKIE, // This is used for CookieTreeCookieNode nodes. + TYPE_LOCAL_STORAGES, // This is used for CookieTreeLocalStoragesNode. + TYPE_LOCAL_STORAGE, // This is used for CookieTreeLocalStorageNode. }; DetailedInfo(const std::wstring& origin, NodeType node_type, - const net::CookieMonster::CookieListPair* cookie) + const net::CookieMonster::CookieListPair* cookie, + const BrowsingDataLocalStorageHelper::LocalStorageInfo* + local_storage_info) : origin(origin), node_type(node_type), - cookie(cookie) {} + cookie(cookie), + local_storage_info(local_storage_info) { + if (node_type == TYPE_LOCAL_STORAGE) + DCHECK(local_storage_info); + } std::wstring origin; NodeType node_type; const net::CookieMonster::CookieListPair* cookie; + const BrowsingDataLocalStorageHelper::LocalStorageInfo* local_storage_info; }; CookieTreeNode() {} @@ -79,7 +91,7 @@ class CookieTreeRootNode : public CookieTreeNode { // CookieTreeNode methods: virtual CookiesTreeModel* GetModel() const { return model_; } virtual DetailedInfo GetDetailedInfo() const { - return DetailedInfo(std::wstring(), DetailedInfo::TYPE_ROOT, NULL); + return DetailedInfo(std::wstring(), DetailedInfo::TYPE_ROOT, NULL, NULL); } private: @@ -92,16 +104,19 @@ class CookieTreeRootNode : public CookieTreeNode { class CookieTreeOriginNode : public CookieTreeNode { public: explicit CookieTreeOriginNode(const std::wstring& origin) - : CookieTreeNode(origin), cookies_child_(NULL) {} + : CookieTreeNode(origin), cookies_child_(NULL), + local_storages_child_(NULL) {} virtual ~CookieTreeOriginNode() {} // CookieTreeNode methods: virtual DetailedInfo GetDetailedInfo() const { - return DetailedInfo(GetTitle(), DetailedInfo::TYPE_ORIGIN, NULL); + return DetailedInfo(GetTitle(), DetailedInfo::TYPE_ORIGIN, NULL, NULL); } // CookieTreeOriginNode methods: CookieTreeCookiesNode* GetOrCreateCookiesNode(); + CookieTreeLocalStoragesNode* GetOrCreateLocalStoragesNode(); + private: // A pointer to the COOKIES node. Eventually we will also have database, @@ -111,6 +126,7 @@ class CookieTreeOriginNode : public CookieTreeNode { // DATABASES etc node seems less preferable than storing an extra pointer per // origin. CookieTreeCookiesNode* cookies_child_; + CookieTreeLocalStoragesNode* local_storages_child_; DISALLOW_COPY_AND_ASSIGN(CookieTreeOriginNode); }; @@ -124,7 +140,7 @@ class CookieTreeCookiesNode : public CookieTreeNode { // CookieTreeNode methods: virtual DetailedInfo GetDetailedInfo() const { return DetailedInfo(GetParent()->GetTitle(), DetailedInfo::TYPE_COOKIES, - NULL); + NULL, NULL); } // CookieTreeCookiesNode methods: @@ -147,7 +163,7 @@ class CookieTreeCookieNode : public CookieTreeNode { virtual void DeleteStoredObjects(); virtual DetailedInfo GetDetailedInfo() const { return DetailedInfo(GetParent()->GetParent()->GetTitle(), - DetailedInfo::TYPE_COOKIE, cookie_); + DetailedInfo::TYPE_COOKIE, cookie_, NULL); } private: @@ -165,11 +181,66 @@ class CookieTreeCookieNode : public CookieTreeNode { DISALLOW_COPY_AND_ASSIGN(CookieTreeCookieNode); }; +// CookieTreeLocalStoragesNode ------------------------------------------------- +class CookieTreeLocalStoragesNode : public CookieTreeNode { + public: + CookieTreeLocalStoragesNode(); + virtual ~CookieTreeLocalStoragesNode() {} + + // CookieTreeNode methods: + virtual DetailedInfo GetDetailedInfo() const { + return DetailedInfo(GetParent()->GetTitle(), + DetailedInfo::TYPE_LOCAL_STORAGES, + NULL, + NULL); + } + + // CookieTreeStoragesNode methods: + void AddLocalStorageNode(CookieTreeLocalStorageNode* child); + + private: + DISALLOW_COPY_AND_ASSIGN(CookieTreeLocalStoragesNode); +}; + +class CookieTreeLocalStorageNode : public CookieTreeNode { + public: + friend class CookieTreeLocalStoragesNode; + + // Does not take ownership of local_storage_info, and local_storage_info + // should remain valid at least as long as the CookieTreeStorageNode is valid. + explicit CookieTreeLocalStorageNode( + BrowsingDataLocalStorageHelper::LocalStorageInfo* local_storage_info); + virtual ~CookieTreeLocalStorageNode() {} + + // CookieTreeStorageNode methods: + virtual void DeleteStoredObjects(); + virtual DetailedInfo GetDetailedInfo() const { + return DetailedInfo(GetParent()->GetParent()->GetTitle(), + DetailedInfo::TYPE_LOCAL_STORAGE, NULL, + local_storage_info_); + } + + 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); + }; + + // local_storage_info_ is not owned by the node, and is expected to remain + // valid as long as the CookieTreeLocalStorageNode is valid. + BrowsingDataLocalStorageHelper::LocalStorageInfo* local_storage_info_; + + DISALLOW_COPY_AND_ASSIGN(CookieTreeLocalStorageNode); +}; class CookiesTreeModel : public TreeNodeModel<CookieTreeNode> { public: - explicit CookiesTreeModel(Profile* profile); - virtual ~CookiesTreeModel() {} + CookiesTreeModel( + Profile* profile, + BrowsingDataLocalStorageHelper* browsing_data_local_storage_helper); + virtual ~CookiesTreeModel(); // TreeModel methods: // Returns the set of icons for the nodes in the tree. You only need override @@ -185,6 +256,8 @@ class CookiesTreeModel : public TreeNodeModel<CookieTreeNode> { void DeleteCookie(const net::CookieMonster::CookieListPair& cookie); void DeleteAllCookies(); void DeleteCookieNode(CookieTreeNode* cookie_node); + void DeleteLocalStorage(const FilePath& file_path); + void DeleteAllLocalStorage(); // Filter the origins to only display matched results. void UpdateSearchResults(const std::wstring& filter); @@ -192,18 +265,28 @@ class CookiesTreeModel : public TreeNodeModel<CookieTreeNode> { private: enum CookieIconIndex { ORIGIN = 0, - COOKIE = 1 + COOKIE = 1, + LOCAL_STORAGE = 2, }; typedef net::CookieMonster::CookieList CookieList; typedef std::vector<net::CookieMonster::CookieListPair*> CookiePtrList; + typedef std::vector<BrowsingDataLocalStorageHelper::LocalStorageInfo> + LocalStorageInfoList; void LoadCookies(); void LoadCookiesWithFilter(const std::wstring& filter); + void OnStorageModelInfoLoaded(const LocalStorageInfoList& local_storage_info); + + void PopulateLocalStorageInfoWithFilter(const std::wstring& filter); + // The profile from which this model sources cookies. Profile* profile_; CookieList all_cookies_; + scoped_refptr<BrowsingDataLocalStorageHelper> local_storage_helper_; + LocalStorageInfoList local_storage_info_list_; + DISALLOW_COPY_AND_ASSIGN(CookiesTreeModel); }; diff --git a/chrome/browser/cookies_tree_model_unittest.cc b/chrome/browser/cookies_tree_model_unittest.cc index 11c312f..07935d3 100644 --- a/chrome/browser/cookies_tree_model_unittest.cc +++ b/chrome/browser/cookies_tree_model_unittest.cc @@ -7,6 +7,7 @@ #include <string> #include "app/l10n_util.h" +#include "chrome/browser/mock_browsing_data_local_storage_helper.h" #include "chrome/browser/net/url_request_context_getter.h" #include "chrome/test/testing_profile.h" #include "net/url_request/url_request_context.h" @@ -26,6 +27,28 @@ class CookiesTreeModelTest : public testing::Test { virtual void SetUp() { profile_.reset(new TestingProfile()); profile_->CreateRequestContext(); + mock_browsing_data_helper_ = + new MockBrowsingDataLocalStorageHelper(profile_.get()); + } + + CookiesTreeModel* CreateCookiesTreeModelWithInitialSample() { + 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 = new CookiesTreeModel( + profile_.get(), mock_browsing_data_helper_); + mock_browsing_data_helper_->AddLocalStorageSamples(); + mock_browsing_data_helper_->Notify(); + { + SCOPED_TRACE("Initial State 3 cookies, 2 local storages"); + // 16 because there's the root, then foo1 -> cookies -> a, + // foo2 -> cookies -> b, foo3 -> cookies -> c, + // host1 -> localstorage -> origin1, host2 -> localstorage -> origin2. + EXPECT_EQ(16, cookies_model->GetRoot()->GetTotalNodeCount()); + EXPECT_EQ("origin1,origin2", GetDisplayedLocalStorages(cookies_model)); + } + return cookies_model; } // Get the cookie names in the cookie list, as a comma seperated string. @@ -42,35 +65,65 @@ class CookiesTreeModelTest : public testing::Test { return JoinString(parts, ','); } - std::string GetCookiesOfChildren(const CookieTreeNode* node) { + std::string GetNodesOfChildren( + const CookieTreeNode* node, + CookieTreeNode::DetailedInfo::NodeType node_type) { if (node->GetChildCount()) { std::string retval; for (int i = 0; i < node->GetChildCount(); ++i) { - retval += GetCookiesOfChildren(node->GetChild(i)); + retval += GetNodesOfChildren(node->GetChild(i), node_type); } return retval; } else { - if (node->GetDetailedInfo().node_type == - CookieTreeNode::DetailedInfo::TYPE_COOKIE) - return node->GetDetailedInfo().cookie->second.Name() + ","; - else + if (node->GetDetailedInfo().node_type == node_type) { + switch (node_type) { + case CookieTreeNode::DetailedInfo::TYPE_LOCAL_STORAGE: + return node->GetDetailedInfo().local_storage_info->origin + ","; + case CookieTreeNode::DetailedInfo::TYPE_COOKIE: + return node->GetDetailedInfo().cookie->second.Name() + ","; + default: + return ""; + } + } else { return ""; + } } } - // Get the cookie names displayed in the view (if we had one) in the order + + std::string GetCookiesOfChildren(const CookieTreeNode* node) { + return GetNodesOfChildren(node, CookieTreeNode::DetailedInfo::TYPE_COOKIE); + } + + std::string GetLocalStoragesOfChildren(const CookieTreeNode* node) { + return GetNodesOfChildren(node, + CookieTreeNode::DetailedInfo::TYPE_LOCAL_STORAGE); + } + + // Get the nodes 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) { + // Ex: EXPECT_STREQ("X,Y", GetDisplayedNodes(cookies_view, type).c_str()); + std::string GetDisplayedNodes(CookiesTreeModel* cookies_model, + CookieTreeNode::DetailedInfo::NodeType type) { CookieTreeRootNode* root = static_cast<CookieTreeRootNode*>( cookies_model->GetRoot()); - std::string retval = GetCookiesOfChildren(root); + std::string retval = GetNodesOfChildren(root, type); if (retval.length() && retval[retval.length() - 1] == ',') retval.erase(retval.length() - 1); return retval; } + std::string GetDisplayedCookies(CookiesTreeModel* cookies_model) { + return GetDisplayedNodes(cookies_model, + CookieTreeNode::DetailedInfo::TYPE_COOKIE); + } + + std::string GetDisplayedLocalStorages(CookiesTreeModel* cookies_model) { + return GetDisplayedNodes(cookies_model, + CookieTreeNode::DetailedInfo::TYPE_LOCAL_STORAGE); + } + // do not call on the root - void DeleteCookie(CookieTreeNode* node) { + void DeleteStoredObjects(CookieTreeNode* node) { node->DeleteStoredObjects(); // find the parent and index CookieTreeNode* parent_node = node->GetParent(); @@ -83,99 +136,114 @@ class CookiesTreeModelTest : public testing::Test { ChromeThread io_thread_; scoped_ptr<TestingProfile> profile_; + MockBrowsingDataLocalStorageHelper* mock_browsing_data_helper_; }; TEST_F(CookiesTreeModelTest, RemoveAll) { + scoped_ptr<CookiesTreeModel> cookies_model( + CreateCookiesTreeModelWithInitialSample()); 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)); + EXPECT_EQ(GetMonsterCookies(monster), + GetDisplayedCookies(cookies_model.get())); + EXPECT_EQ("origin1,origin2", + GetDisplayedLocalStorages(cookies_model.get())); } - cookies_model.DeleteAllCookies(); + cookies_model->DeleteAllCookies(); + cookies_model->DeleteAllLocalStorage(); + { SCOPED_TRACE("After removing"); - EXPECT_EQ(1, cookies_model.GetRoot()->GetTotalNodeCount()); - EXPECT_EQ(0, cookies_model.GetRoot()->GetChildCount()); + 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)); + EXPECT_EQ(GetMonsterCookies(monster), + GetDisplayedCookies(cookies_model.get())); + EXPECT_TRUE(mock_browsing_data_helper_->delete_all_files_called_); } } TEST_F(CookiesTreeModelTest, Remove) { + scoped_ptr<CookiesTreeModel> cookies_model( + CreateCookiesTreeModelWithInitialSample()); 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()); + DeleteStoredObjects(cookies_model->GetRoot()->GetChild(0)); { - 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()); + SCOPED_TRACE("First cookie origin removed"); + EXPECT_STREQ("B,C", GetMonsterCookies(monster).c_str()); + EXPECT_STREQ("B,C", GetDisplayedCookies(cookies_model.get()).c_str()); + EXPECT_EQ("origin1,origin2", GetDisplayedLocalStorages(cookies_model.get())); + EXPECT_EQ(13, cookies_model->GetRoot()->GetTotalNodeCount()); } - DeleteCookie(cookies_model.GetRoot()->GetChild(0)); + + DeleteStoredObjects(cookies_model->GetRoot()->GetChild(2)); { - SCOPED_TRACE("First origin removed"); + SCOPED_TRACE("First local storage 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()); + EXPECT_STREQ("B,C", GetDisplayedCookies(cookies_model.get()).c_str()); + EXPECT_EQ("origin2", GetDisplayedLocalStorages(cookies_model.get())); + EXPECT_EQ(10, cookies_model->GetRoot()->GetTotalNodeCount()); } } TEST_F(CookiesTreeModelTest, RemoveCookiesNode) { + scoped_ptr<CookiesTreeModel> cookies_model( + CreateCookiesTreeModelWithInitialSample()); 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)); + DeleteStoredObjects(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 + EXPECT_STREQ("B,C", GetDisplayedCookies(cookies_model.get()).c_str()); + // 14 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()); + // host1 -> localstorage -> origin1, host2 -> localstorage -> origin2. + EXPECT_EQ(14, cookies_model->GetRoot()->GetTotalNodeCount()); + EXPECT_EQ("origin1,origin2", + GetDisplayedLocalStorages(cookies_model.get())); + } + + DeleteStoredObjects(cookies_model->GetRoot()->GetChild(3)->GetChild(0)); + { + SCOPED_TRACE("First origin removed"); + EXPECT_STREQ("B,C", GetMonsterCookies(monster).c_str()); + EXPECT_STREQ("B,C", GetDisplayedCookies(cookies_model.get()).c_str()); + EXPECT_EQ("origin2", GetDisplayedLocalStorages(cookies_model.get())); + EXPECT_EQ(12, cookies_model->GetRoot()->GetTotalNodeCount()); } } TEST_F(CookiesTreeModelTest, RemoveCookieNode) { + scoped_ptr<CookiesTreeModel> cookies_model( + CreateCookiesTreeModelWithInitialSample()); 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)); + DeleteStoredObjects(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 + EXPECT_STREQ("A,C", GetDisplayedCookies(cookies_model.get()).c_str()); + // 14 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()); + // host1 -> localstorage -> origin1, host2 -> localstorage -> origin2. + EXPECT_EQ(14, cookies_model->GetRoot()->GetTotalNodeCount()); + } + + DeleteStoredObjects(cookies_model->GetRoot()->GetChild(3)->GetChild(0)); + { + SCOPED_TRACE("First origin removed"); + EXPECT_STREQ("A,C", GetMonsterCookies(monster).c_str()); + EXPECT_STREQ("A,C", GetDisplayedCookies(cookies_model.get()).c_str()); + EXPECT_EQ("origin2", GetDisplayedLocalStorages(cookies_model.get())); + EXPECT_EQ(12, cookies_model->GetRoot()->GetTotalNodeCount()); } } @@ -185,22 +253,27 @@ TEST_F(CookiesTreeModelTest, RemoveSingleCookieNode) { 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()); + CookiesTreeModel cookies_model( + profile_.get(), mock_browsing_data_helper_); + mock_browsing_data_helper_->AddLocalStorageSamples(); + mock_browsing_data_helper_->Notify(); { - SCOPED_TRACE("Initial State 4 cookies"); - // 11 because there's the root, then foo1 -> cookies -> a, + SCOPED_TRACE("Initial State 4 cookies, 2 local storages"); + // 17 because there's the root, then foo1 -> cookies -> a, // foo2 -> cookies -> b, foo3 -> cookies -> c,d - EXPECT_EQ(11, cookies_model.GetRoot()->GetTotalNodeCount()); + // host1 -> localstorage -> origin1, host2 -> localstorage -> origin2. + EXPECT_EQ(17, 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()); + EXPECT_EQ("origin1,origin2", GetDisplayedLocalStorages(&cookies_model)); } - DeleteCookie(cookies_model.GetRoot()->GetChild(2)); + DeleteStoredObjects(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()); + EXPECT_EQ(13, cookies_model.GetRoot()->GetTotalNodeCount()); } } @@ -211,23 +284,28 @@ TEST_F(CookiesTreeModelTest, RemoveSingleCookieNodeOf3) { 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()); + CookiesTreeModel cookies_model(profile_.get(), mock_browsing_data_helper_); + mock_browsing_data_helper_->AddLocalStorageSamples(); + mock_browsing_data_helper_->Notify(); { - SCOPED_TRACE("Initial State 5 cookies"); - // 11 because there's the root, then foo1 -> cookies -> a, + SCOPED_TRACE("Initial State 5 cookies, 2 local storages"); + // 17 because there's the root, then foo1 -> cookies -> a, // foo2 -> cookies -> b, foo3 -> cookies -> c,d,e - EXPECT_EQ(12, cookies_model.GetRoot()->GetTotalNodeCount()); + // host1 -> localstorage -> origin1, host2 -> localstorage -> origin2. + EXPECT_EQ(18, 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()); + EXPECT_EQ("origin1,origin2", GetDisplayedLocalStorages(&cookies_model)); } - DeleteCookie(cookies_model.GetRoot()->GetChild(2)->GetChild(0)-> + DeleteStoredObjects(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()); + EXPECT_EQ(17, cookies_model.GetRoot()->GetTotalNodeCount()); + EXPECT_EQ("origin1,origin2", GetDisplayedLocalStorages(&cookies_model)); } } @@ -238,8 +316,7 @@ TEST_F(CookiesTreeModelTest, RemoveSecondOrigin) { 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()); - + CookiesTreeModel cookies_model(profile_.get(), mock_browsing_data_helper_); { SCOPED_TRACE("Initial State 5 cookies"); // 11 because there's the root, then foo1 -> cookies -> a, @@ -248,7 +325,7 @@ TEST_F(CookiesTreeModelTest, RemoveSecondOrigin) { 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)); + DeleteStoredObjects(cookies_model.GetRoot()->GetChild(1)); { SCOPED_TRACE("Second origin removed"); EXPECT_STREQ("A,C,D,E", GetMonsterCookies(monster).c_str()); @@ -270,7 +347,8 @@ TEST_F(CookiesTreeModelTest, OriginOrdering) { monster->SetCookie(GURL("http://foo3.com"), "G=1"); monster->SetCookie(GURL("http://foo4.com"), "H=1"); - CookiesTreeModel cookies_model(profile_.get()); + CookiesTreeModel cookies_model( + profile_.get(), new MockBrowsingDataLocalStorageHelper(profile_.get())); { SCOPED_TRACE("Initial State 8 cookies"); @@ -279,7 +357,7 @@ TEST_F(CookiesTreeModelTest, OriginOrdering) { EXPECT_STREQ("F,E,C,B,A,G,D,H", GetDisplayedCookies(&cookies_model).c_str()); } - DeleteCookie(cookies_model.GetRoot()->GetChild(1)); // Delete "E" + DeleteStoredObjects(cookies_model.GetRoot()->GetChild(1)); // Delete "E" { SCOPED_TRACE("Second origin removed"); EXPECT_STREQ("D,A,C,F,B,G,H", GetMonsterCookies(monster).c_str()); diff --git a/chrome/browser/gtk/options/advanced_contents_gtk.cc b/chrome/browser/gtk/options/advanced_contents_gtk.cc index 908f212..d493a39 100644 --- a/chrome/browser/gtk/options/advanced_contents_gtk.cc +++ b/chrome/browser/gtk/options/advanced_contents_gtk.cc @@ -761,7 +761,9 @@ void PrivacySection::OnCookieBehaviorChanged(GtkComboBox* combo_box, void PrivacySection::OnShowCookiesButtonClicked( GtkButton *button, PrivacySection* privacy_section) { privacy_section->UserMetricsRecordAction("Options_ShowCookies", NULL); - CookiesView::Show(privacy_section->profile()); + CookiesView::Show(privacy_section->profile(), + new BrowsingDataLocalStorageHelper( + privacy_section->profile())); } void PrivacySection::NotifyPrefChanged(const std::wstring* pref_name) { diff --git a/chrome/browser/gtk/options/cookies_view.cc b/chrome/browser/gtk/options/cookies_view.cc index fb9c8c6..896e3b1 100644 --- a/chrome/browser/gtk/options/cookies_view.cc +++ b/chrome/browser/gtk/options/cookies_view.cc @@ -35,8 +35,8 @@ enum { // The currently open cookie manager, if any. CookiesView* instance_ = NULL; -void InitCookieDetailStyle(GtkWidget* entry, GtkStyle* label_style, - GtkStyle* dialog_style) { +void InitBrowserDetailStyle(GtkWidget* entry, GtkStyle* label_style, + GtkStyle* dialog_style) { gtk_widget_modify_fg(entry, GTK_STATE_NORMAL, &label_style->fg[GTK_STATE_NORMAL]); gtk_widget_modify_fg(entry, GTK_STATE_INSENSITIVE, @@ -57,20 +57,26 @@ CookiesView::~CookiesView() { } // static -void CookiesView::Show(Profile* profile) { +void CookiesView::Show( + Profile* profile, + BrowsingDataLocalStorageHelper* browsing_data_local_storage_helper) { DCHECK(profile); + DCHECK(browsing_data_local_storage_helper); // If there's already an existing editor window, activate it. if (instance_) { gtk_window_present(GTK_WINDOW(instance_->dialog_)); } else { - instance_ = new CookiesView(profile); + instance_ = new CookiesView(profile, browsing_data_local_storage_helper); instance_->InitStylesAndShow(); } } -CookiesView::CookiesView(Profile* profile) +CookiesView::CookiesView( + Profile* profile, + BrowsingDataLocalStorageHelper* browsing_data_local_storage_helper) : profile_(profile), + browsing_data_local_storage_helper_(browsing_data_local_storage_helper), filter_update_factory_(this) { Init(); } @@ -160,7 +166,8 @@ void CookiesView::Init() { GTK_SHADOW_ETCHED_IN); gtk_box_pack_start(GTK_BOX(cookie_list_vbox), scroll_window, TRUE, TRUE, 0); - cookies_tree_model_.reset(new CookiesTreeModel(profile_)); + cookies_tree_model_.reset(new CookiesTreeModel( + profile_, browsing_data_local_storage_helper_)); cookies_tree_adapter_.reset( new gtk_tree::TreeAdapter(this, cookies_tree_model_.get())); tree_ = gtk_tree_view_new_with_model( @@ -193,31 +200,54 @@ void CookiesView::Init() { G_CALLBACK(OnSelectionChanged), this); // Cookie details. - GtkWidget* details_frame = gtk_frame_new(NULL); - gtk_frame_set_shadow_type(GTK_FRAME(details_frame), GTK_SHADOW_ETCHED_IN); - gtk_box_pack_start(GTK_BOX(cookie_list_vbox), details_frame, + GtkWidget* cookie_details_frame = gtk_frame_new(NULL); + gtk_frame_set_shadow_type(GTK_FRAME(cookie_details_frame), + GTK_SHADOW_ETCHED_IN); + gtk_box_pack_start(GTK_BOX(cookie_list_vbox), cookie_details_frame, FALSE, FALSE, 0); cookie_details_table_ = gtk_table_new(7, 2, FALSE); - gtk_container_add(GTK_CONTAINER(details_frame), cookie_details_table_); + gtk_container_add(GTK_CONTAINER(cookie_details_frame), cookie_details_table_); gtk_table_set_col_spacing(GTK_TABLE(cookie_details_table_), 0, gtk_util::kLabelSpacing); int row = 0; - InitCookieDetailRow(row++, IDS_COOKIES_COOKIE_NAME_LABEL, - &cookie_name_entry_); - InitCookieDetailRow(row++, IDS_COOKIES_COOKIE_CONTENT_LABEL, - &cookie_content_entry_); - InitCookieDetailRow(row++, IDS_COOKIES_COOKIE_DOMAIN_LABEL, - &cookie_domain_entry_); - InitCookieDetailRow(row++, IDS_COOKIES_COOKIE_PATH_LABEL, - &cookie_path_entry_); - InitCookieDetailRow(row++, IDS_COOKIES_COOKIE_SENDFOR_LABEL, - &cookie_send_for_entry_); - InitCookieDetailRow(row++, IDS_COOKIES_COOKIE_CREATED_LABEL, - &cookie_created_entry_); - InitCookieDetailRow(row++, IDS_COOKIES_COOKIE_EXPIRES_LABEL, - &cookie_expires_entry_); + InitDetailRow(row++, IDS_COOKIES_COOKIE_NAME_LABEL, + cookie_details_table_, &cookie_name_entry_); + InitDetailRow(row++, IDS_COOKIES_COOKIE_CONTENT_LABEL, + cookie_details_table_, &cookie_content_entry_); + InitDetailRow(row++, IDS_COOKIES_COOKIE_DOMAIN_LABEL, + cookie_details_table_, &cookie_domain_entry_); + InitDetailRow(row++, IDS_COOKIES_COOKIE_PATH_LABEL, + cookie_details_table_, &cookie_path_entry_); + InitDetailRow(row++, IDS_COOKIES_COOKIE_SENDFOR_LABEL, + cookie_details_table_, &cookie_send_for_entry_); + InitDetailRow(row++, IDS_COOKIES_COOKIE_CREATED_LABEL, + cookie_details_table_, &cookie_created_entry_); + InitDetailRow(row++, IDS_COOKIES_COOKIE_EXPIRES_LABEL, + cookie_details_table_, &cookie_expires_entry_); + + // Local storage details. + GtkWidget* local_storage_details_frame = gtk_frame_new(NULL); + gtk_frame_set_shadow_type(GTK_FRAME(local_storage_details_frame), + GTK_SHADOW_ETCHED_IN); + gtk_box_pack_start(GTK_BOX(cookie_list_vbox), local_storage_details_frame, + FALSE, FALSE, 0); + local_storage_details_table_ = gtk_table_new(3, 2, FALSE); + gtk_container_add(GTK_CONTAINER(local_storage_details_frame), + local_storage_details_table_); + gtk_table_set_col_spacing(GTK_TABLE(local_storage_details_table_), 0, + gtk_util::kLabelSpacing); + + row = 0; + InitDetailRow(row++, IDS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL, + local_storage_details_table_, &local_storage_origin_entry_); + InitDetailRow(row++, IDS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL, + local_storage_details_table_, &local_storage_size_entry_); + InitDetailRow(row++, IDS_COOKIES_LOCAL_STORAGE_LAST_MODIFIED_LABEL, + local_storage_details_table_, + &local_storage_last_modified_entry_); + UpdateVisibleDetailedInfo(cookie_details_table_); // Populate the view. cookies_tree_adapter_->Init(); SetInitialTreeState(); @@ -231,30 +261,38 @@ void CookiesView::InitStylesAndShow() { GtkStyle* label_style = gtk_widget_get_style(description_label_); GtkStyle* dialog_style = gtk_widget_get_style(dialog_); - InitCookieDetailStyle(cookie_name_entry_, label_style, dialog_style); - InitCookieDetailStyle(cookie_content_entry_, label_style, dialog_style); - InitCookieDetailStyle(cookie_domain_entry_, label_style, dialog_style); - InitCookieDetailStyle(cookie_path_entry_, label_style, dialog_style); - InitCookieDetailStyle(cookie_send_for_entry_, label_style, dialog_style); - InitCookieDetailStyle(cookie_created_entry_, label_style, dialog_style); - InitCookieDetailStyle(cookie_expires_entry_, label_style, dialog_style); + // Cookie details. + InitBrowserDetailStyle(cookie_name_entry_, label_style, dialog_style); + InitBrowserDetailStyle(cookie_content_entry_, label_style, dialog_style); + InitBrowserDetailStyle(cookie_domain_entry_, label_style, dialog_style); + InitBrowserDetailStyle(cookie_path_entry_, label_style, dialog_style); + InitBrowserDetailStyle(cookie_send_for_entry_, label_style, dialog_style); + InitBrowserDetailStyle(cookie_created_entry_, label_style, dialog_style); + InitBrowserDetailStyle(cookie_expires_entry_, label_style, dialog_style); + + // Local storage details. + InitBrowserDetailStyle(local_storage_origin_entry_, label_style, + dialog_style); + InitBrowserDetailStyle(local_storage_size_entry_, label_style, dialog_style); + InitBrowserDetailStyle(local_storage_last_modified_entry_, label_style, + dialog_style); gtk_widget_show_all(dialog_); } -void CookiesView::InitCookieDetailRow(int row, int label_id, - GtkWidget** entry) { +void CookiesView::InitDetailRow(int row, int label_id, + GtkWidget* details_table, GtkWidget** entry) { GtkWidget* name_label = gtk_label_new( l10n_util::GetStringUTF8(label_id).c_str()); gtk_misc_set_alignment(GTK_MISC(name_label), 1, 0.5); - gtk_table_attach(GTK_TABLE(cookie_details_table_), name_label, + gtk_table_attach(GTK_TABLE(details_table), name_label, 0, 1, row, row + 1, GTK_FILL, GTK_FILL, 0, 0); *entry = gtk_entry_new(); gtk_entry_set_editable(GTK_ENTRY(*entry), FALSE); gtk_entry_set_has_frame(GTK_ENTRY(*entry), FALSE); - gtk_table_attach_defaults(GTK_TABLE(cookie_details_table_), *entry, + gtk_table_attach_defaults(GTK_TABLE(details_table), *entry, 1, 2, row, row + 1); } @@ -279,9 +317,15 @@ void CookiesView::EnableControls() { static_cast<CookieTreeNode*>( cookies_tree_adapter_->GetNode(&iter))->GetDetailedInfo(); if (detailed_info.node_type == CookieTreeNode::DetailedInfo::TYPE_COOKIE) { + UpdateVisibleDetailedInfo(cookie_details_table_); PopulateCookieDetails(detailed_info.cookie->first, detailed_info.cookie->second); + } else if (detailed_info.node_type == + CookieTreeNode::DetailedInfo::TYPE_LOCAL_STORAGE) { + UpdateVisibleDetailedInfo(local_storage_details_table_); + PopulateLocalStorageDetails(*detailed_info.local_storage_info); } else { + UpdateVisibleDetailedInfo(cookie_details_table_); ClearCookieDetails(); } } else { @@ -299,6 +343,12 @@ void CookiesView::SetCookieDetailsSensitivity(gboolean enabled) { gtk_widget_set_sensitive(cookie_expires_entry_, enabled); } +void CookiesView::SetLocalStorageDetailsSensitivity(gboolean enabled) { + gtk_widget_set_sensitive(local_storage_origin_entry_, enabled); + gtk_widget_set_sensitive(local_storage_size_entry_, enabled); + gtk_widget_set_sensitive(local_storage_last_modified_entry_, enabled); +} + void CookiesView::PopulateCookieDetails( const std::string& domain, const net::CookieMonster::CanonicalCookie& cookie) { @@ -326,6 +376,22 @@ void CookiesView::PopulateCookieDetails( SetCookieDetailsSensitivity(TRUE); } +void CookiesView::PopulateLocalStorageDetails( + const BrowsingDataLocalStorageHelper::LocalStorageInfo& + local_storage_info) { + gtk_entry_set_text(GTK_ENTRY(local_storage_origin_entry_), + local_storage_info.origin.c_str()); + gtk_entry_set_text(GTK_ENTRY(local_storage_size_entry_), + WideToUTF8(FormatBytes( + local_storage_info.size, + GetByteDisplayUnits(local_storage_info.size), + true)).c_str()); + gtk_entry_set_text(GTK_ENTRY(local_storage_last_modified_entry_), + WideToUTF8(base::TimeFormatFriendlyDateAndTime( + local_storage_info.last_modified)).c_str()); + SetLocalStorageDetailsSensitivity(TRUE); +} + void CookiesView::ClearCookieDetails() { std::string no_cookie = l10n_util::GetStringUTF8(IDS_COOKIES_COOKIE_NONESELECTED); @@ -387,6 +453,7 @@ void CookiesView::OnResponse(GtkDialog* dialog, int response_id, window->RemoveSelectedItems(); } else if (response_id == RESPONSE_REMOVE_ALL) { window->cookies_tree_model_->DeleteAllCookies(); + window->browsing_data_local_storage_helper_->DeleteAllLocalStorageFiles(); } else { gtk_widget_destroy(window->dialog_); } @@ -437,6 +504,21 @@ void CookiesView::UpdateFilterResults() { } } +void CookiesView::UpdateVisibleDetailedInfo(GtkWidget* table) { + // Toggle the parent (the table frame) visibility and sensitivity. + gtk_widget_show(gtk_widget_get_parent(table)); + // Toggle the other tables. + if (table == cookie_details_table_) { + SetCookieDetailsSensitivity(true); + SetLocalStorageDetailsSensitivity(false); + gtk_widget_hide(gtk_widget_get_parent(local_storage_details_table_)); + } else if (table == local_storage_details_table_) { + SetCookieDetailsSensitivity(false); + SetLocalStorageDetailsSensitivity(true); + gtk_widget_hide(gtk_widget_get_parent(cookie_details_table_)); + } +} + // static void CookiesView::OnFilterEntryActivated(GtkEntry* entry, CookiesView* window) { window->filter_update_factory_.RevokeAll(); diff --git a/chrome/browser/gtk/options/cookies_view.h b/chrome/browser/gtk/options/cookies_view.h index 260e3db..7720e13 100644 --- a/chrome/browser/gtk/options/cookies_view.h +++ b/chrome/browser/gtk/options/cookies_view.h @@ -12,6 +12,7 @@ #include "base/basictypes.h" #include "base/scoped_ptr.h" #include "base/task.h" +#include "chrome/browser/browsing_data_local_storage_helper.h" #include "chrome/common/gtk_tree.h" #include "net/base/cookie_monster.h" #include "testing/gtest/include/gtest/gtest_prod.h" @@ -31,14 +32,18 @@ class CookiesView : public gtk_tree::TreeAdapter::Delegate { virtual ~CookiesView(); // Create (if necessary) and show the cookie manager window. - static void Show(Profile* profile); + static void Show( + Profile* profile, + BrowsingDataLocalStorageHelper* browsing_data_local_storage_helper); // gtk_tree::TreeAdapter::Delegate implementation. virtual void OnAnyModelUpdateStart(); virtual void OnAnyModelUpdate(); private: - explicit CookiesView(Profile* profile); + CookiesView( + Profile* profile, + BrowsingDataLocalStorageHelper* browsing_data_local_storage_helper); // Initialize the dialog contents and layout. void Init(); @@ -46,8 +51,9 @@ class CookiesView : public gtk_tree::TreeAdapter::Delegate { // Initialize the widget styles and display the dialog. void InitStylesAndShow(); - // Helper for initializing cookie details table. - void InitCookieDetailRow(int row, int label_id, GtkWidget** display_label); + // Helper for initializing cookie / local storage details table. + void InitDetailRow(int row, int label_id, + GtkWidget* details_table, GtkWidget** display_label); // Set the initial selection and tree expanded state. void SetInitialTreeState(); @@ -58,10 +64,18 @@ class CookiesView : public gtk_tree::TreeAdapter::Delegate { // Set sensitivity of cookie details. void SetCookieDetailsSensitivity(gboolean enabled); + // Set sensitivity of local storage details. + void SetLocalStorageDetailsSensitivity(gboolean enabled); + // Show the details of the currently selected cookie. void PopulateCookieDetails(const std::string& domain, const net::CookieMonster::CanonicalCookie& cookie); + // Show the details of the currently selected local storage. + void PopulateLocalStorageDetails( + const BrowsingDataLocalStorageHelper::LocalStorageInfo& + local_storage_info); + // Reset the cookie details display. void ClearCookieDetails(); @@ -90,6 +104,9 @@ class CookiesView : public gtk_tree::TreeAdapter::Delegate { // Filter the list against the text in |filter_entry_|. void UpdateFilterResults(); + // Sets which of the detailed info table is visible. + void UpdateVisibleDetailedInfo(GtkWidget* table); + // Callbacks for user actions filtering the list. static void OnFilterEntryActivated(GtkEntry* entry, CookiesView* window); static void OnFilterEntryChanged(GtkEditable* editable, CookiesView* window); @@ -120,9 +137,19 @@ class CookiesView : public gtk_tree::TreeAdapter::Delegate { GtkWidget* cookie_created_entry_; GtkWidget* cookie_expires_entry_; + // The local storage details widgets. + GtkWidget* local_storage_details_table_; + GtkWidget* local_storage_origin_entry_; + GtkWidget* local_storage_size_entry_; + GtkWidget* local_storage_last_modified_entry_; + // The profile. Profile* profile_; + // Local Storage Helper. + scoped_refptr<BrowsingDataLocalStorageHelper> + browsing_data_local_storage_helper_; + // 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> filter_update_factory_; diff --git a/chrome/browser/gtk/options/cookies_view_unittest.cc b/chrome/browser/gtk/options/cookies_view_unittest.cc index 935f6c2..4092dbf 100644 --- a/chrome/browser/gtk/options/cookies_view_unittest.cc +++ b/chrome/browser/gtk/options/cookies_view_unittest.cc @@ -10,6 +10,7 @@ #include <gtk/gtk.h> #include "base/string_util.h" +#include "chrome/browser/mock_browsing_data_local_storage_helper.h" #include "chrome/browser/net/url_request_context_getter.h" #include "chrome/test/testing_profile.h" #include "net/url_request/url_request_context.h" @@ -26,24 +27,37 @@ class CookiesViewTest : public testing::Test { virtual void SetUp() { profile_.reset(new TestingProfile()); profile_->CreateRequestContext(); + mock_browsing_data_helper_ = new MockBrowsingDataLocalStorageHelper( + profile_.get()); } - void CheckDetailsSensitivity(gboolean expected, + void CheckDetailsSensitivity(gboolean expected_cookies, + gboolean expected_local_storage, const CookiesView& cookies_view) { - EXPECT_EQ(expected, + // Cookies + EXPECT_EQ(expected_cookies, GTK_WIDGET_SENSITIVE(cookies_view.cookie_name_entry_)); - EXPECT_EQ(expected, + EXPECT_EQ(expected_cookies, GTK_WIDGET_SENSITIVE(cookies_view.cookie_content_entry_)); - EXPECT_EQ(expected, + EXPECT_EQ(expected_cookies, GTK_WIDGET_SENSITIVE(cookies_view.cookie_domain_entry_)); - EXPECT_EQ(expected, + EXPECT_EQ(expected_cookies, GTK_WIDGET_SENSITIVE(cookies_view.cookie_path_entry_)); - EXPECT_EQ(expected, + EXPECT_EQ(expected_cookies, GTK_WIDGET_SENSITIVE(cookies_view.cookie_send_for_entry_)); - EXPECT_EQ(expected, + EXPECT_EQ(expected_cookies, GTK_WIDGET_SENSITIVE(cookies_view.cookie_created_entry_)); - EXPECT_EQ(expected, + EXPECT_EQ(expected_cookies, GTK_WIDGET_SENSITIVE(cookies_view.cookie_expires_entry_)); + // Local Storage + EXPECT_EQ(expected_local_storage, + GTK_WIDGET_SENSITIVE(cookies_view.local_storage_origin_entry_)); + EXPECT_EQ(expected_local_storage, + GTK_WIDGET_SENSITIVE(cookies_view.local_storage_size_entry_)); + EXPECT_EQ(expected_local_storage, + GTK_WIDGET_SENSITIVE( + cookies_view.local_storage_last_modified_entry_)); + } // Get the cookie names in the cookie list, as a comma seperated string. @@ -148,13 +162,14 @@ class CookiesViewTest : public testing::Test { ChromeThread io_thread_; scoped_ptr<TestingProfile> profile_; + scoped_refptr<MockBrowsingDataLocalStorageHelper> mock_browsing_data_helper_; }; TEST_F(CookiesViewTest, Empty) { - CookiesView cookies_view(profile_.get()); + CookiesView cookies_view(profile_.get(), mock_browsing_data_helper_); EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); - CheckDetailsSensitivity(FALSE, cookies_view); + CheckDetailsSensitivity(FALSE, FALSE, cookies_view); EXPECT_STREQ("", GetDisplayedCookies(cookies_view).c_str()); } @@ -167,21 +182,27 @@ TEST_F(CookiesViewTest, Noop) { monster->SetCookie(GURL("http://foo1"), "E=1"); monster->SetCookie(GURL("http://foo2"), "G=1"); monster->SetCookie(GURL("http://foo2"), "X=1"); - CookiesView cookies_view(profile_.get()); + CookiesView cookies_view(profile_.get(), mock_browsing_data_helper_); + mock_browsing_data_helper_->AddLocalStorageSamples(); + mock_browsing_data_helper_->Notify(); EXPECT_STREQ("foo0,_Cookies,__C,__D," "foo1,_Cookies,__A,__B,__E," - "foo2,_Cookies,__G,__X", + "foo2,_Cookies,__G,__X," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); - CheckDetailsSensitivity(FALSE, cookies_view); + CheckDetailsSensitivity(FALSE, FALSE, cookies_view); } TEST_F(CookiesViewTest, RemoveAll) { net::CookieMonster* monster = profile_->GetCookieMonster(); monster->SetCookie(GURL("http://foo"), "A=1"); monster->SetCookie(GURL("http://foo2"), "B=1"); - CookiesView cookies_view(profile_.get()); + CookiesView cookies_view(profile_.get(), mock_browsing_data_helper_); + mock_browsing_data_helper_->AddLocalStorageSamples(); + mock_browsing_data_helper_->Notify(); // Reset the selection of the first row. gtk_tree_selection_unselect_all(cookies_view.selection_); @@ -190,8 +211,10 @@ TEST_F(CookiesViewTest, RemoveAll) { SCOPED_TRACE("Before removing"); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); - CheckDetailsSensitivity(FALSE, cookies_view); - EXPECT_STREQ("foo,_Cookies,__A,foo2,_Cookies,__B", + CheckDetailsSensitivity(FALSE, FALSE, cookies_view); + EXPECT_STREQ("foo,_Cookies,__A,foo2,_Cookies,__B," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); } @@ -201,8 +224,9 @@ TEST_F(CookiesViewTest, RemoveAll) { EXPECT_EQ(0u, monster->GetAllCookies().size()); EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); - CheckDetailsSensitivity(FALSE, cookies_view); + CheckDetailsSensitivity(FALSE, FALSE, cookies_view); EXPECT_STREQ("", GetDisplayedCookies(cookies_view).c_str()); + EXPECT_TRUE(mock_browsing_data_helper_->delete_all_files_called_); } } @@ -210,7 +234,9 @@ TEST_F(CookiesViewTest, RemoveAllWithDefaultSelected) { net::CookieMonster* monster = profile_->GetCookieMonster(); monster->SetCookie(GURL("http://foo"), "A=1"); monster->SetCookie(GURL("http://foo2"), "B=1"); - CookiesView cookies_view(profile_.get()); + CookiesView cookies_view(profile_.get(), mock_browsing_data_helper_); + mock_browsing_data_helper_->AddLocalStorageSamples(); + mock_browsing_data_helper_->Notify(); EXPECT_STREQ("0", GetSelectedPath(cookies_view).c_str()); EXPECT_EQ(1, gtk_tree_selection_count_selected_rows(cookies_view.selection_)); @@ -218,8 +244,10 @@ TEST_F(CookiesViewTest, RemoveAllWithDefaultSelected) { SCOPED_TRACE("Before removing"); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); - CheckDetailsSensitivity(FALSE, cookies_view); - EXPECT_STREQ("foo,_Cookies,__A,foo2,_Cookies,__B", + CheckDetailsSensitivity(FALSE, FALSE, cookies_view); + EXPECT_STREQ("foo,_Cookies,__A,foo2,_Cookies,__B," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); } @@ -229,10 +257,11 @@ TEST_F(CookiesViewTest, RemoveAllWithDefaultSelected) { EXPECT_EQ(0u, monster->GetAllCookies().size()); EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); - CheckDetailsSensitivity(FALSE, cookies_view); + CheckDetailsSensitivity(FALSE, FALSE, cookies_view); EXPECT_STREQ("", GetDisplayedCookies(cookies_view).c_str()); EXPECT_EQ(0, gtk_tree_selection_count_selected_rows(cookies_view.selection_)); + EXPECT_TRUE(mock_browsing_data_helper_->delete_all_files_called_); } } @@ -241,7 +270,9 @@ TEST_F(CookiesViewTest, Remove) { monster->SetCookie(GURL("http://foo1"), "A=1"); monster->SetCookie(GURL("http://foo2"), "B=1"); monster->SetCookie(GURL("http://foo2"), "C=1"); - CookiesView cookies_view(profile_.get()); + CookiesView cookies_view(profile_.get(), mock_browsing_data_helper_); + mock_browsing_data_helper_->AddLocalStorageSamples(); + mock_browsing_data_helper_->Notify(); ASSERT_TRUE(ExpandByPath(cookies_view, "1")); ASSERT_TRUE(SelectByPath(cookies_view, "1:0:0")); @@ -250,8 +281,10 @@ TEST_F(CookiesViewTest, Remove) { SCOPED_TRACE("First selection"); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); - CheckDetailsSensitivity(TRUE, cookies_view); - EXPECT_STREQ("foo1,_Cookies,__A,foo2,+Cookies,++B,++C", + CheckDetailsSensitivity(TRUE, FALSE, cookies_view); + EXPECT_STREQ("foo1,_Cookies,__A,foo2,+Cookies,++B,++C," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); } @@ -260,12 +293,14 @@ TEST_F(CookiesViewTest, Remove) { { SCOPED_TRACE("First selection removed"); EXPECT_STREQ("A,C", GetMonsterCookies(monster).c_str()); - EXPECT_STREQ("foo1,_Cookies,__A,foo2,+Cookies,++C", + EXPECT_STREQ("foo1,_Cookies,__A,foo2,+Cookies,++C," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); EXPECT_STREQ("1:0:0", GetSelectedPath(cookies_view).c_str()); - CheckDetailsSensitivity(TRUE, cookies_view); + CheckDetailsSensitivity(TRUE, FALSE, cookies_view); } EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); @@ -274,16 +309,20 @@ TEST_F(CookiesViewTest, Remove) { { SCOPED_TRACE("Second selection"); EXPECT_STREQ("A", GetMonsterCookies(monster).c_str()); - EXPECT_STREQ("foo1,_Cookies,__A,foo2,+Cookies", + EXPECT_STREQ("foo1,_Cookies,__A,foo2,+Cookies," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); EXPECT_STREQ("1:0", GetSelectedPath(cookies_view).c_str()); - CheckDetailsSensitivity(FALSE, cookies_view); + CheckDetailsSensitivity(FALSE, FALSE, cookies_view); } ASSERT_TRUE(ExpandByPath(cookies_view, "0")); - EXPECT_STREQ("foo1,+Cookies,++A,foo2,+Cookies", + EXPECT_STREQ("foo1,+Cookies,++A,foo2,+Cookies," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); ASSERT_TRUE(SelectByPath(cookies_view, "0:0:0")); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); @@ -295,9 +334,35 @@ TEST_F(CookiesViewTest, Remove) { EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); EXPECT_STREQ("0:0", GetSelectedPath(cookies_view).c_str()); - CheckDetailsSensitivity(FALSE, cookies_view); - EXPECT_STREQ("foo1,+Cookies,foo2,+Cookies", + CheckDetailsSensitivity(FALSE, FALSE, cookies_view); + EXPECT_STREQ("foo1,+Cookies,foo2,+Cookies," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", + GetDisplayedCookies(cookies_view).c_str()); + } + + ASSERT_TRUE(ExpandByPath(cookies_view, "2")); + EXPECT_STREQ("foo1,+Cookies,foo2,+Cookies," + "host1,+Local Storage,++origin1," + "host2,_Local Storage,__origin2", + GetDisplayedCookies(cookies_view).c_str()); + ASSERT_TRUE(SelectByPath(cookies_view, "2:0:0")); + EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); + gtk_button_clicked(GTK_BUTTON(cookies_view.remove_button_)); + + { + SCOPED_TRACE("Third selection removed"); + EXPECT_EQ(0u, monster->GetAllCookies().size()); + EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); + EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); + EXPECT_STREQ("2:0", GetSelectedPath(cookies_view).c_str()); + CheckDetailsSensitivity(FALSE, FALSE, cookies_view); + EXPECT_STREQ("foo1,+Cookies,foo2,+Cookies," + "host1,+Local Storage," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); + EXPECT_TRUE(mock_browsing_data_helper_->last_deleted_file_ == + FilePath(FILE_PATH_LITERAL("file1"))); } } @@ -310,16 +375,23 @@ TEST_F(CookiesViewTest, RemoveCookiesByDomain) { monster->SetCookie(GURL("http://foo1"), "E=1"); monster->SetCookie(GURL("http://foo2"), "G=1"); monster->SetCookie(GURL("http://foo2"), "X=1"); - CookiesView cookies_view(profile_.get()); + CookiesView cookies_view(profile_.get(), mock_browsing_data_helper_); + mock_browsing_data_helper_->AddLocalStorageSamples(); + mock_browsing_data_helper_->Notify(); + EXPECT_STREQ("foo0,_Cookies,__C,__D," "foo1,_Cookies,__A,__B,__E," - "foo2,_Cookies,__G,__X", + "foo2,_Cookies,__G,__X," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); ASSERT_TRUE(ExpandByPath(cookies_view, "1")); EXPECT_STREQ("foo0,_Cookies,__C,__D," "foo1,+Cookies,++A,++B,++E," - "foo2,_Cookies,__G,__X", + "foo2,_Cookies,__G,__X," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); ASSERT_TRUE(SelectByPath(cookies_view, "1:0")); @@ -331,7 +403,9 @@ TEST_F(CookiesViewTest, RemoveCookiesByDomain) { EXPECT_STREQ("C,D,G,X", GetMonsterCookies(monster).c_str()); EXPECT_STREQ("foo0,_Cookies,__C,__D," "foo1," - "foo2,_Cookies,__G,__X", + "foo2,_Cookies,__G,__X," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); @@ -340,7 +414,9 @@ TEST_F(CookiesViewTest, RemoveCookiesByDomain) { ASSERT_TRUE(ExpandByPath(cookies_view, "0")); EXPECT_STREQ("foo0,+Cookies,++C,++D," "foo1," - "foo2,_Cookies,__G,__X", + "foo2,_Cookies,__G,__X," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); ASSERT_TRUE(SelectByPath(cookies_view, "0:0")); gtk_button_clicked(GTK_BUTTON(cookies_view.remove_button_)); @@ -348,7 +424,9 @@ TEST_F(CookiesViewTest, RemoveCookiesByDomain) { EXPECT_STREQ("G,X", GetMonsterCookies(monster).c_str()); EXPECT_STREQ("foo0," "foo1," - "foo2,_Cookies,__G,__X", + "foo2,_Cookies,__G,__X," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); @@ -357,7 +435,9 @@ TEST_F(CookiesViewTest, RemoveCookiesByDomain) { ASSERT_TRUE(ExpandByPath(cookies_view, "2")); EXPECT_STREQ("foo0," "foo1," - "foo2,+Cookies,++G,++X", + "foo2,+Cookies,++G,++X," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); ASSERT_TRUE(SelectByPath(cookies_view, "2:0")); gtk_button_clicked(GTK_BUTTON(cookies_view.remove_button_)); @@ -365,11 +445,36 @@ TEST_F(CookiesViewTest, RemoveCookiesByDomain) { EXPECT_STREQ("", GetMonsterCookies(monster).c_str()); EXPECT_STREQ("foo0," "foo1," - "foo2", + "foo2," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); EXPECT_STREQ("2", GetSelectedPath(cookies_view).c_str()); + + ASSERT_TRUE(ExpandByPath(cookies_view, "3")); + EXPECT_STREQ("foo0," + "foo1," + "foo2," + "host1,+Local Storage,++origin1," + "host2,_Local Storage,__origin2", + GetDisplayedCookies(cookies_view).c_str()); + ASSERT_TRUE(SelectByPath(cookies_view, "3:0")); + gtk_button_clicked(GTK_BUTTON(cookies_view.remove_button_)); + + EXPECT_STREQ("", GetMonsterCookies(monster).c_str()); + EXPECT_STREQ("foo0," + "foo1," + "foo2," + "host1," + "host2,_Local Storage,__origin2", + GetDisplayedCookies(cookies_view).c_str()); + EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); + EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); + EXPECT_STREQ("3", GetSelectedPath(cookies_view).c_str()); + EXPECT_TRUE(mock_browsing_data_helper_->last_deleted_file_ == + FilePath(FILE_PATH_LITERAL("file1"))); } TEST_F(CookiesViewTest, RemoveByDomain) { @@ -381,10 +486,15 @@ TEST_F(CookiesViewTest, RemoveByDomain) { monster->SetCookie(GURL("http://foo1"), "E=1"); monster->SetCookie(GURL("http://foo2"), "G=1"); monster->SetCookie(GURL("http://foo2"), "X=1"); - CookiesView cookies_view(profile_.get()); + CookiesView cookies_view(profile_.get(), mock_browsing_data_helper_); + mock_browsing_data_helper_->AddLocalStorageSamples(); + mock_browsing_data_helper_->Notify(); + EXPECT_STREQ("foo0,_Cookies,__C,__D," "foo1,_Cookies,__A,__B,__E," - "foo2,_Cookies,__G,__X", + "foo2,_Cookies,__G,__X," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); ASSERT_TRUE(SelectByPath(cookies_view, "1")); @@ -396,7 +506,9 @@ TEST_F(CookiesViewTest, RemoveByDomain) { EXPECT_STREQ("C,D,G,X", GetMonsterCookies(monster).c_str()); EXPECT_STREQ("foo0,_Cookies,__C,__D," - "foo2,_Cookies,__G,__X", + "foo2,_Cookies,__G,__X," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); @@ -406,7 +518,9 @@ TEST_F(CookiesViewTest, RemoveByDomain) { gtk_button_clicked(GTK_BUTTON(cookies_view.remove_button_)); EXPECT_STREQ("G,X", GetMonsterCookies(monster).c_str()); - EXPECT_STREQ("foo2,_Cookies,__G,__X", + EXPECT_STREQ("foo2,_Cookies,__G,__X," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); @@ -415,9 +529,33 @@ TEST_F(CookiesViewTest, RemoveByDomain) { gtk_button_clicked(GTK_BUTTON(cookies_view.remove_button_)); EXPECT_STREQ("", GetMonsterCookies(monster).c_str()); - EXPECT_STREQ("", GetDisplayedCookies(cookies_view).c_str()); + EXPECT_STREQ("host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", + GetDisplayedCookies(cookies_view).c_str()); + EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); + EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); + EXPECT_STREQ("0", GetSelectedPath(cookies_view).c_str()); + + gtk_button_clicked(GTK_BUTTON(cookies_view.remove_button_)); + + EXPECT_STREQ("", GetMonsterCookies(monster).c_str()); + EXPECT_STREQ("host2,_Local Storage,__origin2", + GetDisplayedCookies(cookies_view).c_str()); + EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); + EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); + EXPECT_TRUE(mock_browsing_data_helper_->last_deleted_file_ == + FilePath(FILE_PATH_LITERAL("file1"))); + EXPECT_STREQ("0", GetSelectedPath(cookies_view).c_str()); + + gtk_button_clicked(GTK_BUTTON(cookies_view.remove_button_)); + EXPECT_STREQ("", GetMonsterCookies(monster).c_str()); + EXPECT_STREQ("", + GetDisplayedCookies(cookies_view).c_str()); EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); + EXPECT_TRUE(mock_browsing_data_helper_->last_deleted_file_ == + FilePath(FILE_PATH_LITERAL("file2"))); + EXPECT_EQ(0, gtk_tree_selection_count_selected_rows(cookies_view.selection_)); } @@ -430,10 +568,15 @@ TEST_F(CookiesViewTest, RemoveDefaultSelection) { monster->SetCookie(GURL("http://foo1"), "E=1"); monster->SetCookie(GURL("http://foo2"), "G=1"); monster->SetCookie(GURL("http://foo2"), "X=1"); - CookiesView cookies_view(profile_.get()); + CookiesView cookies_view(profile_.get(), mock_browsing_data_helper_); + mock_browsing_data_helper_->AddLocalStorageSamples(); + mock_browsing_data_helper_->Notify(); + EXPECT_STREQ("foo0,_Cookies,__C,__D," "foo1,_Cookies,__A,__B,__E," - "foo2,_Cookies,__G,__X", + "foo2,_Cookies,__G,__X," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); @@ -444,7 +587,9 @@ TEST_F(CookiesViewTest, RemoveDefaultSelection) { EXPECT_STREQ("B,A,E,G,X", GetMonsterCookies(monster).c_str()); EXPECT_STREQ("foo1,_Cookies,__A,__B,__E," - "foo2,_Cookies,__G,__X", + "foo2,_Cookies,__G,__X," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); @@ -453,7 +598,28 @@ TEST_F(CookiesViewTest, RemoveDefaultSelection) { gtk_button_clicked(GTK_BUTTON(cookies_view.remove_button_)); EXPECT_STREQ("G,X", GetMonsterCookies(monster).c_str()); - EXPECT_STREQ("foo2,_Cookies,__G,__X", + EXPECT_STREQ("foo2,_Cookies,__G,__X," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", + GetDisplayedCookies(cookies_view).c_str()); + + EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); + EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); + + gtk_button_clicked(GTK_BUTTON(cookies_view.remove_button_)); + + EXPECT_STREQ("", GetMonsterCookies(monster).c_str()); + EXPECT_STREQ("host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", + GetDisplayedCookies(cookies_view).c_str()); + + EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); + EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); + + gtk_button_clicked(GTK_BUTTON(cookies_view.remove_button_)); + + EXPECT_STREQ("", GetMonsterCookies(monster).c_str()); + EXPECT_STREQ("host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); @@ -474,11 +640,16 @@ TEST_F(CookiesViewTest, Filter) { monster->SetCookie(GURL("http://bar0"), "D=1"); monster->SetCookie(GURL("http://foo1"), "B=1"); monster->SetCookie(GURL("http://bar1"), "A=1"); - CookiesView cookies_view(profile_.get()); + CookiesView cookies_view(profile_.get(), mock_browsing_data_helper_); + mock_browsing_data_helper_->AddLocalStorageSamples(); + mock_browsing_data_helper_->Notify(); + EXPECT_STREQ("bar0,_Cookies,__D," "bar1,_Cookies,__A," "foo0,_Cookies,__C," - "foo1,_Cookies,__B", + "foo1,_Cookies,__B," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(cookies_view.filter_clear_button_)); @@ -489,7 +660,9 @@ TEST_F(CookiesViewTest, Filter) { EXPECT_STREQ("bar0,_Cookies,__D," "bar1,_Cookies,__A," "foo0,_Cookies,__C," - "foo1,_Cookies,__B", + "foo1,_Cookies,__B," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); // Results are filtered immediately if you activate (hit enter in the entry). @@ -504,7 +677,15 @@ TEST_F(CookiesViewTest, Filter) { EXPECT_STREQ("bar0,_Cookies,__D," "bar1,_Cookies,__A," "foo0,_Cookies,__C," - "foo1,_Cookies,__B", + "foo1,_Cookies,__B," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", + GetDisplayedCookies(cookies_view).c_str()); + + gtk_entry_set_text(GTK_ENTRY(cookies_view.filter_entry_), "hos"); + gtk_widget_activate(cookies_view.filter_entry_); + EXPECT_STREQ("host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); } @@ -514,11 +695,16 @@ TEST_F(CookiesViewTest, FilterRemoveAll) { monster->SetCookie(GURL("http://bar0"), "D=1"); monster->SetCookie(GURL("http://foo1"), "B=1"); monster->SetCookie(GURL("http://bar1"), "A=1"); - CookiesView cookies_view(profile_.get()); + CookiesView cookies_view(profile_.get(), mock_browsing_data_helper_); + mock_browsing_data_helper_->AddLocalStorageSamples(); + mock_browsing_data_helper_->Notify(); + EXPECT_STREQ("bar0,_Cookies,__D," "bar1,_Cookies,__A," "foo0,_Cookies,__C," - "foo1,_Cookies,__B", + "foo1,_Cookies,__B," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(cookies_view.filter_clear_button_)); @@ -529,7 +715,9 @@ TEST_F(CookiesViewTest, FilterRemoveAll) { EXPECT_STREQ("bar0,_Cookies,__D," "bar1,_Cookies,__A," "foo0,_Cookies,__C," - "foo1,_Cookies,__B", + "foo1,_Cookies,__B," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); // Results are filtered immediately if you activate (hit enter in the entry). @@ -550,7 +738,9 @@ TEST_F(CookiesViewTest, FilterRemoveAll) { EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(cookies_view.filter_clear_button_)); EXPECT_STREQ("", gtk_entry_get_text(GTK_ENTRY(cookies_view.filter_entry_))); EXPECT_STREQ("foo0,_Cookies,__C," - "foo1,_Cookies,__B", + "foo1,_Cookies,__B," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); } @@ -561,11 +751,16 @@ TEST_F(CookiesViewTest, FilterRemove) { monster->SetCookie(GURL("http://foo1"), "B=1"); monster->SetCookie(GURL("http://bar1"), "A=1"); monster->SetCookie(GURL("http://bar1"), "E=1"); - CookiesView cookies_view(profile_.get()); + CookiesView cookies_view(profile_.get(), mock_browsing_data_helper_); + mock_browsing_data_helper_->AddLocalStorageSamples(); + mock_browsing_data_helper_->Notify(); + EXPECT_STREQ("bar0,_Cookies,__D," "bar1,_Cookies,__A,__E," "foo0,_Cookies,__C," - "foo1,_Cookies,__B", + "foo1,_Cookies,__B," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); EXPECT_STREQ("D,A,E,C,B", GetMonsterCookies(monster).c_str()); @@ -577,7 +772,9 @@ TEST_F(CookiesViewTest, FilterRemove) { EXPECT_STREQ("bar0,_Cookies,__D," "bar1,_Cookies,__A,__E," "foo0,_Cookies,__C," - "foo1,_Cookies,__B", + "foo1,_Cookies,__B," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); // Results are filtered immediately if you activate (hit enter in the entry). @@ -596,7 +793,7 @@ TEST_F(CookiesViewTest, FilterRemove) { SCOPED_TRACE("First selection"); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); - CheckDetailsSensitivity(TRUE, cookies_view); + CheckDetailsSensitivity(TRUE, FALSE, cookies_view); } gtk_button_clicked(GTK_BUTTON(cookies_view.remove_button_)); @@ -610,7 +807,7 @@ TEST_F(CookiesViewTest, FilterRemove) { EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); EXPECT_STREQ("1:0:0", GetSelectedPath(cookies_view).c_str()); - CheckDetailsSensitivity(TRUE, cookies_view); + CheckDetailsSensitivity(TRUE, FALSE, cookies_view); } gtk_button_clicked(GTK_BUTTON(cookies_view.remove_button_)); @@ -624,7 +821,7 @@ TEST_F(CookiesViewTest, FilterRemove) { EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); EXPECT_STREQ("1:0", GetSelectedPath(cookies_view).c_str()); - CheckDetailsSensitivity(FALSE, cookies_view); + CheckDetailsSensitivity(FALSE, FALSE, cookies_view); } ASSERT_TRUE(ExpandByPath(cookies_view, "0")); @@ -638,7 +835,7 @@ TEST_F(CookiesViewTest, FilterRemove) { EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); EXPECT_STREQ("0:0", GetSelectedPath(cookies_view).c_str()); - CheckDetailsSensitivity(FALSE, cookies_view); + CheckDetailsSensitivity(FALSE, FALSE, cookies_view); EXPECT_STREQ("bar0,+Cookies," "bar1,+Cookies", GetDisplayedCookies(cookies_view).c_str()); @@ -648,6 +845,50 @@ TEST_F(CookiesViewTest, FilterRemove) { EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(cookies_view.filter_clear_button_)); EXPECT_STREQ("", gtk_entry_get_text(GTK_ENTRY(cookies_view.filter_entry_))); EXPECT_STREQ("foo0,_Cookies,__C," - "foo1,_Cookies,__B", + "foo1,_Cookies,__B," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", GetDisplayedCookies(cookies_view).c_str()); + + gtk_entry_set_text(GTK_ENTRY(cookies_view.filter_entry_), "hos"); + EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.filter_clear_button_)); + // Entering text doesn't immediately filter the results. + EXPECT_STREQ("foo0,_Cookies,__C," + "foo1,_Cookies,__B," + "host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", + GetDisplayedCookies(cookies_view).c_str()); + + // Results are filtered immediately if you activate (hit enter in the entry). + gtk_widget_activate(cookies_view.filter_entry_); + EXPECT_STREQ("host1,_Local Storage,__origin1," + "host2,_Local Storage,__origin2", + GetDisplayedCookies(cookies_view).c_str()); + + ASSERT_TRUE(ExpandByPath(cookies_view, "1")); + EXPECT_STREQ("host1,_Local Storage,__origin1," + "host2,+Local Storage,++origin2", + GetDisplayedCookies(cookies_view).c_str()); + ASSERT_TRUE(SelectByPath(cookies_view, "1:0:0")); + + { + SCOPED_TRACE("First selection"); + EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); + EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); + CheckDetailsSensitivity(FALSE, TRUE, cookies_view); + } + + gtk_button_clicked(GTK_BUTTON(cookies_view.remove_button_)); + + { + SCOPED_TRACE("First selection removed"); + EXPECT_STREQ("C,B", GetMonsterCookies(monster).c_str()); + EXPECT_STREQ("host1,_Local Storage,__origin1," + "host2,+Local Storage", + GetDisplayedCookies(cookies_view).c_str()); + EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_all_button_)); + EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(cookies_view.remove_button_)); + EXPECT_STREQ("1:0", GetSelectedPath(cookies_view).c_str()); + CheckDetailsSensitivity(FALSE, FALSE, cookies_view); + } } diff --git a/chrome/browser/in_process_webkit/dom_storage_context.cc b/chrome/browser/in_process_webkit/dom_storage_context.cc index 3997c5f..c19b6cf 100644 --- a/chrome/browser/in_process_webkit/dom_storage_context.cc +++ b/chrome/browser/in_process_webkit/dom_storage_context.cc @@ -11,13 +11,22 @@ #include "chrome/browser/in_process_webkit/dom_storage_namespace.h" #include "chrome/browser/in_process_webkit/webkit_context.h" #include "chrome/common/dom_storage_common.h" +#include "webkit/glue/glue_util.h" -static const char* kLocalStorageDirectory = "Local Storage"; +const FilePath::CharType DOMStorageContext::kLocalStorageDirectory[] = + FILE_PATH_LITERAL("Local Storage"); + +const FilePath::CharType DOMStorageContext::kLocalStorageExtension[] = + FILE_PATH_LITERAL(".localstorage"); + +static const FilePath::CharType kLocalStorageOldPath[] = + FILE_PATH_LITERAL("localStorage"); // TODO(jorlow): Remove after Chrome 4 ships. static void MigrateLocalStorageDirectory(const FilePath& data_path) { - FilePath new_path = data_path.AppendASCII(kLocalStorageDirectory); - FilePath old_path = data_path.AppendASCII("localStorage"); + FilePath new_path = data_path.Append( + DOMStorageContext::kLocalStorageDirectory); + FilePath old_path = data_path.Append(kLocalStorageOldPath); if (!file_util::DirectoryExists(new_path) && file_util::DirectoryExists(old_path)) { file_util::Move(old_path, new_path); @@ -148,7 +157,7 @@ void DOMStorageContext::DeleteDataModifiedSince(const base::Time& cutoff) { PurgeMemory(); file_util::FileEnumerator file_enumerator( - webkit_context_->data_path().AppendASCII(kLocalStorageDirectory), false, + webkit_context_->data_path().Append(kLocalStorageDirectory), false, file_util::FileEnumerator::FILES); for (FilePath path = file_enumerator.Next(); !path.value().empty(); path = file_enumerator.Next()) { @@ -159,12 +168,41 @@ void DOMStorageContext::DeleteDataModifiedSince(const base::Time& cutoff) { } } +void DOMStorageContext::DeleteLocalStorageFile(const FilePath& file_path) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::WEBKIT)); + + // Make sure that we don't delete a database that's currently being accessed + // by unloading all of the databases temporarily. + // TODO(bulach): both this method and DeleteDataModifiedSince could purge + // only the memory used by the specific file instead of all memory at once. + // See http://code.google.com/p/chromium/issues/detail?id=32000 + PurgeMemory(); + file_util::Delete(file_path, false); +} + +void DOMStorageContext::DeleteAllLocalStorageFiles() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::WEBKIT)); + + // Make sure that we don't delete a database that's currently being accessed + // by unloading all of the databases temporarily. + PurgeMemory(); + + file_util::FileEnumerator file_enumerator( + webkit_context_->data_path().Append(kLocalStorageDirectory), false, + file_util::FileEnumerator::FILES); + for (FilePath file_path = file_enumerator.Next(); !file_path.empty(); + file_path = file_enumerator.Next()) { + if (file_path.Extension() == kLocalStorageExtension) + file_util::Delete(file_path, false); + } +} + DOMStorageNamespace* DOMStorageContext::CreateLocalStorage() { FilePath data_path = webkit_context_->data_path(); FilePath dir_path; if (!data_path.empty()) { MigrateLocalStorageDirectory(data_path); - dir_path = data_path.AppendASCII(kLocalStorageDirectory); + dir_path = data_path.Append(kLocalStorageDirectory); } DOMStorageNamespace* new_namespace = DOMStorageNamespace::CreateLocalStorageNamespace(this, dir_path); diff --git a/chrome/browser/in_process_webkit/dom_storage_context.h b/chrome/browser/in_process_webkit/dom_storage_context.h index fb68f4c9..4981c07 100644 --- a/chrome/browser/in_process_webkit/dom_storage_context.h +++ b/chrome/browser/in_process_webkit/dom_storage_context.h @@ -66,6 +66,18 @@ class DOMStorageContext { // date that's supplied. void DeleteDataModifiedSince(const base::Time& cutoff); + // Deletes a single local storage file. + void DeleteLocalStorageFile(const FilePath& file_path); + + // Deletes all local storage files. + void DeleteAllLocalStorageFiles(); + + // The local storage directory. + static const FilePath::CharType kLocalStorageDirectory[]; + + // The local storage file extension. + static const FilePath::CharType kLocalStorageExtension[]; + private: // Get the local storage instance. The object is owned by this class. DOMStorageNamespace* CreateLocalStorage(); diff --git a/chrome/browser/mock_browsing_data_local_storage_helper.cc b/chrome/browser/mock_browsing_data_local_storage_helper.cc new file mode 100644 index 0000000..b654a37f --- /dev/null +++ b/chrome/browser/mock_browsing_data_local_storage_helper.cc @@ -0,0 +1,47 @@ +// 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/mock_browsing_data_local_storage_helper.h" + +MockBrowsingDataLocalStorageHelper::MockBrowsingDataLocalStorageHelper( + Profile* profile) + : BrowsingDataLocalStorageHelper(profile), + profile_(profile), + callback_(NULL), + delete_all_files_called_(false) { +} + +void MockBrowsingDataLocalStorageHelper::StartFetching( + Callback1<const std::vector<LocalStorageInfo>& >::Type* callback) { + callback_ = callback; +} + +void MockBrowsingDataLocalStorageHelper::CancelNotification() { + callback_ = NULL; +} + +void MockBrowsingDataLocalStorageHelper::DeleteLocalStorageFile( + const FilePath& file_path) { + last_deleted_file_ = file_path; +} + +void MockBrowsingDataLocalStorageHelper::DeleteAllLocalStorageFiles() { + delete_all_files_called_ = true; +} + +void MockBrowsingDataLocalStorageHelper::AddLocalStorageSamples() { + response_.push_back( + BrowsingDataLocalStorageHelper::LocalStorageInfo( + "http", "host1", 1, "db1", "origin1", + FilePath(FILE_PATH_LITERAL("file1")), 1, base::Time())); + response_.push_back( + BrowsingDataLocalStorageHelper::LocalStorageInfo( + "http", "host2", 2, "db2", "origin2", + FilePath(FILE_PATH_LITERAL("file2")), 2, base::Time())); +} + +void MockBrowsingDataLocalStorageHelper::Notify() { + CHECK(callback_); + callback_->Run(response_); +} diff --git a/chrome/browser/mock_browsing_data_local_storage_helper.h b/chrome/browser/mock_browsing_data_local_storage_helper.h new file mode 100644 index 0000000..e60d772 --- /dev/null +++ b/chrome/browser/mock_browsing_data_local_storage_helper.h @@ -0,0 +1,37 @@ +// 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_MOCK_BROWSING_DATA_LOCAL_STORAGE_HELPER_H_ +#define CHROME_BROWSER_MOCK_BROWSING_DATA_LOCAL_STORAGE_HELPER_H_ + +#include "chrome/browser/browsing_data_local_storage_helper.h" + +// Mock for BrowsingDataLocalStorageHelper. +// Use AddLocalStorageSamples() or add directly to response_ vector, then +// call Notify(). +class MockBrowsingDataLocalStorageHelper + : public BrowsingDataLocalStorageHelper { + public: + explicit MockBrowsingDataLocalStorageHelper(Profile* profile); + + virtual void StartFetching( + Callback1<const std::vector<LocalStorageInfo>& >::Type* callback); + virtual void CancelNotification(); + virtual void DeleteLocalStorageFile(const FilePath& file_path); + virtual void DeleteAllLocalStorageFiles(); + + // Adds some LocalStorageInfo samples. + void AddLocalStorageSamples(); + + // Notifies the callback. + void Notify(); + + Profile* profile_; + Callback1<const std::vector<LocalStorageInfo>& >::Type* callback_; + FilePath last_deleted_file_; + bool delete_all_files_called_; + std::vector<LocalStorageInfo> response_; +}; + +#endif // CHROME_BROWSER_MOCK_BROWSING_DATA_LOCAL_STORAGE_HELPER_H_ diff --git a/chrome/browser/views/options/cookies_view.cc b/chrome/browser/views/options/cookies_view.cc index 7e5f0b4..2500e7e 100644 --- a/chrome/browser/views/options/cookies_view.cc +++ b/chrome/browser/views/options/cookies_view.cc @@ -31,7 +31,6 @@ static const int kCookieInfoViewBorderSize = 1; static const int kCookieInfoViewInsetSize = 3; static const int kSearchFilterDelayMs = 500; - /////////////////////////////////////////////////////////////////////////////// // CookiesTreeView // Overridden to handle Delete key presses @@ -49,20 +48,19 @@ class CookiesTreeView : public views::TreeView { }; CookiesTreeView::CookiesTreeView(CookiesTreeModel* cookies_model) { - SetModel(cookies_model); - SetRootShown(false); - SetEditable(false); + SetModel(cookies_model); + SetRootShown(false); + SetEditable(false); } void CookiesTreeView::RemoveSelectedItems() { TreeModelNode* selected_node = GetSelectedNode(); if (selected_node) { static_cast<CookiesTreeModel*>(model())->DeleteCookieNode( - static_cast<CookieTreeCookieNode*>(GetSelectedNode())); + static_cast<CookieTreeNode*>(GetSelectedNode())); } } - /////////////////////////////////////////////////////////////////////////////// // CookieInfoView, public: @@ -253,6 +251,123 @@ void CookieInfoView::Init() { } /////////////////////////////////////////////////////////////////////////////// +// LocalStorageInfoView, public: + +LocalStorageInfoView::LocalStorageInfoView() + : origin_label_(NULL), + origin_value_field_(NULL), + size_label_(NULL), + size_value_field_(NULL), + last_modified_label_(NULL), + last_modified_value_field_(NULL) { +} + +LocalStorageInfoView::~LocalStorageInfoView() { +} + +void LocalStorageInfoView::SetLocalStorageInfo( + const BrowsingDataLocalStorageHelper::LocalStorageInfo& + local_storage_info) { + origin_value_field_->SetText(UTF8ToWide(local_storage_info.origin)); + size_value_field_->SetText( + FormatBytes(local_storage_info.size, + GetByteDisplayUnits(local_storage_info.size), + true)); + last_modified_value_field_->SetText( + base::TimeFormatFriendlyDateAndTime(local_storage_info.last_modified)); + EnableLocalStorageDisplay(true); +} + +void LocalStorageInfoView::EnableLocalStorageDisplay(bool enabled) { + origin_value_field_->SetEnabled(enabled); + size_value_field_->SetEnabled(enabled); + last_modified_value_field_->SetEnabled(enabled); +} + +void LocalStorageInfoView::ClearLocalStorageDisplay() { + std::wstring no_cookie_string = + l10n_util::GetString(IDS_COOKIES_COOKIE_NONESELECTED); + origin_value_field_->SetText(no_cookie_string); + size_value_field_->SetText(no_cookie_string); + last_modified_value_field_->SetText(no_cookie_string); + EnableLocalStorageDisplay(false); +} + +/////////////////////////////////////////////////////////////////////////////// +// LocalStorageInfoView, views::View overrides: + +void LocalStorageInfoView::ViewHierarchyChanged(bool is_add, + views::View* parent, + views::View* child) { + if (is_add && child == this) + Init(); +} + +/////////////////////////////////////////////////////////////////////////////// +// LocalStorageInfoView, private: + +void LocalStorageInfoView::Init() { + SkColor border_color = color_utils::GetSysSkColor(COLOR_3DSHADOW); + views::Border* border = views::Border::CreateSolidBorder( + kCookieInfoViewBorderSize, border_color); + set_border(border); + + origin_label_ = new views::Label( + l10n_util::GetString(IDS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL)); + origin_value_field_ = new views::Textfield; + size_label_ = new views::Label( + l10n_util::GetString(IDS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL)); + size_value_field_ = new views::Textfield; + last_modified_label_ = new views::Label( + l10n_util::GetString(IDS_COOKIES_LOCAL_STORAGE_LAST_MODIFIED_LABEL)); + last_modified_value_field_ = new views::Textfield; + + using views::GridLayout; + using views::ColumnSet; + + GridLayout* layout = new GridLayout(this); + layout->SetInsets(kCookieInfoViewInsetSize, + kCookieInfoViewInsetSize, + kCookieInfoViewInsetSize, + kCookieInfoViewInsetSize); + SetLayoutManager(layout); + + int three_column_layout_id = 0; + ColumnSet* column_set = layout->AddColumnSet(three_column_layout_id); + column_set->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 0, + GridLayout::USE_PREF, 0, 0); + column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing); + column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, + GridLayout::USE_PREF, 0, 0); + + layout->StartRow(0, three_column_layout_id); + layout->AddView(origin_label_); + layout->AddView(origin_value_field_); + layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing); + layout->StartRow(0, three_column_layout_id); + layout->AddView(size_label_); + layout->AddView(size_value_field_); + layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing); + layout->StartRow(0, three_column_layout_id); + layout->AddView(last_modified_label_); + layout->AddView(last_modified_value_field_); + + // Color these borderless text areas the same as the containing dialog. + SkColor text_area_background = color_utils::GetSysSkColor(COLOR_3DFACE); + // Now that the Textfields are in the view hierarchy, we can initialize them. + origin_value_field_->SetReadOnly(true); + origin_value_field_->RemoveBorder(); + origin_value_field_->SetBackgroundColor(text_area_background); + size_value_field_->SetReadOnly(true); + size_value_field_->RemoveBorder(); + size_value_field_->SetBackgroundColor(text_area_background); + last_modified_value_field_->SetReadOnly(true); + last_modified_value_field_->RemoveBorder(); + last_modified_value_field_->SetBackgroundColor(text_area_background); +} + + +/////////////////////////////////////////////////////////////////////////////// // CookiesView, public: // static @@ -371,10 +486,17 @@ void CookiesView::OnTreeViewSelectionChanged(views::TreeView* tree_view) { 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); + UpdateVisibleDetailedInfo(cookie_info_view_); + cookie_info_view_->SetCookie(detailed_info.cookie->first, + detailed_info.cookie->second); + } else if (detailed_info.node_type == + CookieTreeNode::DetailedInfo::TYPE_LOCAL_STORAGE) { + UpdateVisibleDetailedInfo(local_storage_info_view_); + local_storage_info_view_->SetLocalStorageInfo( + *detailed_info.local_storage_info); } else { - info_view_->ClearCookieDisplay(); + UpdateVisibleDetailedInfo(cookie_info_view_); + cookie_info_view_->ClearCookieDisplay(); } } @@ -393,7 +515,8 @@ CookiesView::CookiesView(Profile* profile) clear_search_button_(NULL), description_label_(NULL), cookies_tree_(NULL), - info_view_(NULL), + cookie_info_view_(NULL), + local_storage_info_view_(NULL), remove_button_(NULL), remove_all_button_(NULL), profile_(profile), @@ -420,8 +543,10 @@ void CookiesView::Init() { description_label_ = new views::Label( l10n_util::GetString(IDS_COOKIES_INFO_LABEL)); description_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); - cookies_tree_model_.reset(new CookiesTreeModel(profile_)); - info_view_ = new CookieInfoView; + cookies_tree_model_.reset(new CookiesTreeModel( + profile_, new BrowsingDataLocalStorageHelper(profile_))); + cookie_info_view_ = new CookieInfoView; + local_storage_info_view_ = new LocalStorageInfoView; cookies_tree_ = new CookiesTreeView(cookies_tree_model_.get()); remove_button_ = new views::NativeButton( this, l10n_util::GetString(IDS_COOKIES_REMOVE_LABEL)); @@ -469,7 +594,10 @@ void CookiesView::Init() { layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); layout->StartRow(0, single_column_layout_id); - layout->AddView(info_view_); + layout->AddView(cookie_info_view_, 1, 2); + + layout->StartRow(0, single_column_layout_id); + layout->AddView(local_storage_info_view_); // Add the Remove/Remove All buttons to the ClientView View* parent = GetParent(); @@ -477,6 +605,8 @@ void CookiesView::Init() { parent->AddChildView(remove_all_button_); if (!cookies_tree_model_.get()->GetRoot()->GetChildCount()) UpdateForEmptyState(); + else + UpdateVisibleDetailedInfo(cookie_info_view_); } void CookiesView::ResetSearchQuery() { @@ -486,7 +616,15 @@ void CookiesView::ResetSearchQuery() { } void CookiesView::UpdateForEmptyState() { - info_view_->ClearCookieDisplay(); + cookie_info_view_->ClearCookieDisplay(); remove_button_->SetEnabled(false); remove_all_button_->SetEnabled(false); + UpdateVisibleDetailedInfo(cookie_info_view_); +} + +void CookiesView::UpdateVisibleDetailedInfo(views::View* view) { + view->SetVisible(true); + views::View* other = local_storage_info_view_; + if (view == local_storage_info_view_) other = cookie_info_view_; + other->SetVisible(false); } diff --git a/chrome/browser/views/options/cookies_view.h b/chrome/browser/views/options/cookies_view.h index d7c1b23..91d6cf4a 100644 --- a/chrome/browser/views/options/cookies_view.h +++ b/chrome/browser/views/options/cookies_view.h @@ -8,6 +8,7 @@ #include <string> #include "base/task.h" +#include "chrome/browser/browsing_data_local_storage_helper.h" #include "net/base/cookie_monster.h" #include "views/controls/button/button.h" #include "views/controls/tree/tree_view.h" @@ -24,9 +25,11 @@ class NativeButton; } // namespace views +class BrowsingDataLocalStorageHelper; class CookieInfoView; class CookiesTreeModel; class CookiesTreeView; +class LocalStorageInfoView; class Profile; class Timer; @@ -96,13 +99,23 @@ class CookiesView : public views::View, // Update the UI when there are no cookies. void UpdateForEmptyState(); + // Update the UI when a cookie is selected. + void UpdateForCookieState(); + + // Update the UI when a local storage is selected. + void UpdateForLocalStorageState(); + + // Updates view to be visible inside detailed_info_view_; + void UpdateVisibleDetailedInfo(views::View* view); + // Assorted dialog controls views::Label* search_label_; views::Textfield* search_field_; views::NativeButton* clear_search_button_; views::Label* description_label_; CookiesTreeView* cookies_tree_; - CookieInfoView* info_view_; + CookieInfoView* cookie_info_view_; + LocalStorageInfoView* local_storage_info_view_; views::NativeButton* remove_button_; views::NativeButton* remove_all_button_; @@ -172,4 +185,46 @@ class CookieInfoView : public views::View { DISALLOW_COPY_AND_ASSIGN(CookieInfoView); }; +/////////////////////////////////////////////////////////////////////////////// +// LocalStorageInfoView +// +// Responsible for displaying a tabular grid of Local Storage information. +class LocalStorageInfoView : public views::View { + public: + LocalStorageInfoView(); + virtual ~LocalStorageInfoView(); + + // Update the display from the specified Local Storage info. + void SetLocalStorageInfo( + const BrowsingDataLocalStorageHelper::LocalStorageInfo& + local_storage_info); + + // Clears the cookie display to indicate that no or multiple local storages + // are selected. + void ClearLocalStorageDisplay(); + + // Enables or disables the local storate property text fields. + void EnableLocalStorageDisplay(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* origin_label_; + views::Textfield* origin_value_field_; + views::Label* size_label_; + views::Textfield* size_value_field_; + views::Label* last_modified_label_; + views::Textfield* last_modified_value_field_; + + DISALLOW_COPY_AND_ASSIGN(LocalStorageInfoView); +}; + + #endif // CHROME_BROWSER_VIEWS_OPTIONS_COOKIES_VIEW_H_ diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index f417554..3fc3a1e 100755 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -226,6 +226,8 @@ 'browser/browser_url_handler.cc', 'browser/browser_url_handler.h', 'browser/browser_window.h', + 'browser/browsing_data_local_storage_helper.cc', + 'browser/browsing_data_local_storage_helper.h', 'browser/browsing_data_remover.cc', 'browser/browsing_data_remover.h', 'browser/browsing_instance.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index ac53510..779f4dc 100755 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -64,12 +64,14 @@ # The only thing used from browser is Browser::Type. 'browser/browser.h', 'browser/cocoa/browser_test_helper.h', + 'browser/mock_browsing_data_local_storage_helper.h', + 'browser/mock_browsing_data_local_storage_helper.cc', # TODO: these should live here but are currently used by # production code code in libbrowser (in chrome.gyp). #'browser/net/url_request_mock_http_job.cc', #'browser/net/url_request_mock_http_job.h', 'browser/net/url_request_mock_net_error_job.cc', - 'browser/net/url_request_mock_net_error_job.h', + 'browser/net/url_request_mock_net_error_job.h', 'browser/renderer_host/mock_render_process_host.cc', 'browser/renderer_host/mock_render_process_host.h', 'browser/renderer_host/test/test_backing_store.cc', @@ -1084,6 +1086,7 @@ 'browser/autocomplete/autocomplete_browsertest.cc', 'browser/browser_browsertest.cc', 'browser/browser_init_browsertest.cc', + 'browser/browsing_data_local_storage_helper_unittest.cc', 'browser/crash_recovery_browsertest.cc', 'browser/download/save_page_browsertest.cc', 'browser/extensions/autoupdate_interceptor.cc', |