Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / pdf / pdf_extension_test.cc
blobb90517984a16bd114c4a23101c6fc67ce236a053
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.
5 #include <vector>
7 #include "base/base_paths.h"
8 #include "base/files/file_enumerator.h"
9 #include "base/files/file_util.h"
10 #include "base/hash.h"
11 #include "base/logging.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/path_service.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/extensions/component_loader.h"
17 #include "chrome/browser/extensions/extension_apitest.h"
18 #include "chrome/browser/extensions/extension_service.h"
19 #include "chrome/browser/pdf/pdf_extension_test_util.h"
20 #include "chrome/browser/pdf/pdf_extension_util.h"
21 #include "chrome/browser/plugins/plugin_prefs.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/ui/browser.h"
24 #include "chrome/browser/ui/tabs/tab_strip_model.h"
25 #include "chrome/common/chrome_content_client.h"
26 #include "chrome/common/chrome_paths.h"
27 #include "chrome/common/chrome_switches.h"
28 #include "chrome/test/base/ui_test_utils.h"
29 #include "content/public/browser/browser_plugin_guest_manager.h"
30 #include "content/public/browser/download_item.h"
31 #include "content/public/browser/download_manager.h"
32 #include "content/public/browser/notification_observer.h"
33 #include "content/public/browser/notification_registrar.h"
34 #include "content/public/browser/plugin_service.h"
35 #include "content/public/browser/web_contents.h"
36 #include "content/public/test/browser_test_utils.h"
37 #include "extensions/browser/extension_registry.h"
38 #include "extensions/common/manifest_handlers/mime_types_handler.h"
39 #include "extensions/test/result_catcher.h"
40 #include "net/test/embedded_test_server/embedded_test_server.h"
41 #include "ui/base/resource/resource_bundle.h"
42 #include "url/gurl.h"
44 const int kNumberLoadTestParts = 10;
46 bool GetGuestCallback(content::WebContents** guest_out,
47 content::WebContents* guest) {
48 EXPECT_FALSE(*guest_out);
49 *guest_out = guest;
50 // Return false so that we iterate through all the guests and verify there is
51 // only one.
52 return false;
55 class PDFExtensionTest : public ExtensionApiTest,
56 public testing::WithParamInterface<int> {
57 public:
58 ~PDFExtensionTest() override {}
60 void SetUpCommandLine(base::CommandLine* command_line) override {
61 command_line->AppendSwitch(switches::kDisablePdfMaterialUI);
64 void SetUpOnMainThread() override {
65 ExtensionApiTest::SetUpOnMainThread();
66 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
69 void TearDownOnMainThread() override {
70 ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
71 ExtensionApiTest::TearDownOnMainThread();
74 bool PdfIsExpectedToFailLoad(const std::string& pdf_file) {
75 const char* kFailingPdfs[] = {
76 "pdf_private/cfuzz5.pdf",
77 "pdf_private/cfuzz6.pdf",
78 "pdf_private/crash-11-14-44.pdf",
79 "pdf_private/js.pdf",
80 "pdf_private/segv-ecx.pdf",
81 "pdf_private/tests.pdf",
83 for (size_t i = 0; i < arraysize(kFailingPdfs); ++i) {
84 if (kFailingPdfs[i] == pdf_file)
85 return true;
87 return false;
90 // Runs the extensions test at chrome/test/data/pdf/<filename> on the PDF file
91 // at chrome/test/data/pdf/<pdf_filename>.
92 void RunTestsInFile(std::string filename, std::string pdf_filename) {
93 extensions::ResultCatcher catcher;
95 GURL url(embedded_test_server()->GetURL("/pdf/" + pdf_filename));
97 // It should be good enough to just navigate to the URL. But loading up the
98 // BrowserPluginGuest seems to happen asynchronously as there was flakiness
99 // being seen due to the BrowserPluginGuest not being available yet (see
100 // crbug.com/498077). So instead use |LoadPdf| which ensures that the PDF is
101 // loaded before continuing.
102 ASSERT_TRUE(LoadPdf(url));
104 content::WebContents* contents =
105 browser()->tab_strip_model()->GetActiveWebContents();
106 content::BrowserPluginGuestManager* guest_manager =
107 contents->GetBrowserContext()->GetGuestManager();
108 content::WebContents* guest_contents =
109 guest_manager->GetFullPageGuest(contents);
110 ASSERT_TRUE(guest_contents);
112 base::FilePath test_data_dir;
113 PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
114 test_data_dir = test_data_dir.Append(FILE_PATH_LITERAL("pdf"));
115 base::FilePath test_util_path = test_data_dir.AppendASCII("test_util.js");
116 std::string test_util_js;
117 ASSERT_TRUE(base::ReadFileToString(test_util_path, &test_util_js));
119 base::FilePath test_file_path = test_data_dir.AppendASCII(filename);
120 std::string test_js;
121 ASSERT_TRUE(base::ReadFileToString(test_file_path, &test_js));
123 test_util_js.append(test_js);
124 ASSERT_TRUE(content::ExecuteScript(guest_contents, test_util_js));
126 if (!catcher.GetNextResult())
127 FAIL() << catcher.message();
130 // Load the PDF at the given URL and use the PDFScriptingAPI to ensure it has
131 // finished loading. Return true if it loads successfully or false if it
132 // fails. If it doesn't finish loading the test will hang. This is done from
133 // outside of the BrowserPlugin guest to ensure the PDFScriptingAPI works
134 // correctly from there.
135 bool LoadPdf(const GURL& url) {
136 ui_test_utils::NavigateToURL(browser(), url);
137 content::WebContents* web_contents =
138 browser()->tab_strip_model()->GetActiveWebContents();
139 return pdf_extension_test_util::EnsurePDFHasLoaded(web_contents);
142 // Load all the PDFs contained in chrome/test/data/<dir_name>. This only runs
143 // the test if base::Hash(filename) mod kNumberLoadTestParts == k in order
144 // to shard the files evenly across values of k in [0, kNumberLoadTestParts).
145 void LoadAllPdfsTest(const std::string& dir_name, int k) {
146 base::FilePath test_data_dir;
147 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
148 base::FileEnumerator file_enumerator(test_data_dir.AppendASCII(dir_name),
149 false, base::FileEnumerator::FILES,
150 FILE_PATH_LITERAL("*.pdf"));
152 size_t count = 0;
153 for (base::FilePath file_path = file_enumerator.Next(); !file_path.empty();
154 file_path = file_enumerator.Next()) {
155 std::string filename = file_path.BaseName().MaybeAsASCII();
156 ASSERT_FALSE(filename.empty());
158 std::string pdf_file = dir_name + "/" + filename;
159 if (static_cast<int>(base::Hash(filename) % kNumberLoadTestParts) == k) {
160 LOG(INFO) << "Loading: " << pdf_file;
161 bool success = LoadPdf(embedded_test_server()->GetURL("/" + pdf_file));
162 EXPECT_EQ(!PdfIsExpectedToFailLoad(pdf_file), success);
164 ++count;
166 // Assume that there is at least 1 pdf in the directory to guard against
167 // someone deleting the directory and silently making the test pass.
168 ASSERT_GE(count, 1u);
171 void TestGetSelectedTextReply(GURL url, bool expect_success) {
172 ui_test_utils::NavigateToURL(browser(), url);
173 content::WebContents* web_contents =
174 browser()->tab_strip_model()->GetActiveWebContents();
175 ASSERT_TRUE(pdf_extension_test_util::EnsurePDFHasLoaded(web_contents));
177 // Reach into the guest and hook into it such that it posts back a 'flush'
178 // message after every getSelectedTextReply message sent.
179 content::BrowserPluginGuestManager* guest_manager =
180 web_contents->GetBrowserContext()->GetGuestManager();
181 content::WebContents* guest_contents = nullptr;
182 ASSERT_NO_FATAL_FAILURE(guest_manager->ForEachGuest(
183 web_contents, base::Bind(&GetGuestCallback, &guest_contents)));
184 ASSERT_TRUE(guest_contents);
185 ASSERT_TRUE(content::ExecuteScript(
186 guest_contents,
187 "var oldSendScriptingMessage = "
188 " PDFViewer.prototype.sendScriptingMessage_;"
189 "PDFViewer.prototype.sendScriptingMessage_ = function(message) {"
190 " oldSendScriptingMessage.bind(this)(message);"
191 " if (message.type == 'getSelectedTextReply')"
192 " this.parentWindow_.postMessage('flush', '*');"
193 "}"));
195 // Add an event listener for flush messages and request the selected text.
196 // If we get a flush message without receiving getSelectedText we know that
197 // the message didn't come through.
198 bool success = false;
199 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
200 web_contents,
201 "window.addEventListener('message', function(event) {"
202 " if (event.data == 'flush')"
203 " window.domAutomationController.send(false);"
204 " if (event.data.type == 'getSelectedTextReply')"
205 " window.domAutomationController.send(true);"
206 "});"
207 "document.getElementsByTagName('embed')[0].postMessage("
208 " {type: 'getSelectedText'});",
209 &success));
210 ASSERT_EQ(expect_success, success);
214 IN_PROC_BROWSER_TEST_P(PDFExtensionTest, Load) {
215 #if defined(GOOGLE_CHROME_BUILD)
216 // Load private PDFs.
217 LoadAllPdfsTest("pdf_private", GetParam());
218 #endif
219 // Load public PDFs.
220 LoadAllPdfsTest("pdf", GetParam());
223 class DisablePluginHelper : public content::DownloadManager::Observer,
224 public content::NotificationObserver {
225 public:
226 DisablePluginHelper() {}
228 virtual ~DisablePluginHelper() {}
230 void DisablePlugin(Profile* profile) {
231 registrar_.Add(this, chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED,
232 content::Source<Profile>(profile));
233 scoped_refptr<PluginPrefs> prefs(PluginPrefs::GetForProfile(profile));
234 DCHECK(prefs.get());
235 prefs->EnablePluginGroup(
236 false, base::UTF8ToUTF16(ChromeContentClient::kPDFPluginName));
237 // Wait until the plugin has been disabled.
238 disable_run_loop_.Run();
241 const GURL& GetLastUrl() {
242 // Wait until the download has been created.
243 download_run_loop_.Run();
244 return last_url_;
247 // content::DownloadManager::Observer implementation.
248 void OnDownloadCreated(content::DownloadManager* manager,
249 content::DownloadItem* item) override {
250 last_url_ = item->GetURL();
251 download_run_loop_.Quit();
254 // content::NotificationObserver implementation.
255 void Observe(int type,
256 const content::NotificationSource& source,
257 const content::NotificationDetails& details) override {
258 DCHECK_EQ(chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED, type);
259 disable_run_loop_.Quit();
262 private:
263 content::NotificationRegistrar registrar_;
264 base::RunLoop disable_run_loop_;
265 base::RunLoop download_run_loop_;
266 GURL last_url_;
269 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, DisablePlugin) {
270 // Disable the PDF plugin.
271 content::WebContents* web_contents =
272 browser()->tab_strip_model()->GetActiveWebContents();
273 content::BrowserContext* browser_context = web_contents->GetBrowserContext();
274 Profile* profile = Profile::FromBrowserContext(browser_context);
275 DisablePluginHelper helper;
276 helper.DisablePlugin(profile);
278 // Register a download observer.
279 content::DownloadManager* download_manager =
280 content::BrowserContext::GetDownloadManager(browser_context);
281 download_manager->AddObserver(&helper);
283 // Navigate to a PDF and test that it is downloaded.
284 GURL url(embedded_test_server()->GetURL("/pdf/test.pdf"));
285 ui_test_utils::NavigateToURL(browser(), url);
286 ASSERT_EQ(url, helper.GetLastUrl());
288 // Cancel the download to shutdown cleanly.
289 download_manager->RemoveObserver(&helper);
290 std::vector<content::DownloadItem*> downloads;
291 download_manager->GetAllDownloads(&downloads);
292 ASSERT_EQ(1u, downloads.size());
293 downloads[0]->Cancel(false);
296 // We break PDFTest.Load up into kNumberLoadTestParts.
297 INSTANTIATE_TEST_CASE_P(PDFTestFiles,
298 PDFExtensionTest,
299 testing::Range(0, kNumberLoadTestParts));
301 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, Basic) {
302 RunTestsInFile("basic_test.js", "test.pdf");
305 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, BasicPlugin) {
306 RunTestsInFile("basic_plugin_test.js", "test.pdf");
309 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, Viewport) {
310 RunTestsInFile("viewport_test.js", "test.pdf");
313 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, Bookmark) {
314 RunTestsInFile("bookmarks_test.js", "test-bookmarks.pdf");
317 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, Navigator) {
318 RunTestsInFile("navigator_test.js", "test.pdf");
321 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, ParamsParser) {
322 RunTestsInFile("params_parser_test.js", "test.pdf");
325 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, ZoomManager) {
326 RunTestsInFile("zoom_manager_test.js", "test.pdf");
329 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, Title) {
330 RunTestsInFile("title_test.js", "test-title.pdf");
333 // Ensure that the internal PDF plugin application/x-google-chrome-pdf won't be
334 // loaded if it's not loaded in the chrome extension page.
335 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, EnsureInternalPluginDisabled) {
336 std::string url = embedded_test_server()->GetURL("/pdf/test.pdf").spec();
337 std::string data_url =
338 "data:text/html,"
339 "<html><body>"
340 "<embed type=\"application/x-google-chrome-pdf\" src=\"" +
341 url +
342 "\">"
343 "</body></html>";
344 ui_test_utils::NavigateToURL(browser(), GURL(data_url));
345 content::WebContents* web_contents =
346 browser()->tab_strip_model()->GetActiveWebContents();
347 bool plugin_loaded = false;
348 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
349 web_contents,
350 "var plugin_loaded = "
351 " document.getElementsByTagName('embed')[0].postMessage !== undefined;"
352 "window.domAutomationController.send(plugin_loaded);",
353 &plugin_loaded));
354 ASSERT_FALSE(plugin_loaded);
357 // Ensure cross-origin replies won't work for getSelectedText.
358 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, EnsureCrossOriginRepliesBlocked) {
359 std::string url = embedded_test_server()->GetURL("/pdf/test.pdf").spec();
360 std::string data_url =
361 "data:text/html,"
362 "<html><body>"
363 "<embed type=\"application/pdf\" src=\"" +
364 url +
365 "\">"
366 "</body></html>";
367 TestGetSelectedTextReply(GURL(data_url), false);
370 // Ensure same-origin replies do work for getSelectedText.
371 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, EnsureSameOriginRepliesAllowed) {
372 TestGetSelectedTextReply(embedded_test_server()->GetURL("/pdf/test.pdf"),
373 true);
376 class MaterialPDFExtensionTest : public PDFExtensionTest {
377 void SetUpCommandLine(base::CommandLine* command_line) override {
378 command_line->AppendSwitch(switches::kEnablePdfMaterialUI);
382 IN_PROC_BROWSER_TEST_F(MaterialPDFExtensionTest, Basic) {
383 RunTestsInFile("basic_test_material.js", "test.pdf");
386 IN_PROC_BROWSER_TEST_F(MaterialPDFExtensionTest, BasicPlugin) {
387 RunTestsInFile("basic_plugin_test.js", "test.pdf");
390 IN_PROC_BROWSER_TEST_F(MaterialPDFExtensionTest, Viewport) {
391 RunTestsInFile("viewport_test.js", "test.pdf");
394 IN_PROC_BROWSER_TEST_F(MaterialPDFExtensionTest, Bookmark) {
395 RunTestsInFile("bookmarks_test.js", "test-bookmarks.pdf");
398 IN_PROC_BROWSER_TEST_F(MaterialPDFExtensionTest, Navigator) {
399 RunTestsInFile("navigator_test.js", "test.pdf");
402 IN_PROC_BROWSER_TEST_F(MaterialPDFExtensionTest, ParamsParser) {
403 RunTestsInFile("params_parser_test.js", "test.pdf");
406 IN_PROC_BROWSER_TEST_F(MaterialPDFExtensionTest, ZoomManager) {
407 RunTestsInFile("zoom_manager_test.js", "test.pdf");
410 IN_PROC_BROWSER_TEST_F(MaterialPDFExtensionTest, Elements) {
411 // Although this test file does not require a PDF to be loaded, loading the
412 // elements without loading a PDF is difficult.
413 RunTestsInFile("material_elements_test.js", "test.pdf");
416 IN_PROC_BROWSER_TEST_F(MaterialPDFExtensionTest, ToolbarManager) {
417 RunTestsInFile("toolbar_manager_test.js", "test.pdf");
420 IN_PROC_BROWSER_TEST_F(MaterialPDFExtensionTest, Title) {
421 RunTestsInFile("title_test.js", "test-title.pdf");