WebView.cpp   [plain text]


/*
 * 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 "NativeWebWheelEvent.h"
#include "Region.h"
#include "RunLoop.h"
#include "WKAPICast.h"
#include "WKCACFViewWindow.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/NotImplemented.h>
#include <WebCore/SoftLinking.h>
#include <WebCore/WebCoreInstanceHandle.h>
#include <WebCore/WindowMessageBroadcaster.h>
#include <WebCore/WindowsTouch.h>
#include <wtf/text/WTFString.h>

#if ENABLE(FULLSCREEN_API)
#include "WebFullScreenManagerProxy.h"
#include <WebCore/FullScreenController.h>
#endif

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_pageOverlayInstalled(false)
    , m_lastPanX(0)
    , m_lastPanY(0)
    , m_overPanY(0)
    , m_gestureReachedScrollingLimit(false)
#if USE(ACCELERATED_COMPOSITING)
    , m_layerHostWindow(0)
#endif 
{
    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();

#if ENABLE(FULLSCREEN_API)
    m_page->fullScreenManager()->setWebView(this);
#endif
}

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)
{
    NativeWebWheelEvent wheelEvent(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)
{
#if ENABLE(FULLSCREEN_API)
    // Trap the ESC key when in full screen mode.
    if (message == WM_KEYDOWN && wParam == VK_ESCAPE && m_fullScreenController && m_fullScreenController->isFullScreen()) {
        m_fullScreenController->exitFullScreen();
        return false;
    }
#endif

    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 WebPageProxy* page, const RECT& rect)
{
    if (!page->drawsBackground() || page->drawsTransparentBackground())
        return;

    ::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, m_page.get(), unpaintedRects[i]);
            }
        } else
            drawPageBackground(hdc, m_page.get(), dirtyRect);

        m_page->didDraw();
    } else {
        if (m_page->isValid() && m_page->drawingArea() && m_page->drawingArea()->paint(dirtyRect, hdc))
            m_page->didDraw();
        else
            drawPageBackground(hdc, m_page.get(), 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)
{
    // Update child windows now so that any areas of our window they reveal will be included in the
    // invalid region that ::BeginPaint sees.
    updateChildWindowGeometries();

    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();
    }

#if USE(ACCELERATED_COMPOSITING)
    if (m_layerHostWindow)
        ::MoveWindow(m_layerHostWindow, 0, 0, width, height, FALSE);
#endif 

    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::setCursorHiddenUntilMouseMoves(bool)
{
    notImplemented();
}

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();
}

void WebView::setCustomDropTarget(IDropTarget* dropTarget)
{
    if (!m_page->isValid() || !m_window)
        return;

    ::RevokeDragDrop(m_window);

    if (dropTarget)
        ::RegisterDragDrop(m_window, dropTarget);
    else
        ::RegisterDragDrop(m_window, this);
}

FloatRect WebView::convertToDeviceSpace(const FloatRect& rect)
{
    return rect;
}

IntPoint WebView::screenToWindow(const IntPoint& point)
{
    return point;
}

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, bool animate)
{
    UNUSED_PARAM(animate);

    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::didInstallOrUninstallPageOverlay(bool didInstall)
{
    m_pageOverlayInstalled = didInstall;
}

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& context)
{
#if HAVE(WKQCA)
    ASSERT(!context.isEmpty());

    m_layerHostWindow = context.window;

    IntSize size = viewSize();
    // Ensure the layer host window is behind all other child windows (since otherwise it would obscure them).
    ::SetWindowPos(m_layerHostWindow, HWND_BOTTOM, 0, 0, size.width(), size.height(), SWP_SHOWWINDOW | SWP_NOACTIVATE);
#else
    ASSERT_NOT_REACHED();
#endif
}

void WebView::exitAcceleratedCompositingMode()
{
#if HAVE(WKQCA)
    ASSERT(m_layerHostWindow);

    // Tell the WKCACFViewWindow to destroy itself. We can't call ::DestroyWindow directly because
    // the window is owned by another thread.
    ::PostMessageW(m_layerHostWindow, WKCACFViewWindow::customDestroyMessage, 0, 0);
    m_layerHostWindow = 0;
#else
    ASSERT_NOT_REACHED();
#endif
}

#endif // USE(ACCELERATED_COMPOSITING)

HWND WebView::nativeWindow()
{
    return m_window;
}

void WebView::scheduleChildWindowGeometryUpdate(const WindowGeometry& geometry)
{
    m_geometriesUpdater.addPendingUpdate(geometry);
}

void WebView::updateChildWindowGeometries()
{
    m_geometriesUpdater.updateGeometries(DoNotBringToTop);
}

// 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;
}

#if ENABLE(FULLSCREEN_API)
FullScreenController* WebView::fullScreenController()
{
    if (!m_fullScreenController)
        m_fullScreenController = adoptPtr(new FullScreenController(this));
    return m_fullScreenController.get();
}

HWND WebView::fullScreenClientWindow() const
{
    return m_window;
}

HWND WebView::fullScreenClientParentWindow() const
{
    return ::GetParent(m_window);
}

void WebView::fullScreenClientSetParentWindow(HWND hostWindow)
{
    setParentWindow(hostWindow);
}

void WebView::fullScreenClientWillEnterFullScreen()
{
    page()->fullScreenManager()->willEnterFullScreen();
}

void WebView::fullScreenClientDidEnterFullScreen()
{
    page()->fullScreenManager()->didEnterFullScreen();
}

void WebView::fullScreenClientWillExitFullScreen()
{
    page()->fullScreenManager()->willExitFullScreen();
}

void WebView::fullScreenClientDidExitFullScreen()
{
    page()->fullScreenManager()->didExitFullScreen();
}

static void fullScreenClientForceRepaintCompleted(WKErrorRef, void* context)
{
    ASSERT(context);
    static_cast<WebView*>(context)->fullScreenController()->repaintCompleted();
}

void WebView::fullScreenClientForceRepaint()
{
    page()->forceRepaint(VoidCallback::create(this, &fullScreenClientForceRepaintCompleted));
}

#endif
} // namespace WebKit