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";
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
,
54 parent_view
, NULL
, GetModuleHandle(NULL
), NULL
);
55 ui::SetWindowUserData(host
->view_
, host
);
57 host
->webwidget_
= WebPopupMenu::create(client
);
62 static WebWidgetHost
* FromWindow(HWND view
) {
63 return reinterpret_cast<WebWidgetHost
*>(ui::GetWindowUserData(view
));
67 LRESULT CALLBACK
WebWidgetHost::WndProc(HWND hwnd
, UINT message
, WPARAM wparam
,
69 WebWidgetHost
* host
= FromWindow(hwnd
);
70 if (host
&& !host
->WndProc(message
, wparam
, lparam
)) {
78 if (GetUpdateRect(hwnd
, &rect
, FALSE
)) {
79 host
->UpdatePaintRect(gfx::Rect(rect
));
86 // Do nothing here to avoid flashing, the background will be erased
102 case WM_LBUTTONDBLCLK
:
103 case WM_MBUTTONDBLCLK
:
104 case WM_RBUTTONDBLCLK
:
105 host
->MouseEvent(message
, wparam
, lparam
);
109 host
->WheelEvent(wparam
, lparam
);
112 case WM_CAPTURECHANGED
:
114 host
->CaptureLostEvent();
117 // TODO(darin): add WM_SYSKEY{DOWN/UP} to capture ALT key actions
124 host
->KeyEvent(message
, wparam
, lparam
);
128 host
->SetFocus(true);
132 host
->SetFocus(false);
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_
);
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
);
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_
);
167 paint_rect_
.Union(clip_rect
);
170 // We will perform scrolling lazily, when requested to actually paint.
171 scroll_rect_
= clip_rect
;
175 RECT r
= clip_rect
.ToRECT();
176 InvalidateRect(view_
, &r
, FALSE
);
179 void WebWidgetHost::ScheduleComposite() {
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
)));
194 void WebWidgetHost::DiscardBackingStore() {
198 WebWidgetHost::WebWidgetHost()
201 track_mouse_leave_(false),
204 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
208 WebWidgetHost::~WebWidgetHost() {
209 ui::SetWindowUserData(view_
, 0);
211 TrackMouseLeave(false);
216 bool WebWidgetHost::WndProc(UINT message
, WPARAM wparam
, LPARAM lparam
) {
219 if (wparam
== WA_INACTIVE
) {
220 PostMessage(view_
, WM_CLOSE
, 0, 0);
229 void WebWidgetHost::UpdatePaintRect(const gfx::Rect
& rect
) {
230 paint_rect_
.Union(rect
);
233 void WebWidgetHost::Paint() {
235 GetClientRect(view_
, &r
);
236 gfx::Rect
client_rect(r
);
238 // Allocate a canvas if necessary
239 if (!canvas_
.get()) {
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
));
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";
277 DCHECK(paint_rect_
.IsEmpty());
279 // Paint to the screen
281 BeginPaint(view_
, &ps
);
282 skia::DrawToNativeContext(canvas_
.get(), ps
.hdc
, ps
.rcPaint
.left
,
283 ps
.rcPaint
.top
, &ps
.rcPaint
);
284 EndPaint(view_
, &ps
);
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);
309 case WebInputEvent::MouseLeave
:
310 TrackMouseLeave(false);
312 case WebInputEvent::MouseDown
:
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.
320 case WebInputEvent::MouseUp
:
321 if (GetCapture() == view_
)
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_
)
353 track_mouse_leave_
= track
;
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();
373 void WebWidgetHost::PaintRect(const gfx::Rect
& rect
) {
377 DCHECK(canvas_
.get());
380 webwidget_
->paint(canvas_
.get(), rect
);