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 "chrome/browser/renderer_host/render_widget_host_view_views.h"
6
7 #include <algorithm>
8 #include <string>
9
10 #include "base/command_line.h"
11 #include "base/logging.h"
12 #include "base/message_loop.h"
13 #include "base/metrics/histogram.h"
14 #include "base/string_number_conversions.h"
15 #include "base/task.h"
16 #include "base/time.h"
17 #include "chrome/common/render_messages.h"
18 #include "content/browser/renderer_host/backing_store_skia.h"
19 #include "content/browser/renderer_host/backing_store_x.h"
20 #include "content/browser/renderer_host/render_widget_host.h"
21 #include "content/common/native_web_keyboard_event.h"
22 #include "content/common/result_codes.h"
23 #include "content/common/view_messages.h"
24 #include "third_party/WebKit/Source/WebKit/chromium/public/gtk/WebInputEventFactory.h"
25 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
26 #include "ui/base/keycodes/keyboard_code_conversion_gtk.h"
27 #include "ui/base/l10n/l10n_util.h"
28 #include "ui/base/x/x11_util.h"
29 #include "ui/gfx/canvas.h"
30 #include "ui/gfx/canvas_skia.h"
31 #include "ui/gfx/gtk_native_view_id_manager.h"
32 #include "views/events/event.h"
33 #include "views/ime/input_method.h"
34 #include "views/widget/widget.h"
35 #include "views/widget/widget_gtk.h"
36
37 static const int kMaxWindowWidth = 4000;
38 static const int kMaxWindowHeight = 4000;
39 static const char kRenderWidgetHostViewKey[] = "__RENDER_WIDGET_HOST_VIEW__";
40 static const char kBackingStoreSkiaSwitch[] = "use-backing-store-skia";
41
42 // Copied from third_party/WebKit/Source/WebCore/page/EventHandler.cpp
43 //
44 // Match key code of composition keydown event on windows.
45 // IE sends VK_PROCESSKEY which has value 229;
46 //
47 // Please refer to following documents for detals:
48 // - Virtual-Key Codes
49 // http://msdn.microsoft.com/en-us/library/ms645540(VS.85).aspx
50 // - How the IME System Works
51 // http://msdn.microsoft.com/en-us/library/cc194848.aspx
52 // - ImmGetVirtualKey Function
53 // http://msdn.microsoft.com/en-us/library/dd318570(VS.85).aspx
54 static const int kCompositionEventKeyCode = 229;
55
56 using WebKit::WebInputEventFactory;
57 using WebKit::WebMouseWheelEvent;
58 using WebKit::WebTouchEvent;
59
60 const char RenderWidgetHostViewViews::kViewClassName[] =
61 "browser/renderer_host/RenderWidgetHostViewViews";
62
63 namespace {
64
UsingBackingStoreSkia()65 bool UsingBackingStoreSkia() {
66 static bool decided = false;
67 static bool use_skia = false;
68 if (!decided) {
69 CommandLine* cmdline = CommandLine::ForCurrentProcess();
70 use_skia = (cmdline && cmdline->HasSwitch(kBackingStoreSkiaSwitch));
71 decided = true;
72 }
73
74 return use_skia;
75 }
76
WebInputEventFlagsFromViewsEvent(const views::Event & event)77 int WebInputEventFlagsFromViewsEvent(const views::Event& event) {
78 int modifiers = 0;
79
80 if (event.IsShiftDown())
81 modifiers |= WebKit::WebInputEvent::ShiftKey;
82 if (event.IsControlDown())
83 modifiers |= WebKit::WebInputEvent::ControlKey;
84 if (event.IsAltDown())
85 modifiers |= WebKit::WebInputEvent::AltKey;
86 if (event.IsCapsLockDown())
87 modifiers |= WebKit::WebInputEvent::CapsLockOn;
88
89 return modifiers;
90 }
91
TouchPointStateFromEvent(const views::TouchEvent * event)92 WebKit::WebTouchPoint::State TouchPointStateFromEvent(
93 const views::TouchEvent* event) {
94 switch (event->type()) {
95 case ui::ET_TOUCH_PRESSED:
96 return WebKit::WebTouchPoint::StatePressed;
97 case ui::ET_TOUCH_RELEASED:
98 return WebKit::WebTouchPoint::StateReleased;
99 case ui::ET_TOUCH_MOVED:
100 return WebKit::WebTouchPoint::StateMoved;
101 case ui::ET_TOUCH_CANCELLED:
102 return WebKit::WebTouchPoint::StateCancelled;
103 default:
104 return WebKit::WebTouchPoint::StateUndefined;
105 }
106 }
107
TouchEventTypeFromEvent(const views::TouchEvent * event)108 WebKit::WebInputEvent::Type TouchEventTypeFromEvent(
109 const views::TouchEvent* event) {
110 switch (event->type()) {
111 case ui::ET_TOUCH_PRESSED:
112 return WebKit::WebInputEvent::TouchStart;
113 case ui::ET_TOUCH_RELEASED:
114 return WebKit::WebInputEvent::TouchEnd;
115 case ui::ET_TOUCH_MOVED:
116 return WebKit::WebInputEvent::TouchMove;
117 case ui::ET_TOUCH_CANCELLED:
118 return WebKit::WebInputEvent::TouchCancel;
119 default:
120 return WebKit::WebInputEvent::Undefined;
121 }
122 }
123
UpdateTouchPointPosition(const views::TouchEvent * event,const gfx::Point & origin,WebKit::WebTouchPoint * tpoint)124 void UpdateTouchPointPosition(const views::TouchEvent* event,
125 const gfx::Point& origin,
126 WebKit::WebTouchPoint* tpoint) {
127 tpoint->position.x = event->x();
128 tpoint->position.y = event->y();
129
130 tpoint->screenPosition.x = tpoint->position.x + origin.x();
131 tpoint->screenPosition.y = tpoint->position.y + origin.y();
132 }
133
InitializeWebMouseEventFromViewsEvent(const views::LocatedEvent & event,const gfx::Point & origin,WebKit::WebMouseEvent * wmevent)134 void InitializeWebMouseEventFromViewsEvent(const views::LocatedEvent& event,
135 const gfx::Point& origin,
136 WebKit::WebMouseEvent* wmevent) {
137 wmevent->timeStampSeconds = base::Time::Now().ToDoubleT();
138 wmevent->modifiers = WebInputEventFlagsFromViewsEvent(event);
139
140 wmevent->windowX = wmevent->x = event.x();
141 wmevent->windowY = wmevent->y = event.y();
142 wmevent->globalX = wmevent->x + origin.x();
143 wmevent->globalY = wmevent->y + origin.y();
144 }
145
146 } // namespace
147
148 // static
CreateViewForWidget(RenderWidgetHost * widget)149 RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget(
150 RenderWidgetHost* widget) {
151 return new RenderWidgetHostViewViews(widget);
152 }
153
RenderWidgetHostViewViews(RenderWidgetHost * host)154 RenderWidgetHostViewViews::RenderWidgetHostViewViews(RenderWidgetHost* host)
155 : host_(host),
156 about_to_validate_and_paint_(false),
157 is_hidden_(false),
158 is_loading_(false),
159 native_cursor_(NULL),
160 is_showing_context_menu_(false),
161 visually_deemphasized_(false),
162 touch_event_(),
163 text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
164 has_composition_text_(false) {
165 SetFocusable(true);
166 host_->set_view(this);
167 }
168
~RenderWidgetHostViewViews()169 RenderWidgetHostViewViews::~RenderWidgetHostViewViews() {
170 }
171
InitAsChild()172 void RenderWidgetHostViewViews::InitAsChild() {
173 Show();
174 }
175
InitAsPopup(RenderWidgetHostView * parent_host_view,const gfx::Rect & pos)176 void RenderWidgetHostViewViews::InitAsPopup(
177 RenderWidgetHostView* parent_host_view,
178 const gfx::Rect& pos) {
179 // TODO(anicolao): figure out cases where popups occur and implement
180 NOTIMPLEMENTED();
181 }
182
InitAsFullscreen()183 void RenderWidgetHostViewViews::InitAsFullscreen() {
184 NOTIMPLEMENTED();
185 }
186
GetRenderWidgetHost() const187 RenderWidgetHost* RenderWidgetHostViewViews::GetRenderWidgetHost() const {
188 return host_;
189 }
190
DidBecomeSelected()191 void RenderWidgetHostViewViews::DidBecomeSelected() {
192 if (!is_hidden_)
193 return;
194
195 if (tab_switch_paint_time_.is_null())
196 tab_switch_paint_time_ = base::TimeTicks::Now();
197 is_hidden_ = false;
198 if (host_)
199 host_->WasRestored();
200 }
201
WasHidden()202 void RenderWidgetHostViewViews::WasHidden() {
203 if (is_hidden_)
204 return;
205
206 // If we receive any more paint messages while we are hidden, we want to
207 // ignore them so we don't re-allocate the backing store. We will paint
208 // everything again when we become selected again.
209 is_hidden_ = true;
210
211 // If we have a renderer, then inform it that we are being hidden so it can
212 // reduce its resource utilization.
213 if (host_)
214 host_->WasHidden();
215 }
216
SetSize(const gfx::Size & size)217 void RenderWidgetHostViewViews::SetSize(const gfx::Size& size) {
218 // This is called when webkit has sent us a Move message.
219 int width = std::min(size.width(), kMaxWindowWidth);
220 int height = std::min(size.height(), kMaxWindowHeight);
221 if (requested_size_.width() != width ||
222 requested_size_.height() != height) {
223 requested_size_ = gfx::Size(width, height);
224 views::View::SetBounds(x(), y(), width, height);
225 if (host_)
226 host_->WasResized();
227 }
228 }
229
SetBounds(const gfx::Rect & rect)230 void RenderWidgetHostViewViews::SetBounds(const gfx::Rect& rect) {
231 NOTIMPLEMENTED();
232 }
233
GetNativeView()234 gfx::NativeView RenderWidgetHostViewViews::GetNativeView() {
235 if (GetWidget())
236 return GetWidget()->GetNativeView();
237 return NULL;
238 }
239
MovePluginWindows(const std::vector<webkit::npapi::WebPluginGeometry> & moves)240 void RenderWidgetHostViewViews::MovePluginWindows(
241 const std::vector<webkit::npapi::WebPluginGeometry>& moves) {
242 // TODO(anicolao): NIY
243 // NOTIMPLEMENTED();
244 }
245
HasFocus()246 bool RenderWidgetHostViewViews::HasFocus() {
247 return View::HasFocus();
248 }
249
Show()250 void RenderWidgetHostViewViews::Show() {
251 SetVisible(true);
252 }
253
Hide()254 void RenderWidgetHostViewViews::Hide() {
255 SetVisible(false);
256 }
257
IsShowing()258 bool RenderWidgetHostViewViews::IsShowing() {
259 return IsVisible();
260 }
261
GetViewBounds() const262 gfx::Rect RenderWidgetHostViewViews::GetViewBounds() const {
263 return bounds();
264 }
265
UpdateCursor(const WebCursor & cursor)266 void RenderWidgetHostViewViews::UpdateCursor(const WebCursor& cursor) {
267 // Optimize the common case, where the cursor hasn't changed.
268 // However, we can switch between different pixmaps, so only on the
269 // non-pixmap branch.
270 if (current_cursor_.GetCursorType() != GDK_CURSOR_IS_PIXMAP &&
271 current_cursor_.GetCursorType() == cursor.GetCursorType()) {
272 return;
273 }
274
275 current_cursor_ = cursor;
276 ShowCurrentCursor();
277 }
278
SetIsLoading(bool is_loading)279 void RenderWidgetHostViewViews::SetIsLoading(bool is_loading) {
280 is_loading_ = is_loading;
281 // Only call ShowCurrentCursor() when it will actually change the cursor.
282 if (current_cursor_.GetCursorType() == GDK_LAST_CURSOR)
283 ShowCurrentCursor();
284 }
285
ImeUpdateTextInputState(WebKit::WebTextInputType type,const gfx::Rect & caret_rect)286 void RenderWidgetHostViewViews::ImeUpdateTextInputState(
287 WebKit::WebTextInputType type,
288 const gfx::Rect& caret_rect) {
289 DCHECK(GetInputMethod());
290 ui::TextInputType new_type = static_cast<ui::TextInputType>(type);
291 if (text_input_type_ != new_type) {
292 text_input_type_ = new_type;
293 GetInputMethod()->OnTextInputTypeChanged(this);
294 }
295 if (caret_bounds_ != caret_rect) {
296 caret_bounds_ = caret_rect;
297 GetInputMethod()->OnCaretBoundsChanged(this);
298 }
299 }
300
ImeCancelComposition()301 void RenderWidgetHostViewViews::ImeCancelComposition() {
302 DCHECK(GetInputMethod());
303 GetInputMethod()->CancelComposition(this);
304 has_composition_text_ = false;
305 }
306
DidUpdateBackingStore(const gfx::Rect & scroll_rect,int scroll_dx,int scroll_dy,const std::vector<gfx::Rect> & copy_rects)307 void RenderWidgetHostViewViews::DidUpdateBackingStore(
308 const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy,
309 const std::vector<gfx::Rect>& copy_rects) {
310 if (is_hidden_)
311 return;
312
313 // TODO(darin): Implement the equivalent of Win32's ScrollWindowEX. Can that
314 // be done using XCopyArea? Perhaps similar to
315 // BackingStore::ScrollBackingStore?
316 if (about_to_validate_and_paint_)
317 invalid_rect_ = invalid_rect_.Union(scroll_rect);
318 else
319 SchedulePaintInRect(scroll_rect);
320
321 for (size_t i = 0; i < copy_rects.size(); ++i) {
322 // Avoid double painting. NOTE: This is only relevant given the call to
323 // Paint(scroll_rect) above.
324 gfx::Rect rect = copy_rects[i].Subtract(scroll_rect);
325 if (rect.IsEmpty())
326 continue;
327
328 if (about_to_validate_and_paint_)
329 invalid_rect_ = invalid_rect_.Union(rect);
330 else
331 SchedulePaintInRect(rect);
332 }
333 invalid_rect_ = invalid_rect_.Intersect(bounds());
334 }
335
RenderViewGone(base::TerminationStatus status,int error_code)336 void RenderWidgetHostViewViews::RenderViewGone(base::TerminationStatus status,
337 int error_code) {
338 DCHECK(host_);
339 host_->ViewDestroyed();
340 Destroy();
341 }
342
Destroy()343 void RenderWidgetHostViewViews::Destroy() {
344 // host_'s destruction brought us here, null it out so we don't use it
345 host_ = NULL;
346
347 if (parent())
348 parent()->RemoveChildView(this);
349 MessageLoop::current()->DeleteSoon(FROM_HERE, this);
350 }
351
SetTooltipText(const std::wstring & tip)352 void RenderWidgetHostViewViews::SetTooltipText(const std::wstring& tip) {
353 // TODO(anicolao): decide if we want tooltips for touch (none specified
354 // right now/might want a press-and-hold display)
355 // NOTIMPLEMENTED(); ... too annoying, it triggers for every mousemove
356 }
357
SelectionChanged(const std::string & text)358 void RenderWidgetHostViewViews::SelectionChanged(const std::string& text) {
359 // TODO(anicolao): deal with the clipboard without GTK
360 NOTIMPLEMENTED();
361 }
362
ShowingContextMenu(bool showing)363 void RenderWidgetHostViewViews::ShowingContextMenu(bool showing) {
364 is_showing_context_menu_ = showing;
365 }
366
AllocBackingStore(const gfx::Size & size)367 BackingStore* RenderWidgetHostViewViews::AllocBackingStore(
368 const gfx::Size& size) {
369 gfx::NativeView nview = GetInnerNativeView();
370 if (!nview)
371 return NULL;
372
373 if (UsingBackingStoreSkia()) {
374 return new BackingStoreSkia(host_, size);
375 } else {
376 return new BackingStoreX(host_, size,
377 ui::GetVisualFromGtkWidget(nview),
378 gtk_widget_get_visual(nview)->depth);
379 }
380 }
381
SetBackground(const SkBitmap & background)382 void RenderWidgetHostViewViews::SetBackground(const SkBitmap& background) {
383 RenderWidgetHostView::SetBackground(background);
384 if (host_)
385 host_->Send(new ViewMsg_SetBackground(host_->routing_id(), background));
386 }
387
CreatePluginContainer(gfx::PluginWindowHandle id)388 void RenderWidgetHostViewViews::CreatePluginContainer(
389 gfx::PluginWindowHandle id) {
390 // TODO(anicolao): plugin_container_manager_.CreatePluginContainer(id);
391 }
392
DestroyPluginContainer(gfx::PluginWindowHandle id)393 void RenderWidgetHostViewViews::DestroyPluginContainer(
394 gfx::PluginWindowHandle id) {
395 // TODO(anicolao): plugin_container_manager_.DestroyPluginContainer(id);
396 }
397
SetVisuallyDeemphasized(const SkColor * color,bool animate)398 void RenderWidgetHostViewViews::SetVisuallyDeemphasized(
399 const SkColor* color, bool animate) {
400 // TODO(anicolao)
401 }
402
ContainsNativeView(gfx::NativeView native_view) const403 bool RenderWidgetHostViewViews::ContainsNativeView(
404 gfx::NativeView native_view) const {
405 // TODO(port)
406 NOTREACHED() <<
407 "RenderWidgetHostViewViews::ContainsNativeView not implemented.";
408 return false;
409 }
410
AcceleratedCompositingActivated(bool activated)411 void RenderWidgetHostViewViews::AcceleratedCompositingActivated(
412 bool activated) {
413 // TODO(anicolao): figure out if we need something here
414 if (activated)
415 NOTIMPLEMENTED();
416 }
417
GetCompositingSurface()418 gfx::PluginWindowHandle RenderWidgetHostViewViews::GetCompositingSurface() {
419 GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance();
420 gfx::PluginWindowHandle surface = gfx::kNullPluginWindow;
421 gfx::NativeViewId view_id = gfx::IdFromNativeView(GetInnerNativeView());
422
423 if (!manager->GetXIDForId(&surface, view_id)) {
424 DLOG(ERROR) << "Can't find XID for view id " << view_id;
425 }
426 return surface;
427 }
428
GetInnerNativeView() const429 gfx::NativeView RenderWidgetHostViewViews::GetInnerNativeView() const {
430 // TODO(sad): Ideally this function should be equivalent to GetNativeView, and
431 // WidgetGtk-specific function call should not be necessary.
432 const views::WidgetGtk* widget =
433 static_cast<const views::WidgetGtk*>(GetWidget());
434 return widget ? widget->window_contents() : NULL;
435 }
436
GetClassName() const437 std::string RenderWidgetHostViewViews::GetClassName() const {
438 return kViewClassName;
439 }
440
GetCursorForPoint(ui::EventType type,const gfx::Point & point)441 gfx::NativeCursor RenderWidgetHostViewViews::GetCursorForPoint(
442 ui::EventType type, const gfx::Point& point) {
443 return native_cursor_;
444 }
445
OnMousePressed(const views::MouseEvent & event)446 bool RenderWidgetHostViewViews::OnMousePressed(const views::MouseEvent& event) {
447 if (!host_)
448 return false;
449
450 RequestFocus();
451
452 // Confirm existing composition text on mouse click events, to make sure
453 // the input caret won't be moved with an ongoing composition text.
454 FinishImeCompositionSession();
455
456 // TODO(anicolao): validate event generation.
457 WebKit::WebMouseEvent e = WebMouseEventFromViewsEvent(event);
458
459 // TODO(anicolao): deal with double clicks
460 e.type = WebKit::WebInputEvent::MouseDown;
461 e.clickCount = 1;
462
463 host_->ForwardMouseEvent(e);
464 return true;
465 }
466
OnMouseDragged(const views::MouseEvent & event)467 bool RenderWidgetHostViewViews::OnMouseDragged(const views::MouseEvent& event) {
468 OnMouseMoved(event);
469 return true;
470 }
471
OnMouseReleased(const views::MouseEvent & event)472 void RenderWidgetHostViewViews::OnMouseReleased(
473 const views::MouseEvent& event) {
474 if (!host_)
475 return;
476
477 WebKit::WebMouseEvent e = WebMouseEventFromViewsEvent(event);
478 e.type = WebKit::WebInputEvent::MouseUp;
479 e.clickCount = 1;
480 host_->ForwardMouseEvent(e);
481 }
482
OnMouseMoved(const views::MouseEvent & event)483 void RenderWidgetHostViewViews::OnMouseMoved(const views::MouseEvent& event) {
484 if (!host_)
485 return;
486
487 WebKit::WebMouseEvent e = WebMouseEventFromViewsEvent(event);
488 e.type = WebKit::WebInputEvent::MouseMove;
489 host_->ForwardMouseEvent(e);
490 }
491
OnMouseEntered(const views::MouseEvent & event)492 void RenderWidgetHostViewViews::OnMouseEntered(const views::MouseEvent& event) {
493 // Already generated synthetically by webkit.
494 }
495
OnMouseExited(const views::MouseEvent & event)496 void RenderWidgetHostViewViews::OnMouseExited(const views::MouseEvent& event) {
497 // Already generated synthetically by webkit.
498 }
499
OnTouchEvent(const views::TouchEvent & event)500 views::View::TouchStatus RenderWidgetHostViewViews::OnTouchEvent(
501 const views::TouchEvent& event) {
502 if (!host_)
503 return TOUCH_STATUS_UNKNOWN;
504
505 // Update the list of touch points first.
506 WebKit::WebTouchPoint* point = NULL;
507 TouchStatus status = TOUCH_STATUS_UNKNOWN;
508
509 switch (event.type()) {
510 case ui::ET_TOUCH_PRESSED:
511 // Add a new touch point.
512 if (touch_event_.touchPointsLength <
513 WebTouchEvent::touchPointsLengthCap) {
514 point = &touch_event_.touchPoints[touch_event_.touchPointsLength++];
515 point->id = event.identity();
516
517 if (touch_event_.touchPointsLength == 1) {
518 // A new touch sequence has started.
519 status = TOUCH_STATUS_START;
520
521 // We also want the focus.
522 RequestFocus();
523
524 // Confirm existing composition text on touch press events, to make
525 // sure the input caret won't be moved with an ongoing composition
526 // text.
527 FinishImeCompositionSession();
528 }
529 }
530 break;
531 case ui::ET_TOUCH_RELEASED:
532 case ui::ET_TOUCH_CANCELLED:
533 case ui::ET_TOUCH_MOVED: {
534 // The touch point should have been added to the event from an earlier
535 // _PRESSED event. So find that.
536 // At the moment, only a maximum of 4 touch-points are allowed. So a
537 // simple loop should be sufficient.
538 for (int i = 0; i < touch_event_.touchPointsLength; ++i) {
539 point = touch_event_.touchPoints + i;
540 if (point->id == event.identity()) {
541 break;
542 }
543 point = NULL;
544 }
545 break;
546 }
547 default:
548 DLOG(WARNING) << "Unknown touch event " << event.type();
549 break;
550 }
551
552 if (!point)
553 return TOUCH_STATUS_UNKNOWN;
554
555 if (status != TOUCH_STATUS_START)
556 status = TOUCH_STATUS_CONTINUE;
557
558 // Update the location and state of the point.
559 point->state = TouchPointStateFromEvent(&event);
560 if (point->state == WebKit::WebTouchPoint::StateMoved) {
561 // It is possible for badly written touch drivers to emit Move events even
562 // when the touch location hasn't changed. In such cases, consume the event
563 // and pretend nothing happened.
564 if (point->position.x == event.x() && point->position.y == event.y()) {
565 return status;
566 }
567 }
568 UpdateTouchPointPosition(&event, GetMirroredPosition(), point);
569
570 // Mark the rest of the points as stationary.
571 for (int i = 0; i < touch_event_.touchPointsLength; ++i) {
572 WebKit::WebTouchPoint* iter = touch_event_.touchPoints + i;
573 if (iter != point) {
574 iter->state = WebKit::WebTouchPoint::StateStationary;
575 }
576 }
577
578 // Update the type of the touch event.
579 touch_event_.type = TouchEventTypeFromEvent(&event);
580 touch_event_.timeStampSeconds = base::Time::Now().ToDoubleT();
581
582 // The event and all the touches have been updated. Dispatch.
583 host_->ForwardTouchEvent(touch_event_);
584
585 // If the touch was released, then remove it from the list of touch points.
586 if (event.type() == ui::ET_TOUCH_RELEASED) {
587 --touch_event_.touchPointsLength;
588 for (int i = point - touch_event_.touchPoints;
589 i < touch_event_.touchPointsLength;
590 ++i) {
591 touch_event_.touchPoints[i] = touch_event_.touchPoints[i + 1];
592 }
593 if (touch_event_.touchPointsLength == 0)
594 status = TOUCH_STATUS_END;
595 } else if (event.type() == ui::ET_TOUCH_CANCELLED) {
596 status = TOUCH_STATUS_CANCEL;
597 }
598
599 return status;
600 }
601
OnKeyPressed(const views::KeyEvent & event)602 bool RenderWidgetHostViewViews::OnKeyPressed(const views::KeyEvent& event) {
603 // TODO(suzhe): Support editor key bindings.
604 if (!host_)
605 return false;
606 host_->ForwardKeyboardEvent(NativeWebKeyboardEvent(event));
607 return true;
608 }
609
OnKeyReleased(const views::KeyEvent & event)610 bool RenderWidgetHostViewViews::OnKeyReleased(const views::KeyEvent& event) {
611 if (!host_)
612 return false;
613 host_->ForwardKeyboardEvent(NativeWebKeyboardEvent(event));
614 return true;
615 }
616
OnMouseWheel(const views::MouseWheelEvent & event)617 bool RenderWidgetHostViewViews::OnMouseWheel(
618 const views::MouseWheelEvent& event) {
619 if (!host_)
620 return false;
621
622 WebMouseWheelEvent wmwe;
623 InitializeWebMouseEventFromViewsEvent(event, GetMirroredPosition(), &wmwe);
624
625 wmwe.type = WebKit::WebInputEvent::MouseWheel;
626 wmwe.button = WebKit::WebMouseEvent::ButtonNone;
627
628 // TODO(sadrul): How do we determine if it's a horizontal scroll?
629 wmwe.deltaY = event.offset();
630 wmwe.wheelTicksY = wmwe.deltaY > 0 ? 1 : -1;
631
632 host_->ForwardWheelEvent(wmwe);
633 return true;
634 }
635
GetTextInputClient()636 views::TextInputClient* RenderWidgetHostViewViews::GetTextInputClient() {
637 return this;
638 }
639
640 // TextInputClient implementation ---------------------------------------------
SetCompositionText(const ui::CompositionText & composition)641 void RenderWidgetHostViewViews::SetCompositionText(
642 const ui::CompositionText& composition) {
643 if (!host_)
644 return;
645
646 // ui::CompositionUnderline should be identical to
647 // WebKit::WebCompositionUnderline, so that we can do reinterpret_cast safely.
648 COMPILE_ASSERT(sizeof(ui::CompositionUnderline) ==
649 sizeof(WebKit::WebCompositionUnderline),
650 ui_CompositionUnderline__WebKit_WebCompositionUnderline_diff);
651
652 // TODO(suzhe): convert both renderer_host and renderer to use
653 // ui::CompositionText.
654 const std::vector<WebKit::WebCompositionUnderline>& underlines =
655 reinterpret_cast<const std::vector<WebKit::WebCompositionUnderline>&>(
656 composition.underlines);
657
658 // TODO(suzhe): due to a bug of webkit, we can't use selection range with
659 // composition string. See: https://bugs.webkit.org/show_bug.cgi?id=37788
660 host_->ImeSetComposition(composition.text, underlines,
661 composition.selection.end(),
662 composition.selection.end());
663
664 has_composition_text_ = !composition.text.empty();
665 }
666
ConfirmCompositionText()667 void RenderWidgetHostViewViews::ConfirmCompositionText() {
668 if (host_ && has_composition_text_)
669 host_->ImeConfirmComposition();
670 has_composition_text_ = false;
671 }
672
ClearCompositionText()673 void RenderWidgetHostViewViews::ClearCompositionText() {
674 if (host_ && has_composition_text_)
675 host_->ImeCancelComposition();
676 has_composition_text_ = false;
677 }
678
InsertText(const string16 & text)679 void RenderWidgetHostViewViews::InsertText(const string16& text) {
680 DCHECK(text_input_type_ != ui::TEXT_INPUT_TYPE_NONE);
681 if (host_)
682 host_->ImeConfirmComposition(text);
683 has_composition_text_ = false;
684 }
685
InsertChar(char16 ch,int flags)686 void RenderWidgetHostViewViews::InsertChar(char16 ch, int flags) {
687 if (host_) {
688 NativeWebKeyboardEvent::FromViewsEvent from_views_event;
689 NativeWebKeyboardEvent wke(ch, flags, base::Time::Now().ToDoubleT(),
690 from_views_event);
691 host_->ForwardKeyboardEvent(wke);
692 }
693 }
694
GetTextInputType()695 ui::TextInputType RenderWidgetHostViewViews::GetTextInputType() {
696 return text_input_type_;
697 }
698
GetCaretBounds()699 gfx::Rect RenderWidgetHostViewViews::GetCaretBounds() {
700 return caret_bounds_;
701 }
702
HasCompositionText()703 bool RenderWidgetHostViewViews::HasCompositionText() {
704 return has_composition_text_;
705 }
706
GetTextRange(ui::Range * range)707 bool RenderWidgetHostViewViews::GetTextRange(ui::Range* range) {
708 // TODO(suzhe): implement this method when fixing http://crbug.com/55130.
709 NOTIMPLEMENTED();
710 return false;
711 }
712
GetCompositionTextRange(ui::Range * range)713 bool RenderWidgetHostViewViews::GetCompositionTextRange(ui::Range* range) {
714 // TODO(suzhe): implement this method when fixing http://crbug.com/55130.
715 NOTIMPLEMENTED();
716 return false;
717 }
718
GetSelectionRange(ui::Range * range)719 bool RenderWidgetHostViewViews::GetSelectionRange(ui::Range* range) {
720 // TODO(suzhe): implement this method when fixing http://crbug.com/55130.
721 NOTIMPLEMENTED();
722 return false;
723 }
724
SetSelectionRange(const ui::Range & range)725 bool RenderWidgetHostViewViews::SetSelectionRange(const ui::Range& range) {
726 // TODO(suzhe): implement this method when fixing http://crbug.com/55130.
727 NOTIMPLEMENTED();
728 return false;
729 }
730
DeleteRange(const ui::Range & range)731 bool RenderWidgetHostViewViews::DeleteRange(const ui::Range& range) {
732 // TODO(suzhe): implement this method when fixing http://crbug.com/55130.
733 NOTIMPLEMENTED();
734 return false;
735 }
736
GetTextFromRange(const ui::Range & range,const base::Callback<void (const string16 &)> & callback)737 bool RenderWidgetHostViewViews::GetTextFromRange(
738 const ui::Range& range,
739 const base::Callback<void(const string16&)>& callback) {
740 // TODO(suzhe): implement this method when fixing http://crbug.com/55130.
741 NOTIMPLEMENTED();
742 return false;
743 }
744
OnInputMethodChanged()745 void RenderWidgetHostViewViews::OnInputMethodChanged() {
746 if (!host_)
747 return;
748
749 DCHECK(GetInputMethod());
750 host_->SetInputMethodActive(GetInputMethod()->IsActive());
751
752 // TODO(suzhe): implement the newly added “locale” property of HTML DOM
753 // TextEvent.
754 }
755
ChangeTextDirectionAndLayoutAlignment(base::i18n::TextDirection direction)756 bool RenderWidgetHostViewViews::ChangeTextDirectionAndLayoutAlignment(
757 base::i18n::TextDirection direction) {
758 if (!host_)
759 return false;
760 host_->UpdateTextDirection(
761 direction == base::i18n::RIGHT_TO_LEFT ?
762 WebKit::WebTextDirectionRightToLeft :
763 WebKit::WebTextDirectionLeftToRight);
764 host_->NotifyTextDirection();
765 return true;
766 }
767
GetOwnerViewOfTextInputClient()768 views::View* RenderWidgetHostViewViews::GetOwnerViewOfTextInputClient() {
769 return this;
770 }
771
OnPaint(gfx::Canvas * canvas)772 void RenderWidgetHostViewViews::OnPaint(gfx::Canvas* canvas) {
773 if (is_hidden_ || !host_)
774 return;
775
776 // Paint a "hole" in the canvas so that the render of the web page is on
777 // top of whatever else has already been painted in the views hierarchy.
778 // Later views might still get to paint on top.
779 canvas->FillRectInt(SK_ColorBLACK, 0, 0,
780 bounds().width(), bounds().height(),
781 SkXfermode::kClear_Mode);
782
783 // Don't do any painting if the GPU process is rendering directly
784 // into the View.
785 if (host_->is_accelerated_compositing_active())
786 return;
787
788 GdkWindow* window = GetInnerNativeView()->window;
789 DCHECK(!about_to_validate_and_paint_);
790
791 // TODO(anicolao): get the damage somehow
792 // invalid_rect_ = damage_rect;
793 invalid_rect_ = bounds();
794 gfx::Point origin;
795 ConvertPointToWidget(this, &origin);
796
797 about_to_validate_and_paint_ = true;
798 BackingStore* backing_store = host_->GetBackingStore(true);
799 // Calling GetBackingStore maybe have changed |invalid_rect_|...
800 about_to_validate_and_paint_ = false;
801
802 gfx::Rect paint_rect = gfx::Rect(0, 0, kMaxWindowWidth, kMaxWindowHeight);
803 paint_rect = paint_rect.Intersect(invalid_rect_);
804
805 if (backing_store) {
806 // Only render the widget if it is attached to a window; there's a short
807 // period where this object isn't attached to a window but hasn't been
808 // Destroy()ed yet and it receives paint messages...
809 if (window) {
810 if (!visually_deemphasized_) {
811 // In the common case, use XCopyArea. We don't draw more than once, so
812 // we don't need to double buffer.
813
814 if (UsingBackingStoreSkia()) {
815 static_cast<BackingStoreSkia*>(backing_store)->SkiaShowRect(
816 gfx::Point(paint_rect.x(), paint_rect.y()), canvas);
817 } else {
818 static_cast<BackingStoreX*>(backing_store)->XShowRect(origin,
819 paint_rect, ui::GetX11WindowFromGdkWindow(window));
820 }
821 } else if (!UsingBackingStoreSkia()) {
822 // If the grey blend is showing, we make two drawing calls. Use double
823 // buffering to prevent flicker. Use CairoShowRect because XShowRect
824 // shortcuts GDK's double buffering.
825 GdkRectangle rect = { paint_rect.x(), paint_rect.y(),
826 paint_rect.width(), paint_rect.height() };
827 gdk_window_begin_paint_rect(window, &rect);
828
829 static_cast<BackingStoreX*>(backing_store)->CairoShowRect(
830 paint_rect, GDK_DRAWABLE(window));
831
832 cairo_t* cr = gdk_cairo_create(window);
833 gdk_cairo_rectangle(cr, &rect);
834 cairo_set_source_rgba(cr, 0, 0, 0, 0.7);
835 cairo_fill(cr);
836 cairo_destroy(cr);
837
838 gdk_window_end_paint(window);
839 } else {
840 // TODO(sad)
841 NOTIMPLEMENTED();
842 }
843 }
844 if (!whiteout_start_time_.is_null()) {
845 base::TimeDelta whiteout_duration = base::TimeTicks::Now() -
846 whiteout_start_time_;
847 UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration);
848
849 // Reset the start time to 0 so that we start recording again the next
850 // time the backing store is NULL...
851 whiteout_start_time_ = base::TimeTicks();
852 }
853 if (!tab_switch_paint_time_.is_null()) {
854 base::TimeDelta tab_switch_paint_duration = base::TimeTicks::Now() -
855 tab_switch_paint_time_;
856 UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration",
857 tab_switch_paint_duration);
858 // Reset tab_switch_paint_time_ to 0 so future tab selections are
859 // recorded.
860 tab_switch_paint_time_ = base::TimeTicks();
861 }
862 } else {
863 if (whiteout_start_time_.is_null())
864 whiteout_start_time_ = base::TimeTicks::Now();
865 }
866 }
867
Focus()868 void RenderWidgetHostViewViews::Focus() {
869 RequestFocus();
870 }
871
Blur()872 void RenderWidgetHostViewViews::Blur() {
873 // TODO(estade): We should be clearing native focus as well, but I know of no
874 // way to do that without focusing another widget.
875 if (host_)
876 host_->Blur();
877 }
878
OnFocus()879 void RenderWidgetHostViewViews::OnFocus() {
880 if (!host_)
881 return;
882
883 DCHECK(GetInputMethod());
884 View::OnFocus();
885 ShowCurrentCursor();
886 host_->GotFocus();
887 host_->SetInputMethodActive(GetInputMethod()->IsActive());
888 }
889
OnBlur()890 void RenderWidgetHostViewViews::OnBlur() {
891 if (!host_)
892 return;
893 View::OnBlur();
894 // If we are showing a context menu, maintain the illusion that webkit has
895 // focus.
896 if (!is_showing_context_menu_ && !is_hidden_)
897 host_->Blur();
898 host_->SetInputMethodActive(false);
899 }
900
NeedsInputGrab()901 bool RenderWidgetHostViewViews::NeedsInputGrab() {
902 return popup_type_ == WebKit::WebPopupTypeSelect;
903 }
904
IsPopup()905 bool RenderWidgetHostViewViews::IsPopup() {
906 return popup_type_ != WebKit::WebPopupTypeNone;
907 }
908
ShowCurrentCursor()909 void RenderWidgetHostViewViews::ShowCurrentCursor() {
910 // The widget may not have a window. If that's the case, abort mission. This
911 // is the same issue as that explained above in Paint().
912 if (!GetInnerNativeView() || !GetInnerNativeView()->window)
913 return;
914
915 native_cursor_ = current_cursor_.GetNativeCursor();
916 }
917
WebMouseEventFromViewsEvent(const views::MouseEvent & event)918 WebKit::WebMouseEvent RenderWidgetHostViewViews::WebMouseEventFromViewsEvent(
919 const views::MouseEvent& event) {
920 WebKit::WebMouseEvent wmevent;
921 InitializeWebMouseEventFromViewsEvent(event, GetMirroredPosition(), &wmevent);
922
923 // Setting |wmevent.button| is not necessary for -move events, but it is
924 // necessary for -clicks and -drags.
925 if (event.IsMiddleMouseButton()) {
926 wmevent.modifiers |= WebKit::WebInputEvent::MiddleButtonDown;
927 wmevent.button = WebKit::WebMouseEvent::ButtonMiddle;
928 }
929 if (event.IsRightMouseButton()) {
930 wmevent.modifiers |= WebKit::WebInputEvent::RightButtonDown;
931 wmevent.button = WebKit::WebMouseEvent::ButtonRight;
932 }
933 if (event.IsLeftMouseButton()) {
934 wmevent.modifiers |= WebKit::WebInputEvent::LeftButtonDown;
935 wmevent.button = WebKit::WebMouseEvent::ButtonLeft;
936 }
937
938 return wmevent;
939 }
940
FinishImeCompositionSession()941 void RenderWidgetHostViewViews::FinishImeCompositionSession() {
942 if (!has_composition_text_)
943 return;
944 if (host_)
945 host_->ImeConfirmComposition();
946 DCHECK(GetInputMethod());
947 GetInputMethod()->CancelComposition(this);
948 has_composition_text_ = false;
949 }
950
951 // static
952 RenderWidgetHostView*
GetRenderWidgetHostViewFromNativeView(gfx::NativeView widget)953 RenderWidgetHostView::GetRenderWidgetHostViewFromNativeView(
954 gfx::NativeView widget) {
955 gpointer user_data = g_object_get_data(G_OBJECT(widget),
956 kRenderWidgetHostViewKey);
957 return reinterpret_cast<RenderWidgetHostView*>(user_data);
958 }
959