Simplify platform_canvas.h by recognizing that PlatformCanvas does not actually extend
[chromium-blink-merge.git] / webkit / tools / test_shell / webwidget_host_win.cc
blobabb6433e84cd174e6e50250ab18cf2fb69e0e358
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.
5 #include "webkit/tools/test_shell/webwidget_host.h"
7 #include "base/logging.h"
8 #include "skia/ext/platform_canvas.h"
9 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
10 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPopupMenu.h"
11 #include "third_party/WebKit/Source/WebKit/chromium/public/WebScreenInfo.h"
12 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebSize.h"
13 #include "third_party/WebKit/Source/WebKit/chromium/public/win/WebInputEventFactory.h"
14 #include "third_party/WebKit/Source/WebKit/chromium/public/win/WebScreenInfoFactory.h"
15 #include "ui/base/win/hwnd_util.h"
16 #include "ui/gfx/rect.h"
17 #include "webkit/tools/test_shell/test_shell.h"
19 using WebKit::WebInputEvent;
20 using WebKit::WebInputEventFactory;
21 using WebKit::WebKeyboardEvent;
22 using WebKit::WebMouseEvent;
23 using WebKit::WebMouseWheelEvent;
24 using WebKit::WebPopupMenu;
25 using WebKit::WebScreenInfo;
26 using WebKit::WebScreenInfoFactory;
27 using WebKit::WebSize;
28 using WebKit::WebWidget;
29 using WebKit::WebWidgetClient;
31 static const wchar_t kWindowClassName[] = L"WebWidgetHost";
33 /*static*/
34 WebWidgetHost* WebWidgetHost::Create(HWND parent_view,
35 WebWidgetClient* client) {
36 WebWidgetHost* host = new WebWidgetHost();
38 static bool registered_class = false;
39 if (!registered_class) {
40 WNDCLASSEX wcex = {0};
41 wcex.cbSize = sizeof(wcex);
42 wcex.style = CS_DBLCLKS;
43 wcex.lpfnWndProc = WebWidgetHost::WndProc;
44 wcex.hInstance = GetModuleHandle(NULL);
45 wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
46 wcex.lpszClassName = kWindowClassName;
47 RegisterClassEx(&wcex);
48 registered_class = true;
51 host->view_ = CreateWindowEx(WS_EX_TOOLWINDOW,
52 kWindowClassName, kWindowClassName, WS_POPUP,
53 0, 0, 0, 0,
54 parent_view, NULL, GetModuleHandle(NULL), NULL);
55 ui::SetWindowUserData(host->view_, host);
57 host->webwidget_ = WebPopupMenu::create(client);
59 return host;
62 static WebWidgetHost* FromWindow(HWND view) {
63 return reinterpret_cast<WebWidgetHost*>(ui::GetWindowUserData(view));
66 /*static*/
67 LRESULT CALLBACK WebWidgetHost::WndProc(HWND hwnd, UINT message, WPARAM wparam,
68 LPARAM lparam) {
69 WebWidgetHost* host = FromWindow(hwnd);
70 if (host && !host->WndProc(message, wparam, lparam)) {
71 switch (message) {
72 case WM_DESTROY:
73 delete host;
74 break;
76 case WM_PAINT: {
77 RECT rect;
78 if (GetUpdateRect(hwnd, &rect, FALSE)) {
79 host->UpdatePaintRect(gfx::Rect(rect));
81 host->Paint();
82 return 0;
85 case WM_ERASEBKGND:
86 // Do nothing here to avoid flashing, the background will be erased
87 // during painting.
88 return 0;
90 case WM_SIZE:
91 host->Resize(lparam);
92 return 0;
94 case WM_MOUSEMOVE:
95 case WM_MOUSELEAVE:
96 case WM_LBUTTONDOWN:
97 case WM_MBUTTONDOWN:
98 case WM_RBUTTONDOWN:
99 case WM_LBUTTONUP:
100 case WM_MBUTTONUP:
101 case WM_RBUTTONUP:
102 case WM_LBUTTONDBLCLK:
103 case WM_MBUTTONDBLCLK:
104 case WM_RBUTTONDBLCLK:
105 host->MouseEvent(message, wparam, lparam);
106 break;
108 case WM_MOUSEWHEEL:
109 host->WheelEvent(wparam, lparam);
110 break;
112 case WM_CAPTURECHANGED:
113 case WM_CANCELMODE:
114 host->CaptureLostEvent();
115 break;
117 // TODO(darin): add WM_SYSKEY{DOWN/UP} to capture ALT key actions
118 case WM_KEYDOWN:
119 case WM_KEYUP:
120 case WM_SYSKEYDOWN:
121 case WM_SYSKEYUP:
122 case WM_CHAR:
123 case WM_SYSCHAR:
124 host->KeyEvent(message, wparam, lparam);
125 break;
127 case WM_SETFOCUS:
128 host->SetFocus(true);
129 break;
131 case WM_KILLFOCUS:
132 host->SetFocus(false);
133 break;
137 return DefWindowProc(hwnd, message, wparam, lparam);;
140 void WebWidgetHost::DidInvalidateRect(const gfx::Rect& damaged_rect) {
141 DLOG_IF(WARNING, painting_) << "unexpected invalidation while painting";
143 // If this invalidate overlaps with a pending scroll, then we have to
144 // downgrade to invalidating the scroll rect.
145 if (damaged_rect.Intersects(scroll_rect_)) {
146 paint_rect_.Union(scroll_rect_);
147 ResetScrollRect();
149 paint_rect_.Union(damaged_rect);
151 RECT r = damaged_rect.ToRECT();
152 InvalidateRect(view_, &r, FALSE);
155 void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) {
156 if (dx != 0 && dy != 0) {
157 // We only support uni-directional scroll
158 DidScrollRect(0, dy, clip_rect);
159 dy = 0;
162 // If we already have a pending scroll operation or if this scroll operation
163 // intersects the existing paint region, then just failover to invalidating.
164 if (!scroll_rect_.IsEmpty() || paint_rect_.Intersects(clip_rect)) {
165 paint_rect_.Union(scroll_rect_);
166 ResetScrollRect();
167 paint_rect_.Union(clip_rect);
170 // We will perform scrolling lazily, when requested to actually paint.
171 scroll_rect_ = clip_rect;
172 scroll_dx_ = dx;
173 scroll_dy_ = dy;
175 RECT r = clip_rect.ToRECT();
176 InvalidateRect(view_, &r, FALSE);
179 void WebWidgetHost::ScheduleComposite() {
180 if (!webwidget_)
181 return;
182 WebSize size = webwidget_->size();
183 gfx::Rect rect(0, 0, size.width, size.height);
184 RECT r = rect.ToRECT();
185 InvalidateRect(view_, &r, FALSE);
188 void WebWidgetHost::SetCursor(HCURSOR cursor) {
189 SetClassLong(view_, GCL_HCURSOR,
190 static_cast<LONG>(reinterpret_cast<LONG_PTR>(cursor)));
191 ::SetCursor(cursor);
194 void WebWidgetHost::DiscardBackingStore() {
195 canvas_.reset();
198 WebWidgetHost::WebWidgetHost()
199 : view_(NULL),
200 webwidget_(NULL),
201 track_mouse_leave_(false),
202 scroll_dx_(0),
203 scroll_dy_(0),
204 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
205 set_painting(false);
208 WebWidgetHost::~WebWidgetHost() {
209 ui::SetWindowUserData(view_, 0);
211 TrackMouseLeave(false);
213 webwidget_->close();
216 bool WebWidgetHost::WndProc(UINT message, WPARAM wparam, LPARAM lparam) {
217 switch (message) {
218 case WM_ACTIVATE:
219 if (wparam == WA_INACTIVE) {
220 PostMessage(view_, WM_CLOSE, 0, 0);
221 return true;
223 break;
226 return false;
229 void WebWidgetHost::UpdatePaintRect(const gfx::Rect& rect) {
230 paint_rect_.Union(rect);
233 void WebWidgetHost::Paint() {
234 RECT r;
235 GetClientRect(view_, &r);
236 gfx::Rect client_rect(r);
238 // Allocate a canvas if necessary
239 if (!canvas_.get()) {
240 ResetScrollRect();
241 paint_rect_ = client_rect;
242 canvas_.reset(skia::CreatePlatformCanvas(
243 paint_rect_.width(), paint_rect_.height(), true));
246 webwidget_->animate(0.0);
248 // This may result in more invalidation
249 webwidget_->layout();
251 // Scroll the canvas if necessary
252 scroll_rect_.Intersect(client_rect);
253 if (!scroll_rect_.IsEmpty()) {
254 skia::ScopedPlatformPaint scoped_platform_paint(canvas_.get());
255 HDC hdc = scoped_platform_paint.GetPlatformSurface();
257 RECT damaged_rect, r = scroll_rect_.ToRECT();
258 ScrollDC(hdc, scroll_dx_, scroll_dy_, NULL, &r, NULL, &damaged_rect);
260 PaintRect(gfx::Rect(damaged_rect));
262 ResetScrollRect();
264 // Paint the canvas if necessary. Allow painting to generate extra rects the
265 // first time we call it. This is necessary because some WebCore rendering
266 // objects update their layout only when painted.
267 for (int i = 0; i < 2; ++i) {
268 paint_rect_.Intersect(client_rect);
269 if (!paint_rect_.IsEmpty()) {
270 gfx::Rect rect(paint_rect_);
271 paint_rect_ = gfx::Rect();
273 DLOG_IF(WARNING, i == 1) << "painting caused additional invalidations";
274 PaintRect(rect);
277 DCHECK(paint_rect_.IsEmpty());
279 // Paint to the screen
280 PAINTSTRUCT ps;
281 BeginPaint(view_, &ps);
282 skia::DrawToNativeContext(canvas_.get(), ps.hdc, ps.rcPaint.left,
283 ps.rcPaint.top, &ps.rcPaint);
284 EndPaint(view_, &ps);
286 // Draw children
287 UpdateWindow(view_);
290 WebScreenInfo WebWidgetHost::GetScreenInfo() {
291 return WebScreenInfoFactory::screenInfo(view_);
294 void WebWidgetHost::Resize(LPARAM lparam) {
295 // Force an entire re-paint. TODO(darin): Maybe reuse this memory buffer.
296 DiscardBackingStore();
298 webwidget_->resize(WebSize(LOWORD(lparam), HIWORD(lparam)));
301 void WebWidgetHost::MouseEvent(UINT message, WPARAM wparam, LPARAM lparam) {
302 const WebMouseEvent& event = WebInputEventFactory::mouseEvent(
303 view_, message, wparam, lparam);
304 webwidget_->handleInputEvent(event);
305 switch (event.type) {
306 case WebInputEvent::MouseMove:
307 TrackMouseLeave(true);
308 break;
309 case WebInputEvent::MouseLeave:
310 TrackMouseLeave(false);
311 break;
312 case WebInputEvent::MouseDown:
313 SetCapture(view_);
314 // This mimics a temporary workaround in RenderWidgetHostViewWin for bug
315 // 765011 to get focus when the mouse is clicked. This happens after the
316 // mouse down event is sent to the renderer because normally Windows does
317 // a WM_SETFOCUS after WM_LBUTTONDOWN.
318 ::SetFocus(view_);
319 break;
320 case WebInputEvent::MouseUp:
321 if (GetCapture() == view_)
322 ReleaseCapture();
323 break;
327 void WebWidgetHost::WheelEvent(WPARAM wparam, LPARAM lparam) {
328 const WebMouseWheelEvent& event = WebInputEventFactory::mouseWheelEvent(
329 view_, WM_MOUSEWHEEL, wparam, lparam);
330 webwidget_->handleInputEvent(event);
333 void WebWidgetHost::KeyEvent(UINT message, WPARAM wparam, LPARAM lparam) {
334 const WebKeyboardEvent& event = WebInputEventFactory::keyboardEvent(
335 view_, message, wparam, lparam);
336 webwidget_->handleInputEvent(event);
339 void WebWidgetHost::CaptureLostEvent() {
340 webwidget_->mouseCaptureLost();
343 void WebWidgetHost::SetFocus(bool enable) {
344 // Ignore focus calls in layout test mode so that tests don't mess with each
345 // other's focus when running in parallel.
346 if (!TestShell::layout_test_mode())
347 webwidget_->setFocus(enable);
350 void WebWidgetHost::TrackMouseLeave(bool track) {
351 if (track == track_mouse_leave_)
352 return;
353 track_mouse_leave_ = track;
355 DCHECK(view_);
357 TRACKMOUSEEVENT tme;
358 tme.cbSize = sizeof(TRACKMOUSEEVENT);
359 tme.dwFlags = TME_LEAVE;
360 if (!track_mouse_leave_)
361 tme.dwFlags |= TME_CANCEL;
362 tme.hwndTrack = view_;
364 TrackMouseEvent(&tme);
367 void WebWidgetHost::ResetScrollRect() {
368 scroll_rect_ = gfx::Rect();
369 scroll_dx_ = 0;
370 scroll_dy_ = 0;
373 void WebWidgetHost::PaintRect(const gfx::Rect& rect) {
374 #ifndef NDEBUG
375 DCHECK(!painting_);
376 #endif
377 DCHECK(canvas_.get());
379 set_painting(true);
380 webwidget_->paint(canvas_.get(), rect);
381 set_painting(false);