summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjochen@chromium.org <jochen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-05 08:15:53 +0000
committerjochen@chromium.org <jochen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-05 08:15:53 +0000
commit21f4d251210a4408da8a3279510ef7eb44cc1e1a (patch)
treea235ca4b807641d671b244fc78ed9bb64205b4ac
parent0c86dbf56c6f3e82ee748f34dca48aedf962dec2 (diff)
downloadchromium_src-21f4d251210a4408da8a3279510ef7eb44cc1e1a.zip
chromium_src-21f4d251210a4408da8a3279510ef7eb44cc1e1a.tar.gz
chromium_src-21f4d251210a4408da8a3279510ef7eb44cc1e1a.tar.bz2
Implement edit mode for history page.
BUG=35338 TEST=none Review URL: http://codereview.chromium.org/660283 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40722 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/app/generated_resources.grd13
-rw-r--r--chrome/browser/browsing_data_remover.cc6
-rw-r--r--chrome/browser/dom_ui/history_ui.cc96
-rw-r--r--chrome/browser/dom_ui/history_ui.h22
-rw-r--r--chrome/browser/extensions/extension_history_api.cc6
-rw-r--r--chrome/browser/history/expire_history_backend.cc28
-rw-r--r--chrome/browser/history/expire_history_backend.h8
-rw-r--r--chrome/browser/history/expire_history_backend_unittest.cc65
-rw-r--r--chrome/browser/history/history.cc7
-rw-r--r--chrome/browser/history/history.h7
-rw-r--r--chrome/browser/history/history_backend.cc9
-rw-r--r--chrome/browser/history/history_backend.h3
-rw-r--r--chrome/browser/history/history_backend_unittest.cc6
-rw-r--r--chrome/browser/history/text_database_manager.cc25
-rw-r--r--chrome/browser/history/text_database_manager.h12
-rw-r--r--chrome/browser/resources/history.html335
16 files changed, 496 insertions, 152 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 2768701..0b0de03 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -222,8 +222,17 @@ each locale. -->
$1<ex>pie</ex>
</ph>'
</message>
- <message name="IDS_HISTORY_DELETE_PRIOR_VISITS_LINK" desc="Title of the link that allows the user to delete visits prior to the specified point">
- Delete history for this day
+ <message name="IDS_HISTORY_START_EDITING_HISTORY" desc="Title of the link that allows the user to enable controls for deleting single history items">
+ Edit items...
+ </message>
+ <message name="IDS_HISTORY_STOP_EDITING_HISTORY" desc="Title of the link that allows the user to disable controls for deleting single history items">
+ Done removing items
+ </message>
+ <message name="IDS_HISTORY_REMOVE_SELECTED_ITEMS" desc="Title of the button that allows the user to remove the selected history items">
+ Remove selected items
+ </message>
+ <message name="IDS_HISTORY_OPEN_CLEAR_BROWSING_DATA_DIALOG" desc="Title of the button that will open the clear browsing data dialog.">
+ Clear all browsing data...
</message>
<message name="IDS_HISTORY_DELETE_PRIOR_VISITS_WARNING" desc="Warning shown before deleting">
Are you sure you want to delete these pages from your history?
diff --git a/chrome/browser/browsing_data_remover.cc b/chrome/browser/browsing_data_remover.cc
index 435c3d7..b4468c7 100644
--- a/chrome/browser/browsing_data_remover.cc
+++ b/chrome/browser/browsing_data_remover.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -78,9 +78,11 @@ void BrowsingDataRemover::Remove(int remove_mask) {
HistoryService* history_service =
profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
if (history_service) {
+ std::set<GURL> restrict_urls;
UserMetrics::RecordAction("ClearBrowsingData_History", profile_);
waiting_for_clear_history_ = true;
- history_service->ExpireHistoryBetween(delete_begin_, delete_end_,
+ history_service->ExpireHistoryBetween(restrict_urls,
+ delete_begin_, delete_end_,
&request_consumer_,
NewCallback(this, &BrowsingDataRemover::OnHistoryDeletionDone));
}
diff --git a/chrome/browser/dom_ui/history_ui.cc b/chrome/browser/dom_ui/history_ui.cc
index 0019059..d81bbc1 100644
--- a/chrome/browser/dom_ui/history_ui.cc
+++ b/chrome/browser/dom_ui/history_ui.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -16,11 +16,14 @@
#include "base/time.h"
#include "base/values.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "chrome/browser/browser.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/dom_ui/dom_ui_favicon_source.h"
#include "chrome/browser/metrics/user_metrics.h"
#include "chrome/browser/history/history_types.h"
#include "chrome/browser/profile.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/tab_contents_delegate.h"
#include "chrome/common/jstemplate_builder.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/time_format.h"
@@ -74,9 +77,15 @@ void HistoryUIHTMLSource::StartDataRequest(const std::string& path,
l10n_util::GetString(IDS_HISTORY_NO_RESULTS));
localized_strings.SetString(L"noitems",
l10n_util::GetString(IDS_HISTORY_NO_ITEMS));
- localized_strings.SetString(L"deleteday",
- l10n_util::GetString(IDS_HISTORY_DELETE_PRIOR_VISITS_LINK));
- localized_strings.SetString(L"deletedaywarning",
+ localized_strings.SetString(L"edithistory",
+ l10n_util::GetString(IDS_HISTORY_START_EDITING_HISTORY));
+ localized_strings.SetString(L"doneediting",
+ l10n_util::GetString(IDS_HISTORY_STOP_EDITING_HISTORY));
+ localized_strings.SetString(L"removeselected",
+ l10n_util::GetString(IDS_HISTORY_REMOVE_SELECTED_ITEMS));
+ localized_strings.SetString(L"clearallhistory",
+ l10n_util::GetString(IDS_HISTORY_OPEN_CLEAR_BROWSING_DATA_DIALOG));
+ localized_strings.SetString(L"deletewarning",
l10n_util::GetString(IDS_HISTORY_DELETE_PRIOR_VISITS_WARNING));
SetFontAndTextDirection(&localized_strings);
@@ -100,14 +109,11 @@ void HistoryUIHTMLSource::StartDataRequest(const std::string& path,
//
////////////////////////////////////////////////////////////////////////////////
BrowsingHistoryHandler::BrowsingHistoryHandler()
- : search_text_(),
- remover_(NULL) {
+ : search_text_() {
}
BrowsingHistoryHandler::~BrowsingHistoryHandler() {
cancelable_consumer_.CancelAllRequests();
- if (remover_)
- remover_->RemoveObserver(this);
}
DOMMessageHandler* BrowsingHistoryHandler::Attach(DOMUI* dom_ui) {
@@ -130,8 +136,10 @@ void BrowsingHistoryHandler::RegisterMessages() {
NewCallback(this, &BrowsingHistoryHandler::HandleGetHistory));
dom_ui_->RegisterMessageCallback("searchHistory",
NewCallback(this, &BrowsingHistoryHandler::HandleSearchHistory));
- dom_ui_->RegisterMessageCallback("deleteDay",
- NewCallback(this, &BrowsingHistoryHandler::HandleDeleteDay));
+ dom_ui_->RegisterMessageCallback("removeURLsOnOneDay",
+ NewCallback(this, &BrowsingHistoryHandler::HandleRemoveURLsOnOneDay));
+ dom_ui_->RegisterMessageCallback("clearBrowsingData",
+ NewCallback(this, &BrowsingHistoryHandler::HandleClearBrowsingData));
}
void BrowsingHistoryHandler::HandleGetHistory(const Value* value) {
@@ -185,37 +193,50 @@ void BrowsingHistoryHandler::HandleSearchHistory(const Value* value) {
NewCallback(this, &BrowsingHistoryHandler::QueryComplete));
}
-void BrowsingHistoryHandler::HandleDeleteDay(const Value* value) {
- if (BrowsingDataRemover::is_removing()) {
+void BrowsingHistoryHandler::HandleRemoveURLsOnOneDay(const Value* value) {
+ if (cancelable_consumer_.HasPendingRequests()) {
dom_ui_->CallJavascriptFunction(L"deleteFailed");
return;
}
- // Anything in-flight is invalid.
- cancelable_consumer_.CancelAllRequests();
+ DCHECK(value && value->GetType() == Value::TYPE_LIST);
- // Get time.
- base::Time time;
- bool success = base::Time::FromString(ExtractStringValue(value).c_str(),
- &time);
- DCHECK(success);
-
- base::Time begin_time = time.LocalMidnight();
+ // Get day to delete data from.
+ int visit_time = 0;
+ ExtractIntegerValue(value, &visit_time);
+ base::Time::Exploded exploded;
+ base::Time::FromTimeT(
+ static_cast<time_t>(visit_time)).LocalExplode(&exploded);
+ exploded.hour = exploded.minute = exploded.second = exploded.millisecond = 0;
+ base::Time begin_time = base::Time::FromLocalExploded(exploded);
base::Time end_time = begin_time + base::TimeDelta::FromDays(1);
- remover_ = new BrowsingDataRemover(dom_ui_->GetProfile(),
- begin_time,
- end_time);
- remover_->AddObserver(this);
- remover_->Remove(BrowsingDataRemover::REMOVE_HISTORY |
- BrowsingDataRemover::REMOVE_CACHE);
+ // Get URLs.
+ std::set<GURL> urls;
+ const ListValue* list_value = static_cast<const ListValue*>(value);
+ for (ListValue::const_iterator v = list_value->begin() + 1;
+ v != list_value->end(); ++v) {
+ if ((*v)->GetType() != Value::TYPE_STRING)
+ continue;
+ const StringValue* string_value = static_cast<const StringValue*>(*v);
+ string16 string16_value;
+ if (!string_value->GetAsUTF16(&string16_value))
+ continue;
+ urls.insert(GURL(string16_value));
+ }
+
+ HistoryService* hs =
+ dom_ui_->GetProfile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ hs->ExpireHistoryBetween(urls, begin_time, end_time, &cancelable_consumer_,
+ NewCallback(this, &BrowsingHistoryHandler::RemoveComplete));
}
-void BrowsingHistoryHandler::OnBrowsingDataRemoverDone() {
- dom_ui_->CallJavascriptFunction(L"deleteComplete");
- // No need to remove ourselves as an observer as BrowsingDataRemover deletes
- // itself after we return.
- remover_ = NULL;
+void BrowsingHistoryHandler::HandleClearBrowsingData(const Value* value) {
+ // Anything in-flight is invalid.
+ cancelable_consumer_.CancelAllRequests();
+
+ dom_ui_->tab_contents()->delegate()->GetBrowser()->
+ OpenClearBrowsingDataDialog();
}
void BrowsingHistoryHandler::QueryComplete(
@@ -271,6 +292,11 @@ void BrowsingHistoryHandler::QueryComplete(
dom_ui_->CallJavascriptFunction(L"historyResult", info_value, results_value);
}
+void BrowsingHistoryHandler::RemoveComplete() {
+ // Some Visits were deleted from history. Reload the list.
+ dom_ui_->CallJavascriptFunction(L"deleteComplete");
+}
+
void BrowsingHistoryHandler::ExtractSearchHistoryArguments(const Value* value,
int* month, std::wstring* query) {
*month = 0;
@@ -292,9 +318,9 @@ void BrowsingHistoryHandler::ExtractSearchHistoryArguments(const Value* value,
list_member->GetType() == Value::TYPE_STRING) {
const StringValue* string_value =
static_cast<const StringValue*>(list_member);
- std::wstring wstring_value;
- string_value->GetAsString(&wstring_value);
- *month = StringToInt(WideToUTF16Hack(wstring_value));
+ string16 string16_value;
+ string_value->GetAsUTF16(&string16_value);
+ *month = StringToInt(string16_value);
}
}
}
diff --git a/chrome/browser/dom_ui/history_ui.h b/chrome/browser/dom_ui/history_ui.h
index c1d4262..a8fceb3 100644
--- a/chrome/browser/dom_ui/history_ui.h
+++ b/chrome/browser/dom_ui/history_ui.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -8,7 +8,6 @@
#include <string>
#include <vector>
-#include "chrome/browser/browsing_data_remover.h"
#include "chrome/browser/dom_ui/chrome_url_data_manager.h"
#include "chrome/browser/dom_ui/dom_ui.h"
#include "chrome/browser/cancelable_request.h"
@@ -38,8 +37,7 @@ class HistoryUIHTMLSource : public ChromeURLDataManager::DataSource {
// The handler for Javascript messages related to the "history" view.
class BrowsingHistoryHandler : public DOMMessageHandler,
- public NotificationObserver,
- public BrowsingDataRemover::Observer {
+ public NotificationObserver {
public:
BrowsingHistoryHandler();
virtual ~BrowsingHistoryHandler();
@@ -54,22 +52,25 @@ class BrowsingHistoryHandler : public DOMMessageHandler,
// Callback for the "searchHistory" message.
void HandleSearchHistory(const Value* value);
- // Callback for the "deleteDay" message.
- void HandleDeleteDay(const Value* value);
+ // Callback for the "removeURLsOnOneDay" message.
+ void HandleRemoveURLsOnOneDay(const Value* value);
+
+ // Handle for "clearBrowsingData" message.
+ void HandleClearBrowsingData(const Value* value);
// NotificationObserver implementation.
virtual void Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details);
- // BrowsingDataRemover observer implementation.
- void OnBrowsingDataRemoverDone();
-
private:
// Callback from the history system when the history list is available.
void QueryComplete(HistoryService::Handle request_handle,
history::QueryResults* results);
+ // Callback from the history system when visits were deleted.
+ void RemoveComplete();
+
// Extract the arguments from the call to HandleSearchHistory.
void ExtractSearchHistoryArguments(const Value* value,
int* month,
@@ -83,9 +84,6 @@ class BrowsingHistoryHandler : public DOMMessageHandler,
// Current search text.
std::wstring search_text_;
- // Browsing history remover
- BrowsingDataRemover* remover_;
-
// Our consumer for the history service.
CancelableRequestConsumerT<int, 0> cancelable_consumer_;
diff --git a/chrome/browser/extensions/extension_history_api.cc b/chrome/browser/extensions/extension_history_api.cc
index 75ed48a..af45f82 100644
--- a/chrome/browser/extensions/extension_history_api.cc
+++ b/chrome/browser/extensions/extension_history_api.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -336,8 +336,10 @@ bool DeleteRangeHistoryFunction::RunAsyncImpl() {
base::Time end_time;
EXTENSION_FUNCTION_VALIDATE(GetTimeFromValue(value, &end_time));
+ std::set<GURL> restrict_urls;
HistoryService* hs = profile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
hs->ExpireHistoryBetween(
+ restrict_urls,
begin_time,
end_time,
&cancelable_consumer_,
@@ -351,8 +353,10 @@ void DeleteRangeHistoryFunction::DeleteComplete() {
}
bool DeleteAllHistoryFunction::RunAsyncImpl() {
+ std::set<GURL> restrict_urls;
HistoryService* hs = profile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
hs->ExpireHistoryBetween(
+ restrict_urls,
base::Time::FromDoubleT(0), // From the beginning of the epoch.
base::Time::Now(), // To the current time.
&cancelable_consumer_,
diff --git a/chrome/browser/history/expire_history_backend.cc b/chrome/browser/history/expire_history_backend.cc
index 882a53c..2a01212 100644
--- a/chrome/browser/history/expire_history_backend.cc
+++ b/chrome/browser/history/expire_history_backend.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -212,19 +212,32 @@ void ExpireHistoryBackend::DeleteURL(const GURL& url) {
BroadcastDeleteNotifications(&dependencies);
}
-void ExpireHistoryBackend::ExpireHistoryBetween(Time begin_time,
- Time end_time) {
+void ExpireHistoryBackend::ExpireHistoryBetween(
+ const std::set<GURL>& restrict_urls, Time begin_time, Time end_time) {
if (!main_db_)
return;
// There may be stuff in the text database manager's temporary cache.
if (text_db_)
- text_db_->DeleteFromUncommitted(begin_time, end_time);
+ text_db_->DeleteFromUncommitted(restrict_urls, begin_time, end_time);
// Find the affected visits and delete them.
// TODO(brettw): bug 1171164: We should query the archived database here, too.
VisitVector visits;
main_db_->GetAllVisitsInRange(begin_time, end_time, 0, &visits);
+ if (!restrict_urls.empty()) {
+ std::set<URLID> url_ids;
+ for (std::set<GURL>::const_iterator url = restrict_urls.begin();
+ url != restrict_urls.end(); ++url)
+ url_ids.insert(main_db_->GetRowForURL(*url, NULL));
+ VisitVector all_visits;
+ all_visits.swap(visits);
+ for (VisitVector::iterator visit = all_visits.begin();
+ visit != all_visits.end(); ++visit) {
+ if (url_ids.find(visit->url_id) != url_ids.end())
+ visits.push_back(*visit);
+ }
+ }
if (visits.empty())
return;
@@ -375,8 +388,11 @@ void ExpireHistoryBackend::DeleteOneURL(
main_db_->DeleteSegmentForURL(url_row.id());
// The URL may be in the text database manager's temporary cache.
- if (text_db_)
- text_db_->DeleteURLFromUncommitted(url_row.url());
+ if (text_db_) {
+ std::set<GURL> restrict_urls;
+ restrict_urls.insert(url_row.url());
+ text_db_->DeleteFromUncommitted(restrict_urls, base::Time(), base::Time());
+ }
if (!is_bookmarked) {
dependencies->deleted_urls.push_back(url_row);
diff --git a/chrome/browser/history/expire_history_backend.h b/chrome/browser/history/expire_history_backend.h
index 5b7525c..11c0416 100644
--- a/chrome/browser/history/expire_history_backend.h
+++ b/chrome/browser/history/expire_history_backend.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -82,8 +82,10 @@ class ExpireHistoryBackend {
// Deletes everything associated with a URL.
void DeleteURL(const GURL& url);
- // Removes all visits in the given time range, updating the URLs accordingly.
- void ExpireHistoryBetween(base::Time begin_time, base::Time end_time);
+ // Removes all visits to restrict_urls (or all URLs if empty) in the given
+ // time range, updating the URLs accordingly,
+ void ExpireHistoryBetween(const std::set<GURL>& restrict_urls,
+ base::Time begin_time, base::Time end_time);
// Archives all visits before and including the given time, updating the URLs
// accordingly. This function is intended for migrating old databases
diff --git a/chrome/browser/history/expire_history_backend_unittest.cc b/chrome/browser/history/expire_history_backend_unittest.cc
index 03c4eef..2cdce64 100644
--- a/chrome/browser/history/expire_history_backend_unittest.cc
+++ b/chrome/browser/history/expire_history_backend_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -529,7 +529,8 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarred) {
visits[0].visit_time);
// This should delete the last two visits.
- expirer_.ExpireHistoryBetween(visit_times[2], Time());
+ std::set<GURL> restrict_urls;
+ expirer_.ExpireHistoryBetween(restrict_urls, visit_times[2], Time());
// Run the text database expirer. This will flush any pending entries so we
// can check that nothing was committed. We use a time far in the future so
@@ -562,6 +563,63 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarred) {
EXPECT_FALSE(HasFavIcon(url_row2.favicon_id()));
}
+// Expires only a specific URLs more recent than a given time, with no starred
+// items. Our time threshold is such that the URL should be updated (we delete
+// one of the two visits).
+TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarredRestricted) {
+ URLID url_ids[3];
+ Time visit_times[4];
+ AddExampleData(url_ids, visit_times);
+
+ URLRow url_row1, url_row2;
+ ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &url_row1));
+ ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &url_row2));
+
+ // In this test we also make sure that any pending entries in the text
+ // database manager are removed.
+ VisitVector visits;
+ main_db_->GetVisitsForURL(url_ids[2], &visits);
+ ASSERT_EQ(1U, visits.size());
+ text_db_->AddPageURL(url_row2.url(), url_row2.id(), visits[0].visit_id,
+ visits[0].visit_time);
+
+ // This should delete the last two visits.
+ std::set<GURL> restrict_urls;
+ restrict_urls.insert(url_row1.url());
+ expirer_.ExpireHistoryBetween(restrict_urls, visit_times[2], Time());
+
+ // Run the text database expirer. This will flush any pending entries so we
+ // can check that nothing was committed. We use a time far in the future so
+ // that anything added recently will get flushed.
+ TimeTicks expiration_time = TimeTicks::Now() + TimeDelta::FromDays(1);
+ text_db_->FlushOldChangesForTime(expiration_time);
+
+ // Verify that the middle URL had its last visit deleted only.
+ visits.clear();
+ main_db_->GetVisitsForURL(url_ids[1], &visits);
+ EXPECT_EQ(1U, visits.size());
+ EXPECT_EQ(0, CountTextMatchesForURL(url_row1.url()));
+
+ // Verify that the middle URL visit time and visit counts were updated.
+ URLRow temp_row;
+ ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &temp_row));
+ EXPECT_TRUE(visit_times[2] == url_row1.last_visit()); // Previous value.
+ EXPECT_TRUE(visit_times[1] == temp_row.last_visit()); // New value.
+ EXPECT_EQ(2, url_row1.visit_count());
+ EXPECT_EQ(1, temp_row.visit_count());
+ EXPECT_EQ(1, url_row1.typed_count());
+ EXPECT_EQ(0, temp_row.typed_count());
+
+ // Verify that the middle URL's favicon and thumbnail is still there.
+ EXPECT_TRUE(HasFavIcon(url_row1.favicon_id()));
+ EXPECT_TRUE(HasThumbnail(url_row1.id()));
+
+ // Verify that the last URL was not touched.
+ EXPECT_TRUE(main_db_->GetURLRow(url_ids[2], &temp_row));
+ EXPECT_TRUE(HasFavIcon(url_row2.favicon_id()));
+ EXPECT_TRUE(HasThumbnail(url_row2.id()));
+}
+
// Expire a starred URL, it shouldn't get deleted
TEST_F(ExpireHistoryTest, FlushRecentURLsStarred) {
URLID url_ids[3];
@@ -577,7 +635,8 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsStarred) {
StarURL(url_row2.url());
// This should delete the last two visits.
- expirer_.ExpireHistoryBetween(visit_times[2], Time());
+ std::set<GURL> restrict_urls;
+ expirer_.ExpireHistoryBetween(restrict_urls, visit_times[2], Time());
// The URL rows should still exist.
URLRow new_url_row1, new_url_row2;
diff --git a/chrome/browser/history/history.cc b/chrome/browser/history/history.cc
index 71d48f0..53ad780 100644
--- a/chrome/browser/history/history.cc
+++ b/chrome/browser/history/history.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -664,14 +664,15 @@ void HistoryService::DeleteURL(const GURL& url) {
}
void HistoryService::ExpireHistoryBetween(
+ const std::set<GURL>& restrict_urls,
Time begin_time, Time end_time,
CancelableRequestConsumerBase* consumer,
ExpireHistoryCallback* callback) {
// We will update the visited links when we observe the delete notifications.
Schedule(PRIORITY_UI, &HistoryBackend::ExpireHistoryBetween, consumer,
- new history::ExpireHistoryRequest(callback),
- begin_time, end_time);
+ new history::ExpireHistoryRequest(callback),
+ restrict_urls, begin_time, end_time);
}
void HistoryService::BroadcastNotifications(
diff --git a/chrome/browser/history/history.h b/chrome/browser/history/history.h
index 5d251c0..8f7c68c 100644
--- a/chrome/browser/history/history.h
+++ b/chrome/browser/history/history.h
@@ -359,7 +359,7 @@ class HistoryService : public CancelableRequestProvider,
// Delete all the information related to a single url.
void DeleteURL(const GURL& url);
- // Implemented by the caller of 'ExpireHistory(Since|Between)' below, and
+ // Implemented by the caller of ExpireHistoryBetween, and
// is called when the history service has deleted the history.
typedef Callback0::Type ExpireHistoryCallback;
@@ -369,7 +369,10 @@ class HistoryService : public CancelableRequestProvider,
// if they are no longer referenced. |callback| runs when the expiration is
// complete. You may use null Time values to do an unbounded delete in
// either direction.
- void ExpireHistoryBetween(base::Time begin_time, base::Time end_time,
+ // If |restrict_urls| is not empty, only visits to the URLs in this set are
+ // removed.
+ void ExpireHistoryBetween(const std::set<GURL>& restrict_urls,
+ base::Time begin_time, base::Time end_time,
CancelableRequestConsumerBase* consumer,
ExpireHistoryCallback* callback);
diff --git a/chrome/browser/history/history_backend.cc b/chrome/browser/history/history_backend.cc
index 4fdcef8..33c079a 100644
--- a/chrome/browser/history/history_backend.cc
+++ b/chrome/browser/history/history_backend.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -1740,19 +1740,20 @@ void HistoryBackend::DeleteURL(const GURL& url) {
void HistoryBackend::ExpireHistoryBetween(
scoped_refptr<ExpireHistoryRequest> request,
+ const std::set<GURL>& restrict_urls,
Time begin_time,
Time end_time) {
if (request->canceled())
return;
if (db_.get()) {
- if (begin_time.is_null() && end_time.is_null()) {
+ if (begin_time.is_null() && end_time.is_null() && restrict_urls.empty()) {
// Special case deleting all history so it can be faster and to reduce the
// possibility of an information leak.
DeleteAllHistory();
} else {
// Clearing parts of history, have the expirer do the depend
- expirer_.ExpireHistoryBetween(begin_time, end_time);
+ expirer_.ExpireHistoryBetween(restrict_urls, begin_time, end_time);
// Force a commit, if the user is deleting something for privacy reasons,
// we want to get it on disk ASAP.
@@ -1765,7 +1766,7 @@ void HistoryBackend::ExpireHistoryBetween(
request->ForwardResult(ExpireHistoryRequest::TupleType());
- if (history_publisher_.get())
+ if (history_publisher_.get() && restrict_urls.empty())
history_publisher_->DeleteUserHistoryBetween(begin_time, end_time);
}
diff --git a/chrome/browser/history/history_backend.h b/chrome/browser/history/history_backend.h
index 49569f5..54d8d63 100644
--- a/chrome/browser/history/history_backend.h
+++ b/chrome/browser/history/history_backend.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -245,6 +245,7 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
// Calls ExpireHistoryBackend::ExpireHistoryBetween and commits the change.
void ExpireHistoryBetween(scoped_refptr<ExpireHistoryRequest> request,
+ const std::set<GURL>& restrict_urls,
base::Time begin_time,
base::Time end_time);
diff --git a/chrome/browser/history/history_backend_unittest.cc b/chrome/browser/history/history_backend_unittest.cc
index 84f1d95..d3d28a4 100644
--- a/chrome/browser/history/history_backend_unittest.cc
+++ b/chrome/browser/history/history_backend_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -465,7 +465,9 @@ TEST_F(HistoryBackendTest, KeywordGenerated) {
EXPECT_TRUE(visits.empty());
// Expire the visits.
- backend_->expire_backend()->ExpireHistoryBetween(visit_time, Time::Now());
+ std::set<GURL> restrict_urls;
+ backend_->expire_backend()->ExpireHistoryBetween(restrict_urls,
+ visit_time, Time::Now());
// The visit should have been nuked.
visits.clear();
diff --git a/chrome/browser/history/text_database_manager.cc b/chrome/browser/history/text_database_manager.cc
index 149cd72..0b27203 100644
--- a/chrome/browser/history/text_database_manager.cc
+++ b/chrome/browser/history/text_database_manager.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -337,7 +337,8 @@ void TextDatabaseManager::DeletePageData(Time time, const GURL& url,
db->DeletePageData(time, URLDatabase::GURLToDatabaseURL(url));
}
-void TextDatabaseManager::DeleteFromUncommitted(Time begin, Time end) {
+void TextDatabaseManager::DeleteFromUncommitted(
+ const std::set<GURL>& restrict_urls, Time begin, Time end) {
// First find the beginning of the range to delete. Recall that the list
// has the most recent item at the beginning. There won't normally be very
// many items, so a brute-force search is fine.
@@ -352,15 +353,17 @@ void TextDatabaseManager::DeleteFromUncommitted(Time begin, Time end) {
// Now delete all visits up to the oldest one we were supposed to delete.
// Note that if begin is_null, it will be less than or equal to any other
// time.
- while (cur != recent_changes_.end() && cur->second.visit_time() >= begin)
- cur = recent_changes_.Erase(cur);
-}
-
-void TextDatabaseManager::DeleteURLFromUncommitted(const GURL& url) {
- RecentChangeList::iterator found = recent_changes_.Peek(url);
- if (found == recent_changes_.end())
- return; // We don't know about this page, give up.
- recent_changes_.Erase(found);
+ if (restrict_urls.empty()) {
+ while (cur != recent_changes_.end() && cur->second.visit_time() >= begin)
+ cur = recent_changes_.Erase(cur);
+ } else {
+ while (cur != recent_changes_.end() && cur->second.visit_time() >= begin) {
+ if (restrict_urls.find(cur->first) != restrict_urls.end())
+ cur = recent_changes_.Erase(cur);
+ else
+ ++cur;
+ }
+ }
}
void TextDatabaseManager::DeleteAll() {
diff --git a/chrome/browser/history/text_database_manager.h b/chrome/browser/history/text_database_manager.h
index b27b9db..cec9dea 100644
--- a/chrome/browser/history/text_database_manager.h
+++ b/chrome/browser/history/text_database_manager.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -124,14 +124,13 @@ class TextDatabaseManager {
// file AddPageURL/Title/Body that may not be committed to the database yet.
// This function removes entires from this list happening between the given
// time range. It is called when the user clears their history for a time
- // range, and we don't want any of our data to "leak."
+ // range, and we don't want any of our data to "leak." If restrict_urls is
+ // not empty, only changes on those URLs are deleted.
//
// Either or both times my be is_null to be unbounded in that direction. When
// non-null, the range is [begin, end).
- void DeleteFromUncommitted(base::Time begin, base::Time end);
-
- // Same as DeleteFromUncommitted but for a single URL.
- void DeleteURLFromUncommitted(const GURL& url);
+ void DeleteFromUncommitted(const std::set<GURL>& restrict_urls,
+ base::Time begin, base::Time end);
// Deletes all full text search data by removing the files from the disk.
// This must be called OUTSIDE of a transaction since it actually deletes the
@@ -161,6 +160,7 @@ class TextDatabaseManager {
FRIEND_TEST(TextDatabaseManagerTest, PartialComplete);
FRIEND_TEST(ExpireHistoryTest, DISABLED_DeleteURLAndFavicon);
FRIEND_TEST(ExpireHistoryTest, FlushRecentURLsUnstarred);
+ FRIEND_TEST(ExpireHistoryTest, FlushRecentURLsUnstarredRestricted);
// Stores "recent stuff" that has happened with the page, since the page
// visit, title, and body all come in at different times.
diff --git a/chrome/browser/resources/history.html b/chrome/browser/resources/history.html
index f32ef0b..39a23d8 100644
--- a/chrome/browser/resources/history.html
+++ b/chrome/browser/resources/history.html
@@ -37,6 +37,9 @@ var localStrings;
var pageState;
var deleteQueue = [];
var deleteInFlight = false;
+var selectionAnchor = -1;
+var selectionEnd = -1;
+var id2checkbox = [];
///////////////////////////////////////////////////////////////////////////////
@@ -47,12 +50,13 @@ var deleteInFlight = false;
* @param {boolean} continued Whether this page is on the same day as the
* page before it
*/
-function Page(result, continued, model) {
+function Page(result, continued, model, id) {
this.model_ = model;
this.title_ = result.title;
this.url_ = result.url;
this.starred_ = result.starred;
this.snippet_ = result.snippet || "";
+ this.id_ = id;
this.changed = false;
@@ -82,6 +86,15 @@ function Page(result, continued, model) {
Page.prototype.getBrowseResultDOM = function() {
var node = createElementWithClassName('div', 'entry');
var time = createElementWithClassName('div', 'time');
+ if (this.model_.getEditMode()) {
+ var checkbox = document.createElement('input');
+ checkbox.type = "checkbox";
+ checkbox.name = this.id_;
+ checkbox.time = this.time.toString();
+ checkbox.addEventListener("click", checkboxClicked, false);
+ id2checkbox[this.id_] = checkbox;
+ time.appendChild(checkbox);
+ }
time.appendChild(document.createTextNode(this.dateTimeOfDay));
node.appendChild(time);
node.appendChild(this.getTitleDOM_());
@@ -131,6 +144,7 @@ Page.prototype.getTitleDOM_ = function() {
link.style.backgroundImage =
'url(chrome://favicon/' + encodeURIForCSS(this.url_) + ')';
link.appendChild(document.createTextNode(this.title_));
+ link.id = "id-" + this.id_;
this.highlightNodeContent_(link);
node.appendChild(link);
@@ -180,6 +194,7 @@ Page.pregQuote_ = function(str) {
*/
function HistoryModel() {
this.clearModel_();
+ this.setEditMode(false);
this.view_;
}
@@ -266,7 +281,8 @@ HistoryModel.prototype.addResults = function(info, results) {
if (!lastURL || lastURL != thisURL) {
// Figure out if this page is in the same day as the previous page,
// this is used to determine how day headers should be drawn.
- this.pages_.push(new Page(thisResult, thisDay == lastDay, this));
+ this.pages_.push(new Page(thisResult, thisDay == lastDay, this,
+ this.last_id_++));
lastDay = thisDay;
lastURL = thisURL;
}
@@ -307,12 +323,31 @@ HistoryModel.prototype.getNumberedRange = function(start, end) {
return this.pages_.slice(start, end);
}
+/**
+ * @return {boolean} Whether we are in edit mode where history items can be
+ * deleted
+ */
+HistoryModel.prototype.getEditMode = function() {
+ return this.editMode_;
+}
+
+/**
+ * @param {boolean} edit_mode Control whether we are in edit mode.
+ */
+HistoryModel.prototype.setEditMode = function(edit_mode) {
+ this.editMode_ = edit_mode;
+}
+
// HistoryModel, Private: -----------------------------------------------------
HistoryModel.prototype.clearModel_ = function() {
this.inFlight_ = false; // Whether a query is inflight.
this.searchText_ = '';
this.searchDepth_ = 0;
this.pages_ = []; // Date-sorted list of pages.
+ this.last_id_ = 0;
+ selectionAnchor = -1;
+ selectionEnd = -1;
+ id2checkbox = [];
// The page that the view wants to see - we only fetch slightly past this
// point. If the view requests a page that we don't have data for, we try
@@ -404,8 +439,10 @@ HistoryModel.prototype.canFillPage_ = function(page) {
* @param {HistoryModel} model The model backing this view.
*/
function HistoryView(model) {
- this.summaryDiv_ = $('results-summary');
- this.summaryDiv_.innerHTML = localStrings.getString('loading');
+ this.summaryTd_ = $('results-summary');
+ this.summaryTd_.innerHTML = localStrings.getString('loading');
+ this.editButtonTd_ = $('edit-button');
+ this.editingControlsDiv_ = $('editing-controls');
this.resultDiv_ = $('results-display');
this.pageDiv_ = $('results-pagination');
this.model_ = model
@@ -420,6 +457,7 @@ function HistoryView(model) {
window.onresize = function() {
self.updateEntryAnchorWidth_();
};
+ self.updateEditControls_();
}
// HistoryView, public: -------------------------------------------------------
@@ -433,7 +471,30 @@ HistoryView.prototype.setSearch = function(term, opt_page) {
this.pageIndex_ = parseInt(opt_page || 0, 10);
window.scrollTo(0, 0);
this.model_.setSearchText(term, this.pageIndex_);
- pageState.setUIState(term, this.pageIndex_);
+ if (term) {
+ this.setEditMode(false);
+ }
+ this.updateEditControls_();
+ pageState.setUIState(this.model_.getEditMode(), term, this.pageIndex_);
+}
+
+/**
+ * Controls edit mode where history can be deleted.
+ * @param {boolean} edit_mode Whether to enable edit mode.
+ */
+HistoryView.prototype.setEditMode = function(edit_mode) {
+ this.model_.setEditMode(edit_mode);
+ pageState.setUIState(this.model_.getEditMode(), this.model_.getSearchText(),
+ this.pageIndex_);
+}
+
+/**
+ * Toggles the edit mode and triggers UI update.
+ */
+HistoryView.prototype.toggleEditMode = function() {
+ var editMode = !this.model_.getEditMode();
+ this.setEditMode(editMode);
+ this.updateEditControls_();
}
/**
@@ -452,7 +513,8 @@ HistoryView.prototype.setPage = function(page) {
this.pageIndex_ = parseInt(page, 10);
window.scrollTo(0, 0);
this.model_.requestPage(page);
- pageState.setUIState(this.model_.getSearchText(), this.pageIndex_);
+ pageState.setUIState(this.model_.getEditMode(), this.model_.getSearchText(),
+ this.pageIndex_);
}
/**
@@ -528,13 +590,6 @@ HistoryView.prototype.displayResults_ = function() {
localStrings.getString('cont')));
}
- var link = createElementWithClassName('button', 'delete-day');
- link.time = page.time.toString();
- link.onclick = deleteDay;
- link.appendChild(
- document.createTextNode(localStrings.getString("deleteday")));
-
- day.appendChild(link);
this.resultDiv_.appendChild(day);
} else if (lastTime - thisTime > BROWSING_GAP_TIME) {
this.resultDiv_.appendChild(createElementWithClassName('div', 'gap'));
@@ -558,10 +613,51 @@ HistoryView.prototype.displayResults_ = function() {
HistoryView.prototype.displaySummaryBar_ = function() {
var searchText = this.model_.getSearchText();
if (searchText != '') {
- this.summaryDiv_.textContent = localStrings.formatString('searchresultsfor',
+ this.summaryTd_.textContent = localStrings.formatString('searchresultsfor',
searchText);
} else {
- this.summaryDiv_.innerHTML = localStrings.getString('history');
+ this.summaryTd_.innerHTML = localStrings.getString('history');
+ }
+}
+
+/**
+ * Update the widgets related to edit mode.
+ */
+HistoryView.prototype.updateEditControls_ = function() {
+ // Display a button (looking like a link) to enable/disable edit mode.
+ var oldButton = this.editButtonTd_.firstChild;
+ if (this.model_.getSearchText()) {
+ this.editButtonTd_.replaceChild(document.createElement('p'), oldButton);
+ while (this.editingControlsDiv_.hasChildNodes()) {
+ this.editingControlsDiv_.removeChild(this.editingControlsDiv_.firstChild);
+ }
+ return;
+ }
+
+ var editMode = this.model_.getEditMode();
+ var button = createElementWithClassName('button', 'edit-button');
+ button.onclick = toggleEditMode;
+ button.appendChild(document.createTextNode(editMode ?
+ localStrings.getString("doneediting") :
+ localStrings.getString("edithistory")));
+ this.editButtonTd_.replaceChild(button, oldButton);
+ if (editMode) {
+ // Button to delete the selected items.
+ button = document.createElement('button');
+ button.onclick = removeItems;
+ button.appendChild(document.createTextNode(
+ localStrings.getString("removeselected")));
+ this.editingControlsDiv_.appendChild(button);
+ // Button that opens up the clear browsing data dialog.
+ button = document.createElement('button');
+ button.onclick = openClearBrowsingData;
+ button.appendChild(document.createTextNode(
+ localStrings.getString("clearallhistory")));
+ this.editingControlsDiv_.appendChild(button);
+ } else {
+ while (this.editingControlsDiv_.hasChildNodes()) {
+ this.editingControlsDiv_.removeChild(this.editingControlsDiv_.firstChild);
+ }
}
}
@@ -592,7 +688,8 @@ HistoryView.prototype.displayNavBar_ = function() {
* @return {string} HTML representation of the pagination link
*/
HistoryView.prototype.createPageNavHTML_ = function(page, name) {
- var hashString = PageState.getHashString(this.model_.getSearchText(), page);
+ var hashString = PageState.getHashString(this.model_.getEditMode(),
+ this.model_.getSearchText(), page);
return '<a href="chrome://history/' +
(hashString ? '#' + hashString : '') +
'"' +
@@ -613,23 +710,24 @@ HistoryView.prototype.updateEntryAnchorWidth_ = function() {
return;
// Create new CSS rules and add them last to the last stylesheet.
- if (!this.entryAnchorRule_) {
- var styleSheets = document.styleSheets;
- var styleSheet = styleSheets[styleSheets.length - 1];
- var rules = styleSheet.cssRules;
- var createRule = function(selector) {
- styleSheet.insertRule(selector + '{}', rules.length);
- return rules[rules.length - 1];
- };
- this.entryAnchorRule_ = createRule('.entry .title > a');
- // The following rule needs to be more specific to have higher priority.
- this.entryAnchorStarredRule_ = createRule('.entry .title.starred > a');
- }
-
- var anchorMaxWith = titleElement.offsetWidth;
- this.entryAnchorRule_.style.maxWidth = anchorMaxWith + 'px';
- // Adjust by the width of star plus its margin.
- this.entryAnchorStarredRule_.style.maxWidth = anchorMaxWith - 23 + 'px';
+ // TODO(jochen): The following code does not work due to WebKit bug #32309
+ // if (!this.entryAnchorRule_) {
+ // var styleSheets = document.styleSheets;
+ // var styleSheet = styleSheets[styleSheets.length - 1];
+ // var rules = styleSheet.cssRules;
+ // var createRule = function(selector) {
+ // styleSheet.insertRule(selector + '{}', rules.length);
+ // return rules[rules.length - 1];
+ // };
+ // this.entryAnchorRule_ = createRule('.entry .title > a');
+ // // The following rule needs to be more specific to have higher priority.
+ // this.entryAnchorStarredRule_ = createRule('.entry .title.starred > a');
+ // }
+ //
+ // var anchorMaxWith = titleElement.offsetWidth;
+ // this.entryAnchorRule_.style.maxWidth = anchorMaxWith + 'px';
+ // // Adjust by the width of star plus its margin.
+ // this.entryAnchorStarredRule_.style.maxWidth = anchorMaxWith - 23 + 'px';
};
///////////////////////////////////////////////////////////////////////////////
@@ -672,6 +770,7 @@ PageState.instance = null;
*/
PageState.prototype.getHashData = function() {
var result = {
+ e : 0,
q : '',
p : 0
};
@@ -698,12 +797,13 @@ PageState.prototype.getHashData = function() {
* @param {string} term The current search string.
* @param {string} page The page currently being viewed.
*/
-PageState.prototype.setUIState = function(term, page) {
+PageState.prototype.setUIState = function(editMode, term, page) {
// Make sure the form looks pretty.
document.forms[0].term.value = term;
var currentHash = this.getHashData();
- if (currentHash.q != term || currentHash.p != page) {
- window.location.hash = PageState.getHashString(term, page);
+ if (Boolean(currentHash.e) != editMode || currentHash.q != term ||
+ currentHash.p != page) {
+ window.location.hash = PageState.getHashString(editMode, term, page);
}
}
@@ -713,8 +813,11 @@ PageState.prototype.setUIState = function(term, page) {
* @param {string} page The page currently being viewed.
* @return {string} The string to be used in a hash.
*/
-PageState.getHashString = function(term, page) {
+PageState.getHashString = function(editMode, term, page) {
var newHash = [];
+ if (editMode) {
+ newHash.push("e=1");
+ }
if (term) {
newHash.push("q=" + encodeURIComponent(term));
}
@@ -740,6 +843,9 @@ function load() {
// Create default view.
var hashData = pageState.getHashData();
+ if (Boolean(hashData.e)) {
+ historyView.toggleEditMode();
+ }
historyView.setSearch(hashData.q, hashData.p);
}
@@ -766,25 +872,14 @@ function setPage(page) {
}
/**
- * Delete a day from history.
- * TODO: Add UI to indicate that something is happening.
- * @param {number} time A time from the day we wish to delete.
+ * TODO(glen): Get rid of this function.
+ * Toggles edit mode.
*/
-function deleteDay() {
- var time = this.time;
-
- // Check to see if item is already being deleted.
- for (var i = 0, deleting; deleting = deleteQueue[i]; i++) {
- if (deleting == time)
- return false;
- }
-
- if (confirm(localStrings.getString("deletedaywarning"))) {
- deleteQueue.push(time);
- deleteNextInQueue();
+function toggleEditMode() {
+ if (historyView) {
+ historyView.toggleEditMode();
+ historyView.reload();
}
-
- return false;
}
/**
@@ -793,7 +888,101 @@ function deleteDay() {
function deleteNextInQueue() {
if (!deleteInFlight && deleteQueue.length) {
deleteInFlight = true;
- chrome.send("deleteDay", [deleteQueue[0]]);
+ chrome.send("removeURLsOnOneDay",
+ [String(deleteQueue[0])].concat(deleteQueue[1]));
+ }
+}
+
+/**
+ * Open the clear browsing data dialog.
+ */
+function openClearBrowsingData() {
+ chrome.send("clearBrowsingData", []);
+ return false;
+}
+
+/**
+ * Collect IDs from checked checkboxes and send to Chrome for deletion.
+ */
+function removeItems() {
+ var checkboxes = document.getElementsByTagName('input');
+ var ids = [];
+ var queue = [];
+ var date = new Date();
+ for (var i = 0; i < checkboxes.length; i++) {
+ if (checkboxes[i].type == "checkbox" && checkboxes[i].checked &&
+ !checkboxes[i].disabled) {
+ var cbDate = new Date(checkboxes[i].time);
+ if (date.getFullYear() != cbDate.getFullYear() ||
+ date.getMonth() != cbDate.getMonth() ||
+ date.getDate() != cbDate.getDate()) {
+ if (ids.length > 0) {
+ queue.push(date.valueOf() / 1000);
+ queue.push(ids);
+ }
+ ids = [];
+ date = cbDate;
+ }
+ var link = $("id-" + checkboxes[i].name);
+ checkboxes[i].disabled = true;
+ link.style.setProperty("text-decoration", "line-through");
+ ids.push(link.href);
+ }
+ }
+ if (ids.length > 0) {
+ queue.push(date.valueOf() / 1000);
+ queue.push(ids);
+ }
+ if (queue.length > 0) {
+ if (confirm(localStrings.getString("deletewarning"))) {
+ deleteQueue = deleteQueue.concat(queue);
+ deleteNextInQueue();
+ }
+ }
+ return false;
+}
+
+/**
+ * Toggle state of checkbox and handle CTRL and Shift modifiers
+ */
+function checkboxClicked(event) {
+ if ((selectionAnchor == -1) || !event.shiftKey) {
+ selectionAnchor = this.name;
+ selectionEnd = this.name;
+ }
+ if (event.shiftKey) {
+ if (((selectionAnchor >= selectionEnd) && (selectionAnchor >= this.name)) ||
+ ((selectionAnchor <= selectionEnd) && (selectionAnchor <= this.name))) {
+ // If the click was on the same side of the anchor as the last click,
+ // extend the current selection.
+ var begin = selectionEnd < this.name ? selectionEnd : this.name;
+ var end = selectionEnd < this.name ? this.name : selectionEnd;
+ var checked = true;
+ if (((selectionAnchor <= this.name) && (this.name <= selectionEnd)) ||
+ ((selectionEnd <= this.name) && (this.name <= selectionAnchor))) {
+ checked = false;
+ }
+ for (var i = begin; i <= end; i++) {
+ if (!id2checkbox[i].disabled) {
+ id2checkbox[i].checked = checked;
+ }
+ }
+ this.checked = true;
+ selectionEnd = this.name;
+ } else {
+ // Otherwise, the last clicked checkbox becomes the new anchor, and
+ // everything between the last anchor and the newly clicked checkbox is
+ // enabled.
+ var begin = selectionAnchor < this.name ? selectionAnchor : this.name;
+ var end = selectionAnchor < this.name ? this.name : selectionAnchor;
+ for (var i = begin; i <= end; i++) {
+ if (!id2checkbox[i].disabled) {
+ id2checkbox[i].checked = true;
+ }
+ }
+ selectionAnchor = selectionEnd;
+ selectionEnd = this.name;
+ }
}
}
@@ -813,8 +1002,11 @@ function deleteComplete() {
window.console.log("Delete complete");
deleteInFlight = false;
if (deleteQueue.length > 1) {
- deleteQueue = deleteQueue.slice(1, deleteQueue.length);
+ deleteQueue = deleteQueue.slice(2);
deleteNextInQueue();
+ } else {
+ deleteQueue = [];
+ historyView.reload();
}
}
@@ -840,7 +1032,7 @@ function historyDeleted() {
</script>
<link rel="stylesheet" href="dom_ui.css">
<style>
-#results-summary {
+#results-separator {
margin-top:12px;
border-top:1px solid #9cc2ef;
background-color:#ebeff9;
@@ -848,6 +1040,26 @@ function historyDeleted() {
padding:3px;
margin-bottom:-8px;
}
+#results-separator table {
+ width: 100%;
+}
+#results-summary {
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ width: 50%;
+}
+#edit-button {
+ text-align: right;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ width: 50%;
+}
+#editing-controls button {
+ margin-top: 18px;
+ margin-bottom: -8px;
+}
#results-display {
max-width:740px;
}
@@ -856,7 +1068,7 @@ function historyDeleted() {
padding:0px 3px;
display:inline-block;
}
-.delete-day {
+.edit-button {
display: inline;
-webkit-appearance: none;
background: none;
@@ -952,7 +1164,12 @@ html[dir='rtl'] .entry .title > a {
</form>
</div>
<div class="main">
- <div id="results-summary"></div>
+ <div id="results-separator">
+ <table border="0" cellPadding="0" cellSpacing="0">
+ <tr><td id="results-summary"></td><td id="edit-button"><p></p></td></tr>
+ </table>
+ </div>
+ <div id="editing-controls"></div>
<div id="results-display"></div>
<div id="results-pagination"></div>
</div>