1 // Copyright 2014 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.
7 #include "base/command_line.h"
9 #include "chrome/browser/dom_distiller/dom_distiller_service_factory.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/ui/browser.h"
12 #include "chrome/browser/ui/tabs/tab_strip_model.h"
13 #include "chrome/common/chrome_switches.h"
14 #include "chrome/common/url_constants.h"
15 #include "chrome/test/base/in_process_browser_test.h"
16 #include "chrome/test/base/ui_test_utils.h"
17 #include "components/dom_distiller/content/dom_distiller_viewer_source.h"
18 #include "components/dom_distiller/core/distiller.h"
19 #include "components/dom_distiller/core/dom_distiller_service.h"
20 #include "components/dom_distiller/core/dom_distiller_store.h"
21 #include "components/dom_distiller/core/dom_distiller_test_util.h"
22 #include "components/dom_distiller/core/fake_db.h"
23 #include "components/dom_distiller/core/fake_distiller.h"
24 #include "components/dom_distiller/core/task_tracker.h"
25 #include "components/dom_distiller/core/url_utils.h"
26 #include "content/public/browser/render_view_host.h"
27 #include "content/public/browser/url_data_source.h"
28 #include "content/public/browser/web_contents.h"
29 #include "content/public/browser/web_contents_observer.h"
30 #include "testing/gtest/include/gtest/gtest.h"
32 namespace dom_distiller {
35 using test::FakeDistiller;
36 using test::MockDistillerFactory;
37 using test::util::CreateStoreWithFakeDB;
41 void AddEntry(const ArticleEntry& e, FakeDB::EntryMap* map) {
42 (*map)[e.entry_id()] = e;
45 ArticleEntry CreateEntry(std::string entry_id, std::string page_url) {
47 entry.set_entry_id(entry_id);
48 if (!page_url.empty()) {
49 ArticleEntryPage* page = entry.add_pages();
50 page->set_url(page_url);
57 // WebContents observer that stores reference to the current |RenderViewHost|.
58 class LoadSuccessObserver : public content::WebContentsObserver {
60 explicit LoadSuccessObserver(content::WebContents* contents)
61 : content::WebContentsObserver(contents),
62 validated_url_(GURL()),
63 finished_load_(false),
65 web_contents_(contents),
66 render_view_host_(NULL) {}
68 virtual void DidFinishLoad(int64 frame_id,
69 const GURL& validated_url,
71 content::RenderViewHost* render_view_host)
73 validated_url_ = validated_url;
74 finished_load_ = true;
75 render_view_host_ = render_view_host;
78 virtual void DidFailProvisionalLoad(int64 frame_id,
79 const base::string16& frame_unique_name,
81 const GURL& validated_url,
83 const base::string16& error_description,
84 content::RenderViewHost* render_view_host)
89 const GURL& validated_url() const { return validated_url_; }
90 bool finished_load() const { return finished_load_; }
91 bool load_failed() const { return load_failed_; }
92 content::WebContents* web_contents() const { return web_contents_; }
94 const content::RenderViewHost* render_view_host() const {
95 return render_view_host_;
102 content::WebContents* web_contents_;
103 content::RenderViewHost* render_view_host_;
105 DISALLOW_COPY_AND_ASSIGN(LoadSuccessObserver);
108 class DomDistillerViewerSourceBrowserTest : public InProcessBrowserTest {
110 DomDistillerViewerSourceBrowserTest() {}
111 virtual ~DomDistillerViewerSourceBrowserTest() {}
113 virtual void SetUpOnMainThread() OVERRIDE {
114 database_model_ = new FakeDB::EntryMap;
117 virtual void CleanUpOnMainThread() OVERRIDE { delete database_model_; }
119 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
120 command_line->AppendSwitch(switches::kEnableDomDistiller);
123 static KeyedService* Build(content::BrowserContext* context) {
124 FakeDB* fake_db = new FakeDB(database_model_);
125 MockDistillerFactory* factory = new MockDistillerFactory();
126 DomDistillerContextKeyedService* service =
127 new DomDistillerContextKeyedService(
128 scoped_ptr<DomDistillerStoreInterface>(
129 CreateStoreWithFakeDB(fake_db, FakeDB::EntryMap())),
130 scoped_ptr<DistillerFactory>(factory),
131 scoped_ptr<DistillerPageFactory>());
132 fake_db->InitCallback(true);
133 fake_db->LoadCallback(true);
134 if (expect_distillation_) {
135 // There will only be destillation of an article if the database contains
137 FakeDistiller* distiller = new FakeDistiller(true);
138 EXPECT_CALL(*factory, CreateDistillerImpl())
139 .WillOnce(testing::Return(distiller));
144 void ViewSingleDistilledPage(const GURL& url);
147 static FakeDB::EntryMap* database_model_;
148 static bool expect_distillation_;
151 FakeDB::EntryMap* DomDistillerViewerSourceBrowserTest::database_model_;
152 bool DomDistillerViewerSourceBrowserTest::expect_distillation_ = false;
154 // The DomDistillerViewerSource renders untrusted content, so ensure no bindings
155 // are enabled when the article exists in the database.
156 // Flakiness: crbug.com/356866
157 IN_PROC_BROWSER_TEST_F(DomDistillerViewerSourceBrowserTest,
158 DISABLED_NoWebUIBindingsArticleExists) {
159 // Ensure there is one item in the database, which will trigger distillation.
160 const ArticleEntry entry = CreateEntry("DISTILLED", "http://example.com/1");
161 AddEntry(entry, database_model_);
162 expect_distillation_ = true;
163 const GURL url = url_utils::GetDistillerViewUrlFromEntryId(
164 chrome::kDomDistillerScheme, entry.entry_id());
165 ViewSingleDistilledPage(url);
168 // The DomDistillerViewerSource renders untrusted content, so ensure no bindings
169 // are enabled when the article is not found.
170 // Flakiness: crbug.com/356866
171 IN_PROC_BROWSER_TEST_F(DomDistillerViewerSourceBrowserTest,
172 DISABLED_NoWebUIBindingsArticleNotFound) {
173 // The article does not exist, so assume no distillation will happen.
174 expect_distillation_ = false;
175 const GURL url(std::string(chrome::kDomDistillerScheme) + "://" +
176 base::GenerateGUID() + "/");
177 ViewSingleDistilledPage(url);
180 // The DomDistillerViewerSource renders untrusted content, so ensure no bindings
181 // are enabled when requesting to view an arbitrary URL.
182 // Flakiness: crbug.com/356866
183 IN_PROC_BROWSER_TEST_F(DomDistillerViewerSourceBrowserTest,
184 DISABLED_NoWebUIBindingsViewUrl) {
185 // We should expect distillation for any valid URL.
186 expect_distillation_ = true;
187 GURL view_url("http://www.example.com/1");
188 const GURL url = url_utils::GetDistillerViewUrlFromUrl(
189 chrome::kDomDistillerScheme, view_url);
190 ViewSingleDistilledPage(url);
193 void DomDistillerViewerSourceBrowserTest::ViewSingleDistilledPage(
195 // Ensure the correct factory is used for the DomDistillerService.
196 dom_distiller::DomDistillerServiceFactory::GetInstance()
197 ->SetTestingFactoryAndUse(browser()->profile(), &Build);
199 // Setup observer to inspect the RenderViewHost after committed navigation.
200 content::WebContents* contents =
201 browser()->tab_strip_model()->GetActiveWebContents();
202 LoadSuccessObserver observer(contents);
204 // Navigate to a URL which the source should respond to.
205 ui_test_utils::NavigateToURL(browser(), url);
207 // A navigation should have succeeded to the correct URL.
208 ASSERT_FALSE(observer.load_failed());
209 ASSERT_TRUE(observer.finished_load());
210 ASSERT_EQ(url, observer.validated_url());
211 // Ensure no bindings.
212 const content::RenderViewHost* render_view_host = observer.render_view_host();
213 ASSERT_EQ(0, render_view_host->GetEnabledBindings());
214 // The MIME-type should always be text/html for the distilled articles.
215 EXPECT_EQ("text/html", observer.web_contents()->GetContentsMimeType());
218 // The DomDistillerViewerSource renders untrusted content, so ensure no bindings
219 // are enabled when the CSS resource is loaded. This CSS might be bundle with
220 // Chrome or provided by an extension.
221 IN_PROC_BROWSER_TEST_F(DomDistillerViewerSourceBrowserTest,
222 NoWebUIBindingsDisplayCSS) {
223 // Setup observer to inspect the RenderViewHost after committed navigation.
224 content::WebContents* contents =
225 browser()->tab_strip_model()->GetActiveWebContents();
226 LoadSuccessObserver observer(contents);
228 // Navigate to a URL which the source should respond to with CSS.
229 std::string url_without_scheme = "://foobar/readability.css";
230 GURL url(chrome::kDomDistillerScheme + url_without_scheme);
231 ui_test_utils::NavigateToURL(browser(), url);
233 // A navigation should have succeeded to the correct URL.
234 ASSERT_FALSE(observer.load_failed());
235 ASSERT_TRUE(observer.finished_load());
236 ASSERT_EQ(url, observer.validated_url());
237 // Ensure no bindings.
238 const content::RenderViewHost* render_view_host = observer.render_view_host();
239 ASSERT_EQ(0, render_view_host->GetEnabledBindings());
240 // The MIME-type should always be text/css for the CSS resources.
241 EXPECT_EQ("text/css", observer.web_contents()->GetContentsMimeType());
244 } // namespace dom_distiller