Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(727)

Side by Side Diff: ios/chrome/browser/ui/history/history_service_facade.mm

Issue 2590473002: Upstream Chrome on iOS source code [5/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import "ios/chrome/browser/ui/history/history_service_facade.h"
6
7 #include <stddef.h>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/i18n/time_formatting.h"
12 #include "base/mac/bind_objc_block.h"
13 #include "base/memory/weak_ptr.h"
14 #include "base/metrics/histogram.h"
15 #include "base/strings/string16.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/time/time.h"
19 #include "base/values.h"
20 #include "components/browser_sync/profile_sync_service.h"
21 #include "components/browsing_data/core/history_notice_utils.h"
22 #include "components/history/core/browser/history_service.h"
23 #include "components/history/core/browser/history_types.h"
24 #include "components/history/core/browser/web_history_service.h"
25 #include "components/keyed_service/core/service_access_type.h"
26 #include "components/prefs/pref_service.h"
27 #include "components/query_parser/snippet.h"
28 #include "components/sync/protocol/history_delete_directive_specifics.pb.h"
29 #include "components/sync/protocol/sync_enums.pb.h"
30 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
31 #include "ios/chrome/browser/history/history_service_factory.h"
32 #include "ios/chrome/browser/history/history_utils.h"
33 #include "ios/chrome/browser/history/web_history_service_factory.h"
34 #include "ios/chrome/browser/pref_names.h"
35 #include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h"
36 #include "ios/chrome/browser/ui/history/history_entry.h"
37 #include "ios/chrome/browser/ui/history/history_service_facade_delegate.h"
38 #include "ios/chrome/browser/ui/history/history_util.h"
39
40 // The amount of time to wait for a response from the WebHistoryService.
41 static const int kWebHistoryTimeoutSeconds = 3;
42
43 namespace {
44
45 // Buckets for UMA histograms.
46 enum WebHistoryQueryBuckets {
47 WEB_HISTORY_QUERY_FAILED = 0,
48 WEB_HISTORY_QUERY_SUCCEEDED,
49 WEB_HISTORY_QUERY_TIMED_OUT,
50 NUM_WEB_HISTORY_QUERY_BUCKETS
51 };
52
53 // Returns true if |entry| represents a local visit that had no corresponding
54 // visit on the server.
55 bool IsLocalOnlyResult(const history::HistoryEntry& entry) {
56 return entry.entry_type == history::HistoryEntry::LOCAL_ENTRY;
57 }
58
59 // Returns true if there are any differences between the URLs observed deleted
60 // and the ones we are expecting to be deleted.
61 static bool DeletionsDiffer(const history::URLRows& observed_deletions,
62 const std::set<GURL>& expected_deletions) {
63 if (observed_deletions.size() != expected_deletions.size())
64 return true;
65 for (const auto& i : observed_deletions) {
66 if (expected_deletions.find(i.url()) == expected_deletions.end())
67 return true;
68 }
69 return false;
70 }
71
72 } // namespace
73
74 #pragma mark - QueryResult
75
76 HistoryServiceFacade::QueryResult::QueryResult()
77 : query(base::string16()),
78 query_start_time(base::string16()),
79 query_end_time(base::string16()),
80 finished(false),
81 has_synced_results(false),
82 sync_finished(false),
83 entries(std::vector<history::HistoryEntry>()) {}
84
85 HistoryServiceFacade::QueryResult::QueryResult(const QueryResult& other) =
86 default;
87
88 HistoryServiceFacade::QueryResult::~QueryResult() {}
89
90 #pragma mark - RemovedEntry
91
92 HistoryServiceFacade::RemovedEntry::RemovedEntry(const GURL& url,
93 const base::Time& timestamp)
94 : url(url) {
95 timestamps = std::vector<base::Time>();
96 timestamps.push_back(timestamp);
97 }
98
99 HistoryServiceFacade::RemovedEntry::RemovedEntry(
100 const GURL& url,
101 const std::vector<base::Time>& timestamps)
102 : url(url), timestamps(timestamps) {}
103
104 HistoryServiceFacade::RemovedEntry::RemovedEntry(const RemovedEntry& other) =
105 default;
106
107 HistoryServiceFacade::RemovedEntry::~RemovedEntry() {}
108
109 #pragma mark - HistoryServiceFacade
110
111 HistoryServiceFacade::HistoryServiceFacade(
112 ios::ChromeBrowserState* browser_state,
113 id<HistoryServiceFacadeDelegate> delegate)
114 : has_pending_delete_request_(false),
115 history_service_observer_(this),
116 browser_state_(browser_state),
117 delegate_(delegate),
118 weak_factory_(this) {
119 // Register as observer of HistoryService.
120 history::HistoryService* history_service =
121 ios::HistoryServiceFactory::GetForBrowserState(
122 browser_state, ServiceAccessType::EXPLICIT_ACCESS);
123 if (history_service)
124 history_service_observer_.Add(history_service);
125 }
126
127 HistoryServiceFacade::~HistoryServiceFacade() {
128 query_task_tracker_.TryCancelAll();
129 web_history_request_.reset();
130 delegate_.reset();
131 }
132
133 void HistoryServiceFacade::QueryHistory(const base::string16& search_text,
134 const history::QueryOptions& options) {
135 // Anything in-flight is invalid.
136 query_task_tracker_.TryCancelAll();
137 web_history_request_.reset();
138
139 // Reset results.
140 query_results_.clear();
141 results_info_value_ = QueryResult();
142
143 // Query local history.
144 history::HistoryService* history_service =
145 ios::HistoryServiceFactory::GetForBrowserState(
146 browser_state_, ServiceAccessType::EXPLICIT_ACCESS);
147 if (history_service) {
148 history_service->QueryHistory(
149 search_text, options,
150 base::Bind(&HistoryServiceFacade::QueryComplete, base::Unretained(this),
151 search_text, options),
152 &query_task_tracker_);
153 }
154
155 // Query synced history.
156 history::WebHistoryService* web_history =
157 ios::WebHistoryServiceFactory::GetForBrowserState(browser_state_);
158 if (web_history) {
159 web_history_query_results_.clear();
160 web_history_request_ = web_history->QueryHistory(
161 search_text, options,
162 base::Bind(&HistoryServiceFacade::WebHistoryQueryComplete,
163 base::Unretained(this), search_text, options,
164 base::TimeTicks::Now()));
165 // Start a timer so we know when to give up.
166 web_history_timer_.Start(
167 FROM_HERE, base::TimeDelta::FromSeconds(kWebHistoryTimeoutSeconds),
168 this, &HistoryServiceFacade::WebHistoryTimeout);
169 }
170 }
171
172 void HistoryServiceFacade::RemoveHistoryEntries(
173 const std::vector<RemovedEntry>& entries) {
174 // Early return if there is a deletion in progress.
175 if (delete_task_tracker_.HasTrackedTasks() || has_pending_delete_request_) {
176 return;
177 }
178
179 history::HistoryService* history_service =
180 ios::HistoryServiceFactory::GetForBrowserState(
181 browser_state_, ServiceAccessType::EXPLICIT_ACCESS);
182 history::WebHistoryService* web_history =
183 ios::WebHistoryServiceFactory::GetForBrowserState(browser_state_);
184
185 base::Time now = base::Time::Now();
186 std::vector<history::ExpireHistoryArgs> expire_list;
187 expire_list.reserve(entries.size());
188
189 DCHECK(urls_to_be_deleted_.empty());
190 for (const RemovedEntry& entry : entries) {
191 GURL url = entry.url;
192 DCHECK(entry.timestamps.size() > 0);
193
194 // In order to ensure that visits will be deleted from the server and other
195 // clients (even if they are offline), create a sync delete directive for
196 // each visit to be deleted.
197 sync_pb::HistoryDeleteDirectiveSpecifics delete_directive;
198 sync_pb::GlobalIdDirective* global_id_directive =
199 delete_directive.mutable_global_id_directive();
200
201 expire_list.resize(expire_list.size() + 1);
202 history::ExpireHistoryArgs* expire_args = &expire_list.back();
203 expire_args->SetTimeRangeForOneDay(entry.timestamps.front());
204 expire_args->urls.insert(entry.url);
205 urls_to_be_deleted_.insert(entry.url);
206
207 for (base::Time visit_time : entry.timestamps) {
208 // The local visit time is treated as a global ID for the visit.
209 global_id_directive->add_global_id(visit_time.ToInternalValue());
210 }
211
212 // Set the start and end time in microseconds since the Unix epoch.
213 global_id_directive->set_start_time_usec(
214 (expire_args->begin_time - base::Time::UnixEpoch()).InMicroseconds());
215
216 // Delete directives shouldn't have an end time in the future.
217 base::Time end_time = std::min(expire_args->end_time, now);
218
219 // -1 because end time in delete directives is inclusive.
220 global_id_directive->set_end_time_usec(
221 (end_time - base::Time::UnixEpoch()).InMicroseconds() - 1);
222
223 if (web_history)
224 history_service->ProcessLocalDeleteDirective(delete_directive);
225 }
226
227 if (history_service) {
228 history_service->ExpireHistory(
229 expire_list, base::Bind(&HistoryServiceFacade::RemoveComplete,
230 base::Unretained(this)),
231 &delete_task_tracker_);
232 }
233
234 if (web_history) {
235 has_pending_delete_request_ = true;
236 web_history->ExpireHistory(
237 expire_list, base::Bind(&HistoryServiceFacade::RemoveWebHistoryComplete,
238 weak_factory_.GetWeakPtr()));
239 }
240 }
241
242 void HistoryServiceFacade::QueryOtherFormsOfBrowsingHistory() {
243 browser_sync::ProfileSyncService* sync_service =
244 IOSChromeProfileSyncServiceFactory::GetForBrowserState(browser_state_);
245 history::WebHistoryService* history_service =
246 ios::WebHistoryServiceFactory::GetForBrowserState(browser_state_);
247 browsing_data::ShouldShowNoticeAboutOtherFormsOfBrowsingHistory(
248 sync_service, history_service,
249 base::Bind(
250 &HistoryServiceFacade::OtherFormsOfBrowsingHistoryQueryComplete,
251 weak_factory_.GetWeakPtr()));
252 }
253
254 #pragma mark - Private methods
255
256 void HistoryServiceFacade::WebHistoryTimeout() {
257 // If there are no outstanding tasks, send results to front end. Would also
258 // be good to communicate the failure to the front end.
259 if (!query_task_tracker_.HasTrackedTasks())
260 ReturnResultsToFrontEnd();
261
262 UMA_HISTOGRAM_ENUMERATION("WebHistory.QueryCompletion",
263 WEB_HISTORY_QUERY_TIMED_OUT,
264 NUM_WEB_HISTORY_QUERY_BUCKETS);
265 }
266
267 void HistoryServiceFacade::QueryComplete(const base::string16& search_text,
268 const history::QueryOptions& options,
269 history::QueryResults* results) {
270 DCHECK_EQ(0U, query_results_.size());
271 query_results_.reserve(results->size());
272
273 for (history::URLResult* result : *results) {
274 query_results_.push_back(history::HistoryEntry(
275 history::HistoryEntry::LOCAL_ENTRY, result->url(), result->title(),
276 result->visit_time(), std::string(), !search_text.empty(),
277 result->snippet().text(), result->blocked_visit()));
278 }
279
280 results_info_value_.query = search_text;
281 results_info_value_.finished = results->reached_beginning();
282 results_info_value_.query_start_time =
283 history::GetRelativeDateLocalized((options.begin_time));
284
285 // Add the specific dates that were searched to display them.
286 // Should put today if the start is in the future.
287 if (!options.end_time.is_null()) {
288 results_info_value_.query_end_time = history::GetRelativeDateLocalized(
289 options.end_time - base::TimeDelta::FromDays(1));
290 } else {
291 results_info_value_.query_end_time =
292 history::GetRelativeDateLocalized(base::Time::Now());
293 }
294 if (!web_history_timer_.IsRunning())
295 ReturnResultsToFrontEnd();
296 }
297
298 void HistoryServiceFacade::WebHistoryQueryComplete(
299 const base::string16& search_text,
300 const history::QueryOptions& options,
301 base::TimeTicks start_time,
302 history::WebHistoryService::Request* request,
303 const base::DictionaryValue* results_value) {
304 base::TimeDelta delta = base::TimeTicks::Now() - start_time;
305 UMA_HISTOGRAM_TIMES("WebHistory.ResponseTime", delta);
306
307 // If the response came in too late, do nothing.
308 if (!web_history_timer_.IsRunning())
309 return;
310 web_history_timer_.Stop();
311
312 UMA_HISTOGRAM_ENUMERATION(
313 "WebHistory.QueryCompletion",
314 results_value ? WEB_HISTORY_QUERY_SUCCEEDED : WEB_HISTORY_QUERY_FAILED,
315 NUM_WEB_HISTORY_QUERY_BUCKETS);
316
317 DCHECK_EQ(0U, web_history_query_results_.size());
318 const base::ListValue* events = NULL;
319 if (results_value && results_value->GetList("event", &events)) {
320 web_history_query_results_.reserve(events->GetSize());
321 for (unsigned int i = 0; i < events->GetSize(); ++i) {
322 const base::DictionaryValue* event = NULL;
323 const base::DictionaryValue* result = NULL;
324 const base::ListValue* results = NULL;
325 const base::ListValue* ids = NULL;
326 base::string16 url;
327 base::string16 title;
328 base::Time visit_time;
329
330 if (!(events->GetDictionary(i, &event) &&
331 event->GetList("result", &results) &&
332 results->GetDictionary(0, &result) &&
333 result->GetString("url", &url) && result->GetList("id", &ids) &&
334 ids->GetSize() > 0)) {
335 LOG(WARNING) << "Improperly formed JSON response from history server.";
336 continue;
337 }
338
339 // Ignore any URLs that should not be shown in the history page.
340 GURL gurl(url);
341 if (!ios::CanAddURLToHistory(gurl))
342 continue;
343
344 // Title is optional, so the return value is ignored here.
345 result->GetString("title", &title);
346
347 // Extract the timestamps of all the visits to this URL.
348 // They are referred to as "IDs" by the server.
349 for (int j = 0; j < static_cast<int>(ids->GetSize()); ++j) {
350 const base::DictionaryValue* id = NULL;
351 std::string timestamp_string;
352 int64_t timestamp_usec = 0;
353
354 if (!ids->GetDictionary(j, &id) ||
355 !id->GetString("timestamp_usec", &timestamp_string) ||
356 !base::StringToInt64(timestamp_string, &timestamp_usec)) {
357 NOTREACHED() << "Unable to extract timestamp.";
358 continue;
359 }
360 // The timestamp on the server is a Unix time.
361 base::Time time = base::Time::UnixEpoch() +
362 base::TimeDelta::FromMicroseconds(timestamp_usec);
363
364 // Get the ID of the client that this visit came from.
365 std::string client_id;
366 id->GetString("client_id", &client_id);
367
368 web_history_query_results_.push_back(history::HistoryEntry(
369 history::HistoryEntry::REMOTE_ENTRY, gurl, title, time, client_id,
370 !search_text.empty(), base::string16(),
371 /* blocked_visit */ false));
372 }
373 }
374 }
375
376 results_info_value_.has_synced_results = results_value != NULL;
377 if (results_value) {
378 std::string continuation_token;
379 results_value->GetString("continuation_token", &continuation_token);
380 results_info_value_.sync_finished = continuation_token.empty();
381 }
382 if (!query_task_tracker_.HasTrackedTasks())
383 ReturnResultsToFrontEnd();
384 }
385
386 void HistoryServiceFacade::RemoveComplete() {
387 urls_to_be_deleted_.clear();
388
389 // Notify the delegate that the deletion request is complete, but only if a
390 // web history delete request is not still pending.
391 if (has_pending_delete_request_)
392 return;
393 if ([delegate_ respondsToSelector:
394 @selector(historyServiceFacadeDidCompleteEntryRemoval:)]) {
395 [delegate_ historyServiceFacadeDidCompleteEntryRemoval:this];
396 }
397 }
398
399 void HistoryServiceFacade::RemoveWebHistoryComplete(bool success) {
400 has_pending_delete_request_ = false;
401 if (!delete_task_tracker_.HasTrackedTasks())
402 RemoveComplete();
403 }
404
405 void HistoryServiceFacade::OtherFormsOfBrowsingHistoryQueryComplete(
406 bool found_other_forms_of_browsing_history) {
407 if ([delegate_ respondsToSelector:
408 @selector(historyServiceFacade:
409 shouldShowNoticeAboutOtherFormsOfBrowsingHistory:)]) {
410 [delegate_ historyServiceFacade:this
411 shouldShowNoticeAboutOtherFormsOfBrowsingHistory:
412 found_other_forms_of_browsing_history];
413 }
414 }
415
416 void HistoryServiceFacade::ReturnResultsToFrontEnd() {
417 // Combine the local and remote results into |query_results_|, and remove
418 // any duplicates.
419 if (!web_history_query_results_.empty()) {
420 int local_result_count = query_results_.size();
421 query_results_.insert(query_results_.end(),
422 web_history_query_results_.begin(),
423 web_history_query_results_.end());
424 history::MergeDuplicateHistoryEntries(&query_results_);
425
426 if (local_result_count) {
427 // In the best case, we expect that all local results are duplicated on
428 // the server. Keep track of how many are missing.
429 int missing_count = std::count_if(
430 query_results_.begin(), query_results_.end(), IsLocalOnlyResult);
431 UMA_HISTOGRAM_PERCENTAGE("WebHistory.LocalResultMissingOnServer",
432 missing_count * 100.0 / local_result_count);
433 }
434 }
435
436 // Send results to delegate. Results may be empty.
437 results_info_value_.entries = query_results_;
438 if ([delegate_ respondsToSelector:@selector(historyServiceFacade:
439 didReceiveQueryResult:)]) {
440 [delegate_ historyServiceFacade:this
441 didReceiveQueryResult:results_info_value_];
442 }
443
444 // Reset results variables.
445 results_info_value_ = QueryResult();
446 query_results_.clear();
447 web_history_query_results_.clear();
448 }
449
450 void HistoryServiceFacade::OnURLsDeleted(
451 history::HistoryService* history_service,
452 bool all_history,
453 bool expired,
454 const history::URLRows& deleted_rows,
455 const std::set<GURL>& favicon_urls) {
456 if (all_history || DeletionsDiffer(deleted_rows, urls_to_be_deleted_)) {
457 if ([delegate_
458 respondsToSelector:
459 @selector(historyServiceFacadeDidObserveHistoryDeletion:)]) {
460 [delegate_ historyServiceFacadeDidObserveHistoryDeletion:this];
461 }
462 }
463 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698