site_data_recorder.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. // Copyright 2020 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/performance_manager/public/decorators/site_data_recorder.h"
  5. #include "base/memory/raw_ptr.h"
  6. #include "base/time/time.h"
  7. #include "components/performance_manager/graph/node_attached_data_impl.h"
  8. #include "components/performance_manager/graph/page_node_impl.h"
  9. #include "components/performance_manager/persistence/site_data/site_data_cache.h"
  10. #include "components/performance_manager/persistence/site_data/site_data_cache_factory.h"
  11. #include "components/performance_manager/persistence/site_data/site_data_writer.h"
  12. #include "components/performance_manager/public/persistence/site_data/site_data_reader.h"
  13. namespace performance_manager {
  14. // The period of time after loading during which we ignore title/favicon
  15. // change events. It's possible for some site that are loaded in background to
  16. // use some of these features without this being an attempt to communicate
  17. // with the user (e.g. the page is just really finishing to load).
  18. constexpr base::TimeDelta kTitleOrFaviconChangePostLoadGracePeriod =
  19. base::Seconds(20);
  20. // The period of time during which audio usage gets ignored after a page gets
  21. // backgrounded. It's necessary because there might be a delay between a media
  22. // request gets initiated and the time the audio actually starts.
  23. constexpr base::TimeDelta kFeatureUsagePostBackgroundGracePeriod =
  24. base::Seconds(10);
  25. // Provides SiteData machinery access to some internals of a PageNodeImpl.
  26. class SiteDataAccess {
  27. public:
  28. static std::unique_ptr<NodeAttachedData>* GetUniquePtrStorage(
  29. PageNodeImpl* page_node) {
  30. return &page_node->GetSiteData(base::PassKey<SiteDataAccess>());
  31. }
  32. };
  33. namespace {
  34. bool IsLoadedIdle(PageNode::LoadingState loading_state) {
  35. switch (loading_state) {
  36. case PageNode::LoadingState::kLoadingNotStarted:
  37. case PageNode::LoadingState::kLoadedBusy:
  38. case PageNode::LoadingState::kLoading:
  39. case PageNode::LoadingState::kLoadingTimedOut:
  40. return false;
  41. case PageNode::LoadingState::kLoadedIdle:
  42. return true;
  43. }
  44. }
  45. // NodeAttachedData used to adorn every page node with a SiteDataWriter.
  46. class SiteDataNodeData : public NodeAttachedDataImpl<SiteDataNodeData>,
  47. public SiteDataRecorder::Data {
  48. public:
  49. struct Traits : public NodeAttachedDataOwnedByNodeType<PageNodeImpl> {};
  50. explicit SiteDataNodeData(const PageNodeImpl* page_node)
  51. : page_node_(page_node) {}
  52. SiteDataNodeData(const SiteDataNodeData&) = delete;
  53. SiteDataNodeData& operator=(const SiteDataNodeData&) = delete;
  54. ~SiteDataNodeData() override = default;
  55. // NodeAttachedData:
  56. static std::unique_ptr<NodeAttachedData>* GetUniquePtrStorage(
  57. PageNodeImpl* page_node) {
  58. return SiteDataAccess::GetUniquePtrStorage(page_node);
  59. }
  60. // Set the SiteDataCache that should be used to create the writer.
  61. void set_data_cache(SiteDataCache* data_cache) {
  62. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  63. DCHECK(data_cache);
  64. data_cache_ = data_cache;
  65. }
  66. // Functions called whenever one of the tracked properties changes.
  67. void OnMainFrameUrlChanged(const GURL& url, bool page_is_visible);
  68. void OnIsLoadedIdleChanged(bool is_loaded_idle);
  69. void OnIsVisibleChanged(bool is_visible);
  70. void OnIsAudibleChanged(bool audible);
  71. void OnTitleUpdated();
  72. void OnFaviconUpdated();
  73. void Reset();
  74. SiteDataWriter* writer() const override {
  75. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  76. return writer_.get();
  77. }
  78. SiteDataReader* reader() const override {
  79. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  80. return reader_.get();
  81. }
  82. private:
  83. // The features tracked by the SiteDataRecorder class.
  84. enum class FeatureType {
  85. kTitleChange,
  86. kFaviconChange,
  87. kAudioUsage,
  88. };
  89. void SetDataCacheForTesting(SiteDataCache* cache) override {
  90. set_data_cache(cache);
  91. }
  92. // Indicates of a feature usage event should be ignored.
  93. bool ShouldIgnoreFeatureUsageEvent(FeatureType feature_type);
  94. // Records a feature usage event if necessary.
  95. void MaybeNotifyBackgroundFeatureUsage(void (SiteDataWriter::*method)(),
  96. FeatureType feature_type);
  97. TabVisibility GetPageNodeVisibility() {
  98. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  99. return page_node_->is_visible() ? TabVisibility::kForeground
  100. : TabVisibility::kBackground;
  101. }
  102. // The SiteDataCache used to serve writers for the PageNode owned by this
  103. // object.
  104. raw_ptr<SiteDataCache> data_cache_ GUARDED_BY_CONTEXT(sequence_checker_) =
  105. nullptr;
  106. // The PageNode that owns this object.
  107. raw_ptr<const PageNodeImpl> page_node_ GUARDED_BY_CONTEXT(sequence_checker_) =
  108. nullptr;
  109. // The time at which this tab switched to LoadingState::kLoadedIdle, null if
  110. // this tab is not currently in that state.
  111. base::TimeTicks loaded_idle_time_ GUARDED_BY_CONTEXT(sequence_checker_);
  112. std::unique_ptr<SiteDataWriter> writer_ GUARDED_BY_CONTEXT(sequence_checker_);
  113. std::unique_ptr<SiteDataReader> reader_ GUARDED_BY_CONTEXT(sequence_checker_);
  114. SEQUENCE_CHECKER(sequence_checker_);
  115. };
  116. void SiteDataNodeData::OnMainFrameUrlChanged(const GURL& url,
  117. bool page_is_visible) {
  118. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  119. url::Origin origin = url::Origin::Create(url);
  120. if (writer_ && origin == writer_->Origin())
  121. return;
  122. // If the origin has changed then the writer should be invalidated.
  123. Reset();
  124. if (!url.SchemeIsHTTPOrHTTPS())
  125. return;
  126. writer_ = data_cache_->GetWriterForOrigin(origin);
  127. reader_ = data_cache_->GetReaderForOrigin(origin);
  128. // The writer is assumed not to be LoadingState::kLoadedIdle at this point.
  129. // Make adjustments if it is LoadingState::kLoadedIdle.
  130. if (IsLoadedIdle(page_node_->loading_state()))
  131. OnIsLoadedIdleChanged(true);
  132. DCHECK_EQ(IsLoadedIdle(page_node_->loading_state()),
  133. !loaded_idle_time_.is_null());
  134. }
  135. void SiteDataNodeData::OnIsLoadedIdleChanged(bool is_loaded_idle) {
  136. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  137. if (!writer_)
  138. return;
  139. if (!is_loaded_idle && !loaded_idle_time_.is_null()) {
  140. writer_->NotifySiteUnloaded(GetPageNodeVisibility());
  141. loaded_idle_time_ = base::TimeTicks();
  142. } else if (is_loaded_idle) {
  143. writer_->NotifySiteLoaded(GetPageNodeVisibility());
  144. loaded_idle_time_ = base::TimeTicks::Now();
  145. }
  146. }
  147. void SiteDataNodeData::OnIsVisibleChanged(bool is_visible) {
  148. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  149. if (!writer_)
  150. return;
  151. if (is_visible) {
  152. writer_->NotifySiteForegrounded(IsLoadedIdle(page_node_->loading_state()));
  153. } else {
  154. writer_->NotifySiteBackgrounded(IsLoadedIdle(page_node_->loading_state()));
  155. }
  156. }
  157. void SiteDataNodeData::OnIsAudibleChanged(bool audible) {
  158. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  159. if (!audible)
  160. return;
  161. MaybeNotifyBackgroundFeatureUsage(
  162. &SiteDataWriter::NotifyUsesAudioInBackground, FeatureType::kAudioUsage);
  163. }
  164. void SiteDataNodeData::OnTitleUpdated() {
  165. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  166. MaybeNotifyBackgroundFeatureUsage(
  167. &SiteDataWriter::NotifyUpdatesTitleInBackground,
  168. FeatureType::kTitleChange);
  169. }
  170. void SiteDataNodeData::OnFaviconUpdated() {
  171. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  172. MaybeNotifyBackgroundFeatureUsage(
  173. &SiteDataWriter::NotifyUpdatesFaviconInBackground,
  174. FeatureType::kFaviconChange);
  175. }
  176. void SiteDataNodeData::Reset() {
  177. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  178. if (writer_ && !loaded_idle_time_.is_null() &&
  179. IsLoadedIdle(page_node_->loading_state())) {
  180. writer_->NotifySiteUnloaded(GetPageNodeVisibility());
  181. loaded_idle_time_ = base::TimeTicks();
  182. }
  183. writer_.reset();
  184. reader_.reset();
  185. }
  186. bool SiteDataNodeData::ShouldIgnoreFeatureUsageEvent(FeatureType feature_type) {
  187. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  188. // The feature usage should be ignored if there's no writer for this page.
  189. if (!writer_)
  190. return true;
  191. // Ignore all features happening before the website gets fully loaded.
  192. if (!IsLoadedIdle(page_node_->loading_state()))
  193. return true;
  194. // Ignore events if the tab is not in background.
  195. if (GetPageNodeVisibility() == TabVisibility::kForeground)
  196. return true;
  197. if (feature_type == FeatureType::kTitleChange ||
  198. feature_type == FeatureType::kFaviconChange) {
  199. DCHECK(!loaded_idle_time_.is_null());
  200. if (base::TimeTicks::Now() - loaded_idle_time_ <
  201. kTitleOrFaviconChangePostLoadGracePeriod) {
  202. return true;
  203. }
  204. }
  205. // Ignore events happening shortly after the tab being backgrounded, they're
  206. // usually false positives.
  207. if ((page_node_->TimeSinceLastVisibilityChange() <
  208. kFeatureUsagePostBackgroundGracePeriod)) {
  209. return true;
  210. }
  211. return false;
  212. }
  213. void SiteDataNodeData::MaybeNotifyBackgroundFeatureUsage(
  214. void (SiteDataWriter::*method)(),
  215. FeatureType feature_type) {
  216. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  217. if (ShouldIgnoreFeatureUsageEvent(feature_type))
  218. return;
  219. (writer_.get()->*method)();
  220. }
  221. SiteDataNodeData* GetSiteDataNodeDataFromPageNode(const PageNode* page_node) {
  222. auto* page_node_impl = PageNodeImpl::FromNode(page_node);
  223. DCHECK(page_node_impl);
  224. auto* data = SiteDataNodeData::Get(page_node_impl);
  225. DCHECK(data);
  226. return data;
  227. }
  228. } // namespace
  229. SiteDataRecorder::SiteDataRecorder() {
  230. DETACH_FROM_SEQUENCE(sequence_checker_);
  231. }
  232. SiteDataRecorder::~SiteDataRecorder() = default;
  233. void SiteDataRecorder::OnPassedToGraph(Graph* graph) {
  234. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  235. RegisterObservers(graph);
  236. }
  237. void SiteDataRecorder::OnTakenFromGraph(Graph* graph) {
  238. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  239. UnregisterObservers(graph);
  240. }
  241. void SiteDataRecorder::OnPageNodeAdded(const PageNode* page_node) {
  242. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  243. SetPageNodeDataCache(page_node);
  244. }
  245. void SiteDataRecorder::OnBeforePageNodeRemoved(const PageNode* page_node) {
  246. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  247. auto* data = GetSiteDataNodeDataFromPageNode(page_node);
  248. data->Reset();
  249. }
  250. void SiteDataRecorder::OnMainFrameUrlChanged(const PageNode* page_node) {
  251. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  252. auto* data = GetSiteDataNodeDataFromPageNode(page_node);
  253. data->OnMainFrameUrlChanged(page_node->GetMainFrameUrl(),
  254. page_node->IsVisible());
  255. }
  256. void SiteDataRecorder::OnLoadingStateChanged(
  257. const PageNode* page_node,
  258. PageNode::LoadingState previous_state) {
  259. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  260. auto* data = GetSiteDataNodeDataFromPageNode(page_node);
  261. data->OnIsLoadedIdleChanged(IsLoadedIdle(page_node->GetLoadingState()));
  262. }
  263. void SiteDataRecorder::OnIsVisibleChanged(const PageNode* page_node) {
  264. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  265. auto* data = GetSiteDataNodeDataFromPageNode(page_node);
  266. data->OnIsVisibleChanged(page_node->IsVisible());
  267. }
  268. void SiteDataRecorder::OnIsAudibleChanged(const PageNode* page_node) {
  269. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  270. auto* data = GetSiteDataNodeDataFromPageNode(page_node);
  271. data->OnIsAudibleChanged(page_node->IsAudible());
  272. }
  273. void SiteDataRecorder::OnTitleUpdated(const PageNode* page_node) {
  274. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  275. auto* data = GetSiteDataNodeDataFromPageNode(page_node);
  276. data->OnTitleUpdated();
  277. }
  278. void SiteDataRecorder::OnFaviconUpdated(const PageNode* page_node) {
  279. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  280. auto* data = GetSiteDataNodeDataFromPageNode(page_node);
  281. data->OnFaviconUpdated();
  282. }
  283. void SiteDataRecorder::RegisterObservers(Graph* graph) {
  284. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  285. graph->AddPageNodeObserver(this);
  286. }
  287. void SiteDataRecorder::UnregisterObservers(Graph* graph) {
  288. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  289. graph->RemovePageNodeObserver(this);
  290. }
  291. void SiteDataRecorder::SetPageNodeDataCache(const PageNode* page_node) {
  292. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  293. auto* page_node_impl = PageNodeImpl::FromNode(page_node);
  294. DCHECK(page_node_impl);
  295. DCHECK(!SiteDataNodeData::Get(page_node_impl));
  296. auto* data = SiteDataNodeData::GetOrCreate(page_node_impl);
  297. data->set_data_cache(
  298. SiteDataCacheFactory::GetInstance()->GetDataCacheForBrowserContext(
  299. page_node->GetBrowserContextID()));
  300. }
  301. SiteDataNodeData::Data::Data() = default;
  302. SiteDataNodeData::Data::~Data() = default;
  303. // static
  304. const SiteDataRecorder::Data* SiteDataRecorder::Data::FromPageNode(
  305. const PageNode* page_node) {
  306. return SiteDataNodeData::Get(PageNodeImpl::FromNode(page_node));
  307. }
  308. // static
  309. SiteDataRecorder::Data* SiteDataRecorder::Data::GetForTesting(
  310. const PageNode* page_node) {
  311. return GetSiteDataNodeDataFromPageNode(page_node);
  312. }
  313. } // namespace performance_manager