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";
32 class DistillerPageWebContentsTest
: public ContentBrowserTest
{
34 // ContentBrowserTest:
35 virtual void SetUpOnMainThread() OVERRIDE
{
36 AddComponentsResources();
38 ContentBrowserTest::SetUpOnMainThread();
41 void DistillPage(const base::Closure
& quit_closure
, const std::string
& url
) {
42 quit_closure_
= quit_closure
;
43 distiller_page_
->DistillPage(
44 embedded_test_server()->GetURL(url
),
45 dom_distiller::proto::DomDistillerOptions(),
46 base::Bind(&DistillerPageWebContentsTest::OnPageDistillationFinished
,
50 void OnPageDistillationFinished(scoped_ptr
<DistilledPageInfo
> distilled_page
,
51 bool distillation_successful
) {
52 page_info_
= distilled_page
.Pass();
57 void AddComponentsResources() {
58 base::FilePath pak_file
;
59 base::FilePath pak_dir
;
60 PathService::Get(base::DIR_MODULE
, &pak_dir
);
61 pak_file
= pak_dir
.Append(FILE_PATH_LITERAL("components_resources.pak"));
62 ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath(
63 pak_file
, ui::SCALE_FACTOR_NONE
);
66 void SetUpTestServer() {
68 PathService::Get(base::DIR_SOURCE_ROOT
, &path
);
69 path
= path
.AppendASCII("components/test/data/dom_distiller");
70 embedded_test_server()->ServeFilesFromDirectory(path
);
71 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
75 void RunUseCurrentWebContentsTest(const std::string
& url
,
76 bool expect_new_web_contents
,
77 bool setup_main_frame_observer
,
78 bool wait_for_document_loaded
);
80 DistillerPageWebContents
* distiller_page_
;
81 base::Closure quit_closure_
;
82 scoped_ptr
<DistilledPageInfo
> page_info_
;
85 // Use this class to be able to leak the WebContents, which is needed for when
86 // the current WebContents is used for distillation.
87 class TestDistillerPageWebContents
: public DistillerPageWebContents
{
89 TestDistillerPageWebContents(
90 content::BrowserContext
* browser_context
,
91 scoped_ptr
<SourcePageHandleWebContents
> optional_web_contents_handle
,
92 bool expect_new_web_contents
)
93 : DistillerPageWebContents(browser_context
,
94 optional_web_contents_handle
.Pass()),
95 expect_new_web_contents_(expect_new_web_contents
),
96 new_web_contents_created_(false) {}
98 virtual void CreateNewWebContents(const GURL
& url
) OVERRIDE
{
99 ASSERT_EQ(true, expect_new_web_contents_
);
100 new_web_contents_created_
= true;
101 // DistillerPageWebContents::CreateNewWebContents resets the scoped_ptr to
102 // the WebContents, so intentionally leak WebContents here, since it is
103 // owned by the shell.
104 content::WebContents
* web_contents
= web_contents_
.release();
105 web_contents
->GetLastCommittedURL();
106 DistillerPageWebContents::CreateNewWebContents(url
);
109 virtual ~TestDistillerPageWebContents() {
110 if (!expect_new_web_contents_
) {
111 // Intentionally leaking WebContents, since it is owned by the shell.
112 content::WebContents
* web_contents
= web_contents_
.release();
113 web_contents
->GetLastCommittedURL();
117 bool new_web_contents_created() { return new_web_contents_created_
; }
120 bool expect_new_web_contents_
;
121 bool new_web_contents_created_
;
124 // Helper class to know how far in the loading process the current WebContents
125 // has come. It will call the callback either after
126 // DidCommitProvisionalLoadForFrame or DocumentLoadedInFrame is called for the
127 // main frame, based on the value of |wait_for_document_loaded|.
128 class WebContentsMainFrameHelper
: public content::WebContentsObserver
{
130 WebContentsMainFrameHelper(content::WebContents
* web_contents
,
131 const base::Closure
& callback
,
132 bool wait_for_document_loaded
)
133 : web_contents_(web_contents
),
135 wait_for_document_loaded_(wait_for_document_loaded
) {
136 content::WebContentsObserver::Observe(web_contents
);
139 virtual void DidCommitProvisionalLoadForFrame(
141 const base::string16
& frame_unique_name
,
144 content::PageTransition transition_type
,
145 content::RenderViewHost
* render_view_host
) OVERRIDE
{
146 if (wait_for_document_loaded_
)
152 virtual void DocumentLoadedInFrame(
154 content::RenderViewHost
* render_view_host
) OVERRIDE
{
155 if (wait_for_document_loaded_
) {
157 frame_id
== web_contents_
->GetMainFrame()->GetRoutingID()) {
164 content::WebContents
* web_contents_
;
165 base::Closure callback_
;
166 bool wait_for_document_loaded_
;
169 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest
, BasicDistillationWorks
) {
170 DistillerPageWebContents
distiller_page(
171 shell()->web_contents()->GetBrowserContext(),
172 scoped_ptr
<SourcePageHandleWebContents
>());
173 distiller_page_
= &distiller_page
;
175 base::RunLoop run_loop
;
176 DistillPage(run_loop
.QuitClosure(), kSimpleArticlePath
);
179 EXPECT_EQ("Test Page Title", page_info_
.get()->title
);
180 EXPECT_THAT(page_info_
.get()->html
, HasSubstr("Lorem ipsum"));
181 EXPECT_THAT(page_info_
.get()->html
, Not(HasSubstr("questionable content")));
182 EXPECT_EQ("", page_info_
.get()->next_page_url
);
183 EXPECT_EQ("", page_info_
.get()->prev_page_url
);
186 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest
, HandlesRelativeLinks
) {
187 DistillerPageWebContents
distiller_page(
188 shell()->web_contents()->GetBrowserContext(),
189 scoped_ptr
<SourcePageHandleWebContents
>());
190 distiller_page_
= &distiller_page
;
192 base::RunLoop run_loop
;
193 DistillPage(run_loop
.QuitClosure(), kSimpleArticlePath
);
196 // A relative link should've been updated.
197 EXPECT_THAT(page_info_
.get()->html
,
198 ContainsRegex("href=\"http://127.0.0.1:.*/relativelink.html\""));
199 EXPECT_THAT(page_info_
.get()->html
,
200 HasSubstr("href=\"http://www.google.com/absolutelink.html\""));
203 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest
, HandlesRelativeImages
) {
204 DistillerPageWebContents
distiller_page(
205 shell()->web_contents()->GetBrowserContext(),
206 scoped_ptr
<SourcePageHandleWebContents
>());
207 distiller_page_
= &distiller_page
;
209 base::RunLoop run_loop
;
210 DistillPage(run_loop
.QuitClosure(), kSimpleArticlePath
);
213 // A relative link should've been updated.
214 EXPECT_THAT(page_info_
.get()->html
,
215 ContainsRegex("src=\"http://127.0.0.1:.*/relativeimage.png\""));
216 EXPECT_THAT(page_info_
.get()->html
,
217 HasSubstr("src=\"http://www.google.com/absoluteimage.png\""));
220 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest
, VisibilityDetection
) {
221 DistillerPageWebContents
distiller_page(
222 shell()->web_contents()->GetBrowserContext(),
223 scoped_ptr
<SourcePageHandleWebContents
>());
224 distiller_page_
= &distiller_page
;
226 // visble_style.html and invisible_style.html only differ by the visibility
227 // internal stylesheet.
230 base::RunLoop run_loop
;
231 DistillPage(run_loop
.QuitClosure(), "/visible_style.html");
233 EXPECT_THAT(page_info_
.get()->html
, HasSubstr("Lorem ipsum"));
237 base::RunLoop run_loop
;
238 DistillPage(run_loop
.QuitClosure(), "/invisible_style.html");
240 EXPECT_THAT(page_info_
.get()->html
, Not(HasSubstr("Lorem ipsum")));
244 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest
,
245 UsingCurrentWebContentsWrongUrl
) {
246 std::string
url("/bogus");
247 bool expect_new_web_contents
= true;
248 bool setup_main_frame_observer
= true;
249 bool wait_for_document_loaded
= true;
250 RunUseCurrentWebContentsTest(url
,
251 expect_new_web_contents
,
252 setup_main_frame_observer
,
253 wait_for_document_loaded
);
256 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest
,
257 UsingCurrentWebContentsNoMainFrameObserver
) {
258 std::string
url(kSimpleArticlePath
);
259 bool expect_new_web_contents
= true;
260 bool setup_main_frame_observer
= false;
261 bool wait_for_document_loaded
= true;
262 RunUseCurrentWebContentsTest(url
,
263 expect_new_web_contents
,
264 setup_main_frame_observer
,
265 wait_for_document_loaded
);
268 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest
,
269 UsingCurrentWebContentsNotFinishedLoadingYet
) {
270 std::string
url(kSimpleArticlePath
);
271 bool expect_new_web_contents
= false;
272 bool setup_main_frame_observer
= true;
273 bool wait_for_document_loaded
= false;
274 RunUseCurrentWebContentsTest(url
,
275 expect_new_web_contents
,
276 setup_main_frame_observer
,
277 wait_for_document_loaded
);
280 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest
,
281 UsingCurrentWebContentsReadyForDistillation
) {
282 std::string
url(kSimpleArticlePath
);
283 bool expect_new_web_contents
= false;
284 bool setup_main_frame_observer
= true;
285 bool wait_for_document_loaded
= true;
286 RunUseCurrentWebContentsTest(url
,
287 expect_new_web_contents
,
288 setup_main_frame_observer
,
289 wait_for_document_loaded
);
292 void DistillerPageWebContentsTest::RunUseCurrentWebContentsTest(
293 const std::string
& url
,
294 bool expect_new_web_contents
,
295 bool setup_main_frame_observer
,
296 bool wait_for_document_loaded
) {
297 content::WebContents
* current_web_contents
= shell()->web_contents();
298 if (setup_main_frame_observer
) {
299 dom_distiller::WebContentsMainFrameObserver::CreateForWebContents(
300 current_web_contents
);
302 base::RunLoop url_loaded_runner
;
303 WebContentsMainFrameHelper
main_frame_loaded(current_web_contents
,
304 url_loaded_runner
.QuitClosure(),
305 wait_for_document_loaded
);
306 current_web_contents
->GetController().LoadURL(
307 embedded_test_server()->GetURL(url
),
309 content::PAGE_TRANSITION_TYPED
,
311 url_loaded_runner
.Run();
313 scoped_ptr
<content::WebContents
> old_web_contents_sptr(current_web_contents
);
314 scoped_ptr
<SourcePageHandleWebContents
> source_page_handle(
315 new SourcePageHandleWebContents(old_web_contents_sptr
.Pass()));
317 TestDistillerPageWebContents
distiller_page(
318 shell()->web_contents()->GetBrowserContext(),
319 source_page_handle
.Pass(),
320 expect_new_web_contents
);
321 distiller_page_
= &distiller_page
;
323 base::RunLoop run_loop
;
324 DistillPage(run_loop
.QuitClosure(), kSimpleArticlePath
);
327 // Sanity check of distillation process.
328 EXPECT_EQ(expect_new_web_contents
, distiller_page
.new_web_contents_created());
329 EXPECT_EQ("Test Page Title", page_info_
.get()->title
);
332 } // namespace dom_distiller