diff options
author | noelutz@google.com <noelutz@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-23 22:42:17 +0000 |
---|---|---|
committer | noelutz@google.com <noelutz@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-23 22:42:17 +0000 |
commit | 0695ef4d095a441148ae80a08576c25c2ab8bc40 (patch) | |
tree | 73909c32f2b4840452d5051b15f7efcaa60c56db | |
parent | 4bb1dc9e6231e64eae2f411f3a528f082f00f47e (diff) | |
download | chromium_src-0695ef4d095a441148ae80a08576c25c2ab8bc40.zip chromium_src-0695ef4d095a441148ae80a08576c25c2ab8bc40.tar.gz chromium_src-0695ef4d095a441148ae80a08576c25c2ab8bc40.tar.bz2 |
Client-side phishing detection: grab snapshot of custom sized view.
Client-side phishing detection needs to be able to take a snapshot of a
particular page that has a constant size and aspect ratio (e.g., 1024x768).
This CL adds a helper function (safe_browsing::GrabPhishingThumbnail)
which re-sizes the view a given size, grabs the snapshot, and then re-sizes
the view back to its previous size. Note: this function can be slow since
it might re-layout the page twice.
This function will only be called if we are very confident that the current page
is a phishing site in which case we are OK with taking a slow snapshot.
Also, this CL adds a test for the OnMsgPaintAtSize method in the RenderWidget
since it does something very similar than the new Thumbnailer.
BUG=None
TEST=GrabPhishingThumbnail
Review URL: http://codereview.chromium.org/3380001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@60368 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/chrome_renderer.gypi | 2 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 3 | ||||
-rw-r--r-- | chrome/renderer/render_view.cc | 9 | ||||
-rw-r--r-- | chrome/renderer/render_view.h | 8 | ||||
-rw-r--r-- | chrome/renderer/render_widget.h | 2 | ||||
-rw-r--r-- | chrome/renderer/render_widget_browsertest.cc | 141 | ||||
-rw-r--r-- | chrome/renderer/render_widget_browsertest.h | 59 | ||||
-rw-r--r-- | chrome/renderer/safe_browsing/phishing_thumbnailer.cc | 82 | ||||
-rw-r--r-- | chrome/renderer/safe_browsing/phishing_thumbnailer.h | 40 | ||||
-rw-r--r-- | chrome/renderer/safe_browsing/phishing_thumbnailer_browsertest.cc | 33 |
10 files changed, 372 insertions, 7 deletions
diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi index 3d072c0..9fe4e5b 100644 --- a/chrome/chrome_renderer.gypi +++ b/chrome/chrome_renderer.gypi @@ -205,6 +205,8 @@ 'renderer/safe_browsing/phishing_dom_feature_extractor.h', 'renderer/safe_browsing/phishing_term_feature_extractor.cc', 'renderer/safe_browsing/phishing_term_feature_extractor.h', + 'renderer/safe_browsing/phishing_thumbnailer.cc', + 'renderer/safe_browsing/phishing_thumbnailer.h', 'renderer/safe_browsing/phishing_url_feature_extractor.cc', 'renderer/safe_browsing/phishing_url_feature_extractor.h', 'renderer/safe_browsing/scorer.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index dbd9519..3d66fde 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1808,9 +1808,12 @@ 'renderer/pepper_devices_browsertest.cc', 'renderer/render_view_browsertest.cc', 'renderer/render_view_browsertest_mac.mm', + 'renderer/render_widget_browsertest.cc', + 'renderer/render_widget_browsertest.h', 'renderer/safe_browsing/mock_feature_extractor_clock.h', 'renderer/safe_browsing/phishing_classifier_browsertest.cc', 'renderer/safe_browsing/phishing_dom_feature_extractor_browsertest.cc', + 'renderer/safe_browsing/phishing_thumbnailer_browsertest.cc', 'renderer/safe_browsing/render_view_fake_resources_test.cc', 'renderer/safe_browsing/render_view_fake_resources_test.h', 'renderer/translate_helper_browsertest.cc', diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index b570f7e..037a275 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -4661,14 +4661,9 @@ void RenderView::OnResize(const gfx::Size& new_size, const gfx::Rect& resizer_rect) { if (webview()) { webview()->hidePopups(); - if (send_preferred_size_changes_) { - // If resizing to a size larger than |disable_scrollbars_size_limit_| in - // either width or height, allow scroll bars. - bool allow_scrollbars = ( - disable_scrollbars_size_limit_.width() <= new_size.width() || - disable_scrollbars_size_limit_.height() <= new_size.height()); - webview()->mainFrame()->setCanHaveScrollbars(allow_scrollbars); + webview()->mainFrame()->setCanHaveScrollbars( + should_display_scrollbars(new_size.width(), new_size.height())); } } diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h index bf43b2e..f0dad4d4 100644 --- a/chrome/renderer/render_view.h +++ b/chrome/renderer/render_view.h @@ -220,6 +220,14 @@ class RenderView : public RenderWidget, return page_click_tracker_.get(); } + // Returns true if we should display scrollbars for the given view size and + // false if the scrollbars should be hidden. + bool should_display_scrollbars(int width, int height) const { + return (!send_preferred_size_changes_ || + (disable_scrollbars_size_limit_.width() <= width || + disable_scrollbars_size_limit_.height() <= height)); + } + // Called from JavaScript window.external.AddSearchProvider() to add a // keyword for a provider described in the given OpenSearch document. void AddSearchProvider(const std::string& url); diff --git a/chrome/renderer/render_widget.h b/chrome/renderer/render_widget.h index f08f333..6d80b62 100644 --- a/chrome/renderer/render_widget.h +++ b/chrome/renderer/render_widget.h @@ -128,6 +128,8 @@ class RenderWidget : public IPC::Channel::Listener, // Friend RefCounted so that the dtor can be non-public. Using this class // without ref-counting is an error. friend class base::RefCounted<RenderWidget>; + // For unit tests. + friend class RenderWidgetTest; RenderWidget(RenderThreadBase* render_thread, WebKit::WebPopupType popup_type); diff --git a/chrome/renderer/render_widget_browsertest.cc b/chrome/renderer/render_widget_browsertest.cc new file mode 100644 index 0000000..0c4ff04 --- /dev/null +++ b/chrome/renderer/render_widget_browsertest.cc @@ -0,0 +1,141 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "app/surface/transport_dib.h" +#include "base/basictypes.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/ref_counted_memory.h" +#include "base/stringprintf.h" +#include "chrome/common/render_messages.h" +#include "chrome/common/render_messages_params.h" +#include "chrome/renderer/render_widget_browsertest.h" +#include "gfx/codec/jpeg_codec.h" +#include "gfx/size.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/WebKit/WebKit/chromium/public/WebSize.h" +#include "third_party/WebKit/WebKit/chromium/public/WebView.h" + +const int RenderWidgetTest::kNumBytesPerPixel = 4; +const int RenderWidgetTest::kLargeWidth = 1024; +const int RenderWidgetTest::kLargeHeight = 768; +const int RenderWidgetTest::kSmallWidth = 600; +const int RenderWidgetTest::kSmallHeight = 450; +const int RenderWidgetTest::kTextPositionX = 800; +const int RenderWidgetTest::kTextPositionY = 600; +const int RenderWidgetTest::kSequenceNum = 1; +const uint32 RenderWidgetTest::kRedARGB = 0xFFFF0000; + +RenderWidgetTest::RenderWidgetTest() {} + +void RenderWidgetTest::ResizeAndPaint(const gfx::Size& page_size, + const gfx::Size& desired_size, + SkBitmap* snapshot) { + ASSERT_TRUE(snapshot); + scoped_ptr<TransportDIB> pixels( + TransportDIB::Create( + page_size.width() * page_size.height() * kNumBytesPerPixel, + kSequenceNum)); + view_->OnMsgPaintAtSize(pixels->handle(), kSequenceNum, page_size, + desired_size); + ProcessPendingMessages(); + const IPC::Message* msg = render_thread_.sink().GetUniqueMessageMatching( + ViewHostMsg_PaintAtSize_ACK::ID); + ASSERT_NE(static_cast<IPC::Message*>(NULL), msg); + ViewHostMsg_PaintAtSize_ACK::Param params; + ViewHostMsg_PaintAtSize_ACK::Read(msg, ¶ms); + render_thread_.sink().ClearMessages(); + EXPECT_EQ(kSequenceNum, params.a); + gfx::Size size = params.b; + EXPECT_EQ(desired_size, size); + + SkBitmap tmp_bitmap; + tmp_bitmap.setConfig(SkBitmap::kARGB_8888_Config, + size.width(), size.height()); + tmp_bitmap.setPixels(pixels->memory()); + // Copy the pixels from the TransportDIB object to the given snapshot. + ASSERT_TRUE(tmp_bitmap.copyTo(snapshot, SkBitmap::kARGB_8888_Config)); +} + +void RenderWidgetTest::TestResizeAndPaint() { + // Hello World message is only visible if the view size is at least + // kTextPositionX x kTextPositionY + LoadHTML(StringPrintf( + "<html><body><div style='position: absolute; top: %d; left: " + "%d; background-color: red;'>Hello World</div></body></html>", + kTextPositionY, kTextPositionX).c_str()); + WebKit::WebSize old_size = view_->webview()->size(); + + SkBitmap bitmap; + // If we re-size the view to something smaller than where the 'Hello World' + // text is displayed we won't see any text in the snapshot. Hence, + // the snapshot should not contain any red. + gfx::Size size(kSmallWidth, kSmallHeight); + ResizeAndPaint(size, size, &bitmap); + // Make sure that the view has been re-sized to its old size. + EXPECT_EQ(old_size, view_->webview()->size()); + EXPECT_EQ(kSmallWidth, bitmap.width()); + EXPECT_EQ(kSmallHeight, bitmap.height()); + EXPECT_FALSE(ImageContainsColor(bitmap, kRedARGB)); + + // Since we ask for the view to be re-sized to something larger than where the + // 'Hello World' text is written the text should be visible in the snapshot. + // Hence, the snapshot should contain some red. + size.SetSize(kLargeWidth, kLargeHeight); + ResizeAndPaint(size, size, &bitmap); + EXPECT_EQ(old_size, view_->webview()->size()); + EXPECT_EQ(kLargeWidth, bitmap.width()); + EXPECT_EQ(kLargeHeight, bitmap.height()); + EXPECT_TRUE(ImageContainsColor(bitmap, kRedARGB)); + + // Even if the desired size is smaller than where the text is located we + // should still see the 'Hello World' message since the view size is + // still large enough. + ResizeAndPaint(size, gfx::Size(kSmallWidth, kSmallHeight), &bitmap); + EXPECT_EQ(old_size, view_->webview()->size()); + EXPECT_EQ(kSmallWidth, bitmap.width()); + EXPECT_EQ(kSmallHeight, bitmap.height()); + EXPECT_TRUE(ImageContainsColor(bitmap, kRedARGB)); +} + +bool RenderWidgetTest::ImageContainsColor(const SkBitmap& bitmap, + uint32 argb_color) { + SkAutoLockPixels lock(bitmap); + bool ready = bitmap.readyToDraw(); + EXPECT_TRUE(ready); + if (!ready) { + return false; + } + for (int x = 0; x < bitmap.width(); ++x) { + for (int y = 0; y < bitmap.height(); ++y) { + if (argb_color == *bitmap.getAddr32(x, y)) { + return true; + } + } + } + return false; +} + +void RenderWidgetTest::OutputBitmapToFile(const SkBitmap& bitmap, + const FilePath& file_path) { + scoped_refptr<RefCountedBytes> bitmap_data = new RefCountedBytes(); + SkAutoLockPixels lock(bitmap); + ASSERT_TRUE(gfx::JPEGCodec::Encode( + reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)), + gfx::JPEGCodec::FORMAT_BGRA, + bitmap.width(), + bitmap.height(), + static_cast<int>(bitmap.rowBytes()), + 90 /* quality */, + &bitmap_data->data)); + ASSERT_LT(0, file_util::WriteFile( + file_path, + reinterpret_cast<const char*>(bitmap_data->front()), + bitmap_data->size())); +} + +TEST_F(RenderWidgetTest, OnMsgPaintAtSize) { + TestResizeAndPaint(); +} diff --git a/chrome/renderer/render_widget_browsertest.h b/chrome/renderer/render_widget_browsertest.h new file mode 100644 index 0000000..cf124dd --- /dev/null +++ b/chrome/renderer/render_widget_browsertest.h @@ -0,0 +1,59 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_RENDERER_RENDER_WIDGET_BROWSERTEST_H_ +#define CHROME_RENDERER_RENDER_WIDGET_BROWSERTEST_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/file_path.h" +#include "chrome/test/render_view_test.h" + +namespace gfx { +class Size; +} + +class SkBitmap; +class TransportDIB; + +class RenderWidgetTest : public RenderViewTest { + public: + RenderWidgetTest(); + + protected: + static const int kNumBytesPerPixel; + static const int kLargeWidth; + static const int kLargeHeight; + static const int kSmallWidth; + static const int kSmallHeight; + static const int kTextPositionX; + static const int kTextPositionY; + static const int kSequenceNum; + static const uint32 kRedARGB; + + // Helper function which calls OnMsgPaintAtSize and also paints the result + // in the given bitmap. The widget is resized to |page_size| before we paint + // and the final image is resized to |desired_size|. This method is virtual so + // that TestResizeAndPaint() can be reused by subclasses of this test class. + virtual void ResizeAndPaint(const gfx::Size& page_size, + const gfx::Size& desired_size, + SkBitmap* snapshot); + + // Test for ResizeAndPaint. + void TestResizeAndPaint(); + + // Helper function which returns true if the given bitmap contains the given + // ARGB color and false otherwise. + bool ImageContainsColor(const SkBitmap& bitmap, uint32 argb_color); + + // This can be used for debugging if you want to output a bitmap + // image to a file. + // FilePath tmp_path; + // file_util::CreateTemporaryFile(&tmp_path); + // OutputBitmapToFile(bitmap, tmp_path); + // LOG(INFO) << "Bitmap image stored at: " << tmp_path.value(); + void OutputBitmapToFile(const SkBitmap& bitmap, const FilePath& file_path); +}; + +#endif // CHROME_RENDERER_RENDER_WIDGET_BROWSERTEST_H_ diff --git a/chrome/renderer/safe_browsing/phishing_thumbnailer.cc b/chrome/renderer/safe_browsing/phishing_thumbnailer.cc new file mode 100644 index 0000000..439a40e --- /dev/null +++ b/chrome/renderer/safe_browsing/phishing_thumbnailer.cc @@ -0,0 +1,82 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/renderer/safe_browsing/phishing_thumbnailer.h" + +#include "base/histogram.h" +#include "base/logging.h" +#include "base/time.h" +#include "chrome/renderer/render_view.h" +#include "gfx/size.h" +#include "skia/ext/bitmap_platform_device.h" +#include "skia/ext/image_operations.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h" +#include "third_party/WebKit/WebKit/chromium/public/WebRect.h" +#include "third_party/WebKit/WebKit/chromium/public/WebSize.h" +#include "third_party/WebKit/WebKit/chromium/public/WebView.h" +#include "webkit/glue/webkit_glue.h" + +using WebKit::WebRect; +using WebKit::WebSize; +using WebKit::WebView; + +namespace safe_browsing { + +SkBitmap GrabPhishingThumbnail(RenderView* render_view, + const gfx::Size& view_size, + const gfx::Size& thumbnail_size) { + if (!render_view || !render_view->webview()) { + NOTREACHED(); + return SkBitmap(); + } + WebView* view = render_view->webview(); + base::TimeTicks beginning_time = base::TimeTicks::Now(); + skia::PlatformCanvas canvas; + if (!canvas.initialize(view_size.width(), view_size.height(), true)) { + return SkBitmap(); + } + + // Make sure we are not using any zoom when we take the snapshot. We will + // restore the previous zoom level after the snapshot is taken. + int old_zoom_level = view->zoomLevel(); + if (view->zoomLevel() != 0) { + view->setZoomLevel(false, 0); + } + WebSize old_size = view->size(); + // TODO(noelutz): not only should we hide all scroll bars but we should also + // make sure that all scroll-bars are at the top. + view->mainFrame()->setCanHaveScrollbars(false); // always hide scrollbars. + view->resize(view_size); + view->layout(); + view->paint(webkit_glue::ToWebCanvas(&canvas), + WebRect(0, 0, view_size.width(), view_size.height())); + + skia::BitmapPlatformDevice& device = + static_cast<skia::BitmapPlatformDevice&>(canvas.getTopPlatformDevice()); + + // Now resize the thumbnail to the right size. Note: it is important that we + // use this resize algorithm here. + const SkBitmap& bitmap = device.accessBitmap(false); + SkBitmap thumbnail = skia::ImageOperations::Resize( + bitmap, + skia::ImageOperations::RESIZE_LANCZOS3, + thumbnail_size.width(), + thumbnail_size.height()); + + // Put things back as they were before. + if (view->zoomLevel() != old_zoom_level) { + view->setZoomLevel(false, old_zoom_level); + } + // Maybe re-display the scrollbars and resize the view to its old size. + view->mainFrame()->setCanHaveScrollbars( + render_view->should_display_scrollbars(old_size.width, old_size.height)); + view->resize(old_size); + + HISTOGRAM_TIMES("SBClientPhishing.GrabPhishingThumbnail", + base::TimeTicks::Now() - beginning_time); + return thumbnail; +} + +} // namespace safe_browsing diff --git a/chrome/renderer/safe_browsing/phishing_thumbnailer.h b/chrome/renderer/safe_browsing/phishing_thumbnailer.h new file mode 100644 index 0000000..99460c6 --- /dev/null +++ b/chrome/renderer/safe_browsing/phishing_thumbnailer.h @@ -0,0 +1,40 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Helper function which takes a custom thumbnail for the client-side phishing +// detector. +// +// Important note: in some circumstances grabbing a thumbnail using +// this function can be very slow since it may need to re-layout the page +// twice. Also, using this class may have some side effects (e.g., +// onScroll and onResize will be called, etc). Currently, this function +// is only used if Chrome is almost certain that the current page is +// phishing (according to the client-side phishing detector) in which +// case we are not too worried about performance or possibly causing +// some JavaScript weirdness on the page. + +#ifndef CHROME_RENDERER_SAFE_BROWSING_PHISHING_THUMBNAILER_H_ +#define CHROME_RENDERER_SAFE_BROWSING_PHISHING_THUMBNAILER_H_ +#pragma once + +namespace gfx { +class Size; +} +class RenderView; +class SkBitmap; + +namespace safe_browsing { + +// Grabs a thumbnail returns a bitmap that contains the result. Before grabbing +// a snapshot the view will be re-sized to |view_size| and the resulting +// snapshot will then be re-sized to the given |thumbnail_size|. If grabbing +// the thumbnail fails this function returns SkBitmap() in which case calling +// isNull() on the returned bitmap will return true. +SkBitmap GrabPhishingThumbnail(RenderView* render_view, + const gfx::Size& view_size, + const gfx::Size& thumbnail_size); + +} // namespace safe_browsing + +#endif // CHROME_RENDERER_SAFE_BROWSING_PHISHING_THUMBNAILER_H_ diff --git a/chrome/renderer/safe_browsing/phishing_thumbnailer_browsertest.cc b/chrome/renderer/safe_browsing/phishing_thumbnailer_browsertest.cc new file mode 100644 index 0000000..a894b2d --- /dev/null +++ b/chrome/renderer/safe_browsing/phishing_thumbnailer_browsertest.cc @@ -0,0 +1,33 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/basictypes.h" +#include "chrome/renderer/render_widget_browsertest.h" +#include "chrome/renderer/safe_browsing/phishing_thumbnailer.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/WebKit/WebKit/chromium/public/WebSize.h" +#include "third_party/WebKit/WebKit/chromium/public/WebView.h" + +namespace safe_browsing { + +class ThumbnailerTest : public RenderWidgetTest { + public: + ThumbnailerTest() {} + + protected: + virtual void ResizeAndPaint(const gfx::Size& page_size, + const gfx::Size& desired_size, + SkBitmap* snapshot) { + ASSERT_TRUE(snapshot); + *snapshot = GrabPhishingThumbnail(view_.get(), page_size, desired_size); + EXPECT_FALSE(snapshot->isNull()); + } +}; + +TEST_F(ThumbnailerTest, GrabPhishingThumbnail) { + TestResizeAndPaint(); +} + +} // namespace safe_browsing |