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