WebKit2/UIProcess/win/WebView.cpp
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebKit2/UIProcess/win/WebView.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,580 @@
+/*
+ * 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 "WebView.h"
+
+#include "ChunkedUpdateDrawingAreaProxy.h"
+#include "RunLoop.h"
+#include "WebEventFactory.h"
+#include "WebPageNamespace.h"
+#include "WebPageProxy.h"
+#include <Commctrl.h>
+#include <WebCore/IntRect.h>
+#include <WebCore/PlatformString.h>
+#include <WebCore/WebCoreInstanceHandle.h>
+#include <WebCore/WindowMessageBroadcaster.h>
+
+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,
+};
+
+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_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_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_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;
+        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, WebPageNamespace* pageNamespace, HWND hostWindow)
+    : m_rect(rect)
+    , m_hostWindow(hostWindow)
+    , m_topLevelParentWindow(0)
+    , m_toolTipWindow(0)
+    , m_lastCursorSet(0)
+    , m_trackingMouseLeave(false)
+    , m_isBeingDestroyed(false)
+{
+    registerWebViewWindowClass();
+
+    m_page = pageNamespace->createWebPage();
+    m_page->setPageClient(this);
+    m_page->initializeWebPage(IntRect(rect).size(), new ChunkedUpdateDrawingAreaProxy(this));
+
+    m_window = ::CreateWindowEx(0, kWebKit2WebViewWindowClassName, 0, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
+        rect.top, rect.left, rect.right - rect.left, rect.bottom - rect.top, m_hostWindow ? m_hostWindow : HWND_MESSAGE, 0, instanceHandle(), this);
+    ASSERT(::IsWindow(m_window));
+
+    ::ShowWindow(m_window, SW_SHOW);
+
+    // 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::setHostWindow(HWND hostWindow)
+{
+    if (m_window) {
+        // If the host window hasn't changed, bail.
+        if (GetParent(m_window) == hostWindow)
+            return;
+        if (hostWindow)
+            SetParent(m_window, hostWindow);
+        else if (!m_isBeingDestroyed) {
+            // Turn the WebView into a message-only window so it will no longer be a child of the
+            // old host 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);
+        }
+    }
+
+    m_hostWindow = hostWindow;
+    
+    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_hostWindow);
+    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)
+{
+    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();
+    }
+
+    WebMouseEvent mouseEvent = WebEventFactory::createWebMouseEvent(hWnd, message, wParam, lParam);
+    m_page->mouseEvent(mouseEvent);
+
+    handled = true;
+    return 0;
+}
+
+LRESULT WebView::onWheelEvent(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled)
+{
+    // Ctrl+Mouse wheel doesn't ever go into WebCore.  It is used to
+    // zoom instead (Mac zooms the whole Desktop, but Windows browsers trigger their
+    // own local zoom modes for Ctrl+wheel).
+    /*
+    if (wParam & MK_CONTROL) {
+        short delta = static_cast<short>(HIWORD(wParam));
+        if (delta < 0)
+            m_page->makeTextSmaller(0);
+        else
+            m_page->makeTextLarger(0);
+
+        handled = true;
+        return 0;
+    }
+    */
+
+    WebWheelEvent wheelEvent = WebEventFactory::createWebWheelEvent(hWnd, message, wParam, lParam);
+    m_page->wheelEvent(wheelEvent);
+
+    handled = true;
+    return 0;
+}
+
+LRESULT WebView::onKeyEvent(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled)
+{
+    WebKeyboardEvent keyboardEvent = WebEventFactory::createWebKeyboardEvent(hWnd, message, wParam, lParam);
+    m_page->keyEvent(keyboardEvent);
+
+    handled = true;
+    return 0;
+}
+
+LRESULT WebView::onPaintEvent(HWND hWnd, UINT message, WPARAM, LPARAM, bool& handled)
+{
+    PAINTSTRUCT paintStruct;
+    HDC hdc = ::BeginPaint(m_window, &paintStruct);
+
+    m_page->drawingArea()->paint(IntRect(paintStruct.rcPaint), hdc);
+
+    ::EndPaint(m_window, &paintStruct);
+
+    handled = true;
+    return 0;
+}
+
+LRESULT WebView::onSizeEvent(HWND, UINT, WPARAM, LPARAM lParam, bool& handled)
+{
+    int width = LOWORD(lParam);
+    int height = HIWORD(lParam);
+
+    m_page->drawingArea()->setSize(IntSize(width, height));
+
+    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->setFocused(true);
+
+    handled = true;
+    return 0;
+}
+
+LRESULT WebView::onKillFocusEvent(HWND, UINT, WPARAM, LPARAM lParam, bool& handled)
+{
+    m_page->setFocused(false);
+
+    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: Is WM_SHOWWINDOW sent when ShowWindow is called on an ancestor of our window?
+    if (!lParam) {
+        bool isVisible = wParam;
+
+        // Notify the drawing area that the visibility changed.
+        m_page->drawingArea()->setPageIsVisible(isVisible);
+
+        handled = true;
+    }
+
+    return 0;
+}
+
+LRESULT WebView::onSetCursor(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled)
+{
+    handled = ::SetCursor(m_lastCursorSet);
+    return 0;
+}
+
+bool WebView::isActive()
+{
+    HWND activeWindow = ::GetActiveWindow();
+    return (activeWindow && m_topLevelParentWindow == findTopLevelParentWindow(activeWindow));
+}
+
+void WebView::updateActiveState()
+{
+    m_page->setActive(isActive());
+}
+
+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);
+}
+
+void WebView::close()
+{
+    setHostWindow(0);
+    m_page->close();
+}
+
+// PageClient
+
+void WebView::processDidExit()
+{
+}
+
+void WebView::processDidRevive()
+{
+}
+
+void WebView::takeFocus(bool)
+{
+}
+
+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);
+}
+
+void WebView::setCursor(const WebCore::Cursor& cursor)
+{
+    HCURSOR platformCursor = cursor.platformCursor()->nativeCursor();
+    if (!platformCursor)
+        return;
+
+    m_lastCursorSet = platformCursor;
+    ::SetCursor(platformCursor);
+}
+
+#if USE(ACCELERATED_COMPOSITING)
+void WebView::pageDidEnterAcceleratedCompositing()
+{
+}
+
+void WebView::pageDidLeaveAcceleratedCompositing()
+{
+}
+#endif // USE(ACCELERATED_COMPOSITING)
+
+// WebCore::WindowMessageListener
+
+void WebView::windowReceivedMessage(HWND, UINT message, WPARAM wParam, LPARAM)
+{
+    switch (message) {
+        case WM_NCACTIVATE:
+            updateActiveStateSoon();
+            break;
+        case WM_SETTINGCHANGE:
+            // systemParameterChanged(wParam);
+            break;
+    }
+}
+
+} // namespace WebKit