Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(333)

Side by Side Diff: webkit/tools/test_shell/webwidget_host_gtk.cc

Issue 15028002: Delete test_shell. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add dummy test_shell build target. Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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.
4
5 #include "webkit/tools/test_shell/webwidget_host.h"
6
7 #include <cairo/cairo.h>
8 #include <gtk/gtk.h>
9
10 #include "base/basictypes.h"
11 #include "base/logging.h"
12 #include "skia/ext/bitmap_platform_device.h"
13 #include "skia/ext/platform_canvas.h"
14 #include "skia/ext/platform_device.h"
15 #include "third_party/WebKit/Source/Platform/chromium/public/WebSize.h"
16 #include "third_party/WebKit/Source/WebKit/chromium/public/gtk/WebInputEventFact ory.h"
17 #include "third_party/WebKit/Source/WebKit/chromium/public/x11/WebScreenInfoFact ory.h"
18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPopupMenu.h"
20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebScreenInfo.h"
21 #include "webkit/tools/test_shell/test_shell.h"
22 #include "webkit/tools/test_shell/test_shell_x11.h"
23
24 using WebKit::WebInputEventFactory;
25 using WebKit::WebKeyboardEvent;
26 using WebKit::WebMouseEvent;
27 using WebKit::WebMouseWheelEvent;
28 using WebKit::WebPopupMenu;
29 using WebKit::WebScreenInfo;
30 using WebKit::WebScreenInfoFactory;
31 using WebKit::WebSize;
32 using WebKit::WebWidgetClient;
33
34 namespace {
35
36 // Used to store a backpointer to WebWidgetHost from our GtkWidget.
37 const char kWebWidgetHostKey[] = "webwidgethost";
38
39 // In response to an invalidation, we call into WebKit to do layout. On
40 // Windows, WM_PAINT is a virtual message so any extra invalidates that come up
41 // while it's doing layout are implicitly swallowed as soon as we actually do
42 // drawing via BeginPaint.
43 //
44 // Though GTK does know how to collapse multiple paint requests, it won't erase
45 // paint requests from the future when we start drawing. To avoid an infinite
46 // cycle of repaints, we track whether we're currently handling a redraw, and
47 // during that if we get told by WebKit that a region has become invalid, we
48 // still add that region to the local dirty rect but *don't* enqueue yet
49 // another "do a paint" message.
50 bool g_handling_expose = false;
51
52 // -----------------------------------------------------------------------------
53 // Callback functions to proxy to host...
54
55 // The web contents are completely drawn and handled by WebKit, except that
56 // windowed plugins are GtkSockets on top of it. We need to place the
57 // GtkSockets inside a GtkContainer. We use a GtkFixed container, and the
58 // GtkSocket objects override a little bit to manage their size (see the code
59 // in webplugin_delegate_impl_gtk.cc). We listen on a the events we're
60 // interested in and forward them on to the WebWidgetHost. This class is a
61 // collection of static methods, implementing the widget related code.
62 class WebWidgetHostGtkWidget {
63 public:
64 // This will create a new widget used for hosting the web contents. We use
65 // our GtkDrawingAreaContainer here, for the reasons mentioned above.
66 static GtkWidget* CreateNewWidget(GtkWidget* parent_view,
67 WebWidgetHost* host) {
68 GtkWidget* widget = gtk_fixed_new();
69 gtk_fixed_set_has_window(GTK_FIXED(widget), true);
70
71 gtk_box_pack_start(GTK_BOX(parent_view), widget, TRUE, TRUE, 0);
72
73 gtk_widget_add_events(widget, GDK_EXPOSURE_MASK |
74 GDK_POINTER_MOTION_MASK |
75 GDK_BUTTON_PRESS_MASK |
76 GDK_BUTTON_RELEASE_MASK |
77 GDK_KEY_PRESS_MASK |
78 GDK_KEY_RELEASE_MASK);
79 gtk_widget_set_can_focus(widget, TRUE);
80 g_signal_connect(widget, "size-request",
81 G_CALLBACK(&HandleSizeRequest), host);
82 g_signal_connect(widget, "size-allocate",
83 G_CALLBACK(&HandleSizeAllocate), host);
84 g_signal_connect(widget, "configure-event",
85 G_CALLBACK(&HandleConfigure), host);
86 g_signal_connect(widget, "expose-event",
87 G_CALLBACK(&HandleExpose), host);
88 g_signal_connect(widget, "destroy",
89 G_CALLBACK(&HandleDestroy), host);
90 g_signal_connect(widget, "key-press-event",
91 G_CALLBACK(&HandleKeyPress), host);
92 g_signal_connect(widget, "key-release-event",
93 G_CALLBACK(&HandleKeyRelease), host);
94 g_signal_connect(widget, "focus",
95 G_CALLBACK(&HandleFocus), host);
96 g_signal_connect(widget, "focus-in-event",
97 G_CALLBACK(&HandleFocusIn), host);
98 g_signal_connect(widget, "focus-out-event",
99 G_CALLBACK(&HandleFocusOut), host);
100 g_signal_connect(widget, "button-press-event",
101 G_CALLBACK(&HandleButtonPress), host);
102 g_signal_connect(widget, "button-release-event",
103 G_CALLBACK(&HandleButtonRelease), host);
104 g_signal_connect(widget, "motion-notify-event",
105 G_CALLBACK(&HandleMotionNotify), host);
106 g_signal_connect(widget, "scroll-event",
107 G_CALLBACK(&HandleScroll), host);
108
109 g_object_set_data(G_OBJECT(widget), kWebWidgetHostKey, host);
110 return widget;
111 }
112
113 private:
114 // Our size was requested. We let the GtkFixed do its normal calculation,
115 // after which this callback is called. The GtkFixed will come up with a
116 // requisition based on its children, which include plugin windows. Since
117 // we don't want to prevent resizing smaller than a plugin window, we need to
118 // control the size ourself.
119 static void HandleSizeRequest(GtkWidget* widget,
120 GtkRequisition* req,
121 WebWidgetHost* host) {
122 // This is arbitrary, but the WebKit scrollbars try to shrink themselves
123 // if the browser window is too small. Give them some space.
124 static const int kMinWidthHeight = 64;
125
126 req->width = kMinWidthHeight;
127 req->height = kMinWidthHeight;
128 }
129
130 // Our size has changed.
131 static void HandleSizeAllocate(GtkWidget* widget,
132 GtkAllocation* allocation,
133 WebWidgetHost* host) {
134 host->Resize(WebSize(allocation->width, allocation->height));
135 }
136
137 // Size, position, or stacking of the GdkWindow changed.
138 static gboolean HandleConfigure(GtkWidget* widget,
139 GdkEventConfigure* config,
140 WebWidgetHost* host) {
141 host->Resize(WebSize(config->width, config->height));
142 return FALSE;
143 }
144
145 // A portion of the GdkWindow needs to be redraw.
146 static gboolean HandleExpose(GtkWidget* widget,
147 GdkEventExpose* expose,
148 WebWidgetHost* host) {
149 // See comments above about what g_handling_expose is for.
150 g_handling_expose = true;
151 gfx::Rect rect(expose->area);
152 host->UpdatePaintRect(rect);
153 host->Paint();
154 g_handling_expose = false;
155 return FALSE;
156 }
157
158 // The GdkWindow was destroyed.
159 static gboolean HandleDestroy(GtkWidget* widget, void* unused) {
160 // The associated WebWidgetHost instance may have already been destroyed.
161 WebWidgetHost* host = static_cast<WebWidgetHost*>(
162 g_object_get_data(G_OBJECT(widget), kWebWidgetHostKey));
163 if (host)
164 host->WindowDestroyed();
165 return FALSE;
166 }
167
168 // Keyboard key pressed.
169 static gboolean HandleKeyPress(GtkWidget* widget,
170 GdkEventKey* event,
171 WebWidgetHost* host) {
172 host->webwidget()->handleInputEvent(
173 WebInputEventFactory::keyboardEvent(event));
174
175 // In the browser we do a ton of work with IMEs. This is some minimal
176 // code to make basic text work in test_shell, but doesn't cover IME.
177 // This is a copy of the logic in ProcessUnfilteredKeyPressEvent in
178 // render_widget_host_view_gtk.cc .
179 if (event->type == GDK_KEY_PRESS) {
180 WebKeyboardEvent wke = WebInputEventFactory::keyboardEvent(event);
181 if (wke.text[0]) {
182 wke.type = WebKit::WebInputEvent::Char;
183 host->webwidget()->handleInputEvent(wke);
184 }
185 }
186
187 return FALSE;
188 }
189
190 // Keyboard key released.
191 static gboolean HandleKeyRelease(GtkWidget* widget,
192 GdkEventKey* event,
193 WebWidgetHost* host) {
194 return HandleKeyPress(widget, event, host);
195 }
196
197 // This signal is called when arrow keys or tab is pressed. If we return
198 // true, we prevent focus from being moved to another widget. If we want to
199 // allow focus to be moved outside of web contents, we need to implement
200 // WebViewDelegate::TakeFocus in the test webview delegate.
201 static gboolean HandleFocus(GtkWidget* widget,
202 GdkEventFocus* focus,
203 WebWidgetHost* host) {
204 return TRUE;
205 }
206
207 // Keyboard focus entered.
208 static gboolean HandleFocusIn(GtkWidget* widget,
209 GdkEventFocus* focus,
210 WebWidgetHost* host) {
211 // Ignore focus calls in layout test mode so that tests don't mess with each
212 // other's focus when running in parallel.
213 if (!TestShell::layout_test_mode())
214 host->webwidget()->setFocus(true);
215 return TRUE;
216 }
217
218 // Keyboard focus left.
219 static gboolean HandleFocusOut(GtkWidget* widget,
220 GdkEventFocus* focus,
221 WebWidgetHost* host) {
222 // Ignore focus calls in layout test mode so that tests don't mess with each
223 // other's focus when running in parallel.
224 if (!TestShell::layout_test_mode())
225 host->webwidget()->setFocus(false);
226 return TRUE;
227 }
228
229 // Mouse button down.
230 static gboolean HandleButtonPress(GtkWidget* widget,
231 GdkEventButton* event,
232 WebWidgetHost* host) {
233 if (!(event->button == 1 || event->button == 2 || event->button == 3))
234 return FALSE; // We do not forward any other buttons to the renderer.
235 if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS)
236 return FALSE;
237 host->webwidget()->handleInputEvent(
238 WebInputEventFactory::mouseEvent(event));
239 return FALSE;
240 }
241
242 // Mouse button up.
243 static gboolean HandleButtonRelease(GtkWidget* widget,
244 GdkEventButton* event,
245 WebWidgetHost* host) {
246 return HandleButtonPress(widget, event, host);
247 }
248
249 // Mouse pointer movements.
250 static gboolean HandleMotionNotify(GtkWidget* widget,
251 GdkEventMotion* event,
252 WebWidgetHost* host) {
253 host->webwidget()->handleInputEvent(
254 WebInputEventFactory::mouseEvent(event));
255 return FALSE;
256 }
257
258 // Mouse scroll wheel.
259 static gboolean HandleScroll(GtkWidget* widget,
260 GdkEventScroll* event,
261 WebWidgetHost* host) {
262 host->webwidget()->handleInputEvent(
263 WebInputEventFactory::mouseWheelEvent(event));
264 return FALSE;
265 }
266
267 DISALLOW_IMPLICIT_CONSTRUCTORS(WebWidgetHostGtkWidget);
268 };
269
270 } // namespace
271
272 // This is provided so that the webview can reuse the custom GTK window code.
273 // static
274 gfx::NativeView WebWidgetHost::CreateWidget(
275 gfx::NativeView parent_view, WebWidgetHost* host) {
276 return WebWidgetHostGtkWidget::CreateNewWidget(parent_view, host);
277 }
278
279 // static
280 WebWidgetHost* WebWidgetHost::Create(GtkWidget* parent_view,
281 WebWidgetClient* client) {
282 WebWidgetHost* host = new WebWidgetHost();
283 host->view_ = CreateWidget(parent_view, host);
284 host->webwidget_ = WebPopupMenu::create(client);
285 // We manage our own double buffering because we need to be able to update
286 // the expose area in an ExposeEvent within the lifetime of the event handler.
287 gtk_widget_set_double_buffered(GTK_WIDGET(host->view_), false);
288
289 return host;
290 }
291
292 void WebWidgetHost::UpdatePaintRect(const gfx::Rect& rect) {
293 paint_rect_.Union(rect);
294 }
295
296 void WebWidgetHost::DidInvalidateRect(const gfx::Rect& damaged_rect) {
297 DLOG_IF(WARNING, painting_) << "unexpected invalidation while painting";
298
299 UpdatePaintRect(damaged_rect);
300
301 if (!g_handling_expose) {
302 gtk_widget_queue_draw_area(GTK_WIDGET(view_), damaged_rect.x(),
303 damaged_rect.y(), damaged_rect.width(), damaged_rect.height());
304 }
305 }
306
307 void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) {
308 // This is used for optimizing painting when the renderer is scrolled. We're
309 // currently not doing any optimizations so just invalidate the region.
310 DidInvalidateRect(clip_rect);
311 }
312
313 void WebWidgetHost::ScheduleComposite() {
314 int width = logical_size_.width();
315 int height = logical_size_.height();
316 GdkRectangle grect = {
317 0,
318 0,
319 width,
320 height
321 };
322 GdkWindow* window = view_->window;
323 gdk_window_invalidate_rect(window, &grect, 0);
324 }
325
326 WebWidgetHost::WebWidgetHost()
327 : view_(NULL),
328 webwidget_(NULL),
329 scroll_dx_(0),
330 scroll_dy_(0),
331 weak_factory_(this) {
332 set_painting(false);
333 }
334
335 WebWidgetHost::~WebWidgetHost() {
336 // We may be deleted before the view_. Clear out the signals so that we don't
337 // attempt to invoke something on a deleted object.
338 g_object_set_data(G_OBJECT(view_), kWebWidgetHostKey, NULL);
339 g_signal_handlers_disconnect_matched(view_,
340 G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, this);
341 webwidget_->close();
342 }
343
344 void WebWidgetHost::Resize(const gfx::Size &newsize) {
345 // The pixel buffer backing us is now the wrong size
346 canvas_.reset();
347 logical_size_ = newsize;
348 webwidget_->resize(newsize);
349 }
350
351 void WebWidgetHost::Paint() {
352 int width = logical_size_.width();
353 int height = logical_size_.height();
354 gfx::Rect client_rect(width, height);
355
356 // Allocate a canvas if necessary
357 if (!canvas_.get()) {
358 ResetScrollRect();
359 paint_rect_ = client_rect;
360 canvas_.reset(skia::CreatePlatformCanvas(width, height, true, 0,
361 skia::RETURN_NULL_ON_FAILURE));
362 if (!canvas_.get()) {
363 // memory allocation failed, we can't paint.
364 LOG(ERROR) << "Failed to allocate memory for " << width << "x" << height;
365 return;
366 }
367 }
368
369 webwidget_->animate(0.0);
370
371 // This may result in more invalidation
372 webwidget_->layout();
373
374 // Paint the canvas if necessary. Allow painting to generate extra rects the
375 // first time we call it. This is necessary because some WebCore rendering
376 // objects update their layout only when painted.
377 // Store the total area painted in total_paint. Then tell the gdk window
378 // to update that area after we're done painting it.
379 gfx::Rect total_paint;
380 for (int i = 0; i < 2; ++i) {
381 paint_rect_.Intersect(client_rect);
382 if (!paint_rect_.IsEmpty()) {
383 gfx::Rect rect(paint_rect_);
384 paint_rect_ = gfx::Rect();
385
386 DLOG_IF(WARNING, i == 1) << "painting caused additional invalidations";
387 PaintRect(rect);
388 total_paint.Union(rect);
389 }
390 }
391 //DCHECK(paint_rect_.IsEmpty());
392
393 // Invalidate the paint region on the widget's underlying gdk window. Note
394 // that gdk_window_invalidate_* will generate extra expose events, which
395 // we wish to avoid. So instead we use calls to begin_paint/end_paint.
396 GdkRectangle grect = {
397 total_paint.x(),
398 total_paint.y(),
399 total_paint.width(),
400 total_paint.height(),
401 };
402 GdkWindow* window = view_->window;
403 gdk_window_begin_paint_rect(window, &grect);
404
405 // BitBlit to the gdk window.
406 skia::ScopedPlatformPaint scoped_platform_paint(canvas_.get());
407 cairo_t* source_surface = scoped_platform_paint.GetPlatformSurface();
408 cairo_t* cairo_drawable = gdk_cairo_create(window);
409 cairo_set_source_surface(cairo_drawable, cairo_get_target(source_surface),
410 0, 0);
411 cairo_paint(cairo_drawable);
412 cairo_destroy(cairo_drawable);
413
414 gdk_window_end_paint(window);
415 }
416
417 WebScreenInfo WebWidgetHost::GetScreenInfo() {
418 Display* display = test_shell_x11::GtkWidgetGetDisplay(view_);
419 int screen_num = test_shell_x11::GtkWidgetGetScreenNum(view_);
420 return WebScreenInfoFactory::screenInfo(display, screen_num);
421 }
422
423 void WebWidgetHost::ResetScrollRect() {
424 // This method is only needed for optimized scroll painting, which we don't
425 // care about in the test shell, yet.
426 }
427
428 void WebWidgetHost::PaintRect(const gfx::Rect& rect) {
429 set_painting(true);
430 webwidget_->paint(canvas_.get(), rect);
431 set_painting(false);
432 }
433
434 void WebWidgetHost::WindowDestroyed() {
435 delete this;
436 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698