• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/visitedlink/browser/visitedlink_event_listener.h"
6 
7 #include "base/memory/shared_memory.h"
8 #include "components/visitedlink/browser/visitedlink_delegate.h"
9 #include "components/visitedlink/common/visitedlink_messages.h"
10 #include "content/public/browser/notification_service.h"
11 #include "content/public/browser/notification_types.h"
12 #include "content/public/browser/render_process_host.h"
13 #include "content/public/browser/render_widget_host.h"
14 
15 using base::Time;
16 using base::TimeDelta;
17 using content::RenderWidgetHost;
18 
19 namespace {
20 
21 // The amount of time we wait to accumulate visited link additions.
22 const int kCommitIntervalMs = 100;
23 
24 // Size of the buffer after which individual link updates deemed not warranted
25 // and the overall update should be used instead.
26 const unsigned kVisitedLinkBufferThreshold = 50;
27 
28 }  // namespace
29 
30 namespace visitedlink {
31 
32 // This class manages buffering and sending visited link hashes (fingerprints)
33 // to renderer based on widget visibility.
34 // As opposed to the VisitedLinkEventListener, which coalesces to
35 // reduce the rate of messages being sent to render processes, this class
36 // ensures that the updates occur only when explicitly requested. This is
37 // used for RenderProcessHostImpl to only send Add/Reset link events to the
38 // renderers when their tabs are visible and the corresponding RenderViews are
39 // created.
40 class VisitedLinkUpdater {
41  public:
VisitedLinkUpdater(int render_process_id)42   explicit VisitedLinkUpdater(int render_process_id)
43       : reset_needed_(false), render_process_id_(render_process_id) {
44   }
45 
46   // Informs the renderer about a new visited link table.
SendVisitedLinkTable(base::SharedMemory * table_memory)47   void SendVisitedLinkTable(base::SharedMemory* table_memory) {
48     content::RenderProcessHost* process =
49         content::RenderProcessHost::FromID(render_process_id_);
50     if (!process)
51       return;  // Happens in tests
52     base::SharedMemoryHandle handle_for_process;
53     table_memory->ShareReadOnlyToProcess(process->GetHandle(),
54                                          &handle_for_process);
55     if (base::SharedMemory::IsHandleValid(handle_for_process))
56       process->Send(new ChromeViewMsg_VisitedLink_NewTable(
57           handle_for_process));
58   }
59 
60   // Buffers |links| to update, but doesn't actually relay them.
AddLinks(const VisitedLinkCommon::Fingerprints & links)61   void AddLinks(const VisitedLinkCommon::Fingerprints& links) {
62     if (reset_needed_)
63       return;
64 
65     if (pending_.size() + links.size() > kVisitedLinkBufferThreshold) {
66       // Once the threshold is reached, there's no need to store pending visited
67       // link updates -- we opt for resetting the state for all links.
68       AddReset();
69       return;
70     }
71 
72     pending_.insert(pending_.end(), links.begin(), links.end());
73   }
74 
75   // Tells the updater that sending individual link updates is no longer
76   // necessary and the visited state for all links should be reset.
AddReset()77   void AddReset() {
78     reset_needed_ = true;
79     pending_.clear();
80   }
81 
82   // Sends visited link update messages: a list of links whose visited state
83   // changed or reset of visited state for all links.
Update()84   void Update() {
85     content::RenderProcessHost* process =
86         content::RenderProcessHost::FromID(render_process_id_);
87     if (!process)
88       return;  // Happens in tests
89 
90     if (!process->VisibleWidgetCount())
91       return;
92 
93     if (reset_needed_) {
94       process->Send(new ChromeViewMsg_VisitedLink_Reset());
95       reset_needed_ = false;
96       return;
97     }
98 
99     if (pending_.empty())
100       return;
101 
102     process->Send(new ChromeViewMsg_VisitedLink_Add(pending_));
103 
104     pending_.clear();
105   }
106 
107  private:
108   bool reset_needed_;
109   int render_process_id_;
110   VisitedLinkCommon::Fingerprints pending_;
111 };
112 
VisitedLinkEventListener(VisitedLinkMaster * master,content::BrowserContext * browser_context)113 VisitedLinkEventListener::VisitedLinkEventListener(
114     VisitedLinkMaster* master,
115     content::BrowserContext* browser_context)
116     : master_(master),
117       browser_context_(browser_context) {
118   registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED,
119                  content::NotificationService::AllBrowserContextsAndSources());
120   registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
121                  content::NotificationService::AllBrowserContextsAndSources());
122   registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
123                  content::NotificationService::AllBrowserContextsAndSources());
124 }
125 
~VisitedLinkEventListener()126 VisitedLinkEventListener::~VisitedLinkEventListener() {
127   if (!pending_visited_links_.empty())
128     pending_visited_links_.clear();
129 }
130 
NewTable(base::SharedMemory * table_memory)131 void VisitedLinkEventListener::NewTable(base::SharedMemory* table_memory) {
132   if (!table_memory)
133     return;
134 
135   // Send to all RenderProcessHosts.
136   for (Updaters::iterator i = updaters_.begin(); i != updaters_.end(); ++i) {
137     // Make sure to not send to incognito renderers.
138     content::RenderProcessHost* process =
139         content::RenderProcessHost::FromID(i->first);
140     if (!process)
141       continue;
142 
143     i->second->SendVisitedLinkTable(table_memory);
144   }
145 }
146 
Add(VisitedLinkMaster::Fingerprint fingerprint)147 void VisitedLinkEventListener::Add(VisitedLinkMaster::Fingerprint fingerprint) {
148   pending_visited_links_.push_back(fingerprint);
149 
150   if (!coalesce_timer_.IsRunning()) {
151     coalesce_timer_.Start(FROM_HERE,
152         TimeDelta::FromMilliseconds(kCommitIntervalMs), this,
153         &VisitedLinkEventListener::CommitVisitedLinks);
154   }
155 }
156 
Reset()157 void VisitedLinkEventListener::Reset() {
158   pending_visited_links_.clear();
159   coalesce_timer_.Stop();
160 
161   for (Updaters::iterator i = updaters_.begin(); i != updaters_.end(); ++i) {
162     i->second->AddReset();
163     i->second->Update();
164   }
165 }
166 
CommitVisitedLinks()167 void VisitedLinkEventListener::CommitVisitedLinks() {
168   // Send to all RenderProcessHosts.
169   for (Updaters::iterator i = updaters_.begin(); i != updaters_.end(); ++i) {
170     i->second->AddLinks(pending_visited_links_);
171     i->second->Update();
172   }
173 
174   pending_visited_links_.clear();
175 }
176 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)177 void VisitedLinkEventListener::Observe(
178     int type,
179     const content::NotificationSource& source,
180     const content::NotificationDetails& details) {
181   switch (type) {
182     case content::NOTIFICATION_RENDERER_PROCESS_CREATED: {
183       content::RenderProcessHost* process =
184           content::Source<content::RenderProcessHost>(source).ptr();
185       if (browser_context_ != process->GetBrowserContext())
186         return;
187 
188       // Happens on browser start up.
189       if (!master_->shared_memory())
190         return;
191 
192       updaters_[process->GetID()] =
193           make_linked_ptr(new VisitedLinkUpdater(process->GetID()));
194       updaters_[process->GetID()]->SendVisitedLinkTable(
195           master_->shared_memory());
196       break;
197     }
198     case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: {
199       content::RenderProcessHost* process =
200           content::Source<content::RenderProcessHost>(source).ptr();
201       if (updaters_.count(process->GetID())) {
202         updaters_.erase(process->GetID());
203       }
204       break;
205     }
206     case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED: {
207       RenderWidgetHost* widget =
208           content::Source<RenderWidgetHost>(source).ptr();
209       int child_id = widget->GetProcess()->GetID();
210       if (updaters_.count(child_id))
211         updaters_[child_id]->Update();
212       break;
213     }
214     default:
215       NOTREACHED();
216       break;
217   }
218 }
219 
220 }  // namespace visitedlink
221