OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 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 "app/clipboard/clipboard.h" |
| 6 #include "base/command_line.h" |
| 7 #include "base/file_util.h" |
| 8 #include "base/path_service.h" |
| 9 #include "base/string_number_conversions.h" |
| 10 #include "base/utf_string_conversions.h" |
| 11 #include "chrome/browser/browser.h" |
| 12 #include "chrome/browser/browser_window.h" |
| 13 #include "chrome/browser/renderer_host/render_view_host.h" |
| 14 #include "chrome/browser/tab_contents/tab_contents.h" |
| 15 #include "chrome/browser/window_sizer.h" |
| 16 #include "chrome/common/chrome_paths.h" |
| 17 #include "chrome/common/chrome_switches.h" |
| 18 #include "chrome/common/notification_observer.h" |
| 19 #include "chrome/test/in_process_browser_test.h" |
| 20 #include "chrome/test/ui_test_utils.h" |
| 21 #include "gfx/codec/png_codec.h" |
| 22 |
| 23 namespace { |
| 24 |
| 25 // Include things like browser frame and scrollbar and make sure we're bigger |
| 26 // than the test pdf document. |
| 27 static const int kBrowserWidth = 1000; |
| 28 static const int kBrowserHeight = 600; |
| 29 |
| 30 class PDFBrowserTest : public InProcessBrowserTest, |
| 31 public NotificationObserver { |
| 32 public: |
| 33 PDFBrowserTest() |
| 34 : have_plugin_(false), |
| 35 snapshot_different_(true), |
| 36 next_dummy_search_value_(0) { |
| 37 } |
| 38 |
| 39 protected: |
| 40 bool have_plugin() const { return have_plugin_; } |
| 41 |
| 42 virtual void SetUp() { |
| 43 FilePath pdf_path; |
| 44 PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf_path); |
| 45 have_plugin_ = file_util::PathExists(pdf_path); |
| 46 InProcessBrowserTest::SetUp(); |
| 47 } |
| 48 |
| 49 void Load() { |
| 50 GURL url(ui_test_utils::GetTestUrl( |
| 51 FilePath(FilePath::kCurrentDirectory), |
| 52 FilePath(FILE_PATH_LITERAL("pdf_browsertest.pdf")))); |
| 53 ui_test_utils::NavigateToURL(browser(), url); |
| 54 gfx::Rect bounds(gfx::Rect(0, 0, kBrowserWidth, kBrowserHeight)); |
| 55 |
| 56 scoped_ptr<WindowSizer::MonitorInfoProvider> monitor_info( |
| 57 WindowSizer::CreateDefaultMonitorInfoProvider()); |
| 58 gfx::Rect screen_bounds = monitor_info->GetPrimaryMonitorBounds(); |
| 59 ASSERT_GT(screen_bounds.width(), kBrowserWidth); |
| 60 ASSERT_GT(screen_bounds.height(), kBrowserHeight); |
| 61 |
| 62 browser()->window()->SetBounds(bounds); |
| 63 } |
| 64 |
| 65 void VerifySnapshot(const std::string& expected_filename) { |
| 66 snapshot_different_ = true; |
| 67 expected_filename_ = expected_filename; |
| 68 RenderViewHost* host = |
| 69 browser()->GetSelectedTabContents()->render_view_host(); |
| 70 |
| 71 host->CaptureSnapshot(); |
| 72 ui_test_utils::RegisterAndWait(this, |
| 73 NotificationType::TAB_SNAPSHOT_TAKEN, |
| 74 Source<RenderViewHost>(host)); |
| 75 ASSERT_FALSE(snapshot_different_) << "Rendering didn't match, see result " |
| 76 "at " << snapshot_filename_.value().c_str(); |
| 77 } |
| 78 |
| 79 void WaitForResponse() { |
| 80 // Even if the plugin has loaded the data or scrolled, because of how |
| 81 // pepper painting works, we might not have the data. One way to force this |
| 82 // to be flushed is to do a find operation, since on this two-page test |
| 83 // document, it'll wait for us to flush the renderer message loop twice and |
| 84 // also the browser's once, at which point we're guaranteed to have updated |
| 85 // the backingstore. Hacky, but it works. |
| 86 // Note that we need to change the text each time, because if we don't the |
| 87 // renderer code will think the second message is to go to next result, but |
| 88 // there are none so the plugin will assert. |
| 89 |
| 90 string16 query = UTF8ToUTF16( |
| 91 std::string("xyzxyz" + base::IntToString(next_dummy_search_value_++))); |
| 92 ASSERT_EQ(0, ui_test_utils::FindInPage( |
| 93 browser()->GetSelectedTabContents(), query, true, false, NULL)); |
| 94 } |
| 95 |
| 96 private: |
| 97 virtual void SetUpCommandLine(CommandLine* command_line) { |
| 98 command_line->AppendSwitch(switches::kForceInternalPDFPlugin); |
| 99 } |
| 100 |
| 101 // NotificationObserver |
| 102 virtual void Observe(NotificationType type, |
| 103 const NotificationSource& source, |
| 104 const NotificationDetails& details) { |
| 105 if (type == NotificationType::TAB_SNAPSHOT_TAKEN) { |
| 106 MessageLoopForUI::current()->Quit(); |
| 107 FilePath reference = ui_test_utils::GetTestFilePath( |
| 108 FilePath(FilePath::kCurrentDirectory), |
| 109 FilePath().AppendASCII(expected_filename_)); |
| 110 base::PlatformFileInfo info; |
| 111 ASSERT_TRUE(file_util::GetFileInfo(reference, &info)); |
| 112 int size = static_cast<size_t>(info.size); |
| 113 scoped_array<char> data(new char[size]); |
| 114 ASSERT_EQ(size, file_util::ReadFile(reference, data.get(), size)); |
| 115 |
| 116 int w, h; |
| 117 std::vector<unsigned char> decoded; |
| 118 ASSERT_TRUE(gfx::PNGCodec::Decode( |
| 119 reinterpret_cast<unsigned char*>(data.get()), size, |
| 120 gfx::PNGCodec::FORMAT_BGRA, &decoded, &w, &h)); |
| 121 int32* ref_pixels = reinterpret_cast<int32*>(&decoded[0]); |
| 122 |
| 123 const SkBitmap* bitmap = Details<const SkBitmap>(details).ptr(); |
| 124 int32* pixels = static_cast<int32*>(bitmap->getPixels()); |
| 125 |
| 126 // Get the background color, and use it to figure out the x-offsets in |
| 127 // each image. The reason is that depending on the theme in the OS, the |
| 128 // same browser width can lead to slightly different plugin sizes, so the |
| 129 // pdf content will start at different x offsets. |
| 130 // Also note that the images we saved are cut off before the scrollbar, as |
| 131 // that'll change depending on the theme, and also cut off vertically so |
| 132 // that the ui controls don't show up, as those fade-in and so the timing |
| 133 // will affect their transparency. |
| 134 int32 bg_color = ref_pixels[0]; |
| 135 int ref_x_offset, snapshot_x_offset; |
| 136 for (ref_x_offset = 0; ref_x_offset < w; ++ref_x_offset) { |
| 137 if (ref_pixels[ref_x_offset] != bg_color) |
| 138 break; |
| 139 } |
| 140 |
| 141 for (snapshot_x_offset = 0; snapshot_x_offset < bitmap->width(); |
| 142 ++snapshot_x_offset) { |
| 143 if (pixels[snapshot_x_offset] != bg_color) |
| 144 break; |
| 145 } |
| 146 |
| 147 int x_max = std::min( |
| 148 w - ref_x_offset, bitmap->width() - snapshot_x_offset); |
| 149 int y_max = std::min(h, bitmap->height()); |
| 150 int stride = bitmap->rowBytes(); |
| 151 snapshot_different_ = false; |
| 152 for (int y = 0; y < y_max && !snapshot_different_; ++y) { |
| 153 for (int x = 0; x < x_max && !snapshot_different_; ++x) { |
| 154 if (pixels[y * stride / sizeof(int32) + x + snapshot_x_offset] != |
| 155 ref_pixels[y * w + x + ref_x_offset]) |
| 156 snapshot_different_ = true; |
| 157 } |
| 158 } |
| 159 |
| 160 if (snapshot_different_) { |
| 161 std::vector<unsigned char> png_data; |
| 162 gfx::PNGCodec::EncodeBGRASkBitmap(*bitmap, false, &png_data); |
| 163 if (file_util::CreateTemporaryFile(&snapshot_filename_)) { |
| 164 file_util::WriteFile(snapshot_filename_, |
| 165 reinterpret_cast<char*>(&png_data[0]), png_data.size()); |
| 166 } |
| 167 } |
| 168 } |
| 169 } |
| 170 |
| 171 // True if we found the pdf plugin. Needed since only official builders have |
| 172 // the pdf plugin, so for the rest, and for other devs, we don't want the test |
| 173 // to fail. |
| 174 bool have_plugin_; |
| 175 // True if the snapshot differed from the expected value. |
| 176 bool snapshot_different_; |
| 177 // Internal variable used to synchronize to the renderer. |
| 178 int next_dummy_search_value_; |
| 179 // The filename of the bitmap to compare the snapshot to. |
| 180 std::string expected_filename_; |
| 181 // If the snapshot is different, holds the location where it's saved. |
| 182 FilePath snapshot_filename_; |
| 183 }; |
| 184 |
| 185 // Tests basic PDF rendering. This can be broken depending on bad merges with |
| 186 // the vendor, so it's important that we have basic sanity checking. |
| 187 IN_PROC_BROWSER_TEST_F(PDFBrowserTest, Basic) { |
| 188 if (!have_plugin()) |
| 189 return; |
| 190 |
| 191 ASSERT_NO_FATAL_FAILURE(Load()); |
| 192 ASSERT_NO_FATAL_FAILURE(WaitForResponse()); |
| 193 ASSERT_NO_FATAL_FAILURE(VerifySnapshot("pdf_browsertest.png")); |
| 194 } |
| 195 |
| 196 // Tests that scrolling works. |
| 197 IN_PROC_BROWSER_TEST_F(PDFBrowserTest, Scroll) { |
| 198 if (!have_plugin()) |
| 199 return; |
| 200 |
| 201 ASSERT_NO_FATAL_FAILURE(Load()); |
| 202 |
| 203 // We use wheel mouse event since that's the only one we can easily push to |
| 204 // the renderer. There's no way to push a cross-platform keyboard event at |
| 205 // the moment. |
| 206 WebKit::WebMouseWheelEvent wheel_event; |
| 207 wheel_event.type = WebKit::WebInputEvent::MouseWheel; |
| 208 wheel_event.deltaY = -200; |
| 209 wheel_event.wheelTicksY = -2; |
| 210 TabContents* tab_contents = browser()->GetSelectedTabContents(); |
| 211 tab_contents->render_view_host()->ForwardWheelEvent(wheel_event); |
| 212 ASSERT_NO_FATAL_FAILURE(WaitForResponse()); |
| 213 ASSERT_NO_FATAL_FAILURE(VerifySnapshot("pdf_browsertest_scroll.png")); |
| 214 } |
| 215 |
| 216 IN_PROC_BROWSER_TEST_F(PDFBrowserTest, FindAndCopy) { |
| 217 if (!have_plugin()) |
| 218 return; |
| 219 |
| 220 ASSERT_NO_FATAL_FAILURE(Load()); |
| 221 // Verifies that find in page works. |
| 222 ASSERT_EQ(3, ui_test_utils::FindInPage( |
| 223 browser()->GetSelectedTabContents(), UTF8ToUTF16("adipiscing"), true, |
| 224 false, NULL)); |
| 225 |
| 226 // Verify that copying selected text works. |
| 227 Clipboard clipboard; |
| 228 // Reset the clipboard first. |
| 229 Clipboard::ObjectMap objects; |
| 230 Clipboard::ObjectMapParams params; |
| 231 params.push_back(std::vector<char>()); |
| 232 objects[Clipboard::CBF_TEXT] = params; |
| 233 clipboard.WriteObjects(objects); |
| 234 |
| 235 browser()->GetSelectedTabContents()->render_view_host()->Copy(); |
| 236 ASSERT_NO_FATAL_FAILURE(WaitForResponse()); |
| 237 |
| 238 std::string text; |
| 239 clipboard.ReadAsciiText(Clipboard::BUFFER_STANDARD, &text); |
| 240 ASSERT_EQ("adipiscing", text); |
| 241 } |
| 242 |
| 243 } // namespace |
OLD | NEW |