// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/renderer_host/render_widget_host_view_views.h" #include #include #include "base/command_line.h" #include "base/logging.h" #include "base/message_loop.h" #include "base/metrics/histogram.h" #include "base/string_number_conversions.h" #include "base/task.h" #include "base/time.h" #include "chrome/common/native_web_keyboard_event.h" #include "chrome/common/render_messages.h" #include "chrome/common/result_codes.h" #include "content/browser/renderer_host/backing_store_skia.h" #include "content/browser/renderer_host/backing_store_x.h" #include "content/browser/renderer_host/render_widget_host.h" #include "third_party/WebKit/Source/WebKit/chromium/public/gtk/WebInputEventFactory.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" #include "ui/base/keycodes/keyboard_code_conversion_gtk.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/x/x11_util.h" #include "ui/gfx/canvas.h" #include "ui/gfx/canvas_skia.h" #include "ui/gfx/gtk_native_view_id_manager.h" #include "views/events/event.h" #include "views/ime/ime_context.h" #include "views/widget/widget.h" #include "views/widget/widget_gtk.h" static const int kMaxWindowWidth = 4000; static const int kMaxWindowHeight = 4000; static const char kRenderWidgetHostViewKey[] = "__RENDER_WIDGET_HOST_VIEW__"; static const char kBackingStoreSkiaSwitch[] = "use-backing-store-skia"; // Copied from third_party/WebKit/Source/WebCore/page/EventHandler.cpp // // Match key code of composition keydown event on windows. // IE sends VK_PROCESSKEY which has value 229; // // Please refer to following documents for detals: // - Virtual-Key Codes // http://msdn.microsoft.com/en-us/library/ms645540(VS.85).aspx // - How the IME System Works // http://msdn.microsoft.com/en-us/library/cc194848.aspx // - ImmGetVirtualKey Function // http://msdn.microsoft.com/en-us/library/dd318570(VS.85).aspx static const int kCompositionEventKeyCode = 229; using WebKit::WebInputEventFactory; using WebKit::WebMouseWheelEvent; using WebKit::WebTouchEvent; const char RenderWidgetHostViewViews::kViewClassName[] = "browser/renderer_host/RenderWidgetHostViewViews"; namespace { bool UsingBackingStoreSkia() { static bool decided = false; static bool use_skia = false; if (!decided) { CommandLine* cmdline = CommandLine::ForCurrentProcess(); use_skia = (cmdline && cmdline->HasSwitch(kBackingStoreSkiaSwitch)); decided = true; } return use_skia; } int WebInputEventFlagsFromViewsEvent(const views::Event& event) { int modifiers = 0; if (event.IsShiftDown()) modifiers |= WebKit::WebInputEvent::ShiftKey; if (event.IsControlDown()) modifiers |= WebKit::WebInputEvent::ControlKey; if (event.IsAltDown()) modifiers |= WebKit::WebInputEvent::AltKey; if (event.IsCapsLockDown()) modifiers |= WebKit::WebInputEvent::CapsLockOn; return modifiers; } WebKit::WebTouchPoint::State TouchPointStateFromEvent( const views::TouchEvent* event) { switch (event->type()) { case ui::ET_TOUCH_PRESSED: return WebKit::WebTouchPoint::StatePressed; case ui::ET_TOUCH_RELEASED: return WebKit::WebTouchPoint::StateReleased; case ui::ET_TOUCH_MOVED: return WebKit::WebTouchPoint::StateMoved; case ui::ET_TOUCH_CANCELLED: return WebKit::WebTouchPoint::StateCancelled; default: return WebKit::WebTouchPoint::StateUndefined; } } WebKit::WebInputEvent::Type TouchEventTypeFromEvent( const views::TouchEvent* event) { switch (event->type()) { case ui::ET_TOUCH_PRESSED: return WebKit::WebInputEvent::TouchStart; case ui::ET_TOUCH_RELEASED: return WebKit::WebInputEvent::TouchEnd; case ui::ET_TOUCH_MOVED: return WebKit::WebInputEvent::TouchMove; case ui::ET_TOUCH_CANCELLED: return WebKit::WebInputEvent::TouchCancel; default: return WebKit::WebInputEvent::Undefined; } } void UpdateTouchPointPosition(const views::TouchEvent* event, const gfx::Point& origin, WebKit::WebTouchPoint* tpoint) { tpoint->position.x = event->x(); tpoint->position.y = event->y(); tpoint->screenPosition.x = tpoint->position.x + origin.x(); tpoint->screenPosition.y = tpoint->position.y + origin.y(); } void InitializeWebMouseEventFromViewsEvent(const views::LocatedEvent& e, const gfx::Point& origin, WebKit::WebMouseEvent* wmevent) { wmevent->timeStampSeconds = base::Time::Now().ToDoubleT(); wmevent->modifiers = WebInputEventFlagsFromViewsEvent(e); wmevent->windowX = wmevent->x = e.x(); wmevent->windowY = wmevent->y = e.y(); wmevent->globalX = wmevent->x + origin.x(); wmevent->globalY = wmevent->y + origin.y(); } } // namespace class IMEContextHandler : public views::CommitTextListener, public views::CompositionListener, public views::ForwardKeyEventListener { public: explicit IMEContextHandler( RenderWidgetHostViewViews* host_view) : host_view_(host_view), is_enabled_(false), is_focused_(false), ime_context_(views::IMEContext::Create(host_view_)) { ime_context_->set_commit_text_listener(this); ime_context_->set_composition_listener(this); ime_context_->set_forward_key_event_listener(this); } // IMEContext Listeners implementation virtual void OnCommitText(views::IMEContext* sender, const string16& text) { DCHECK(ime_context_ == sender); RenderWidgetHost* host = host_view_->GetRenderWidgetHost(); if (host) { SendFakeCompositionWebKeyboardEvent(WebKit::WebInputEvent::RawKeyDown); host->ImeConfirmComposition(text); SendFakeCompositionWebKeyboardEvent(WebKit::WebInputEvent::KeyUp); } } virtual void OnStartComposition(views::IMEContext* sender) { DCHECK(ime_context_ == sender); } virtual void OnEndComposition(views::IMEContext* sender) { DCHECK(ime_context_ == sender); RenderWidgetHost* host = host_view_->GetRenderWidgetHost(); if (host) host->ImeCancelComposition(); } virtual void OnSetComposition(views::IMEContext* sender, const string16& text, const views::CompositionAttributeList& attributes, uint32 cursor_pos) { DCHECK(ime_context_ == sender); RenderWidgetHost* host = host_view_->GetRenderWidgetHost(); if (host) { SendFakeCompositionWebKeyboardEvent(WebKit::WebInputEvent::RawKeyDown); // Cast CompositonAttribute to WebKit::WebCompositionUnderline directly, // becasue CompositionAttribute is duplicated from // WebKit::WebCompositionUnderline. const std::vector& underlines = reinterpret_cast&>( attributes); host->ImeSetComposition(text, underlines, cursor_pos, cursor_pos); SendFakeCompositionWebKeyboardEvent(WebKit::WebInputEvent::KeyUp); } } virtual void OnForwardKeyEvent(views::IMEContext* sender, const views::KeyEvent& event) { DCHECK(ime_context_ == sender); host_view_->ForwardKeyEvent(event); } bool FilterKeyEvent(const views::KeyEvent& event) { return is_enabled_ && ime_context_->FilterKeyEvent(event); } void Focus() { if (!is_focused_) { ime_context_->Focus(); is_focused_ = true; } // Enables RenderWidget's IME related events, so that we can be notified // when WebKit wants to enable or disable IME. RenderWidgetHost* host = host_view_->GetRenderWidgetHost(); if (host) host->SetInputMethodActive(true); } void Blur() { if (is_focused_) { ime_context_->Blur(); is_focused_ = false; } // Disable RenderWidget's IME related events to save bandwidth. RenderWidgetHost* host = host_view_->GetRenderWidgetHost(); if (host) host->SetInputMethodActive(false); } void ImeUpdateTextInputState(WebKit::WebTextInputType type, const gfx::Rect& caret_rect) { bool enable = (type != WebKit::WebTextInputTypeNone) && (type != WebKit::WebTextInputTypePassword); if (is_enabled_ != enable) { is_enabled_ = enable; if (is_focused_) { if (is_enabled_) ime_context_->Focus(); else ime_context_->Blur(); } } if (is_enabled_) { gfx::Point p(caret_rect.origin()); views::View::ConvertPointToScreen(host_view_, &p); ime_context_->SetCursorLocation(gfx::Rect(p, caret_rect.size())); } } void Reset() { ime_context_->Reset(); } private: void SendFakeCompositionWebKeyboardEvent(WebKit::WebInputEvent::Type type) { NativeWebKeyboardEvent fake_event; fake_event.windowsKeyCode = kCompositionEventKeyCode; fake_event.skip_in_browser = true; fake_event.type = type; host_view_->ForwardWebKeyboardEvent(fake_event); } private: RenderWidgetHostViewViews* host_view_; bool is_enabled_; bool is_focused_; scoped_ptr ime_context_; DISALLOW_COPY_AND_ASSIGN(IMEContextHandler); }; // static RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget( RenderWidgetHost* widget) { return new RenderWidgetHostViewViews(widget); } RenderWidgetHostViewViews::RenderWidgetHostViewViews(RenderWidgetHost* host) : host_(host), about_to_validate_and_paint_(false), is_hidden_(false), is_loading_(false), native_cursor_(NULL), is_showing_context_menu_(false), visually_deemphasized_(false), touch_event_() { SetFocusable(true); host_->set_view(this); } RenderWidgetHostViewViews::~RenderWidgetHostViewViews() { } void RenderWidgetHostViewViews::InitAsChild() { Show(); ime_context_.reset(new IMEContextHandler(this)); } RenderWidgetHost* RenderWidgetHostViewViews::GetRenderWidgetHost() const { return host_; } void RenderWidgetHostViewViews::InitAsPopup( RenderWidgetHostView* parent_host_view, const gfx::Rect& pos) { // TODO(anicolao): figure out cases where popups occur and implement NOTIMPLEMENTED(); } void RenderWidgetHostViewViews::InitAsFullscreen() { NOTIMPLEMENTED(); } void RenderWidgetHostViewViews::DidBecomeSelected() { if (!is_hidden_) return; if (tab_switch_paint_time_.is_null()) tab_switch_paint_time_ = base::TimeTicks::Now(); is_hidden_ = false; host_->WasRestored(); } void RenderWidgetHostViewViews::WasHidden() { if (is_hidden_) return; // If we receive any more paint messages while we are hidden, we want to // ignore them so we don't re-allocate the backing store. We will paint // everything again when we become selected again. is_hidden_ = true; // If we have a renderer, then inform it that we are being hidden so it can // reduce its resource utilization. GetRenderWidgetHost()->WasHidden(); } void RenderWidgetHostViewViews::SetSize(const gfx::Size& size) { // This is called when webkit has sent us a Move message. int width = std::min(size.width(), kMaxWindowWidth); int height = std::min(size.height(), kMaxWindowHeight); if (requested_size_.width() != width || requested_size_.height() != height) { requested_size_ = gfx::Size(width, height); SetBounds(x(), y(), width, height); host_->WasResized(); } } void RenderWidgetHostViewViews::MovePluginWindows( const std::vector& moves) { // TODO(anicolao): NIY // NOTIMPLEMENTED(); } void RenderWidgetHostViewViews::Focus() { RequestFocus(); } bool RenderWidgetHostViewViews::HasFocus() { return View::HasFocus(); } void RenderWidgetHostViewViews::Show() { SetVisible(true); } void RenderWidgetHostViewViews::Hide() { SetVisible(false); } void RenderWidgetHostViewViews::Blur() { // TODO(estade): We should be clearing native focus as well, but I know of no // way to do that without focusing another widget. host_->Blur(); } bool RenderWidgetHostViewViews::IsShowing() { return IsVisible(); } gfx::Rect RenderWidgetHostViewViews::GetViewBounds() const { return bounds(); } void RenderWidgetHostViewViews::UpdateCursor(const WebCursor& cursor) { // Optimize the common case, where the cursor hasn't changed. // However, we can switch between different pixmaps, so only on the // non-pixmap branch. if (current_cursor_.GetCursorType() != GDK_CURSOR_IS_PIXMAP && current_cursor_.GetCursorType() == cursor.GetCursorType()) { return; } current_cursor_ = cursor; ShowCurrentCursor(); } void RenderWidgetHostViewViews::SetIsLoading(bool is_loading) { is_loading_ = is_loading; // Only call ShowCurrentCursor() when it will actually change the cursor. if (current_cursor_.GetCursorType() == GDK_LAST_CURSOR) ShowCurrentCursor(); } void RenderWidgetHostViewViews::ImeUpdateTextInputState( WebKit::WebTextInputType type, const gfx::Rect& caret_rect) { ime_context_->ImeUpdateTextInputState(type, caret_rect); } void RenderWidgetHostViewViews::ImeCancelComposition() { ime_context_->Reset(); } void RenderWidgetHostViewViews::DidUpdateBackingStore( const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy, const std::vector& copy_rects) { if (is_hidden_) return; // TODO(darin): Implement the equivalent of Win32's ScrollWindowEX. Can that // be done using XCopyArea? Perhaps similar to // BackingStore::ScrollBackingStore? if (about_to_validate_and_paint_) invalid_rect_ = invalid_rect_.Union(scroll_rect); else SchedulePaintInRect(scroll_rect); for (size_t i = 0; i < copy_rects.size(); ++i) { // Avoid double painting. NOTE: This is only relevant given the call to // Paint(scroll_rect) above. gfx::Rect rect = copy_rects[i].Subtract(scroll_rect); if (rect.IsEmpty()) continue; if (about_to_validate_and_paint_) invalid_rect_ = invalid_rect_.Union(rect); else SchedulePaintInRect(rect); } invalid_rect_ = invalid_rect_.Intersect(bounds()); } void RenderWidgetHostViewViews::RenderViewGone(base::TerminationStatus status, int error_code) { GetRenderWidgetHost()->ViewDestroyed(); Destroy(); } void RenderWidgetHostViewViews::Destroy() { // host_'s destruction brought us here, null it out so we don't use it host_ = NULL; if (parent()) parent()->RemoveChildView(this); MessageLoop::current()->DeleteSoon(FROM_HERE, this); } void RenderWidgetHostViewViews::SetTooltipText(const std::wstring& tip) { // TODO(anicolao): decide if we want tooltips for touch (none specified // right now/might want a press-and-hold display) // NOTIMPLEMENTED(); ... too annoying, it triggers for every mousemove } void RenderWidgetHostViewViews::SelectionChanged(const std::string& text) { // TODO(anicolao): deal with the clipboard without GTK NOTIMPLEMENTED(); } void RenderWidgetHostViewViews::ShowingContextMenu(bool showing) { is_showing_context_menu_ = showing; } bool RenderWidgetHostViewViews::NeedsInputGrab() { return popup_type_ == WebKit::WebPopupTypeSelect; } bool RenderWidgetHostViewViews::IsPopup() { return popup_type_ != WebKit::WebPopupTypeNone; } BackingStore* RenderWidgetHostViewViews::AllocBackingStore( const gfx::Size& size) { gfx::NativeView nview = GetInnerNativeView(); if (!nview) return NULL; if (UsingBackingStoreSkia()) { return new BackingStoreSkia(host_, size); } else { return new BackingStoreX(host_, size, ui::GetVisualFromGtkWidget(nview), gtk_widget_get_visual(nview)->depth); } } gfx::NativeView RenderWidgetHostViewViews::GetInnerNativeView() const { // TODO(sad): Ideally this function should be equivalent to GetNativeView, and // WidgetGtk-specific function call should not be necessary. const views::WidgetGtk* widget = static_cast(GetWidget()); return widget ? widget->window_contents() : NULL; } gfx::NativeView RenderWidgetHostViewViews::GetNativeView() { if (GetWidget()) return GetWidget()->GetNativeView(); return NULL; } void RenderWidgetHostViewViews::SetBackground(const SkBitmap& background) { RenderWidgetHostView::SetBackground(background); host_->Send(new ViewMsg_SetBackground(host_->routing_id(), background)); } void RenderWidgetHostViewViews::OnPaint(gfx::Canvas* canvas) { if (is_hidden_) { return; } // Paint a "hole" in the canvas so that the render of the web page is on // top of whatever else has already been painted in the views hierarchy. // Later views might still get to paint on top. canvas->FillRectInt(SK_ColorBLACK, 0, 0, bounds().width(), bounds().height(), SkXfermode::kClear_Mode); // Don't do any painting if the GPU process is rendering directly // into the View. RenderWidgetHost* render_widget_host = GetRenderWidgetHost(); if (render_widget_host->is_accelerated_compositing_active()) { return; } GdkWindow* window = GetInnerNativeView()->window; DCHECK(!about_to_validate_and_paint_); // TODO(anicolao): get the damage somehow // invalid_rect_ = damage_rect; invalid_rect_ = bounds(); gfx::Point origin; ConvertPointToWidget(this, &origin); about_to_validate_and_paint_ = true; BackingStore* backing_store = host_->GetBackingStore(true); // Calling GetBackingStore maybe have changed |invalid_rect_|... about_to_validate_and_paint_ = false; gfx::Rect paint_rect = gfx::Rect(0, 0, kMaxWindowWidth, kMaxWindowHeight); paint_rect = paint_rect.Intersect(invalid_rect_); if (backing_store) { // Only render the widget if it is attached to a window; there's a short // period where this object isn't attached to a window but hasn't been // Destroy()ed yet and it receives paint messages... if (window) { if (!visually_deemphasized_) { // In the common case, use XCopyArea. We don't draw more than once, so // we don't need to double buffer. if (UsingBackingStoreSkia()) { static_cast(backing_store)->SkiaShowRect( gfx::Point(paint_rect.x(), paint_rect.y()), canvas); } else { static_cast(backing_store)->XShowRect(origin, paint_rect, ui::GetX11WindowFromGdkWindow(window)); } } else if (!UsingBackingStoreSkia()) { // If the grey blend is showing, we make two drawing calls. Use double // buffering to prevent flicker. Use CairoShowRect because XShowRect // shortcuts GDK's double buffering. GdkRectangle rect = { paint_rect.x(), paint_rect.y(), paint_rect.width(), paint_rect.height() }; gdk_window_begin_paint_rect(window, &rect); static_cast(backing_store)->CairoShowRect( paint_rect, GDK_DRAWABLE(window)); cairo_t* cr = gdk_cairo_create(window); gdk_cairo_rectangle(cr, &rect); cairo_set_source_rgba(cr, 0, 0, 0, 0.7); cairo_fill(cr); cairo_destroy(cr); gdk_window_end_paint(window); } else { // TODO(sad) NOTIMPLEMENTED(); } } if (!whiteout_start_time_.is_null()) { base::TimeDelta whiteout_duration = base::TimeTicks::Now() - whiteout_start_time_; UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration); // Reset the start time to 0 so that we start recording again the next // time the backing store is NULL... whiteout_start_time_ = base::TimeTicks(); } if (!tab_switch_paint_time_.is_null()) { base::TimeDelta tab_switch_paint_duration = base::TimeTicks::Now() - tab_switch_paint_time_; UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration", tab_switch_paint_duration); // Reset tab_switch_paint_time_ to 0 so future tab selections are // recorded. tab_switch_paint_time_ = base::TimeTicks(); } } else { if (whiteout_start_time_.is_null()) whiteout_start_time_ = base::TimeTicks::Now(); } } gfx::NativeCursor RenderWidgetHostViewViews::GetCursorForPoint( ui::EventType type, const gfx::Point& point) { return native_cursor_; } bool RenderWidgetHostViewViews::OnMousePressed(const views::MouseEvent& event) { RequestFocus(); // TODO(anicolao): validate event generation. WebKit::WebMouseEvent e = WebMouseEventFromViewsEvent(event); // TODO(anicolao): deal with double clicks e.type = WebKit::WebInputEvent::MouseDown; e.clickCount = 1; GetRenderWidgetHost()->ForwardMouseEvent(e); return true; } void RenderWidgetHostViewViews::OnMouseReleased(const views::MouseEvent& event, bool canceled) { WebKit::WebMouseEvent e = WebMouseEventFromViewsEvent(event); e.type = WebKit::WebInputEvent::MouseUp; e.clickCount = 1; GetRenderWidgetHost()->ForwardMouseEvent(e); } bool RenderWidgetHostViewViews::OnMouseDragged(const views::MouseEvent& event) { OnMouseMoved(event); return true; } void RenderWidgetHostViewViews::OnMouseMoved(const views::MouseEvent& event) { WebKit::WebMouseEvent e = WebMouseEventFromViewsEvent(event); e.type = WebKit::WebInputEvent::MouseMove; GetRenderWidgetHost()->ForwardMouseEvent(e); } void RenderWidgetHostViewViews::OnMouseEntered(const views::MouseEvent& event) { // Already generated synthetically by webkit. } void RenderWidgetHostViewViews::OnMouseExited(const views::MouseEvent& event) { // Already generated synthetically by webkit. } bool RenderWidgetHostViewViews::OnMouseWheel(const views::MouseWheelEvent& e) { WebMouseWheelEvent wmwe; InitializeWebMouseEventFromViewsEvent(e, GetMirroredPosition(), &wmwe); wmwe.type = WebKit::WebInputEvent::MouseWheel; wmwe.button = WebKit::WebMouseEvent::ButtonNone; // TODO(sadrul): How do we determine if it's a horizontal scroll? wmwe.deltaY = e.offset(); wmwe.wheelTicksY = wmwe.deltaY > 0 ? 1 : -1; GetRenderWidgetHost()->ForwardWheelEvent(wmwe); return true; } bool RenderWidgetHostViewViews::OnKeyPressed(const views::KeyEvent& e) { if (!ime_context_->FilterKeyEvent(e)) ForwardKeyEvent(e); return TRUE; } bool RenderWidgetHostViewViews::OnKeyReleased(const views::KeyEvent& e) { if (!ime_context_->FilterKeyEvent(e)) ForwardKeyEvent(e); return TRUE; } void RenderWidgetHostViewViews::OnFocus() { ime_context_->Focus(); ShowCurrentCursor(); GetRenderWidgetHost()->GotFocus(); } void RenderWidgetHostViewViews::OnBlur() { // If we are showing a context menu, maintain the illusion that webkit has // focus. if (!is_showing_context_menu_ && !is_hidden_ && host_) host_->Blur(); ime_context_->Blur(); } void RenderWidgetHostViewViews::ShowCurrentCursor() { // The widget may not have a window. If that's the case, abort mission. This // is the same issue as that explained above in Paint(). if (!GetInnerNativeView() || !GetInnerNativeView()->window) return; native_cursor_ = current_cursor_.GetNativeCursor(); } void RenderWidgetHostViewViews::CreatePluginContainer( gfx::PluginWindowHandle id) { // TODO(anicolao): plugin_container_manager_.CreatePluginContainer(id); } void RenderWidgetHostViewViews::DestroyPluginContainer( gfx::PluginWindowHandle id) { // TODO(anicolao): plugin_container_manager_.DestroyPluginContainer(id); } void RenderWidgetHostViewViews::SetVisuallyDeemphasized( const SkColor* color, bool animate) { // TODO(anicolao) } bool RenderWidgetHostViewViews::ContainsNativeView( gfx::NativeView native_view) const { // TODO(port) NOTREACHED() << "RenderWidgetHostViewViews::ContainsNativeView not implemented."; return false; } void RenderWidgetHostViewViews::AcceleratedCompositingActivated( bool activated) { // TODO(anicolao): figure out if we need something here if (activated) NOTIMPLEMENTED(); } gfx::PluginWindowHandle RenderWidgetHostViewViews::AcquireCompositingSurface() { GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance(); gfx::PluginWindowHandle surface = gfx::kNullPluginWindow; gfx::NativeViewId view_id = gfx::IdFromNativeView(GetInnerNativeView()); if (!manager->GetXIDForId(&surface, view_id)) { DLOG(ERROR) << "Can't find XID for view id " << view_id; } return surface; } void RenderWidgetHostViewViews::ReleaseCompositingSurface( gfx::PluginWindowHandle surface) { } WebKit::WebMouseEvent RenderWidgetHostViewViews::WebMouseEventFromViewsEvent( const views::MouseEvent& event) { WebKit::WebMouseEvent wmevent; InitializeWebMouseEventFromViewsEvent(event, GetMirroredPosition(), &wmevent); // Setting |wmevent.button| is not necessary for -move events, but it is // necessary for -clicks and -drags. if (event.IsMiddleMouseButton()) { wmevent.modifiers |= WebKit::WebInputEvent::MiddleButtonDown; wmevent.button = WebKit::WebMouseEvent::ButtonMiddle; } if (event.IsRightMouseButton()) { wmevent.modifiers |= WebKit::WebInputEvent::RightButtonDown; wmevent.button = WebKit::WebMouseEvent::ButtonRight; } if (event.IsLeftMouseButton()) { wmevent.modifiers |= WebKit::WebInputEvent::LeftButtonDown; wmevent.button = WebKit::WebMouseEvent::ButtonLeft; } return wmevent; } void RenderWidgetHostViewViews::ForwardKeyEvent( const views::KeyEvent& event) { // This is how it works: // (1) If a RawKeyDown event is an accelerator for a reserved command (see // Browser::IsReservedCommand), then the command is executed. Otherwise, // the event is first sent off to the renderer. The renderer is also // notified whether the event would trigger an accelerator in the browser. // (2) A Char event is then sent to the renderer. // (3) If the renderer does not process the event in step (1), and the event // triggers an accelerator, then it will ignore the event in step (2). The // renderer also sends back notification to the browser for both steps (1) // and (2) about whether the events were processed or not. If the event // for (1) is not processed by the renderer, then it is processed by the // browser, and (2) is ignored. if (event.type() == ui::ET_KEY_PRESSED) { NativeWebKeyboardEvent wke; wke.type = WebKit::WebInputEvent::RawKeyDown; wke.windowsKeyCode = event.key_code(); wke.setKeyIdentifierFromWindowsKeyCode(); int keyval = ui::GdkKeyCodeForWindowsKeyCode(event.key_code(), event.IsShiftDown() ^ event.IsCapsLockDown()); wke.text[0] = wke.unmodifiedText[0] = static_cast(gdk_keyval_to_unicode(keyval)); wke.modifiers = WebInputEventFlagsFromViewsEvent(event); ForwardWebKeyboardEvent(wke); wke.type = WebKit::WebInputEvent::Char; ForwardWebKeyboardEvent(wke); } else { NativeWebKeyboardEvent wke; wke.type = WebKit::WebInputEvent::KeyUp; wke.windowsKeyCode = event.key_code(); wke.setKeyIdentifierFromWindowsKeyCode(); ForwardWebKeyboardEvent(wke); } } void RenderWidgetHostViewViews::ForwardWebKeyboardEvent( const NativeWebKeyboardEvent& event) { if (!host_) return; EditCommands edit_commands; #if 0 // TODO(bryeung): key bindings if (!event.skip_in_browser && key_bindings_handler_->Match(event, &edit_commands)) { host_->ForwardEditCommandsForNextKeyEvent(edit_commands); } #endif host_->ForwardKeyboardEvent(event); } views::View::TouchStatus RenderWidgetHostViewViews::OnTouchEvent( const views::TouchEvent& e) { // Update the list of touch points first. WebKit::WebTouchPoint* point = NULL; TouchStatus status = TOUCH_STATUS_UNKNOWN; switch (e.type()) { case ui::ET_TOUCH_PRESSED: // Add a new touch point. if (touch_event_.touchPointsLength < WebTouchEvent::touchPointsLengthCap) { point = &touch_event_.touchPoints[touch_event_.touchPointsLength++]; point->id = e.identity(); if (touch_event_.touchPointsLength == 1) { // A new touch sequence has started. status = TOUCH_STATUS_START; // We also want the focus. RequestFocus(); } } break; case ui::ET_TOUCH_RELEASED: case ui::ET_TOUCH_CANCELLED: case ui::ET_TOUCH_MOVED: { // The touch point should have been added to the event from an earlier // _PRESSED event. So find that. // At the moment, only a maximum of 4 touch-points are allowed. So a // simple loop should be sufficient. for (int i = 0; i < touch_event_.touchPointsLength; ++i) { point = touch_event_.touchPoints + i; if (point->id == e.identity()) { break; } point = NULL; } break; } default: DLOG(WARNING) << "Unknown touch event " << e.type(); break; } if (!point) return TOUCH_STATUS_UNKNOWN; if (status != TOUCH_STATUS_START) status = TOUCH_STATUS_CONTINUE; // Update the location and state of the point. point->state = TouchPointStateFromEvent(&e); if (point->state == WebKit::WebTouchPoint::StateMoved) { // It is possible for badly written touch drivers to emit Move events even // when the touch location hasn't changed. In such cases, consume the event // and pretend nothing happened. if (point->position.x == e.x() && point->position.y == e.y()) { return status; } } UpdateTouchPointPosition(&e, GetMirroredPosition(), point); // Mark the rest of the points as stationary. for (int i = 0; i < touch_event_.touchPointsLength; ++i) { WebKit::WebTouchPoint* iter = touch_event_.touchPoints + i; if (iter != point) { iter->state = WebKit::WebTouchPoint::StateStationary; } } // Update the type of the touch event. touch_event_.type = TouchEventTypeFromEvent(&e); touch_event_.timeStampSeconds = base::Time::Now().ToDoubleT(); // The event and all the touches have been updated. Dispatch. host_->ForwardTouchEvent(touch_event_); // If the touch was released, then remove it from the list of touch points. if (e.type() == ui::ET_TOUCH_RELEASED) { --touch_event_.touchPointsLength; for (int i = point - touch_event_.touchPoints; i < touch_event_.touchPointsLength; ++i) { touch_event_.touchPoints[i] = touch_event_.touchPoints[i + 1]; } if (touch_event_.touchPointsLength == 0) status = TOUCH_STATUS_END; } else if (e.type() == ui::ET_TOUCH_CANCELLED) { status = TOUCH_STATUS_CANCEL; } return status; } std::string RenderWidgetHostViewViews::GetClassName() const { return kViewClassName; } // static RenderWidgetHostView* RenderWidgetHostView::GetRenderWidgetHostViewFromNativeView( gfx::NativeView widget) { gpointer user_data = g_object_get_data(G_OBJECT(widget), kRenderWidgetHostViewKey); return reinterpret_cast(user_data); }