1// Copyright (c) 2006-2008 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 "chrome/browser/history/visit_tracker.h"
6
7#include "base/stl_util.h"
8
9namespace history {
10
11// When the list gets longer than 'MaxItems', CleanupTransitionList will resize
12// the list down to 'ResizeTo' size. This is so we only do few block moves of
13// the data rather than constantly shuffle stuff around in the vector.
14static const size_t kMaxItemsInTransitionList = 96;
15static const size_t kResizeBigTransitionListTo = 64;
16COMPILE_ASSERT(kResizeBigTransitionListTo < kMaxItemsInTransitionList,
17               max_items_must_be_larger_than_resize_to);
18
19VisitTracker::VisitTracker() {
20}
21
22VisitTracker::~VisitTracker() {
23  STLDeleteContainerPairSecondPointers(hosts_.begin(), hosts_.end());
24}
25
26// This function is potentially slow because it may do up to two brute-force
27// searches of the transitions list. This transitions list is kept to a
28// relatively small number by CleanupTransitionList so it shouldn't be a big
29// deal. However, if this ends up being noticable for performance, we may want
30// to optimize lookup.
31VisitID VisitTracker::GetLastVisit(const void* host,
32                                   int32 page_id,
33                                   const GURL& referrer) {
34  if (referrer.is_empty() || !host)
35    return 0;
36
37  HostList::iterator i = hosts_.find(host);
38  if (i == hosts_.end())
39    return 0;  // We don't have any entries for this host.
40  TransitionList& transitions = *i->second;
41
42  // Recall that a page ID is associated with a single session history entry.
43  // In the case of automatically loaded iframes, many visits/URLs can have the
44  // same page ID.
45  //
46  // We search backwards, starting at the current page ID, for the referring
47  // URL. This won't always be correct. For example, if a render process has
48  // the same page open in two different tabs, or even in two different frames,
49  // we can get confused about which was which. We can have the renderer
50  // report more precise referrer information in the future, but this is a
51  // hard problem and doesn't affect much in terms of real-world issues.
52  //
53  // We assume that the page IDs are increasing over time, so larger IDs than
54  // the current input ID happened in the future (this will occur if the user
55  // goes back). We can ignore future transitions because if you navigate, go
56  // back, and navigate some more, we'd like to have one node with two out
57  // edges in our visit graph.
58  for (int i = static_cast<int>(transitions.size()) - 1; i >= 0; i--) {
59    if (transitions[i].page_id <= page_id && transitions[i].url == referrer) {
60      // Found it.
61      return transitions[i].visit_id;
62    }
63  }
64
65  // We can't find the referrer.
66  return 0;
67}
68
69void VisitTracker::AddVisit(const void* host,
70                            int32 page_id,
71                            const GURL& url,
72                            VisitID visit_id) {
73  TransitionList* transitions = hosts_[host];
74  if (!transitions) {
75    transitions = new TransitionList;
76    hosts_[host] = transitions;
77  }
78
79  Transition t;
80  t.url = url;
81  t.page_id = page_id;
82  t.visit_id = visit_id;
83  transitions->push_back(t);
84
85  CleanupTransitionList(transitions);
86}
87
88void VisitTracker::NotifyRenderProcessHostDestruction(const void* host) {
89  HostList::iterator i = hosts_.find(host);
90  if (i == hosts_.end())
91    return;  // We don't have any entries for this host.
92
93  delete i->second;
94  hosts_.erase(i);
95}
96
97
98void VisitTracker::CleanupTransitionList(TransitionList* transitions) {
99  if (transitions->size() <= kMaxItemsInTransitionList)
100    return;  // Nothing to do.
101
102  transitions->erase(transitions->begin(),
103                     transitions->begin() + kResizeBigTransitionListTo);
104}
105
106}  // namespace history
107