Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / pdf / pdf_browsertest.cc
blobc424323d100097ff0f89c02ba5b33646103bb3c8
1 // Copyright (c) 2012 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/file_util.h"
6 #include "base/files/file.h"
7 #include "base/files/file_enumerator.h"
8 #include "base/hash.h"
9 #include "base/path_service.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_window.h"
16 #include "chrome/browser/ui/tabs/tab_strip_model.h"
17 #include "chrome/common/chrome_paths.h"
18 #include "chrome/test/base/in_process_browser_test.h"
19 #include "chrome/test/base/ui_test_utils.h"
20 #include "content/public/browser/navigation_controller.h"
21 #include "content/public/browser/notification_observer.h"
22 #include "content/public/browser/render_view_host.h"
23 #include "content/public/browser/web_contents.h"
24 #include "content/public/test/browser_test_utils.h"
25 #include "net/test/embedded_test_server/embedded_test_server.h"
26 #include "third_party/skia/include/core/SkBitmap.h"
27 #include "ui/base/clipboard/clipboard.h"
28 #include "ui/gfx/codec/png_codec.h"
29 #include "ui/gfx/screen.h"
31 using content::NavigationController;
32 using content::WebContents;
34 // Note: All tests in here require the internal PDF plugin, so they're disabled
35 // in non-official builds. We still compile them though, to prevent bitrot.
37 namespace {
39 // Include things like browser frame and scrollbar and make sure we're bigger
40 // than the test pdf document.
41 static const int kBrowserWidth = 1000;
42 static const int kBrowserHeight = 600;
44 class PDFBrowserTest : public InProcessBrowserTest,
45 public testing::WithParamInterface<int>,
46 public content::NotificationObserver {
47 public:
48 PDFBrowserTest()
49 : snapshot_different_(true),
50 next_dummy_search_value_(0),
51 load_stop_notification_count_(0) {
52 base::FilePath src_dir;
53 EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &src_dir));
54 pdf_test_server_.ServeFilesFromDirectory(src_dir.AppendASCII("pdf/test"));
57 protected:
58 // Use our own TestServer so that we can serve files from the pdf directory.
59 net::test_server::EmbeddedTestServer* pdf_test_server() {
60 return &pdf_test_server_;
63 int load_stop_notification_count() const {
64 return load_stop_notification_count_;
67 base::FilePath GetPDFTestDir() {
68 return base::FilePath(base::FilePath::kCurrentDirectory).AppendASCII("..").
69 AppendASCII("..").AppendASCII("..").AppendASCII("pdf").
70 AppendASCII("test");
73 void Load() {
74 // Make sure to set the window size before rendering, as otherwise rendering
75 // to a smaller window and then expanding leads to slight anti-aliasing
76 // differences of the text and the pixel comparison fails.
77 gfx::Rect bounds(gfx::Rect(0, 0, kBrowserWidth, kBrowserHeight));
78 gfx::Rect screen_bounds =
79 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().bounds();
80 ASSERT_GT(screen_bounds.width(), kBrowserWidth);
81 ASSERT_GT(screen_bounds.height(), kBrowserHeight);
82 browser()->window()->SetBounds(bounds);
84 GURL url(ui_test_utils::GetTestUrl(
85 GetPDFTestDir(),
86 base::FilePath(FILE_PATH_LITERAL("pdf_browsertest.pdf"))));
87 ui_test_utils::NavigateToURL(browser(), url);
90 bool VerifySnapshot(const std::string& expected_filename) {
91 snapshot_different_ = true;
92 expected_filename_ = expected_filename;
93 WebContents* web_contents =
94 browser()->tab_strip_model()->GetActiveWebContents();
95 DCHECK(web_contents);
97 content::RenderWidgetHost* rwh = web_contents->GetRenderViewHost();
98 rwh->GetSnapshotFromRenderer(gfx::Rect(), base::Bind(
99 &PDFBrowserTest::GetSnapshotFromRendererCallback, this));
101 content::RunMessageLoop();
103 if (snapshot_different_) {
104 LOG(INFO) << "Rendering didn't match, see result " <<
105 snapshot_filename_.value().c_str();
107 return !snapshot_different_;
110 void WaitForResponse() {
111 // Even if the plugin has loaded the data or scrolled, because of how
112 // pepper painting works, we might not have the data. One way to force this
113 // to be flushed is to do a find operation, since on this two-page test
114 // document, it'll wait for us to flush the renderer message loop twice and
115 // also the browser's once, at which point we're guaranteed to have updated
116 // the backingstore. Hacky, but it works.
117 // Note that we need to change the text each time, because if we don't the
118 // renderer code will think the second message is to go to next result, but
119 // there are none so the plugin will assert.
121 base::string16 query = base::UTF8ToUTF16(
122 std::string("xyzxyz" + base::IntToString(next_dummy_search_value_++)));
123 ASSERT_EQ(0, ui_test_utils::FindInPage(
124 browser()->tab_strip_model()->GetActiveWebContents(),
125 query, true, false, NULL, NULL));
128 private:
129 void GetSnapshotFromRendererCallback(bool success,
130 const SkBitmap& bitmap) {
131 base::MessageLoopForUI::current()->Quit();
132 ASSERT_EQ(success, true);
133 base::FilePath reference = ui_test_utils::GetTestFilePath(
134 GetPDFTestDir(),
135 base::FilePath().AppendASCII(expected_filename_));
136 base::File::Info info;
137 ASSERT_TRUE(base::GetFileInfo(reference, &info));
138 int size = static_cast<size_t>(info.size);
139 scoped_ptr<char[]> data(new char[size]);
140 ASSERT_EQ(size, base::ReadFile(reference, data.get(), size));
142 int w, h;
143 std::vector<unsigned char> decoded;
144 ASSERT_TRUE(gfx::PNGCodec::Decode(
145 reinterpret_cast<unsigned char*>(data.get()), size,
146 gfx::PNGCodec::FORMAT_BGRA, &decoded, &w, &h));
147 int32* ref_pixels = reinterpret_cast<int32*>(&decoded[0]);
149 int32* pixels = static_cast<int32*>(bitmap.getPixels());
151 // Get the background color, and use it to figure out the x-offsets in
152 // each image. The reason is that depending on the theme in the OS, the
153 // same browser width can lead to slightly different plugin sizes, so the
154 // pdf content will start at different x offsets.
155 // Also note that the images we saved are cut off before the scrollbar, as
156 // that'll change depending on the theme, and also cut off vertically so
157 // that the ui controls don't show up, as those fade-in and so the timing
158 // will affect their transparency.
159 int32 bg_color = ref_pixels[0];
160 int ref_x_offset, snapshot_x_offset;
161 for (ref_x_offset = 0; ref_x_offset < w; ++ref_x_offset) {
162 if (ref_pixels[ref_x_offset] != bg_color)
163 break;
166 for (snapshot_x_offset = 0; snapshot_x_offset < bitmap.width();
167 ++snapshot_x_offset) {
168 if (pixels[snapshot_x_offset] != bg_color)
169 break;
172 int x_max = std::min(
173 w - ref_x_offset, bitmap.width() - snapshot_x_offset);
174 int y_max = std::min(h, bitmap.height());
175 int stride = bitmap.rowBytes();
176 snapshot_different_ = false;
177 for (int y = 0; y < y_max && !snapshot_different_; ++y) {
178 for (int x = 0; x < x_max && !snapshot_different_; ++x) {
179 if (pixels[y * stride / sizeof(int32) + x + snapshot_x_offset] !=
180 ref_pixels[y * w + x + ref_x_offset])
181 snapshot_different_ = true;
185 if (snapshot_different_) {
186 std::vector<unsigned char> png_data;
187 gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &png_data);
188 if (base::CreateTemporaryFile(&snapshot_filename_)) {
189 file_util::WriteFile(snapshot_filename_,
190 reinterpret_cast<char*>(&png_data[0]), png_data.size());
195 // content::NotificationObserver
196 virtual void Observe(int type,
197 const content::NotificationSource& source,
198 const content::NotificationDetails& details) {
199 if (type == content::NOTIFICATION_LOAD_STOP) {
200 load_stop_notification_count_++;
204 // True if the snapshot differed from the expected value.
205 bool snapshot_different_;
206 // Internal variable used to synchronize to the renderer.
207 int next_dummy_search_value_;
208 // The filename of the bitmap to compare the snapshot to.
209 std::string expected_filename_;
210 // If the snapshot is different, holds the location where it's saved.
211 base::FilePath snapshot_filename_;
212 // How many times we've seen chrome::LOAD_STOP.
213 int load_stop_notification_count_;
215 net::test_server::EmbeddedTestServer pdf_test_server_;
218 #if !defined(GOOGLE_CHROME_BUILD) || defined(OS_CHROMEOS)
219 // TODO(thestig): http://crbug.com/79837
220 #define MAYBE_Basic DISABLED_Basic
221 #else
222 #define MAYBE_Basic Basic
223 #endif
224 // Tests basic PDF rendering. This can be broken depending on bad merges with
225 // the vendor, so it's important that we have basic sanity checking.
226 IN_PROC_BROWSER_TEST_F(PDFBrowserTest, MAYBE_Basic) {
227 ASSERT_NO_FATAL_FAILURE(Load());
228 ASSERT_NO_FATAL_FAILURE(WaitForResponse());
229 // OS X uses CoreText, and FreeType renders slightly different on Linux and
230 // Win.
231 #if defined(OS_MACOSX)
232 // The bots render differently than locally, see http://crbug.com/142531.
233 ASSERT_TRUE(VerifySnapshot("pdf_browsertest_mac.png") ||
234 VerifySnapshot("pdf_browsertest_mac2.png"));
235 #elif defined(OS_LINUX)
236 ASSERT_TRUE(VerifySnapshot("pdf_browsertest_linux.png"));
237 #else
238 ASSERT_TRUE(VerifySnapshot("pdf_browsertest.png"));
239 #endif
242 #if !defined(GOOGLE_CHROME_BUILD) || defined(OS_CHROMEOS)
243 // TODO(sanjeevr): http://crbug.com/79837
244 #define MAYBE_Scroll DISABLED_Scroll
245 #else
246 #define MAYBE_Scroll Scroll
247 #endif
248 // Tests that scrolling works.
249 IN_PROC_BROWSER_TEST_F(PDFBrowserTest, MAYBE_Scroll) {
250 ASSERT_NO_FATAL_FAILURE(Load());
252 // We use wheel mouse event since that's the only one we can easily push to
253 // the renderer. There's no way to push a cross-platform keyboard event at
254 // the moment.
255 blink::WebMouseWheelEvent wheel_event;
256 wheel_event.type = blink::WebInputEvent::MouseWheel;
257 wheel_event.deltaY = -200;
258 wheel_event.wheelTicksY = -2;
259 WebContents* web_contents =
260 browser()->tab_strip_model()->GetActiveWebContents();
261 web_contents->GetRenderViewHost()->ForwardWheelEvent(wheel_event);
262 ASSERT_NO_FATAL_FAILURE(WaitForResponse());
264 int y_offset = 0;
265 ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
266 browser()->tab_strip_model()->GetActiveWebContents(),
267 "window.domAutomationController.send(plugin.pageYOffset())",
268 &y_offset));
269 ASSERT_GT(y_offset, 0);
272 #if !defined(GOOGLE_CHROME_BUILD) || defined(OS_CHROMEOS)
273 // TODO(thestig): http://crbug.com/79837
274 #define MAYBE_FindAndCopy DISABLED_FindAndCopy
275 #else
276 #define MAYBE_FindAndCopy FindAndCopy
277 #endif
278 IN_PROC_BROWSER_TEST_F(PDFBrowserTest, MAYBE_FindAndCopy) {
279 ASSERT_NO_FATAL_FAILURE(Load());
280 // Verifies that find in page works.
281 ASSERT_EQ(3, ui_test_utils::FindInPage(
282 browser()->tab_strip_model()->GetActiveWebContents(),
283 base::UTF8ToUTF16("adipiscing"),
284 true, false, NULL, NULL));
286 // Verify that copying selected text works.
287 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
288 // Reset the clipboard first.
289 ui::Clipboard::ObjectMap objects;
290 ui::Clipboard::ObjectMapParams params;
291 params.push_back(std::vector<char>());
292 objects[ui::Clipboard::CBF_TEXT] = params;
293 clipboard->WriteObjects(ui::CLIPBOARD_TYPE_COPY_PASTE, objects);
295 browser()->tab_strip_model()->GetActiveWebContents()->
296 GetRenderViewHost()->Copy();
297 ASSERT_NO_FATAL_FAILURE(WaitForResponse());
299 std::string text;
300 clipboard->ReadAsciiText(ui::CLIPBOARD_TYPE_COPY_PASTE, &text);
301 ASSERT_EQ("adipiscing", text);
304 const int kLoadingNumberOfParts = 10;
306 // Tests that loading async pdfs works correctly (i.e. document fully loads).
307 // This also loads all documents that used to crash, to ensure we don't have
308 // regressions.
309 // If it flakes, reopen http://crbug.com/74548.
310 #if !defined(GOOGLE_CHROME_BUILD)
311 #define MAYBE_Loading DISABLED_Loading
312 #else
313 #define MAYBE_Loading Loading
314 #endif
315 IN_PROC_BROWSER_TEST_P(PDFBrowserTest, MAYBE_Loading) {
316 ASSERT_TRUE(pdf_test_server()->InitializeAndWaitUntilReady());
318 NavigationController* controller =
319 &(browser()->tab_strip_model()->GetActiveWebContents()->GetController());
320 content::NotificationRegistrar registrar;
321 registrar.Add(this,
322 content::NOTIFICATION_LOAD_STOP,
323 content::Source<NavigationController>(controller));
324 std::string base_url = std::string("/");
326 base::FileEnumerator file_enumerator(
327 ui_test_utils::GetTestFilePath(GetPDFTestDir(), base::FilePath()),
328 false,
329 base::FileEnumerator::FILES,
330 FILE_PATH_LITERAL("*.pdf"));
331 for (base::FilePath file_path = file_enumerator.Next();
332 !file_path.empty();
333 file_path = file_enumerator.Next()) {
334 std::string filename = file_path.BaseName().MaybeAsASCII();
335 ASSERT_FALSE(filename.empty());
337 #if defined(OS_POSIX)
338 if (filename == "sample.pdf")
339 continue; // Crashes on Mac and Linux. http://crbug.com/63549
340 #endif
342 // Split the test into smaller sub-tests. Each one only loads
343 // every k-th file.
344 if (static_cast<int>(base::Hash(filename) % kLoadingNumberOfParts) !=
345 GetParam()) {
346 continue;
349 LOG(WARNING) << "PDFBrowserTest.Loading: " << filename;
351 GURL url = pdf_test_server()->GetURL(base_url + filename);
352 ui_test_utils::NavigateToURL(browser(), url);
354 while (true) {
355 int last_count = load_stop_notification_count();
356 // We might get extraneous chrome::LOAD_STOP notifications when
357 // doing async loading. This happens when the first loader is cancelled
358 // and before creating a byte-range request loader.
359 bool complete = false;
360 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
361 browser()->tab_strip_model()->GetActiveWebContents(),
362 "window.domAutomationController.send(plugin.documentLoadComplete())",
363 &complete));
364 if (complete)
365 break;
367 // Check if the LOAD_STOP notification could have come while we run a
368 // nested message loop for the JS call.
369 if (last_count != load_stop_notification_count())
370 continue;
371 content::WaitForLoadStop(
372 browser()->tab_strip_model()->GetActiveWebContents());
377 INSTANTIATE_TEST_CASE_P(PDFTestFiles,
378 PDFBrowserTest,
379 testing::Range(0, kLoadingNumberOfParts));
381 #if !defined(GOOGLE_CHROME_BUILD) || defined(OS_MACOSX)
382 // http://crbug.com/315160
383 #define MAYBE_Action DISABLED_Action
384 #else
385 #define MAYBE_Action Action
386 #endif
387 IN_PROC_BROWSER_TEST_F(PDFBrowserTest, MAYBE_Action) {
388 ASSERT_NO_FATAL_FAILURE(Load());
390 ASSERT_TRUE(content::ExecuteScript(
391 browser()->tab_strip_model()->GetActiveWebContents(),
392 "document.getElementsByName('plugin')[0].fitToHeight();"));
394 std::string zoom1, zoom2;
395 ASSERT_TRUE(content::ExecuteScriptAndExtractString(
396 browser()->tab_strip_model()->GetActiveWebContents(),
397 "window.domAutomationController.send("
398 " document.getElementsByName('plugin')[0].getZoomLevel().toString())",
399 &zoom1));
401 ASSERT_TRUE(content::ExecuteScript(
402 browser()->tab_strip_model()->GetActiveWebContents(),
403 "document.getElementsByName('plugin')[0].fitToWidth();"));
405 ASSERT_TRUE(content::ExecuteScriptAndExtractString(
406 browser()->tab_strip_model()->GetActiveWebContents(),
407 "window.domAutomationController.send("
408 " document.getElementsByName('plugin')[0].getZoomLevel().toString())",
409 &zoom2));
410 ASSERT_NE(zoom1, zoom2);
413 // Flaky as per http://crbug.com/74549.
414 IN_PROC_BROWSER_TEST_F(PDFBrowserTest, DISABLED_OnLoadAndReload) {
415 ASSERT_TRUE(pdf_test_server()->InitializeAndWaitUntilReady());
417 GURL url = pdf_test_server()->GetURL("/onload_reload.html");
418 ui_test_utils::NavigateToURL(browser(), url);
420 content::WindowedNotificationObserver observer(
421 content::NOTIFICATION_LOAD_STOP,
422 content::Source<NavigationController>(
423 &browser()->tab_strip_model()->GetActiveWebContents()->
424 GetController()));
425 ASSERT_TRUE(content::ExecuteScript(
426 browser()->tab_strip_model()->GetActiveWebContents(),
427 "reloadPDF();"));
428 observer.Wait();
430 ASSERT_EQ("success",
431 browser()->tab_strip_model()->GetActiveWebContents()->
432 GetURL().query());
435 } // namespace