| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/ui/webui/browsing_history_handler.h" | 5 #include "chrome/browser/ui/webui/browsing_history_handler.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <set> | 9 #include <set> |
| 10 #include <utility> | 10 #include <utility> |
| 11 | 11 |
| 12 #include "base/bind.h" | 12 #include "base/bind.h" |
| 13 #include "base/bind_helpers.h" | 13 #include "base/bind_helpers.h" |
| 14 #include "base/i18n/rtl.h" | 14 #include "base/i18n/rtl.h" |
| 15 #include "base/i18n/time_formatting.h" | 15 #include "base/i18n/time_formatting.h" |
| 16 #include "base/logging.h" | 16 #include "base/logging.h" |
| 17 #include "base/metrics/histogram_macros.h" | |
| 18 #include "base/strings/string16.h" | 17 #include "base/strings/string16.h" |
| 19 #include "base/strings/string_number_conversions.h" | 18 #include "base/strings/string_number_conversions.h" |
| 20 #include "base/strings/utf_string_conversions.h" | 19 #include "base/strings/utf_string_conversions.h" |
| 21 #include "base/time/default_clock.h" | 20 #include "base/time/default_clock.h" |
| 22 #include "base/time/time.h" | 21 #include "base/time/time.h" |
| 23 #include "base/values.h" | 22 #include "base/values.h" |
| 24 #include "chrome/browser/banners/app_banner_settings_helper.h" | |
| 25 #include "chrome/browser/bookmarks/bookmark_model_factory.h" | 23 #include "chrome/browser/bookmarks/bookmark_model_factory.h" |
| 26 #include "chrome/browser/engagement/site_engagement_service.h" | |
| 27 #include "chrome/browser/favicon/fallback_icon_service_factory.h" | 24 #include "chrome/browser/favicon/fallback_icon_service_factory.h" |
| 28 #include "chrome/browser/favicon/large_icon_service_factory.h" | 25 #include "chrome/browser/favicon/large_icon_service_factory.h" |
| 29 #include "chrome/browser/history/history_service_factory.h" | |
| 30 #include "chrome/browser/history/history_utils.h" | |
| 31 #include "chrome/browser/history/web_history_service_factory.h" | |
| 32 #include "chrome/browser/profiles/profile.h" | 26 #include "chrome/browser/profiles/profile.h" |
| 33 #include "chrome/browser/sync/profile_sync_service_factory.h" | 27 #include "chrome/browser/sync/profile_sync_service_factory.h" |
| 34 #include "chrome/browser/ui/browser_finder.h" | 28 #include "chrome/browser/ui/browser_finder.h" |
| 35 #include "chrome/browser/ui/chrome_pages.h" | 29 #include "chrome/browser/ui/chrome_pages.h" |
| 36 #include "chrome/browser/ui/webui/favicon_source.h" | 30 #include "chrome/browser/ui/webui/favicon_source.h" |
| 37 #include "chrome/browser/ui/webui/large_icon_source.h" | 31 #include "chrome/browser/ui/webui/large_icon_source.h" |
| 38 #include "chrome/common/features.h" | 32 #include "chrome/common/features.h" |
| 39 #include "chrome/common/pref_names.h" | |
| 40 #include "components/bookmarks/browser/bookmark_model.h" | 33 #include "components/bookmarks/browser/bookmark_model.h" |
| 41 #include "components/bookmarks/browser/bookmark_utils.h" | 34 #include "components/bookmarks/browser/bookmark_utils.h" |
| 42 #include "components/browser_sync/profile_sync_service.h" | 35 #include "components/browser_sync/profile_sync_service.h" |
| 43 #include "components/browsing_data/core/history_notice_utils.h" | |
| 44 #include "components/favicon/core/fallback_icon_service.h" | 36 #include "components/favicon/core/fallback_icon_service.h" |
| 45 #include "components/favicon/core/fallback_url_util.h" | 37 #include "components/favicon/core/fallback_url_util.h" |
| 46 #include "components/favicon/core/large_icon_service.h" | 38 #include "components/favicon/core/large_icon_service.h" |
| 47 #include "components/history/core/browser/history_service.h" | |
| 48 #include "components/history/core/browser/history_types.h" | |
| 49 #include "components/history/core/browser/web_history_service.h" | |
| 50 #include "components/keyed_service/core/service_access_type.h" | |
| 51 #include "components/prefs/pref_service.h" | |
| 52 #include "components/query_parser/snippet.h" | 39 #include "components/query_parser/snippet.h" |
| 53 #include "components/strings/grit/components_strings.h" | 40 #include "components/strings/grit/components_strings.h" |
| 54 #include "components/sync/device_info/device_info.h" | 41 #include "components/sync/device_info/device_info.h" |
| 55 #include "components/sync/device_info/device_info_tracker.h" | 42 #include "components/sync/device_info/device_info_tracker.h" |
| 56 #include "components/sync/driver/sync_service_observer.h" | |
| 57 #include "components/sync/protocol/history_delete_directive_specifics.pb.h" | |
| 58 #include "components/sync/protocol/sync_enums.pb.h" | |
| 59 #include "components/url_formatter/url_formatter.h" | 43 #include "components/url_formatter/url_formatter.h" |
| 60 #include "content/public/browser/url_data_source.h" | 44 #include "content/public/browser/url_data_source.h" |
| 61 #include "content/public/browser/web_ui.h" | 45 #include "content/public/browser/web_ui.h" |
| 62 #include "extensions/features/features.h" | 46 #include "extensions/features/features.h" |
| 63 #include "ui/base/l10n/l10n_util.h" | 47 #include "ui/base/l10n/l10n_util.h" |
| 64 #include "ui/base/l10n/time_format.h" | 48 #include "ui/base/l10n/time_format.h" |
| 65 | 49 |
| 66 #if BUILDFLAG(ENABLE_EXTENSIONS) | |
| 67 #include "chrome/browser/extensions/activity_log/activity_log.h" | |
| 68 #endif | |
| 69 | |
| 70 #if BUILDFLAG(ENABLE_SUPERVISED_USERS) | 50 #if BUILDFLAG(ENABLE_SUPERVISED_USERS) |
| 71 #include "chrome/browser/supervised_user/supervised_user_navigation_observer.h" | 51 #include "chrome/browser/supervised_user/supervised_user_navigation_observer.h" |
| 72 #include "chrome/browser/supervised_user/supervised_user_service.h" | 52 #include "chrome/browser/supervised_user/supervised_user_service.h" |
| 73 #include "chrome/browser/supervised_user/supervised_user_service_factory.h" | 53 #include "chrome/browser/supervised_user/supervised_user_service_factory.h" |
| 74 #include "chrome/browser/supervised_user/supervised_user_url_filter.h" | 54 #include "chrome/browser/supervised_user/supervised_user_url_filter.h" |
| 75 #endif | 55 #endif |
| 76 | 56 |
| 77 #if BUILDFLAG(ANDROID_JAVA_UI) | 57 #if BUILDFLAG(ANDROID_JAVA_UI) |
| 78 #include "chrome/browser/android/chrome_application.h" | 58 #include "chrome/browser/android/chrome_application.h" |
| 79 #endif | 59 #endif |
| 80 | 60 |
| 81 #if !defined(OS_ANDROID) | 61 #if !defined(OS_ANDROID) |
| 82 #include "chrome/browser/ui/webui/md_history_ui.h" | 62 #include "chrome/browser/ui/webui/md_history_ui.h" |
| 83 #endif | 63 #endif |
| 84 | 64 |
| 85 // The amount of time to wait for a response from the WebHistoryService. | |
| 86 static const int kWebHistoryTimeoutSeconds = 3; | |
| 87 | |
| 88 // Number of chars to truncate titles when making them "short". | 65 // Number of chars to truncate titles when making them "short". |
| 89 static const size_t kShortTitleLength = 300; | 66 static const size_t kShortTitleLength = 300; |
| 90 | 67 |
| 91 using bookmarks::BookmarkModel; | 68 using bookmarks::BookmarkModel; |
| 92 | 69 |
| 93 namespace { | 70 namespace { |
| 94 | 71 |
| 95 // Buckets for UMA histograms. | |
| 96 enum WebHistoryQueryBuckets { | |
| 97 WEB_HISTORY_QUERY_FAILED = 0, | |
| 98 WEB_HISTORY_QUERY_SUCCEEDED, | |
| 99 WEB_HISTORY_QUERY_TIMED_OUT, | |
| 100 NUM_WEB_HISTORY_QUERY_BUCKETS | |
| 101 }; | |
| 102 | |
| 103 // Identifiers for the type of device from which a history entry originated. | 72 // Identifiers for the type of device from which a history entry originated. |
| 104 static const char kDeviceTypeLaptop[] = "laptop"; | 73 static const char kDeviceTypeLaptop[] = "laptop"; |
| 105 static const char kDeviceTypePhone[] = "phone"; | 74 static const char kDeviceTypePhone[] = "phone"; |
| 106 static const char kDeviceTypeTablet[] = "tablet"; | 75 static const char kDeviceTypeTablet[] = "tablet"; |
| 107 | 76 |
| 108 // Returns a localized version of |visit_time| including a relative | 77 // Returns a localized version of |visit_time| including a relative |
| 109 // indicator (e.g. today, yesterday). | 78 // indicator (e.g. today, yesterday). |
| 110 base::string16 GetRelativeDateLocalized(base::Clock* clock, | 79 base::string16 GetRelativeDateLocalized(base::Clock* clock, |
| 111 const base::Time& visit_time) { | 80 const base::Time& visit_time) { |
| 112 base::Time midnight = clock->Now().LocalMidnight(); | 81 base::Time midnight = clock->Now().LocalMidnight(); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 124 | 93 |
| 125 // Sets the correct year when substracting months from a date. | 94 // Sets the correct year when substracting months from a date. |
| 126 void NormalizeMonths(base::Time::Exploded* exploded) { | 95 void NormalizeMonths(base::Time::Exploded* exploded) { |
| 127 // Decrease a year at a time until we have a proper date. | 96 // Decrease a year at a time until we have a proper date. |
| 128 while (exploded->month < 1) { | 97 while (exploded->month < 1) { |
| 129 exploded->month += 12; | 98 exploded->month += 12; |
| 130 exploded->year--; | 99 exploded->year--; |
| 131 } | 100 } |
| 132 } | 101 } |
| 133 | 102 |
| 134 // Returns true if |entry| represents a local visit that had no corresponding | |
| 135 // visit on the server. | |
| 136 bool IsLocalOnlyResult(const BrowsingHistoryHandler::HistoryEntry& entry) { | |
| 137 return entry.entry_type == BrowsingHistoryHandler::HistoryEntry::LOCAL_ENTRY; | |
| 138 } | |
| 139 | |
| 140 // Gets the name and type of a device for the given sync client ID. | 103 // Gets the name and type of a device for the given sync client ID. |
| 141 // |name| and |type| are out parameters. | 104 // |name| and |type| are out parameters. |
| 142 void GetDeviceNameAndType(const browser_sync::ProfileSyncService* sync_service, | 105 void GetDeviceNameAndType(const browser_sync::ProfileSyncService* sync_service, |
| 143 const std::string& client_id, | 106 const std::string& client_id, |
| 144 std::string* name, | 107 std::string* name, |
| 145 std::string* type) { | 108 std::string* type) { |
| 146 // DeviceInfoTracker must be syncing in order for remote history entries to | 109 // DeviceInfoTracker must be syncing in order for remote history entries to |
| 147 // be available. | 110 // be available. |
| 148 DCHECK(sync_service); | 111 DCHECK(sync_service); |
| 149 DCHECK(sync_service->GetDeviceInfoTracker()); | 112 DCHECK(sync_service->GetDeviceInfoTracker()); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 163 default: | 126 default: |
| 164 *type = kDeviceTypeLaptop; | 127 *type = kDeviceTypeLaptop; |
| 165 } | 128 } |
| 166 return; | 129 return; |
| 167 } | 130 } |
| 168 | 131 |
| 169 *name = l10n_util::GetStringUTF8(IDS_HISTORY_UNKNOWN_DEVICE); | 132 *name = l10n_util::GetStringUTF8(IDS_HISTORY_UNKNOWN_DEVICE); |
| 170 *type = kDeviceTypeLaptop; | 133 *type = kDeviceTypeLaptop; |
| 171 } | 134 } |
| 172 | 135 |
| 173 void RecordMetricsForNoticeAboutOtherFormsOfBrowsingHistory(bool shown) { | 136 // Formats |entry|'s URL and title and adds them to |result|. |
| 174 UMA_HISTOGRAM_BOOLEAN( | 137 void SetHistoryEntryUrlAndTitle(BrowsingHistoryService::HistoryEntry* entry, |
| 175 "History.ShownHeaderAboutOtherFormsOfBrowsingHistory", | 138 base::DictionaryValue* result, |
| 176 shown); | 139 bool limit_title_length) { |
| 177 } | 140 result->SetString("url", entry->url.spec()); |
| 178 | |
| 179 } // namespace | |
| 180 | |
| 181 BrowsingHistoryHandler::HistoryEntry::HistoryEntry( | |
| 182 BrowsingHistoryHandler::HistoryEntry::EntryType entry_type, | |
| 183 const GURL& url, | |
| 184 const base::string16& title, | |
| 185 base::Time time, | |
| 186 const std::string& client_id, | |
| 187 bool is_search_result, | |
| 188 const base::string16& snippet, | |
| 189 bool blocked_visit, | |
| 190 base::Clock* clock) { | |
| 191 this->entry_type = entry_type; | |
| 192 this->url = url; | |
| 193 this->title = title; | |
| 194 this->time = time; | |
| 195 this->client_id = client_id; | |
| 196 all_timestamps.insert(time.ToInternalValue()); | |
| 197 this->is_search_result = is_search_result; | |
| 198 this->snippet = snippet; | |
| 199 this->blocked_visit = blocked_visit; | |
| 200 this->clock = clock; | |
| 201 } | |
| 202 | |
| 203 BrowsingHistoryHandler::HistoryEntry::HistoryEntry() | |
| 204 : entry_type(EMPTY_ENTRY), is_search_result(false), blocked_visit(false) { | |
| 205 } | |
| 206 | |
| 207 BrowsingHistoryHandler::HistoryEntry::HistoryEntry(const HistoryEntry& other) = | |
| 208 default; | |
| 209 | |
| 210 BrowsingHistoryHandler::HistoryEntry::~HistoryEntry() { | |
| 211 } | |
| 212 | |
| 213 void BrowsingHistoryHandler::HistoryEntry::SetUrlAndTitle( | |
| 214 base::DictionaryValue* result, | |
| 215 bool limit_title_length) const { | |
| 216 result->SetString("url", url.spec()); | |
| 217 | 141 |
| 218 bool using_url_as_the_title = false; | 142 bool using_url_as_the_title = false; |
| 219 base::string16 title_to_set(title); | 143 base::string16 title_to_set(entry->title); |
| 220 if (title.empty()) { | 144 if (entry->title.empty()) { |
| 221 using_url_as_the_title = true; | 145 using_url_as_the_title = true; |
| 222 title_to_set = base::UTF8ToUTF16(url.spec()); | 146 title_to_set = base::UTF8ToUTF16(entry->url.spec()); |
| 223 } | 147 } |
| 224 | 148 |
| 225 // Since the title can contain BiDi text, we need to mark the text as either | 149 // Since the title can contain BiDi text, we need to mark the text as either |
| 226 // RTL or LTR, depending on the characters in the string. If we use the URL | 150 // RTL or LTR, depending on the characters in the string. If we use the URL |
| 227 // as the title, we mark the title as LTR since URLs are always treated as | 151 // as the title, we mark the title as LTR since URLs are always treated as |
| 228 // left to right strings. | 152 // left to right strings. |
| 229 if (base::i18n::IsRTL()) { | 153 if (base::i18n::IsRTL()) { |
| 230 if (using_url_as_the_title) | 154 if (using_url_as_the_title) |
| 231 base::i18n::WrapStringWithLTRFormatting(&title_to_set); | 155 base::i18n::WrapStringWithLTRFormatting(&title_to_set); |
| 232 else | 156 else |
| 233 base::i18n::AdjustStringForLocaleDirection(&title_to_set); | 157 base::i18n::AdjustStringForLocaleDirection(&title_to_set); |
| 234 } | 158 } |
| 235 | 159 |
| 236 result->SetString("title", | 160 result->SetString("title", |
| 237 limit_title_length ? title_to_set.substr(0, kShortTitleLength) | 161 limit_title_length ? title_to_set.substr(0, kShortTitleLength) |
| 238 : title_to_set); | 162 : title_to_set); |
| 239 } | 163 } |
| 240 | 164 |
| 241 std::unique_ptr<base::DictionaryValue> | 165 // Converts |entry| to a DictionaryValue to be owned by the caller. |
| 242 BrowsingHistoryHandler::HistoryEntry::ToValue( | 166 std::unique_ptr<base::DictionaryValue> HistoryEntryToValue( |
| 167 BrowsingHistoryService::HistoryEntry* entry, |
| 243 BookmarkModel* bookmark_model, | 168 BookmarkModel* bookmark_model, |
| 244 SupervisedUserService* supervised_user_service, | 169 SupervisedUserService* supervised_user_service, |
| 245 const browser_sync::ProfileSyncService* sync_service, | 170 const browser_sync::ProfileSyncService* sync_service, |
| 246 bool limit_title_length) const { | 171 bool limit_title_length) { |
| 247 std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue()); | 172 std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue()); |
| 248 SetUrlAndTitle(result.get(), limit_title_length); | 173 SetHistoryEntryUrlAndTitle(entry, result.get(), limit_title_length); |
| 249 | 174 |
| 250 base::string16 domain = url_formatter::IDNToUnicode(url.host()); | 175 base::string16 domain = url_formatter::IDNToUnicode(entry->url.host()); |
| 251 // When the domain is empty, use the scheme instead. This allows for a | 176 // When the domain is empty, use the scheme instead. This allows for a |
| 252 // sensible treatment of e.g. file: URLs when group by domain is on. | 177 // sensible treatment of e.g. file: URLs when group by domain is on. |
| 253 if (domain.empty()) | 178 if (domain.empty()) |
| 254 domain = base::UTF8ToUTF16(url.scheme() + ":"); | 179 domain = base::UTF8ToUTF16(entry->url.scheme() + ":"); |
| 255 | 180 |
| 256 // The items which are to be written into result are also described in | 181 // The items which are to be written into result are also described in |
| 257 // chrome/browser/resources/history/history.js in @typedef for | 182 // chrome/browser/resources/history/history.js in @typedef for |
| 258 // HistoryEntry. Please update it whenever you add or remove | 183 // HistoryEntry. Please update it whenever you add or remove |
| 259 // any keys in result. | 184 // any keys in result. |
| 260 result->SetString("domain", domain); | 185 result->SetString("domain", domain); |
| 261 | 186 |
| 262 result->SetString("fallbackFaviconText", | 187 result->SetString( |
| 263 base::UTF16ToASCII(favicon::GetFallbackIconText(url))); | 188 "fallbackFaviconText", |
| 189 base::UTF16ToASCII(favicon::GetFallbackIconText(entry->url))); |
| 264 | 190 |
| 265 result->SetDouble("time", time.ToJsTime()); | 191 result->SetDouble("time", entry->time.ToJsTime()); |
| 266 | 192 |
| 267 // Pass the timestamps in a list. | 193 // Pass the timestamps in a list. |
| 268 std::unique_ptr<base::ListValue> timestamps(new base::ListValue); | 194 std::unique_ptr<base::ListValue> timestamps(new base::ListValue); |
| 269 for (std::set<int64_t>::const_iterator it = all_timestamps.begin(); | 195 for (std::set<int64_t>::const_iterator it = entry->all_timestamps.begin(); |
| 270 it != all_timestamps.end(); ++it) { | 196 it != entry->all_timestamps.end(); ++it) { |
| 271 timestamps->AppendDouble(base::Time::FromInternalValue(*it).ToJsTime()); | 197 timestamps->AppendDouble(base::Time::FromInternalValue(*it).ToJsTime()); |
| 272 } | 198 } |
| 273 result->Set("allTimestamps", timestamps.release()); | 199 result->Set("allTimestamps", timestamps.release()); |
| 274 | 200 |
| 275 // Always pass the short date since it is needed both in the search and in | 201 // Always pass the short date since it is needed both in the search and in |
| 276 // the monthly view. | 202 // the monthly view. |
| 277 result->SetString("dateShort", base::TimeFormatShortDate(time)); | 203 result->SetString("dateShort", base::TimeFormatShortDate(entry->time)); |
| 278 | 204 |
| 279 base::string16 snippet_string; | 205 base::string16 snippet_string; |
| 280 base::string16 date_relative_day; | 206 base::string16 date_relative_day; |
| 281 base::string16 date_time_of_day; | 207 base::string16 date_time_of_day; |
| 282 bool is_blocked_visit = false; | 208 bool is_blocked_visit = false; |
| 283 int host_filtering_behavior = -1; | 209 int host_filtering_behavior = -1; |
| 284 | 210 |
| 285 // Only pass in the strings we need (search results need a shortdate | 211 // Only pass in the strings we need (search results need a shortdate |
| 286 // and snippet, browse results need day and time information). Makes sure that | 212 // and snippet, browse results need day and time information). Makes sure that |
| 287 // values of result are never undefined | 213 // values of result are never undefined |
| 288 if (is_search_result) { | 214 if (entry->is_search_result) { |
| 289 snippet_string = snippet; | 215 snippet_string = entry->snippet; |
| 290 } else { | 216 } else { |
| 291 base::Time midnight = clock->Now().LocalMidnight(); | 217 base::Time midnight = entry->clock->Now().LocalMidnight(); |
| 292 base::string16 date_str = ui::TimeFormat::RelativeDate(time, &midnight); | 218 base::string16 date_str = ui::TimeFormat::RelativeDate(entry->time, |
| 219 &midnight); |
| 293 if (date_str.empty()) { | 220 if (date_str.empty()) { |
| 294 date_str = base::TimeFormatFriendlyDate(time); | 221 date_str = base::TimeFormatFriendlyDate(entry->time); |
| 295 } else { | 222 } else { |
| 296 date_str = l10n_util::GetStringFUTF16( | 223 date_str = l10n_util::GetStringFUTF16( |
| 297 IDS_HISTORY_DATE_WITH_RELATIVE_TIME, | 224 IDS_HISTORY_DATE_WITH_RELATIVE_TIME, |
| 298 date_str, | 225 date_str, |
| 299 base::TimeFormatFriendlyDate(time)); | 226 base::TimeFormatFriendlyDate(entry->time)); |
| 300 } | 227 } |
| 301 date_relative_day = date_str; | 228 date_relative_day = date_str; |
| 302 date_time_of_day = base::TimeFormatTimeOfDay(time); | 229 date_time_of_day = base::TimeFormatTimeOfDay(entry->time); |
| 303 } | 230 } |
| 304 | 231 |
| 305 std::string device_name; | 232 std::string device_name; |
| 306 std::string device_type; | 233 std::string device_type; |
| 307 if (!client_id.empty()) | 234 if (!entry->client_id.empty()) |
| 308 GetDeviceNameAndType(sync_service, client_id, &device_name, &device_type); | 235 GetDeviceNameAndType(sync_service, entry->client_id, &device_name, |
| 236 &device_type); |
| 309 result->SetString("deviceName", device_name); | 237 result->SetString("deviceName", device_name); |
| 310 result->SetString("deviceType", device_type); | 238 result->SetString("deviceType", device_type); |
| 311 | 239 |
| 312 #if BUILDFLAG(ENABLE_SUPERVISED_USERS) | 240 #if BUILDFLAG(ENABLE_SUPERVISED_USERS) |
| 313 if (supervised_user_service) { | 241 if (supervised_user_service) { |
| 314 const SupervisedUserURLFilter* url_filter = | 242 const SupervisedUserURLFilter* url_filter = |
| 315 supervised_user_service->GetURLFilterForUIThread(); | 243 supervised_user_service->GetURLFilterForUIThread(); |
| 316 int filtering_behavior = | 244 int filtering_behavior = |
| 317 url_filter->GetFilteringBehaviorForURL(url.GetWithEmptyPath()); | 245 url_filter->GetFilteringBehaviorForURL(entry->url.GetWithEmptyPath()); |
| 318 is_blocked_visit = blocked_visit; | 246 is_blocked_visit = entry->blocked_visit; |
| 319 host_filtering_behavior = filtering_behavior; | 247 host_filtering_behavior = filtering_behavior; |
| 320 } | 248 } |
| 321 #endif | 249 #endif |
| 322 | 250 |
| 323 result->SetString("dateTimeOfDay", date_time_of_day); | 251 result->SetString("dateTimeOfDay", date_time_of_day); |
| 324 result->SetString("dateRelativeDay", date_relative_day); | 252 result->SetString("dateRelativeDay", date_relative_day); |
| 325 result->SetString("snippet", snippet_string); | 253 result->SetString("snippet", snippet_string); |
| 326 result->SetBoolean("starred", bookmark_model->IsBookmarked(url)); | 254 result->SetBoolean("starred", bookmark_model->IsBookmarked(entry->url)); |
| 327 result->SetInteger("hostFilteringBehavior", host_filtering_behavior); | 255 result->SetInteger("hostFilteringBehavior", host_filtering_behavior); |
| 328 result->SetBoolean("blockedVisit", is_blocked_visit); | 256 result->SetBoolean("blockedVisit", is_blocked_visit); |
| 329 | 257 |
| 330 return result; | 258 return result; |
| 331 } | 259 } |
| 332 | 260 |
| 333 bool BrowsingHistoryHandler::HistoryEntry::SortByTimeDescending( | 261 } // namespace |
| 334 const BrowsingHistoryHandler::HistoryEntry& entry1, | |
| 335 const BrowsingHistoryHandler::HistoryEntry& entry2) { | |
| 336 return entry1.time > entry2.time; | |
| 337 } | |
| 338 | 262 |
| 339 BrowsingHistoryHandler::BrowsingHistoryHandler() | 263 BrowsingHistoryHandler::BrowsingHistoryHandler() |
| 340 : has_pending_delete_request_(false), | 264 : clock_(new base::DefaultClock()), |
| 341 history_service_observer_(this), | 265 browsing_history_service_(nullptr) {} |
| 342 web_history_service_observer_(this), | |
| 343 sync_service_observer_(this), | |
| 344 has_synced_results_(false), | |
| 345 has_other_forms_of_browsing_history_(false), | |
| 346 clock_(new base::DefaultClock()), | |
| 347 weak_factory_(this) {} | |
| 348 | 266 |
| 349 BrowsingHistoryHandler::~BrowsingHistoryHandler() { | 267 BrowsingHistoryHandler::~BrowsingHistoryHandler() { |
| 350 query_task_tracker_.TryCancelAll(); | |
| 351 web_history_request_.reset(); | |
| 352 } | 268 } |
| 353 | 269 |
| 354 void BrowsingHistoryHandler::RegisterMessages() { | 270 void BrowsingHistoryHandler::RegisterMessages() { |
| 271 browsing_history_service_ = base::MakeUnique<BrowsingHistoryService>( |
| 272 Profile::FromWebUI(web_ui()), this); |
| 273 |
| 355 // Create our favicon data source. | 274 // Create our favicon data source. |
| 356 Profile* profile = Profile::FromWebUI(web_ui()); | 275 Profile* profile = Profile::FromWebUI(web_ui()); |
| 357 | 276 |
| 358 #if defined(OS_ANDROID) | 277 #if defined(OS_ANDROID) |
| 359 favicon::FallbackIconService* fallback_icon_service = | 278 favicon::FallbackIconService* fallback_icon_service = |
| 360 FallbackIconServiceFactory::GetForBrowserContext(profile); | 279 FallbackIconServiceFactory::GetForBrowserContext(profile); |
| 361 favicon::LargeIconService* large_icon_service = | 280 favicon::LargeIconService* large_icon_service = |
| 362 LargeIconServiceFactory::GetForBrowserContext(profile); | 281 LargeIconServiceFactory::GetForBrowserContext(profile); |
| 363 content::URLDataSource::Add( | 282 content::URLDataSource::Add( |
| 364 profile, new LargeIconSource(fallback_icon_service, large_icon_service)); | 283 profile, new LargeIconSource(fallback_icon_service, large_icon_service)); |
| 365 #else | 284 #else |
| 366 content::URLDataSource::Add( | 285 content::URLDataSource::Add( |
| 367 profile, new FaviconSource(profile, FaviconSource::ANY)); | 286 profile, new FaviconSource(profile, FaviconSource::ANY)); |
| 368 #endif | 287 #endif |
| 369 | 288 |
| 370 // Get notifications when history is cleared. | |
| 371 history::HistoryService* local_history = HistoryServiceFactory::GetForProfile( | |
| 372 profile, ServiceAccessType::EXPLICIT_ACCESS); | |
| 373 if (local_history) | |
| 374 history_service_observer_.Add(local_history); | |
| 375 | |
| 376 // Get notifications when web history is deleted. | |
| 377 history::WebHistoryService* web_history = | |
| 378 WebHistoryServiceFactory::GetForProfile(profile); | |
| 379 if (web_history) { | |
| 380 web_history_service_observer_.Add(web_history); | |
| 381 } else { | |
| 382 // If |web_history| is not available, it means that the history sync is | |
| 383 // disabled. Observe |sync_service| so that we can attach the listener | |
| 384 // in case it gets enabled later. | |
| 385 browser_sync::ProfileSyncService* sync_service = | |
| 386 ProfileSyncServiceFactory::GetForProfile(profile); | |
| 387 if (sync_service) | |
| 388 sync_service_observer_.Add(sync_service); | |
| 389 } | |
| 390 | |
| 391 web_ui()->RegisterMessageCallback("queryHistory", | 289 web_ui()->RegisterMessageCallback("queryHistory", |
| 392 base::Bind(&BrowsingHistoryHandler::HandleQueryHistory, | 290 base::Bind(&BrowsingHistoryHandler::HandleQueryHistory, |
| 393 base::Unretained(this))); | 291 base::Unretained(this))); |
| 394 web_ui()->RegisterMessageCallback("removeVisits", | 292 web_ui()->RegisterMessageCallback("removeVisits", |
| 395 base::Bind(&BrowsingHistoryHandler::HandleRemoveVisits, | 293 base::Bind(&BrowsingHistoryHandler::HandleRemoveVisits, |
| 396 base::Unretained(this))); | 294 base::Unretained(this))); |
| 397 web_ui()->RegisterMessageCallback("clearBrowsingData", | 295 web_ui()->RegisterMessageCallback("clearBrowsingData", |
| 398 base::Bind(&BrowsingHistoryHandler::HandleClearBrowsingData, | 296 base::Bind(&BrowsingHistoryHandler::HandleClearBrowsingData, |
| 399 base::Unretained(this))); | 297 base::Unretained(this))); |
| 400 web_ui()->RegisterMessageCallback("removeBookmark", | 298 web_ui()->RegisterMessageCallback("removeBookmark", |
| 401 base::Bind(&BrowsingHistoryHandler::HandleRemoveBookmark, | 299 base::Bind(&BrowsingHistoryHandler::HandleRemoveBookmark, |
| 402 base::Unretained(this))); | 300 base::Unretained(this))); |
| 403 } | 301 } |
| 404 | 302 |
| 405 bool BrowsingHistoryHandler::ExtractIntegerValueAtIndex( | 303 bool BrowsingHistoryHandler::ExtractIntegerValueAtIndex( |
| 406 const base::ListValue* value, | 304 const base::ListValue* value, |
| 407 int index, | 305 int index, |
| 408 int* out_int) { | 306 int* out_int) { |
| 409 double double_value; | 307 double double_value; |
| 410 if (value->GetDouble(index, &double_value)) { | 308 if (value->GetDouble(index, &double_value)) { |
| 411 *out_int = static_cast<int>(double_value); | 309 *out_int = static_cast<int>(double_value); |
| 412 return true; | 310 return true; |
| 413 } | 311 } |
| 414 NOTREACHED(); | 312 NOTREACHED(); |
| 415 return false; | 313 return false; |
| 416 } | 314 } |
| 417 | 315 |
| 418 void BrowsingHistoryHandler::OnStateChanged() { | |
| 419 // If the history sync was enabled, start observing WebHistoryService. | |
| 420 // This method should not be called after we already added the observer. | |
| 421 history::WebHistoryService* web_history = | |
| 422 WebHistoryServiceFactory::GetForProfile(Profile::FromWebUI(web_ui())); | |
| 423 if (web_history) { | |
| 424 DCHECK(!web_history_service_observer_.IsObserving(web_history)); | |
| 425 web_history_service_observer_.Add(web_history); | |
| 426 sync_service_observer_.RemoveAll(); | |
| 427 } | |
| 428 } | |
| 429 | |
| 430 void BrowsingHistoryHandler::WebHistoryTimeout() { | |
| 431 has_synced_results_ = false; | |
| 432 // TODO(dubroy): Communicate the failure to the front end. | |
| 433 if (!query_task_tracker_.HasTrackedTasks()) | |
| 434 ReturnResultsToFrontEnd(); | |
| 435 | |
| 436 UMA_HISTOGRAM_ENUMERATION( | |
| 437 "WebHistory.QueryCompletion", | |
| 438 WEB_HISTORY_QUERY_TIMED_OUT, NUM_WEB_HISTORY_QUERY_BUCKETS); | |
| 439 } | |
| 440 | |
| 441 void BrowsingHistoryHandler::QueryHistory( | |
| 442 const base::string16& search_text, | |
| 443 const history::QueryOptions& options) { | |
| 444 Profile* profile = Profile::FromWebUI(web_ui()); | |
| 445 | |
| 446 // Anything in-flight is invalid. | |
| 447 query_task_tracker_.TryCancelAll(); | |
| 448 web_history_request_.reset(); | |
| 449 | |
| 450 query_results_.clear(); | |
| 451 results_info_value_.Clear(); | |
| 452 | |
| 453 history::HistoryService* hs = HistoryServiceFactory::GetForProfile( | |
| 454 profile, ServiceAccessType::EXPLICIT_ACCESS); | |
| 455 hs->QueryHistory(search_text, | |
| 456 options, | |
| 457 base::Bind(&BrowsingHistoryHandler::QueryComplete, | |
| 458 base::Unretained(this), | |
| 459 search_text, | |
| 460 options), | |
| 461 &query_task_tracker_); | |
| 462 | |
| 463 history::WebHistoryService* web_history = | |
| 464 WebHistoryServiceFactory::GetForProfile(profile); | |
| 465 | |
| 466 // Set this to false until the results actually arrive. | |
| 467 results_info_value_.SetBoolean("hasSyncedResults", false); | |
| 468 | |
| 469 if (web_history) { | |
| 470 web_history_query_results_.clear(); | |
| 471 web_history_request_ = web_history->QueryHistory( | |
| 472 search_text, | |
| 473 options, | |
| 474 base::Bind(&BrowsingHistoryHandler::WebHistoryQueryComplete, | |
| 475 base::Unretained(this), | |
| 476 search_text, options, | |
| 477 base::TimeTicks::Now())); | |
| 478 // Start a timer so we know when to give up. | |
| 479 web_history_timer_.Start( | |
| 480 FROM_HERE, base::TimeDelta::FromSeconds(kWebHistoryTimeoutSeconds), | |
| 481 this, &BrowsingHistoryHandler::WebHistoryTimeout); | |
| 482 | |
| 483 // Test the existence of other forms of browsing history. | |
| 484 browsing_data::ShouldShowNoticeAboutOtherFormsOfBrowsingHistory( | |
| 485 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile), | |
| 486 web_history, | |
| 487 base::Bind( | |
| 488 &BrowsingHistoryHandler::OtherFormsOfBrowsingHistoryQueryComplete, | |
| 489 weak_factory_.GetWeakPtr())); | |
| 490 } else { | |
| 491 // The notice could not have been shown, because there is no web history. | |
| 492 RecordMetricsForNoticeAboutOtherFormsOfBrowsingHistory(false); | |
| 493 has_synced_results_ = false; | |
| 494 has_other_forms_of_browsing_history_ = false; | |
| 495 } | |
| 496 } | |
| 497 | |
| 498 void BrowsingHistoryHandler::HandleQueryHistory(const base::ListValue* args) { | 316 void BrowsingHistoryHandler::HandleQueryHistory(const base::ListValue* args) { |
| 499 history::QueryOptions options; | 317 history::QueryOptions options; |
| 500 | 318 |
| 501 // Parse the arguments from JavaScript. There are five required arguments: | 319 // Parse the arguments from JavaScript. There are five required arguments: |
| 502 // - the text to search for (may be empty) | 320 // - the text to search for (may be empty) |
| 503 // - the offset from which the search should start (in multiples of week or | 321 // - the offset from which the search should start (in multiples of week or |
| 504 // month, set by the next argument). | 322 // month, set by the next argument). |
| 505 // - the range (BrowsingHistoryHandler::Range) Enum value that sets the range | 323 // - the range (BrowsingHistoryHandler::Range) Enum value that sets the range |
| 506 // of the query. | 324 // of the query. |
| 507 // - the end time for the query. Only results older than this time will be | 325 // - the end time for the query. Only results older than this time will be |
| (...skipping 24 matching lines...) Expand all Loading... |
| 532 } | 350 } |
| 533 if (end_time) | 351 if (end_time) |
| 534 options.end_time = base::Time::FromJsTime(end_time); | 352 options.end_time = base::Time::FromJsTime(end_time); |
| 535 | 353 |
| 536 if (!ExtractIntegerValueAtIndex(args, 4, &options.max_count)) { | 354 if (!ExtractIntegerValueAtIndex(args, 4, &options.max_count)) { |
| 537 NOTREACHED() << "Failed to convert argument 4."; | 355 NOTREACHED() << "Failed to convert argument 4."; |
| 538 return; | 356 return; |
| 539 } | 357 } |
| 540 | 358 |
| 541 options.duplicate_policy = history::QueryOptions::REMOVE_DUPLICATES_PER_DAY; | 359 options.duplicate_policy = history::QueryOptions::REMOVE_DUPLICATES_PER_DAY; |
| 542 QueryHistory(search_text, options); | 360 browsing_history_service_->QueryHistory(search_text, options); |
| 543 } | 361 } |
| 544 | 362 |
| 545 void BrowsingHistoryHandler::HandleRemoveVisits(const base::ListValue* args) { | 363 void BrowsingHistoryHandler::HandleRemoveVisits(const base::ListValue* args) { |
| 546 Profile* profile = Profile::FromWebUI(web_ui()); | 364 std::vector<std::unique_ptr<BrowsingHistoryService::HistoryEntry>> |
| 547 // TODO(davidben): history.js is not aware of this failure and will still | 365 items_to_remove; |
| 548 // override |deleteCompleteCallback_|. | 366 items_to_remove.reserve(args->GetSize()); |
| 549 if (delete_task_tracker_.HasTrackedTasks() || | |
| 550 has_pending_delete_request_ || | |
| 551 !profile->GetPrefs()->GetBoolean(prefs::kAllowDeletingBrowserHistory)) { | |
| 552 web_ui()->CallJavascriptFunctionUnsafe("deleteFailed"); | |
| 553 return; | |
| 554 } | |
| 555 | |
| 556 history::HistoryService* history_service = | |
| 557 HistoryServiceFactory::GetForProfile(profile, | |
| 558 ServiceAccessType::EXPLICIT_ACCESS); | |
| 559 history::WebHistoryService* web_history = | |
| 560 WebHistoryServiceFactory::GetForProfile(profile); | |
| 561 | |
| 562 base::Time now = clock_->Now(); | |
| 563 std::vector<history::ExpireHistoryArgs> expire_list; | |
| 564 expire_list.reserve(args->GetSize()); | |
| 565 | |
| 566 DCHECK(urls_to_be_deleted_.empty()); | |
| 567 for (base::ListValue::const_iterator it = args->begin(); | 367 for (base::ListValue::const_iterator it = args->begin(); |
| 568 it != args->end(); ++it) { | 368 it != args->end(); ++it) { |
| 569 base::DictionaryValue* deletion = NULL; | 369 base::DictionaryValue* deletion = NULL; |
| 570 base::string16 url; | 370 base::string16 url; |
| 571 base::ListValue* timestamps = NULL; | 371 base::ListValue* timestamps = NULL; |
| 572 | 372 |
| 573 // Each argument is a dictionary with properties "url" and "timestamps". | 373 // Each argument is a dictionary with properties "url" and "timestamps". |
| 574 if (!((*it)->GetAsDictionary(&deletion) && | 374 if (!((*it)->GetAsDictionary(&deletion) && |
| 575 deletion->GetString("url", &url) && | 375 deletion->GetString("url", &url) && |
| 576 deletion->GetList("timestamps", ×tamps))) { | 376 deletion->GetList("timestamps", ×tamps))) { |
| 577 NOTREACHED() << "Unable to extract arguments"; | 377 NOTREACHED() << "Unable to extract arguments"; |
| 578 return; | 378 return; |
| 579 } | 379 } |
| 580 DCHECK(timestamps->GetSize() > 0); | 380 DCHECK(timestamps->GetSize() > 0); |
| 381 std::unique_ptr<BrowsingHistoryService::HistoryEntry> entry( |
| 382 new BrowsingHistoryService::HistoryEntry()); |
| 581 | 383 |
| 582 // In order to ensure that visits will be deleted from the server and other | 384 entry->url = GURL(url); |
| 583 // clients (even if they are offline), create a sync delete directive for | |
| 584 // each visit to be deleted. | |
| 585 sync_pb::HistoryDeleteDirectiveSpecifics delete_directive; | |
| 586 sync_pb::GlobalIdDirective* global_id_directive = | |
| 587 delete_directive.mutable_global_id_directive(); | |
| 588 | 385 |
| 589 double timestamp; | 386 double timestamp; |
| 590 history::ExpireHistoryArgs* expire_args = NULL; | |
| 591 for (base::ListValue::const_iterator ts_iterator = timestamps->begin(); | 387 for (base::ListValue::const_iterator ts_iterator = timestamps->begin(); |
| 592 ts_iterator != timestamps->end(); ++ts_iterator) { | 388 ts_iterator != timestamps->end(); ++ts_iterator) { |
| 593 if (!(*ts_iterator)->GetAsDouble(×tamp)) { | 389 if (!(*ts_iterator)->GetAsDouble(×tamp)) { |
| 594 NOTREACHED() << "Unable to extract visit timestamp."; | 390 NOTREACHED() << "Unable to extract visit timestamp."; |
| 595 continue; | 391 continue; |
| 596 } | 392 } |
| 393 |
| 597 base::Time visit_time = base::Time::FromJsTime(timestamp); | 394 base::Time visit_time = base::Time::FromJsTime(timestamp); |
| 598 if (!expire_args) { | 395 entry->all_timestamps.insert(visit_time.ToInternalValue()); |
| 599 GURL gurl(url); | |
| 600 expire_list.resize(expire_list.size() + 1); | |
| 601 expire_args = &expire_list.back(); | |
| 602 expire_args->SetTimeRangeForOneDay(visit_time); | |
| 603 expire_args->urls.insert(gurl); | |
| 604 urls_to_be_deleted_.insert(gurl); | |
| 605 } | |
| 606 // The local visit time is treated as a global ID for the visit. | |
| 607 global_id_directive->add_global_id(visit_time.ToInternalValue()); | |
| 608 } | 396 } |
| 609 | 397 |
| 610 // Set the start and end time in microseconds since the Unix epoch. | 398 items_to_remove.push_back(std::move(entry)); |
| 611 global_id_directive->set_start_time_usec( | |
| 612 (expire_args->begin_time - base::Time::UnixEpoch()).InMicroseconds()); | |
| 613 | |
| 614 // Delete directives shouldn't have an end time in the future. | |
| 615 // TODO(dubroy): Use sane time (crbug.com/146090) here when it's ready. | |
| 616 base::Time end_time = std::min(expire_args->end_time, now); | |
| 617 | |
| 618 // -1 because end time in delete directives is inclusive. | |
| 619 global_id_directive->set_end_time_usec( | |
| 620 (end_time - base::Time::UnixEpoch()).InMicroseconds() - 1); | |
| 621 | |
| 622 // TODO(dubroy): Figure out the proper way to handle an error here. | |
| 623 if (web_history) | |
| 624 history_service->ProcessLocalDeleteDirective(delete_directive); | |
| 625 } | 399 } |
| 626 | 400 |
| 627 history_service->ExpireHistory( | 401 browsing_history_service_->RemoveVisits(&items_to_remove); |
| 628 expire_list, | 402 items_to_remove.clear(); |
| 629 base::Bind(&BrowsingHistoryHandler::RemoveComplete, | |
| 630 base::Unretained(this)), | |
| 631 &delete_task_tracker_); | |
| 632 | |
| 633 if (web_history) { | |
| 634 has_pending_delete_request_ = true; | |
| 635 web_history->ExpireHistory( | |
| 636 expire_list, | |
| 637 base::Bind(&BrowsingHistoryHandler::RemoveWebHistoryComplete, | |
| 638 weak_factory_.GetWeakPtr())); | |
| 639 } | |
| 640 | |
| 641 #if BUILDFLAG(ENABLE_EXTENSIONS) | |
| 642 // If the profile has activity logging enabled also clean up any URLs from | |
| 643 // the extension activity log. The extension activity log contains URLS | |
| 644 // which websites an extension has activity on so it will indirectly | |
| 645 // contain websites that a user has visited. | |
| 646 extensions::ActivityLog* activity_log = | |
| 647 extensions::ActivityLog::GetInstance(profile); | |
| 648 for (std::vector<history::ExpireHistoryArgs>::const_iterator it = | |
| 649 expire_list.begin(); it != expire_list.end(); ++it) { | |
| 650 activity_log->RemoveURLs(it->urls); | |
| 651 } | |
| 652 #endif | |
| 653 | |
| 654 for (const history::ExpireHistoryArgs& expire_entry : expire_list) | |
| 655 AppBannerSettingsHelper::ClearHistoryForURLs(profile, expire_entry.urls); | |
| 656 } | 403 } |
| 657 | 404 |
| 658 void BrowsingHistoryHandler::HandleClearBrowsingData( | 405 void BrowsingHistoryHandler::HandleClearBrowsingData( |
| 659 const base::ListValue* args) { | 406 const base::ListValue* args) { |
| 660 #if BUILDFLAG(ANDROID_JAVA_UI) | 407 #if BUILDFLAG(ANDROID_JAVA_UI) |
| 661 chrome::android::ChromeApplication::OpenClearBrowsingData( | 408 chrome::android::ChromeApplication::OpenClearBrowsingData( |
| 662 web_ui()->GetWebContents()); | 409 web_ui()->GetWebContents()); |
| 663 #else | 410 #else |
| 664 // TODO(beng): This is an improper direct dependency on Browser. Route this | 411 // TODO(beng): This is an improper direct dependency on Browser. Route this |
| 665 // through some sort of delegate. | 412 // through some sort of delegate. |
| 666 Browser* browser = chrome::FindBrowserWithWebContents( | 413 Browser* browser = chrome::FindBrowserWithWebContents( |
| 667 web_ui()->GetWebContents()); | 414 web_ui()->GetWebContents()); |
| 668 chrome::ShowClearBrowsingDataDialog(browser); | 415 chrome::ShowClearBrowsingDataDialog(browser); |
| 669 #endif | 416 #endif |
| 670 } | 417 } |
| 671 | 418 |
| 672 void BrowsingHistoryHandler::HandleRemoveBookmark(const base::ListValue* args) { | 419 void BrowsingHistoryHandler::HandleRemoveBookmark(const base::ListValue* args) { |
| 673 base::string16 url = ExtractStringValue(args); | 420 base::string16 url = ExtractStringValue(args); |
| 674 Profile* profile = Profile::FromWebUI(web_ui()); | 421 Profile* profile = Profile::FromWebUI(web_ui()); |
| 675 BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile); | 422 BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile); |
| 676 bookmarks::RemoveAllBookmarks(model, GURL(url)); | 423 bookmarks::RemoveAllBookmarks(model, GURL(url)); |
| 677 } | 424 } |
| 678 | 425 |
| 679 // static | |
| 680 void BrowsingHistoryHandler::MergeDuplicateResults( | |
| 681 std::vector<BrowsingHistoryHandler::HistoryEntry>* results) { | |
| 682 std::vector<BrowsingHistoryHandler::HistoryEntry> new_results; | |
| 683 // Pre-reserve the size of the new vector. Since we're working with pointers | |
| 684 // later on not doing this could lead to the vector being resized and to | |
| 685 // pointers to invalid locations. | |
| 686 new_results.reserve(results->size()); | |
| 687 // Maps a URL to the most recent entry on a particular day. | |
| 688 std::map<GURL, BrowsingHistoryHandler::HistoryEntry*> current_day_entries; | |
| 689 | |
| 690 // Keeps track of the day that |current_day_urls| is holding the URLs for, | |
| 691 // in order to handle removing per-day duplicates. | |
| 692 base::Time current_day_midnight; | |
| 693 | |
| 694 std::sort( | |
| 695 results->begin(), results->end(), HistoryEntry::SortByTimeDescending); | |
| 696 | |
| 697 for (std::vector<BrowsingHistoryHandler::HistoryEntry>::const_iterator it = | |
| 698 results->begin(); it != results->end(); ++it) { | |
| 699 // Reset the list of found URLs when a visit from a new day is encountered. | |
| 700 if (current_day_midnight != it->time.LocalMidnight()) { | |
| 701 current_day_entries.clear(); | |
| 702 current_day_midnight = it->time.LocalMidnight(); | |
| 703 } | |
| 704 | |
| 705 // Keep this visit if it's the first visit to this URL on the current day. | |
| 706 if (current_day_entries.count(it->url) == 0) { | |
| 707 new_results.push_back(*it); | |
| 708 current_day_entries[it->url] = &new_results.back(); | |
| 709 } else { | |
| 710 // Keep track of the timestamps of all visits to the URL on the same day. | |
| 711 BrowsingHistoryHandler::HistoryEntry* entry = | |
| 712 current_day_entries[it->url]; | |
| 713 entry->all_timestamps.insert( | |
| 714 it->all_timestamps.begin(), it->all_timestamps.end()); | |
| 715 | |
| 716 if (entry->entry_type != it->entry_type) { | |
| 717 entry->entry_type = | |
| 718 BrowsingHistoryHandler::HistoryEntry::COMBINED_ENTRY; | |
| 719 } | |
| 720 } | |
| 721 } | |
| 722 results->swap(new_results); | |
| 723 } | |
| 724 | |
| 725 void BrowsingHistoryHandler::QueryComplete( | |
| 726 const base::string16& search_text, | |
| 727 const history::QueryOptions& options, | |
| 728 history::QueryResults* results) { | |
| 729 DCHECK_EQ(0U, query_results_.size()); | |
| 730 query_results_.reserve(results->size()); | |
| 731 | |
| 732 for (size_t i = 0; i < results->size(); ++i) { | |
| 733 history::URLResult const &page = (*results)[i]; | |
| 734 // TODO(dubroy): Use sane time (crbug.com/146090) here when it's ready. | |
| 735 query_results_.push_back(HistoryEntry( | |
| 736 HistoryEntry::LOCAL_ENTRY, page.url(), page.title(), page.visit_time(), | |
| 737 std::string(), !search_text.empty(), page.snippet().text(), | |
| 738 page.blocked_visit(), clock_.get())); | |
| 739 } | |
| 740 | |
| 741 // The items which are to be written into results_info_value_ are also | |
| 742 // described in chrome/browser/resources/history/history.js in @typedef for | |
| 743 // HistoryQuery. Please update it whenever you add or remove any keys in | |
| 744 // results_info_value_. | |
| 745 results_info_value_.SetString("term", search_text); | |
| 746 results_info_value_.SetBoolean("finished", results->reached_beginning()); | |
| 747 | |
| 748 // Add the specific dates that were searched to display them. | |
| 749 // TODO(sergiu): Put today if the start is in the future. | |
| 750 results_info_value_.SetString( | |
| 751 "queryStartTime", | |
| 752 GetRelativeDateLocalized(clock_.get(), options.begin_time)); | |
| 753 if (!options.end_time.is_null()) { | |
| 754 results_info_value_.SetString( | |
| 755 "queryEndTime", | |
| 756 GetRelativeDateLocalized( | |
| 757 clock_.get(), options.end_time - base::TimeDelta::FromDays(1))); | |
| 758 } else { | |
| 759 results_info_value_.SetString( | |
| 760 "queryEndTime", GetRelativeDateLocalized(clock_.get(), clock_->Now())); | |
| 761 } | |
| 762 if (!web_history_timer_.IsRunning()) | |
| 763 ReturnResultsToFrontEnd(); | |
| 764 } | |
| 765 | |
| 766 void BrowsingHistoryHandler::ReturnResultsToFrontEnd() { | |
| 767 Profile* profile = Profile::FromWebUI(web_ui()); | |
| 768 BookmarkModel* bookmark_model = | |
| 769 BookmarkModelFactory::GetForBrowserContext(profile); | |
| 770 SupervisedUserService* supervised_user_service = NULL; | |
| 771 #if BUILDFLAG(ENABLE_SUPERVISED_USERS) | |
| 772 if (profile->IsSupervised()) | |
| 773 supervised_user_service = | |
| 774 SupervisedUserServiceFactory::GetForProfile(profile); | |
| 775 #endif | |
| 776 browser_sync::ProfileSyncService* sync_service = | |
| 777 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile); | |
| 778 | |
| 779 // Combine the local and remote results into |query_results_|, and remove | |
| 780 // any duplicates. | |
| 781 if (!web_history_query_results_.empty()) { | |
| 782 int local_result_count = query_results_.size(); | |
| 783 query_results_.insert(query_results_.end(), | |
| 784 web_history_query_results_.begin(), | |
| 785 web_history_query_results_.end()); | |
| 786 MergeDuplicateResults(&query_results_); | |
| 787 | |
| 788 if (local_result_count) { | |
| 789 // In the best case, we expect that all local results are duplicated on | |
| 790 // the server. Keep track of how many are missing. | |
| 791 int missing_count = std::count_if( | |
| 792 query_results_.begin(), query_results_.end(), IsLocalOnlyResult); | |
| 793 UMA_HISTOGRAM_PERCENTAGE("WebHistory.LocalResultMissingOnServer", | |
| 794 missing_count * 100.0 / local_result_count); | |
| 795 } | |
| 796 } | |
| 797 | |
| 798 bool is_md = false; | |
| 799 #if !defined(OS_ANDROID) | |
| 800 is_md = MdHistoryUI::IsEnabled(profile); | |
| 801 #endif | |
| 802 | |
| 803 // Convert the result vector into a ListValue. | |
| 804 base::ListValue results_value; | |
| 805 for (std::vector<BrowsingHistoryHandler::HistoryEntry>::iterator it = | |
| 806 query_results_.begin(); it != query_results_.end(); ++it) { | |
| 807 std::unique_ptr<base::Value> value(it->ToValue( | |
| 808 bookmark_model, supervised_user_service, sync_service, is_md)); | |
| 809 results_value.Append(std::move(value)); | |
| 810 } | |
| 811 | |
| 812 web_ui()->CallJavascriptFunctionUnsafe("historyResult", results_info_value_, | |
| 813 results_value); | |
| 814 web_ui()->CallJavascriptFunctionUnsafe( | |
| 815 "showNotification", base::FundamentalValue(has_synced_results_), | |
| 816 base::FundamentalValue(has_other_forms_of_browsing_history_)); | |
| 817 results_info_value_.Clear(); | |
| 818 query_results_.clear(); | |
| 819 web_history_query_results_.clear(); | |
| 820 } | |
| 821 | |
| 822 void BrowsingHistoryHandler::WebHistoryQueryComplete( | |
| 823 const base::string16& search_text, | |
| 824 const history::QueryOptions& options, | |
| 825 base::TimeTicks start_time, | |
| 826 history::WebHistoryService::Request* request, | |
| 827 const base::DictionaryValue* results_value) { | |
| 828 base::TimeDelta delta = base::TimeTicks::Now() - start_time; | |
| 829 UMA_HISTOGRAM_TIMES("WebHistory.ResponseTime", delta); | |
| 830 | |
| 831 // If the response came in too late, do nothing. | |
| 832 // TODO(dubroy): Maybe show a banner, and prompt the user to reload? | |
| 833 if (!web_history_timer_.IsRunning()) | |
| 834 return; | |
| 835 web_history_timer_.Stop(); | |
| 836 | |
| 837 UMA_HISTOGRAM_ENUMERATION( | |
| 838 "WebHistory.QueryCompletion", | |
| 839 results_value ? WEB_HISTORY_QUERY_SUCCEEDED : WEB_HISTORY_QUERY_FAILED, | |
| 840 NUM_WEB_HISTORY_QUERY_BUCKETS); | |
| 841 | |
| 842 DCHECK_EQ(0U, web_history_query_results_.size()); | |
| 843 const base::ListValue* events = NULL; | |
| 844 if (results_value && results_value->GetList("event", &events)) { | |
| 845 web_history_query_results_.reserve(events->GetSize()); | |
| 846 for (unsigned int i = 0; i < events->GetSize(); ++i) { | |
| 847 const base::DictionaryValue* event = NULL; | |
| 848 const base::DictionaryValue* result = NULL; | |
| 849 const base::ListValue* results = NULL; | |
| 850 const base::ListValue* ids = NULL; | |
| 851 base::string16 url; | |
| 852 base::string16 title; | |
| 853 base::Time visit_time; | |
| 854 | |
| 855 if (!(events->GetDictionary(i, &event) && | |
| 856 event->GetList("result", &results) && | |
| 857 results->GetDictionary(0, &result) && | |
| 858 result->GetString("url", &url) && | |
| 859 result->GetList("id", &ids) && | |
| 860 ids->GetSize() > 0)) { | |
| 861 LOG(WARNING) << "Improperly formed JSON response from history server."; | |
| 862 continue; | |
| 863 } | |
| 864 | |
| 865 // Ignore any URLs that should not be shown in the history page. | |
| 866 GURL gurl(url); | |
| 867 if (!CanAddURLToHistory(gurl)) | |
| 868 continue; | |
| 869 | |
| 870 // Title is optional, so the return value is ignored here. | |
| 871 result->GetString("title", &title); | |
| 872 | |
| 873 // Extract the timestamps of all the visits to this URL. | |
| 874 // They are referred to as "IDs" by the server. | |
| 875 for (int j = 0; j < static_cast<int>(ids->GetSize()); ++j) { | |
| 876 const base::DictionaryValue* id = NULL; | |
| 877 std::string timestamp_string; | |
| 878 int64_t timestamp_usec = 0; | |
| 879 | |
| 880 if (!ids->GetDictionary(j, &id) || | |
| 881 !id->GetString("timestamp_usec", ×tamp_string) || | |
| 882 !base::StringToInt64(timestamp_string, ×tamp_usec)) { | |
| 883 NOTREACHED() << "Unable to extract timestamp."; | |
| 884 continue; | |
| 885 } | |
| 886 // The timestamp on the server is a Unix time. | |
| 887 base::Time time = base::Time::UnixEpoch() + | |
| 888 base::TimeDelta::FromMicroseconds(timestamp_usec); | |
| 889 | |
| 890 // Get the ID of the client that this visit came from. | |
| 891 std::string client_id; | |
| 892 id->GetString("client_id", &client_id); | |
| 893 | |
| 894 web_history_query_results_.push_back( | |
| 895 HistoryEntry(HistoryEntry::REMOTE_ENTRY, gurl, title, time, | |
| 896 client_id, !search_text.empty(), base::string16(), | |
| 897 /* blocked_visit */ false, clock_.get())); | |
| 898 } | |
| 899 } | |
| 900 } | |
| 901 has_synced_results_ = results_value != nullptr; | |
| 902 results_info_value_.SetBoolean("hasSyncedResults", has_synced_results_); | |
| 903 if (!query_task_tracker_.HasTrackedTasks()) | |
| 904 ReturnResultsToFrontEnd(); | |
| 905 } | |
| 906 | |
| 907 void BrowsingHistoryHandler::OtherFormsOfBrowsingHistoryQueryComplete( | |
| 908 bool found_other_forms_of_browsing_history) { | |
| 909 has_other_forms_of_browsing_history_ = found_other_forms_of_browsing_history; | |
| 910 | |
| 911 RecordMetricsForNoticeAboutOtherFormsOfBrowsingHistory( | |
| 912 has_other_forms_of_browsing_history_); | |
| 913 | |
| 914 web_ui()->CallJavascriptFunctionUnsafe( | |
| 915 "showNotification", base::FundamentalValue(has_synced_results_), | |
| 916 base::FundamentalValue(has_other_forms_of_browsing_history_)); | |
| 917 } | |
| 918 | |
| 919 void BrowsingHistoryHandler::RemoveComplete() { | |
| 920 urls_to_be_deleted_.clear(); | |
| 921 | |
| 922 // Notify the page that the deletion request is complete, but only if a web | |
| 923 // history delete request is not still pending. | |
| 924 if (!has_pending_delete_request_) | |
| 925 web_ui()->CallJavascriptFunctionUnsafe("deleteComplete"); | |
| 926 } | |
| 927 | |
| 928 void BrowsingHistoryHandler::RemoveWebHistoryComplete(bool success) { | |
| 929 has_pending_delete_request_ = false; | |
| 930 // TODO(dubroy): Should we handle failure somehow? Delete directives will | |
| 931 // ensure that the visits are eventually deleted, so maybe it's not necessary. | |
| 932 if (!delete_task_tracker_.HasTrackedTasks()) | |
| 933 RemoveComplete(); | |
| 934 } | |
| 935 | |
| 936 void BrowsingHistoryHandler::SetQueryTimeInWeeks( | 426 void BrowsingHistoryHandler::SetQueryTimeInWeeks( |
| 937 int offset, history::QueryOptions* options) { | 427 int offset, history::QueryOptions* options) { |
| 938 // LocalMidnight returns the beginning of the current day so get the | 428 // LocalMidnight returns the beginning of the current day so get the |
| 939 // beginning of the next one. | 429 // beginning of the next one. |
| 940 base::Time midnight = | 430 base::Time midnight = |
| 941 clock_->Now().LocalMidnight() + base::TimeDelta::FromDays(1); | 431 clock_->Now().LocalMidnight() + base::TimeDelta::FromDays(1); |
| 942 options->end_time = midnight - | 432 options->end_time = midnight - |
| 943 base::TimeDelta::FromDays(7 * offset); | 433 base::TimeDelta::FromDays(7 * offset); |
| 944 options->begin_time = midnight - | 434 options->begin_time = midnight - |
| 945 base::TimeDelta::FromDays(7 * (offset + 1)); | 435 base::TimeDelta::FromDays(7 * (offset + 1)); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 979 exploded.month -= 1; | 469 exploded.month -= 1; |
| 980 // Set the correct year | 470 // Set the correct year |
| 981 NormalizeMonths(&exploded); | 471 NormalizeMonths(&exploded); |
| 982 if (!base::Time::FromLocalExploded(exploded, &options->begin_time)) { | 472 if (!base::Time::FromLocalExploded(exploded, &options->begin_time)) { |
| 983 // TODO(maksims): implement errors handling here. | 473 // TODO(maksims): implement errors handling here. |
| 984 NOTIMPLEMENTED(); | 474 NOTIMPLEMENTED(); |
| 985 } | 475 } |
| 986 } | 476 } |
| 987 } | 477 } |
| 988 | 478 |
| 989 // Helper function for Observe that determines if there are any differences | 479 void BrowsingHistoryHandler::OnQueryComplete( |
| 990 // between the URLs noticed for deletion and the ones we are expecting. | 480 std::vector<BrowsingHistoryService::HistoryEntry>* results, |
| 991 static bool DeletionsDiffer(const history::URLRows& deleted_rows, | 481 BrowsingHistoryService::QueryResultsInfo* query_results_info) { |
| 992 const std::set<GURL>& urls_to_be_deleted) { | 482 Profile* profile = Profile::FromWebUI(web_ui()); |
| 993 if (deleted_rows.size() != urls_to_be_deleted.size()) | 483 BookmarkModel* bookmark_model = |
| 994 return true; | 484 BookmarkModelFactory::GetForBrowserContext(profile); |
| 995 for (const auto& i : deleted_rows) { | 485 SupervisedUserService* supervised_user_service = NULL; |
| 996 if (urls_to_be_deleted.find(i.url()) == urls_to_be_deleted.end()) | 486 #if defined(ENABLE_SUPERVISED_USERS) |
| 997 return true; | 487 if (profile->IsSupervised()) |
| 488 supervised_user_service = |
| 489 SupervisedUserServiceFactory::GetForProfile(profile); |
| 490 #endif |
| 491 browser_sync::ProfileSyncService* sync_service = |
| 492 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile); |
| 493 |
| 494 bool is_md = false; |
| 495 #if !defined(OS_ANDROID) |
| 496 is_md = MdHistoryUI::IsEnabled(profile); |
| 497 #endif |
| 498 |
| 499 // Convert the result vector into a ListValue. |
| 500 base::ListValue results_value; |
| 501 for (std::vector<BrowsingHistoryService::HistoryEntry>::iterator it = |
| 502 results->begin(); it != results->end(); ++it) { |
| 503 std::unique_ptr<base::Value> value(HistoryEntryToValue(&(*it), |
| 504 bookmark_model, supervised_user_service, sync_service, is_md)); |
| 505 results_value.Append(std::move(value)); |
| 998 } | 506 } |
| 999 return false; | 507 |
| 508 base::DictionaryValue results_info; |
| 509 // The items which are to be written into results_info_value_ are also |
| 510 // described in chrome/browser/resources/history/history.js in @typedef for |
| 511 // HistoryQuery. Please update it whenever you add or remove any keys in |
| 512 // results_info_value_. |
| 513 results_info.SetString("term", query_results_info->search_text); |
| 514 results_info.SetBoolean("finished", query_results_info->reached_beginning); |
| 515 results_info.SetBoolean("hasSyncedResults", |
| 516 query_results_info->has_synced_results); |
| 517 |
| 518 // Add the specific dates that were searched to display them. |
| 519 // TODO(sergiu): Put today if the start is in the future. |
| 520 results_info.SetString( |
| 521 "queryStartTime", |
| 522 GetRelativeDateLocalized(clock_.get(), query_results_info->start_time)); |
| 523 results_info.SetString( |
| 524 "queryEndTime", |
| 525 GetRelativeDateLocalized(clock_.get(), query_results_info->end_time)); |
| 526 |
| 527 web_ui()->CallJavascriptFunctionUnsafe("historyResult", results_info, |
| 528 results_value); |
| 1000 } | 529 } |
| 1001 | 530 |
| 1002 void BrowsingHistoryHandler::OnURLsDeleted( | 531 void BrowsingHistoryHandler::OnRemoveVisitsComplete() { |
| 1003 history::HistoryService* history_service, | 532 web_ui()->CallJavascriptFunctionUnsafe("deleteComplete"); |
| 1004 bool all_history, | |
| 1005 bool expired, | |
| 1006 const history::URLRows& deleted_rows, | |
| 1007 const std::set<GURL>& favicon_urls) { | |
| 1008 if (all_history || DeletionsDiffer(deleted_rows, urls_to_be_deleted_)) | |
| 1009 web_ui()->CallJavascriptFunctionUnsafe("historyDeleted"); | |
| 1010 } | 533 } |
| 1011 | 534 |
| 1012 void BrowsingHistoryHandler::OnWebHistoryDeleted() { | 535 void BrowsingHistoryHandler::OnRemoveVisitsFailed() { |
| 1013 if (!has_pending_delete_request_) | 536 web_ui()->CallJavascriptFunctionUnsafe("deleteFailed"); |
| 1014 web_ui()->CallJavascriptFunctionUnsafe("historyDeleted"); | |
| 1015 } | 537 } |
| 538 |
| 539 void BrowsingHistoryHandler::HistoryDeleted() { |
| 540 web_ui()->CallJavascriptFunctionUnsafe("historyDeleted"); |
| 541 } |
| 542 |
| 543 void BrowsingHistoryHandler::HasOtherFormsOfBrowsingHistory( |
| 544 bool has_other_forms, |
| 545 bool has_synced_results) { |
| 546 web_ui()->CallJavascriptFunctionUnsafe( |
| 547 "showNotification", base::FundamentalValue(has_synced_results), |
| 548 base::FundamentalValue(has_other_forms)); |
| 549 } |
| OLD | NEW |