visitedlink_event_listener.cc 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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. #include "components/visitedlink/browser/visitedlink_event_listener.h"
  5. #include <memory>
  6. #include "base/bind.h"
  7. #include "base/time/time.h"
  8. #include "components/visitedlink/browser/visitedlink_delegate.h"
  9. #include "components/visitedlink/common/visitedlink.mojom.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. #include "mojo/public/cpp/bindings/remote.h"
  15. using base::Time;
  16. using content::RenderWidgetHost;
  17. namespace {
  18. // The amount of time we wait to accumulate visited link additions.
  19. constexpr int kCommitIntervalMs = 100;
  20. // Size of the buffer after which individual link updates deemed not warranted
  21. // and the overall update should be used instead.
  22. const unsigned kVisitedLinkBufferThreshold = 50;
  23. } // namespace
  24. namespace visitedlink {
  25. // This class manages buffering and sending visited link hashes (fingerprints)
  26. // to renderer based on widget visibility.
  27. // As opposed to the VisitedLinkEventListener, which coalesces to
  28. // reduce the rate of messages being sent to render processes, this class
  29. // ensures that the updates occur only when explicitly requested. This is
  30. // used for RenderProcessHostImpl to only send Add/Reset link events to the
  31. // renderers when their tabs are visible and the corresponding RenderViews are
  32. // created.
  33. class VisitedLinkUpdater {
  34. public:
  35. explicit VisitedLinkUpdater(int render_process_id)
  36. : reset_needed_(false),
  37. invalidate_hashes_(false),
  38. render_process_id_(render_process_id) {
  39. content::RenderProcessHost::FromID(render_process_id)
  40. ->BindReceiver(sink_.BindNewPipeAndPassReceiver());
  41. }
  42. // Informs the renderer about a new visited link table.
  43. void SendVisitedLinkTable(base::ReadOnlySharedMemoryRegion* region) {
  44. if (region->IsValid())
  45. sink_->UpdateVisitedLinks(region->Duplicate());
  46. }
  47. // Buffers |links| to update, but doesn't actually relay them.
  48. void AddLinks(const VisitedLinkCommon::Fingerprints& links) {
  49. if (reset_needed_)
  50. return;
  51. if (pending_.size() + links.size() > kVisitedLinkBufferThreshold) {
  52. // Once the threshold is reached, there's no need to store pending visited
  53. // link updates -- we opt for resetting the state for all links.
  54. AddReset(false);
  55. return;
  56. }
  57. pending_.insert(pending_.end(), links.begin(), links.end());
  58. }
  59. // Tells the updater that sending individual link updates is no longer
  60. // necessary and the visited state for all links should be reset. If
  61. // |invalidateHashes| is true all cached visited links hashes should be
  62. // dropped.
  63. void AddReset(bool invalidate_hashes) {
  64. reset_needed_ = true;
  65. // Do not set to false. If tab is invisible the reset message will not be
  66. // sent until tab became visible.
  67. if (invalidate_hashes)
  68. invalidate_hashes_ = true;
  69. pending_.clear();
  70. }
  71. // Sends visited link update messages: a list of links whose visited state
  72. // changed or reset of visited state for all links.
  73. void Update() {
  74. content::RenderProcessHost* process =
  75. content::RenderProcessHost::FromID(render_process_id_);
  76. if (!process)
  77. return; // Happens in tests
  78. if (!process->VisibleClientCount())
  79. return;
  80. if (reset_needed_) {
  81. sink_->ResetVisitedLinks(invalidate_hashes_);
  82. reset_needed_ = false;
  83. invalidate_hashes_ = false;
  84. return;
  85. }
  86. if (pending_.empty())
  87. return;
  88. sink_->AddVisitedLinks(pending_);
  89. pending_.clear();
  90. }
  91. private:
  92. bool reset_needed_;
  93. bool invalidate_hashes_;
  94. int render_process_id_;
  95. mojo::Remote<mojom::VisitedLinkNotificationSink> sink_;
  96. VisitedLinkCommon::Fingerprints pending_;
  97. };
  98. VisitedLinkEventListener::VisitedLinkEventListener(
  99. content::BrowserContext* browser_context)
  100. : coalesce_timer_(&default_coalesce_timer_),
  101. browser_context_(browser_context) {
  102. registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
  103. content::NotificationService::AllBrowserContextsAndSources());
  104. registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
  105. content::NotificationService::AllBrowserContextsAndSources());
  106. }
  107. VisitedLinkEventListener::~VisitedLinkEventListener() {
  108. if (!pending_visited_links_.empty())
  109. pending_visited_links_.clear();
  110. }
  111. void VisitedLinkEventListener::NewTable(
  112. base::ReadOnlySharedMemoryRegion* table_region) {
  113. DCHECK(table_region && table_region->IsValid());
  114. table_region_ = table_region->Duplicate();
  115. if (!table_region_.IsValid())
  116. return;
  117. // Send to all RenderProcessHosts.
  118. for (auto i = updaters_.begin(); i != updaters_.end(); ++i) {
  119. // Make sure to not send to incognito renderers.
  120. content::RenderProcessHost* process =
  121. content::RenderProcessHost::FromID(i->first);
  122. if (!process)
  123. continue;
  124. i->second->SendVisitedLinkTable(&table_region_);
  125. }
  126. }
  127. void VisitedLinkEventListener::Add(VisitedLinkWriter::Fingerprint fingerprint) {
  128. pending_visited_links_.push_back(fingerprint);
  129. if (!coalesce_timer_->IsRunning()) {
  130. coalesce_timer_->Start(
  131. FROM_HERE, base::Milliseconds(kCommitIntervalMs),
  132. base::BindOnce(&VisitedLinkEventListener::CommitVisitedLinks,
  133. base::Unretained(this)));
  134. }
  135. }
  136. void VisitedLinkEventListener::Reset(bool invalidate_hashes) {
  137. pending_visited_links_.clear();
  138. coalesce_timer_->Stop();
  139. for (auto i = updaters_.begin(); i != updaters_.end(); ++i) {
  140. i->second->AddReset(invalidate_hashes);
  141. i->second->Update();
  142. }
  143. }
  144. void VisitedLinkEventListener::SetCoalesceTimerForTest(
  145. base::OneShotTimer* coalesce_timer_override) {
  146. coalesce_timer_ = coalesce_timer_override;
  147. }
  148. void VisitedLinkEventListener::CommitVisitedLinks() {
  149. // Send to all RenderProcessHosts.
  150. for (auto i = updaters_.begin(); i != updaters_.end(); ++i) {
  151. i->second->AddLinks(pending_visited_links_);
  152. i->second->Update();
  153. }
  154. pending_visited_links_.clear();
  155. }
  156. void VisitedLinkEventListener::OnRenderProcessHostCreated(
  157. content::RenderProcessHost* rph) {
  158. if (browser_context_ != rph->GetBrowserContext())
  159. return;
  160. // Happens on browser start up.
  161. if (!table_region_.IsValid())
  162. return;
  163. updaters_[rph->GetID()] = std::make_unique<VisitedLinkUpdater>(rph->GetID());
  164. updaters_[rph->GetID()]->SendVisitedLinkTable(&table_region_);
  165. }
  166. void VisitedLinkEventListener::Observe(
  167. int type,
  168. const content::NotificationSource& source,
  169. const content::NotificationDetails& details) {
  170. switch (type) {
  171. case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: {
  172. content::RenderProcessHost* process =
  173. content::Source<content::RenderProcessHost>(source).ptr();
  174. if (updaters_.count(process->GetID())) {
  175. updaters_.erase(process->GetID());
  176. }
  177. break;
  178. }
  179. case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED: {
  180. RenderWidgetHost* widget =
  181. content::Source<RenderWidgetHost>(source).ptr();
  182. int child_id = widget->GetProcess()->GetID();
  183. if (updaters_.count(child_id))
  184. updaters_[child_id]->Update();
  185. break;
  186. }
  187. default:
  188. NOTREACHED();
  189. break;
  190. }
  191. }
  192. } // namespace visitedlink