1 // Copyright 2013 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.
5 #include "base/memory/weak_ptr.h"
6 #include "base/path_service.h"
7 #include "base/run_loop.h"
8 #include "base/values.h"
9 #include "components/dom_distiller/content/distiller_page_web_contents.h"
10 #include "components/dom_distiller/content/web_contents_main_frame_observer.h"
11 #include "components/dom_distiller/core/distiller_page.h"
12 #include "content/public/browser/browser_context.h"
13 #include "content/public/browser/navigation_controller.h"
14 #include "content/public/browser/render_frame_host.h"
15 #include "content/public/browser/web_contents_observer.h"
16 #include "content/public/test/content_browser_test.h"
17 #include "content/shell/browser/shell.h"
18 #include "grit/component_resources.h"
19 #include "net/test/embedded_test_server/embedded_test_server.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 #include "ui/base/resource/resource_bundle.h"
23 using content::ContentBrowserTest;
24 using testing::ContainsRegex;
25 using testing::HasSubstr;
28 namespace dom_distiller {
30 const char* kSimpleArticlePath = "/simple_article.html";
31 const char* kVideoArticlePath = "/video_article.html";
33 class DistillerPageWebContentsTest : public ContentBrowserTest {
35 // ContentBrowserTest:
36 virtual void SetUpOnMainThread() OVERRIDE {
37 AddComponentsResources();
39 ContentBrowserTest::SetUpOnMainThread();
42 void DistillPage(const base::Closure& quit_closure, const std::string& url) {
43 quit_closure_ = quit_closure;
44 distiller_page_->DistillPage(
45 embedded_test_server()->GetURL(url),
46 dom_distiller::proto::DomDistillerOptions(),
47 base::Bind(&DistillerPageWebContentsTest::OnPageDistillationFinished,
51 void OnPageDistillationFinished(scoped_ptr<DistilledPageInfo> distilled_page,
52 bool distillation_successful) {
53 page_info_ = distilled_page.Pass();
58 void AddComponentsResources() {
59 base::FilePath pak_file;
60 base::FilePath pak_dir;
61 PathService::Get(base::DIR_MODULE, &pak_dir);
62 pak_file = pak_dir.Append(FILE_PATH_LITERAL("components_resources.pak"));
63 ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath(
64 pak_file, ui::SCALE_FACTOR_NONE);
67 void SetUpTestServer() {
69 PathService::Get(base::DIR_SOURCE_ROOT, &path);
70 path = path.AppendASCII("components/test/data/dom_distiller");
71 embedded_test_server()->ServeFilesFromDirectory(path);
72 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
76 void RunUseCurrentWebContentsTest(const std::string& url,
77 bool expect_new_web_contents,
78 bool setup_main_frame_observer,
79 bool wait_for_document_loaded);
81 DistillerPageWebContents* distiller_page_;
82 base::Closure quit_closure_;
83 scoped_ptr<DistilledPageInfo> page_info_;
86 // Use this class to be able to leak the WebContents, which is needed for when
87 // the current WebContents is used for distillation.
88 class TestDistillerPageWebContents : public DistillerPageWebContents {
90 TestDistillerPageWebContents(
91 content::BrowserContext* browser_context,
92 scoped_ptr<SourcePageHandleWebContents> optional_web_contents_handle,
93 bool expect_new_web_contents)
94 : DistillerPageWebContents(browser_context,
95 optional_web_contents_handle.Pass()),
96 expect_new_web_contents_(expect_new_web_contents),
97 new_web_contents_created_(false) {}
99 virtual void CreateNewWebContents(const GURL& url) OVERRIDE {
100 ASSERT_EQ(true, expect_new_web_contents_);
101 new_web_contents_created_ = true;
102 // DistillerPageWebContents::CreateNewWebContents resets the scoped_ptr to
103 // the WebContents, so intentionally leak WebContents here, since it is
104 // owned by the shell.
105 content::WebContents* web_contents = web_contents_.release();
106 web_contents->GetLastCommittedURL();
107 DistillerPageWebContents::CreateNewWebContents(url);
110 virtual ~TestDistillerPageWebContents() {
111 if (!expect_new_web_contents_) {
112 // Intentionally leaking WebContents, since it is owned by the shell.
113 content::WebContents* web_contents = web_contents_.release();
114 web_contents->GetLastCommittedURL();
118 bool new_web_contents_created() { return new_web_contents_created_; }
121 bool expect_new_web_contents_;
122 bool new_web_contents_created_;
125 // Helper class to know how far in the loading process the current WebContents
126 // has come. It will call the callback either after
127 // DidCommitProvisionalLoadForFrame or DocumentLoadedInFrame is called for the
128 // main frame, based on the value of |wait_for_document_loaded|.
129 class WebContentsMainFrameHelper : public content::WebContentsObserver {
131 WebContentsMainFrameHelper(content::WebContents* web_contents,
132 const base::Closure& callback,
133 bool wait_for_document_loaded)
134 : web_contents_(web_contents),
136 wait_for_document_loaded_(wait_for_document_loaded) {
137 content::WebContentsObserver::Observe(web_contents);
140 virtual void DidCommitProvisionalLoadForFrame(
142 const base::string16& frame_unique_name,
145 content::PageTransition transition_type,
146 content::RenderViewHost* render_view_host) OVERRIDE {
147 if (wait_for_document_loaded_)
153 virtual void DocumentLoadedInFrame(
155 content::RenderViewHost* render_view_host) OVERRIDE {
156 if (wait_for_document_loaded_) {
158 frame_id == web_contents_->GetMainFrame()->GetRoutingID()) {
165 content::WebContents* web_contents_;
166 base::Closure callback_;
167 bool wait_for_document_loaded_;
170 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest, BasicDistillationWorks) {
171 DistillerPageWebContents distiller_page(
172 shell()->web_contents()->GetBrowserContext(),
173 scoped_ptr<SourcePageHandleWebContents>());
174 distiller_page_ = &distiller_page;
176 base::RunLoop run_loop;
177 DistillPage(run_loop.QuitClosure(), kSimpleArticlePath);
180 EXPECT_EQ("Test Page Title", page_info_.get()->title);
181 EXPECT_THAT(page_info_.get()->html, HasSubstr("Lorem ipsum"));
182 EXPECT_THAT(page_info_.get()->html, Not(HasSubstr("questionable content")));
183 EXPECT_EQ("", page_info_.get()->next_page_url);
184 EXPECT_EQ("", page_info_.get()->prev_page_url);
187 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest, HandlesRelativeLinks) {
188 DistillerPageWebContents distiller_page(
189 shell()->web_contents()->GetBrowserContext(),
190 scoped_ptr<SourcePageHandleWebContents>());
191 distiller_page_ = &distiller_page;
193 base::RunLoop run_loop;
194 DistillPage(run_loop.QuitClosure(), kSimpleArticlePath);
197 // A relative link should've been updated.
198 EXPECT_THAT(page_info_.get()->html,
199 ContainsRegex("href=\"http://127.0.0.1:.*/relativelink.html\""));
200 EXPECT_THAT(page_info_.get()->html,
201 HasSubstr("href=\"http://www.google.com/absolutelink.html\""));
204 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest, HandlesRelativeImages) {
205 DistillerPageWebContents distiller_page(
206 shell()->web_contents()->GetBrowserContext(),
207 scoped_ptr<SourcePageHandleWebContents>());
208 distiller_page_ = &distiller_page;
210 base::RunLoop run_loop;
211 DistillPage(run_loop.QuitClosure(), kSimpleArticlePath);
214 // A relative link should've been updated.
215 EXPECT_THAT(page_info_.get()->html,
216 ContainsRegex("src=\"http://127.0.0.1:.*/relativeimage.png\""));
217 EXPECT_THAT(page_info_.get()->html,
218 HasSubstr("src=\"http://www.google.com/absoluteimage.png\""));
222 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest, HandlesRelativeVideos) {
223 DistillerPageWebContents distiller_page(
224 shell()->web_contents()->GetBrowserContext(),
225 scoped_ptr<SourcePageHandleWebContents>());
226 distiller_page_ = &distiller_page;
228 base::RunLoop run_loop;
229 DistillPage(run_loop.QuitClosure(), kVideoArticlePath);
232 // A relative source/track should've been updated.
234 page_info_.get()->html,
235 ContainsRegex("src=\"http://127.0.0.1:.*/relative_video.mp4\""));
237 page_info_.get()->html,
238 ContainsRegex("src=\"http://127.0.0.1:.*/relative_track_en.vtt\""));
240 page_info_.get()->html,
241 HasSubstr("src=\"http://www.google.com/absolute_video.ogg\""));
243 page_info_.get()->html,
244 HasSubstr("src=\"http://www.google.com/absolute_track_fr.vtt\""));
247 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest, VisibilityDetection) {
248 DistillerPageWebContents distiller_page(
249 shell()->web_contents()->GetBrowserContext(),
250 scoped_ptr<SourcePageHandleWebContents>());
251 distiller_page_ = &distiller_page;
253 // visble_style.html and invisible_style.html only differ by the visibility
254 // internal stylesheet.
257 base::RunLoop run_loop;
258 DistillPage(run_loop.QuitClosure(), "/visible_style.html");
260 EXPECT_THAT(page_info_.get()->html, HasSubstr("Lorem ipsum"));
264 base::RunLoop run_loop;
265 DistillPage(run_loop.QuitClosure(), "/invisible_style.html");
267 EXPECT_THAT(page_info_.get()->html, Not(HasSubstr("Lorem ipsum")));
271 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest,
272 UsingCurrentWebContentsWrongUrl) {
273 std::string url("/bogus");
274 bool expect_new_web_contents = true;
275 bool setup_main_frame_observer = true;
276 bool wait_for_document_loaded = true;
277 RunUseCurrentWebContentsTest(url,
278 expect_new_web_contents,
279 setup_main_frame_observer,
280 wait_for_document_loaded);
283 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest,
284 UsingCurrentWebContentsNoMainFrameObserver) {
285 std::string url(kSimpleArticlePath);
286 bool expect_new_web_contents = true;
287 bool setup_main_frame_observer = false;
288 bool wait_for_document_loaded = true;
289 RunUseCurrentWebContentsTest(url,
290 expect_new_web_contents,
291 setup_main_frame_observer,
292 wait_for_document_loaded);
295 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest,
296 UsingCurrentWebContentsNotFinishedLoadingYet) {
297 std::string url(kSimpleArticlePath);
298 bool expect_new_web_contents = false;
299 bool setup_main_frame_observer = true;
300 bool wait_for_document_loaded = false;
301 RunUseCurrentWebContentsTest(url,
302 expect_new_web_contents,
303 setup_main_frame_observer,
304 wait_for_document_loaded);
307 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest,
308 UsingCurrentWebContentsReadyForDistillation) {
309 std::string url(kSimpleArticlePath);
310 bool expect_new_web_contents = false;
311 bool setup_main_frame_observer = true;
312 bool wait_for_document_loaded = true;
313 RunUseCurrentWebContentsTest(url,
314 expect_new_web_contents,
315 setup_main_frame_observer,
316 wait_for_document_loaded);
319 void DistillerPageWebContentsTest::RunUseCurrentWebContentsTest(
320 const std::string& url,
321 bool expect_new_web_contents,
322 bool setup_main_frame_observer,
323 bool wait_for_document_loaded) {
324 content::WebContents* current_web_contents = shell()->web_contents();
325 if (setup_main_frame_observer) {
326 dom_distiller::WebContentsMainFrameObserver::CreateForWebContents(
327 current_web_contents);
329 base::RunLoop url_loaded_runner;
330 WebContentsMainFrameHelper main_frame_loaded(current_web_contents,
331 url_loaded_runner.QuitClosure(),
332 wait_for_document_loaded);
333 current_web_contents->GetController().LoadURL(
334 embedded_test_server()->GetURL(url),
336 content::PAGE_TRANSITION_TYPED,
338 url_loaded_runner.Run();
340 scoped_ptr<content::WebContents> old_web_contents_sptr(current_web_contents);
341 scoped_ptr<SourcePageHandleWebContents> source_page_handle(
342 new SourcePageHandleWebContents(old_web_contents_sptr.Pass()));
344 TestDistillerPageWebContents distiller_page(
345 shell()->web_contents()->GetBrowserContext(),
346 source_page_handle.Pass(),
347 expect_new_web_contents);
348 distiller_page_ = &distiller_page;
350 base::RunLoop run_loop;
351 DistillPage(run_loop.QuitClosure(), kSimpleArticlePath);
354 // Sanity check of distillation process.
355 EXPECT_EQ(expect_new_web_contents, distiller_page.new_web_contents_created());
356 EXPECT_EQ("Test Page Title", page_info_.get()->title);
359 } // namespace dom_distiller