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. 4 5 #include "chrome/browser/ui/pdf/pdf_browsertest_base.h" 6 7 #include <algorithm> 8 #include <vector> 9 10 #include "base/path_service.h" 11 #include "base/strings/string_number_conversions.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/ui_test_utils.h" 19 #include "content/public/browser/render_view_host.h" 20 #include "content/public/browser/web_contents.h" 21 #include "third_party/skia/include/core/SkBitmap.h" 22 #include "ui/gfx/codec/png_codec.h" 23 #include "ui/gfx/screen.h" 24 25 #if defined(OS_LINUX) 26 #include "base/command_line.h" 27 #include "content/public/common/content_switches.h" 28 #endif 29 30 #if defined(OS_CHROMEOS) 31 #include "ui/compositor/compositor_switches.h" 32 #endif 33 34 namespace { 35 36 // Include things like browser frame and scrollbar and make sure we're bigger 37 // than the test pdf document. 38 const int kBrowserWidth = 1000; 39 const int kBrowserHeight = 600; 40 41 } // namespace 42 43 PDFBrowserTest::PDFBrowserTest() 44 : snapshot_different_(true), 45 next_dummy_search_value_(0), 46 load_stop_notification_count_(0) { 47 base::FilePath src_dir; 48 EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &src_dir)); 49 pdf_test_server_.ServeFilesFromDirectory(src_dir.AppendASCII( 50 "chrome/test/data/pdf_private")); 51 } 52 53 PDFBrowserTest::~PDFBrowserTest() { 54 } 55 56 void PDFBrowserTest::Load() { 57 // Make sure to set the window size before rendering, as otherwise rendering 58 // to a smaller window and then expanding leads to slight anti-aliasing 59 // differences of the text and the pixel comparison fails. 60 gfx::Rect bounds(gfx::Rect(0, 0, kBrowserWidth, kBrowserHeight)); 61 gfx::Rect screen_bounds = 62 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().bounds(); 63 ASSERT_GT(screen_bounds.width(), kBrowserWidth); 64 ASSERT_GT(screen_bounds.height(), kBrowserHeight); 65 browser()->window()->SetBounds(bounds); 66 67 GURL url(ui_test_utils::GetTestUrl( 68 base::FilePath(FILE_PATH_LITERAL("pdf_private")), 69 base::FilePath(FILE_PATH_LITERAL("pdf_browsertest.pdf")))); 70 ui_test_utils::NavigateToURL(browser(), url); 71 } 72 73 void PDFBrowserTest::WaitForResponse() { 74 // Even if the plugin has loaded the data or scrolled, because of how 75 // pepper painting works, we might not have the data. One way to force this 76 // to be flushed is to do a find operation, since on this two-page test 77 // document, it'll wait for us to flush the renderer message loop twice and 78 // also the browser's once, at which point we're guaranteed to have updated 79 // the backingstore. Hacky, but it works. 80 // Note that we need to change the text each time, because if we don't the 81 // renderer code will think the second message is to go to next result, but 82 // there are none so the plugin will assert. 83 84 base::string16 query = base::UTF8ToUTF16( 85 std::string("xyzxyz" + base::IntToString(next_dummy_search_value_++))); 86 ASSERT_EQ(0, ui_test_utils::FindInPage( 87 browser()->tab_strip_model()->GetActiveWebContents(), 88 query, true, false, NULL, NULL)); 89 } 90 91 bool PDFBrowserTest::VerifySnapshot(const std::string& expected_filename) { 92 snapshot_different_ = true; 93 expected_filename_ = expected_filename; 94 content::WebContents* web_contents = 95 browser()->tab_strip_model()->GetActiveWebContents(); 96 DCHECK(web_contents); 97 98 content::RenderWidgetHost* rwh = web_contents->GetRenderViewHost(); 99 rwh->CopyFromBackingStore( 100 gfx::Rect(), 101 gfx::Size(), 102 base::Bind(&PDFBrowserTest::CopyFromBackingStoreCallback, this), 103 kN32_SkColorType); 104 105 content::RunMessageLoop(); 106 107 if (snapshot_different_) { 108 LOG(INFO) << "Rendering didn't match, see result " 109 << snapshot_filename_.value(); 110 } 111 return !snapshot_different_; 112 } 113 114 void PDFBrowserTest::CopyFromBackingStoreCallback(bool success, 115 const SkBitmap& bitmap) { 116 base::MessageLoopForUI::current()->Quit(); 117 ASSERT_EQ(success, true); 118 base::FilePath reference = ui_test_utils::GetTestFilePath( 119 base::FilePath(FILE_PATH_LITERAL("pdf_private")), 120 base::FilePath().AppendASCII(expected_filename_)); 121 base::File::Info info; 122 ASSERT_TRUE(base::GetFileInfo(reference, &info)); 123 int size = static_cast<size_t>(info.size); 124 scoped_ptr<char[]> data(new char[size]); 125 ASSERT_EQ(size, base::ReadFile(reference, data.get(), size)); 126 127 int w, h; 128 std::vector<unsigned char> decoded; 129 ASSERT_TRUE(gfx::PNGCodec::Decode( 130 reinterpret_cast<unsigned char*>(data.get()), size, 131 gfx::PNGCodec::FORMAT_BGRA, &decoded, &w, &h)); 132 int32* ref_pixels = reinterpret_cast<int32*>(&decoded[0]); 133 134 int32* pixels = static_cast<int32*>(bitmap.getPixels()); 135 136 // Get the background color, and use it to figure out the x-offsets in 137 // each image. The reason is that depending on the theme in the OS, the 138 // same browser width can lead to slightly different plugin sizes, so the 139 // pdf content will start at different x offsets. 140 // Also note that the images we saved are cut off before the scrollbar, as 141 // that'll change depending on the theme, and also cut off vertically so 142 // that the ui controls don't show up, as those fade-in and so the timing 143 // will affect their transparency. 144 int32 bg_color = ref_pixels[0]; 145 int ref_x_offset, snapshot_x_offset; 146 for (ref_x_offset = 0; ref_x_offset < w; ++ref_x_offset) { 147 if (ref_pixels[ref_x_offset] != bg_color) 148 break; 149 } 150 151 for (snapshot_x_offset = 0; snapshot_x_offset < bitmap.width(); 152 ++snapshot_x_offset) { 153 if (pixels[snapshot_x_offset] != bg_color) 154 break; 155 } 156 157 int x_max = std::min(w - ref_x_offset, bitmap.width() - snapshot_x_offset); 158 int y_max = std::min(h, bitmap.height()); 159 int stride = bitmap.rowBytes(); 160 snapshot_different_ = false; 161 for (int y = 0; y < y_max && !snapshot_different_; ++y) { 162 for (int x = 0; x < x_max && !snapshot_different_; ++x) { 163 if (pixels[y * stride / sizeof(int32) + x + snapshot_x_offset] != 164 ref_pixels[y * w + x + ref_x_offset]) 165 snapshot_different_ = true; 166 } 167 } 168 169 if (snapshot_different_) { 170 std::vector<unsigned char> png_data; 171 gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &png_data); 172 if (base::CreateTemporaryFile(&snapshot_filename_)) { 173 base::WriteFile(snapshot_filename_, 174 reinterpret_cast<char*>(&png_data[0]), png_data.size()); 175 } 176 } 177 } 178 179 void PDFBrowserTest::Observe(int type, 180 const content::NotificationSource& source, 181 const content::NotificationDetails& details) { 182 DCHECK_EQ(content::NOTIFICATION_LOAD_STOP, type); 183 load_stop_notification_count_++; 184 } 185 186 void PDFBrowserTest::SetUpCommandLine(base::CommandLine* command_line) { 187 #if defined(OS_LINUX) 188 // Calling RenderWidgetHost::CopyFromBackingStore() with the GPU enabled 189 // fails on Linux. 190 command_line->AppendSwitch(switches::kDisableGpu); 191 #endif 192 193 #if defined(OS_CHROMEOS) 194 // Also need on CrOS in addition to disabling the GPU above. 195 command_line->AppendSwitch(switches::kUIDisableThreadedCompositing); 196 #endif 197 } 198