1 // Copyright (c) 2012 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 #include "components/history/core/browser/history_backend.h"
6 
7 #include <algorithm>
8 #include <functional>
9 #include <limits>
10 #include <map>
11 #include <memory>
12 #include <set>
13 #include <utility>
14 #include <vector>
15 
16 #include "base/bind.h"
17 #include "base/callback_helpers.h"
18 #include "base/compiler_specific.h"
19 #include "base/containers/flat_set.h"
20 #include "base/files/file_enumerator.h"
21 #include "base/files/file_path.h"
22 #include "base/files/file_util.h"
23 #include "base/memory/memory_pressure_listener.h"
24 #include "base/metrics/histogram_macros.h"
25 #include "base/no_destructor.h"
26 #include "base/rand_util.h"
27 #include "base/sequenced_task_runner.h"
28 #include "base/single_thread_task_runner.h"
29 #include "base/strings/string_util.h"
30 #include "base/strings/utf_string_conversions.h"
31 #include "base/time/time.h"
32 #include "base/timer/elapsed_timer.h"
33 #include "base/trace_event/trace_event.h"
34 #include "build/build_config.h"
35 #include "components/favicon/core/favicon_backend.h"
36 #include "components/history/core/browser/download_constants.h"
37 #include "components/history/core/browser/download_row.h"
38 #include "components/history/core/browser/history_backend_client.h"
39 #include "components/history/core/browser/history_backend_observer.h"
40 #include "components/history/core/browser/history_constants.h"
41 #include "components/history/core/browser/history_database.h"
42 #include "components/history/core/browser/history_database_params.h"
43 #include "components/history/core/browser/history_db_task.h"
44 #include "components/history/core/browser/in_memory_history_backend.h"
45 #include "components/history/core/browser/keyword_search_term.h"
46 #include "components/history/core/browser/page_usage_data.h"
47 #include "components/history/core/browser/sync/typed_url_sync_bridge.h"
48 #include "components/history/core/browser/url_utils.h"
49 #include "components/sync/model_impl/client_tag_based_model_type_processor.h"
50 #include "components/url_formatter/url_formatter.h"
51 #include "net/base/escape.h"
52 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
53 #include "sql/error_delegate_util.h"
54 #include "third_party/skia/include/core/SkBitmap.h"
55 #include "ui/gfx/codec/png_codec.h"
56 #include "url/gurl.h"
57 #include "url/url_constants.h"
58 
59 #if defined(OS_IOS)
60 #include "base/ios/scoped_critical_action.h"
61 #endif
62 
63 using base::Time;
64 using base::TimeDelta;
65 using base::TimeTicks;
66 using favicon::FaviconBitmap;
67 using favicon::FaviconBitmapID;
68 using favicon::FaviconBitmapIDSize;
69 using favicon::FaviconBitmapType;
70 using favicon::IconMapping;
71 using syncer::ClientTagBasedModelTypeProcessor;
72 
73 /* The HistoryBackend consists of two components:
74 
75     HistoryDatabase (stores past 3 months of history)
76       URLDatabase (stores a list of URLs)
77       DownloadDatabase (stores a list of downloads)
78       VisitDatabase (stores a list of visits for the URLs)
79       VisitSegmentDatabase (stores groups of URLs for the most visited view).
80 
81     ExpireHistoryBackend (manages deleting things older than 3 months)
82 */
83 
84 namespace history {
85 
86 namespace {
87 
88 #if DCHECK_IS_ON()
89 // Use to keep track of paths used to host HistoryBackends. This class
90 // is thread-safe. No two backends should ever run at the same time using the
91 // same directory since they will contend on the files created there.
92 class HistoryPathsTracker {
93  public:
94   HistoryPathsTracker(const HistoryPathsTracker&) = delete;
95   HistoryPathsTracker& operator=(const HistoryPathsTracker&) = delete;
96 
GetInstance()97   static HistoryPathsTracker* GetInstance() {
98     static base::NoDestructor<HistoryPathsTracker> instance;
99     return instance.get();
100   }
101 
AddPath(const base::FilePath & file_path)102   void AddPath(const base::FilePath& file_path) {
103     base::AutoLock auto_lock(lock_);
104     paths_.insert(file_path);
105   }
106 
RemovePath(const base::FilePath & file_path)107   void RemovePath(const base::FilePath& file_path) {
108     base::AutoLock auto_lock(lock_);
109     auto it = paths_.find(file_path);
110 
111     // If the backend was created without a db we are not tracking it.
112     if (it != paths_.end())
113       paths_.erase(it);
114   }
115 
HasPath(const base::FilePath & file_path)116   bool HasPath(const base::FilePath& file_path) {
117     base::AutoLock auto_lock(lock_);
118     return paths_.find(file_path) != paths_.end();
119   }
120 
121  private:
122   friend class base::NoDestructor<HistoryPathsTracker>;
123 
124   HistoryPathsTracker() = default;
125   ~HistoryPathsTracker() = default;
126 
127   base::Lock lock_;
128   base::flat_set<base::FilePath> paths_ GUARDED_BY(lock_);
129 };
130 #endif
131 
HasApiTransition2or3(ui::PageTransition transition)132 bool HasApiTransition2or3(ui::PageTransition transition) {
133   return (ui::PageTransitionGetQualifier(transition) &
134           (ui::PageTransitionGetQualifier(ui::PAGE_TRANSITION_FROM_API_2) |
135            ui::PageTransitionGetQualifier(ui::PAGE_TRANSITION_FROM_API_3))) !=
136          0;
137 }
138 
RunUnlessCanceled(base::OnceClosure closure,const base::CancelableTaskTracker::IsCanceledCallback & is_canceled)139 void RunUnlessCanceled(
140     base::OnceClosure closure,
141     const base::CancelableTaskTracker::IsCanceledCallback& is_canceled) {
142   if (!is_canceled.Run())
143     std::move(closure).Run();
144 }
145 
146 // How long we'll wait to do a commit, so that things are batched together.
147 const int kCommitIntervalSeconds = 10;
148 
149 // The maximum number of items we'll allow in the redirect list before
150 // deleting some.
151 const int kMaxRedirectCount = 32;
152 
153 // The number of days old a history entry can be before it is considered "old"
154 // and is deleted.
155 const int kExpireDaysThreshold = 90;
156 
157 // The maximum number of days for which domain visit metrics are computed
158 // each time HistoryBackend::GetDomainDiversity() is called.
159 constexpr int kDomainDiversityMaxBacktrackedDays = 7;
160 
161 // An offset that corrects possible error in date/time arithmetic caused by
162 // fluctuation of day length due to Daylight Saving Time (DST). For example,
163 // given midnight M, its next midnight can be computed as (M + 24 hour
164 // + offset).LocalMidnight(). In most modern DST systems, the DST shift is
165 // typically 1 hour. However, a larger value of 4 is chosen here to
166 // accommodate larger DST shifts that have been used historically and to
167 // avoid other potential issues.
168 constexpr int kDSTRoundingOffsetHours = 4;
169 
170 }  // namespace
171 
FormatUrlForRedirectComparison(const GURL & url)172 base::string16 FormatUrlForRedirectComparison(const GURL& url) {
173   url::Replacements<char> remove_port;
174   remove_port.ClearPort();
175   return url_formatter::FormatUrl(
176       url.ReplaceComponents(remove_port),
177       url_formatter::kFormatUrlOmitHTTP | url_formatter::kFormatUrlOmitHTTPS |
178           url_formatter::kFormatUrlOmitUsernamePassword |
179           url_formatter::kFormatUrlOmitTrivialSubdomains,
180       net::UnescapeRule::NONE, nullptr, nullptr, nullptr);
181 }
182 
MidnightNDaysLater(base::Time time,int days)183 base::Time MidnightNDaysLater(base::Time time, int days) {
184   return (time.LocalMidnight() + base::TimeDelta::FromDays(days) +
185           base::TimeDelta::FromHours(kDSTRoundingOffsetHours))
186       .LocalMidnight();
187 }
188 
QueuedHistoryDBTask(std::unique_ptr<HistoryDBTask> task,scoped_refptr<base::SingleThreadTaskRunner> origin_loop,const base::CancelableTaskTracker::IsCanceledCallback & is_canceled)189 QueuedHistoryDBTask::QueuedHistoryDBTask(
190     std::unique_ptr<HistoryDBTask> task,
191     scoped_refptr<base::SingleThreadTaskRunner> origin_loop,
192     const base::CancelableTaskTracker::IsCanceledCallback& is_canceled)
193     : task_(std::move(task)),
194       origin_loop_(origin_loop),
195       is_canceled_(is_canceled) {
196   DCHECK(task_);
197   DCHECK(origin_loop_);
198   DCHECK(!is_canceled_.is_null());
199 }
200 
~QueuedHistoryDBTask()201 QueuedHistoryDBTask::~QueuedHistoryDBTask() {
202   // Ensure that |task_| is destroyed on its origin thread.
203   origin_loop_->PostTask(FROM_HERE,
204                          base::BindOnce(&base::DeletePointer<HistoryDBTask>,
205                                         base::Unretained(task_.release())));
206 }
207 
is_canceled()208 bool QueuedHistoryDBTask::is_canceled() {
209   return is_canceled_.Run();
210 }
211 
Run(HistoryBackend * backend,HistoryDatabase * db)212 bool QueuedHistoryDBTask::Run(HistoryBackend* backend, HistoryDatabase* db) {
213   return task_->RunOnDBThread(backend, db);
214 }
215 
DoneRun()216 void QueuedHistoryDBTask::DoneRun() {
217   origin_loop_->PostTask(
218       FROM_HERE,
219       base::BindOnce(&RunUnlessCanceled,
220                      base::BindOnce(&HistoryDBTask::DoneRunOnMainThread,
221                                     base::Unretained(task_.get())),
222                      is_canceled_));
223 }
224 
225 // HistoryBackendHelper --------------------------------------------------------
226 
227 // Wrapper around base::SupportsUserData with a public destructor.
228 class HistoryBackendHelper : public base::SupportsUserData {
229  public:
230   HistoryBackendHelper();
231   ~HistoryBackendHelper() override;
232 };
233 
234 HistoryBackendHelper::HistoryBackendHelper() = default;
235 
236 HistoryBackendHelper::~HistoryBackendHelper() = default;
237 
238 // HistoryBackend --------------------------------------------------------------
239 
240 // static
IsTypedIncrement(ui::PageTransition transition)241 bool HistoryBackend::IsTypedIncrement(ui::PageTransition transition) {
242   if (ui::PageTransitionIsNewNavigation(transition) &&
243       ((ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED) &&
244         !ui::PageTransitionIsRedirect(transition)) ||
245        ui::PageTransitionCoreTypeIs(transition,
246                                     ui::PAGE_TRANSITION_KEYWORD_GENERATED))) {
247     return true;
248   }
249   return false;
250 }
251 
HistoryBackend(std::unique_ptr<Delegate> delegate,std::unique_ptr<HistoryBackendClient> backend_client,scoped_refptr<base::SequencedTaskRunner> task_runner)252 HistoryBackend::HistoryBackend(
253     std::unique_ptr<Delegate> delegate,
254     std::unique_ptr<HistoryBackendClient> backend_client,
255     scoped_refptr<base::SequencedTaskRunner> task_runner)
256     : delegate_(std::move(delegate)),
257       scheduled_kill_db_(false),
258       expirer_(this, backend_client.get(), task_runner),
259       recent_redirects_(kMaxRedirectCount),
260       segment_queried_(false),
261       backend_client_(std::move(backend_client)),
262       task_runner_(task_runner) {
263   DCHECK(delegate_);
264 }
265 
~HistoryBackend()266 HistoryBackend::~HistoryBackend() {
267   DCHECK(scheduled_commit_.IsCancelled()) << "Deleting without cleanup";
268   queued_history_db_tasks_.clear();
269 
270   // Release stashed embedder object before cleaning up the databases.
271   supports_user_data_helper_.reset();
272 
273   // First close the databases before optionally running the "destroy" task.
274   CloseAllDatabases();
275 
276   if (!backend_destroy_task_.is_null()) {
277     // Notify an interested party (typically a unit test) that we're done.
278     DCHECK(backend_destroy_task_runner_);
279     backend_destroy_task_runner_->PostTask(FROM_HERE,
280                                            std::move(backend_destroy_task_));
281   }
282 
283 #if DCHECK_IS_ON()
284   HistoryPathsTracker::GetInstance()->RemovePath(history_dir_);
285 #endif
286 
287 #if defined(OS_ANDROID)
288   if (backend_client_ && !history_dir_.empty())
289     backend_client_->OnHistoryBackendDestroyed(this, history_dir_);
290 #endif
291 }
292 
Init(bool force_fail,const HistoryDatabaseParams & history_database_params)293 void HistoryBackend::Init(
294     bool force_fail,
295     const HistoryDatabaseParams& history_database_params) {
296   TRACE_EVENT0("browser", "HistoryBackend::Init");
297 
298   DCHECK(base::PathExists(history_database_params.history_dir))
299       << "History directory does not exist. If you are in a test make sure "
300          "that ~TestingProfile() has not been called or that the "
301          "ScopedTempDirectory used outlives this task.";
302 
303   // HistoryBackend is created on the UI thread by HistoryService, then the
304   // HistoryBackend::Init() method is called on the DB thread. Create the
305   // base::SupportsUserData on the DB thread since it is not thread-safe.
306   supports_user_data_helper_.reset(new HistoryBackendHelper);
307 
308   if (!force_fail)
309     InitImpl(history_database_params);
310   delegate_->DBLoaded();
311 
312   typed_url_sync_bridge_ = std::make_unique<TypedURLSyncBridge>(
313       this, db_.get(),
314       std::make_unique<ClientTagBasedModelTypeProcessor>(
315           syncer::TYPED_URLS, /*dump_stack=*/base::RepeatingClosure()));
316   typed_url_sync_bridge_->Init();
317 
318   memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>(
319       FROM_HERE, base::BindRepeating(&HistoryBackend::OnMemoryPressure,
320                                      base::Unretained(this)));
321 }
322 
SetOnBackendDestroyTask(scoped_refptr<base::SingleThreadTaskRunner> task_runner,base::OnceClosure task)323 void HistoryBackend::SetOnBackendDestroyTask(
324     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
325     base::OnceClosure task) {
326   TRACE_EVENT0("browser", "HistoryBackend::SetOnBackendDestroyTask");
327   if (!backend_destroy_task_.is_null())
328     DLOG(WARNING) << "Setting more than one destroy task, overriding";
329   backend_destroy_task_runner_ = std::move(task_runner);
330   backend_destroy_task_ = std::move(task);
331 }
332 
Closing()333 void HistoryBackend::Closing() {
334   TRACE_EVENT0("browser", "HistoryBackend::Closing");
335   // Any scheduled commit will have a reference to us, we must make it
336   // release that reference before we can be destroyed.
337   CancelScheduledCommit();
338 }
339 
340 #if defined(OS_IOS)
PersistState()341 void HistoryBackend::PersistState() {
342   TRACE_EVENT0("browser", "HistoryBackend::PersistState");
343   Commit();
344 }
345 #endif
346 
ClearCachedDataForContextID(ContextID context_id)347 void HistoryBackend::ClearCachedDataForContextID(ContextID context_id) {
348   TRACE_EVENT0("browser", "HistoryBackend::ClearCachedDataForContextID");
349   tracker_.ClearCachedDataForContextID(context_id);
350 }
351 
GetFaviconsFileName() const352 base::FilePath HistoryBackend::GetFaviconsFileName() const {
353   return history_dir_.Append(kFaviconsFilename);
354 }
355 
GetLastSegmentID(VisitID from_visit)356 SegmentID HistoryBackend::GetLastSegmentID(VisitID from_visit) {
357   // Set is used to detect referrer loops.  Should not happen, but can
358   // if the database is corrupt.
359   std::set<VisitID> visit_set;
360   VisitID visit_id = from_visit;
361   while (visit_id) {
362     VisitRow row;
363     if (!db_->GetRowForVisit(visit_id, &row))
364       return 0;
365     if (row.segment_id)
366       return row.segment_id;  // Found a visit in this change with a segment.
367 
368     // Check the referrer of this visit, if any.
369     visit_id = row.referring_visit;
370 
371     if (visit_set.find(visit_id) != visit_set.end()) {
372       NOTREACHED() << "Loop in referer chain, giving up";
373       break;
374     }
375     visit_set.insert(visit_id);
376   }
377   return 0;
378 }
379 
UpdateSegments(const GURL & url,VisitID from_visit,VisitID visit_id,ui::PageTransition transition_type,const Time ts)380 SegmentID HistoryBackend::UpdateSegments(const GURL& url,
381                                          VisitID from_visit,
382                                          VisitID visit_id,
383                                          ui::PageTransition transition_type,
384                                          const Time ts) {
385   if (!db_)
386     return 0;
387 
388   // We only consider main frames.
389   if (!ui::PageTransitionIsMainFrame(transition_type))
390     return 0;
391 
392   SegmentID segment_id = 0;
393 
394   // Are we at the beginning of a new segment?
395   // Note that navigating to an existing entry (with back/forward) reuses the
396   // same transition type.  We are not adding it as a new segment in that case
397   // because if this was the target of a redirect, we might end up with
398   // 2 entries for the same final URL. Ex: User types google.net, gets
399   // redirected to google.com. A segment is created for google.net. On
400   // google.com users navigates through a link, then press back. That last
401   // navigation is for the entry google.com transition typed. We end up adding
402   // a segment for that one as well. So we end up with google.net and google.com
403   // in the segment table, showing as 2 entries in the NTP.
404   // Note also that we should still be updating the visit count for that segment
405   // which we are not doing now. It should be addressed when
406   // http://crbug.com/96860 is fixed.
407   if ((ui::PageTransitionCoreTypeIs(transition_type,
408                                     ui::PAGE_TRANSITION_TYPED) ||
409        ui::PageTransitionCoreTypeIs(transition_type,
410                                     ui::PAGE_TRANSITION_AUTO_BOOKMARK)) &&
411       (transition_type & ui::PAGE_TRANSITION_FORWARD_BACK) == 0) {
412     // If so, create or get the segment.
413     std::string segment_name = db_->ComputeSegmentName(url);
414     URLID url_id = db_->GetRowForURL(url, nullptr);
415     if (!url_id)
416       return 0;
417 
418     segment_id = db_->GetSegmentNamed(segment_name);
419     if (!segment_id) {
420       segment_id = db_->CreateSegment(url_id, segment_name);
421       if (!segment_id) {
422         NOTREACHED();
423         return 0;
424       }
425     } else {
426       // Note: if we update an existing segment, we update the url used to
427       // represent that segment in order to minimize stale most visited
428       // images.
429       db_->UpdateSegmentRepresentationURL(segment_id, url_id);
430     }
431   } else {
432     // Note: it is possible there is no segment ID set for this visit chain.
433     // This can happen if the initial navigation wasn't AUTO_BOOKMARK or
434     // TYPED. (For example GENERATED). In this case this visit doesn't count
435     // toward any segment.
436     segment_id = GetLastSegmentID(from_visit);
437     if (!segment_id)
438       return 0;
439   }
440 
441   // Set the segment in the visit.
442   if (!db_->SetSegmentID(visit_id, segment_id)) {
443     NOTREACHED();
444     return 0;
445   }
446 
447   // Finally, increase the counter for that segment / day.
448   if (!db_->IncreaseSegmentVisitCount(segment_id, ts, 1)) {
449     NOTREACHED();
450     return 0;
451   }
452   return segment_id;
453 }
454 
UpdateWithPageEndTime(ContextID context_id,int nav_entry_id,const GURL & url,Time end_ts)455 void HistoryBackend::UpdateWithPageEndTime(ContextID context_id,
456                                            int nav_entry_id,
457                                            const GURL& url,
458                                            Time end_ts) {
459   TRACE_EVENT0("browser", "HistoryBackend::UpdateWithPageEndTime");
460   // Will be filled with the URL ID and the visit ID of the last addition.
461   VisitID visit_id = tracker_.GetLastVisit(context_id, nav_entry_id, url);
462   UpdateVisitDuration(visit_id, end_ts);
463 }
464 
UpdateVisitDuration(VisitID visit_id,const Time end_ts)465 void HistoryBackend::UpdateVisitDuration(VisitID visit_id, const Time end_ts) {
466   if (!db_)
467     return;
468 
469   // Get the starting visit_time for visit_id.
470   VisitRow visit_row;
471   if (db_->GetRowForVisit(visit_id, &visit_row)) {
472     // We should never have a negative duration time even when time is skewed.
473     visit_row.visit_duration = end_ts > visit_row.visit_time
474                                    ? end_ts - visit_row.visit_time
475                                    : TimeDelta::FromMicroseconds(0);
476     db_->UpdateVisitRow(visit_row);
477   }
478 }
479 
IsUntypedIntranetHost(const GURL & url)480 bool HistoryBackend::IsUntypedIntranetHost(const GURL& url) {
481   if (!url.SchemeIs(url::kHttpScheme) && !url.SchemeIs(url::kHttpsScheme) &&
482       !url.SchemeIs(url::kFtpScheme))
483     return false;
484 
485   const std::string host = url.host();
486   const size_t registry_length =
487       net::registry_controlled_domains::GetCanonicalHostRegistryLength(
488           host, net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES,
489           net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
490   return (registry_length == 0) && !db_->IsTypedHost(host, /*scheme=*/nullptr);
491 }
492 
GetCountsAndLastVisitForOrigins(const std::set<GURL> & origins) const493 OriginCountAndLastVisitMap HistoryBackend::GetCountsAndLastVisitForOrigins(
494     const std::set<GURL>& origins) const {
495   if (!db_)
496     return OriginCountAndLastVisitMap();
497   if (origins.empty())
498     return OriginCountAndLastVisitMap();
499 
500   URLDatabase::URLEnumerator it;
501   if (!db_->InitURLEnumeratorForEverything(&it))
502     return OriginCountAndLastVisitMap();
503 
504   OriginCountAndLastVisitMap origin_count_map;
505   for (const GURL& origin : origins)
506     origin_count_map[origin] = std::make_pair(0, base::Time());
507 
508   URLRow row;
509   while (it.GetNextURL(&row)) {
510     GURL origin = row.url().GetOrigin();
511     auto iter = origin_count_map.find(origin);
512     if (iter != origin_count_map.end()) {
513       std::pair<int, base::Time>& value = iter->second;
514       ++(value.first);
515       if (value.second.is_null() || value.second < row.last_visit())
516         value.second = row.last_visit();
517     }
518   }
519 
520   return origin_count_map;
521 }
522 
AddPage(const HistoryAddPageArgs & request)523 void HistoryBackend::AddPage(const HistoryAddPageArgs& request) {
524   TRACE_EVENT0("browser", "HistoryBackend::AddPage");
525 
526   if (!db_)
527     return;
528 
529   // Will be filled with the URL ID and the visit ID of the last addition.
530   std::pair<URLID, VisitID> last_ids(
531       0, tracker_.GetLastVisit(request.context_id, request.nav_entry_id,
532                                request.referrer));
533 
534   VisitID from_visit_id = last_ids.second;
535 
536   // If a redirect chain is given, we expect the last item in that chain to be
537   // the final URL.
538   DCHECK(request.redirects.empty() || request.redirects.back() == request.url);
539 
540   // If the user is adding older history, we need to make sure our times
541   // are correct.
542   if (request.time < first_recorded_time_)
543     first_recorded_time_ = request.time;
544 
545   ui::PageTransition request_transition = request.transition;
546   bool is_keyword_generated = ui::PageTransitionCoreTypeIs(
547       request_transition, ui::PAGE_TRANSITION_KEYWORD_GENERATED);
548 
549   // If the user is navigating to a not-previously-typed intranet hostname,
550   // change the transition to TYPED so that the omnibox will learn that this is
551   // a known host. This logic is disabled if API_2/API_3 is present as such
552   // visits are not intended to influence the omnibox, and shouldn't be
553   // changed to TYPED. (API_2/API_3 are not used with TYPED transitions).
554   bool has_redirects = request.redirects.size() > 1;
555   if (ui::PageTransitionIsMainFrame(request_transition) &&
556       !ui::PageTransitionCoreTypeIs(request_transition,
557                                     ui::PAGE_TRANSITION_TYPED) &&
558       !is_keyword_generated && !HasApiTransition2or3(request_transition)) {
559     // Check both the start and end of a redirect chain, since the user will
560     // consider both to have been "navigated to".
561     if (IsUntypedIntranetHost(request.url) ||
562         (has_redirects && IsUntypedIntranetHost(request.redirects[0]))) {
563       request_transition = ui::PageTransitionFromInt(
564           ui::PAGE_TRANSITION_TYPED |
565           ui::PageTransitionGetQualifier(request_transition));
566     }
567   }
568 
569   // FROM_API_2/FROM_API_3 should never be used with a transition type that
570   // increments the typed-count as that defeats the purpose.
571   DCHECK(!IsTypedIncrement(request_transition) ||
572          !HasApiTransition2or3(request_transition));
573 
574   if (!has_redirects) {
575     // The single entry is both a chain start and end.
576     ui::PageTransition t = ui::PageTransitionFromInt(
577         request_transition | ui::PAGE_TRANSITION_CHAIN_START |
578         ui::PAGE_TRANSITION_CHAIN_END);
579 
580     // No redirect case (one element means just the page itself).
581     last_ids =
582         AddPageVisit(request.url, request.time, last_ids.second, t,
583                      request.hidden, request.visit_source, IsTypedIncrement(t),
584                      request.publicly_routable, request.title);
585 
586     // Update the segment for this visit. KEYWORD_GENERATED visits should not
587     // result in changing most visited, so we don't update segments (most
588     // visited db).
589     if (!is_keyword_generated && request.consider_for_ntp_most_visited) {
590       UpdateSegments(request.url, from_visit_id, last_ids.second, t,
591                      request.time);
592 
593       // Update the referrer's duration.
594       UpdateVisitDuration(from_visit_id, request.time);
595     }
596   } else {
597     // Redirect case. Add the redirect chain.
598 
599     ui::PageTransition redirect_info = ui::PAGE_TRANSITION_CHAIN_START;
600 
601     RedirectList redirects = request.redirects;
602     // In the presence of client redirects, |request.redirects| can be a partial
603     // chain because previous calls to this function may have reported a
604     // redirect chain already. This is fine for the visits database where we'll
605     // just append data but insufficient for |recent_redirects_|
606     // (backpropagation of favicons and titles), where we'd like the full
607     // (extended) redirect chain. We use |extended_redirect_chain| to represent
608     // this.
609     RedirectList extended_redirect_chain;
610 
611     if (redirects[0].SchemeIs(url::kAboutScheme)) {
612       // When the redirect source + referrer is "about" we skip it. This
613       // happens when a page opens a new frame/window to about:blank and then
614       // script sets the URL to somewhere else (used to hide the referrer). It
615       // would be nice to keep all these redirects properly but we don't ever
616       // see the initial about:blank load, so we don't know where the
617       // subsequent client redirect came from.
618       //
619       // In this case, we just don't bother hooking up the source of the
620       // redirects, so we remove it.
621       redirects.erase(redirects.begin());
622     } else if (request_transition & ui::PAGE_TRANSITION_CLIENT_REDIRECT) {
623       redirect_info = ui::PAGE_TRANSITION_CLIENT_REDIRECT;
624       // The first entry in the redirect chain initiated a client redirect.
625       // We don't add this to the database since the referrer is already
626       // there, so we skip over it but change the transition type of the first
627       // transition to client redirect.
628       //
629       // The referrer is invalid when restoring a session that features an
630       // https tab that redirects to a different host or to http. In this
631       // case we don't need to reconnect the new redirect with the existing
632       // chain.
633       if (request.referrer.is_valid()) {
634         DCHECK_EQ(request.referrer, redirects[0]);
635         redirects.erase(redirects.begin());
636 
637         // If the navigation entry for this visit has replaced that for the
638         // first visit, remove the CHAIN_END marker from the first visit. This
639         // can be called a lot, for example, the page cycler, and most of the
640         // time we won't have changed anything.
641         VisitRow visit_row;
642         if (request.did_replace_entry) {
643           if (db_->GetRowForVisit(last_ids.second, &visit_row) &&
644               visit_row.transition & ui::PAGE_TRANSITION_CHAIN_END) {
645             visit_row.transition = ui::PageTransitionFromInt(
646                 visit_row.transition & ~ui::PAGE_TRANSITION_CHAIN_END);
647             db_->UpdateVisitRow(visit_row);
648           }
649 
650           extended_redirect_chain = GetCachedRecentRedirects(request.referrer);
651         }
652       }
653     }
654 
655     bool transfer_typed_credit_from_first_to_second_url = false;
656     if (redirects.size() > 1) {
657       // Check if the first redirect is the same as the original URL but
658       // upgraded to HTTPS. This ignores the port numbers (in case of
659       // non-standard HTTP or HTTPS ports) and trivial subdomains (e.g., "www."
660       // or "m.").
661       if (IsTypedIncrement(request_transition) &&
662           redirects[0].SchemeIs(url::kHttpScheme) &&
663           redirects[1].SchemeIs(url::kHttpsScheme) &&
664           FormatUrlForRedirectComparison(redirects[0]) ==
665               FormatUrlForRedirectComparison(redirects[1])) {
666         transfer_typed_credit_from_first_to_second_url = true;
667       }
668     }
669 
670     for (size_t redirect_index = 0; redirect_index < redirects.size();
671          redirect_index++) {
672       ui::PageTransition t = ui::PageTransitionFromInt(
673           ui::PageTransitionStripQualifier(request_transition) | redirect_info);
674 
675       bool publicly_routable = false;
676 
677       // If this is the last transition, add a CHAIN_END marker
678       if (redirect_index == (redirects.size() - 1)) {
679         t = ui::PageTransitionFromInt(t | ui::PAGE_TRANSITION_CHAIN_END);
680         // In order for a visit to be visible, it must have CHAIN_END. If the
681         // requested transition contained PAGE_TRANSITION_FROM_API_3, then
682         // add it to the CHAIN_END visit so that the visit is not visible.
683         if ((ui::PageTransitionGetQualifier(request_transition) &
684              ui::PAGE_TRANSITION_FROM_API_3) != 0) {
685           t = ui::PageTransitionFromInt(t | ui::PAGE_TRANSITION_FROM_API_3);
686         }
687 
688         // Since request.publicly_routable is a property of the visit to
689         // request.url, it only applies to the final redirect.
690         publicly_routable = request.publicly_routable;
691       }
692 
693       bool should_increment_typed_count = IsTypedIncrement(t);
694       if (transfer_typed_credit_from_first_to_second_url) {
695         if (redirect_index == 0)
696           should_increment_typed_count = false;
697         else if (redirect_index == 1)
698           should_increment_typed_count = true;
699       }
700 
701       // Record all redirect visits with the same timestamp. We don't display
702       // them anyway, and if we ever decide to, we can reconstruct their order
703       // from the redirect chain.
704       last_ids = AddPageVisit(
705           redirects[redirect_index], request.time, last_ids.second, t,
706           request.hidden, request.visit_source, should_increment_typed_count,
707           publicly_routable, request.title);
708 
709       if (t & ui::PAGE_TRANSITION_CHAIN_START) {
710         if (request.consider_for_ntp_most_visited) {
711           UpdateSegments(redirects[redirect_index], from_visit_id,
712                          last_ids.second, t, request.time);
713         }
714 
715         // Update the visit_details for this visit.
716         UpdateVisitDuration(from_visit_id, request.time);
717       }
718 
719       // Subsequent transitions in the redirect list must all be server
720       // redirects.
721       redirect_info = ui::PAGE_TRANSITION_SERVER_REDIRECT;
722     }
723 
724     // Last, save this redirect chain for later so we can set titles & favicons
725     // on the redirected pages properly. For this we use the extended redirect
726     // chain, which includes URLs from chained redirects.
727     extended_redirect_chain.insert(extended_redirect_chain.end(),
728                                    std::make_move_iterator(redirects.begin()),
729                                    std::make_move_iterator(redirects.end()));
730     recent_redirects_.Put(request.url, extended_redirect_chain);
731   }
732 
733   // TODO(brettw) bug 1140015: Add an "add page" notification so the history
734   // views can keep in sync.
735 
736   // Add the last visit to the tracker so we can get outgoing transitions.
737   // TODO(evanm): Due to http://b/1194536 we lose the referrers of a subframe
738   // navigation anyway, so last_visit_id is always zero for them.  But adding
739   // them here confuses main frame history, so we skip them for now.
740   if (!ui::PageTransitionCoreTypeIs(request_transition,
741                                     ui::PAGE_TRANSITION_AUTO_SUBFRAME) &&
742       !ui::PageTransitionCoreTypeIs(request_transition,
743                                     ui::PAGE_TRANSITION_MANUAL_SUBFRAME) &&
744       !is_keyword_generated) {
745     tracker_.AddVisit(request.context_id, request.nav_entry_id, request.url,
746                       last_ids.second);
747   }
748 
749   ScheduleCommit();
750 }
751 
InitImpl(const HistoryDatabaseParams & history_database_params)752 void HistoryBackend::InitImpl(
753     const HistoryDatabaseParams& history_database_params) {
754   DCHECK(!db_) << "Initializing HistoryBackend twice";
755   // In the rare case where the db fails to initialize a dialog may get shown
756   // the blocks the caller, yet allows other messages through. For this reason
757   // we only set db_ to the created database if creation is successful. That
758   // way other methods won't do anything as db_ is still null.
759 
760   TimeTicks beginning_time = TimeTicks::Now();
761 
762   // Compute the file names.
763   history_dir_ = history_database_params.history_dir;
764 
765 #if DCHECK_IS_ON()
766   DCHECK(!HistoryPathsTracker::GetInstance()->HasPath(history_dir_))
767       << "There already is a HistoryBackend running using the file at: "
768       << history_database_params.history_dir
769       << ". Tests have to make sure that HistoryBackend destruction is "
770          "complete using SetOnBackendDestroyTask() or other flush mechanisms "
771          "before creating a new HistoryBackend that uses the same directory.";
772 
773   HistoryPathsTracker::GetInstance()->AddPath(history_dir_);
774 #endif
775 
776   base::FilePath history_name = history_dir_.Append(kHistoryFilename);
777   base::FilePath favicon_name = GetFaviconsFileName();
778 
779   // Delete the old index database files which are no longer used.
780   DeleteFTSIndexDatabases();
781 
782   // History database.
783   db_.reset(new HistoryDatabase(
784       history_database_params.download_interrupt_reason_none,
785       history_database_params.download_interrupt_reason_crash));
786 
787   // Unretained to avoid a ref loop with db_.
788   db_->set_error_callback(base::BindRepeating(
789       &HistoryBackend::DatabaseErrorCallback, base::Unretained(this)));
790 
791   db_diagnostics_.clear();
792   sql::InitStatus status = db_->Init(history_name);
793   switch (status) {
794     case sql::INIT_OK:
795       break;
796     case sql::INIT_FAILURE: {
797       // A null db_ will cause all calls on this object to notice this error
798       // and to not continue. If the error callback scheduled killing the
799       // database, the task it posted has not executed yet. Try killing the
800       // database now before we close it.
801       bool kill_db = scheduled_kill_db_;
802       if (kill_db)
803         KillHistoryDatabase();
804 
805       // The frequency of this UMA will indicate how often history
806       // initialization fails.
807       UMA_HISTOGRAM_BOOLEAN("History.AttemptedToFixProfileError", kill_db);
808       FALLTHROUGH;
809     }
810     case sql::INIT_TOO_NEW: {
811       db_diagnostics_ += sql::GetCorruptFileDiagnosticsInfo(history_name);
812       delegate_->NotifyProfileError(status, db_diagnostics_);
813       db_.reset();
814       return;
815     }
816     default:
817       NOTREACHED();
818   }
819 
820   // Fill the in-memory database and send it back to the history service on the
821   // main thread.
822   {
823     std::unique_ptr<InMemoryHistoryBackend> mem_backend(
824         new InMemoryHistoryBackend);
825     if (mem_backend->Init(history_name))
826       delegate_->SetInMemoryBackend(std::move(mem_backend));
827   }
828   db_->BeginExclusiveMode();  // Must be after the mem backend read the data.
829 
830   // Favicon database.
831   favicon_backend_ = favicon::FaviconBackend::Create(favicon_name, this);
832   // Unlike the main database, we don't error out if the favicon database can't
833   // be created. Generally, this shouldn't happen since the favicon and main
834   // database versions should be in sync. We'll just continue without favicons
835   // in this case or any other error.
836 
837   // Generate the history and favicon database metrics only after performing
838   // any migration work.
839   if (base::RandInt(1, 100) == 50) {
840     // Only do this computation sometimes since it can be expensive.
841     db_->ComputeDatabaseMetrics(history_name);
842   }
843 
844   favicon::FaviconDatabase* favicon_db_ptr =
845       favicon_backend_ ? favicon_backend_->db() : nullptr;
846 
847   expirer_.SetDatabases(db_.get(), favicon_db_ptr);
848 
849   // Open the long-running transaction.
850   db_->BeginTransaction();
851 
852   // Get the first item in our database.
853   db_->GetStartDate(&first_recorded_time_);
854 
855   // Start expiring old stuff.
856   expirer_.StartExpiringOldStuff(TimeDelta::FromDays(kExpireDaysThreshold));
857 
858 #if defined(OS_ANDROID)
859   if (backend_client_) {
860     backend_client_->OnHistoryBackendInitialized(this, db_.get(),
861                                                  favicon_db_ptr, history_dir_);
862   }
863 #endif
864 
865   LOCAL_HISTOGRAM_TIMES("History.InitTime", TimeTicks::Now() - beginning_time);
866 }
867 
OnMemoryPressure(base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level)868 void HistoryBackend::OnMemoryPressure(
869     base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
870   // TODO(sebmarchand): Check if MEMORY_PRESSURE_LEVEL_MODERATE should also be
871   // ignored.
872   if (memory_pressure_level ==
873       base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) {
874     return;
875   }
876   if (db_)
877     db_->TrimMemory();
878   if (favicon_backend_)
879     favicon_backend_->TrimMemory();
880 }
881 
CloseAllDatabases()882 void HistoryBackend::CloseAllDatabases() {
883   if (db_) {
884     // Commit the long-running transaction.
885     db_->CommitTransaction();
886     db_.reset();
887     // Forget the first recorded time since the database is closed.
888     first_recorded_time_ = base::Time();
889   }
890   favicon_backend_.reset();
891 }
892 
AddPageVisit(const GURL & url,Time time,VisitID referring_visit,ui::PageTransition transition,bool hidden,VisitSource visit_source,bool should_increment_typed_count,bool publicly_routable,base::Optional<base::string16> title)893 std::pair<URLID, VisitID> HistoryBackend::AddPageVisit(
894     const GURL& url,
895     Time time,
896     VisitID referring_visit,
897     ui::PageTransition transition,
898     bool hidden,
899     VisitSource visit_source,
900     bool should_increment_typed_count,
901     bool publicly_routable,
902     base::Optional<base::string16> title) {
903   // See if this URL is already in the DB.
904   URLRow url_info(url);
905   URLID url_id = db_->GetRowForURL(url, &url_info);
906   if (url_id) {
907     // Update of an existing row.
908     if (!ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_RELOAD))
909       url_info.set_visit_count(url_info.visit_count() + 1);
910     if (should_increment_typed_count)
911       url_info.set_typed_count(url_info.typed_count() + 1);
912     if (url_info.last_visit() < time)
913       url_info.set_last_visit(time);
914     if (title)
915       url_info.set_title(title.value());
916 
917     // Only allow un-hiding of pages, never hiding.
918     if (!hidden)
919       url_info.set_hidden(false);
920 
921     db_->UpdateURLRow(url_id, url_info);
922   } else {
923     // Addition of a new row.
924     url_info.set_visit_count(1);
925     url_info.set_typed_count(should_increment_typed_count ? 1 : 0);
926     url_info.set_last_visit(time);
927     if (title)
928       url_info.set_title(title.value());
929     url_info.set_hidden(hidden);
930 
931     url_id = db_->AddURL(url_info);
932     if (!url_id) {
933       NOTREACHED() << "Adding URL failed.";
934       return std::make_pair(0, 0);
935     }
936     url_info.set_id(url_id);
937   }
938 
939   // Add the visit with the time to the database.
940   VisitRow visit_info(url_id, time, referring_visit, transition, 0,
941                       should_increment_typed_count, publicly_routable);
942   VisitID visit_id = db_->AddVisit(&visit_info, visit_source);
943 
944   if (visit_info.visit_time < first_recorded_time_)
945     first_recorded_time_ = visit_info.visit_time;
946 
947   // Broadcast a notification of the visit.
948   if (visit_id) {
949     RedirectList redirects;
950     // TODO(meelapshah) Disabled due to potential PageCycler regression.
951     // Re-enable this.
952     // QueryRedirectsTo(url, &redirects);
953     NotifyURLVisited(transition, url_info, redirects, time);
954   } else {
955     DVLOG(0) << "Failed to build visit insert statement:  "
956              << "url_id = " << url_id;
957   }
958 
959   return std::make_pair(url_id, visit_id);
960 }
961 
AddPagesWithDetails(const URLRows & urls,VisitSource visit_source)962 void HistoryBackend::AddPagesWithDetails(const URLRows& urls,
963                                          VisitSource visit_source) {
964   TRACE_EVENT0("browser", "HistoryBackend::AddPagesWithDetails");
965 
966   if (!db_)
967     return;
968 
969   URLRows changed_urls;
970   for (auto i = urls.begin(); i != urls.end(); ++i) {
971     DCHECK(!i->last_visit().is_null());
972 
973     // As of M37, we no longer maintain an archived database, ignore old visits.
974     if (IsExpiredVisitTime(i->last_visit()))
975       continue;
976 
977     URLRow existing_url;
978     URLID url_id = db_->GetRowForURL(i->url(), &existing_url);
979     if (!url_id) {
980       // Add the page if it doesn't exist.
981       url_id = db_->AddURL(*i);
982       if (!url_id) {
983         NOTREACHED() << "Could not add row to DB";
984         return;
985       }
986 
987       changed_urls.push_back(*i);
988       changed_urls.back().set_id(url_id);  // i->id_ is likely 0.
989     }
990 
991     // Sync code manages the visits itself.
992     if (visit_source != SOURCE_SYNCED) {
993       // Make up a visit to correspond to the last visit to the page.
994       VisitRow visit_info(
995           url_id, i->last_visit(), /*referring_visit=*/0,
996           ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
997                                     ui::PAGE_TRANSITION_CHAIN_START |
998                                     ui::PAGE_TRANSITION_CHAIN_END),
999           /*segment_id=*/0, /*incremented_omnibox_typed_score=*/false,
1000           /*publicly_routable=*/false);
1001       if (!db_->AddVisit(&visit_info, visit_source)) {
1002         NOTREACHED() << "Adding visit failed.";
1003         return;
1004       }
1005 
1006       if (visit_info.visit_time < first_recorded_time_)
1007         first_recorded_time_ = visit_info.visit_time;
1008     }
1009   }
1010 
1011   // Broadcast a notification for typed URLs that have been modified. This
1012   // will be picked up by the in-memory URL database on the main thread.
1013   //
1014   // TODO(brettw) bug 1140015: Add an "add page" notification so the history
1015   // views can keep in sync.
1016   // HistoryService::AddPagesWithDetails() is only called from sync.
1017   NotifyURLsModified(changed_urls, UrlsModifiedReason::kSync);
1018   ScheduleCommit();
1019 }
1020 
SetTypedURLSyncBridgeForTest(std::unique_ptr<TypedURLSyncBridge> bridge)1021 void HistoryBackend::SetTypedURLSyncBridgeForTest(
1022     std::unique_ptr<TypedURLSyncBridge> bridge) {
1023   typed_url_sync_bridge_ = std::move(bridge);
1024 }
1025 
IsExpiredVisitTime(const base::Time & time)1026 bool HistoryBackend::IsExpiredVisitTime(const base::Time& time) {
1027   return time < expirer_.GetCurrentExpirationTime();
1028 }
1029 
SetPageTitle(const GURL & url,const base::string16 & title)1030 void HistoryBackend::SetPageTitle(const GURL& url,
1031                                   const base::string16& title) {
1032   TRACE_EVENT0("browser", "HistoryBackend::SetPageTitle");
1033 
1034   if (!db_)
1035     return;
1036 
1037   // Search for recent redirects which should get the same title. We make a
1038   // dummy list containing the exact URL visited if there are no redirects so
1039   // the processing below can be the same.
1040   RedirectList dummy_list;
1041   RedirectList* redirects;
1042   auto iter = recent_redirects_.Get(url);
1043   if (iter != recent_redirects_.end()) {
1044     redirects = &iter->second;
1045 
1046     // This redirect chain should have the destination URL as the last item.
1047     DCHECK(!redirects->empty());
1048     DCHECK_EQ(redirects->back(), url);
1049   } else {
1050     // No redirect chain stored, make up one containing the URL we want so we
1051     // can use the same logic below.
1052     dummy_list.push_back(url);
1053     redirects = &dummy_list;
1054   }
1055 
1056   URLRows changed_urls;
1057   for (size_t i = 0; i < redirects->size(); i++) {
1058     URLRow row;
1059     URLID row_id = db_->GetRowForURL(redirects->at(i), &row);
1060     if (row_id && row.title() != title) {
1061       row.set_title(title);
1062       db_->UpdateURLRow(row_id, row);
1063       changed_urls.push_back(row);
1064     }
1065   }
1066 
1067   // Broadcast notifications for any URLs that have changed. This will
1068   // update the in-memory database and the InMemoryURLIndex.
1069   if (!changed_urls.empty()) {
1070     NotifyURLsModified(changed_urls, UrlsModifiedReason::kTitleChanged);
1071     ScheduleCommit();
1072   }
1073 }
1074 
AddPageNoVisitForBookmark(const GURL & url,const base::string16 & title)1075 void HistoryBackend::AddPageNoVisitForBookmark(const GURL& url,
1076                                                const base::string16& title) {
1077   TRACE_EVENT0("browser", "HistoryBackend::AddPageNoVisitForBookmark");
1078 
1079   if (!db_)
1080     return;
1081 
1082   URLRow url_info(url);
1083   URLID url_id = db_->GetRowForURL(url, &url_info);
1084   if (url_id) {
1085     // URL is already known, nothing to do.
1086     return;
1087   }
1088 
1089   if (!title.empty()) {
1090     url_info.set_title(title);
1091   } else {
1092     url_info.set_title(base::UTF8ToUTF16(url.spec()));
1093   }
1094 
1095   url_info.set_last_visit(Time::Now());
1096   // Mark the page hidden. If the user types it in, it'll unhide.
1097   url_info.set_hidden(true);
1098 
1099   db_->AddURL(url_info);
1100 }
1101 
GetAllTypedURLs(URLRows * urls)1102 bool HistoryBackend::GetAllTypedURLs(URLRows* urls) {
1103   DCHECK(urls);
1104   if (!db_)
1105     return false;
1106   std::vector<URLID> url_ids;
1107   if (!db_->GetAllURLIDsForTransition(ui::PAGE_TRANSITION_TYPED, &url_ids))
1108     return false;
1109   urls->reserve(url_ids.size());
1110   for (const auto& url_id : url_ids) {
1111     URLRow url;
1112     if (!db_->GetURLRow(url_id, &url))
1113       return false;
1114     urls->push_back(url);
1115   }
1116   return true;
1117 }
1118 
GetVisitsForURL(URLID id,VisitVector * visits)1119 bool HistoryBackend::GetVisitsForURL(URLID id, VisitVector* visits) {
1120   if (db_)
1121     return db_->GetVisitsForURL(id, visits);
1122   return false;
1123 }
1124 
GetMostRecentVisitsForURL(URLID id,int max_visits,VisitVector * visits)1125 bool HistoryBackend::GetMostRecentVisitsForURL(URLID id,
1126                                                int max_visits,
1127                                                VisitVector* visits) {
1128   if (db_)
1129     return db_->GetMostRecentVisitsForURL(id, max_visits, visits);
1130   return false;
1131 }
1132 
UpdateURLs(const URLRows & urls)1133 size_t HistoryBackend::UpdateURLs(const URLRows& urls) {
1134   if (!db_)
1135     return 0;
1136 
1137   URLRows changed_urls;
1138   for (auto it = urls.begin(); it != urls.end(); ++it) {
1139     DCHECK(it->id());
1140     if (db_->UpdateURLRow(it->id(), *it))
1141       changed_urls.push_back(*it);
1142   }
1143 
1144   // Broadcast notifications for any URLs that have actually been changed. This
1145   // will update the in-memory database and the InMemoryURLIndex.
1146   size_t num_updated_records = changed_urls.size();
1147   if (num_updated_records) {
1148     // HistoryService::UpdateURLs() is only called from sync.
1149     NotifyURLsModified(changed_urls, UrlsModifiedReason::kSync);
1150     ScheduleCommit();
1151   }
1152   return num_updated_records;
1153 }
1154 
AddVisits(const GURL & url,const std::vector<VisitInfo> & visits,VisitSource visit_source)1155 bool HistoryBackend::AddVisits(const GURL& url,
1156                                const std::vector<VisitInfo>& visits,
1157                                VisitSource visit_source) {
1158   if (db_) {
1159     for (auto visit = visits.begin(); visit != visits.end(); ++visit) {
1160       if (!AddPageVisit(url, visit->first, 0, visit->second,
1161                         !ui::PageTransitionIsMainFrame(visit->second),
1162                         visit_source, IsTypedIncrement(visit->second),
1163                         /*publicly_routable=*/false)
1164                .first) {
1165         return false;
1166       }
1167     }
1168     ScheduleCommit();
1169     return true;
1170   }
1171   return false;
1172 }
1173 
RemoveVisits(const VisitVector & visits)1174 bool HistoryBackend::RemoveVisits(const VisitVector& visits) {
1175   if (!db_)
1176     return false;
1177 
1178   expirer_.ExpireVisits(visits);
1179   ScheduleCommit();
1180   return true;
1181 }
1182 
GetVisitsSource(const VisitVector & visits,VisitSourceMap * sources)1183 bool HistoryBackend::GetVisitsSource(const VisitVector& visits,
1184                                      VisitSourceMap* sources) {
1185   if (!db_)
1186     return false;
1187 
1188   db_->GetVisitsSource(visits, sources);
1189   return true;
1190 }
1191 
GetURL(const GURL & url,URLRow * url_row)1192 bool HistoryBackend::GetURL(const GURL& url, URLRow* url_row) {
1193   if (db_)
1194     return db_->GetRowForURL(url, url_row) != 0;
1195   return false;
1196 }
1197 
GetURLByID(URLID url_id,URLRow * url_row)1198 bool HistoryBackend::GetURLByID(URLID url_id, URLRow* url_row) {
1199   if (db_)
1200     return db_->GetURLRow(url_id, url_row);
1201   return false;
1202 }
1203 
QueryURL(const GURL & url,bool want_visits)1204 QueryURLResult HistoryBackend::QueryURL(const GURL& url, bool want_visits) {
1205   QueryURLResult result;
1206   result.success = db_ && db_->GetRowForURL(url, &result.row);
1207   // Optionally query the visits.
1208   if (result.success && want_visits)
1209     db_->GetVisitsForURL(result.row.id(), &result.visits);
1210   return result;
1211 }
1212 
1213 base::WeakPtr<syncer::ModelTypeControllerDelegate>
GetTypedURLSyncControllerDelegate()1214 HistoryBackend::GetTypedURLSyncControllerDelegate() {
1215   DCHECK(typed_url_sync_bridge_);
1216   return typed_url_sync_bridge_->change_processor()->GetControllerDelegate();
1217 }
1218 
1219 // Statistics ------------------------------------------------------------------
1220 
GetHistoryCount(const Time & begin_time,const Time & end_time)1221 HistoryCountResult HistoryBackend::GetHistoryCount(const Time& begin_time,
1222                                                    const Time& end_time) {
1223   int count = 0;
1224   return {db_ && db_->GetHistoryCount(begin_time, end_time, &count), count};
1225 }
1226 
CountUniqueHostsVisitedLastMonth()1227 HistoryCountResult HistoryBackend::CountUniqueHostsVisitedLastMonth() {
1228   return {!!db_, db_ ? db_->CountUniqueHostsVisitedLastMonth() : 0};
1229 }
1230 
GetDomainDiversity(base::Time report_time,int number_of_days_to_report,DomainMetricBitmaskType metric_type_bitmask)1231 DomainDiversityResults HistoryBackend::GetDomainDiversity(
1232     base::Time report_time,
1233     int number_of_days_to_report,
1234     DomainMetricBitmaskType metric_type_bitmask) {
1235   DCHECK_GE(number_of_days_to_report, 0);
1236   DomainDiversityResults result;
1237 
1238   if (!db_)
1239     return result;
1240 
1241   number_of_days_to_report =
1242       std::min(number_of_days_to_report, kDomainDiversityMaxBacktrackedDays);
1243 
1244   base::Time current_midnight = report_time.LocalMidnight();
1245   SCOPED_UMA_HISTOGRAM_TIMER("History.DomainCountQueryTime");
1246 
1247   for (int days_back = 0; days_back < number_of_days_to_report; ++days_back) {
1248     DomainMetricSet single_metric_set;
1249     single_metric_set.end_time = current_midnight;
1250 
1251     if (metric_type_bitmask & kEnableLast1DayMetric) {
1252       base::Time last_midnight = MidnightNDaysLater(current_midnight, -1);
1253       single_metric_set.one_day_metric = DomainMetricCountType(
1254           db_->CountUniqueDomainsVisited(last_midnight, current_midnight),
1255           last_midnight);
1256     }
1257 
1258     if (metric_type_bitmask & kEnableLast7DayMetric) {
1259       base::Time seven_midnights_ago = MidnightNDaysLater(current_midnight, -7);
1260       single_metric_set.seven_day_metric = DomainMetricCountType(
1261           db_->CountUniqueDomainsVisited(seven_midnights_ago, current_midnight),
1262           seven_midnights_ago);
1263     }
1264 
1265     if (metric_type_bitmask & kEnableLast28DayMetric) {
1266       base::Time twenty_eight_midnights_ago =
1267           MidnightNDaysLater(current_midnight, -28);
1268       single_metric_set.twenty_eight_day_metric = DomainMetricCountType(
1269           db_->CountUniqueDomainsVisited(twenty_eight_midnights_ago,
1270                                          current_midnight),
1271           twenty_eight_midnights_ago);
1272     }
1273     result.push_back(single_metric_set);
1274 
1275     current_midnight = MidnightNDaysLater(current_midnight, -1);
1276   }
1277 
1278   return result;
1279 }
1280 
GetLastVisitToHost(const GURL & host,base::Time begin_time,base::Time end_time)1281 HistoryLastVisitToHostResult HistoryBackend::GetLastVisitToHost(
1282     const GURL& host,
1283     base::Time begin_time,
1284     base::Time end_time) {
1285   base::Time last_visit;
1286   return {
1287       db_ && db_->GetLastVisitToHost(host, begin_time, end_time, &last_visit),
1288       last_visit};
1289 }
1290 
1291 // Keyword visits --------------------------------------------------------------
1292 
SetKeywordSearchTermsForURL(const GURL & url,KeywordID keyword_id,const base::string16 & term)1293 void HistoryBackend::SetKeywordSearchTermsForURL(const GURL& url,
1294                                                  KeywordID keyword_id,
1295                                                  const base::string16& term) {
1296   TRACE_EVENT0("browser", "HistoryBackend::SetKeywordSearchTermsForURL");
1297 
1298   if (!db_)
1299     return;
1300 
1301   // Get the ID for this URL.
1302   URLRow row;
1303   if (!db_->GetRowForURL(url, &row)) {
1304     // There is a small possibility the url was deleted before the keyword
1305     // was added. Ignore the request.
1306     return;
1307   }
1308 
1309   db_->SetKeywordSearchTermsForURL(row.id(), keyword_id, term);
1310   delegate_->NotifyKeywordSearchTermUpdated(row, keyword_id, term);
1311 
1312   ScheduleCommit();
1313 }
1314 
DeleteAllSearchTermsForKeyword(KeywordID keyword_id)1315 void HistoryBackend::DeleteAllSearchTermsForKeyword(KeywordID keyword_id) {
1316   TRACE_EVENT0("browser", "HistoryBackend::DeleteAllSearchTermsForKeyword");
1317 
1318   if (!db_)
1319     return;
1320 
1321   db_->DeleteAllSearchTermsForKeyword(keyword_id);
1322   ScheduleCommit();
1323 }
1324 
DeleteKeywordSearchTermForURL(const GURL & url)1325 void HistoryBackend::DeleteKeywordSearchTermForURL(const GURL& url) {
1326   TRACE_EVENT0("browser", "HistoryBackend::DeleteKeywordSearchTermForURL");
1327 
1328   if (!db_)
1329     return;
1330 
1331   URLID url_id = db_->GetRowForURL(url, nullptr);
1332   if (!url_id)
1333     return;
1334   db_->DeleteKeywordSearchTermForURL(url_id);
1335   delegate_->NotifyKeywordSearchTermDeleted(url_id);
1336 
1337   ScheduleCommit();
1338 }
1339 
DeleteMatchingURLsForKeyword(KeywordID keyword_id,const base::string16 & term)1340 void HistoryBackend::DeleteMatchingURLsForKeyword(KeywordID keyword_id,
1341                                                   const base::string16& term) {
1342   TRACE_EVENT0("browser", "HistoryBackend::DeleteMatchingURLsForKeyword");
1343 
1344   if (!db_)
1345     return;
1346 
1347   std::vector<KeywordSearchTermRow> rows;
1348   if (db_->GetKeywordSearchTermRows(term, &rows)) {
1349     std::vector<GURL> items_to_delete;
1350     URLRow row;
1351     for (auto it = rows.begin(); it != rows.end(); ++it) {
1352       if ((it->keyword_id == keyword_id) && db_->GetURLRow(it->url_id, &row))
1353         items_to_delete.push_back(row.url());
1354     }
1355     DeleteURLs(items_to_delete);
1356   }
1357 }
1358 
1359 // Observers -------------------------------------------------------------------
1360 
AddObserver(HistoryBackendObserver * observer)1361 void HistoryBackend::AddObserver(HistoryBackendObserver* observer) {
1362   observers_.AddObserver(observer);
1363 }
1364 
RemoveObserver(HistoryBackendObserver * observer)1365 void HistoryBackend::RemoveObserver(HistoryBackendObserver* observer) {
1366   observers_.RemoveObserver(observer);
1367 }
1368 
1369 // Downloads -------------------------------------------------------------------
1370 
GetNextDownloadId()1371 uint32_t HistoryBackend::GetNextDownloadId() {
1372   return db_ ? db_->GetNextDownloadId() : kInvalidDownloadId;
1373 }
1374 
1375 // Get all the download entries from the database.
QueryDownloads()1376 std::vector<DownloadRow> HistoryBackend::QueryDownloads() {
1377   std::vector<DownloadRow> rows;
1378   if (db_)
1379     db_->QueryDownloads(&rows);
1380   return rows;
1381 }
1382 
1383 // Update a particular download entry.
UpdateDownload(const DownloadRow & data,bool should_commit_immediately)1384 void HistoryBackend::UpdateDownload(const DownloadRow& data,
1385                                     bool should_commit_immediately) {
1386   TRACE_EVENT0("browser", "HistoryBackend::UpdateDownload");
1387   if (!db_)
1388     return;
1389   db_->UpdateDownload(data);
1390   if (should_commit_immediately)
1391     Commit();
1392   else
1393     ScheduleCommit();
1394 }
1395 
CreateDownload(const DownloadRow & history_info)1396 bool HistoryBackend::CreateDownload(const DownloadRow& history_info) {
1397   TRACE_EVENT0("browser", "HistoryBackend::CreateDownload");
1398   if (!db_)
1399     return false;
1400   bool success = db_->CreateDownload(history_info);
1401 #if defined(OS_ANDROID)
1402   // On android, browser process can get easily killed. Download will no longer
1403   // be able to resume and the temporary file will linger forever if the
1404   // download is not committed before that. Do the commit right away to avoid
1405   // uncommitted download entry if browser is killed.
1406   Commit();
1407 #else
1408   ScheduleCommit();
1409 #endif
1410   return success;
1411 }
1412 
RemoveDownloads(const std::set<uint32_t> & ids)1413 void HistoryBackend::RemoveDownloads(const std::set<uint32_t>& ids) {
1414   TRACE_EVENT0("browser", "HistoryBackend::RemoveDownloads");
1415   if (!db_)
1416     return;
1417   size_t downloads_count_before = db_->CountDownloads();
1418   // HistoryBackend uses a long-running Transaction that is committed
1419   // periodically, so this loop doesn't actually hit the disk too hard.
1420   for (auto it = ids.begin(); it != ids.end(); ++it) {
1421     db_->RemoveDownload(*it);
1422   }
1423   ScheduleCommit();
1424   size_t downloads_count_after = db_->CountDownloads();
1425 
1426   DCHECK_LE(downloads_count_after, downloads_count_before);
1427   if (downloads_count_after > downloads_count_before)
1428     return;
1429   size_t num_downloads_deleted = downloads_count_before - downloads_count_after;
1430   DCHECK_GE(ids.size(), num_downloads_deleted);
1431 }
1432 
QueryHistory(const base::string16 & text_query,const QueryOptions & options)1433 QueryResults HistoryBackend::QueryHistory(const base::string16& text_query,
1434                                           const QueryOptions& options) {
1435   QueryResults query_results;
1436   base::TimeTicks beginning_time = base::TimeTicks::Now();
1437   if (db_) {
1438     if (text_query.empty()) {
1439       // Basic history query for the main database.
1440       QueryHistoryBasic(options, &query_results);
1441     } else {
1442       // Text history query.
1443       QueryHistoryText(text_query, options, &query_results);
1444     }
1445   }
1446   UMA_HISTOGRAM_TIMES("History.QueryHistory",
1447                       TimeTicks::Now() - beginning_time);
1448   return query_results;
1449 }
1450 
1451 // Basic time-based querying of history.
QueryHistoryBasic(const QueryOptions & options,QueryResults * result)1452 void HistoryBackend::QueryHistoryBasic(const QueryOptions& options,
1453                                        QueryResults* result) {
1454   // First get all visits.
1455   VisitVector visits;
1456   bool has_more_results = db_->GetVisibleVisitsInRange(options, &visits);
1457   DCHECK_LE(static_cast<int>(visits.size()), options.EffectiveMaxCount());
1458 
1459   // Now add them and the URL rows to the results.
1460   std::vector<URLResult> matching_results;
1461   URLResult url_result;
1462   for (size_t i = 0; i < visits.size(); i++) {
1463     const VisitRow visit = visits[i];
1464 
1465     // Add a result row for this visit, get the URL info from the DB.
1466     if (!db_->GetURLRow(visit.url_id, &url_result)) {
1467       DVLOG(0) << "Failed to get id " << visit.url_id << " from history.urls.";
1468       continue;  // DB out of sync and URL doesn't exist, try to recover.
1469     }
1470 
1471     if (!url_result.url().is_valid()) {
1472       DVLOG(0) << "Got invalid URL from history.urls with id " << visit.url_id
1473                << ":  " << url_result.url().possibly_invalid_spec();
1474       continue;  // Don't report invalid URLs in case of corruption.
1475     }
1476 
1477     url_result.set_visit_time(visit.visit_time);
1478     url_result.set_publicly_routable(visit.publicly_routable);
1479 
1480     // Set whether the visit was blocked for a managed user by looking at the
1481     // transition type.
1482     url_result.set_blocked_visit(
1483         (visit.transition & ui::PAGE_TRANSITION_BLOCKED) != 0);
1484 
1485     // We don't set any of the query-specific parts of the URLResult, since
1486     // snippets and stuff don't apply to basic querying.
1487     matching_results.push_back(std::move(url_result));
1488   }
1489   result->SetURLResults(std::move(matching_results));
1490 
1491   if (!has_more_results && options.begin_time <= first_recorded_time_)
1492     result->set_reached_beginning(true);
1493 }
1494 
1495 // Text-based querying of history.
QueryHistoryText(const base::string16 & text_query,const QueryOptions & options,QueryResults * result)1496 void HistoryBackend::QueryHistoryText(const base::string16& text_query,
1497                                       const QueryOptions& options,
1498                                       QueryResults* result) {
1499   URLRows text_matches;
1500   db_->GetTextMatchesWithAlgorithm(text_query, options.matching_algorithm,
1501                                    &text_matches);
1502 
1503   std::vector<URLResult> matching_visits;
1504   VisitVector visits;  // Declare outside loop to prevent re-construction.
1505   for (size_t i = 0; i < text_matches.size(); i++) {
1506     const URLRow& text_match = text_matches[i];
1507     // Get all visits for given URL match.
1508     db_->GetVisibleVisitsForURL(text_match.id(), options, &visits);
1509     for (size_t j = 0; j < visits.size(); j++) {
1510       URLResult url_result(text_match);
1511       url_result.set_visit_time(visits[j].visit_time);
1512       url_result.set_publicly_routable(visits[j].publicly_routable);
1513       matching_visits.push_back(url_result);
1514     }
1515   }
1516 
1517   std::sort(matching_visits.begin(), matching_visits.end(),
1518             URLResult::CompareVisitTime);
1519 
1520   size_t max_results = options.max_count == 0
1521                            ? std::numeric_limits<size_t>::max()
1522                            : static_cast<int>(options.max_count);
1523   bool has_more_results = false;
1524   if (matching_visits.size() > max_results) {
1525     has_more_results = true;
1526     matching_visits.resize(max_results);
1527   }
1528   result->SetURLResults(std::move(matching_visits));
1529 
1530   if (!has_more_results && options.begin_time <= first_recorded_time_)
1531     result->set_reached_beginning(true);
1532 }
1533 
QueryRedirectsFrom(const GURL & from_url)1534 RedirectList HistoryBackend::QueryRedirectsFrom(const GURL& from_url) {
1535   if (!db_)
1536     return {};
1537 
1538   URLID from_url_id = db_->GetRowForURL(from_url, nullptr);
1539   VisitID cur_visit = db_->GetMostRecentVisitForURL(from_url_id, nullptr);
1540   if (!cur_visit)
1541     return {};  // No visits for URL.
1542 
1543   RedirectList redirects;
1544   GetRedirectsFromSpecificVisit(cur_visit, &redirects);
1545   return redirects;
1546 }
1547 
QueryRedirectsTo(const GURL & to_url)1548 RedirectList HistoryBackend::QueryRedirectsTo(const GURL& to_url) {
1549   if (!db_)
1550     return {};
1551 
1552   URLID to_url_id = db_->GetRowForURL(to_url, nullptr);
1553   VisitID cur_visit = db_->GetMostRecentVisitForURL(to_url_id, nullptr);
1554   if (!cur_visit)
1555     return {};  // No visits for URL.
1556 
1557   RedirectList redirects;
1558   GetRedirectsToSpecificVisit(cur_visit, &redirects);
1559   return redirects;
1560 }
1561 
GetVisibleVisitCountToHost(const GURL & url)1562 VisibleVisitCountToHostResult HistoryBackend::GetVisibleVisitCountToHost(
1563     const GURL& url) {
1564   VisibleVisitCountToHostResult result;
1565   result.success = db_ && db_->GetVisibleVisitCountToHost(url, &result.count,
1566                                                           &result.first_visit);
1567   return result;
1568 }
1569 
QueryMostVisitedURLs(int result_count,int days_back)1570 MostVisitedURLList HistoryBackend::QueryMostVisitedURLs(int result_count,
1571                                                         int days_back) {
1572   if (!db_)
1573     return {};
1574 
1575   base::TimeTicks begin_time = base::TimeTicks::Now();
1576 
1577   auto url_filter =
1578       backend_client_
1579           ? base::BindRepeating(&HistoryBackendClient::IsWebSafe,
1580                                 base::Unretained(backend_client_.get()))
1581           : base::NullCallback();
1582   std::vector<std::unique_ptr<PageUsageData>> data = db_->QuerySegmentUsage(
1583       base::Time::Now() - base::TimeDelta::FromDays(days_back), result_count,
1584       url_filter);
1585 
1586   MostVisitedURLList result;
1587   for (const std::unique_ptr<PageUsageData>& current_data : data)
1588     result.emplace_back(current_data->GetURL(), current_data->GetTitle());
1589 
1590   UMA_HISTOGRAM_TIMES("History.QueryMostVisitedURLsTime",
1591                       base::TimeTicks::Now() - begin_time);
1592 
1593   return result;
1594 }
1595 
GetRedirectsFromSpecificVisit(VisitID cur_visit,RedirectList * redirects)1596 void HistoryBackend::GetRedirectsFromSpecificVisit(VisitID cur_visit,
1597                                                    RedirectList* redirects) {
1598   // Follow any redirects from the given visit and add them to the list.
1599   // It *should* be impossible to get a circular chain here, but we check
1600   // just in case to avoid infinite loops.
1601   GURL cur_url;
1602   std::set<VisitID> visit_set;
1603   visit_set.insert(cur_visit);
1604   while (db_->GetRedirectFromVisit(cur_visit, &cur_visit, &cur_url)) {
1605     if (visit_set.find(cur_visit) != visit_set.end()) {
1606       NOTREACHED() << "Loop in visit chain, giving up";
1607       return;
1608     }
1609     visit_set.insert(cur_visit);
1610     redirects->push_back(cur_url);
1611   }
1612 }
1613 
GetRedirectsToSpecificVisit(VisitID cur_visit,RedirectList * redirects)1614 void HistoryBackend::GetRedirectsToSpecificVisit(VisitID cur_visit,
1615                                                  RedirectList* redirects) {
1616   // Follow redirects going to cur_visit. These are added to |redirects| in
1617   // the order they are found. If a redirect chain looks like A -> B -> C and
1618   // |cur_visit| = C, redirects will be {B, A} in that order.
1619   if (!db_)
1620     return;
1621 
1622   GURL cur_url;
1623   std::set<VisitID> visit_set;
1624   visit_set.insert(cur_visit);
1625   while (db_->GetRedirectToVisit(cur_visit, &cur_visit, &cur_url)) {
1626     if (visit_set.find(cur_visit) != visit_set.end()) {
1627       NOTREACHED() << "Loop in visit chain, giving up";
1628       return;
1629     }
1630     visit_set.insert(cur_visit);
1631     redirects->push_back(cur_url);
1632   }
1633 }
1634 
ScheduleAutocomplete(base::OnceCallback<void (HistoryBackend *,URLDatabase *)> callback)1635 void HistoryBackend::ScheduleAutocomplete(
1636     base::OnceCallback<void(HistoryBackend*, URLDatabase*)> callback) {
1637   std::move(callback).Run(this, db_.get());
1638 }
1639 
DeleteFTSIndexDatabases()1640 void HistoryBackend::DeleteFTSIndexDatabases() {
1641   // Find files on disk matching the text databases file pattern so we can
1642   // quickly test for and delete them.
1643   base::FilePath::StringType filepattern = FILE_PATH_LITERAL("History Index *");
1644   base::FileEnumerator enumerator(history_dir_, false,
1645                                   base::FileEnumerator::FILES, filepattern);
1646   int num_databases_deleted = 0;
1647   base::FilePath current_file;
1648   while (!(current_file = enumerator.Next()).empty()) {
1649     if (sql::Database::Delete(current_file))
1650       num_databases_deleted++;
1651   }
1652   UMA_HISTOGRAM_COUNTS_1M("History.DeleteFTSIndexDatabases",
1653                           num_databases_deleted);
1654 }
1655 
GetFavicon(const GURL & icon_url,favicon_base::IconType icon_type,const std::vector<int> & desired_sizes)1656 std::vector<favicon_base::FaviconRawBitmapResult> HistoryBackend::GetFavicon(
1657     const GURL& icon_url,
1658     favicon_base::IconType icon_type,
1659     const std::vector<int>& desired_sizes) {
1660   return UpdateFaviconMappingsAndFetch({}, icon_url, icon_type, desired_sizes);
1661 }
1662 
GetLargestFaviconForURL(const GURL & page_url,const std::vector<favicon_base::IconTypeSet> & icon_types_list,int minimum_size_in_pixels)1663 favicon_base::FaviconRawBitmapResult HistoryBackend::GetLargestFaviconForURL(
1664     const GURL& page_url,
1665     const std::vector<favicon_base::IconTypeSet>& icon_types_list,
1666     int minimum_size_in_pixels) {
1667   if (!db_ || !favicon_backend_)
1668     return {};
1669 
1670   return favicon_backend_->GetLargestFaviconForUrl(page_url, icon_types_list,
1671                                                    minimum_size_in_pixels);
1672 }
1673 
1674 std::vector<favicon_base::FaviconRawBitmapResult>
GetFaviconsForURL(const GURL & page_url,const favicon_base::IconTypeSet & icon_types,const std::vector<int> & desired_sizes,bool fallback_to_host)1675 HistoryBackend::GetFaviconsForURL(const GURL& page_url,
1676                                   const favicon_base::IconTypeSet& icon_types,
1677                                   const std::vector<int>& desired_sizes,
1678                                   bool fallback_to_host) {
1679   if (!favicon_backend_)
1680     return {};
1681   return favicon_backend_->GetFaviconsForUrl(page_url, icon_types,
1682                                              desired_sizes, fallback_to_host);
1683 }
1684 
1685 std::vector<favicon_base::FaviconRawBitmapResult>
GetFaviconForID(favicon_base::FaviconID favicon_id,int desired_size)1686 HistoryBackend::GetFaviconForID(favicon_base::FaviconID favicon_id,
1687                                 int desired_size) {
1688   if (!favicon_backend_)
1689     return {};
1690   return favicon_backend_->GetFaviconForId(favicon_id, desired_size);
1691 }
1692 
1693 std::vector<favicon_base::FaviconRawBitmapResult>
UpdateFaviconMappingsAndFetch(const base::flat_set<GURL> & page_urls,const GURL & icon_url,favicon_base::IconType icon_type,const std::vector<int> & desired_sizes)1694 HistoryBackend::UpdateFaviconMappingsAndFetch(
1695     const base::flat_set<GURL>& page_urls,
1696     const GURL& icon_url,
1697     favicon_base::IconType icon_type,
1698     const std::vector<int>& desired_sizes) {
1699   if (!favicon_backend_)
1700     return {};
1701   auto result = favicon_backend_->UpdateFaviconMappingsAndFetch(
1702       page_urls, icon_url, icon_type, desired_sizes);
1703   if (!result.updated_page_urls.empty()) {
1704     for (auto& page_url : result.updated_page_urls)
1705       SendFaviconChangedNotificationForPageAndRedirects(page_url);
1706     ScheduleCommit();
1707   }
1708   return result.bitmap_results;
1709 }
1710 
DeleteFaviconMappings(const base::flat_set<GURL> & page_urls,favicon_base::IconType icon_type)1711 void HistoryBackend::DeleteFaviconMappings(
1712     const base::flat_set<GURL>& page_urls,
1713     favicon_base::IconType icon_type) {
1714   if (!favicon_backend_ || !db_)
1715     return;
1716 
1717   auto deleted_page_urls =
1718       favicon_backend_->DeleteFaviconMappings(page_urls, icon_type);
1719   for (auto& deleted_page_url : deleted_page_urls)
1720     SendFaviconChangedNotificationForPageAndRedirects(deleted_page_url);
1721   if (!deleted_page_urls.empty())
1722     ScheduleCommit();
1723 }
1724 
MergeFavicon(const GURL & page_url,const GURL & icon_url,favicon_base::IconType icon_type,scoped_refptr<base::RefCountedMemory> bitmap_data,const gfx::Size & pixel_size)1725 void HistoryBackend::MergeFavicon(
1726     const GURL& page_url,
1727     const GURL& icon_url,
1728     favicon_base::IconType icon_type,
1729     scoped_refptr<base::RefCountedMemory> bitmap_data,
1730     const gfx::Size& pixel_size) {
1731   if (!favicon_backend_ || !db_)
1732     return;
1733 
1734   favicon::MergeFaviconResult result = favicon_backend_->MergeFavicon(
1735       page_url, icon_url, icon_type, bitmap_data, pixel_size);
1736   if (result.did_page_to_icon_mapping_change)
1737     SendFaviconChangedNotificationForPageAndRedirects(page_url);
1738   if (result.did_icon_change)
1739     SendFaviconChangedNotificationForIconURL(icon_url);
1740   ScheduleCommit();
1741 }
1742 
SetFavicons(const base::flat_set<GURL> & page_urls,favicon_base::IconType icon_type,const GURL & icon_url,const std::vector<SkBitmap> & bitmaps)1743 void HistoryBackend::SetFavicons(const base::flat_set<GURL>& page_urls,
1744                                  favicon_base::IconType icon_type,
1745                                  const GURL& icon_url,
1746                                  const std::vector<SkBitmap>& bitmaps) {
1747   if (!favicon_backend_)
1748     return;
1749 
1750   ProcessSetFaviconsResult(
1751       favicon_backend_->SetFavicons(page_urls, icon_type, icon_url, bitmaps,
1752                                     FaviconBitmapType::ON_VISIT),
1753       icon_url);
1754 }
1755 
CloneFaviconMappingsForPages(const GURL & page_url_to_read,const favicon_base::IconTypeSet & icon_types,const base::flat_set<GURL> & page_urls_to_write)1756 void HistoryBackend::CloneFaviconMappingsForPages(
1757     const GURL& page_url_to_read,
1758     const favicon_base::IconTypeSet& icon_types,
1759     const base::flat_set<GURL>& page_urls_to_write) {
1760   TRACE_EVENT0("browser", "HistoryBackend::CloneFaviconMappingsForPages");
1761 
1762   if (!db_ || !favicon_backend_)
1763     return;
1764 
1765   std::set<GURL> changed_urls = favicon_backend_->CloneFaviconMappingsForPages(
1766       page_url_to_read, icon_types, page_urls_to_write);
1767   if (changed_urls.empty())
1768     return;
1769 
1770   ScheduleCommit();
1771   NotifyFaviconsChanged(changed_urls, GURL());
1772 }
1773 
CanSetOnDemandFavicons(const GURL & page_url,favicon_base::IconType icon_type)1774 bool HistoryBackend::CanSetOnDemandFavicons(const GURL& page_url,
1775                                             favicon_base::IconType icon_type) {
1776   return favicon_backend_ && db_ &&
1777          favicon_backend_->CanSetOnDemandFavicons(page_url, icon_type);
1778 }
1779 
SetOnDemandFavicons(const GURL & page_url,favicon_base::IconType icon_type,const GURL & icon_url,const std::vector<SkBitmap> & bitmaps)1780 bool HistoryBackend::SetOnDemandFavicons(const GURL& page_url,
1781                                          favicon_base::IconType icon_type,
1782                                          const GURL& icon_url,
1783                                          const std::vector<SkBitmap>& bitmaps) {
1784   if (!favicon_backend_ || !db_)
1785     return false;
1786 
1787   return ProcessSetFaviconsResult(favicon_backend_->SetOnDemandFavicons(
1788                                       page_url, icon_type, icon_url, bitmaps),
1789                                   icon_url);
1790 }
1791 
SetFaviconsOutOfDateForPage(const GURL & page_url)1792 void HistoryBackend::SetFaviconsOutOfDateForPage(const GURL& page_url) {
1793   if (favicon_backend_ &&
1794       favicon_backend_->SetFaviconsOutOfDateForPage(page_url)) {
1795     ScheduleCommit();
1796   }
1797 }
1798 
TouchOnDemandFavicon(const GURL & icon_url)1799 void HistoryBackend::TouchOnDemandFavicon(const GURL& icon_url) {
1800   TRACE_EVENT0("browser", "HistoryBackend::TouchOnDemandFavicon");
1801 
1802   if (!favicon_backend_)
1803     return;
1804   favicon_backend_->TouchOnDemandFavicon(icon_url);
1805   ScheduleCommit();
1806 }
1807 
SetImportedFavicons(const favicon_base::FaviconUsageDataList & favicon_usage)1808 void HistoryBackend::SetImportedFavicons(
1809     const favicon_base::FaviconUsageDataList& favicon_usage) {
1810   TRACE_EVENT0("browser", "HistoryBackend::SetImportedFavicons");
1811 
1812   if (!db_ || !favicon_backend_)
1813     return;
1814 
1815   Time now = Time::Now();
1816 
1817   // Track all URLs that had their favicons set or updated.
1818   std::set<GURL> favicons_changed;
1819 
1820   favicon::FaviconDatabase* favicon_db = favicon_backend_->db();
1821   for (size_t i = 0; i < favicon_usage.size(); i++) {
1822     favicon_base::FaviconID favicon_id = favicon_db->GetFaviconIDForFaviconURL(
1823         favicon_usage[i].favicon_url, favicon_base::IconType::kFavicon);
1824     if (!favicon_id) {
1825       // This favicon doesn't exist yet, so we create it using the given data.
1826       // TODO(pkotwicz): Pass in real pixel size.
1827       favicon_id = favicon_db->AddFavicon(
1828           favicon_usage[i].favicon_url, favicon_base::IconType::kFavicon,
1829           new base::RefCountedBytes(favicon_usage[i].png_data),
1830           FaviconBitmapType::ON_VISIT, now, gfx::Size());
1831     }
1832 
1833     // Save the mapping from all the URLs to the favicon.
1834     for (auto url = favicon_usage[i].urls.begin();
1835          url != favicon_usage[i].urls.end(); ++url) {
1836       URLRow url_row;
1837       if (!db_->GetRowForURL(*url, &url_row)) {
1838         // If the URL is present as a bookmark, add the url in history to
1839         // save the favicon mapping. This will match with what history db does
1840         // for regular bookmarked URLs with favicons - when history db is
1841         // cleaned, we keep an entry in the db with 0 visits as long as that
1842         // url is bookmarked. The same is applicable to the saved credential's
1843         // URLs.
1844         if (backend_client_ && backend_client_->IsPinnedURL(*url)) {
1845           URLRow url_info(*url);
1846           url_info.set_visit_count(0);
1847           url_info.set_typed_count(0);
1848           url_info.set_last_visit(base::Time());
1849           url_info.set_hidden(false);
1850           db_->AddURL(url_info);
1851           favicon_db->AddIconMapping(*url, favicon_id);
1852           favicons_changed.insert(*url);
1853         }
1854       } else {
1855         if (!favicon_db->GetIconMappingsForPageURL(
1856                 *url, {favicon_base::IconType::kFavicon},
1857                 /*mapping_data=*/nullptr)) {
1858           // URL is present in history, update the favicon *only* if it is not
1859           // set already.
1860           favicon_db->AddIconMapping(*url, favicon_id);
1861           favicons_changed.insert(*url);
1862         }
1863       }
1864     }
1865   }
1866 
1867   if (!favicons_changed.empty()) {
1868     // Send the notification about the changed favicon URLs.
1869     NotifyFaviconsChanged(favicons_changed, GURL());
1870   }
1871 }
1872 
GetCachedRecentRedirects(const GURL & page_url)1873 RedirectList HistoryBackend::GetCachedRecentRedirects(const GURL& page_url) {
1874   auto iter = recent_redirects_.Get(page_url);
1875   if (iter != recent_redirects_.end()) {
1876     // The redirect chain should have the destination URL as the last item.
1877     DCHECK(!iter->second.empty());
1878     DCHECK_EQ(iter->second.back(), page_url);
1879     return iter->second;
1880   }
1881   // No known redirects, construct mock redirect chain containing |page_url|.
1882   return RedirectList{page_url};
1883 }
1884 
SendFaviconChangedNotificationForPageAndRedirects(const GURL & page_url)1885 void HistoryBackend::SendFaviconChangedNotificationForPageAndRedirects(
1886     const GURL& page_url) {
1887   RedirectList redirect_list = GetCachedRecentRedirects(page_url);
1888   if (!redirect_list.empty()) {
1889     std::set<GURL> favicons_changed(redirect_list.begin(), redirect_list.end());
1890     NotifyFaviconsChanged(favicons_changed, GURL());
1891   }
1892 }
1893 
SendFaviconChangedNotificationForIconURL(const GURL & icon_url)1894 void HistoryBackend::SendFaviconChangedNotificationForIconURL(
1895     const GURL& icon_url) {
1896   NotifyFaviconsChanged(std::set<GURL>(), icon_url);
1897 }
1898 
Commit()1899 void HistoryBackend::Commit() {
1900   if (!db_)
1901     return;
1902 
1903 #if defined(OS_IOS)
1904   // Attempts to get the application running long enough to commit the database
1905   // transaction if it is currently being backgrounded.
1906   base::ios::ScopedCriticalAction scoped_critical_action(
1907       "HistoryBackend::Commit");
1908 #endif
1909 
1910   // Note that a commit may not actually have been scheduled if a caller
1911   // explicitly calls this instead of using ScheduleCommit. Likewise, we
1912   // may reset the flag written by a pending commit. But this is OK! It
1913   // will merely cause extra commits (which is kind of the idea). We
1914   // could optimize more for this case (we may get two extra commits in
1915   // some cases) but it hasn't been important yet.
1916   CancelScheduledCommit();
1917 
1918   db_->CommitTransaction();
1919   DCHECK_EQ(db_->transaction_nesting(), 0)
1920       << "Somebody left a transaction open";
1921   db_->BeginTransaction();
1922 
1923   if (favicon_backend_)
1924     favicon_backend_->Commit();
1925 }
1926 
ScheduleCommit()1927 void HistoryBackend::ScheduleCommit() {
1928   // Non-cancelled means there's an already scheduled commit. Note that
1929   // CancelableClosure starts cancelled with the default constructor.
1930   if (!scheduled_commit_.IsCancelled())
1931     return;
1932 
1933   scheduled_commit_.Reset(
1934       base::BindOnce(&HistoryBackend::Commit, base::Unretained(this)));
1935 
1936   task_runner_->PostDelayedTask(
1937       FROM_HERE, scheduled_commit_.callback(),
1938       base::TimeDelta::FromSeconds(kCommitIntervalSeconds));
1939 }
1940 
CancelScheduledCommit()1941 void HistoryBackend::CancelScheduledCommit() {
1942   scheduled_commit_.Cancel();
1943 }
1944 
ProcessDBTaskImpl()1945 void HistoryBackend::ProcessDBTaskImpl() {
1946   if (!db_) {
1947     // db went away, release all the refs.
1948     queued_history_db_tasks_.clear();
1949     return;
1950   }
1951 
1952   // Remove any canceled tasks.
1953   while (!queued_history_db_tasks_.empty()) {
1954     QueuedHistoryDBTask* task = queued_history_db_tasks_.front().get();
1955     if (!task->is_canceled())
1956       break;
1957 
1958     queued_history_db_tasks_.pop_front();
1959   }
1960   if (queued_history_db_tasks_.empty())
1961     return;
1962 
1963   // Run the first task.
1964   std::unique_ptr<QueuedHistoryDBTask> task =
1965       std::move(queued_history_db_tasks_.front());
1966   queued_history_db_tasks_.pop_front();
1967   if (task->Run(this, db_.get())) {
1968     // The task is done, notify the callback.
1969     task->DoneRun();
1970   } else {
1971     // The task wants to run some more. Schedule it at the end of the current
1972     // tasks, and process it after an invoke later.
1973     queued_history_db_tasks_.push_back(std::move(task));
1974     task_runner_->PostTask(
1975         FROM_HERE, base::BindOnce(&HistoryBackend::ProcessDBTaskImpl, this));
1976   }
1977 }
1978 
1979 ////////////////////////////////////////////////////////////////////////////////
1980 //
1981 // Generic operations
1982 //
1983 ////////////////////////////////////////////////////////////////////////////////
1984 
DeleteURLs(const std::vector<GURL> & urls)1985 void HistoryBackend::DeleteURLs(const std::vector<GURL>& urls) {
1986   TRACE_EVENT0("browser", "HistoryBackend::DeleteURLs");
1987 
1988   expirer_.DeleteURLs(urls, base::Time::Max());
1989 
1990   db_->GetStartDate(&first_recorded_time_);
1991   // Force a commit, if the user is deleting something for privacy reasons, we
1992   // want to get it on disk ASAP.
1993   Commit();
1994 }
1995 
DeleteURL(const GURL & url)1996 void HistoryBackend::DeleteURL(const GURL& url) {
1997   TRACE_EVENT0("browser", "HistoryBackend::DeleteURL");
1998 
1999   expirer_.DeleteURL(url, base::Time::Max());
2000 
2001   db_->GetStartDate(&first_recorded_time_);
2002   // Force a commit, if the user is deleting something for privacy reasons, we
2003   // want to get it on disk ASAP.
2004   Commit();
2005 }
2006 
DeleteURLsUntil(const std::vector<std::pair<GURL,base::Time>> & urls_and_timestamps)2007 void HistoryBackend::DeleteURLsUntil(
2008     const std::vector<std::pair<GURL, base::Time>>& urls_and_timestamps) {
2009   TRACE_EVENT0("browser", "HistoryBackend::DeleteURLsUntil");
2010 
2011   for (const auto& pair : urls_and_timestamps) {
2012     expirer_.DeleteURL(pair.first, pair.second);
2013   }
2014   db_->GetStartDate(&first_recorded_time_);
2015   // Force a commit, if the user is deleting something for privacy reasons, we
2016   // want to get it on disk ASAP.
2017   Commit();
2018 }
2019 
ExpireHistoryBetween(const std::set<GURL> & restrict_urls,Time begin_time,Time end_time,bool user_initiated)2020 void HistoryBackend::ExpireHistoryBetween(const std::set<GURL>& restrict_urls,
2021                                           Time begin_time,
2022                                           Time end_time,
2023                                           bool user_initiated) {
2024   if (!db_)
2025     return;
2026 
2027   if (begin_time.is_null() && (end_time.is_null() || end_time.is_max()) &&
2028       restrict_urls.empty()) {
2029     // Special case deleting all history so it can be faster and to reduce the
2030     // possibility of an information leak.
2031     DeleteAllHistory();
2032   } else {
2033     // Clearing parts of history, have the expirer do the depend
2034     expirer_.ExpireHistoryBetween(restrict_urls, begin_time, end_time,
2035                                   user_initiated);
2036 
2037     // Force a commit, if the user is deleting something for privacy reasons,
2038     // we want to get it on disk ASAP.
2039     Commit();
2040   }
2041 
2042   if (begin_time <= first_recorded_time_)
2043     db_->GetStartDate(&first_recorded_time_);
2044 }
2045 
ExpireHistoryForTimes(const std::set<base::Time> & times,base::Time begin_time,base::Time end_time)2046 void HistoryBackend::ExpireHistoryForTimes(const std::set<base::Time>& times,
2047                                            base::Time begin_time,
2048                                            base::Time end_time) {
2049   if (times.empty() || !db_)
2050     return;
2051 
2052   QueryOptions options;
2053   options.begin_time = begin_time;
2054   options.end_time = end_time;
2055   options.duplicate_policy = QueryOptions::KEEP_ALL_DUPLICATES;
2056   QueryResults results;
2057   QueryHistoryBasic(options, &results);
2058 
2059   // 1st pass: find URLs that are visited at one of |times|.
2060   std::set<GURL> urls;
2061   for (size_t i = 0; i < results.size(); ++i) {
2062     if (times.count(results[i].visit_time()) > 0)
2063       urls.insert(results[i].url());
2064   }
2065   if (urls.empty())
2066     return;
2067 
2068   // 2nd pass: collect all visit times of those URLs.
2069   std::vector<base::Time> times_to_expire;
2070   for (size_t i = 0; i < results.size(); ++i) {
2071     if (urls.count(results[i].url()))
2072       times_to_expire.push_back(results[i].visit_time());
2073   }
2074 
2075   // Put the times in reverse chronological order and remove
2076   // duplicates (for expirer_.ExpireHistoryForTimes()).
2077   std::sort(times_to_expire.begin(), times_to_expire.end(),
2078             std::greater<base::Time>());
2079   times_to_expire.erase(
2080       std::unique(times_to_expire.begin(), times_to_expire.end()),
2081       times_to_expire.end());
2082 
2083   // Expires by times and commit.
2084   DCHECK(!times_to_expire.empty());
2085   expirer_.ExpireHistoryForTimes(times_to_expire);
2086   Commit();
2087 
2088   DCHECK_GE(times_to_expire.back(), first_recorded_time_);
2089   // Update |first_recorded_time_| if we expired it.
2090   if (times_to_expire.back() == first_recorded_time_)
2091     db_->GetStartDate(&first_recorded_time_);
2092 }
2093 
ExpireHistory(const std::vector<ExpireHistoryArgs> & expire_list)2094 void HistoryBackend::ExpireHistory(
2095     const std::vector<ExpireHistoryArgs>& expire_list) {
2096   if (db_) {
2097     bool update_first_recorded_time = false;
2098 
2099     for (auto it = expire_list.begin(); it != expire_list.end(); ++it) {
2100       expirer_.ExpireHistoryBetween(it->urls, it->begin_time, it->end_time,
2101                                     true);
2102 
2103       if (it->begin_time < first_recorded_time_)
2104         update_first_recorded_time = true;
2105     }
2106     Commit();
2107 
2108     // Update |first_recorded_time_| if any deletion might have affected it.
2109     if (update_first_recorded_time)
2110       db_->GetStartDate(&first_recorded_time_);
2111   }
2112 }
2113 
ExpireHistoryBeforeForTesting(base::Time end_time)2114 void HistoryBackend::ExpireHistoryBeforeForTesting(base::Time end_time) {
2115   if (!db_)
2116     return;
2117 
2118   expirer_.ExpireHistoryBeforeForTesting(end_time);
2119 }
2120 
URLsNoLongerBookmarked(const std::set<GURL> & urls)2121 void HistoryBackend::URLsNoLongerBookmarked(const std::set<GURL>& urls) {
2122   TRACE_EVENT0("browser", "HistoryBackend::URLsNoLongerBookmarked");
2123 
2124   if (!db_)
2125     return;
2126 
2127   for (auto i = urls.begin(); i != urls.end(); ++i) {
2128     VisitVector visits;
2129     URLRow url_row;
2130     if (db_->GetRowForURL(*i, &url_row))
2131       db_->GetVisitsForURL(url_row.id(), &visits);
2132     // We need to call DeleteURL() even if the DB didn't contain this URL, so
2133     // that we can delete all associated icons in the case of deleting an
2134     // unvisited bookmarked URL.
2135     if (visits.empty())
2136       expirer_.DeleteURL(*i, base::Time::Max());
2137   }
2138 }
2139 
DatabaseErrorCallback(int error,sql::Statement * stmt)2140 void HistoryBackend::DatabaseErrorCallback(int error, sql::Statement* stmt) {
2141   if (!scheduled_kill_db_ && sql::IsErrorCatastrophic(error)) {
2142     scheduled_kill_db_ = true;
2143 
2144     db_diagnostics_ = db_->GetDiagnosticInfo(error, stmt);
2145 
2146     // Don't just do the close/delete here, as we are being called by |db| and
2147     // that seems dangerous.
2148     // TODO(https://crbug.com/854258): It is also dangerous to kill the database
2149     // by a posted task: tasks that run before KillHistoryDatabase still can try
2150     // to use the broken database. Consider protecting against other tasks using
2151     // the DB or consider changing KillHistoryDatabase() to use RazeAndClose()
2152     // (then it can be cleared immediately).
2153     task_runner_->PostTask(
2154         FROM_HERE, base::BindOnce(&HistoryBackend::KillHistoryDatabase, this));
2155   }
2156 }
2157 
KillHistoryDatabase()2158 void HistoryBackend::KillHistoryDatabase() {
2159   scheduled_kill_db_ = false;
2160   if (!db_)
2161     return;
2162 
2163   // Notify SyncBridge about storage error. It will report failure to sync
2164   // engine and stop accepting remote updates.
2165   if (typed_url_sync_bridge_)
2166     typed_url_sync_bridge_->OnDatabaseError();
2167 
2168   // Rollback transaction because Raze() cannot be called from within a
2169   // transaction.
2170   db_->RollbackTransaction();
2171   bool success = db_->Raze();
2172   UMA_HISTOGRAM_BOOLEAN("History.KillHistoryDatabaseResult", success);
2173 
2174   // Release stashed embedder object before cleaning up the databases.
2175   supports_user_data_helper_.reset();
2176 
2177   // The expirer keeps tabs on the active databases. Tell it about the
2178   // databases which will be closed.
2179   expirer_.SetDatabases(nullptr, nullptr);
2180 
2181   // Reopen a new transaction for |db_| for the sake of CloseAllDatabases().
2182   db_->BeginTransaction();
2183   CloseAllDatabases();
2184 }
2185 
GetUserData(const void * key) const2186 base::SupportsUserData::Data* HistoryBackend::GetUserData(
2187     const void* key) const {
2188   DCHECK(supports_user_data_helper_);
2189   return supports_user_data_helper_->GetUserData(key);
2190 }
2191 
SetUserData(const void * key,std::unique_ptr<base::SupportsUserData::Data> data)2192 void HistoryBackend::SetUserData(
2193     const void* key,
2194     std::unique_ptr<base::SupportsUserData::Data> data) {
2195   DCHECK(supports_user_data_helper_);
2196   supports_user_data_helper_->SetUserData(key, std::move(data));
2197 }
2198 
ProcessDBTask(std::unique_ptr<HistoryDBTask> task,scoped_refptr<base::SingleThreadTaskRunner> origin_loop,const base::CancelableTaskTracker::IsCanceledCallback & is_canceled)2199 void HistoryBackend::ProcessDBTask(
2200     std::unique_ptr<HistoryDBTask> task,
2201     scoped_refptr<base::SingleThreadTaskRunner> origin_loop,
2202     const base::CancelableTaskTracker::IsCanceledCallback& is_canceled) {
2203   TRACE_EVENT0("browser", "HistoryBackend::ProcessDBTask");
2204   bool scheduled = !queued_history_db_tasks_.empty();
2205   queued_history_db_tasks_.push_back(std::make_unique<QueuedHistoryDBTask>(
2206       std::move(task), origin_loop, is_canceled));
2207   if (!scheduled)
2208     ProcessDBTaskImpl();
2209 }
2210 
NotifyFaviconsChanged(const std::set<GURL> & page_urls,const GURL & icon_url)2211 void HistoryBackend::NotifyFaviconsChanged(const std::set<GURL>& page_urls,
2212                                            const GURL& icon_url) {
2213   delegate_->NotifyFaviconsChanged(page_urls, icon_url);
2214 }
2215 
NotifyURLVisited(ui::PageTransition transition,const URLRow & row,const RedirectList & redirects,base::Time visit_time)2216 void HistoryBackend::NotifyURLVisited(ui::PageTransition transition,
2217                                       const URLRow& row,
2218                                       const RedirectList& redirects,
2219                                       base::Time visit_time) {
2220   for (HistoryBackendObserver& observer : observers_)
2221     observer.OnURLVisited(this, transition, row, redirects, visit_time);
2222 
2223   delegate_->NotifyURLVisited(transition, row, redirects, visit_time);
2224 }
2225 
NotifyURLsModified(const URLRows & changed_urls,UrlsModifiedReason reason)2226 void HistoryBackend::NotifyURLsModified(const URLRows& changed_urls,
2227                                         UrlsModifiedReason reason) {
2228   for (HistoryBackendObserver& observer : observers_)
2229     observer.OnURLsModified(this, changed_urls,
2230                             reason == UrlsModifiedReason::kExpired);
2231 
2232   delegate_->NotifyURLsModified(changed_urls, reason);
2233 }
2234 
NotifyURLsDeleted(DeletionInfo deletion_info)2235 void HistoryBackend::NotifyURLsDeleted(DeletionInfo deletion_info) {
2236   std::set<GURL> origins;
2237   for (const history::URLRow& row : deletion_info.deleted_rows())
2238     origins.insert(row.url().GetOrigin());
2239 
2240   deletion_info.set_deleted_urls_origin_map(
2241       GetCountsAndLastVisitForOrigins(origins));
2242 
2243   for (HistoryBackendObserver& observer : observers_) {
2244     observer.OnURLsDeleted(
2245         this, deletion_info.IsAllHistory(), deletion_info.is_from_expiration(),
2246         deletion_info.deleted_rows(), deletion_info.favicon_urls());
2247   }
2248 
2249   delegate_->NotifyURLsDeleted(std::move(deletion_info));
2250 }
2251 
2252 // Deleting --------------------------------------------------------------------
2253 
DeleteAllHistory()2254 void HistoryBackend::DeleteAllHistory() {
2255   // Our approach to deleting all history is:
2256   //  1. Copy the pinned URLs and their dependencies to new tables with
2257   //     temporary names.
2258   //  2. Delete the original tables. Since tables can not share pages, we know
2259   //     that any data we don't want to keep is now in an unused page.
2260   //  3. Renaming the temporary tables to match the original.
2261   //  4. Vacuuming the database to delete the unused pages.
2262   //
2263   // Since we are likely to have very few pinned URLs and their dependencies
2264   // compared to all history, this is also much faster than just deleting from
2265   // the original tables directly.
2266 
2267   // Get the pinned URLs.
2268   std::vector<URLAndTitle> pinned_url;
2269   if (backend_client_)
2270     pinned_url = backend_client_->GetPinnedURLs();
2271 
2272   URLRows kept_url_rows;
2273   std::vector<GURL> starred_urls;
2274   for (URLAndTitle& url_and_title : pinned_url) {
2275     URLRow row;
2276     if (db_->GetRowForURL(url_and_title.url, &row)) {
2277       // Clear the last visit time so when we write these rows they are "clean."
2278       row.set_last_visit(Time());
2279       row.set_visit_count(0);
2280       row.set_typed_count(0);
2281       kept_url_rows.push_back(row);
2282     }
2283 
2284     starred_urls.push_back(std::move(url_and_title.url));
2285   }
2286 
2287   // Delete all cached favicons which are not used by the UI.
2288   if (!ClearAllFaviconHistory(starred_urls)) {
2289     LOG(ERROR) << "Favicon history could not be cleared";
2290     // We continue in this error case. If the user wants to delete their
2291     // history, we should delete as much as we can.
2292   }
2293 
2294   // ClearAllMainHistory will change the IDs of the URLs in kept_urls.
2295   // Therefore, we clear the list afterwards to make sure nobody uses this
2296   // invalid data.
2297   if (!ClearAllMainHistory(kept_url_rows))
2298     LOG(ERROR) << "Main history could not be cleared";
2299   kept_url_rows.clear();
2300 
2301   db_->GetStartDate(&first_recorded_time_);
2302 
2303   // Send out the notification that history is cleared. The in-memory database
2304   // will pick this up and clear itself.
2305   NotifyURLsDeleted(DeletionInfo::ForAllHistory());
2306 }
2307 
ClearAllFaviconHistory(const std::vector<GURL> & kept_urls)2308 bool HistoryBackend::ClearAllFaviconHistory(
2309     const std::vector<GURL>& kept_urls) {
2310   if (!favicon_backend_) {
2311     // When we have no reference to the favicon database, maybe there was an
2312     // error opening it. In this case, we just try to blow it away to try to
2313     // fix the error if it exists. This may fail, in which case either the
2314     // file doesn't exist or there's no more we can do.
2315     sql::Database::Delete(GetFaviconsFileName());
2316     return true;
2317   }
2318   if (!favicon_backend_->ClearAllExcept(kept_urls))
2319     return false;
2320 
2321 #if defined(OS_ANDROID)
2322   // TODO(michaelbai): Add the unit test once AndroidProviderBackend is
2323   // available in HistoryBackend.
2324   db_->ClearAndroidURLRows();
2325 #endif
2326   return true;
2327 }
2328 
ClearAllOnDemandFavicons()2329 void HistoryBackend::ClearAllOnDemandFavicons() {
2330   expirer_.ClearOldOnDemandFaviconsIfPossible(base::Time::Now());
2331 }
2332 
ClearAllMainHistory(const URLRows & kept_urls)2333 bool HistoryBackend::ClearAllMainHistory(const URLRows& kept_urls) {
2334   // Create the duplicate URL table. We will copy the kept URLs into this.
2335   if (!db_->CreateTemporaryURLTable())
2336     return false;
2337 
2338   // Insert the URLs into the temporary table.
2339   for (auto i = kept_urls.begin(); i != kept_urls.end(); ++i) {
2340     db_->AddTemporaryURL(*i);
2341   }
2342 
2343   // Replace the original URL table with the temporary one.
2344   if (!db_->CommitTemporaryURLTable())
2345     return false;
2346 
2347   // Delete the old tables and recreate them empty.
2348   db_->RecreateAllTablesButURL();
2349 
2350   // Vacuum to reclaim the space from the dropped tables. This must be done
2351   // when there is no transaction open, and we assume that our long-running
2352   // transaction is currently open.
2353   db_->CommitTransaction();
2354   db_->Vacuum();
2355   db_->BeginTransaction();
2356   db_->GetStartDate(&first_recorded_time_);
2357 
2358   return true;
2359 }
2360 
GetCachedRecentRedirectsForPage(const GURL & page_url)2361 std::vector<GURL> HistoryBackend::GetCachedRecentRedirectsForPage(
2362     const GURL& page_url) {
2363   return GetCachedRecentRedirects(page_url);
2364 }
2365 
ProcessSetFaviconsResult(const favicon::SetFaviconsResult & result,const GURL & icon_url)2366 bool HistoryBackend::ProcessSetFaviconsResult(
2367     const favicon::SetFaviconsResult& result,
2368     const GURL& icon_url) {
2369   if (!result.did_change_database())
2370     return false;
2371 
2372   ScheduleCommit();
2373   if (result.did_update_bitmap)
2374     SendFaviconChangedNotificationForIconURL(icon_url);
2375   for (const GURL& page_url : result.updated_page_urls)
2376     SendFaviconChangedNotificationForPageAndRedirects(page_url);
2377   return true;
2378 }
2379 
NotifyURLsModified(const URLRows & changed_urls,UrlsModifiedReason reason)2380 void HistoryBackend::Delegate::NotifyURLsModified(const URLRows& changed_urls,
2381                                                   UrlsModifiedReason reason) {
2382   NotifyURLsModified(changed_urls);
2383 }
2384 
2385 }  // namespace history
2386