Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / dom_distiller / content / browser / distiller_page_web_contents_browsertest.cc
blobac503d9859ee365381ddfc71d0c4ec8f1342d156
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/strings/utf_string_conversions.h"
9 #include "base/values.h"
10 #include "components/dom_distiller/content/browser/distiller_javascript_utils.h"
11 #include "components/dom_distiller/content/browser/distiller_page_web_contents.h"
12 #include "components/dom_distiller/content/browser/web_contents_main_frame_observer.h"
13 #include "components/dom_distiller/core/distiller_page.h"
14 #include "components/dom_distiller/core/proto/distilled_article.pb.h"
15 #include "components/dom_distiller/core/proto/distilled_page.pb.h"
16 #include "components/dom_distiller/core/viewer.h"
17 #include "content/public/browser/browser_context.h"
18 #include "content/public/browser/navigation_controller.h"
19 #include "content/public/browser/render_frame_host.h"
20 #include "content/public/browser/web_contents_observer.h"
21 #include "content/public/common/isolated_world_ids.h"
22 #include "content/public/test/content_browser_test.h"
23 #include "content/shell/browser/shell.h"
24 #include "grit/components_strings.h"
25 #include "net/test/embedded_test_server/embedded_test_server.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "third_party/dom_distiller_js/dom_distiller.pb.h"
28 #include "ui/base/l10n/l10n_util.h"
29 #include "ui/base/resource/resource_bundle.h"
31 using content::ContentBrowserTest;
32 using testing::ContainsRegex;
33 using testing::HasSubstr;
34 using testing::Not;
36 namespace {
38 // Helper class to know how far in the loading process the current WebContents
39 // has come. It will call the callback either after
40 // DidCommitProvisionalLoadForFrame or DocumentLoadedInFrame is called for the
41 // main frame, based on the value of |wait_for_document_loaded|.
42 class WebContentsMainFrameHelper : public content::WebContentsObserver {
43 public:
44 WebContentsMainFrameHelper(content::WebContents* web_contents,
45 const base::Closure& callback,
46 bool wait_for_document_loaded)
47 : WebContentsObserver(web_contents),
48 callback_(callback),
49 wait_for_document_loaded_(wait_for_document_loaded) {}
51 void DidCommitProvisionalLoadForFrame(
52 content::RenderFrameHost* render_frame_host,
53 const GURL& url,
54 ui::PageTransition transition_type) override {
55 if (wait_for_document_loaded_)
56 return;
57 if (!render_frame_host->GetParent())
58 callback_.Run();
61 void DocumentLoadedInFrame(
62 content::RenderFrameHost* render_frame_host) override {
63 if (wait_for_document_loaded_) {
64 if (!render_frame_host->GetParent())
65 callback_.Run();
69 private:
70 base::Closure callback_;
71 bool wait_for_document_loaded_;
74 } // namespace
76 namespace dom_distiller {
78 const char* kSimpleArticlePath = "/simple_article.html";
79 const char* kVideoArticlePath = "/video_article.html";
81 class DistillerPageWebContentsTest : public ContentBrowserTest {
82 public:
83 // ContentBrowserTest:
84 void SetUpOnMainThread() override {
85 if (!DistillerJavaScriptWorldIdIsSet()) {
86 SetDistillerJavaScriptWorldId(content::ISOLATED_WORLD_ID_CONTENT_END);
88 AddComponentsResources();
89 SetUpTestServer();
90 ContentBrowserTest::SetUpOnMainThread();
93 void DistillPage(const base::Closure& quit_closure, const std::string& url) {
94 distiller_page_->DistillPage(
95 embedded_test_server()->GetURL(url),
96 dom_distiller::proto::DomDistillerOptions(),
97 base::Bind(&DistillerPageWebContentsTest::OnPageDistillationFinished,
98 this, quit_closure));
101 void OnPageDistillationFinished(
102 base::Closure quit_closure,
103 scoped_ptr<proto::DomDistillerResult> distiller_result,
104 bool distillation_successful) {
105 distiller_result_ = distiller_result.Pass();
106 quit_closure.Run();
109 void OnJsExecutionDone(base::Closure callback, const base::Value* value) {
110 js_result_.reset(value->DeepCopy());
111 callback.Run();
114 private:
115 void AddComponentsResources() {
116 base::FilePath pak_file;
117 base::FilePath pak_dir;
118 #if defined(OS_ANDROID)
119 CHECK(PathService::Get(base::DIR_ANDROID_APP_DATA, &pak_dir));
120 pak_dir = pak_dir.Append(FILE_PATH_LITERAL("paks"));
121 #else
122 PathService::Get(base::DIR_MODULE, &pak_dir);
123 #endif // OS_ANDROID
124 pak_file =
125 pak_dir.Append(FILE_PATH_LITERAL("components_tests_resources.pak"));
126 ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath(
127 pak_file, ui::SCALE_FACTOR_NONE);
130 void SetUpTestServer() {
131 base::FilePath path;
132 PathService::Get(base::DIR_SOURCE_ROOT, &path);
133 embedded_test_server()->ServeFilesFromDirectory(
134 path.AppendASCII("components/test/data/dom_distiller"));
135 embedded_test_server()->ServeFilesFromDirectory(
136 path.AppendASCII("components/dom_distiller/core/javascript"));
137 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
140 protected:
141 void RunUseCurrentWebContentsTest(const std::string& url,
142 bool expect_new_web_contents,
143 bool setup_main_frame_observer,
144 bool wait_for_document_loaded);
146 DistillerPageWebContents* distiller_page_;
147 scoped_ptr<proto::DomDistillerResult> distiller_result_;
148 scoped_ptr<base::Value> js_result_;
151 // Use this class to be able to leak the WebContents, which is needed for when
152 // the current WebContents is used for distillation.
153 class TestDistillerPageWebContents : public DistillerPageWebContents {
154 public:
155 TestDistillerPageWebContents(
156 content::BrowserContext* browser_context,
157 const gfx::Size& render_view_size,
158 scoped_ptr<SourcePageHandleWebContents> optional_web_contents_handle,
159 bool expect_new_web_contents)
160 : DistillerPageWebContents(browser_context, render_view_size,
161 optional_web_contents_handle.Pass()),
162 expect_new_web_contents_(expect_new_web_contents),
163 new_web_contents_created_(false) {}
165 void CreateNewWebContents(const GURL& url) override {
166 ASSERT_EQ(true, expect_new_web_contents_);
167 new_web_contents_created_ = true;
168 DistillerPageWebContents::CreateNewWebContents(url);
171 bool new_web_contents_created() { return new_web_contents_created_; }
173 private:
174 bool expect_new_web_contents_;
175 bool new_web_contents_created_;
178 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest, BasicDistillationWorks) {
179 DistillerPageWebContents distiller_page(
180 shell()->web_contents()->GetBrowserContext(),
181 shell()->web_contents()->GetContainerBounds().size(),
182 scoped_ptr<SourcePageHandleWebContents>());
183 distiller_page_ = &distiller_page;
185 base::RunLoop run_loop;
186 DistillPage(run_loop.QuitClosure(), kSimpleArticlePath);
187 run_loop.Run();
189 EXPECT_EQ("Test Page Title", distiller_result_->title());
190 EXPECT_THAT(distiller_result_->distilled_content().html(),
191 HasSubstr("Lorem ipsum"));
192 EXPECT_THAT(distiller_result_->distilled_content().html(),
193 Not(HasSubstr("questionable content")));
194 EXPECT_EQ("", distiller_result_->pagination_info().next_page());
195 EXPECT_EQ("", distiller_result_->pagination_info().prev_page());
198 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest, HandlesRelativeLinks) {
199 DistillerPageWebContents distiller_page(
200 shell()->web_contents()->GetBrowserContext(),
201 shell()->web_contents()->GetContainerBounds().size(),
202 scoped_ptr<SourcePageHandleWebContents>());
203 distiller_page_ = &distiller_page;
205 base::RunLoop run_loop;
206 DistillPage(run_loop.QuitClosure(), kSimpleArticlePath);
207 run_loop.Run();
209 // A relative link should've been updated.
210 EXPECT_THAT(distiller_result_->distilled_content().html(),
211 ContainsRegex("href=\"http://127.0.0.1:.*/relativelink.html\""));
212 EXPECT_THAT(distiller_result_->distilled_content().html(),
213 HasSubstr("href=\"http://www.google.com/absolutelink.html\""));
216 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest, HandlesRelativeImages) {
217 DistillerPageWebContents distiller_page(
218 shell()->web_contents()->GetBrowserContext(),
219 shell()->web_contents()->GetContainerBounds().size(),
220 scoped_ptr<SourcePageHandleWebContents>());
221 distiller_page_ = &distiller_page;
223 base::RunLoop run_loop;
224 DistillPage(run_loop.QuitClosure(), kSimpleArticlePath);
225 run_loop.Run();
227 // A relative link should've been updated.
228 EXPECT_THAT(distiller_result_->distilled_content().html(),
229 ContainsRegex("src=\"http://127.0.0.1:.*/relativeimage.png\""));
230 EXPECT_THAT(distiller_result_->distilled_content().html(),
231 HasSubstr("src=\"http://www.google.com/absoluteimage.png\""));
235 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest, HandlesRelativeVideos) {
236 DistillerPageWebContents distiller_page(
237 shell()->web_contents()->GetBrowserContext(),
238 shell()->web_contents()->GetContainerBounds().size(),
239 scoped_ptr<SourcePageHandleWebContents>());
240 distiller_page_ = &distiller_page;
242 base::RunLoop run_loop;
243 DistillPage(run_loop.QuitClosure(), kVideoArticlePath);
244 run_loop.Run();
246 // A relative source/track should've been updated.
247 EXPECT_THAT(distiller_result_->distilled_content().html(),
248 ContainsRegex("src=\"http://127.0.0.1:.*/relative_video.mp4\""));
249 EXPECT_THAT(
250 distiller_result_->distilled_content().html(),
251 ContainsRegex("src=\"http://127.0.0.1:.*/relative_track_en.vtt\""));
252 EXPECT_THAT(distiller_result_->distilled_content().html(),
253 HasSubstr("src=\"http://www.google.com/absolute_video.ogg\""));
254 EXPECT_THAT(distiller_result_->distilled_content().html(),
255 HasSubstr("src=\"http://www.google.com/absolute_track_fr.vtt\""));
258 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest, VisibilityDetection) {
259 DistillerPageWebContents distiller_page(
260 shell()->web_contents()->GetBrowserContext(),
261 shell()->web_contents()->GetContainerBounds().size(),
262 scoped_ptr<SourcePageHandleWebContents>());
263 distiller_page_ = &distiller_page;
265 // visble_style.html and invisible_style.html only differ by the visibility
266 // internal stylesheet.
269 base::RunLoop run_loop;
270 DistillPage(run_loop.QuitClosure(), "/visible_style.html");
271 run_loop.Run();
272 EXPECT_THAT(distiller_result_->distilled_content().html(),
273 HasSubstr("Lorem ipsum"));
277 base::RunLoop run_loop;
278 DistillPage(run_loop.QuitClosure(), "/invisible_style.html");
279 run_loop.Run();
280 EXPECT_THAT(distiller_result_->distilled_content().html(),
281 Not(HasSubstr("Lorem ipsum")));
285 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest,
286 UsingCurrentWebContentsWrongUrl) {
287 std::string url("/bogus");
288 bool expect_new_web_contents = true;
289 bool setup_main_frame_observer = true;
290 bool wait_for_document_loaded = true;
291 RunUseCurrentWebContentsTest(url,
292 expect_new_web_contents,
293 setup_main_frame_observer,
294 wait_for_document_loaded);
297 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest,
298 UsingCurrentWebContentsNoMainFrameObserver) {
299 std::string url(kSimpleArticlePath);
300 bool expect_new_web_contents = true;
301 bool setup_main_frame_observer = false;
302 bool wait_for_document_loaded = true;
303 RunUseCurrentWebContentsTest(url,
304 expect_new_web_contents,
305 setup_main_frame_observer,
306 wait_for_document_loaded);
309 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest,
310 UsingCurrentWebContentsNotFinishedLoadingYet) {
311 std::string url(kSimpleArticlePath);
312 bool expect_new_web_contents = false;
313 bool setup_main_frame_observer = true;
314 bool wait_for_document_loaded = false;
315 RunUseCurrentWebContentsTest(url,
316 expect_new_web_contents,
317 setup_main_frame_observer,
318 wait_for_document_loaded);
321 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest,
322 UsingCurrentWebContentsReadyForDistillation) {
323 std::string url(kSimpleArticlePath);
324 bool expect_new_web_contents = false;
325 bool setup_main_frame_observer = true;
326 bool wait_for_document_loaded = true;
327 RunUseCurrentWebContentsTest(url,
328 expect_new_web_contents,
329 setup_main_frame_observer,
330 wait_for_document_loaded);
333 void DistillerPageWebContentsTest::RunUseCurrentWebContentsTest(
334 const std::string& url,
335 bool expect_new_web_contents,
336 bool setup_main_frame_observer,
337 bool wait_for_document_loaded) {
338 content::WebContents* current_web_contents = shell()->web_contents();
339 if (setup_main_frame_observer) {
340 dom_distiller::WebContentsMainFrameObserver::CreateForWebContents(
341 current_web_contents);
343 base::RunLoop url_loaded_runner;
344 WebContentsMainFrameHelper main_frame_loaded(current_web_contents,
345 url_loaded_runner.QuitClosure(),
346 wait_for_document_loaded);
347 current_web_contents->GetController().LoadURL(
348 embedded_test_server()->GetURL(url),
349 content::Referrer(),
350 ui::PAGE_TRANSITION_TYPED,
351 std::string());
352 url_loaded_runner.Run();
354 scoped_ptr<SourcePageHandleWebContents> source_page_handle(
355 new SourcePageHandleWebContents(current_web_contents, false));
357 TestDistillerPageWebContents distiller_page(
358 shell()->web_contents()->GetBrowserContext(),
359 shell()->web_contents()->GetContainerBounds().size(),
360 source_page_handle.Pass(),
361 expect_new_web_contents);
362 distiller_page_ = &distiller_page;
364 base::RunLoop run_loop;
365 DistillPage(run_loop.QuitClosure(), kSimpleArticlePath);
366 run_loop.Run();
368 // Sanity check of distillation process.
369 EXPECT_EQ(expect_new_web_contents, distiller_page.new_web_contents_created());
370 EXPECT_EQ("Test Page Title", distiller_result_->title());
373 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest,
374 PageDestroyedBeforeFinishDistillation) {
376 content::WebContents* current_web_contents = shell()->web_contents();
378 dom_distiller::WebContentsMainFrameObserver::CreateForWebContents(
379 current_web_contents);
381 base::RunLoop url_loaded_runner;
382 WebContentsMainFrameHelper main_frame_loaded(current_web_contents,
383 url_loaded_runner.QuitClosure(),
384 true);
385 current_web_contents->GetController().LoadURL(
386 embedded_test_server()->GetURL(kSimpleArticlePath),
387 content::Referrer(),
388 ui::PAGE_TRANSITION_TYPED,
389 std::string());
390 url_loaded_runner.Run();
392 scoped_ptr<SourcePageHandleWebContents> source_page_handle(
393 new SourcePageHandleWebContents(current_web_contents, false));
395 TestDistillerPageWebContents* distiller_page(
396 new TestDistillerPageWebContents(
397 current_web_contents->GetBrowserContext(),
398 current_web_contents->GetContainerBounds().size(),
399 source_page_handle.Pass(),
400 false));
401 distiller_page_ = distiller_page;
403 base::RunLoop run_loop;
404 DistillPage(run_loop.QuitClosure(), kSimpleArticlePath);
406 // It can not crash the loop when returning the result.
407 delete distiller_page_;
409 // Make sure the test ends when it does not crash.
410 base::MessageLoop::current()->PostDelayedTask(
411 FROM_HERE, run_loop.QuitClosure(), base::TimeDelta::FromSeconds(2));
413 run_loop.Run();
416 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest, MarkupInfo) {
417 DistillerPageWebContents distiller_page(
418 shell()->web_contents()->GetBrowserContext(),
419 shell()->web_contents()->GetContainerBounds().size(),
420 scoped_ptr<SourcePageHandleWebContents>());
421 distiller_page_ = &distiller_page;
423 base::RunLoop run_loop;
424 DistillPage(run_loop.QuitClosure(), "/markup_article.html");
425 run_loop.Run();
427 EXPECT_THAT(distiller_result_->distilled_content().html(),
428 HasSubstr("Lorem ipsum"));
429 EXPECT_EQ("Marked-up Markup Test Page Title", distiller_result_->title());
431 const proto::MarkupInfo markup_info = distiller_result_->markup_info();
432 EXPECT_EQ("Marked-up Markup Test Page Title", markup_info.title());
433 EXPECT_EQ("Article", markup_info.type());
434 EXPECT_EQ("http://test/markup.html", markup_info.url());
435 EXPECT_EQ("This page tests Markup Info.", markup_info.description());
436 EXPECT_EQ("Whoever Published", markup_info.publisher());
437 EXPECT_EQ("Copyright 2000-2014 Whoever Copyrighted", markup_info.copyright());
438 EXPECT_EQ("Whoever Authored", markup_info.author());
440 const proto::MarkupArticle markup_article = markup_info.article();
441 EXPECT_EQ("Whatever Section", markup_article.section());
442 EXPECT_EQ("July 23, 2014", markup_article.published_time());
443 EXPECT_EQ("2014-07-23T23:59", markup_article.modified_time());
444 EXPECT_EQ("", markup_article.expiration_time());
445 ASSERT_EQ(1, markup_article.authors_size());
446 EXPECT_EQ("Whoever Authored", markup_article.authors(0));
448 ASSERT_EQ(2, markup_info.images_size());
450 const proto::MarkupImage markup_image1 = markup_info.images(0);
451 EXPECT_EQ("http://test/markup1.jpeg", markup_image1.url());
452 EXPECT_EQ("https://test/markup1.jpeg", markup_image1.secure_url());
453 EXPECT_EQ("jpeg", markup_image1.type());
454 EXPECT_EQ("", markup_image1.caption());
455 EXPECT_EQ(600, markup_image1.width());
456 EXPECT_EQ(400, markup_image1.height());
458 const proto::MarkupImage markup_image2 = markup_info.images(1);
459 EXPECT_EQ("http://test/markup2.gif", markup_image2.url());
460 EXPECT_EQ("https://test/markup2.gif", markup_image2.secure_url());
461 EXPECT_EQ("gif", markup_image2.type());
462 EXPECT_EQ("", markup_image2.caption());
463 EXPECT_EQ(1000, markup_image2.width());
464 EXPECT_EQ(600, markup_image2.height());
467 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest,
468 TestNoContentDoesNotCrash) {
469 const std::string no_content =
470 l10n_util::GetStringUTF8(IDS_DOM_DISTILLER_VIEWER_NO_DATA_CONTENT);
472 { // Test zero pages.
473 scoped_ptr<DistilledArticleProto> article_proto(
474 new DistilledArticleProto());
475 std::string js = viewer::GetUnsafeArticleContentJs(article_proto.get());
476 EXPECT_THAT(js, HasSubstr(no_content));
479 { // Test empty content.
480 scoped_ptr<DistilledArticleProto> article_proto(
481 new DistilledArticleProto());
482 (*(article_proto->add_pages())).set_html("");
483 std::string js = viewer::GetUnsafeArticleContentJs(article_proto.get());
484 EXPECT_THAT(js, HasSubstr(no_content));
488 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest,
489 TestPinch) {
490 // Load the test file in content shell and wait until it has fully loaded.
491 content::WebContents* web_contents = shell()->web_contents();
492 dom_distiller::WebContentsMainFrameObserver::CreateForWebContents(
493 web_contents);
494 base::RunLoop url_loaded_runner;
495 WebContentsMainFrameHelper main_frame_loaded(web_contents,
496 url_loaded_runner.QuitClosure(),
497 true);
498 web_contents->GetController().LoadURL(
499 embedded_test_server()->GetURL("/pinch_tester.html"),
500 content::Referrer(),
501 ui::PAGE_TRANSITION_TYPED,
502 std::string());
503 url_loaded_runner.Run();
505 // Execute the JS to run the tests, and wait until it has finished.
506 base::RunLoop run_loop;
507 web_contents->GetMainFrame()->ExecuteJavaScriptForTests(
508 base::UTF8ToUTF16("(function() {return pinchtest.run();})();"),
509 base::Bind(&DistillerPageWebContentsTest::OnJsExecutionDone,
510 base::Unretained(this), run_loop.QuitClosure()));
511 run_loop.Run();
513 // Convert to dictionary and parse the results.
514 const base::DictionaryValue* dict;
515 ASSERT_TRUE(js_result_);
516 ASSERT_TRUE(js_result_->GetAsDictionary(&dict));
518 ASSERT_TRUE(dict->HasKey("success"));
519 bool success;
520 ASSERT_TRUE(dict->GetBoolean("success", &success));
521 EXPECT_TRUE(success);
524 } // namespace dom_distiller