source_url_recorder_test.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. // Copyright 2017 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/ukm/content/source_url_recorder.h"
  5. #include "base/test/scoped_feature_list.h"
  6. #include "components/ukm/test_ukm_recorder.h"
  7. #include "content/public/browser/web_contents.h"
  8. #include "content/public/test/navigation_simulator.h"
  9. #include "content/public/test/test_renderer_host.h"
  10. #include "services/metrics/public/cpp/ukm_recorder.h"
  11. #include "services/metrics/public/cpp/ukm_source.h"
  12. #include "services/metrics/public/cpp/ukm_source_id.h"
  13. #include "testing/gtest/include/gtest/gtest.h"
  14. #include "third_party/metrics_proto/ukm/source.pb.h"
  15. #include "url/gurl.h"
  16. using content::NavigationSimulator;
  17. class SourceUrlRecorderWebContentsObserverTest
  18. : public content::RenderViewHostTestHarness {
  19. public:
  20. void SetUp() override {
  21. content::RenderViewHostTestHarness::SetUp();
  22. ukm::InitializeSourceUrlRecorderForWebContents(web_contents());
  23. }
  24. GURL GetAssociatedURLForWebContentsDocument() {
  25. const ukm::UkmSource* src = test_ukm_recorder_.GetSourceForSourceId(
  26. web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId());
  27. return src ? src->url() : GURL();
  28. }
  29. protected:
  30. ukm::TestAutoSetUkmRecorder test_ukm_recorder_;
  31. };
  32. TEST_F(SourceUrlRecorderWebContentsObserverTest, Basic) {
  33. GURL url("https://www.example.com/");
  34. NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(), url);
  35. const auto& sources = test_ukm_recorder_.GetSources();
  36. // Expect two sources, one for navigation, one for document.
  37. EXPECT_EQ(2ul, sources.size());
  38. for (const auto& kv : sources) {
  39. EXPECT_EQ(url, kv.second->url());
  40. EXPECT_EQ(1u, kv.second->urls().size());
  41. }
  42. }
  43. TEST_F(SourceUrlRecorderWebContentsObserverTest, InitialUrl) {
  44. GURL initial_url("https://www.a.com/");
  45. GURL final_url("https://www.b.com/");
  46. auto simulator =
  47. NavigationSimulator::CreateRendererInitiated(initial_url, main_rfh());
  48. simulator->Start();
  49. simulator->Redirect(final_url);
  50. simulator->Commit();
  51. EXPECT_EQ(final_url, GetAssociatedURLForWebContentsDocument());
  52. const auto& sources = test_ukm_recorder_.GetSources();
  53. // Expect two sources, one for navigation, one for document.
  54. EXPECT_EQ(2ul, sources.size());
  55. size_t navigation_type_source_count = 0;
  56. size_t document_type_source_count = 0;
  57. for (const auto& kv : sources) {
  58. if (ukm::GetSourceIdType(kv.first) ==
  59. ukm::SourceIdObj::Type::NAVIGATION_ID) {
  60. // The navigation source has both URLs.
  61. EXPECT_EQ(initial_url, kv.second->urls().front());
  62. EXPECT_EQ(final_url, kv.second->urls().back());
  63. navigation_type_source_count++;
  64. }
  65. if (ukm::GetSourceIdType(kv.first) == ukm::SourceIdObj::Type::DEFAULT) {
  66. // The document source has the final URL which is one set on the
  67. // committed document.
  68. EXPECT_EQ(final_url, kv.second->urls().front());
  69. document_type_source_count++;
  70. }
  71. }
  72. EXPECT_EQ(1u, navigation_type_source_count);
  73. EXPECT_EQ(1u, document_type_source_count);
  74. }
  75. TEST_F(SourceUrlRecorderWebContentsObserverTest, IgnoreUrlInSubframe) {
  76. GURL main_frame_url("https://www.example.com/");
  77. GURL sub_frame_url("https://www.example.com/iframe.html");
  78. NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(),
  79. main_frame_url);
  80. NavigationSimulator::NavigateAndCommitFromDocument(
  81. sub_frame_url,
  82. content::RenderFrameHostTester::For(main_rfh())->AppendChild("subframe"));
  83. const auto& sources = test_ukm_recorder_.GetSources();
  84. // Expect two sources created, one for navigation and one for document, both
  85. // should have the URL of the main frame.
  86. EXPECT_EQ(2ul, sources.size());
  87. for (const auto& kv : sources) {
  88. EXPECT_EQ(main_frame_url, kv.second->url());
  89. EXPECT_EQ(1u, kv.second->urls().size());
  90. }
  91. EXPECT_EQ(main_frame_url, GetAssociatedURLForWebContentsDocument());
  92. }
  93. TEST_F(SourceUrlRecorderWebContentsObserverTest, SameDocumentNavigation) {
  94. GURL url1("https://www.example.com/1");
  95. GURL url2("https://www.example.com/2");
  96. GURL same_document_url1("https://www.example.com/#samedocument1");
  97. GURL same_document_url2("https://www.example.com/#samedocument2");
  98. NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(), url1);
  99. NavigationSimulator::CreateRendererInitiated(same_document_url1, main_rfh())
  100. ->CommitSameDocument();
  101. NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(), url2);
  102. NavigationSimulator::CreateRendererInitiated(same_document_url2, main_rfh())
  103. ->CommitSameDocument();
  104. EXPECT_EQ(same_document_url2, web_contents()->GetLastCommittedURL());
  105. // Serialize each source so we can verify expectations below.
  106. ukm::Source full_nav_source1, full_nav_source2, same_doc_source1,
  107. same_doc_source2;
  108. // We expect 6 sources to be created, 4 of which are from the 4 committed
  109. // navigations, and 2 of which are from the 2 created Document instance.
  110. EXPECT_EQ(6ul, test_ukm_recorder_.GetSources().size());
  111. for (auto& kv : test_ukm_recorder_.GetSources()) {
  112. // Populate protos from the navigation sources.
  113. if (ukm::GetSourceIdType(kv.first) != ukm::SourceIdObj::Type::NAVIGATION_ID)
  114. continue;
  115. if (kv.second->url() == url1) {
  116. kv.second->PopulateProto(&full_nav_source1);
  117. } else if (kv.second->url() == url2) {
  118. kv.second->PopulateProto(&full_nav_source2);
  119. } else if (kv.second->url() == same_document_url1) {
  120. kv.second->PopulateProto(&same_doc_source1);
  121. } else if (kv.second->url() == same_document_url2) {
  122. kv.second->PopulateProto(&same_doc_source2);
  123. } else {
  124. FAIL() << "Encountered unexpected source.";
  125. }
  126. }
  127. // The first navigation was a non-same-document navigation to url1. As such,
  128. // it shouldn't have any previous source ids.
  129. EXPECT_EQ(url1, full_nav_source1.urls(0).url());
  130. EXPECT_TRUE(full_nav_source1.has_id());
  131. EXPECT_FALSE(full_nav_source1.is_same_document_navigation());
  132. EXPECT_FALSE(full_nav_source1.has_previous_source_id());
  133. EXPECT_FALSE(full_nav_source1.has_previous_same_document_source_id());
  134. EXPECT_TRUE(full_nav_source1.has_navigation_time_msec());
  135. // The second navigation was a same-document navigation to
  136. // same_document_url1. It should have a previous_source_id that points to
  137. // url1's source, but no previous_same_document_source_id.
  138. EXPECT_EQ(same_document_url1, same_doc_source1.urls(0).url());
  139. EXPECT_TRUE(same_doc_source1.has_id());
  140. EXPECT_TRUE(same_doc_source1.is_same_document_navigation());
  141. EXPECT_EQ(full_nav_source1.id(), same_doc_source1.previous_source_id());
  142. EXPECT_FALSE(same_doc_source1.has_previous_same_document_source_id());
  143. EXPECT_TRUE(same_doc_source1.has_navigation_time_msec());
  144. // The third navigation was a non-same-document navigation to url2. It should
  145. // have a previous_source_id pointing to the source for url1, and a
  146. // previous_same_document_source_id pointing to the source for
  147. // same_document_url1.
  148. EXPECT_EQ(url2, full_nav_source2.urls(0).url());
  149. EXPECT_TRUE(full_nav_source2.has_id());
  150. EXPECT_FALSE(full_nav_source2.is_same_document_navigation());
  151. EXPECT_EQ(full_nav_source1.id(), full_nav_source2.previous_source_id());
  152. EXPECT_EQ(same_doc_source1.id(),
  153. full_nav_source2.previous_same_document_source_id());
  154. EXPECT_TRUE(full_nav_source2.has_navigation_time_msec());
  155. // The fourth navigation was a same-document navigation to
  156. // same_document_url2. It should have a previous_source_id pointing to the
  157. // source for url2, and no previous_same_document_source_id.
  158. EXPECT_EQ(same_document_url2, same_doc_source2.urls(0).url());
  159. EXPECT_TRUE(same_doc_source2.has_id());
  160. EXPECT_TRUE(same_doc_source2.is_same_document_navigation());
  161. EXPECT_EQ(full_nav_source2.id(), same_doc_source2.previous_source_id());
  162. EXPECT_FALSE(same_doc_source2.has_previous_same_document_source_id());
  163. EXPECT_TRUE(same_doc_source2.has_navigation_time_msec());
  164. EXPECT_EQ(url2, GetAssociatedURLForWebContentsDocument());
  165. // The recorded time of each navigation should increase monotonically.
  166. EXPECT_LE(full_nav_source1.navigation_time_msec(),
  167. same_doc_source1.navigation_time_msec());
  168. EXPECT_LE(same_doc_source1.navigation_time_msec(),
  169. full_nav_source2.navigation_time_msec());
  170. EXPECT_LE(full_nav_source2.navigation_time_msec(),
  171. same_doc_source2.navigation_time_msec());
  172. }
  173. TEST_F(SourceUrlRecorderWebContentsObserverTest, NavigationMetadata) {
  174. GURL url1("https://www.example.com/1");
  175. GURL same_origin_url1("https://www.example.com/same_origin");
  176. GURL url2("https://test.example.com/2");
  177. GURL cross_origin_url2("https://test1.example.com/cross_origin");
  178. GURL error_url("chrome://error");
  179. NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(), url1);
  180. NavigationSimulator::CreateRendererInitiated(same_origin_url1, main_rfh())
  181. ->Commit();
  182. NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(), url2);
  183. NavigationSimulator::CreateRendererInitiated(cross_origin_url2, main_rfh())
  184. ->Commit();
  185. NavigationSimulator::NavigateAndFailFromBrowser(web_contents(), error_url,
  186. net::ERR_FAILED);
  187. EXPECT_EQ(error_url, web_contents()->GetLastCommittedURL());
  188. // Serialize each source so we can verify expectations below.
  189. ukm::Source full_nav_source1;
  190. ukm::Source full_nav_source2;
  191. ukm::Source same_origin_source1;
  192. ukm::Source cross_origin_source2;
  193. ukm::Source error_page_source;
  194. for (auto& kv : test_ukm_recorder_.GetSources()) {
  195. // Populate protos from the navigation sources.
  196. if (ukm::GetSourceIdType(kv.first) != ukm::SourceIdObj::Type::NAVIGATION_ID)
  197. continue;
  198. if (kv.second->url() == url1) {
  199. kv.second->PopulateProto(&full_nav_source1);
  200. } else if (kv.second->url() == url2) {
  201. kv.second->PopulateProto(&full_nav_source2);
  202. } else if (kv.second->url() == same_origin_url1) {
  203. kv.second->PopulateProto(&same_origin_source1);
  204. } else if (kv.second->url() == cross_origin_url2) {
  205. kv.second->PopulateProto(&cross_origin_source2);
  206. } else if (kv.second->url() == error_url) {
  207. kv.second->PopulateProto(&error_page_source);
  208. } else {
  209. FAIL() << "Encountered unexpected source.";
  210. }
  211. }
  212. // The first navigation was a browser initiated navigation to url1.
  213. EXPECT_EQ(url1, full_nav_source1.urls(0).url());
  214. EXPECT_TRUE(full_nav_source1.has_id());
  215. EXPECT_FALSE(full_nav_source1.navigation_metadata().is_renderer_initiated());
  216. EXPECT_EQ(full_nav_source1.navigation_metadata().same_origin_status(),
  217. ukm::Source::CROSS_ORIGIN);
  218. EXPECT_FALSE(full_nav_source1.navigation_metadata().is_error_page());
  219. // The second navigation was a same-origin renderer-initiated navigation to
  220. // same_origin_url1.
  221. EXPECT_EQ(same_origin_url1, same_origin_source1.urls(0).url());
  222. EXPECT_TRUE(same_origin_source1.has_id());
  223. EXPECT_TRUE(
  224. same_origin_source1.navigation_metadata().is_renderer_initiated());
  225. EXPECT_EQ(same_origin_source1.navigation_metadata().same_origin_status(),
  226. ukm::Source::SAME_ORIGIN);
  227. EXPECT_FALSE(same_origin_source1.navigation_metadata().is_error_page());
  228. // The third navigation was a browser initiated navigation to url2.
  229. EXPECT_EQ(url2, full_nav_source2.urls(0).url());
  230. EXPECT_TRUE(full_nav_source2.has_id());
  231. EXPECT_FALSE(full_nav_source2.navigation_metadata().is_renderer_initiated());
  232. EXPECT_EQ(full_nav_source2.navigation_metadata().same_origin_status(),
  233. ukm::Source::CROSS_ORIGIN);
  234. EXPECT_FALSE(full_nav_source2.navigation_metadata().is_error_page());
  235. // The fourth navigation was a cross-origin renderer-initiated navigation to
  236. // url2.
  237. EXPECT_EQ(cross_origin_url2, cross_origin_source2.urls(0).url());
  238. EXPECT_TRUE(cross_origin_source2.has_id());
  239. EXPECT_TRUE(
  240. cross_origin_source2.navigation_metadata().is_renderer_initiated());
  241. EXPECT_EQ(cross_origin_source2.navigation_metadata().same_origin_status(),
  242. ukm::Source::CROSS_ORIGIN);
  243. EXPECT_FALSE(cross_origin_source2.navigation_metadata().is_error_page());
  244. // The fifth navigation was an error page. Make sure it's is_error_page flag
  245. // is set.
  246. EXPECT_EQ(error_url, error_page_source.urls(0).url());
  247. EXPECT_TRUE(error_page_source.has_id());
  248. EXPECT_FALSE(error_page_source.navigation_metadata().is_renderer_initiated());
  249. EXPECT_EQ(error_page_source.navigation_metadata().same_origin_status(),
  250. ukm::Source::UNSET);
  251. EXPECT_TRUE(error_page_source.navigation_metadata().is_error_page());
  252. }