1 /* 2 * Copyright (C) 2010 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "WebView.h" 28 29 #include "ChunkedUpdateDrawingAreaProxy.h" 30 #include "DrawingAreaProxyImpl.h" 31 #include "FindIndicator.h" 32 #include "Logging.h" 33 #include "NativeWebKeyboardEvent.h" 34 #include "NativeWebMouseEvent.h" 35 #include "Region.h" 36 #include "RunLoop.h" 37 #include "WKAPICast.h" 38 #include "WebContext.h" 39 #include "WebContextMenuProxyWin.h" 40 #include "WebEditCommandProxy.h" 41 #include "WebEventFactory.h" 42 #include "WebPageProxy.h" 43 #include "WebPopupMenuProxyWin.h" 44 #include <Commctrl.h> 45 #include <WebCore/BitmapInfo.h> 46 #include <WebCore/Cursor.h> 47 #include <WebCore/FloatRect.h> 48 #if USE(CG) 49 #include <WebCore/GraphicsContextCG.h> 50 #endif 51 #include <WebCore/IntRect.h> 52 #include <WebCore/SoftLinking.h> 53 #include <WebCore/WebCoreInstanceHandle.h> 54 #include <WebCore/WindowMessageBroadcaster.h> 55 #include <WebCore/WindowsTouch.h> 56 #include <wtf/text/WTFString.h> 57 58 namespace Ime { 59 // We need these functions in a separate namespace, because in the global namespace they conflict 60 // with the definitions in imm.h only by the type modifier (the macro defines them as static) and 61 // imm.h is included by windows.h 62 SOFT_LINK_LIBRARY(IMM32) 63 SOFT_LINK(IMM32, ImmGetContext, HIMC, WINAPI, (HWND hwnd), (hwnd)) 64 SOFT_LINK(IMM32, ImmReleaseContext, BOOL, WINAPI, (HWND hWnd, HIMC hIMC), (hWnd, hIMC)) 65 SOFT_LINK(IMM32, ImmGetCompositionStringW, LONG, WINAPI, (HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen), (hIMC, dwIndex, lpBuf, dwBufLen)) 66 SOFT_LINK(IMM32, ImmSetCandidateWindow, BOOL, WINAPI, (HIMC hIMC, LPCANDIDATEFORM lpCandidate), (hIMC, lpCandidate)) 67 SOFT_LINK(IMM32, ImmSetOpenStatus, BOOL, WINAPI, (HIMC hIMC, BOOL fOpen), (hIMC, fOpen)) 68 SOFT_LINK(IMM32, ImmNotifyIME, BOOL, WINAPI, (HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue), (hIMC, dwAction, dwIndex, dwValue)) 69 SOFT_LINK(IMM32, ImmAssociateContextEx, BOOL, WINAPI, (HWND hWnd, HIMC hIMC, DWORD dwFlags), (hWnd, hIMC, dwFlags)) 70 }; 71 72 // Soft link functions for gestures and panning. 73 SOFT_LINK_LIBRARY(USER32); 74 SOFT_LINK_OPTIONAL(USER32, GetGestureInfo, BOOL, WINAPI, (HGESTUREINFO, PGESTUREINFO)); 75 SOFT_LINK_OPTIONAL(USER32, SetGestureConfig, BOOL, WINAPI, (HWND, DWORD, UINT, PGESTURECONFIG, UINT)); 76 SOFT_LINK_OPTIONAL(USER32, CloseGestureInfoHandle, BOOL, WINAPI, (HGESTUREINFO)); 77 78 SOFT_LINK_LIBRARY(Uxtheme); 79 SOFT_LINK_OPTIONAL(Uxtheme, BeginPanningFeedback, BOOL, WINAPI, (HWND)); 80 SOFT_LINK_OPTIONAL(Uxtheme, EndPanningFeedback, BOOL, WINAPI, (HWND, BOOL)); 81 SOFT_LINK_OPTIONAL(Uxtheme, UpdatePanningFeedback, BOOL, WINAPI, (HWND, LONG, LONG, BOOL)); 82 83 using namespace WebCore; 84 85 namespace WebKit { 86 87 static const LPCWSTR kWebKit2WebViewWindowClassName = L"WebKit2WebViewWindowClass"; 88 89 // Constants not available on all platforms. 90 const int WM_XP_THEMECHANGED = 0x031A; 91 const int WM_VISTA_MOUSEHWHEEL = 0x020E; 92 93 static const int kMaxToolTipWidth = 250; 94 95 enum { 96 UpdateActiveStateTimer = 1, 97 }; 98 99 static bool useNewDrawingArea() 100 { 101 // FIXME: Remove this function and the old drawing area code once we aren't interested in 102 // testing the old drawing area anymore. 103 return true; 104 } 105 106 LRESULT CALLBACK WebView::WebViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 107 { 108 LONG_PTR longPtr = ::GetWindowLongPtr(hWnd, 0); 109 110 if (WebView* webView = reinterpret_cast<WebView*>(longPtr)) 111 return webView->wndProc(hWnd, message, wParam, lParam); 112 113 if (message == WM_CREATE) { 114 LPCREATESTRUCT createStruct = reinterpret_cast<LPCREATESTRUCT>(lParam); 115 116 // Associate the WebView with the window. 117 ::SetWindowLongPtr(hWnd, 0, (LONG_PTR)createStruct->lpCreateParams); 118 return 0; 119 } 120 121 return ::DefWindowProc(hWnd, message, wParam, lParam); 122 } 123 124 LRESULT WebView::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 125 { 126 LRESULT lResult = 0; 127 bool handled = true; 128 129 switch (message) { 130 case WM_CLOSE: 131 m_page->tryClose(); 132 break; 133 case WM_DESTROY: 134 m_isBeingDestroyed = true; 135 close(); 136 break; 137 case WM_ERASEBKGND: 138 lResult = 1; 139 break; 140 case WM_PAINT: 141 lResult = onPaintEvent(hWnd, message, wParam, lParam, handled); 142 break; 143 case WM_PRINTCLIENT: 144 lResult = onPrintClientEvent(hWnd, message, wParam, lParam, handled); 145 break; 146 case WM_MOUSEACTIVATE: 147 setWasActivatedByMouseEvent(true); 148 handled = false; 149 break; 150 case WM_MOUSEMOVE: 151 case WM_LBUTTONDOWN: 152 case WM_MBUTTONDOWN: 153 case WM_RBUTTONDOWN: 154 case WM_LBUTTONDBLCLK: 155 case WM_MBUTTONDBLCLK: 156 case WM_RBUTTONDBLCLK: 157 case WM_LBUTTONUP: 158 case WM_MBUTTONUP: 159 case WM_RBUTTONUP: 160 case WM_MOUSELEAVE: 161 lResult = onMouseEvent(hWnd, message, wParam, lParam, handled); 162 break; 163 case WM_MOUSEWHEEL: 164 case WM_VISTA_MOUSEHWHEEL: 165 lResult = onWheelEvent(hWnd, message, wParam, lParam, handled); 166 break; 167 case WM_HSCROLL: 168 lResult = onHorizontalScroll(hWnd, message, wParam, lParam, handled); 169 break; 170 case WM_VSCROLL: 171 lResult = onVerticalScroll(hWnd, message, wParam, lParam, handled); 172 break; 173 case WM_GESTURENOTIFY: 174 lResult = onGestureNotify(hWnd, message, wParam, lParam, handled); 175 break; 176 case WM_GESTURE: 177 lResult = onGesture(hWnd, message, wParam, lParam, handled); 178 break; 179 case WM_SYSKEYDOWN: 180 case WM_KEYDOWN: 181 case WM_SYSCHAR: 182 case WM_CHAR: 183 case WM_SYSKEYUP: 184 case WM_KEYUP: 185 lResult = onKeyEvent(hWnd, message, wParam, lParam, handled); 186 break; 187 case WM_SIZE: 188 lResult = onSizeEvent(hWnd, message, wParam, lParam, handled); 189 break; 190 case WM_WINDOWPOSCHANGED: 191 lResult = onWindowPositionChangedEvent(hWnd, message, wParam, lParam, handled); 192 break; 193 case WM_SETFOCUS: 194 lResult = onSetFocusEvent(hWnd, message, wParam, lParam, handled); 195 break; 196 case WM_KILLFOCUS: 197 lResult = onKillFocusEvent(hWnd, message, wParam, lParam, handled); 198 break; 199 case WM_TIMER: 200 lResult = onTimerEvent(hWnd, message, wParam, lParam, handled); 201 break; 202 case WM_SHOWWINDOW: 203 lResult = onShowWindowEvent(hWnd, message, wParam, lParam, handled); 204 break; 205 case WM_SETCURSOR: 206 lResult = onSetCursor(hWnd, message, wParam, lParam, handled); 207 break; 208 case WM_IME_STARTCOMPOSITION: 209 handled = onIMEStartComposition(); 210 break; 211 case WM_IME_REQUEST: 212 lResult = onIMERequest(wParam, lParam); 213 break; 214 case WM_IME_COMPOSITION: 215 handled = onIMEComposition(lParam); 216 break; 217 case WM_IME_ENDCOMPOSITION: 218 handled = onIMEEndComposition(); 219 break; 220 case WM_IME_SELECT: 221 handled = onIMESelect(wParam, lParam); 222 break; 223 case WM_IME_SETCONTEXT: 224 handled = onIMESetContext(wParam, lParam); 225 break; 226 default: 227 handled = false; 228 break; 229 } 230 231 if (!handled) 232 lResult = ::DefWindowProc(hWnd, message, wParam, lParam); 233 234 return lResult; 235 } 236 237 bool WebView::registerWebViewWindowClass() 238 { 239 static bool haveRegisteredWindowClass = false; 240 if (haveRegisteredWindowClass) 241 return true; 242 haveRegisteredWindowClass = true; 243 244 WNDCLASSEX wcex; 245 246 wcex.cbSize = sizeof(WNDCLASSEX); 247 wcex.style = CS_DBLCLKS; 248 wcex.lpfnWndProc = WebView::WebViewWndProc; 249 wcex.cbClsExtra = 0; 250 wcex.cbWndExtra = sizeof(WebView*); 251 wcex.hInstance = instanceHandle(); 252 wcex.hIcon = 0; 253 wcex.hCursor = ::LoadCursor(0, IDC_ARROW); 254 wcex.hbrBackground = 0; 255 wcex.lpszMenuName = 0; 256 wcex.lpszClassName = kWebKit2WebViewWindowClassName; 257 wcex.hIconSm = 0; 258 259 return !!::RegisterClassEx(&wcex); 260 } 261 262 WebView::WebView(RECT rect, WebContext* context, WebPageGroup* pageGroup, HWND parentWindow) 263 : m_topLevelParentWindow(0) 264 , m_toolTipWindow(0) 265 , m_lastCursorSet(0) 266 , m_webCoreCursor(0) 267 , m_overrideCursor(0) 268 , m_trackingMouseLeave(false) 269 , m_isInWindow(false) 270 , m_isVisible(false) 271 , m_wasActivatedByMouseEvent(false) 272 , m_isBeingDestroyed(false) 273 , m_inIMEComposition(0) 274 , m_findIndicatorCallback(0) 275 , m_findIndicatorCallbackContext(0) 276 , m_lastPanX(0) 277 , m_lastPanY(0) 278 , m_overPanY(0) 279 , m_gestureReachedScrollingLimit(false) 280 { 281 registerWebViewWindowClass(); 282 283 m_window = ::CreateWindowExW(0, kWebKit2WebViewWindowClassName, 0, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE, 284 rect.top, rect.left, rect.right - rect.left, rect.bottom - rect.top, parentWindow ? parentWindow : HWND_MESSAGE, 0, instanceHandle(), this); 285 ASSERT(::IsWindow(m_window)); 286 // We only check our window style, and not ::IsWindowVisible, because m_isVisible only tracks 287 // this window's visibility status, while ::IsWindowVisible takes our ancestors' visibility 288 // status into account. <http://webkit.org/b/54104> 289 ASSERT(m_isVisible == static_cast<bool>(::GetWindowLong(m_window, GWL_STYLE) & WS_VISIBLE)); 290 291 m_page = context->createWebPage(this, pageGroup); 292 m_page->initializeWebPage(); 293 294 CoCreateInstance(CLSID_DragDropHelper, 0, CLSCTX_INPROC_SERVER, IID_IDropTargetHelper, (void**)&m_dropTargetHelper); 295 296 // FIXME: Initializing the tooltip window here matches WebKit win, but seems like something 297 // we could do on demand to save resources. 298 initializeToolTipWindow(); 299 300 // Initialize the top level parent window and register it with the WindowMessageBroadcaster. 301 windowAncestryDidChange(); 302 } 303 304 WebView::~WebView() 305 { 306 // Tooltip window needs to be explicitly destroyed since it isn't a WS_CHILD. 307 if (::IsWindow(m_toolTipWindow)) 308 ::DestroyWindow(m_toolTipWindow); 309 } 310 311 void WebView::initialize() 312 { 313 ::RegisterDragDrop(m_window, this); 314 315 if (shouldInitializeTrackPointHack()) { 316 // If we detected a registry key belonging to a TrackPoint driver, then create fake 317 // scrollbars, so the WebView will receive WM_VSCROLL and WM_HSCROLL messages. 318 // We create an invisible vertical scrollbar and an invisible horizontal scrollbar to allow 319 // for receiving both types of messages. 320 ::CreateWindow(TEXT("SCROLLBAR"), TEXT("FAKETRACKPOINTHSCROLLBAR"), WS_CHILD | WS_VISIBLE | SBS_HORZ, 0, 0, 0, 0, m_window, 0, instanceHandle(), 0); 321 ::CreateWindow(TEXT("SCROLLBAR"), TEXT("FAKETRACKPOINTVSCROLLBAR"), WS_CHILD | WS_VISIBLE | SBS_VERT, 0, 0, 0, 0, m_window, 0, instanceHandle(), 0); 322 } 323 } 324 325 void WebView::initializeUndoClient(const WKViewUndoClient* client) 326 { 327 m_undoClient.initialize(client); 328 } 329 330 void WebView::setParentWindow(HWND parentWindow) 331 { 332 if (m_window) { 333 // If the host window hasn't changed, bail. 334 if (::GetParent(m_window) == parentWindow) 335 return; 336 if (parentWindow) 337 ::SetParent(m_window, parentWindow); 338 else if (!m_isBeingDestroyed) { 339 // Turn the WebView into a message-only window so it will no longer be a child of the 340 // old parent window and will be hidden from screen. We only do this when 341 // isBeingDestroyed() is false because doing this while handling WM_DESTROY can leave 342 // m_window in a weird state (see <http://webkit.org/b/29337>). 343 ::SetParent(m_window, HWND_MESSAGE); 344 } 345 } 346 347 windowAncestryDidChange(); 348 } 349 350 static HWND findTopLevelParentWindow(HWND window) 351 { 352 if (!window) 353 return 0; 354 355 HWND current = window; 356 for (HWND parent = GetParent(current); current; current = parent, parent = GetParent(parent)) { 357 if (!parent || !(GetWindowLongPtr(current, GWL_STYLE) & (WS_POPUP | WS_CHILD))) 358 return current; 359 } 360 ASSERT_NOT_REACHED(); 361 return 0; 362 } 363 364 void WebView::windowAncestryDidChange() 365 { 366 HWND newTopLevelParentWindow; 367 if (m_window) 368 newTopLevelParentWindow = findTopLevelParentWindow(m_window); 369 else { 370 // There's no point in tracking active state changes of our parent window if we don't have 371 // a window ourselves. 372 newTopLevelParentWindow = 0; 373 } 374 375 if (newTopLevelParentWindow == m_topLevelParentWindow) 376 return; 377 378 if (m_topLevelParentWindow) 379 WindowMessageBroadcaster::removeListener(m_topLevelParentWindow, this); 380 381 m_topLevelParentWindow = newTopLevelParentWindow; 382 383 if (m_topLevelParentWindow) 384 WindowMessageBroadcaster::addListener(m_topLevelParentWindow, this); 385 386 updateActiveState(); 387 } 388 389 LRESULT WebView::onMouseEvent(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) 390 { 391 NativeWebMouseEvent mouseEvent = NativeWebMouseEvent(hWnd, message, wParam, lParam, m_wasActivatedByMouseEvent); 392 setWasActivatedByMouseEvent(false); 393 394 switch (message) { 395 case WM_LBUTTONDOWN: 396 case WM_MBUTTONDOWN: 397 case WM_RBUTTONDOWN: 398 ::SetFocus(m_window); 399 ::SetCapture(m_window); 400 break; 401 case WM_LBUTTONUP: 402 case WM_MBUTTONUP: 403 case WM_RBUTTONUP: 404 ::ReleaseCapture(); 405 break; 406 case WM_MOUSEMOVE: 407 startTrackingMouseLeave(); 408 break; 409 case WM_MOUSELEAVE: 410 stopTrackingMouseLeave(); 411 break; 412 case WM_LBUTTONDBLCLK: 413 case WM_MBUTTONDBLCLK: 414 case WM_RBUTTONDBLCLK: 415 break; 416 default: 417 ASSERT_NOT_REACHED(); 418 } 419 420 m_page->handleMouseEvent(mouseEvent); 421 422 handled = true; 423 return 0; 424 } 425 426 LRESULT WebView::onWheelEvent(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) 427 { 428 WebWheelEvent wheelEvent = WebEventFactory::createWebWheelEvent(hWnd, message, wParam, lParam); 429 if (wheelEvent.controlKey()) { 430 // We do not want WebKit to handle Control + Wheel, this should be handled by the client application 431 // to zoom the page. 432 handled = false; 433 return 0; 434 } 435 436 m_page->handleWheelEvent(wheelEvent); 437 438 handled = true; 439 return 0; 440 } 441 442 LRESULT WebView::onHorizontalScroll(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) 443 { 444 ScrollDirection direction; 445 ScrollGranularity granularity; 446 switch (LOWORD(wParam)) { 447 case SB_LINELEFT: 448 granularity = ScrollByLine; 449 direction = ScrollLeft; 450 break; 451 case SB_LINERIGHT: 452 granularity = ScrollByLine; 453 direction = ScrollRight; 454 break; 455 case SB_PAGELEFT: 456 granularity = ScrollByDocument; 457 direction = ScrollLeft; 458 break; 459 case SB_PAGERIGHT: 460 granularity = ScrollByDocument; 461 direction = ScrollRight; 462 break; 463 default: 464 handled = false; 465 return 0; 466 } 467 468 m_page->scrollBy(direction, granularity); 469 470 handled = true; 471 return 0; 472 } 473 474 LRESULT WebView::onVerticalScroll(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) 475 { 476 ScrollDirection direction; 477 ScrollGranularity granularity; 478 switch (LOWORD(wParam)) { 479 case SB_LINEDOWN: 480 granularity = ScrollByLine; 481 direction = ScrollDown; 482 break; 483 case SB_LINEUP: 484 granularity = ScrollByLine; 485 direction = ScrollUp; 486 break; 487 case SB_PAGEDOWN: 488 granularity = ScrollByDocument; 489 direction = ScrollDown; 490 break; 491 case SB_PAGEUP: 492 granularity = ScrollByDocument; 493 direction = ScrollUp; 494 break; 495 default: 496 handled = false; 497 return 0; 498 } 499 500 m_page->scrollBy(direction, granularity); 501 502 handled = true; 503 return 0; 504 } 505 506 LRESULT WebView::onGestureNotify(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) 507 { 508 // We shouldn't be getting any gesture messages without SetGestureConfig soft-linking correctly. 509 ASSERT(SetGestureConfigPtr()); 510 511 GESTURENOTIFYSTRUCT* gn = reinterpret_cast<GESTURENOTIFYSTRUCT*>(lParam); 512 513 POINT localPoint = { gn->ptsLocation.x, gn->ptsLocation.y }; 514 ::ScreenToClient(m_window, &localPoint); 515 516 bool canPan = m_page->gestureWillBegin(localPoint); 517 518 DWORD dwPanWant = GC_PAN | GC_PAN_WITH_INERTIA | GC_PAN_WITH_GUTTER; 519 DWORD dwPanBlock = GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY; 520 if (canPan) 521 dwPanWant |= GC_PAN_WITH_SINGLE_FINGER_VERTICALLY; 522 else 523 dwPanBlock |= GC_PAN_WITH_SINGLE_FINGER_VERTICALLY; 524 525 GESTURECONFIG gc = { GID_PAN, dwPanWant, dwPanBlock }; 526 return SetGestureConfigPtr()(m_window, 0, 1, &gc, sizeof(gc)); 527 } 528 529 LRESULT WebView::onGesture(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) 530 { 531 ASSERT(GetGestureInfoPtr()); 532 ASSERT(CloseGestureInfoHandlePtr()); 533 ASSERT(UpdatePanningFeedbackPtr()); 534 ASSERT(BeginPanningFeedbackPtr()); 535 ASSERT(EndPanningFeedbackPtr()); 536 537 if (!GetGestureInfoPtr() || !CloseGestureInfoHandlePtr() || !UpdatePanningFeedbackPtr() || !BeginPanningFeedbackPtr() || !EndPanningFeedbackPtr()) { 538 handled = false; 539 return 0; 540 } 541 542 HGESTUREINFO gestureHandle = reinterpret_cast<HGESTUREINFO>(lParam); 543 GESTUREINFO gi = {0}; 544 gi.cbSize = sizeof(GESTUREINFO); 545 546 if (!GetGestureInfoPtr()(gestureHandle, &gi)) { 547 handled = false; 548 return 0; 549 } 550 551 switch (gi.dwID) { 552 case GID_BEGIN: 553 m_lastPanX = gi.ptsLocation.x; 554 m_lastPanY = gi.ptsLocation.y; 555 break; 556 case GID_END: 557 m_page->gestureDidEnd(); 558 break; 559 case GID_PAN: { 560 int currentX = gi.ptsLocation.x; 561 int currentY = gi.ptsLocation.y; 562 563 // Reverse the calculations because moving your fingers up should move the screen down, and 564 // vice-versa. 565 int deltaX = m_lastPanX - currentX; 566 int deltaY = m_lastPanY - currentY; 567 568 m_lastPanX = currentX; 569 m_lastPanY = currentY; 570 571 // Calculate the overpan for window bounce. 572 m_overPanY -= deltaY; 573 574 if (deltaX || deltaY) 575 m_page->gestureDidScroll(IntSize(deltaX, deltaY)); 576 577 if (gi.dwFlags & GF_BEGIN) { 578 BeginPanningFeedbackPtr()(m_window); 579 m_gestureReachedScrollingLimit = false; 580 m_overPanY = 0; 581 } else if (gi.dwFlags & GF_END) { 582 EndPanningFeedbackPtr()(m_window, true); 583 m_overPanY = 0; 584 } 585 586 // FIXME: Support horizontal window bounce - <http://webkit.org/b/58068>. 587 // FIXME: Window Bounce doesn't undo until user releases their finger - <http://webkit.org/b/58069>. 588 589 if (m_gestureReachedScrollingLimit) 590 UpdatePanningFeedbackPtr()(m_window, 0, m_overPanY, gi.dwFlags & GF_INERTIA); 591 592 CloseGestureInfoHandlePtr()(gestureHandle); 593 594 handled = true; 595 return 0; 596 } 597 default: 598 break; 599 } 600 601 // If we get to this point, the gesture has not been handled. We forward 602 // the call to DefWindowProc by returning false, and we don't need to 603 // to call CloseGestureInfoHandle. 604 // http://msdn.microsoft.com/en-us/library/dd353228(VS.85).aspx 605 handled = false; 606 return 0; 607 } 608 609 LRESULT WebView::onKeyEvent(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) 610 { 611 m_page->handleKeyboardEvent(NativeWebKeyboardEvent(hWnd, message, wParam, lParam)); 612 613 // We claim here to always have handled the event. If the event is not in fact handled, we will 614 // find out later in didNotHandleKeyEvent. 615 handled = true; 616 return 0; 617 } 618 619 static void drawPageBackground(HDC dc, const RECT& rect) 620 { 621 // Mac checks WebPageProxy::drawsBackground and 622 // WebPageProxy::drawsTransparentBackground here, but those are always false on 623 // Windows currently (see <http://webkit.org/b/52009>). 624 ::FillRect(dc, &rect, reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1)); 625 } 626 627 void WebView::paint(HDC hdc, const IntRect& dirtyRect) 628 { 629 m_page->endPrinting(); 630 if (useNewDrawingArea()) { 631 if (DrawingAreaProxyImpl* drawingArea = static_cast<DrawingAreaProxyImpl*>(m_page->drawingArea())) { 632 // FIXME: We should port WebKit1's rect coalescing logic here. 633 Region unpaintedRegion; 634 drawingArea->paint(hdc, dirtyRect, unpaintedRegion); 635 636 Vector<IntRect> unpaintedRects = unpaintedRegion.rects(); 637 for (size_t i = 0; i < unpaintedRects.size(); ++i) { 638 RECT winRect = unpaintedRects[i]; 639 drawPageBackground(hdc, unpaintedRects[i]); 640 } 641 } else 642 drawPageBackground(hdc, dirtyRect); 643 644 m_page->didDraw(); 645 } else { 646 if (m_page->isValid() && m_page->drawingArea() && m_page->drawingArea()->paint(dirtyRect, hdc)) 647 m_page->didDraw(); 648 else 649 drawPageBackground(hdc, dirtyRect); 650 } 651 } 652 653 static void flashRects(HDC dc, const IntRect rects[], size_t rectCount, HBRUSH brush) 654 { 655 for (size_t i = 0; i < rectCount; ++i) { 656 RECT winRect = rects[i]; 657 ::FillRect(dc, &winRect, brush); 658 } 659 660 ::GdiFlush(); 661 ::Sleep(50); 662 } 663 664 static OwnPtr<HBRUSH> createBrush(const Color& color) 665 { 666 return adoptPtr(::CreateSolidBrush(RGB(color.red(), color.green(), color.blue()))); 667 } 668 669 LRESULT WebView::onPaintEvent(HWND hWnd, UINT message, WPARAM, LPARAM, bool& handled) 670 { 671 PAINTSTRUCT paintStruct; 672 HDC hdc = ::BeginPaint(m_window, &paintStruct); 673 674 if (WebPageProxy::debugPaintFlags() & kWKDebugFlashViewUpdates) { 675 static HBRUSH brush = createBrush(WebPageProxy::viewUpdatesFlashColor().rgb()).leakPtr(); 676 IntRect rect = paintStruct.rcPaint; 677 flashRects(hdc, &rect, 1, brush); 678 } 679 680 paint(hdc, paintStruct.rcPaint); 681 682 ::EndPaint(m_window, &paintStruct); 683 684 handled = true; 685 return 0; 686 } 687 688 LRESULT WebView::onPrintClientEvent(HWND hWnd, UINT, WPARAM wParam, LPARAM, bool& handled) 689 { 690 HDC hdc = reinterpret_cast<HDC>(wParam); 691 RECT winRect; 692 ::GetClientRect(hWnd, &winRect); 693 694 // Twidding the visibility flags tells the DrawingArea to resume painting. Right now, the 695 // the visible state of the view only affects whether or not painting happens, but in the 696 // future it could affect more, which we wouldn't want to touch here. 697 698 // FIXME: We should have a better way of telling the WebProcess to draw even if we're 699 // invisible than twiddling the visibility flag. 700 701 bool wasVisible = isViewVisible(); 702 if (!wasVisible) 703 setIsVisible(true); 704 705 paint(hdc, winRect); 706 707 if (!wasVisible) 708 setIsVisible(false); 709 710 handled = true; 711 return 0; 712 } 713 714 LRESULT WebView::onSizeEvent(HWND, UINT, WPARAM, LPARAM lParam, bool& handled) 715 { 716 int width = LOWORD(lParam); 717 int height = HIWORD(lParam); 718 719 if (m_page && m_page->drawingArea()) { 720 m_page->drawingArea()->setSize(IntSize(width, height), m_nextResizeScrollOffset); 721 m_nextResizeScrollOffset = IntSize(); 722 } 723 724 handled = true; 725 return 0; 726 } 727 728 LRESULT WebView::onWindowPositionChangedEvent(HWND, UINT, WPARAM, LPARAM lParam, bool& handled) 729 { 730 if (reinterpret_cast<WINDOWPOS*>(lParam)->flags & SWP_SHOWWINDOW) 731 updateActiveStateSoon(); 732 733 handled = false; 734 return 0; 735 } 736 737 LRESULT WebView::onSetFocusEvent(HWND, UINT, WPARAM, LPARAM lParam, bool& handled) 738 { 739 m_page->viewStateDidChange(WebPageProxy::ViewIsFocused); 740 handled = true; 741 return 0; 742 } 743 744 LRESULT WebView::onKillFocusEvent(HWND, UINT, WPARAM, LPARAM lParam, bool& handled) 745 { 746 m_page->viewStateDidChange(WebPageProxy::ViewIsFocused); 747 handled = true; 748 return 0; 749 } 750 751 LRESULT WebView::onTimerEvent(HWND hWnd, UINT, WPARAM wParam, LPARAM, bool& handled) 752 { 753 switch (wParam) { 754 case UpdateActiveStateTimer: 755 ::KillTimer(hWnd, UpdateActiveStateTimer); 756 updateActiveState(); 757 break; 758 } 759 760 handled = true; 761 return 0; 762 } 763 764 LRESULT WebView::onShowWindowEvent(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) 765 { 766 // lParam is 0 when the message is sent because of a ShowWindow call. 767 // FIXME: Since we don't get notified when an ancestor window is hidden or shown, we will keep 768 // painting even when we have a hidden ancestor. <http://webkit.org/b/54104> 769 if (!lParam) 770 setIsVisible(wParam); 771 772 handled = false; 773 return 0; 774 } 775 776 LRESULT WebView::onSetCursor(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) 777 { 778 if (!m_lastCursorSet) { 779 handled = false; 780 return 0; 781 } 782 783 ::SetCursor(m_lastCursorSet); 784 return 0; 785 } 786 787 void WebView::updateActiveState() 788 { 789 m_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive); 790 } 791 792 void WebView::updateActiveStateSoon() 793 { 794 // This function is called while processing the WM_NCACTIVATE message. 795 // While processing WM_NCACTIVATE when we are being deactivated, GetActiveWindow() will 796 // still return our window. If we were to call updateActiveState() in that case, we would 797 // wrongly think that we are still the active window. To work around this, we update our 798 // active state after a 0-delay timer fires, at which point GetActiveWindow() will return 799 // the newly-activated window. 800 801 ::SetTimer(m_window, UpdateActiveStateTimer, 0, 0); 802 } 803 804 static bool initCommonControls() 805 { 806 static bool haveInitialized = false; 807 if (haveInitialized) 808 return true; 809 810 INITCOMMONCONTROLSEX init; 811 init.dwSize = sizeof(init); 812 init.dwICC = ICC_TREEVIEW_CLASSES; 813 haveInitialized = !!::InitCommonControlsEx(&init); 814 return haveInitialized; 815 } 816 817 void WebView::initializeToolTipWindow() 818 { 819 if (!initCommonControls()) 820 return; 821 822 m_toolTipWindow = ::CreateWindowEx(WS_EX_TRANSPARENT, TOOLTIPS_CLASS, 0, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, 823 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 824 m_window, 0, 0, 0); 825 if (!m_toolTipWindow) 826 return; 827 828 TOOLINFO info = {0}; 829 info.cbSize = sizeof(info); 830 info.uFlags = TTF_IDISHWND | TTF_SUBCLASS; 831 info.uId = reinterpret_cast<UINT_PTR>(m_window); 832 833 ::SendMessage(m_toolTipWindow, TTM_ADDTOOL, 0, reinterpret_cast<LPARAM>(&info)); 834 ::SendMessage(m_toolTipWindow, TTM_SETMAXTIPWIDTH, 0, kMaxToolTipWidth); 835 ::SetWindowPos(m_toolTipWindow, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); 836 } 837 838 void WebView::startTrackingMouseLeave() 839 { 840 if (m_trackingMouseLeave) 841 return; 842 m_trackingMouseLeave = true; 843 844 TRACKMOUSEEVENT trackMouseEvent; 845 trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); 846 trackMouseEvent.dwFlags = TME_LEAVE; 847 trackMouseEvent.hwndTrack = m_window; 848 849 ::TrackMouseEvent(&trackMouseEvent); 850 } 851 852 void WebView::stopTrackingMouseLeave() 853 { 854 if (!m_trackingMouseLeave) 855 return; 856 m_trackingMouseLeave = false; 857 858 TRACKMOUSEEVENT trackMouseEvent; 859 trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); 860 trackMouseEvent.dwFlags = TME_LEAVE | TME_CANCEL; 861 trackMouseEvent.hwndTrack = m_window; 862 863 ::TrackMouseEvent(&trackMouseEvent); 864 } 865 866 bool WebView::shouldInitializeTrackPointHack() 867 { 868 static bool shouldCreateScrollbars; 869 static bool hasRunTrackPointCheck; 870 871 if (hasRunTrackPointCheck) 872 return shouldCreateScrollbars; 873 874 hasRunTrackPointCheck = true; 875 const wchar_t* trackPointKeys[] = { 876 L"Software\\Lenovo\\TrackPoint", 877 L"Software\\Lenovo\\UltraNav", 878 L"Software\\Alps\\Apoint\\TrackPoint", 879 L"Software\\Synaptics\\SynTPEnh\\UltraNavUSB", 880 L"Software\\Synaptics\\SynTPEnh\\UltraNavPS2" 881 }; 882 883 for (size_t i = 0; i < WTF_ARRAY_LENGTH(trackPointKeys); ++i) { 884 HKEY trackPointKey; 885 int readKeyResult = ::RegOpenKeyExW(HKEY_CURRENT_USER, trackPointKeys[i], 0, KEY_READ, &trackPointKey); 886 ::RegCloseKey(trackPointKey); 887 if (readKeyResult == ERROR_SUCCESS) { 888 shouldCreateScrollbars = true; 889 return shouldCreateScrollbars; 890 } 891 } 892 893 return shouldCreateScrollbars; 894 } 895 896 void WebView::close() 897 { 898 m_undoClient.initialize(0); 899 ::RevokeDragDrop(m_window); 900 if (m_window) { 901 // We can't check IsWindow(m_window) here, because that will return true even while 902 // we're already handling WM_DESTROY. So we check !m_isBeingDestroyed instead. 903 if (!m_isBeingDestroyed) 904 DestroyWindow(m_window); 905 // Either we just destroyed m_window, or it's in the process of being destroyed. Either 906 // way, we clear it out to make sure we don't try to use it later. 907 m_window = 0; 908 } 909 setParentWindow(0); 910 m_page->close(); 911 } 912 913 // PageClient 914 915 PassOwnPtr<DrawingAreaProxy> WebView::createDrawingAreaProxy() 916 { 917 if (useNewDrawingArea()) 918 return DrawingAreaProxyImpl::create(m_page.get()); 919 920 return ChunkedUpdateDrawingAreaProxy::create(this, m_page.get()); 921 } 922 923 void WebView::setViewNeedsDisplay(const WebCore::IntRect& rect) 924 { 925 RECT r = rect; 926 ::InvalidateRect(m_window, &r, false); 927 } 928 929 void WebView::displayView() 930 { 931 ::UpdateWindow(m_window); 932 } 933 934 void WebView::scrollView(const IntRect& scrollRect, const IntSize& scrollOffset) 935 { 936 // FIXME: Actually scroll the view contents. 937 setViewNeedsDisplay(scrollRect); 938 } 939 940 void WebView::flashBackingStoreUpdates(const Vector<IntRect>& updateRects) 941 { 942 static HBRUSH brush = createBrush(WebPageProxy::backingStoreUpdatesFlashColor().rgb()).leakPtr(); 943 HDC dc = ::GetDC(m_window); 944 flashRects(dc, updateRects.data(), updateRects.size(), brush); 945 ::ReleaseDC(m_window, dc); 946 } 947 948 WebCore::IntSize WebView::viewSize() 949 { 950 RECT clientRect; 951 GetClientRect(m_window, &clientRect); 952 953 return IntRect(clientRect).size(); 954 } 955 956 bool WebView::isViewWindowActive() 957 { 958 HWND activeWindow = ::GetActiveWindow(); 959 return (activeWindow && m_topLevelParentWindow == findTopLevelParentWindow(activeWindow)); 960 } 961 962 bool WebView::isViewFocused() 963 { 964 return ::GetFocus() == m_window; 965 } 966 967 bool WebView::isViewVisible() 968 { 969 return m_isVisible; 970 } 971 972 bool WebView::isViewInWindow() 973 { 974 return m_isInWindow; 975 } 976 977 void WebView::pageClosed() 978 { 979 } 980 981 void WebView::processDidCrash() 982 { 983 updateNativeCursor(); 984 ::InvalidateRect(m_window, 0, TRUE); 985 } 986 987 void WebView::didRelaunchProcess() 988 { 989 updateNativeCursor(); 990 ::InvalidateRect(m_window, 0, TRUE); 991 } 992 993 void WebView::toolTipChanged(const String&, const String& newToolTip) 994 { 995 if (!m_toolTipWindow) 996 return; 997 998 if (!newToolTip.isEmpty()) { 999 // This is necessary because String::charactersWithNullTermination() is not const. 1000 String toolTip = newToolTip; 1001 1002 TOOLINFO info = {0}; 1003 info.cbSize = sizeof(info); 1004 info.uFlags = TTF_IDISHWND; 1005 info.uId = reinterpret_cast<UINT_PTR>(m_window); 1006 info.lpszText = const_cast<UChar*>(toolTip.charactersWithNullTermination()); 1007 ::SendMessage(m_toolTipWindow, TTM_UPDATETIPTEXT, 0, reinterpret_cast<LPARAM>(&info)); 1008 } 1009 1010 ::SendMessage(m_toolTipWindow, TTM_ACTIVATE, !newToolTip.isEmpty(), 0); 1011 } 1012 1013 HCURSOR WebView::cursorToShow() const 1014 { 1015 if (!m_page->isValid()) 1016 return 0; 1017 1018 // We only show the override cursor if the default (arrow) cursor is showing. 1019 static HCURSOR arrowCursor = ::LoadCursor(0, IDC_ARROW); 1020 if (m_overrideCursor && m_webCoreCursor == arrowCursor) 1021 return m_overrideCursor; 1022 1023 return m_webCoreCursor; 1024 } 1025 1026 void WebView::updateNativeCursor() 1027 { 1028 m_lastCursorSet = cursorToShow(); 1029 if (!m_lastCursorSet) 1030 return; 1031 ::SetCursor(m_lastCursorSet); 1032 } 1033 1034 void WebView::setCursor(const WebCore::Cursor& cursor) 1035 { 1036 if (!cursor.platformCursor()->nativeCursor()) 1037 return; 1038 m_webCoreCursor = cursor.platformCursor()->nativeCursor(); 1039 updateNativeCursor(); 1040 } 1041 1042 void WebView::setOverrideCursor(HCURSOR overrideCursor) 1043 { 1044 m_overrideCursor = overrideCursor; 1045 updateNativeCursor(); 1046 } 1047 1048 void WebView::setInitialFocus(bool forward) 1049 { 1050 m_page->setInitialFocus(forward); 1051 } 1052 1053 void WebView::setScrollOffsetOnNextResize(const IntSize& scrollOffset) 1054 { 1055 // The next time we get a WM_SIZE message, scroll by the specified amount in onSizeEvent(). 1056 m_nextResizeScrollOffset = scrollOffset; 1057 } 1058 1059 void WebView::setViewportArguments(const WebCore::ViewportArguments&) 1060 { 1061 } 1062 1063 void WebView::registerEditCommand(PassRefPtr<WebEditCommandProxy> prpCommand, WebPageProxy::UndoOrRedo undoOrRedo) 1064 { 1065 RefPtr<WebEditCommandProxy> command = prpCommand; 1066 m_undoClient.registerEditCommand(this, command, undoOrRedo); 1067 } 1068 1069 void WebView::clearAllEditCommands() 1070 { 1071 m_undoClient.clearAllEditCommands(this); 1072 } 1073 1074 bool WebView::canUndoRedo(WebPageProxy::UndoOrRedo undoOrRedo) 1075 { 1076 return m_undoClient.canUndoRedo(this, undoOrRedo); 1077 } 1078 1079 void WebView::executeUndoRedo(WebPageProxy::UndoOrRedo undoOrRedo) 1080 { 1081 m_undoClient.executeUndoRedo(this, undoOrRedo); 1082 } 1083 1084 void WebView::reapplyEditCommand(WebEditCommandProxy* command) 1085 { 1086 if (!m_page->isValid() || !m_page->isValidEditCommand(command)) 1087 return; 1088 1089 command->reapply(); 1090 } 1091 1092 void WebView::unapplyEditCommand(WebEditCommandProxy* command) 1093 { 1094 if (!m_page->isValid() || !m_page->isValidEditCommand(command)) 1095 return; 1096 1097 command->unapply(); 1098 } 1099 1100 FloatRect WebView::convertToDeviceSpace(const FloatRect& rect) 1101 { 1102 return rect; 1103 } 1104 1105 IntRect WebView::windowToScreen(const IntRect& rect) 1106 { 1107 return rect; 1108 } 1109 1110 FloatRect WebView::convertToUserSpace(const FloatRect& rect) 1111 { 1112 return rect; 1113 } 1114 1115 HIMC WebView::getIMMContext() 1116 { 1117 return Ime::ImmGetContext(m_window); 1118 } 1119 1120 void WebView::prepareCandidateWindow(HIMC hInputContext) 1121 { 1122 IntRect caret = m_page->firstRectForCharacterInSelectedRange(0); 1123 CANDIDATEFORM form; 1124 form.dwIndex = 0; 1125 form.dwStyle = CFS_EXCLUDE; 1126 form.ptCurrentPos.x = caret.x(); 1127 form.ptCurrentPos.y = caret.maxY(); 1128 form.rcArea.top = caret.y(); 1129 form.rcArea.bottom = caret.maxY(); 1130 form.rcArea.left = caret.x(); 1131 form.rcArea.right = caret.maxX(); 1132 Ime::ImmSetCandidateWindow(hInputContext, &form); 1133 } 1134 1135 void WebView::resetIME() 1136 { 1137 HIMC hInputContext = getIMMContext(); 1138 if (!hInputContext) 1139 return; 1140 Ime::ImmNotifyIME(hInputContext, NI_COMPOSITIONSTR, CPS_CANCEL, 0); 1141 Ime::ImmReleaseContext(m_window, hInputContext); 1142 } 1143 1144 void WebView::setInputMethodState(bool enabled) 1145 { 1146 Ime::ImmAssociateContextEx(m_window, 0, enabled ? IACE_DEFAULT : 0); 1147 } 1148 1149 void WebView::compositionSelectionChanged(bool hasChanged) 1150 { 1151 if (m_page->editorState().hasComposition && !hasChanged) 1152 resetIME(); 1153 } 1154 1155 bool WebView::onIMEStartComposition() 1156 { 1157 LOG(TextInput, "onIMEStartComposition"); 1158 m_inIMEComposition++; 1159 1160 HIMC hInputContext = getIMMContext(); 1161 if (!hInputContext) 1162 return false; 1163 prepareCandidateWindow(hInputContext); 1164 Ime::ImmReleaseContext(m_window, hInputContext); 1165 return true; 1166 } 1167 1168 static bool getCompositionString(HIMC hInputContext, DWORD type, String& result) 1169 { 1170 LONG compositionLength = Ime::ImmGetCompositionStringW(hInputContext, type, 0, 0); 1171 if (compositionLength <= 0) 1172 return false; 1173 Vector<UChar> compositionBuffer(compositionLength / 2); 1174 compositionLength = Ime::ImmGetCompositionStringW(hInputContext, type, compositionBuffer.data(), compositionLength); 1175 result = String::adopt(compositionBuffer); 1176 return true; 1177 } 1178 1179 static void compositionToUnderlines(const Vector<DWORD>& clauses, const Vector<BYTE>& attributes, Vector<CompositionUnderline>& underlines) 1180 { 1181 if (clauses.isEmpty()) { 1182 underlines.clear(); 1183 return; 1184 } 1185 1186 size_t numBoundaries = clauses.size() - 1; 1187 underlines.resize(numBoundaries); 1188 for (unsigned i = 0; i < numBoundaries; ++i) { 1189 underlines[i].startOffset = clauses[i]; 1190 underlines[i].endOffset = clauses[i + 1]; 1191 BYTE attribute = attributes[clauses[i]]; 1192 underlines[i].thick = attribute == ATTR_TARGET_CONVERTED || attribute == ATTR_TARGET_NOTCONVERTED; 1193 underlines[i].color = Color::black; 1194 } 1195 } 1196 1197 #if !LOG_DISABLED 1198 #define APPEND_ARGUMENT_NAME(name) \ 1199 if (lparam & name) { \ 1200 if (needsComma) \ 1201 result += ", "; \ 1202 result += #name; \ 1203 needsComma = true; \ 1204 } 1205 1206 static String imeCompositionArgumentNames(LPARAM lparam) 1207 { 1208 String result; 1209 bool needsComma = false; 1210 1211 APPEND_ARGUMENT_NAME(GCS_COMPATTR); 1212 APPEND_ARGUMENT_NAME(GCS_COMPCLAUSE); 1213 APPEND_ARGUMENT_NAME(GCS_COMPREADSTR); 1214 APPEND_ARGUMENT_NAME(GCS_COMPREADATTR); 1215 APPEND_ARGUMENT_NAME(GCS_COMPREADCLAUSE); 1216 APPEND_ARGUMENT_NAME(GCS_COMPSTR); 1217 APPEND_ARGUMENT_NAME(GCS_CURSORPOS); 1218 APPEND_ARGUMENT_NAME(GCS_DELTASTART); 1219 APPEND_ARGUMENT_NAME(GCS_RESULTCLAUSE); 1220 APPEND_ARGUMENT_NAME(GCS_RESULTREADCLAUSE); 1221 APPEND_ARGUMENT_NAME(GCS_RESULTREADSTR); 1222 APPEND_ARGUMENT_NAME(GCS_RESULTSTR); 1223 APPEND_ARGUMENT_NAME(CS_INSERTCHAR); 1224 APPEND_ARGUMENT_NAME(CS_NOMOVECARET); 1225 1226 return result; 1227 } 1228 1229 static String imeRequestName(WPARAM wparam) 1230 { 1231 switch (wparam) { 1232 case IMR_CANDIDATEWINDOW: 1233 return "IMR_CANDIDATEWINDOW"; 1234 case IMR_COMPOSITIONFONT: 1235 return "IMR_COMPOSITIONFONT"; 1236 case IMR_COMPOSITIONWINDOW: 1237 return "IMR_COMPOSITIONWINDOW"; 1238 case IMR_CONFIRMRECONVERTSTRING: 1239 return "IMR_CONFIRMRECONVERTSTRING"; 1240 case IMR_DOCUMENTFEED: 1241 return "IMR_DOCUMENTFEED"; 1242 case IMR_QUERYCHARPOSITION: 1243 return "IMR_QUERYCHARPOSITION"; 1244 case IMR_RECONVERTSTRING: 1245 return "IMR_RECONVERTSTRING"; 1246 default: 1247 return "Unknown (" + String::number(wparam) + ")"; 1248 } 1249 } 1250 #endif 1251 1252 bool WebView::onIMEComposition(LPARAM lparam) 1253 { 1254 LOG(TextInput, "onIMEComposition %s", imeCompositionArgumentNames(lparam).latin1().data()); 1255 HIMC hInputContext = getIMMContext(); 1256 if (!hInputContext) 1257 return true; 1258 1259 if (!m_page->editorState().isContentEditable) 1260 return true; 1261 1262 prepareCandidateWindow(hInputContext); 1263 1264 if (lparam & GCS_RESULTSTR || !lparam) { 1265 String compositionString; 1266 if (!getCompositionString(hInputContext, GCS_RESULTSTR, compositionString) && lparam) 1267 return true; 1268 1269 m_page->confirmComposition(compositionString); 1270 return true; 1271 } 1272 1273 String compositionString; 1274 if (!getCompositionString(hInputContext, GCS_COMPSTR, compositionString)) 1275 return true; 1276 1277 // Composition string attributes 1278 int numAttributes = Ime::ImmGetCompositionStringW(hInputContext, GCS_COMPATTR, 0, 0); 1279 Vector<BYTE> attributes(numAttributes); 1280 Ime::ImmGetCompositionStringW(hInputContext, GCS_COMPATTR, attributes.data(), numAttributes); 1281 1282 // Get clauses 1283 int numBytes = Ime::ImmGetCompositionStringW(hInputContext, GCS_COMPCLAUSE, 0, 0); 1284 Vector<DWORD> clauses(numBytes / sizeof(DWORD)); 1285 Ime::ImmGetCompositionStringW(hInputContext, GCS_COMPCLAUSE, clauses.data(), numBytes); 1286 1287 Vector<CompositionUnderline> underlines; 1288 compositionToUnderlines(clauses, attributes, underlines); 1289 1290 int cursorPosition = LOWORD(Ime::ImmGetCompositionStringW(hInputContext, GCS_CURSORPOS, 0, 0)); 1291 1292 m_page->setComposition(compositionString, underlines, cursorPosition); 1293 1294 return true; 1295 } 1296 1297 bool WebView::onIMEEndComposition() 1298 { 1299 LOG(TextInput, "onIMEEndComposition"); 1300 // If the composition hasn't been confirmed yet, it needs to be cancelled. 1301 // This happens after deleting the last character from inline input hole. 1302 if (m_page->editorState().hasComposition) 1303 m_page->confirmComposition(String()); 1304 1305 if (m_inIMEComposition) 1306 m_inIMEComposition--; 1307 1308 return true; 1309 } 1310 1311 LRESULT WebView::onIMERequestCharPosition(IMECHARPOSITION* charPos) 1312 { 1313 if (charPos->dwCharPos && !m_page->editorState().hasComposition) 1314 return 0; 1315 IntRect caret = m_page->firstRectForCharacterInSelectedRange(charPos->dwCharPos); 1316 charPos->pt.x = caret.x(); 1317 charPos->pt.y = caret.y(); 1318 ::ClientToScreen(m_window, &charPos->pt); 1319 charPos->cLineHeight = caret.height(); 1320 ::GetWindowRect(m_window, &charPos->rcDocument); 1321 return true; 1322 } 1323 1324 LRESULT WebView::onIMERequestReconvertString(RECONVERTSTRING* reconvertString) 1325 { 1326 String text = m_page->getSelectedText(); 1327 unsigned totalSize = sizeof(RECONVERTSTRING) + text.length() * sizeof(UChar); 1328 1329 if (!reconvertString) 1330 return totalSize; 1331 1332 if (totalSize > reconvertString->dwSize) 1333 return 0; 1334 reconvertString->dwCompStrLen = text.length(); 1335 reconvertString->dwStrLen = text.length(); 1336 reconvertString->dwTargetStrLen = text.length(); 1337 reconvertString->dwStrOffset = sizeof(RECONVERTSTRING); 1338 memcpy(reconvertString + 1, text.characters(), text.length() * sizeof(UChar)); 1339 return totalSize; 1340 } 1341 1342 LRESULT WebView::onIMERequest(WPARAM request, LPARAM data) 1343 { 1344 LOG(TextInput, "onIMERequest %s", imeRequestName(request).latin1().data()); 1345 if (!m_page->editorState().isContentEditable) 1346 return 0; 1347 1348 switch (request) { 1349 case IMR_RECONVERTSTRING: 1350 return onIMERequestReconvertString(reinterpret_cast<RECONVERTSTRING*>(data)); 1351 1352 case IMR_QUERYCHARPOSITION: 1353 return onIMERequestCharPosition(reinterpret_cast<IMECHARPOSITION*>(data)); 1354 } 1355 return 0; 1356 } 1357 1358 bool WebView::onIMESelect(WPARAM wparam, LPARAM lparam) 1359 { 1360 UNUSED_PARAM(wparam); 1361 UNUSED_PARAM(lparam); 1362 LOG(TextInput, "onIMESelect locale %ld %s", lparam, wparam ? "select" : "deselect"); 1363 return false; 1364 } 1365 1366 bool WebView::onIMESetContext(WPARAM wparam, LPARAM) 1367 { 1368 LOG(TextInput, "onIMESetContext %s", wparam ? "active" : "inactive"); 1369 return false; 1370 } 1371 1372 void WebView::doneWithKeyEvent(const NativeWebKeyboardEvent& event, bool wasEventHandled) 1373 { 1374 // Calling ::DefWindowProcW will ensure that pressing the Alt key will generate a WM_SYSCOMMAND 1375 // event, e.g. See <http://webkit.org/b/47671>. 1376 if (!wasEventHandled) 1377 ::DefWindowProcW(event.nativeEvent()->hwnd, event.nativeEvent()->message, event.nativeEvent()->wParam, event.nativeEvent()->lParam); 1378 } 1379 1380 PassRefPtr<WebPopupMenuProxy> WebView::createPopupMenuProxy(WebPageProxy* page) 1381 { 1382 return WebPopupMenuProxyWin::create(this, page); 1383 } 1384 1385 PassRefPtr<WebContextMenuProxy> WebView::createContextMenuProxy(WebPageProxy* page) 1386 { 1387 return WebContextMenuProxyWin::create(m_window, page); 1388 } 1389 1390 void WebView::setFindIndicator(PassRefPtr<FindIndicator> prpFindIndicator, bool fadeOut) 1391 { 1392 if (!m_findIndicatorCallback) 1393 return; 1394 1395 HBITMAP hbmp = 0; 1396 IntRect selectionRect; 1397 1398 if (RefPtr<FindIndicator> findIndicator = prpFindIndicator) { 1399 if (ShareableBitmap* contentImage = findIndicator->contentImage()) { 1400 // Render the contentImage to an HBITMAP. 1401 void* bits; 1402 HDC hdc = ::CreateCompatibleDC(0); 1403 int width = contentImage->bounds().width(); 1404 int height = contentImage->bounds().height(); 1405 BitmapInfo bitmapInfo = BitmapInfo::create(contentImage->size()); 1406 1407 hbmp = CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, static_cast<void**>(&bits), 0, 0); 1408 HBITMAP hbmpOld = static_cast<HBITMAP>(SelectObject(hdc, hbmp)); 1409 #if USE(CG) 1410 RetainPtr<CGContextRef> context(AdoptCF, CGBitmapContextCreate(bits, width, height, 1411 8, width * sizeof(RGBQUAD), deviceRGBColorSpaceRef(), kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst)); 1412 1413 GraphicsContext graphicsContext(context.get()); 1414 contentImage->paint(graphicsContext, IntPoint(), contentImage->bounds()); 1415 #else 1416 // FIXME: Implement! 1417 #endif 1418 1419 ::SelectObject(hdc, hbmpOld); 1420 ::DeleteDC(hdc); 1421 } 1422 1423 selectionRect = IntRect(findIndicator->selectionRectInWindowCoordinates()); 1424 } 1425 1426 // The callback is responsible for calling ::DeleteObject(hbmp). 1427 (*m_findIndicatorCallback)(toAPI(this), hbmp, selectionRect, fadeOut, m_findIndicatorCallbackContext); 1428 } 1429 1430 void WebView::setFindIndicatorCallback(WKViewFindIndicatorCallback callback, void* context) 1431 { 1432 m_findIndicatorCallback = callback; 1433 m_findIndicatorCallbackContext = context; 1434 } 1435 1436 WKViewFindIndicatorCallback WebView::getFindIndicatorCallback(void** context) 1437 { 1438 if (context) 1439 *context = m_findIndicatorCallbackContext; 1440 1441 return m_findIndicatorCallback; 1442 } 1443 1444 void WebView::didCommitLoadForMainFrame(bool useCustomRepresentation) 1445 { 1446 } 1447 1448 void WebView::didFinishLoadingDataForCustomRepresentation(const String& suggestedFilename, const CoreIPC::DataReference&) 1449 { 1450 } 1451 1452 double WebView::customRepresentationZoomFactor() 1453 { 1454 return 1; 1455 } 1456 1457 void WebView::setCustomRepresentationZoomFactor(double) 1458 { 1459 } 1460 1461 void WebView::didChangeScrollbarsForMainFrame() const 1462 { 1463 } 1464 1465 void WebView::findStringInCustomRepresentation(const String&, FindOptions, unsigned) 1466 { 1467 } 1468 1469 void WebView::countStringMatchesInCustomRepresentation(const String&, FindOptions, unsigned) 1470 { 1471 } 1472 1473 void WebView::setIsInWindow(bool isInWindow) 1474 { 1475 m_isInWindow = isInWindow; 1476 m_page->viewStateDidChange(WebPageProxy::ViewIsInWindow); 1477 } 1478 1479 void WebView::setIsVisible(bool isVisible) 1480 { 1481 m_isVisible = isVisible; 1482 1483 if (m_page) 1484 m_page->viewStateDidChange(WebPageProxy::ViewIsVisible); 1485 } 1486 1487 #if USE(ACCELERATED_COMPOSITING) 1488 1489 void WebView::enterAcceleratedCompositingMode(const LayerTreeContext&) 1490 { 1491 ASSERT(useNewDrawingArea()); 1492 // FIXME: Implement. 1493 ASSERT_NOT_REACHED(); 1494 } 1495 1496 void WebView::exitAcceleratedCompositingMode() 1497 { 1498 ASSERT(useNewDrawingArea()); 1499 // FIXME: Implement. 1500 ASSERT_NOT_REACHED(); 1501 } 1502 1503 #endif // USE(ACCELERATED_COMPOSITING) 1504 1505 HWND WebView::nativeWindow() 1506 { 1507 return m_window; 1508 } 1509 1510 // WebCore::WindowMessageListener 1511 1512 void WebView::windowReceivedMessage(HWND, UINT message, WPARAM wParam, LPARAM) 1513 { 1514 switch (message) { 1515 case WM_NCACTIVATE: 1516 updateActiveStateSoon(); 1517 break; 1518 case WM_SETTINGCHANGE: 1519 // systemParameterChanged(wParam); 1520 break; 1521 } 1522 } 1523 1524 HRESULT STDMETHODCALLTYPE WebView::QueryInterface(REFIID riid, void** ppvObject) 1525 { 1526 *ppvObject = 0; 1527 if (IsEqualGUID(riid, IID_IUnknown)) 1528 *ppvObject = static_cast<IUnknown*>(this); 1529 else if (IsEqualGUID(riid, IID_IDropTarget)) 1530 *ppvObject = static_cast<IDropTarget*>(this); 1531 else 1532 return E_NOINTERFACE; 1533 1534 AddRef(); 1535 return S_OK; 1536 } 1537 1538 ULONG STDMETHODCALLTYPE WebView::AddRef(void) 1539 { 1540 ref(); 1541 return refCount(); 1542 } 1543 1544 ULONG STDMETHODCALLTYPE WebView::Release(void) 1545 { 1546 deref(); 1547 return refCount(); 1548 } 1549 1550 static DWORD dragOperationToDragCursor(DragOperation op) 1551 { 1552 DWORD res = DROPEFFECT_NONE; 1553 if (op & DragOperationCopy) 1554 res = DROPEFFECT_COPY; 1555 else if (op & DragOperationLink) 1556 res = DROPEFFECT_LINK; 1557 else if (op & DragOperationMove) 1558 res = DROPEFFECT_MOVE; 1559 else if (op & DragOperationGeneric) 1560 res = DROPEFFECT_MOVE; // This appears to be the Firefox behaviour 1561 return res; 1562 } 1563 1564 WebCore::DragOperation WebView::keyStateToDragOperation(DWORD grfKeyState) const 1565 { 1566 if (!m_page) 1567 return DragOperationNone; 1568 1569 // Conforms to Microsoft's key combinations as documented for 1570 // IDropTarget::DragOver. Note, grfKeyState is the current 1571 // state of the keyboard modifier keys on the keyboard. See: 1572 // <http://msdn.microsoft.com/en-us/library/ms680129(VS.85).aspx>. 1573 DragOperation operation = m_page->dragOperation(); 1574 1575 if ((grfKeyState & (MK_CONTROL | MK_SHIFT)) == (MK_CONTROL | MK_SHIFT)) 1576 operation = DragOperationLink; 1577 else if ((grfKeyState & MK_CONTROL) == MK_CONTROL) 1578 operation = DragOperationCopy; 1579 else if ((grfKeyState & MK_SHIFT) == MK_SHIFT) 1580 operation = DragOperationGeneric; 1581 1582 return operation; 1583 } 1584 1585 HRESULT STDMETHODCALLTYPE WebView::DragEnter(IDataObject* pDataObject, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) 1586 { 1587 m_dragData = 0; 1588 m_page->resetDragOperation(); 1589 1590 if (m_dropTargetHelper) 1591 m_dropTargetHelper->DragEnter(m_window, pDataObject, (POINT*)&pt, *pdwEffect); 1592 1593 POINTL localpt = pt; 1594 ::ScreenToClient(m_window, (LPPOINT)&localpt); 1595 DragData data(pDataObject, IntPoint(localpt.x, localpt.y), IntPoint(pt.x, pt.y), keyStateToDragOperation(grfKeyState)); 1596 m_page->dragEntered(&data); 1597 *pdwEffect = dragOperationToDragCursor(m_page->dragOperation()); 1598 1599 m_lastDropEffect = *pdwEffect; 1600 m_dragData = pDataObject; 1601 1602 return S_OK; 1603 } 1604 1605 HRESULT STDMETHODCALLTYPE WebView::DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) 1606 { 1607 if (m_dropTargetHelper) 1608 m_dropTargetHelper->DragOver((POINT*)&pt, *pdwEffect); 1609 1610 if (m_dragData) { 1611 POINTL localpt = pt; 1612 ::ScreenToClient(m_window, (LPPOINT)&localpt); 1613 DragData data(m_dragData.get(), IntPoint(localpt.x, localpt.y), IntPoint(pt.x, pt.y), keyStateToDragOperation(grfKeyState)); 1614 m_page->dragUpdated(&data); 1615 *pdwEffect = dragOperationToDragCursor(m_page->dragOperation()); 1616 } else 1617 *pdwEffect = DROPEFFECT_NONE; 1618 1619 m_lastDropEffect = *pdwEffect; 1620 return S_OK; 1621 } 1622 1623 HRESULT STDMETHODCALLTYPE WebView::DragLeave() 1624 { 1625 if (m_dropTargetHelper) 1626 m_dropTargetHelper->DragLeave(); 1627 1628 if (m_dragData) { 1629 DragData data(m_dragData.get(), IntPoint(), IntPoint(), DragOperationNone); 1630 m_page->dragExited(&data); 1631 m_dragData = 0; 1632 m_page->resetDragOperation(); 1633 } 1634 return S_OK; 1635 } 1636 1637 HRESULT STDMETHODCALLTYPE WebView::Drop(IDataObject* pDataObject, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) 1638 { 1639 if (m_dropTargetHelper) 1640 m_dropTargetHelper->Drop(pDataObject, (POINT*)&pt, *pdwEffect); 1641 1642 m_dragData = 0; 1643 *pdwEffect = m_lastDropEffect; 1644 POINTL localpt = pt; 1645 ::ScreenToClient(m_window, (LPPOINT)&localpt); 1646 DragData data(pDataObject, IntPoint(localpt.x, localpt.y), IntPoint(pt.x, pt.y), keyStateToDragOperation(grfKeyState)); 1647 1648 SandboxExtension::Handle sandboxExtensionHandle; 1649 m_page->performDrag(&data, String(), sandboxExtensionHandle); 1650 return S_OK; 1651 } 1652 1653 } // namespace WebKit 1654