• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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