| /* |
| * Copyright (C) 2010 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "WebView.h" |
| |
| #include "ChunkedUpdateDrawingAreaProxy.h" |
| #include "DrawingAreaProxyImpl.h" |
| #include "FindIndicator.h" |
| #include "Logging.h" |
| #include "NativeWebKeyboardEvent.h" |
| #include "NativeWebMouseEvent.h" |
| #include "Region.h" |
| #include "RunLoop.h" |
| #include "WKAPICast.h" |
| #include "WebContext.h" |
| #include "WebContextMenuProxyWin.h" |
| #include "WebEditCommandProxy.h" |
| #include "WebEventFactory.h" |
| #include "WebPageProxy.h" |
| #include "WebPopupMenuProxyWin.h" |
| #include <Commctrl.h> |
| #include <WebCore/BitmapInfo.h> |
| #include <WebCore/Cursor.h> |
| #include <WebCore/FloatRect.h> |
| #if USE(CG) |
| #include <WebCore/GraphicsContextCG.h> |
| #endif |
| #include <WebCore/IntRect.h> |
| #include <WebCore/SoftLinking.h> |
| #include <WebCore/WebCoreInstanceHandle.h> |
| #include <WebCore/WindowMessageBroadcaster.h> |
| #include <WebCore/WindowsTouch.h> |
| #include <wtf/text/WTFString.h> |
| |
| namespace Ime { |
| // We need these functions in a separate namespace, because in the global namespace they conflict |
| // with the definitions in imm.h only by the type modifier (the macro defines them as static) and |
| // imm.h is included by windows.h |
| SOFT_LINK_LIBRARY(IMM32) |
| SOFT_LINK(IMM32, ImmGetContext, HIMC, WINAPI, (HWND hwnd), (hwnd)) |
| SOFT_LINK(IMM32, ImmReleaseContext, BOOL, WINAPI, (HWND hWnd, HIMC hIMC), (hWnd, hIMC)) |
| SOFT_LINK(IMM32, ImmGetCompositionStringW, LONG, WINAPI, (HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen), (hIMC, dwIndex, lpBuf, dwBufLen)) |
| SOFT_LINK(IMM32, ImmSetCandidateWindow, BOOL, WINAPI, (HIMC hIMC, LPCANDIDATEFORM lpCandidate), (hIMC, lpCandidate)) |
| SOFT_LINK(IMM32, ImmSetOpenStatus, BOOL, WINAPI, (HIMC hIMC, BOOL fOpen), (hIMC, fOpen)) |
| SOFT_LINK(IMM32, ImmNotifyIME, BOOL, WINAPI, (HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue), (hIMC, dwAction, dwIndex, dwValue)) |
| SOFT_LINK(IMM32, ImmAssociateContextEx, BOOL, WINAPI, (HWND hWnd, HIMC hIMC, DWORD dwFlags), (hWnd, hIMC, dwFlags)) |
| }; |
| |
| // Soft link functions for gestures and panning. |
| SOFT_LINK_LIBRARY(USER32); |
| SOFT_LINK_OPTIONAL(USER32, GetGestureInfo, BOOL, WINAPI, (HGESTUREINFO, PGESTUREINFO)); |
| SOFT_LINK_OPTIONAL(USER32, SetGestureConfig, BOOL, WINAPI, (HWND, DWORD, UINT, PGESTURECONFIG, UINT)); |
| SOFT_LINK_OPTIONAL(USER32, CloseGestureInfoHandle, BOOL, WINAPI, (HGESTUREINFO)); |
| |
| SOFT_LINK_LIBRARY(Uxtheme); |
| SOFT_LINK_OPTIONAL(Uxtheme, BeginPanningFeedback, BOOL, WINAPI, (HWND)); |
| SOFT_LINK_OPTIONAL(Uxtheme, EndPanningFeedback, BOOL, WINAPI, (HWND, BOOL)); |
| SOFT_LINK_OPTIONAL(Uxtheme, UpdatePanningFeedback, BOOL, WINAPI, (HWND, LONG, LONG, BOOL)); |
| |
| using namespace WebCore; |
| |
| namespace WebKit { |
| |
| static const LPCWSTR kWebKit2WebViewWindowClassName = L"WebKit2WebViewWindowClass"; |
| |
| // Constants not available on all platforms. |
| const int WM_XP_THEMECHANGED = 0x031A; |
| const int WM_VISTA_MOUSEHWHEEL = 0x020E; |
| |
| static const int kMaxToolTipWidth = 250; |
| |
| enum { |
| UpdateActiveStateTimer = 1, |
| }; |
| |
| static bool useNewDrawingArea() |
| { |
| // FIXME: Remove this function and the old drawing area code once we aren't interested in |
| // testing the old drawing area anymore. |
| return true; |
| } |
| |
| LRESULT CALLBACK WebView::WebViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) |
| { |
| LONG_PTR longPtr = ::GetWindowLongPtr(hWnd, 0); |
| |
| if (WebView* webView = reinterpret_cast<WebView*>(longPtr)) |
| return webView->wndProc(hWnd, message, wParam, lParam); |
| |
| if (message == WM_CREATE) { |
| LPCREATESTRUCT createStruct = reinterpret_cast<LPCREATESTRUCT>(lParam); |
| |
| // Associate the WebView with the window. |
| ::SetWindowLongPtr(hWnd, 0, (LONG_PTR)createStruct->lpCreateParams); |
| return 0; |
| } |
| |
| return ::DefWindowProc(hWnd, message, wParam, lParam); |
| } |
| |
| LRESULT WebView::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) |
| { |
| LRESULT lResult = 0; |
| bool handled = true; |
| |
| switch (message) { |
| case WM_CLOSE: |
| m_page->tryClose(); |
| break; |
| case WM_DESTROY: |
| m_isBeingDestroyed = true; |
| close(); |
| break; |
| case WM_ERASEBKGND: |
| lResult = 1; |
| break; |
| case WM_PAINT: |
| lResult = onPaintEvent(hWnd, message, wParam, lParam, handled); |
| break; |
| case WM_PRINTCLIENT: |
| lResult = onPrintClientEvent(hWnd, message, wParam, lParam, handled); |
| break; |
| case WM_MOUSEACTIVATE: |
| setWasActivatedByMouseEvent(true); |
| handled = false; |
| break; |
| case WM_MOUSEMOVE: |
| case WM_LBUTTONDOWN: |
| case WM_MBUTTONDOWN: |
| case WM_RBUTTONDOWN: |
| case WM_LBUTTONDBLCLK: |
| case WM_MBUTTONDBLCLK: |
| case WM_RBUTTONDBLCLK: |
| case WM_LBUTTONUP: |
| case WM_MBUTTONUP: |
| case WM_RBUTTONUP: |
| case WM_MOUSELEAVE: |
| lResult = onMouseEvent(hWnd, message, wParam, lParam, handled); |
| break; |
| case WM_MOUSEWHEEL: |
| case WM_VISTA_MOUSEHWHEEL: |
| lResult = onWheelEvent(hWnd, message, wParam, lParam, handled); |
| break; |
| case WM_HSCROLL: |
| lResult = onHorizontalScroll(hWnd, message, wParam, lParam, handled); |
| break; |
| case WM_VSCROLL: |
| lResult = onVerticalScroll(hWnd, message, wParam, lParam, handled); |
| break; |
| case WM_GESTURENOTIFY: |
| lResult = onGestureNotify(hWnd, message, wParam, lParam, handled); |
| break; |
| case WM_GESTURE: |
| lResult = onGesture(hWnd, message, wParam, lParam, handled); |
| break; |
| case WM_SYSKEYDOWN: |
| case WM_KEYDOWN: |
| case WM_SYSCHAR: |
| case WM_CHAR: |
| case WM_SYSKEYUP: |
| case WM_KEYUP: |
| lResult = onKeyEvent(hWnd, message, wParam, lParam, handled); |
| break; |
| case WM_SIZE: |
| lResult = onSizeEvent(hWnd, message, wParam, lParam, handled); |
| break; |
| case WM_WINDOWPOSCHANGED: |
| lResult = onWindowPositionChangedEvent(hWnd, message, wParam, lParam, handled); |
| break; |
| case WM_SETFOCUS: |
| lResult = onSetFocusEvent(hWnd, message, wParam, lParam, handled); |
| break; |
| case WM_KILLFOCUS: |
| lResult = onKillFocusEvent(hWnd, message, wParam, lParam, handled); |
| break; |
| case WM_TIMER: |
| lResult = onTimerEvent(hWnd, message, wParam, lParam, handled); |
| break; |
| case WM_SHOWWINDOW: |
| lResult = onShowWindowEvent(hWnd, message, wParam, lParam, handled); |
| break; |
| case WM_SETCURSOR: |
| lResult = onSetCursor(hWnd, message, wParam, lParam, handled); |
| break; |
| case WM_IME_STARTCOMPOSITION: |
| handled = onIMEStartComposition(); |
| break; |
| case WM_IME_REQUEST: |
| lResult = onIMERequest(wParam, lParam); |
| break; |
| case WM_IME_COMPOSITION: |
| handled = onIMEComposition(lParam); |
| break; |
| case WM_IME_ENDCOMPOSITION: |
| handled = onIMEEndComposition(); |
| break; |
| case WM_IME_SELECT: |
| handled = onIMESelect(wParam, lParam); |
| break; |
| case WM_IME_SETCONTEXT: |
| handled = onIMESetContext(wParam, lParam); |
| break; |
| default: |
| handled = false; |
| break; |
| } |
| |
| if (!handled) |
| lResult = ::DefWindowProc(hWnd, message, wParam, lParam); |
| |
| return lResult; |
| } |
| |
| bool WebView::registerWebViewWindowClass() |
| { |
| static bool haveRegisteredWindowClass = false; |
| if (haveRegisteredWindowClass) |
| return true; |
| haveRegisteredWindowClass = true; |
| |
| WNDCLASSEX wcex; |
| |
| wcex.cbSize = sizeof(WNDCLASSEX); |
| wcex.style = CS_DBLCLKS; |
| wcex.lpfnWndProc = WebView::WebViewWndProc; |
| wcex.cbClsExtra = 0; |
| wcex.cbWndExtra = sizeof(WebView*); |
| wcex.hInstance = instanceHandle(); |
| wcex.hIcon = 0; |
| wcex.hCursor = ::LoadCursor(0, IDC_ARROW); |
| wcex.hbrBackground = 0; |
| wcex.lpszMenuName = 0; |
| wcex.lpszClassName = kWebKit2WebViewWindowClassName; |
| wcex.hIconSm = 0; |
| |
| return !!::RegisterClassEx(&wcex); |
| } |
| |
| WebView::WebView(RECT rect, WebContext* context, WebPageGroup* pageGroup, HWND parentWindow) |
| : m_topLevelParentWindow(0) |
| , m_toolTipWindow(0) |
| , m_lastCursorSet(0) |
| , m_webCoreCursor(0) |
| , m_overrideCursor(0) |
| , m_trackingMouseLeave(false) |
| , m_isInWindow(false) |
| , m_isVisible(false) |
| , m_wasActivatedByMouseEvent(false) |
| , m_isBeingDestroyed(false) |
| , m_inIMEComposition(0) |
| , m_findIndicatorCallback(0) |
| , m_findIndicatorCallbackContext(0) |
| , m_lastPanX(0) |
| , m_lastPanY(0) |
| , m_overPanY(0) |
| , m_gestureReachedScrollingLimit(false) |
| { |
| registerWebViewWindowClass(); |
| |
| m_window = ::CreateWindowExW(0, kWebKit2WebViewWindowClassName, 0, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE, |
| rect.top, rect.left, rect.right - rect.left, rect.bottom - rect.top, parentWindow ? parentWindow : HWND_MESSAGE, 0, instanceHandle(), this); |
| ASSERT(::IsWindow(m_window)); |
| // We only check our window style, and not ::IsWindowVisible, because m_isVisible only tracks |
| // this window's visibility status, while ::IsWindowVisible takes our ancestors' visibility |
| // status into account. <http://webkit.org/b/54104> |
| ASSERT(m_isVisible == static_cast<bool>(::GetWindowLong(m_window, GWL_STYLE) & WS_VISIBLE)); |
| |
| m_page = context->createWebPage(this, pageGroup); |
| m_page->initializeWebPage(); |
| |
| CoCreateInstance(CLSID_DragDropHelper, 0, CLSCTX_INPROC_SERVER, IID_IDropTargetHelper, (void**)&m_dropTargetHelper); |
| |
| // FIXME: Initializing the tooltip window here matches WebKit win, but seems like something |
| // we could do on demand to save resources. |
| initializeToolTipWindow(); |
| |
| // Initialize the top level parent window and register it with the WindowMessageBroadcaster. |
| windowAncestryDidChange(); |
| } |
| |
| WebView::~WebView() |
| { |
| // Tooltip window needs to be explicitly destroyed since it isn't a WS_CHILD. |
| if (::IsWindow(m_toolTipWindow)) |
| ::DestroyWindow(m_toolTipWindow); |
| } |
| |
| void WebView::initialize() |
| { |
| ::RegisterDragDrop(m_window, this); |
| |
| if (shouldInitializeTrackPointHack()) { |
| // If we detected a registry key belonging to a TrackPoint driver, then create fake |
| // scrollbars, so the WebView will receive WM_VSCROLL and WM_HSCROLL messages. |
| // We create an invisible vertical scrollbar and an invisible horizontal scrollbar to allow |
| // for receiving both types of messages. |
| ::CreateWindow(TEXT("SCROLLBAR"), TEXT("FAKETRACKPOINTHSCROLLBAR"), WS_CHILD | WS_VISIBLE | SBS_HORZ, 0, 0, 0, 0, m_window, 0, instanceHandle(), 0); |
| ::CreateWindow(TEXT("SCROLLBAR"), TEXT("FAKETRACKPOINTVSCROLLBAR"), WS_CHILD | WS_VISIBLE | SBS_VERT, 0, 0, 0, 0, m_window, 0, instanceHandle(), 0); |
| } |
| } |
| |
| void WebView::initializeUndoClient(const WKViewUndoClient* client) |
| { |
| m_undoClient.initialize(client); |
| } |
| |
| void WebView::setParentWindow(HWND parentWindow) |
| { |
| if (m_window) { |
| // If the host window hasn't changed, bail. |
| if (::GetParent(m_window) == parentWindow) |
| return; |
| if (parentWindow) |
| ::SetParent(m_window, parentWindow); |
| else if (!m_isBeingDestroyed) { |
| // Turn the WebView into a message-only window so it will no longer be a child of the |
| // old parent window and will be hidden from screen. We only do this when |
| // isBeingDestroyed() is false because doing this while handling WM_DESTROY can leave |
| // m_window in a weird state (see <http://webkit.org/b/29337>). |
| ::SetParent(m_window, HWND_MESSAGE); |
| } |
| } |
| |
| windowAncestryDidChange(); |
| } |
| |
| static HWND findTopLevelParentWindow(HWND window) |
| { |
| if (!window) |
| return 0; |
| |
| HWND current = window; |
| for (HWND parent = GetParent(current); current; current = parent, parent = GetParent(parent)) { |
| if (!parent || !(GetWindowLongPtr(current, GWL_STYLE) & (WS_POPUP | WS_CHILD))) |
| return current; |
| } |
| ASSERT_NOT_REACHED(); |
| return 0; |
| } |
| |
| void WebView::windowAncestryDidChange() |
| { |
| HWND newTopLevelParentWindow; |
| if (m_window) |
| newTopLevelParentWindow = findTopLevelParentWindow(m_window); |
| else { |
| // There's no point in tracking active state changes of our parent window if we don't have |
| // a window ourselves. |
| newTopLevelParentWindow = 0; |
| } |
| |
| if (newTopLevelParentWindow == m_topLevelParentWindow) |
| return; |
| |
| if (m_topLevelParentWindow) |
| WindowMessageBroadcaster::removeListener(m_topLevelParentWindow, this); |
| |
| m_topLevelParentWindow = newTopLevelParentWindow; |
| |
| if (m_topLevelParentWindow) |
| WindowMessageBroadcaster::addListener(m_topLevelParentWindow, this); |
| |
| updateActiveState(); |
| } |
| |
| LRESULT WebView::onMouseEvent(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) |
| { |
| NativeWebMouseEvent mouseEvent = NativeWebMouseEvent(hWnd, message, wParam, lParam, m_wasActivatedByMouseEvent); |
| setWasActivatedByMouseEvent(false); |
| |
| switch (message) { |
| case WM_LBUTTONDOWN: |
| case WM_MBUTTONDOWN: |
| case WM_RBUTTONDOWN: |
| ::SetFocus(m_window); |
| ::SetCapture(m_window); |
| break; |
| case WM_LBUTTONUP: |
| case WM_MBUTTONUP: |
| case WM_RBUTTONUP: |
| ::ReleaseCapture(); |
| break; |
| case WM_MOUSEMOVE: |
| startTrackingMouseLeave(); |
| break; |
| case WM_MOUSELEAVE: |
| stopTrackingMouseLeave(); |
| break; |
| case WM_LBUTTONDBLCLK: |
| case WM_MBUTTONDBLCLK: |
| case WM_RBUTTONDBLCLK: |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| |
| m_page->handleMouseEvent(mouseEvent); |
| |
| handled = true; |
| return 0; |
| } |
| |
| LRESULT WebView::onWheelEvent(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) |
| { |
| WebWheelEvent wheelEvent = WebEventFactory::createWebWheelEvent(hWnd, message, wParam, lParam); |
| if (wheelEvent.controlKey()) { |
| // We do not want WebKit to handle Control + Wheel, this should be handled by the client application |
| // to zoom the page. |
| handled = false; |
| return 0; |
| } |
| |
| m_page->handleWheelEvent(wheelEvent); |
| |
| handled = true; |
| return 0; |
| } |
| |
| LRESULT WebView::onHorizontalScroll(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) |
| { |
| ScrollDirection direction; |
| ScrollGranularity granularity; |
| switch (LOWORD(wParam)) { |
| case SB_LINELEFT: |
| granularity = ScrollByLine; |
| direction = ScrollLeft; |
| break; |
| case SB_LINERIGHT: |
| granularity = ScrollByLine; |
| direction = ScrollRight; |
| break; |
| case SB_PAGELEFT: |
| granularity = ScrollByDocument; |
| direction = ScrollLeft; |
| break; |
| case SB_PAGERIGHT: |
| granularity = ScrollByDocument; |
| direction = ScrollRight; |
| break; |
| default: |
| handled = false; |
| return 0; |
| } |
| |
| m_page->scrollBy(direction, granularity); |
| |
| handled = true; |
| return 0; |
| } |
| |
| LRESULT WebView::onVerticalScroll(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) |
| { |
| ScrollDirection direction; |
| ScrollGranularity granularity; |
| switch (LOWORD(wParam)) { |
| case SB_LINEDOWN: |
| granularity = ScrollByLine; |
| direction = ScrollDown; |
| break; |
| case SB_LINEUP: |
| granularity = ScrollByLine; |
| direction = ScrollUp; |
| break; |
| case SB_PAGEDOWN: |
| granularity = ScrollByDocument; |
| direction = ScrollDown; |
| break; |
| case SB_PAGEUP: |
| granularity = ScrollByDocument; |
| direction = ScrollUp; |
| break; |
| default: |
| handled = false; |
| return 0; |
| } |
| |
| m_page->scrollBy(direction, granularity); |
| |
| handled = true; |
| return 0; |
| } |
| |
| LRESULT WebView::onGestureNotify(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) |
| { |
| // We shouldn't be getting any gesture messages without SetGestureConfig soft-linking correctly. |
| ASSERT(SetGestureConfigPtr()); |
| |
| GESTURENOTIFYSTRUCT* gn = reinterpret_cast<GESTURENOTIFYSTRUCT*>(lParam); |
| |
| POINT localPoint = { gn->ptsLocation.x, gn->ptsLocation.y }; |
| ::ScreenToClient(m_window, &localPoint); |
| |
| bool canPan = m_page->gestureWillBegin(localPoint); |
| |
| DWORD dwPanWant = GC_PAN | GC_PAN_WITH_INERTIA | GC_PAN_WITH_GUTTER; |
| DWORD dwPanBlock = GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY; |
| if (canPan) |
| dwPanWant |= GC_PAN_WITH_SINGLE_FINGER_VERTICALLY; |
| else |
| dwPanBlock |= GC_PAN_WITH_SINGLE_FINGER_VERTICALLY; |
| |
| GESTURECONFIG gc = { GID_PAN, dwPanWant, dwPanBlock }; |
| return SetGestureConfigPtr()(m_window, 0, 1, &gc, sizeof(gc)); |
| } |
| |
| LRESULT WebView::onGesture(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) |
| { |
| ASSERT(GetGestureInfoPtr()); |
| ASSERT(CloseGestureInfoHandlePtr()); |
| ASSERT(UpdatePanningFeedbackPtr()); |
| ASSERT(BeginPanningFeedbackPtr()); |
| ASSERT(EndPanningFeedbackPtr()); |
| |
| if (!GetGestureInfoPtr() || !CloseGestureInfoHandlePtr() || !UpdatePanningFeedbackPtr() || !BeginPanningFeedbackPtr() || !EndPanningFeedbackPtr()) { |
| handled = false; |
| return 0; |
| } |
| |
| HGESTUREINFO gestureHandle = reinterpret_cast<HGESTUREINFO>(lParam); |
| GESTUREINFO gi = {0}; |
| gi.cbSize = sizeof(GESTUREINFO); |
| |
| if (!GetGestureInfoPtr()(gestureHandle, &gi)) { |
| handled = false; |
| return 0; |
| } |
| |
| switch (gi.dwID) { |
| case GID_BEGIN: |
| m_lastPanX = gi.ptsLocation.x; |
| m_lastPanY = gi.ptsLocation.y; |
| break; |
| case GID_END: |
| m_page->gestureDidEnd(); |
| break; |
| case GID_PAN: { |
| int currentX = gi.ptsLocation.x; |
| int currentY = gi.ptsLocation.y; |
| |
| // Reverse the calculations because moving your fingers up should move the screen down, and |
| // vice-versa. |
| int deltaX = m_lastPanX - currentX; |
| int deltaY = m_lastPanY - currentY; |
| |
| m_lastPanX = currentX; |
| m_lastPanY = currentY; |
| |
| // Calculate the overpan for window bounce. |
| m_overPanY -= deltaY; |
| |
| if (deltaX || deltaY) |
| m_page->gestureDidScroll(IntSize(deltaX, deltaY)); |
| |
| if (gi.dwFlags & GF_BEGIN) { |
| BeginPanningFeedbackPtr()(m_window); |
| m_gestureReachedScrollingLimit = false; |
| m_overPanY = 0; |
| } else if (gi.dwFlags & GF_END) { |
| EndPanningFeedbackPtr()(m_window, true); |
| m_overPanY = 0; |
| } |
| |
| // FIXME: Support horizontal window bounce - <http://webkit.org/b/58068>. |
| // FIXME: Window Bounce doesn't undo until user releases their finger - <http://webkit.org/b/58069>. |
| |
| if (m_gestureReachedScrollingLimit) |
| UpdatePanningFeedbackPtr()(m_window, 0, m_overPanY, gi.dwFlags & GF_INERTIA); |
| |
| CloseGestureInfoHandlePtr()(gestureHandle); |
| |
| handled = true; |
| return 0; |
| } |
| default: |
| break; |
| } |
| |
| // If we get to this point, the gesture has not been handled. We forward |
| // the call to DefWindowProc by returning false, and we don't need to |
| // to call CloseGestureInfoHandle. |
| // http://msdn.microsoft.com/en-us/library/dd353228(VS.85).aspx |
| handled = false; |
| return 0; |
| } |
| |
| LRESULT WebView::onKeyEvent(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) |
| { |
| m_page->handleKeyboardEvent(NativeWebKeyboardEvent(hWnd, message, wParam, lParam)); |
| |
| // We claim here to always have handled the event. If the event is not in fact handled, we will |
| // find out later in didNotHandleKeyEvent. |
| handled = true; |
| return 0; |
| } |
| |
| static void drawPageBackground(HDC dc, const RECT& rect) |
| { |
| // Mac checks WebPageProxy::drawsBackground and |
| // WebPageProxy::drawsTransparentBackground here, but those are always false on |
| // Windows currently (see <http://webkit.org/b/52009>). |
| ::FillRect(dc, &rect, reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1)); |
| } |
| |
| void WebView::paint(HDC hdc, const IntRect& dirtyRect) |
| { |
| m_page->endPrinting(); |
| if (useNewDrawingArea()) { |
| if (DrawingAreaProxyImpl* drawingArea = static_cast<DrawingAreaProxyImpl*>(m_page->drawingArea())) { |
| // FIXME: We should port WebKit1's rect coalescing logic here. |
| Region unpaintedRegion; |
| drawingArea->paint(hdc, dirtyRect, unpaintedRegion); |
| |
| Vector<IntRect> unpaintedRects = unpaintedRegion.rects(); |
| for (size_t i = 0; i < unpaintedRects.size(); ++i) { |
| RECT winRect = unpaintedRects[i]; |
| drawPageBackground(hdc, unpaintedRects[i]); |
| } |
| } else |
| drawPageBackground(hdc, dirtyRect); |
| |
| m_page->didDraw(); |
| } else { |
| if (m_page->isValid() && m_page->drawingArea() && m_page->drawingArea()->paint(dirtyRect, hdc)) |
| m_page->didDraw(); |
| else |
| drawPageBackground(hdc, dirtyRect); |
| } |
| } |
| |
| static void flashRects(HDC dc, const IntRect rects[], size_t rectCount, HBRUSH brush) |
| { |
| for (size_t i = 0; i < rectCount; ++i) { |
| RECT winRect = rects[i]; |
| ::FillRect(dc, &winRect, brush); |
| } |
| |
| ::GdiFlush(); |
| ::Sleep(50); |
| } |
| |
| static OwnPtr<HBRUSH> createBrush(const Color& color) |
| { |
| return adoptPtr(::CreateSolidBrush(RGB(color.red(), color.green(), color.blue()))); |
| } |
| |
| LRESULT WebView::onPaintEvent(HWND hWnd, UINT message, WPARAM, LPARAM, bool& handled) |
| { |
| PAINTSTRUCT paintStruct; |
| HDC hdc = ::BeginPaint(m_window, &paintStruct); |
| |
| if (WebPageProxy::debugPaintFlags() & kWKDebugFlashViewUpdates) { |
| static HBRUSH brush = createBrush(WebPageProxy::viewUpdatesFlashColor().rgb()).leakPtr(); |
| IntRect rect = paintStruct.rcPaint; |
| flashRects(hdc, &rect, 1, brush); |
| } |
| |
| paint(hdc, paintStruct.rcPaint); |
| |
| ::EndPaint(m_window, &paintStruct); |
| |
| handled = true; |
| return 0; |
| } |
| |
| LRESULT WebView::onPrintClientEvent(HWND hWnd, UINT, WPARAM wParam, LPARAM, bool& handled) |
| { |
| HDC hdc = reinterpret_cast<HDC>(wParam); |
| RECT winRect; |
| ::GetClientRect(hWnd, &winRect); |
| |
| // Twidding the visibility flags tells the DrawingArea to resume painting. Right now, the |
| // the visible state of the view only affects whether or not painting happens, but in the |
| // future it could affect more, which we wouldn't want to touch here. |
| |
| // FIXME: We should have a better way of telling the WebProcess to draw even if we're |
| // invisible than twiddling the visibility flag. |
| |
| bool wasVisible = isViewVisible(); |
| if (!wasVisible) |
| setIsVisible(true); |
| |
| paint(hdc, winRect); |
| |
| if (!wasVisible) |
| setIsVisible(false); |
| |
| handled = true; |
| return 0; |
| } |
| |
| LRESULT WebView::onSizeEvent(HWND, UINT, WPARAM, LPARAM lParam, bool& handled) |
| { |
| int width = LOWORD(lParam); |
| int height = HIWORD(lParam); |
| |
| if (m_page && m_page->drawingArea()) { |
| m_page->drawingArea()->setSize(IntSize(width, height), m_nextResizeScrollOffset); |
| m_nextResizeScrollOffset = IntSize(); |
| } |
| |
| handled = true; |
| return 0; |
| } |
| |
| LRESULT WebView::onWindowPositionChangedEvent(HWND, UINT, WPARAM, LPARAM lParam, bool& handled) |
| { |
| if (reinterpret_cast<WINDOWPOS*>(lParam)->flags & SWP_SHOWWINDOW) |
| updateActiveStateSoon(); |
| |
| handled = false; |
| return 0; |
| } |
| |
| LRESULT WebView::onSetFocusEvent(HWND, UINT, WPARAM, LPARAM lParam, bool& handled) |
| { |
| m_page->viewStateDidChange(WebPageProxy::ViewIsFocused); |
| handled = true; |
| return 0; |
| } |
| |
| LRESULT WebView::onKillFocusEvent(HWND, UINT, WPARAM, LPARAM lParam, bool& handled) |
| { |
| m_page->viewStateDidChange(WebPageProxy::ViewIsFocused); |
| handled = true; |
| return 0; |
| } |
| |
| LRESULT WebView::onTimerEvent(HWND hWnd, UINT, WPARAM wParam, LPARAM, bool& handled) |
| { |
| switch (wParam) { |
| case UpdateActiveStateTimer: |
| ::KillTimer(hWnd, UpdateActiveStateTimer); |
| updateActiveState(); |
| break; |
| } |
| |
| handled = true; |
| return 0; |
| } |
| |
| LRESULT WebView::onShowWindowEvent(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) |
| { |
| // lParam is 0 when the message is sent because of a ShowWindow call. |
| // FIXME: Since we don't get notified when an ancestor window is hidden or shown, we will keep |
| // painting even when we have a hidden ancestor. <http://webkit.org/b/54104> |
| if (!lParam) |
| setIsVisible(wParam); |
| |
| handled = false; |
| return 0; |
| } |
| |
| LRESULT WebView::onSetCursor(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) |
| { |
| if (!m_lastCursorSet) { |
| handled = false; |
| return 0; |
| } |
| |
| ::SetCursor(m_lastCursorSet); |
| return 0; |
| } |
| |
| void WebView::updateActiveState() |
| { |
| m_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive); |
| } |
| |
| void WebView::updateActiveStateSoon() |
| { |
| // This function is called while processing the WM_NCACTIVATE message. |
| // While processing WM_NCACTIVATE when we are being deactivated, GetActiveWindow() will |
| // still return our window. If we were to call updateActiveState() in that case, we would |
| // wrongly think that we are still the active window. To work around this, we update our |
| // active state after a 0-delay timer fires, at which point GetActiveWindow() will return |
| // the newly-activated window. |
| |
| ::SetTimer(m_window, UpdateActiveStateTimer, 0, 0); |
| } |
| |
| static bool initCommonControls() |
| { |
| static bool haveInitialized = false; |
| if (haveInitialized) |
| return true; |
| |
| INITCOMMONCONTROLSEX init; |
| init.dwSize = sizeof(init); |
| init.dwICC = ICC_TREEVIEW_CLASSES; |
| haveInitialized = !!::InitCommonControlsEx(&init); |
| return haveInitialized; |
| } |
| |
| void WebView::initializeToolTipWindow() |
| { |
| if (!initCommonControls()) |
| return; |
| |
| m_toolTipWindow = ::CreateWindowEx(WS_EX_TRANSPARENT, TOOLTIPS_CLASS, 0, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, |
| CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, |
| m_window, 0, 0, 0); |
| if (!m_toolTipWindow) |
| return; |
| |
| TOOLINFO info = {0}; |
| info.cbSize = sizeof(info); |
| info.uFlags = TTF_IDISHWND | TTF_SUBCLASS; |
| info.uId = reinterpret_cast<UINT_PTR>(m_window); |
| |
| ::SendMessage(m_toolTipWindow, TTM_ADDTOOL, 0, reinterpret_cast<LPARAM>(&info)); |
| ::SendMessage(m_toolTipWindow, TTM_SETMAXTIPWIDTH, 0, kMaxToolTipWidth); |
| ::SetWindowPos(m_toolTipWindow, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); |
| } |
| |
| void WebView::startTrackingMouseLeave() |
| { |
| if (m_trackingMouseLeave) |
| return; |
| m_trackingMouseLeave = true; |
| |
| TRACKMOUSEEVENT trackMouseEvent; |
| trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); |
| trackMouseEvent.dwFlags = TME_LEAVE; |
| trackMouseEvent.hwndTrack = m_window; |
| |
| ::TrackMouseEvent(&trackMouseEvent); |
| } |
| |
| void WebView::stopTrackingMouseLeave() |
| { |
| if (!m_trackingMouseLeave) |
| return; |
| m_trackingMouseLeave = false; |
| |
| TRACKMOUSEEVENT trackMouseEvent; |
| trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT); |
| trackMouseEvent.dwFlags = TME_LEAVE | TME_CANCEL; |
| trackMouseEvent.hwndTrack = m_window; |
| |
| ::TrackMouseEvent(&trackMouseEvent); |
| } |
| |
| bool WebView::shouldInitializeTrackPointHack() |
| { |
| static bool shouldCreateScrollbars; |
| static bool hasRunTrackPointCheck; |
| |
| if (hasRunTrackPointCheck) |
| return shouldCreateScrollbars; |
| |
| hasRunTrackPointCheck = true; |
| const wchar_t* trackPointKeys[] = { |
| L"Software\\Lenovo\\TrackPoint", |
| L"Software\\Lenovo\\UltraNav", |
| L"Software\\Alps\\Apoint\\TrackPoint", |
| L"Software\\Synaptics\\SynTPEnh\\UltraNavUSB", |
| L"Software\\Synaptics\\SynTPEnh\\UltraNavPS2" |
| }; |
| |
| for (size_t i = 0; i < WTF_ARRAY_LENGTH(trackPointKeys); ++i) { |
| HKEY trackPointKey; |
| int readKeyResult = ::RegOpenKeyExW(HKEY_CURRENT_USER, trackPointKeys[i], 0, KEY_READ, &trackPointKey); |
| ::RegCloseKey(trackPointKey); |
| if (readKeyResult == ERROR_SUCCESS) { |
| shouldCreateScrollbars = true; |
| return shouldCreateScrollbars; |
| } |
| } |
| |
| return shouldCreateScrollbars; |
| } |
| |
| void WebView::close() |
| { |
| m_undoClient.initialize(0); |
| ::RevokeDragDrop(m_window); |
| if (m_window) { |
| // We can't check IsWindow(m_window) here, because that will return true even while |
| // we're already handling WM_DESTROY. So we check !m_isBeingDestroyed instead. |
| if (!m_isBeingDestroyed) |
| DestroyWindow(m_window); |
| // Either we just destroyed m_window, or it's in the process of being destroyed. Either |
| // way, we clear it out to make sure we don't try to use it later. |
| m_window = 0; |
| } |
| setParentWindow(0); |
| m_page->close(); |
| } |
| |
| // PageClient |
| |
| PassOwnPtr<DrawingAreaProxy> WebView::createDrawingAreaProxy() |
| { |
| if (useNewDrawingArea()) |
| return DrawingAreaProxyImpl::create(m_page.get()); |
| |
| return ChunkedUpdateDrawingAreaProxy::create(this, m_page.get()); |
| } |
| |
| void WebView::setViewNeedsDisplay(const WebCore::IntRect& rect) |
| { |
| RECT r = rect; |
| ::InvalidateRect(m_window, &r, false); |
| } |
| |
| void WebView::displayView() |
| { |
| ::UpdateWindow(m_window); |
| } |
| |
| void WebView::scrollView(const IntRect& scrollRect, const IntSize& scrollOffset) |
| { |
| // FIXME: Actually scroll the view contents. |
| setViewNeedsDisplay(scrollRect); |
| } |
| |
| void WebView::flashBackingStoreUpdates(const Vector<IntRect>& updateRects) |
| { |
| static HBRUSH brush = createBrush(WebPageProxy::backingStoreUpdatesFlashColor().rgb()).leakPtr(); |
| HDC dc = ::GetDC(m_window); |
| flashRects(dc, updateRects.data(), updateRects.size(), brush); |
| ::ReleaseDC(m_window, dc); |
| } |
| |
| WebCore::IntSize WebView::viewSize() |
| { |
| RECT clientRect; |
| GetClientRect(m_window, &clientRect); |
| |
| return IntRect(clientRect).size(); |
| } |
| |
| bool WebView::isViewWindowActive() |
| { |
| HWND activeWindow = ::GetActiveWindow(); |
| return (activeWindow && m_topLevelParentWindow == findTopLevelParentWindow(activeWindow)); |
| } |
| |
| bool WebView::isViewFocused() |
| { |
| return ::GetFocus() == m_window; |
| } |
| |
| bool WebView::isViewVisible() |
| { |
| return m_isVisible; |
| } |
| |
| bool WebView::isViewInWindow() |
| { |
| return m_isInWindow; |
| } |
| |
| void WebView::pageClosed() |
| { |
| } |
| |
| void WebView::processDidCrash() |
| { |
| updateNativeCursor(); |
| ::InvalidateRect(m_window, 0, TRUE); |
| } |
| |
| void WebView::didRelaunchProcess() |
| { |
| updateNativeCursor(); |
| ::InvalidateRect(m_window, 0, TRUE); |
| } |
| |
| void WebView::toolTipChanged(const String&, const String& newToolTip) |
| { |
| if (!m_toolTipWindow) |
| return; |
| |
| if (!newToolTip.isEmpty()) { |
| // This is necessary because String::charactersWithNullTermination() is not const. |
| String toolTip = newToolTip; |
| |
| TOOLINFO info = {0}; |
| info.cbSize = sizeof(info); |
| info.uFlags = TTF_IDISHWND; |
| info.uId = reinterpret_cast<UINT_PTR>(m_window); |
| info.lpszText = const_cast<UChar*>(toolTip.charactersWithNullTermination()); |
| ::SendMessage(m_toolTipWindow, TTM_UPDATETIPTEXT, 0, reinterpret_cast<LPARAM>(&info)); |
| } |
| |
| ::SendMessage(m_toolTipWindow, TTM_ACTIVATE, !newToolTip.isEmpty(), 0); |
| } |
| |
| HCURSOR WebView::cursorToShow() const |
| { |
| if (!m_page->isValid()) |
| return 0; |
| |
| // We only show the override cursor if the default (arrow) cursor is showing. |
| static HCURSOR arrowCursor = ::LoadCursor(0, IDC_ARROW); |
| if (m_overrideCursor && m_webCoreCursor == arrowCursor) |
| return m_overrideCursor; |
| |
| return m_webCoreCursor; |
| } |
| |
| void WebView::updateNativeCursor() |
| { |
| m_lastCursorSet = cursorToShow(); |
| if (!m_lastCursorSet) |
| return; |
| ::SetCursor(m_lastCursorSet); |
| } |
| |
| void WebView::setCursor(const WebCore::Cursor& cursor) |
| { |
| if (!cursor.platformCursor()->nativeCursor()) |
| return; |
| m_webCoreCursor = cursor.platformCursor()->nativeCursor(); |
| updateNativeCursor(); |
| } |
| |
| void WebView::setOverrideCursor(HCURSOR overrideCursor) |
| { |
| m_overrideCursor = overrideCursor; |
| updateNativeCursor(); |
| } |
| |
| void WebView::setInitialFocus(bool forward) |
| { |
| m_page->setInitialFocus(forward); |
| } |
| |
| void WebView::setScrollOffsetOnNextResize(const IntSize& scrollOffset) |
| { |
| // The next time we get a WM_SIZE message, scroll by the specified amount in onSizeEvent(). |
| m_nextResizeScrollOffset = scrollOffset; |
| } |
| |
| void WebView::setViewportArguments(const WebCore::ViewportArguments&) |
| { |
| } |
| |
| void WebView::registerEditCommand(PassRefPtr<WebEditCommandProxy> prpCommand, WebPageProxy::UndoOrRedo undoOrRedo) |
| { |
| RefPtr<WebEditCommandProxy> command = prpCommand; |
| m_undoClient.registerEditCommand(this, command, undoOrRedo); |
| } |
| |
| void WebView::clearAllEditCommands() |
| { |
| m_undoClient.clearAllEditCommands(this); |
| } |
| |
| bool WebView::canUndoRedo(WebPageProxy::UndoOrRedo undoOrRedo) |
| { |
| return m_undoClient.canUndoRedo(this, undoOrRedo); |
| } |
| |
| void WebView::executeUndoRedo(WebPageProxy::UndoOrRedo undoOrRedo) |
| { |
| m_undoClient.executeUndoRedo(this, undoOrRedo); |
| } |
| |
| void WebView::reapplyEditCommand(WebEditCommandProxy* command) |
| { |
| if (!m_page->isValid() || !m_page->isValidEditCommand(command)) |
| return; |
| |
| command->reapply(); |
| } |
| |
| void WebView::unapplyEditCommand(WebEditCommandProxy* command) |
| { |
| if (!m_page->isValid() || !m_page->isValidEditCommand(command)) |
| return; |
| |
| command->unapply(); |
| } |
| |
| FloatRect WebView::convertToDeviceSpace(const FloatRect& rect) |
| { |
| return rect; |
| } |
| |
| IntRect WebView::windowToScreen(const IntRect& rect) |
| { |
| return rect; |
| } |
| |
| FloatRect WebView::convertToUserSpace(const FloatRect& rect) |
| { |
| return rect; |
| } |
| |
| HIMC WebView::getIMMContext() |
| { |
| return Ime::ImmGetContext(m_window); |
| } |
| |
| void WebView::prepareCandidateWindow(HIMC hInputContext) |
| { |
| IntRect caret = m_page->firstRectForCharacterInSelectedRange(0); |
| CANDIDATEFORM form; |
| form.dwIndex = 0; |
| form.dwStyle = CFS_EXCLUDE; |
| form.ptCurrentPos.x = caret.x(); |
| form.ptCurrentPos.y = caret.maxY(); |
| form.rcArea.top = caret.y(); |
| form.rcArea.bottom = caret.maxY(); |
| form.rcArea.left = caret.x(); |
| form.rcArea.right = caret.maxX(); |
| Ime::ImmSetCandidateWindow(hInputContext, &form); |
| } |
| |
| void WebView::resetIME() |
| { |
| HIMC hInputContext = getIMMContext(); |
| if (!hInputContext) |
| return; |
| Ime::ImmNotifyIME(hInputContext, NI_COMPOSITIONSTR, CPS_CANCEL, 0); |
| Ime::ImmReleaseContext(m_window, hInputContext); |
| } |
| |
| void WebView::setInputMethodState(bool enabled) |
| { |
| Ime::ImmAssociateContextEx(m_window, 0, enabled ? IACE_DEFAULT : 0); |
| } |
| |
| void WebView::compositionSelectionChanged(bool hasChanged) |
| { |
| if (m_page->editorState().hasComposition && !hasChanged) |
| resetIME(); |
| } |
| |
| bool WebView::onIMEStartComposition() |
| { |
| LOG(TextInput, "onIMEStartComposition"); |
| m_inIMEComposition++; |
| |
| HIMC hInputContext = getIMMContext(); |
| if (!hInputContext) |
| return false; |
| prepareCandidateWindow(hInputContext); |
| Ime::ImmReleaseContext(m_window, hInputContext); |
| return true; |
| } |
| |
| static bool getCompositionString(HIMC hInputContext, DWORD type, String& result) |
| { |
| LONG compositionLength = Ime::ImmGetCompositionStringW(hInputContext, type, 0, 0); |
| if (compositionLength <= 0) |
| return false; |
| Vector<UChar> compositionBuffer(compositionLength / 2); |
| compositionLength = Ime::ImmGetCompositionStringW(hInputContext, type, compositionBuffer.data(), compositionLength); |
| result = String::adopt(compositionBuffer); |
| return true; |
| } |
| |
| static void compositionToUnderlines(const Vector<DWORD>& clauses, const Vector<BYTE>& attributes, Vector<CompositionUnderline>& underlines) |
| { |
| if (clauses.isEmpty()) { |
| underlines.clear(); |
| return; |
| } |
| |
| size_t numBoundaries = clauses.size() - 1; |
| underlines.resize(numBoundaries); |
| for (unsigned i = 0; i < numBoundaries; ++i) { |
| underlines[i].startOffset = clauses[i]; |
| underlines[i].endOffset = clauses[i + 1]; |
| BYTE attribute = attributes[clauses[i]]; |
| underlines[i].thick = attribute == ATTR_TARGET_CONVERTED || attribute == ATTR_TARGET_NOTCONVERTED; |
| underlines[i].color = Color::black; |
| } |
| } |
| |
| #if !LOG_DISABLED |
| #define APPEND_ARGUMENT_NAME(name) \ |
| if (lparam & name) { \ |
| if (needsComma) \ |
| result += ", "; \ |
| result += #name; \ |
| needsComma = true; \ |
| } |
| |
| static String imeCompositionArgumentNames(LPARAM lparam) |
| { |
| String result; |
| bool needsComma = false; |
| |
| APPEND_ARGUMENT_NAME(GCS_COMPATTR); |
| APPEND_ARGUMENT_NAME(GCS_COMPCLAUSE); |
| APPEND_ARGUMENT_NAME(GCS_COMPREADSTR); |
| APPEND_ARGUMENT_NAME(GCS_COMPREADATTR); |
| APPEND_ARGUMENT_NAME(GCS_COMPREADCLAUSE); |
| APPEND_ARGUMENT_NAME(GCS_COMPSTR); |
| APPEND_ARGUMENT_NAME(GCS_CURSORPOS); |
| APPEND_ARGUMENT_NAME(GCS_DELTASTART); |
| APPEND_ARGUMENT_NAME(GCS_RESULTCLAUSE); |
| APPEND_ARGUMENT_NAME(GCS_RESULTREADCLAUSE); |
| APPEND_ARGUMENT_NAME(GCS_RESULTREADSTR); |
| APPEND_ARGUMENT_NAME(GCS_RESULTSTR); |
| APPEND_ARGUMENT_NAME(CS_INSERTCHAR); |
| APPEND_ARGUMENT_NAME(CS_NOMOVECARET); |
| |
| return result; |
| } |
| |
| static String imeRequestName(WPARAM wparam) |
| { |
| switch (wparam) { |
| case IMR_CANDIDATEWINDOW: |
| return "IMR_CANDIDATEWINDOW"; |
| case IMR_COMPOSITIONFONT: |
| return "IMR_COMPOSITIONFONT"; |
| case IMR_COMPOSITIONWINDOW: |
| return "IMR_COMPOSITIONWINDOW"; |
| case IMR_CONFIRMRECONVERTSTRING: |
| return "IMR_CONFIRMRECONVERTSTRING"; |
| case IMR_DOCUMENTFEED: |
| return "IMR_DOCUMENTFEED"; |
| case IMR_QUERYCHARPOSITION: |
| return "IMR_QUERYCHARPOSITION"; |
| case IMR_RECONVERTSTRING: |
| return "IMR_RECONVERTSTRING"; |
| default: |
| return "Unknown (" + String::number(wparam) + ")"; |
| } |
| } |
| #endif |
| |
| bool WebView::onIMEComposition(LPARAM lparam) |
| { |
| LOG(TextInput, "onIMEComposition %s", imeCompositionArgumentNames(lparam).latin1().data()); |
| HIMC hInputContext = getIMMContext(); |
| if (!hInputContext) |
| return true; |
| |
| if (!m_page->editorState().isContentEditable) |
| return true; |
| |
| prepareCandidateWindow(hInputContext); |
| |
| if (lparam & GCS_RESULTSTR || !lparam) { |
| String compositionString; |
| if (!getCompositionString(hInputContext, GCS_RESULTSTR, compositionString) && lparam) |
| return true; |
| |
| m_page->confirmComposition(compositionString); |
| return true; |
| } |
| |
| String compositionString; |
| if (!getCompositionString(hInputContext, GCS_COMPSTR, compositionString)) |
| return true; |
| |
| // Composition string attributes |
| int numAttributes = Ime::ImmGetCompositionStringW(hInputContext, GCS_COMPATTR, 0, 0); |
| Vector<BYTE> attributes(numAttributes); |
| Ime::ImmGetCompositionStringW(hInputContext, GCS_COMPATTR, attributes.data(), numAttributes); |
| |
| // Get clauses |
| int numBytes = Ime::ImmGetCompositionStringW(hInputContext, GCS_COMPCLAUSE, 0, 0); |
| Vector<DWORD> clauses(numBytes / sizeof(DWORD)); |
| Ime::ImmGetCompositionStringW(hInputContext, GCS_COMPCLAUSE, clauses.data(), numBytes); |
| |
| Vector<CompositionUnderline> underlines; |
| compositionToUnderlines(clauses, attributes, underlines); |
| |
| int cursorPosition = LOWORD(Ime::ImmGetCompositionStringW(hInputContext, GCS_CURSORPOS, 0, 0)); |
| |
| m_page->setComposition(compositionString, underlines, cursorPosition); |
| |
| return true; |
| } |
| |
| bool WebView::onIMEEndComposition() |
| { |
| LOG(TextInput, "onIMEEndComposition"); |
| // If the composition hasn't been confirmed yet, it needs to be cancelled. |
| // This happens after deleting the last character from inline input hole. |
| if (m_page->editorState().hasComposition) |
| m_page->confirmComposition(String()); |
| |
| if (m_inIMEComposition) |
| m_inIMEComposition--; |
| |
| return true; |
| } |
| |
| LRESULT WebView::onIMERequestCharPosition(IMECHARPOSITION* charPos) |
| { |
| if (charPos->dwCharPos && !m_page->editorState().hasComposition) |
| return 0; |
| IntRect caret = m_page->firstRectForCharacterInSelectedRange(charPos->dwCharPos); |
| charPos->pt.x = caret.x(); |
| charPos->pt.y = caret.y(); |
| ::ClientToScreen(m_window, &charPos->pt); |
| charPos->cLineHeight = caret.height(); |
| ::GetWindowRect(m_window, &charPos->rcDocument); |
| return true; |
| } |
| |
| LRESULT WebView::onIMERequestReconvertString(RECONVERTSTRING* reconvertString) |
| { |
| String text = m_page->getSelectedText(); |
| unsigned totalSize = sizeof(RECONVERTSTRING) + text.length() * sizeof(UChar); |
| |
| if (!reconvertString) |
| return totalSize; |
| |
| if (totalSize > reconvertString->dwSize) |
| return 0; |
| reconvertString->dwCompStrLen = text.length(); |
| reconvertString->dwStrLen = text.length(); |
| reconvertString->dwTargetStrLen = text.length(); |
| reconvertString->dwStrOffset = sizeof(RECONVERTSTRING); |
| memcpy(reconvertString + 1, text.characters(), text.length() * sizeof(UChar)); |
| return totalSize; |
| } |
| |
| LRESULT WebView::onIMERequest(WPARAM request, LPARAM data) |
| { |
| LOG(TextInput, "onIMERequest %s", imeRequestName(request).latin1().data()); |
| if (!m_page->editorState().isContentEditable) |
| return 0; |
| |
| switch (request) { |
| case IMR_RECONVERTSTRING: |
| return onIMERequestReconvertString(reinterpret_cast<RECONVERTSTRING*>(data)); |
| |
| case IMR_QUERYCHARPOSITION: |
| return onIMERequestCharPosition(reinterpret_cast<IMECHARPOSITION*>(data)); |
| } |
| return 0; |
| } |
| |
| bool WebView::onIMESelect(WPARAM wparam, LPARAM lparam) |
| { |
| UNUSED_PARAM(wparam); |
| UNUSED_PARAM(lparam); |
| LOG(TextInput, "onIMESelect locale %ld %s", lparam, wparam ? "select" : "deselect"); |
| return false; |
| } |
| |
| bool WebView::onIMESetContext(WPARAM wparam, LPARAM) |
| { |
| LOG(TextInput, "onIMESetContext %s", wparam ? "active" : "inactive"); |
| return false; |
| } |
| |
| void WebView::doneWithKeyEvent(const NativeWebKeyboardEvent& event, bool wasEventHandled) |
| { |
| // Calling ::DefWindowProcW will ensure that pressing the Alt key will generate a WM_SYSCOMMAND |
| // event, e.g. See <http://webkit.org/b/47671>. |
| if (!wasEventHandled) |
| ::DefWindowProcW(event.nativeEvent()->hwnd, event.nativeEvent()->message, event.nativeEvent()->wParam, event.nativeEvent()->lParam); |
| } |
| |
| PassRefPtr<WebPopupMenuProxy> WebView::createPopupMenuProxy(WebPageProxy* page) |
| { |
| return WebPopupMenuProxyWin::create(this, page); |
| } |
| |
| PassRefPtr<WebContextMenuProxy> WebView::createContextMenuProxy(WebPageProxy* page) |
| { |
| return WebContextMenuProxyWin::create(m_window, page); |
| } |
| |
| void WebView::setFindIndicator(PassRefPtr<FindIndicator> prpFindIndicator, bool fadeOut) |
| { |
| if (!m_findIndicatorCallback) |
| return; |
| |
| HBITMAP hbmp = 0; |
| IntRect selectionRect; |
| |
| if (RefPtr<FindIndicator> findIndicator = prpFindIndicator) { |
| if (ShareableBitmap* contentImage = findIndicator->contentImage()) { |
| // Render the contentImage to an HBITMAP. |
| void* bits; |
| HDC hdc = ::CreateCompatibleDC(0); |
| int width = contentImage->bounds().width(); |
| int height = contentImage->bounds().height(); |
| BitmapInfo bitmapInfo = BitmapInfo::create(contentImage->size()); |
| |
| hbmp = CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, static_cast<void**>(&bits), 0, 0); |
| HBITMAP hbmpOld = static_cast<HBITMAP>(SelectObject(hdc, hbmp)); |
| #if USE(CG) |
| RetainPtr<CGContextRef> context(AdoptCF, CGBitmapContextCreate(bits, width, height, |
| 8, width * sizeof(RGBQUAD), deviceRGBColorSpaceRef(), kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst)); |
| |
| GraphicsContext graphicsContext(context.get()); |
| contentImage->paint(graphicsContext, IntPoint(), contentImage->bounds()); |
| #else |
| // FIXME: Implement! |
| #endif |
| |
| ::SelectObject(hdc, hbmpOld); |
| ::DeleteDC(hdc); |
| } |
| |
| selectionRect = IntRect(findIndicator->selectionRectInWindowCoordinates()); |
| } |
| |
| // The callback is responsible for calling ::DeleteObject(hbmp). |
| (*m_findIndicatorCallback)(toAPI(this), hbmp, selectionRect, fadeOut, m_findIndicatorCallbackContext); |
| } |
| |
| void WebView::setFindIndicatorCallback(WKViewFindIndicatorCallback callback, void* context) |
| { |
| m_findIndicatorCallback = callback; |
| m_findIndicatorCallbackContext = context; |
| } |
| |
| WKViewFindIndicatorCallback WebView::getFindIndicatorCallback(void** context) |
| { |
| if (context) |
| *context = m_findIndicatorCallbackContext; |
| |
| return m_findIndicatorCallback; |
| } |
| |
| void WebView::didCommitLoadForMainFrame(bool useCustomRepresentation) |
| { |
| } |
| |
| void WebView::didFinishLoadingDataForCustomRepresentation(const String& suggestedFilename, const CoreIPC::DataReference&) |
| { |
| } |
| |
| double WebView::customRepresentationZoomFactor() |
| { |
| return 1; |
| } |
| |
| void WebView::setCustomRepresentationZoomFactor(double) |
| { |
| } |
| |
| void WebView::didChangeScrollbarsForMainFrame() const |
| { |
| } |
| |
| void WebView::findStringInCustomRepresentation(const String&, FindOptions, unsigned) |
| { |
| } |
| |
| void WebView::countStringMatchesInCustomRepresentation(const String&, FindOptions, unsigned) |
| { |
| } |
| |
| void WebView::setIsInWindow(bool isInWindow) |
| { |
| m_isInWindow = isInWindow; |
| m_page->viewStateDidChange(WebPageProxy::ViewIsInWindow); |
| } |
| |
| void WebView::setIsVisible(bool isVisible) |
| { |
| m_isVisible = isVisible; |
| |
| if (m_page) |
| m_page->viewStateDidChange(WebPageProxy::ViewIsVisible); |
| } |
| |
| #if USE(ACCELERATED_COMPOSITING) |
| |
| void WebView::enterAcceleratedCompositingMode(const LayerTreeContext&) |
| { |
| ASSERT(useNewDrawingArea()); |
| // FIXME: Implement. |
| ASSERT_NOT_REACHED(); |
| } |
| |
| void WebView::exitAcceleratedCompositingMode() |
| { |
| ASSERT(useNewDrawingArea()); |
| // FIXME: Implement. |
| ASSERT_NOT_REACHED(); |
| } |
| |
| #endif // USE(ACCELERATED_COMPOSITING) |
| |
| HWND WebView::nativeWindow() |
| { |
| return m_window; |
| } |
| |
| // WebCore::WindowMessageListener |
| |
| void WebView::windowReceivedMessage(HWND, UINT message, WPARAM wParam, LPARAM) |
| { |
| switch (message) { |
| case WM_NCACTIVATE: |
| updateActiveStateSoon(); |
| break; |
| case WM_SETTINGCHANGE: |
| // systemParameterChanged(wParam); |
| break; |
| } |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebView::QueryInterface(REFIID riid, void** ppvObject) |
| { |
| *ppvObject = 0; |
| if (IsEqualGUID(riid, IID_IUnknown)) |
| *ppvObject = static_cast<IUnknown*>(this); |
| else if (IsEqualGUID(riid, IID_IDropTarget)) |
| *ppvObject = static_cast<IDropTarget*>(this); |
| else |
| return E_NOINTERFACE; |
| |
| AddRef(); |
| return S_OK; |
| } |
| |
| ULONG STDMETHODCALLTYPE WebView::AddRef(void) |
| { |
| ref(); |
| return refCount(); |
| } |
| |
| ULONG STDMETHODCALLTYPE WebView::Release(void) |
| { |
| deref(); |
| return refCount(); |
| } |
| |
| static DWORD dragOperationToDragCursor(DragOperation op) |
| { |
| DWORD res = DROPEFFECT_NONE; |
| if (op & DragOperationCopy) |
| res = DROPEFFECT_COPY; |
| else if (op & DragOperationLink) |
| res = DROPEFFECT_LINK; |
| else if (op & DragOperationMove) |
| res = DROPEFFECT_MOVE; |
| else if (op & DragOperationGeneric) |
| res = DROPEFFECT_MOVE; // This appears to be the Firefox behaviour |
| return res; |
| } |
| |
| WebCore::DragOperation WebView::keyStateToDragOperation(DWORD grfKeyState) const |
| { |
| if (!m_page) |
| return DragOperationNone; |
| |
| // Conforms to Microsoft's key combinations as documented for |
| // IDropTarget::DragOver. Note, grfKeyState is the current |
| // state of the keyboard modifier keys on the keyboard. See: |
| // <http://msdn.microsoft.com/en-us/library/ms680129(VS.85).aspx>. |
| DragOperation operation = m_page->dragOperation(); |
| |
| if ((grfKeyState & (MK_CONTROL | MK_SHIFT)) == (MK_CONTROL | MK_SHIFT)) |
| operation = DragOperationLink; |
| else if ((grfKeyState & MK_CONTROL) == MK_CONTROL) |
| operation = DragOperationCopy; |
| else if ((grfKeyState & MK_SHIFT) == MK_SHIFT) |
| operation = DragOperationGeneric; |
| |
| return operation; |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebView::DragEnter(IDataObject* pDataObject, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) |
| { |
| m_dragData = 0; |
| m_page->resetDragOperation(); |
| |
| if (m_dropTargetHelper) |
| m_dropTargetHelper->DragEnter(m_window, pDataObject, (POINT*)&pt, *pdwEffect); |
| |
| POINTL localpt = pt; |
| ::ScreenToClient(m_window, (LPPOINT)&localpt); |
| DragData data(pDataObject, IntPoint(localpt.x, localpt.y), IntPoint(pt.x, pt.y), keyStateToDragOperation(grfKeyState)); |
| m_page->dragEntered(&data); |
| *pdwEffect = dragOperationToDragCursor(m_page->dragOperation()); |
| |
| m_lastDropEffect = *pdwEffect; |
| m_dragData = pDataObject; |
| |
| return S_OK; |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebView::DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) |
| { |
| if (m_dropTargetHelper) |
| m_dropTargetHelper->DragOver((POINT*)&pt, *pdwEffect); |
| |
| if (m_dragData) { |
| POINTL localpt = pt; |
| ::ScreenToClient(m_window, (LPPOINT)&localpt); |
| DragData data(m_dragData.get(), IntPoint(localpt.x, localpt.y), IntPoint(pt.x, pt.y), keyStateToDragOperation(grfKeyState)); |
| m_page->dragUpdated(&data); |
| *pdwEffect = dragOperationToDragCursor(m_page->dragOperation()); |
| } else |
| *pdwEffect = DROPEFFECT_NONE; |
| |
| m_lastDropEffect = *pdwEffect; |
| return S_OK; |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebView::DragLeave() |
| { |
| if (m_dropTargetHelper) |
| m_dropTargetHelper->DragLeave(); |
| |
| if (m_dragData) { |
| DragData data(m_dragData.get(), IntPoint(), IntPoint(), DragOperationNone); |
| m_page->dragExited(&data); |
| m_dragData = 0; |
| m_page->resetDragOperation(); |
| } |
| return S_OK; |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebView::Drop(IDataObject* pDataObject, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) |
| { |
| if (m_dropTargetHelper) |
| m_dropTargetHelper->Drop(pDataObject, (POINT*)&pt, *pdwEffect); |
| |
| m_dragData = 0; |
| *pdwEffect = m_lastDropEffect; |
| POINTL localpt = pt; |
| ::ScreenToClient(m_window, (LPPOINT)&localpt); |
| DragData data(pDataObject, IntPoint(localpt.x, localpt.y), IntPoint(pt.x, pt.y), keyStateToDragOperation(grfKeyState)); |
| |
| SandboxExtension::Handle sandboxExtensionHandle; |
| m_page->performDrag(&data, String(), sandboxExtensionHandle); |
| return S_OK; |
| } |
| |
| } // namespace WebKit |