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
37static const int kMaxWindowWidth = 4000;
38static const int kMaxWindowHeight = 4000;
39static const char kRenderWidgetHostViewKey[] = "__RENDER_WIDGET_HOST_VIEW__";
40static 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
54static const int kCompositionEventKeyCode = 229;
55
56using WebKit::WebInputEventFactory;
57using WebKit::WebMouseWheelEvent;
58using WebKit::WebTouchEvent;
59
60const char RenderWidgetHostViewViews::kViewClassName[] =
61    "browser/renderer_host/RenderWidgetHostViewViews";
62
63namespace {
64
65bool 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
77int 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
92WebKit::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
108WebKit::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
124void 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
134void 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
149RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget(
150    RenderWidgetHost* widget) {
151  return new RenderWidgetHostViewViews(widget);
152}
153
154RenderWidgetHostViewViews::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
169RenderWidgetHostViewViews::~RenderWidgetHostViewViews() {
170}
171
172void RenderWidgetHostViewViews::InitAsChild() {
173  Show();
174}
175
176void 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
183void RenderWidgetHostViewViews::InitAsFullscreen() {
184  NOTIMPLEMENTED();
185}
186
187RenderWidgetHost* RenderWidgetHostViewViews::GetRenderWidgetHost() const {
188  return host_;
189}
190
191void 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
202void 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
217void 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
230void RenderWidgetHostViewViews::SetBounds(const gfx::Rect& rect) {
231  NOTIMPLEMENTED();
232}
233
234gfx::NativeView RenderWidgetHostViewViews::GetNativeView() {
235  if (GetWidget())
236    return GetWidget()->GetNativeView();
237  return NULL;
238}
239
240void RenderWidgetHostViewViews::MovePluginWindows(
241    const std::vector<webkit::npapi::WebPluginGeometry>& moves) {
242  // TODO(anicolao): NIY
243  // NOTIMPLEMENTED();
244}
245
246bool RenderWidgetHostViewViews::HasFocus() {
247  return View::HasFocus();
248}
249
250void RenderWidgetHostViewViews::Show() {
251  SetVisible(true);
252}
253
254void RenderWidgetHostViewViews::Hide() {
255  SetVisible(false);
256}
257
258bool RenderWidgetHostViewViews::IsShowing() {
259  return IsVisible();
260}
261
262gfx::Rect RenderWidgetHostViewViews::GetViewBounds() const {
263  return bounds();
264}
265
266void 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
279void 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
286void 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
301void RenderWidgetHostViewViews::ImeCancelComposition() {
302  DCHECK(GetInputMethod());
303  GetInputMethod()->CancelComposition(this);
304  has_composition_text_ = false;
305}
306
307void 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
336void RenderWidgetHostViewViews::RenderViewGone(base::TerminationStatus status,
337                                               int error_code) {
338  DCHECK(host_);
339  host_->ViewDestroyed();
340  Destroy();
341}
342
343void 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
352void 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
358void RenderWidgetHostViewViews::SelectionChanged(const std::string& text) {
359  // TODO(anicolao): deal with the clipboard without GTK
360  NOTIMPLEMENTED();
361}
362
363void RenderWidgetHostViewViews::ShowingContextMenu(bool showing) {
364  is_showing_context_menu_ = showing;
365}
366
367BackingStore* 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
382void RenderWidgetHostViewViews::SetBackground(const SkBitmap& background) {
383  RenderWidgetHostView::SetBackground(background);
384  if (host_)
385    host_->Send(new ViewMsg_SetBackground(host_->routing_id(), background));
386}
387
388void RenderWidgetHostViewViews::CreatePluginContainer(
389    gfx::PluginWindowHandle id) {
390  // TODO(anicolao): plugin_container_manager_.CreatePluginContainer(id);
391}
392
393void RenderWidgetHostViewViews::DestroyPluginContainer(
394    gfx::PluginWindowHandle id) {
395  // TODO(anicolao): plugin_container_manager_.DestroyPluginContainer(id);
396}
397
398void RenderWidgetHostViewViews::SetVisuallyDeemphasized(
399    const SkColor* color, bool animate) {
400  // TODO(anicolao)
401}
402
403bool RenderWidgetHostViewViews::ContainsNativeView(
404    gfx::NativeView native_view) const {
405  // TODO(port)
406  NOTREACHED() <<
407    "RenderWidgetHostViewViews::ContainsNativeView not implemented.";
408  return false;
409}
410
411void RenderWidgetHostViewViews::AcceleratedCompositingActivated(
412    bool activated) {
413  // TODO(anicolao): figure out if we need something here
414  if (activated)
415    NOTIMPLEMENTED();
416}
417
418gfx::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
429gfx::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
437std::string RenderWidgetHostViewViews::GetClassName() const {
438  return kViewClassName;
439}
440
441gfx::NativeCursor RenderWidgetHostViewViews::GetCursorForPoint(
442    ui::EventType type, const gfx::Point& point) {
443  return native_cursor_;
444}
445
446bool 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
467bool RenderWidgetHostViewViews::OnMouseDragged(const views::MouseEvent& event) {
468  OnMouseMoved(event);
469  return true;
470}
471
472void 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
483void 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
492void RenderWidgetHostViewViews::OnMouseEntered(const views::MouseEvent& event) {
493  // Already generated synthetically by webkit.
494}
495
496void RenderWidgetHostViewViews::OnMouseExited(const views::MouseEvent& event) {
497  // Already generated synthetically by webkit.
498}
499
500views::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
602bool 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
610bool RenderWidgetHostViewViews::OnKeyReleased(const views::KeyEvent& event) {
611  if (!host_)
612    return false;
613  host_->ForwardKeyboardEvent(NativeWebKeyboardEvent(event));
614  return true;
615}
616
617bool 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
636views::TextInputClient* RenderWidgetHostViewViews::GetTextInputClient() {
637  return this;
638}
639
640// TextInputClient implementation ---------------------------------------------
641void 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
667void RenderWidgetHostViewViews::ConfirmCompositionText() {
668  if (host_ && has_composition_text_)
669    host_->ImeConfirmComposition();
670  has_composition_text_ = false;
671}
672
673void RenderWidgetHostViewViews::ClearCompositionText() {
674  if (host_ && has_composition_text_)
675    host_->ImeCancelComposition();
676  has_composition_text_ = false;
677}
678
679void 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
686void 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
695ui::TextInputType RenderWidgetHostViewViews::GetTextInputType() {
696  return text_input_type_;
697}
698
699gfx::Rect RenderWidgetHostViewViews::GetCaretBounds() {
700  return caret_bounds_;
701}
702
703bool RenderWidgetHostViewViews::HasCompositionText() {
704  return has_composition_text_;
705}
706
707bool RenderWidgetHostViewViews::GetTextRange(ui::Range* range) {
708  // TODO(suzhe): implement this method when fixing http://crbug.com/55130.
709  NOTIMPLEMENTED();
710  return false;
711}
712
713bool RenderWidgetHostViewViews::GetCompositionTextRange(ui::Range* range) {
714  // TODO(suzhe): implement this method when fixing http://crbug.com/55130.
715  NOTIMPLEMENTED();
716  return false;
717}
718
719bool RenderWidgetHostViewViews::GetSelectionRange(ui::Range* range) {
720  // TODO(suzhe): implement this method when fixing http://crbug.com/55130.
721  NOTIMPLEMENTED();
722  return false;
723}
724
725bool 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
731bool 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
737bool 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
745void 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
756bool 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
768views::View* RenderWidgetHostViewViews::GetOwnerViewOfTextInputClient() {
769  return this;
770}
771
772void 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
868void RenderWidgetHostViewViews::Focus() {
869  RequestFocus();
870}
871
872void 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
879void 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
890void 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
901bool RenderWidgetHostViewViews::NeedsInputGrab() {
902  return popup_type_ == WebKit::WebPopupTypeSelect;
903}
904
905bool RenderWidgetHostViewViews::IsPopup() {
906  return popup_type_ != WebKit::WebPopupTypeNone;
907}
908
909void 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
918WebKit::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
941void 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
952RenderWidgetHostView*
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