| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/run_loop.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/prerender/prerender_final_status.h" |
| #include "chrome/browser/prerender/prerender_handle.h" |
| #include "chrome/browser/prerender/prerender_manager.h" |
| #include "chrome/browser/prerender/prerender_manager_factory.h" |
| #include "chrome/browser/prerender/prerender_test_utils.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_io_data.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/data_use_measurement/core/data_use_user_data.h" |
| #include "content/public/browser/content_browser_client.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/common/referrer.h" |
| #include "content/public/test/browser_test_base.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/test/embedded_test_server/http_request.h" |
| #include "net/test/embedded_test_server/http_response.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| class LazyLoadBrowserTest : public InProcessBrowserTest { |
| protected: |
| void SetUpOnMainThread() override { |
| InProcessBrowserTest::SetUpOnMainThread(); |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| } |
| void SetUp() override { |
| scoped_feature_list_.InitWithFeaturesAndParameters( |
| {{features::kLazyImageLoading, |
| {{"automatic-lazy-load-images-enabled", "true"}, |
| {"restrict-lazy-load-images-to-data-saver-only", "false"}}}, |
| {features::kLazyFrameLoading, |
| {{"automatic-lazy-load-frames-enabled", "true"}, |
| {"restrict-lazy-load-frames-to-data-saver-only", "false"}}}}, |
| {}); |
| InProcessBrowserTest::SetUp(); |
| } |
| |
| void ScrollToAndWaitForScroll(unsigned int scroll_offset) { |
| ASSERT_TRUE(content::ExecuteScript( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| base::StringPrintf("window.scrollTo(0, %d);", scroll_offset))); |
| content::RenderFrameSubmissionObserver observer( |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| observer.WaitForScrollOffset(gfx::Vector2dF(0, scroll_offset)); |
| } |
| |
| // Sets up test pages with in-viewport and below-viewport cross-origin frames. |
| void SetUpLazyLoadFrameTestPage() { |
| cross_origin_server_.ServeFilesFromSourceDirectory(GetChromeTestDataDir()); |
| ASSERT_TRUE(cross_origin_server_.Start()); |
| |
| embedded_test_server()->RegisterRequestHandler(base::Bind( |
| [](uint16_t cross_origin_port, |
| const net::test_server::HttpRequest& request) |
| -> std::unique_ptr<net::test_server::HttpResponse> { |
| auto response = |
| std::make_unique<net::test_server::BasicHttpResponse>(); |
| if (request.relative_url == "/mainpage.html") { |
| response->set_content(base::StringPrintf( |
| R"HTML( |
| <body onload="on_load('document')"> |
| <script> |
| function on_load(msg) { |
| console.log("LAZY_LOAD CONSOLE", msg, "ON_LOAD FOR TEST"); |
| } |
| </script> |
| |
| <iframe src="http://bar.com:%d/simple.html?auto" |
| width="100" height="100" |
| onload="on_load('in-viewport iframe')"></iframe> |
| <iframe src="http://bar.com:%d/simple.html?lazy" |
| width="100" height="100" loading="lazy" |
| onload="on_load('in-viewport loading=lazy iframe')"> |
| </iframe> |
| <iframe src="http://bar.com:%d/simple.html?eager" |
| width="100" height="100" loading="eager" |
| onload="on_load('in-viewport loading=eager iframe')"> |
| </iframe> |
| |
| <div style="height:11000px;"></div> |
| Below the viewport cross-origin iframe <br> |
| <iframe src="http://bar.com:%d/simple.html?auto&belowviewport" |
| width="100" height="100" |
| onload="on_load('below-viewport iframe')"></iframe> |
| <iframe src="http://bar.com:%d/simple.html?lazy&belowviewport" |
| width="100" height="100" loading="lazy" |
| onload="on_load('below-viewport loading=lazy iframe')"> |
| </iframe> |
| <iframe src="http://bar.com:%d/simple.html?eager&belowviewport" |
| width="100" height="100" loading="eager" |
| onload="on_load('below-viewport loading=eager iframe')"> |
| </iframe> |
| </body>)HTML", |
| cross_origin_port, cross_origin_port, cross_origin_port, |
| cross_origin_port, cross_origin_port, cross_origin_port)); |
| } |
| return response; |
| }, |
| cross_origin_server_.port())); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| } |
| |
| void SetUpURLMonitor() { |
| embedded_test_server()->RegisterRequestMonitor(base::Bind( |
| [](std::vector<std::string>* request_paths, |
| const net::test_server::HttpRequest& request) { |
| request_paths->push_back(request.relative_url); |
| }, |
| &request_paths_)); |
| } |
| |
| const std::vector<std::string>& request_paths() const { |
| return request_paths_; |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| net::EmbeddedTestServer cross_origin_server_; |
| std::vector<std::string> request_paths_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(LazyLoadBrowserTest, CSSBackgroundImageDeferred) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| base::HistogramTester histogram_tester; |
| ui_test_utils::NavigateToURL( |
| browser(), |
| embedded_test_server()->GetURL("/lazyload/css-background-image.html")); |
| |
| base::RunLoop().RunUntilIdle(); |
| // Navigate away to finish the histogram recording. |
| ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)); |
| |
| // Verify that nothing is recorded for the image bucket. |
| EXPECT_GE(0, histogram_tester.GetBucketCount( |
| "DataUse.ContentType.UserTrafficKB", |
| data_use_measurement::DataUseUserData::IMAGE)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LazyLoadBrowserTest, CSSPseudoBackgroundImageLoaded) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| base::HistogramTester histogram_tester; |
| ui_test_utils::NavigateToURL( |
| browser(), embedded_test_server()->GetURL( |
| "/lazyload/css-pseudo-background-image.html")); |
| |
| base::RunLoop().RunUntilIdle(); |
| // Navigate away to finish the histogram recording. |
| ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)); |
| |
| // Verify that the image bucket has substantial kilobytes recorded. |
| EXPECT_GE(30 /* KB */, histogram_tester.GetBucketCount( |
| "DataUse.ContentType.UserTrafficKB", |
| data_use_measurement::DataUseUserData::IMAGE)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LazyLoadBrowserTest, |
| LazyLoadImage_DeferredAndLoadedOnScroll) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL test_url(embedded_test_server()->GetURL("/lazyload/img.html")); |
| |
| auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); |
| content::ConsoleObserverDelegate console_observer( |
| contents, "LAZY_LOAD CONSOLE * ON_LOAD FOR TEST"); |
| contents->SetDelegate(&console_observer); |
| ui_test_utils::NavigateToURL(browser(), test_url); |
| |
| // Wait for the four images (3 in-viewport, 1 eager below-viewport) and the |
| // document to load. |
| while (console_observer.messages().size() < 5) { |
| base::RunLoop().RunUntilIdle(); |
| } |
| console_observer.Wait(); |
| |
| EXPECT_THAT( |
| console_observer.messages(), |
| testing::UnorderedElementsAre( |
| "LAZY_LOAD CONSOLE document ON_LOAD FOR TEST", |
| "LAZY_LOAD CONSOLE in-viewport img ON_LOAD FOR TEST", |
| "LAZY_LOAD CONSOLE in-viewport loading=lazy img ON_LOAD FOR TEST", |
| "LAZY_LOAD CONSOLE in-viewport loading=eager img ON_LOAD FOR TEST", |
| "LAZY_LOAD CONSOLE below-viewport loading=eager img ON_LOAD FOR " |
| "TEST")); |
| |
| // Scroll down and verify the below-viewport images load. |
| ScrollToAndWaitForScroll(10000); |
| while (console_observer.messages().size() < 7) { |
| base::RunLoop().RunUntilIdle(); |
| } |
| const auto messages = console_observer.messages(); |
| EXPECT_THAT(messages, |
| testing::Contains( |
| "LAZY_LOAD CONSOLE below-viewport img ON_LOAD FOR TEST")); |
| EXPECT_THAT(messages, testing::Contains("LAZY_LOAD CONSOLE below-viewport " |
| "loading=lazy img ON_LOAD FOR TEST")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LazyLoadBrowserTest, |
| LazyLoadFrame_DeferredAndLoadedOnScroll) { |
| SetUpLazyLoadFrameTestPage(); |
| GURL test_url(embedded_test_server()->GetURL("/mainpage.html")); |
| |
| auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); |
| content::ConsoleObserverDelegate console_observer( |
| contents, "LAZY_LOAD CONSOLE * ON_LOAD FOR TEST"); |
| contents->SetDelegate(&console_observer); |
| ui_test_utils::NavigateToURL(browser(), test_url); |
| |
| // Wait for the four iframes (3 in-viewport, 1 eager below-viewport) and the |
| // document to load. |
| while (console_observer.messages().size() < 5) { |
| base::RunLoop().RunUntilIdle(); |
| } |
| console_observer.Wait(); |
| |
| EXPECT_THAT( |
| console_observer.messages(), |
| testing::UnorderedElementsAre( |
| "LAZY_LOAD CONSOLE document ON_LOAD FOR TEST", |
| "LAZY_LOAD CONSOLE in-viewport iframe ON_LOAD FOR TEST", |
| "LAZY_LOAD CONSOLE in-viewport loading=lazy iframe ON_LOAD FOR TEST", |
| "LAZY_LOAD CONSOLE in-viewport loading=eager iframe ON_LOAD FOR TEST", |
| "LAZY_LOAD CONSOLE below-viewport loading=eager iframe ON_LOAD FOR " |
| "TEST")); |
| |
| // Scroll down and verify the below-viewport iframes load. |
| ScrollToAndWaitForScroll(10000); |
| while (console_observer.messages().size() < 7) { |
| base::RunLoop().RunUntilIdle(); |
| } |
| const auto messages = console_observer.messages(); |
| EXPECT_THAT(messages, |
| testing::Contains( |
| "LAZY_LOAD CONSOLE below-viewport iframe ON_LOAD FOR TEST")); |
| EXPECT_THAT(messages, |
| testing::Contains("LAZY_LOAD CONSOLE below-viewport " |
| "loading=lazy iframe ON_LOAD FOR TEST")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LazyLoadBrowserTest, |
| LazyLoadImage_DisabledInBackgroundTab) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL test_url(embedded_test_server()->GetURL("/lazyload/img.html")); |
| |
| auto* background_contents = browser()->OpenURL(content::OpenURLParams( |
| test_url, content::Referrer(), WindowOpenDisposition::NEW_BACKGROUND_TAB, |
| ui::PAGE_TRANSITION_TYPED, false)); |
| content::ConsoleObserverDelegate console_observer( |
| background_contents, "LAZY_LOAD CONSOLE * ON_LOAD FOR TEST"); |
| background_contents->SetDelegate(&console_observer); |
| |
| // Wait for the four images and the document to load. |
| while (console_observer.messages().size() < 7) { |
| base::RunLoop().RunUntilIdle(); |
| } |
| console_observer.Wait(); |
| |
| EXPECT_THAT( |
| console_observer.messages(), |
| testing::UnorderedElementsAre( |
| "LAZY_LOAD CONSOLE document ON_LOAD FOR TEST", |
| "LAZY_LOAD CONSOLE in-viewport img ON_LOAD FOR TEST", |
| "LAZY_LOAD CONSOLE in-viewport loading=lazy img ON_LOAD FOR TEST", |
| "LAZY_LOAD CONSOLE in-viewport loading=eager img ON_LOAD FOR TEST", |
| "LAZY_LOAD CONSOLE below-viewport img ON_LOAD FOR TEST", |
| "LAZY_LOAD CONSOLE below-viewport loading=lazy img ON_LOAD FOR TEST", |
| "LAZY_LOAD CONSOLE below-viewport loading=eager img ON_LOAD FOR " |
| "TEST")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LazyLoadBrowserTest, |
| LazyLoadFrame_DisabledInBackgroundTab) { |
| SetUpLazyLoadFrameTestPage(); |
| GURL test_url(embedded_test_server()->GetURL("/mainpage.html")); |
| |
| auto* background_contents = browser()->OpenURL(content::OpenURLParams( |
| test_url, content::Referrer(), WindowOpenDisposition::NEW_BACKGROUND_TAB, |
| ui::PAGE_TRANSITION_TYPED, false)); |
| content::ConsoleObserverDelegate console_observer( |
| background_contents, "LAZY_LOAD CONSOLE * ON_LOAD FOR TEST"); |
| background_contents->SetDelegate(&console_observer); |
| |
| // Wait for the four iframes and the document to load. |
| while (console_observer.messages().size() < 7) { |
| base::RunLoop().RunUntilIdle(); |
| } |
| console_observer.Wait(); |
| |
| EXPECT_THAT( |
| console_observer.messages(), |
| testing::UnorderedElementsAre( |
| "LAZY_LOAD CONSOLE document ON_LOAD FOR TEST", |
| "LAZY_LOAD CONSOLE in-viewport iframe ON_LOAD FOR TEST", |
| "LAZY_LOAD CONSOLE in-viewport loading=lazy iframe ON_LOAD FOR TEST", |
| "LAZY_LOAD CONSOLE in-viewport loading=eager iframe ON_LOAD FOR TEST", |
| "LAZY_LOAD CONSOLE below-viewport iframe ON_LOAD FOR TEST", |
| "LAZY_LOAD CONSOLE below-viewport loading=lazy iframe ON_LOAD FOR " |
| "TEST", |
| "LAZY_LOAD CONSOLE below-viewport loading=eager iframe ON_LOAD FOR " |
| "TEST")); |
| } |
| |
| class LazyLoadPrerenderBrowserTest : public LazyLoadBrowserTest { |
| public: |
| void SetUpOnMainThread() override { |
| LazyLoadBrowserTest::SetUpOnMainThread(); |
| |
| prerender::PrerenderManager::SetMode( |
| prerender::PrerenderManager::PRERENDER_MODE_NOSTATE_PREFETCH); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(LazyLoadPrerenderBrowserTest, ImagesIgnored) { |
| SetUpURLMonitor(); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL test_url(embedded_test_server()->GetURL("/lazyload/img.html")); |
| |
| prerender::PrerenderManager* prerender_manager = |
| prerender::PrerenderManagerFactory::GetForBrowserContext( |
| browser()->profile()); |
| ASSERT_TRUE(prerender_manager); |
| |
| prerender::test_utils::TestPrerenderContentsFactory* |
| prerender_contents_factory = |
| new prerender::test_utils::TestPrerenderContentsFactory(); |
| prerender_manager->SetPrerenderContentsFactoryForTest( |
| prerender_contents_factory); |
| |
| content::SessionStorageNamespace* storage_namespace = |
| browser() |
| ->tab_strip_model() |
| ->GetActiveWebContents() |
| ->GetController() |
| .GetDefaultSessionStorageNamespace(); |
| ASSERT_TRUE(storage_namespace); |
| |
| std::unique_ptr<prerender::test_utils::TestPrerender> test_prerender = |
| prerender_contents_factory->ExpectPrerenderContents( |
| prerender::FINAL_STATUS_NOSTATE_PREFETCH_FINISHED); |
| |
| std::unique_ptr<prerender::PrerenderHandle> prerender_handle = |
| prerender_manager->AddPrerenderFromOmnibox(test_url, storage_namespace, |
| gfx::Size(640, 480)); |
| |
| ASSERT_EQ(prerender_handle->contents(), test_prerender->contents()); |
| |
| test_prerender->WaitForStop(); |
| EXPECT_THAT(request_paths(), |
| testing::UnorderedElementsAre( |
| "/lazyload/img.html", "/lazyload/images/fruit1.jpg?auto", |
| "/lazyload/images/fruit1.jpg?lazy", |
| "/lazyload/images/fruit1.jpg?eager", |
| "/lazyload/images/fruit2.jpg?auto", |
| "/lazyload/images/fruit2.jpg?lazy", |
| "/lazyload/images/fruit2.jpg?eager")); |
| } |