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