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 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 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 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 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 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 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 149 RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget( 150 RenderWidgetHost* widget) { 151 return new RenderWidgetHostViewViews(widget); 152 } 153 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 169 RenderWidgetHostViewViews::~RenderWidgetHostViewViews() { 170 } 171 172 void RenderWidgetHostViewViews::InitAsChild() { 173 Show(); 174 } 175 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 183 void RenderWidgetHostViewViews::InitAsFullscreen() { 184 NOTIMPLEMENTED(); 185 } 186 187 RenderWidgetHost* RenderWidgetHostViewViews::GetRenderWidgetHost() const { 188 return host_; 189 } 190 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 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 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 230 void RenderWidgetHostViewViews::SetBounds(const gfx::Rect& rect) { 231 NOTIMPLEMENTED(); 232 } 233 234 gfx::NativeView RenderWidgetHostViewViews::GetNativeView() { 235 if (GetWidget()) 236 return GetWidget()->GetNativeView(); 237 return NULL; 238 } 239 240 void RenderWidgetHostViewViews::MovePluginWindows( 241 const std::vector<webkit::npapi::WebPluginGeometry>& moves) { 242 // TODO(anicolao): NIY 243 // NOTIMPLEMENTED(); 244 } 245 246 bool RenderWidgetHostViewViews::HasFocus() { 247 return View::HasFocus(); 248 } 249 250 void RenderWidgetHostViewViews::Show() { 251 SetVisible(true); 252 } 253 254 void RenderWidgetHostViewViews::Hide() { 255 SetVisible(false); 256 } 257 258 bool RenderWidgetHostViewViews::IsShowing() { 259 return IsVisible(); 260 } 261 262 gfx::Rect RenderWidgetHostViewViews::GetViewBounds() const { 263 return bounds(); 264 } 265 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 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 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 301 void RenderWidgetHostViewViews::ImeCancelComposition() { 302 DCHECK(GetInputMethod()); 303 GetInputMethod()->CancelComposition(this); 304 has_composition_text_ = false; 305 } 306 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 336 void RenderWidgetHostViewViews::RenderViewGone(base::TerminationStatus status, 337 int error_code) { 338 DCHECK(host_); 339 host_->ViewDestroyed(); 340 Destroy(); 341 } 342 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 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 358 void RenderWidgetHostViewViews::SelectionChanged(const std::string& text) { 359 // TODO(anicolao): deal with the clipboard without GTK 360 NOTIMPLEMENTED(); 361 } 362 363 void RenderWidgetHostViewViews::ShowingContextMenu(bool showing) { 364 is_showing_context_menu_ = showing; 365 } 366 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 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 388 void RenderWidgetHostViewViews::CreatePluginContainer( 389 gfx::PluginWindowHandle id) { 390 // TODO(anicolao): plugin_container_manager_.CreatePluginContainer(id); 391 } 392 393 void RenderWidgetHostViewViews::DestroyPluginContainer( 394 gfx::PluginWindowHandle id) { 395 // TODO(anicolao): plugin_container_manager_.DestroyPluginContainer(id); 396 } 397 398 void RenderWidgetHostViewViews::SetVisuallyDeemphasized( 399 const SkColor* color, bool animate) { 400 // TODO(anicolao) 401 } 402 403 bool RenderWidgetHostViewViews::ContainsNativeView( 404 gfx::NativeView native_view) const { 405 // TODO(port) 406 NOTREACHED() << 407 "RenderWidgetHostViewViews::ContainsNativeView not implemented."; 408 return false; 409 } 410 411 void RenderWidgetHostViewViews::AcceleratedCompositingActivated( 412 bool activated) { 413 // TODO(anicolao): figure out if we need something here 414 if (activated) 415 NOTIMPLEMENTED(); 416 } 417 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 429 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 437 std::string RenderWidgetHostViewViews::GetClassName() const { 438 return kViewClassName; 439 } 440 441 gfx::NativeCursor RenderWidgetHostViewViews::GetCursorForPoint( 442 ui::EventType type, const gfx::Point& point) { 443 return native_cursor_; 444 } 445 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 467 bool RenderWidgetHostViewViews::OnMouseDragged(const views::MouseEvent& event) { 468 OnMouseMoved(event); 469 return true; 470 } 471 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 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 492 void RenderWidgetHostViewViews::OnMouseEntered(const views::MouseEvent& event) { 493 // Already generated synthetically by webkit. 494 } 495 496 void RenderWidgetHostViewViews::OnMouseExited(const views::MouseEvent& event) { 497 // Already generated synthetically by webkit. 498 } 499 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 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 610 bool RenderWidgetHostViewViews::OnKeyReleased(const views::KeyEvent& event) { 611 if (!host_) 612 return false; 613 host_->ForwardKeyboardEvent(NativeWebKeyboardEvent(event)); 614 return true; 615 } 616 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 636 views::TextInputClient* RenderWidgetHostViewViews::GetTextInputClient() { 637 return this; 638 } 639 640 // TextInputClient implementation --------------------------------------------- 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 667 void RenderWidgetHostViewViews::ConfirmCompositionText() { 668 if (host_ && has_composition_text_) 669 host_->ImeConfirmComposition(); 670 has_composition_text_ = false; 671 } 672 673 void RenderWidgetHostViewViews::ClearCompositionText() { 674 if (host_ && has_composition_text_) 675 host_->ImeCancelComposition(); 676 has_composition_text_ = false; 677 } 678 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 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 695 ui::TextInputType RenderWidgetHostViewViews::GetTextInputType() { 696 return text_input_type_; 697 } 698 699 gfx::Rect RenderWidgetHostViewViews::GetCaretBounds() { 700 return caret_bounds_; 701 } 702 703 bool RenderWidgetHostViewViews::HasCompositionText() { 704 return has_composition_text_; 705 } 706 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 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 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 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 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 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 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 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 768 views::View* RenderWidgetHostViewViews::GetOwnerViewOfTextInputClient() { 769 return this; 770 } 771 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 868 void RenderWidgetHostViewViews::Focus() { 869 RequestFocus(); 870 } 871 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 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 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 901 bool RenderWidgetHostViewViews::NeedsInputGrab() { 902 return popup_type_ == WebKit::WebPopupTypeSelect; 903 } 904 905 bool RenderWidgetHostViewViews::IsPopup() { 906 return popup_type_ != WebKit::WebPopupTypeNone; 907 } 908 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 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 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* 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