source_url_recorder_browsertest.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  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 <memory>
  5. #include "base/files/scoped_temp_dir.h"
  6. #include "base/strings/stringprintf.h"
  7. #include "base/test/scoped_feature_list.h"
  8. #include "components/ukm/content/source_url_recorder.h"
  9. #include "components/ukm/test_ukm_recorder.h"
  10. #include "content/public/browser/web_contents.h"
  11. #include "content/public/test/browser_test.h"
  12. #include "content/public/test/content_browser_test.h"
  13. #include "content/public/test/content_browser_test_utils.h"
  14. #include "content/public/test/navigation_handle_observer.h"
  15. #include "content/public/test/prerender_test_util.h"
  16. #include "content/shell/browser/shell.h"
  17. #include "content/shell/browser/shell_browser_context.h"
  18. #include "content/shell/browser/shell_download_manager_delegate.h"
  19. #include "services/metrics/public/cpp/ukm_builders.h"
  20. #include "services/metrics/public/cpp/ukm_source.h"
  21. #include "third_party/blink/public/common/features.h"
  22. class SourceUrlRecorderWebContentsObserverBrowserTest
  23. : public content::ContentBrowserTest {
  24. public:
  25. SourceUrlRecorderWebContentsObserverBrowserTest(
  26. const SourceUrlRecorderWebContentsObserverBrowserTest&) = delete;
  27. SourceUrlRecorderWebContentsObserverBrowserTest& operator=(
  28. const SourceUrlRecorderWebContentsObserverBrowserTest&) = delete;
  29. protected:
  30. SourceUrlRecorderWebContentsObserverBrowserTest() {
  31. scoped_feature_list_.InitWithFeatures(
  32. {ukm::kUkmFeature, blink::features::kPortals}, {});
  33. }
  34. ~SourceUrlRecorderWebContentsObserverBrowserTest() override {}
  35. void SetUpOnMainThread() override {
  36. content::ContentBrowserTest::SetUpOnMainThread();
  37. ASSERT_TRUE(embedded_test_server()->Start());
  38. test_ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
  39. ukm::InitializeSourceUrlRecorderForWebContents(shell()->web_contents());
  40. }
  41. const ukm::UkmSource* GetSourceForSourceId(const ukm::SourceId source_id) {
  42. return test_ukm_recorder_->GetSourceForSourceId(source_id);
  43. }
  44. const ukm::UkmSource* GetSourceForNavigationId(int64_t navigation_id) {
  45. CHECK_GT(navigation_id, 0);
  46. return GetSourceForSourceId(ukm::ConvertToSourceId(
  47. navigation_id, ukm::SourceIdType::NAVIGATION_ID));
  48. }
  49. GURL GetAssociatedURLForWebContentsDocument() {
  50. const ukm::UkmSource* src = test_ukm_recorder_->GetSourceForSourceId(
  51. shell()->web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId());
  52. return src ? src->url() : GURL();
  53. }
  54. const ukm::TestAutoSetUkmRecorder& test_ukm_recorder() const {
  55. return *test_ukm_recorder_;
  56. }
  57. private:
  58. base::test::ScopedFeatureList scoped_feature_list_;
  59. std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder_;
  60. };
  61. class SourceUrlRecorderWebContentsObserverDownloadBrowserTest
  62. : public SourceUrlRecorderWebContentsObserverBrowserTest {
  63. protected:
  64. void SetUpOnMainThread() override {
  65. SourceUrlRecorderWebContentsObserverBrowserTest::SetUpOnMainThread();
  66. // Set up a test download directory, in order to prevent prompting for
  67. // handling downloads.
  68. ASSERT_TRUE(downloads_directory_.CreateUniqueTempDir());
  69. content::ShellDownloadManagerDelegate* delegate =
  70. static_cast<content::ShellDownloadManagerDelegate*>(
  71. shell()
  72. ->web_contents()
  73. ->GetBrowserContext()
  74. ->GetDownloadManagerDelegate());
  75. delegate->SetDownloadBehaviorForTesting(downloads_directory_.GetPath());
  76. }
  77. private:
  78. base::ScopedTempDir downloads_directory_;
  79. };
  80. IN_PROC_BROWSER_TEST_F(SourceUrlRecorderWebContentsObserverBrowserTest, Basic) {
  81. using Entry = ukm::builders::DocumentCreated;
  82. GURL url = embedded_test_server()->GetURL("/title1.html");
  83. content::NavigationHandleObserver observer(shell()->web_contents(), url);
  84. EXPECT_TRUE(content::NavigateToURL(shell(), url));
  85. EXPECT_TRUE(observer.has_committed());
  86. const ukm::UkmSource* source =
  87. GetSourceForNavigationId(observer.navigation_id());
  88. EXPECT_NE(nullptr, source);
  89. EXPECT_EQ(url, source->url());
  90. EXPECT_EQ(1u, source->urls().size());
  91. EXPECT_EQ(url, GetAssociatedURLForWebContentsDocument());
  92. // Check we have created a DocumentCreated event.
  93. auto ukm_entries = test_ukm_recorder().GetEntriesByName(Entry::kEntryName);
  94. EXPECT_EQ(1u, ukm_entries.size());
  95. EXPECT_EQ(source->id(), *test_ukm_recorder().GetEntryMetric(
  96. ukm_entries[0], Entry::kNavigationSourceIdName));
  97. EXPECT_EQ(1, *test_ukm_recorder().GetEntryMetric(ukm_entries[0],
  98. Entry::kIsMainFrameName));
  99. EXPECT_NE(source->id(), ukm_entries[0]->source_id);
  100. }
  101. // Test correctness of sources and DocumentCreated entries when a navigation
  102. // leads to creation of main frame and embedded subframe documents.
  103. IN_PROC_BROWSER_TEST_F(SourceUrlRecorderWebContentsObserverBrowserTest,
  104. IgnoreUrlInSubframe) {
  105. using Entry = ukm::builders::DocumentCreated;
  106. GURL main_url = embedded_test_server()->GetURL("/page_with_iframe.html");
  107. GURL subframe_url = embedded_test_server()->GetURL("/title1.html");
  108. content::NavigationHandleObserver main_observer(shell()->web_contents(),
  109. main_url);
  110. content::NavigationHandleObserver subframe_observer(shell()->web_contents(),
  111. subframe_url);
  112. EXPECT_TRUE(content::NavigateToURL(shell(), main_url));
  113. EXPECT_TRUE(main_observer.has_committed());
  114. EXPECT_TRUE(main_observer.is_main_frame());
  115. EXPECT_TRUE(subframe_observer.has_committed());
  116. EXPECT_FALSE(subframe_observer.is_main_frame());
  117. const ukm::UkmSource* navigation_source =
  118. GetSourceForNavigationId(main_observer.navigation_id());
  119. EXPECT_NE(nullptr, navigation_source);
  120. EXPECT_EQ(main_url, navigation_source->url());
  121. EXPECT_EQ(nullptr,
  122. GetSourceForNavigationId(subframe_observer.navigation_id()));
  123. EXPECT_EQ(main_url, GetAssociatedURLForWebContentsDocument());
  124. // Check we have created one DocumentCreated event for each of the frames.
  125. auto ukm_entries = test_ukm_recorder().GetEntriesByName(Entry::kEntryName);
  126. EXPECT_EQ(2u, ukm_entries.size());
  127. // Both events have the same navigation source id, and have different values
  128. // for the kIsMainFrameName metric.
  129. EXPECT_EQ(navigation_source->id(),
  130. *test_ukm_recorder().GetEntryMetric(
  131. ukm_entries[0], Entry::kNavigationSourceIdName));
  132. EXPECT_EQ(navigation_source->id(),
  133. *test_ukm_recorder().GetEntryMetric(
  134. ukm_entries[1], Entry::kNavigationSourceIdName));
  135. EXPECT_NE(*test_ukm_recorder().GetEntryMetric(ukm_entries[0],
  136. Entry::kIsMainFrameName),
  137. *test_ukm_recorder().GetEntryMetric(ukm_entries[1],
  138. Entry::kIsMainFrameName));
  139. // The two DocumentCreated entries have source ids corresponding to the
  140. // document source ids, which are different from the id of the navigation
  141. // source.
  142. EXPECT_NE(ukm_entries[0]->source_id, ukm_entries[1]->source_id);
  143. EXPECT_NE(navigation_source->id(), ukm_entries[0]->source_id);
  144. EXPECT_NE(navigation_source->id(), ukm_entries[1]->source_id);
  145. }
  146. IN_PROC_BROWSER_TEST_F(SourceUrlRecorderWebContentsObserverDownloadBrowserTest,
  147. IgnoreDownload) {
  148. GURL url(embedded_test_server()->GetURL("/download-test1.lib"));
  149. content::NavigationHandleObserver observer(shell()->web_contents(), url);
  150. EXPECT_TRUE(content::NavigateToURLAndExpectNoCommit(shell(), url));
  151. EXPECT_FALSE(observer.has_committed());
  152. EXPECT_TRUE(observer.is_download());
  153. EXPECT_EQ(nullptr, GetSourceForNavigationId(observer.navigation_id()));
  154. EXPECT_EQ(GURL(), GetAssociatedURLForWebContentsDocument());
  155. }
  156. IN_PROC_BROWSER_TEST_F(SourceUrlRecorderWebContentsObserverBrowserTest,
  157. Portal) {
  158. GURL url = embedded_test_server()->GetURL("/title1.html");
  159. {
  160. content::NavigationHandleObserver observer(shell()->web_contents(), url);
  161. EXPECT_TRUE(content::NavigateToURL(shell(), url));
  162. const ukm::UkmSource* source =
  163. GetSourceForNavigationId(observer.navigation_id());
  164. EXPECT_NE(nullptr, source);
  165. EXPECT_EQ(url, source->url());
  166. }
  167. // Create the portal and get its associated WebContents.
  168. content::WebContentsAddedObserver contents_observer;
  169. EXPECT_TRUE(
  170. ExecJs(shell()->web_contents(),
  171. "document.body.appendChild(document.createElement('portal'));"));
  172. content::WebContents* portal_contents = contents_observer.GetWebContents();
  173. EXPECT_TRUE(portal_contents->IsPortal());
  174. EXPECT_NE(portal_contents, shell()->web_contents());
  175. // Register the UKM SourceUrlRecorder for the portal WebContents. Normally
  176. // this would be done automatically via TabHelper registration, but TabHelper
  177. // registration doesn't run in //content.
  178. ukm::InitializeSourceUrlRecorderForWebContents(portal_contents);
  179. // Navigate the portal and wait for the navigation to finish.
  180. GURL portal_url = embedded_test_server()->GetURL("/title2.html");
  181. content::TestNavigationManager portal_nav_manager(portal_contents,
  182. portal_url);
  183. content::NavigationHandleObserver portal_nav_observer(portal_contents,
  184. portal_url);
  185. EXPECT_TRUE(
  186. ExecJs(shell()->web_contents(),
  187. base::StringPrintf("document.querySelector('portal').src = '%s';",
  188. portal_url.spec().c_str())));
  189. portal_nav_manager.WaitForNavigationFinished();
  190. EXPECT_TRUE(portal_nav_observer.has_committed());
  191. EXPECT_FALSE(portal_nav_observer.is_error());
  192. // Ensure no UKM source was created for the portal navigation.
  193. // TODO(crbug/1078355): Ensure the source was created but no URL was recorded.
  194. EXPECT_EQ(nullptr,
  195. GetSourceForNavigationId(portal_nav_observer.navigation_id()));
  196. // Activate the portal, which should cause a URL to be recorded for the
  197. // associated UKM source. Activation is similar to a main frame navigation
  198. // from the user standpoint, as it causes the portal to shift from being in a
  199. // more iframe-like state to becoming the main frame in the associated browser
  200. // tab.
  201. std::string activated_listener = R"(
  202. activated = false;
  203. window.addEventListener('portalactivate', e => {
  204. activated = true;
  205. });
  206. )";
  207. EXPECT_TRUE(ExecJs(portal_contents, activated_listener));
  208. EXPECT_TRUE(ExecJs(shell()->web_contents(),
  209. "document.querySelector('portal').activate()"));
  210. std::string activated_poll = R"(
  211. setInterval(() => {
  212. if (activated)
  213. window.domAutomationController.send(true);
  214. }, 10);
  215. )";
  216. EXPECT_EQ(true, EvalJs(portal_contents, activated_poll,
  217. content::EXECUTE_SCRIPT_USE_MANUAL_REPLY));
  218. // The activated portal contents should be the currently active contents.
  219. EXPECT_EQ(portal_contents, shell()->web_contents());
  220. // TODO(crbug/1078143): enable when UKM sources are created for activated
  221. // portals.
  222. #if 0
  223. // Ensure a UKM source was created for the activated portal, and a URL was
  224. // recorded.
  225. const ukm::UkmSource* portal_source =
  226. GetSourceForNavigationId(portal_nav_observer.navigation_id());
  227. EXPECT_NE(nullptr, portal_source);
  228. EXPECT_EQ(portal_url, portal_source->url());
  229. #endif
  230. {
  231. // Ensure a UKM source is recorded for navigations in an activated portal.
  232. content::NavigationHandleObserver observer(portal_contents, url);
  233. EXPECT_TRUE(content::NavigateToURL(shell(), url));
  234. EXPECT_NE(nullptr, GetSourceForNavigationId(observer.navigation_id()));
  235. }
  236. }
  237. class SourceUrlRecorderWebContentsObserverPrerenderBrowserTest
  238. : public SourceUrlRecorderWebContentsObserverBrowserTest {
  239. public:
  240. SourceUrlRecorderWebContentsObserverPrerenderBrowserTest()
  241. : prerender_helper_(base::BindRepeating(
  242. &SourceUrlRecorderWebContentsObserverPrerenderBrowserTest::
  243. web_contents,
  244. base::Unretained(this))) {}
  245. ~SourceUrlRecorderWebContentsObserverPrerenderBrowserTest() override =
  246. default;
  247. content::test::PrerenderTestHelper* prerender_helper() {
  248. return &prerender_helper_;
  249. }
  250. content::WebContents* web_contents() { return shell()->web_contents(); }
  251. private:
  252. content::test::PrerenderTestHelper prerender_helper_;
  253. };
  254. IN_PROC_BROWSER_TEST_F(SourceUrlRecorderWebContentsObserverPrerenderBrowserTest,
  255. IgnoreUrlInPrerender) {
  256. GURL url = embedded_test_server()->GetURL("/title1.html");
  257. content::NavigationHandleObserver observer(web_contents(), url);
  258. EXPECT_TRUE(content::NavigateToURL(shell(), url));
  259. EXPECT_TRUE(observer.has_committed());
  260. const ukm::UkmSource* source1 =
  261. GetSourceForNavigationId(observer.navigation_id());
  262. EXPECT_NE(nullptr, source1);
  263. EXPECT_EQ(1u, source1->urls().size());
  264. EXPECT_EQ(url, source1->url());
  265. EXPECT_EQ(url, GetAssociatedURLForWebContentsDocument());
  266. // Load a page in the prerendering.
  267. GURL prerender_url =
  268. embedded_test_server()->GetURL("/title1.html?prerendering");
  269. content::NavigationHandleObserver prerender_observer(web_contents(),
  270. prerender_url);
  271. prerender_helper()->AddPrerender(prerender_url);
  272. // Ensure no UKM source was created for the prerendering navigation.
  273. EXPECT_EQ(nullptr,
  274. GetSourceForNavigationId(prerender_observer.navigation_id()));
  275. EXPECT_EQ(url, GetAssociatedURLForWebContentsDocument());
  276. // Navigate the primary page to the URL.
  277. prerender_helper()->NavigatePrimaryPage(prerender_url);
  278. const ukm::UkmSource* source2 =
  279. GetSourceForNavigationId(prerender_observer.navigation_id());
  280. EXPECT_EQ(1u, source2->urls().size());
  281. EXPECT_EQ(prerender_url, source2->url());
  282. GURL expected_ukm_url;
  283. // TODO(crbug.com/1245014): The URL is not assigned yet for prerendering
  284. // UKM source ids, so expect it to not be set.
  285. // expected_ukm_url = prerender_url;
  286. EXPECT_EQ(expected_ukm_url, GetAssociatedURLForWebContentsDocument());
  287. EXPECT_NE(source1, source2);
  288. }