diff options
Diffstat (limited to 'Source/WebKit2/UIProcess/win')
-rw-r--r-- | Source/WebKit2/UIProcess/win/ChunkedUpdateDrawingAreaProxyWin.cpp | 106 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/win/LayerBackedDrawingAreaProxyWin.cpp | 65 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/win/TextCheckerWin.cpp | 98 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/win/WebContextMenuProxyWin.cpp | 103 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/win/WebContextMenuProxyWin.h | 61 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/win/WebContextWin.cpp | 56 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/win/WebInspectorProxyWin.cpp | 194 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/win/WebPageProxyWin.cpp | 113 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/win/WebPopupMenuProxyWin.cpp | 937 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/win/WebPopupMenuProxyWin.h | 136 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/win/WebView.cpp | 1085 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/win/WebView.h | 170 |
12 files changed, 3124 insertions, 0 deletions
diff --git a/Source/WebKit2/UIProcess/win/ChunkedUpdateDrawingAreaProxyWin.cpp b/Source/WebKit2/UIProcess/win/ChunkedUpdateDrawingAreaProxyWin.cpp new file mode 100644 index 0000000..6a1ee36 --- /dev/null +++ b/Source/WebKit2/UIProcess/win/ChunkedUpdateDrawingAreaProxyWin.cpp @@ -0,0 +1,106 @@ +/* + * 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 "ChunkedUpdateDrawingAreaProxy.h" + +#include "UpdateChunk.h" +#include "WebProcessProxy.h" +#include "WebView.h" +#include <WebCore/BitmapInfo.h> + +using namespace WebCore; + +namespace WebKit { + +WebPageProxy* ChunkedUpdateDrawingAreaProxy::page() +{ + return m_webView->page(); +} + +void ChunkedUpdateDrawingAreaProxy::ensureBackingStore() +{ + if (m_backingStoreBitmap) + return; + + BitmapInfo bitmapInfo = BitmapInfo::createBottomUp(size()); + + void* pixels = 0; + m_backingStoreBitmap.set(::CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0)); + + if (!m_backingStoreDC) { + // Create a DC for the backing store. + HDC screenDC = ::GetDC(0); + m_backingStoreDC.set(::CreateCompatibleDC(screenDC)); + ::ReleaseDC(0, screenDC); + } + + ::SelectObject(m_backingStoreDC.get(), m_backingStoreBitmap.get()); +} + +void ChunkedUpdateDrawingAreaProxy::invalidateBackingStore() +{ + m_backingStoreBitmap.clear(); +} + +bool ChunkedUpdateDrawingAreaProxy::platformPaint(const IntRect& rect, HDC hdc) +{ + if (!m_backingStoreBitmap) + return false; + + // BitBlt from the backing-store to the passed in hdc. + ::BitBlt(hdc, rect.x(), rect.y(), rect.width(), rect.height(), m_backingStoreDC.get(), rect.x(), rect.y(), SRCCOPY); + return true; +} + +void ChunkedUpdateDrawingAreaProxy::drawUpdateChunkIntoBackingStore(UpdateChunk* updateChunk) +{ + ensureBackingStore(); + + OwnPtr<HDC> updateChunkBitmapDC(::CreateCompatibleDC(m_backingStoreDC.get())); + + // Create a bitmap. + BitmapInfo bitmapInfo = BitmapInfo::createBottomUp(updateChunk->rect().size()); + + // Duplicate the update chunk handle. + HANDLE updateChunkHandle; + BOOL result = ::DuplicateHandle(m_webView->page()->process()->processIdentifier(), updateChunk->memory(), + ::GetCurrentProcess(), &updateChunkHandle, STANDARD_RIGHTS_REQUIRED | FILE_MAP_READ | FILE_MAP_WRITE, false, DUPLICATE_CLOSE_SOURCE); + + void* pixels = 0; + OwnPtr<HBITMAP> hBitmap(::CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &pixels, updateChunkHandle, 0)); + ::SelectObject(updateChunkBitmapDC.get(), hBitmap.get()); + + // BitBlt from the UpdateChunk to the backing store. + ::BitBlt(m_backingStoreDC.get(), updateChunk->rect().x(), updateChunk->rect().y(), updateChunk->rect().width(), updateChunk->rect().height(), updateChunkBitmapDC.get(), 0, 0, SRCCOPY); + + // FIXME: We should not do this here. + ::CloseHandle(updateChunkHandle); + + // Invalidate the WebView's HWND. + RECT rect = updateChunk->rect(); + ::InvalidateRect(m_webView->window(), &rect, false); +} + +} // namespace WebKit diff --git a/Source/WebKit2/UIProcess/win/LayerBackedDrawingAreaProxyWin.cpp b/Source/WebKit2/UIProcess/win/LayerBackedDrawingAreaProxyWin.cpp new file mode 100644 index 0000000..8259272 --- /dev/null +++ b/Source/WebKit2/UIProcess/win/LayerBackedDrawingAreaProxyWin.cpp @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#if USE(ACCELERATED_COMPOSITING) + +#include "LayerBackedDrawingAreaProxy.h" + +#include "DrawingAreaMessageKinds.h" +#include "DrawingAreaProxyMessageKinds.h" +#include "WebView.h" +#include "WebPageProxy.h" +#include "WebProcessProxy.h" + +using namespace WebCore; + +namespace WebKit { + +WebPageProxy* LayerBackedDrawingAreaProxy::page() +{ + return m_webView->page(); +} + +void LayerBackedDrawingAreaProxy::platformSetSize() +{ +} + +void LayerBackedDrawingAreaProxy::attachCompositingContext(uint32_t contextID) +{ + +} + +void LayerBackedDrawingAreaProxy::detachCompositingContext() +{ +} + +bool LayerBackedDrawingAreaProxy::paint(const IntRect&, PlatformDrawingContext) +{ + return false; +} + +} // namespace WebKit + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebKit2/UIProcess/win/TextCheckerWin.cpp b/Source/WebKit2/UIProcess/win/TextCheckerWin.cpp new file mode 100644 index 0000000..3c4b1eb --- /dev/null +++ b/Source/WebKit2/UIProcess/win/TextCheckerWin.cpp @@ -0,0 +1,98 @@ +/* + * 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 "TextChecker.h" + +#include "NotImplemented.h" +#include "TextCheckerState.h" + +using namespace WebCore; + +namespace WebKit { + +static TextCheckerState textCheckerState; + +const TextCheckerState& TextChecker::state() +{ + notImplemented(); + + return textCheckerState; +} + +bool TextChecker::isContinuousSpellCheckingAllowed() +{ + notImplemented(); + + return false; +} + +void TextChecker::setContinuousSpellCheckingEnabled(bool isContinuousSpellCheckingEnabled) +{ + notImplemented(); +} + +void TextChecker::setGrammarCheckingEnabled(bool isGrammarCheckingEnabled) +{ + notImplemented(); +} + +int64_t TextChecker::uniqueSpellDocumentTag() +{ + notImplemented(); + return 0; +} + +void TextChecker::closeSpellDocumentWithTag(int64_t) +{ + notImplemented(); +} + +Vector<TextCheckingResult> TextChecker::checkTextOfParagraph(int64_t spellDocumentTag, const UChar* text, int length, uint64_t checkingTypes) +{ + notImplemented(); + return Vector<WebCore::TextCheckingResult>(); +} + +void TextChecker::updateSpellingUIWithMisspelledWord(const String& misspelledWord) +{ + notImplemented(); +} + +void TextChecker::getGuessesForWord(int64_t spellDocumentTag, const String& word, const String& context, Vector<String>& guesses) +{ + notImplemented(); +} + +void TextChecker::learnWord(const String& word) +{ + notImplemented(); +} + +void TextChecker::ignoreWord(int64_t spellDocumentTag, const String& word) +{ + notImplemented(); +} + +} // namespace WebKit diff --git a/Source/WebKit2/UIProcess/win/WebContextMenuProxyWin.cpp b/Source/WebKit2/UIProcess/win/WebContextMenuProxyWin.cpp new file mode 100644 index 0000000..090598f --- /dev/null +++ b/Source/WebKit2/UIProcess/win/WebContextMenuProxyWin.cpp @@ -0,0 +1,103 @@ +/* + * 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 "WebContextMenuProxyWin.h" + +#include "NotImplemented.h" + +using namespace WebCore; + +namespace WebKit { + +WebContextMenuProxyWin::WebContextMenuProxyWin(HWND parentWindow, WebPageProxy* page) + : m_window(parentWindow) + , m_page(page) + , m_menu(0) +{ +} + +void WebContextMenuProxyWin::populateMenu(HMENU menu, const Vector<WebContextMenuItemData>& items) +{ + for (size_t i = 0; i < items.size(); ++i) { + const WebContextMenuItemData& itemData = items[i]; + switch (itemData.type()) { + case ActionType: + case CheckableActionType: { + UINT flags = itemData.enabled() ? MF_ENABLED : MF_DISABLED; + if (itemData.checked()) + flags |= MF_CHECKED; + String title = itemData.title(); + ::AppendMenu(menu, flags, itemData.action(), title.charactersWithNullTermination()); + + m_actionMap.add(itemData.action(), itemData); + break; + } + case SeparatorType: + ::AppendMenu(menu, MF_SEPARATOR, 0, 0); + break; + case SubmenuType: { + HMENU subMenu = ::CreatePopupMenu(); + populateMenu(subMenu, itemData.submenu()); + String title = itemData.title(); + ::AppendMenu(menu, MF_POPUP, reinterpret_cast<UINT>(subMenu), title.charactersWithNullTermination()); + break; + } + default: + ASSERT_NOT_REACHED(); + } + } +} + +void WebContextMenuProxyWin::showContextMenu(const IntPoint& origin, const Vector<WebContextMenuItemData>& items) +{ + // Hide any context menu we have showing (this also destroys the menu). + hideContextMenu(); + + m_menu = ::CreatePopupMenu(); + populateMenu(m_menu, items); + + POINT point = POINT(origin); + if (!::ClientToScreen(m_window, &point)) + return; + + UINT flags = TPM_RIGHTBUTTON | TPM_TOPALIGN | TPM_VERPOSANIMATION | TPM_HORIZONTAL | TPM_LEFTALIGN | TPM_HORPOSANIMATION | TPM_RETURNCMD | TPM_NONOTIFY; + int selectedCommand = ::TrackPopupMenuEx(m_menu, flags, point.x, point.y, m_window, 0); + if (!selectedCommand) + return; + + m_page->contextMenuItemSelected(m_actionMap.get(selectedCommand)); +} + +void WebContextMenuProxyWin::hideContextMenu() +{ + if (m_menu) { + ::DestroyMenu(m_menu); + m_menu = 0; + } + + m_actionMap.clear(); +} + +} // namespace WebKit diff --git a/Source/WebKit2/UIProcess/win/WebContextMenuProxyWin.h b/Source/WebKit2/UIProcess/win/WebContextMenuProxyWin.h new file mode 100644 index 0000000..a843b26 --- /dev/null +++ b/Source/WebKit2/UIProcess/win/WebContextMenuProxyWin.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#ifndef WebContextMenuProxyWin_h +#define WebContextMenuProxyWin_h + +#include "WebContextMenuItemData.h" +#include "WebContextMenuProxy.h" +#include "WebPageProxy.h" +#include <wtf/HashMap.h> + +namespace WebKit { + +class WebContextMenuProxyWin : public WebContextMenuProxy { +public: + static PassRefPtr<WebContextMenuProxyWin> create(HWND parentWindow, WebPageProxy* page) + { + return adoptRef(new WebContextMenuProxyWin(parentWindow, page)); + } + +private: + WebContextMenuProxyWin(HWND parentWindow, WebPageProxy* page); + + virtual void showContextMenu(const WebCore::IntPoint&, const Vector<WebContextMenuItemData>&); + virtual void hideContextMenu(); + + void populateMenu(HMENU, const Vector<WebContextMenuItemData>&); + + HMENU m_menu; + HWND m_window; + WebPageProxy* m_page; + + // Creates a map from the context menu item's action to the context menu item itself. + HashMap<int, WebContextMenuItemData> m_actionMap; +}; + +} // namespace WebKit + +#endif // WebContextMenuProxyWin_h diff --git a/Source/WebKit2/UIProcess/win/WebContextWin.cpp b/Source/WebKit2/UIProcess/win/WebContextWin.cpp new file mode 100644 index 0000000..83b586d --- /dev/null +++ b/Source/WebKit2/UIProcess/win/WebContextWin.cpp @@ -0,0 +1,56 @@ +/* + * 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 "WebContext.h" + +#include "WebProcessCreationParameters.h" +#include "WebProcessMessages.h" +#include <WebCore/FileSystem.h> + +using namespace WebCore; + +namespace WebKit { + +String WebContext::applicationCacheDirectory() +{ + return localUserSpecificStorageDirectory(); +} + +void WebContext::setShouldPaintNativeControls(bool b) +{ + m_shouldPaintNativeControls = b; + + if (!hasValidProcess()) + return; + m_process->send(Messages::WebProcess::SetShouldPaintNativeControls(m_shouldPaintNativeControls), 0); +} + +void WebContext::platformInitializeWebProcess(WebProcessCreationParameters& parameters) +{ + parameters.shouldPaintNativeControls = m_shouldPaintNativeControls; +} + +} // namespace WebKit + diff --git a/Source/WebKit2/UIProcess/win/WebInspectorProxyWin.cpp b/Source/WebKit2/UIProcess/win/WebInspectorProxyWin.cpp new file mode 100644 index 0000000..7637429 --- /dev/null +++ b/Source/WebKit2/UIProcess/win/WebInspectorProxyWin.cpp @@ -0,0 +1,194 @@ +/* + * 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 "WebInspectorProxy.h" + +#if ENABLE(INSPECTOR) + +#include "WebPageProxy.h" +#include "WebView.h" +#include <WebCore/WebCoreInstanceHandle.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RetainPtr.h> +#include <wtf/text/WTFString.h> + +using namespace WebCore; + +namespace WebKit { + +static const LPCWSTR kWebKit2InspectorWindowClassName = L"WebKit2InspectorWindowClass"; + +bool WebInspectorProxy::registerInspectorViewWindowClass() +{ + static bool haveRegisteredWindowClass = false; + if (haveRegisteredWindowClass) + return true; + haveRegisteredWindowClass = true; + + WNDCLASSEX wcex; + + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_DBLCLKS; + wcex.lpfnWndProc = WebInspectorProxy::InspectorViewWndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = sizeof(WebInspectorProxy*); + wcex.hInstance = instanceHandle(); + wcex.hIcon = 0; + wcex.hCursor = ::LoadCursor(0, IDC_ARROW); + wcex.hbrBackground = 0; + wcex.lpszMenuName = 0; + wcex.lpszClassName = kWebKit2InspectorWindowClassName; + wcex.hIconSm = 0; + + return !!::RegisterClassEx(&wcex); +} + +LRESULT CALLBACK WebInspectorProxy::InspectorViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + LONG_PTR longPtr = ::GetWindowLongPtr(hWnd, 0); + + if (WebInspectorProxy* inspectorView = reinterpret_cast<WebInspectorProxy*>(longPtr)) + return inspectorView->wndProc(hWnd, message, wParam, lParam); + + if (message == WM_CREATE) { + LPCREATESTRUCT createStruct = reinterpret_cast<LPCREATESTRUCT>(lParam); + + // Associate the WebInspectorProxy with the window. + ::SetWindowLongPtr(hWnd, 0, (LONG_PTR)createStruct->lpCreateParams); + return 0; + } + + return ::DefWindowProc(hWnd, message, wParam, lParam); +} + +LRESULT WebInspectorProxy::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + LRESULT lResult = 0; + bool handled = true; + + switch (message) { + case WM_SIZE: + lResult = onSizeEvent(hWnd, message, wParam, lParam, handled); + break; + case WM_GETMINMAXINFO: + lResult = onMinMaxInfoEvent(hWnd, message, wParam, lParam, handled); + break; + case WM_SETFOCUS: + lResult = onSetFocusEvent(hWnd, message, wParam, lParam, handled); + break; + case WM_CLOSE: + lResult = onCloseEvent(hWnd, message, wParam, lParam, handled); + break; + default: + handled = false; + break; + } + + if (!handled) + lResult = ::DefWindowProc(hWnd, message, wParam, lParam); + + return lResult; +} + +LRESULT WebInspectorProxy::onSizeEvent(HWND, UINT, WPARAM, LPARAM, bool&) +{ + RECT rect; + ::GetClientRect(m_inspectorWindow, &rect); + + ::SetWindowPos(m_inspectorView->window(), 0, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER); + + return 0; +} + +LRESULT WebInspectorProxy::onSetFocusEvent(HWND, UINT, WPARAM, LPARAM lParam, bool&) +{ + ::SetFocus(m_inspectorView->window()); + + return 0; +} + +LRESULT WebInspectorProxy::onMinMaxInfoEvent(HWND, UINT, WPARAM, LPARAM lParam, bool&) +{ + MINMAXINFO* info = reinterpret_cast<MINMAXINFO*>(lParam); + POINT size = {minimumWindowWidth, minimumWindowHeight}; + info->ptMinTrackSize = size; + + return 0; +} + +LRESULT WebInspectorProxy::onCloseEvent(HWND, UINT, WPARAM, LPARAM, bool&) +{ + ::ShowWindow(m_inspectorWindow, SW_HIDE); + close(); + + return 0; +} + +WebPageProxy* WebInspectorProxy::platformCreateInspectorPage() +{ + ASSERT(!m_inspectorView); + ASSERT(!m_inspectorWindow); + + RECT emptyRect = { 0 }; + m_inspectorView = WebView::create(emptyRect, m_page->context(), inspectorPageGroup(), 0); + + return m_inspectorView->page(); +} + +void WebInspectorProxy::platformOpen() +{ + registerInspectorViewWindowClass(); + + m_inspectorWindow = ::CreateWindowEx(0, kWebKit2InspectorWindowClassName, 0, WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + 0, 0, initialWindowWidth, initialWindowHeight, 0, 0, instanceHandle(), this); + ASSERT(::IsWindow(m_inspectorWindow)); + + m_inspectorView->setParentWindow(m_inspectorWindow); + ::ShowWindow(m_inspectorWindow, SW_SHOW); +} + +void WebInspectorProxy::platformClose() +{ + ASSERT(m_inspectorWindow); + ASSERT(m_inspectorView); + + ::DestroyWindow(m_inspectorWindow); + + m_inspectorWindow = 0; + m_inspectorView = 0; +} + +String WebInspectorProxy::inspectorPageURL() const +{ + RetainPtr<CFURLRef> htmlURLRef(AdoptCF, CFBundleCopyResourceURL(CFBundleGetBundleWithIdentifier(CFSTR("com.apple.WebKit")), CFSTR("inspector"), CFSTR("html"), CFSTR("inspector"))); + if (!htmlURLRef) + return String(); + + return String(CFURLGetString(htmlURLRef.get())); +} + +} // namespace WebKit + +#endif // ENABLE(INSPECTOR) diff --git a/Source/WebKit2/UIProcess/win/WebPageProxyWin.cpp b/Source/WebKit2/UIProcess/win/WebPageProxyWin.cpp new file mode 100644 index 0000000..6b0efd1 --- /dev/null +++ b/Source/WebKit2/UIProcess/win/WebPageProxyWin.cpp @@ -0,0 +1,113 @@ +/* +* 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 "WebPageProxy.h" + +#include <tchar.h> +#include <WebCore/Language.h> +#include <WebCore/WebCoreInstanceHandle.h> +#include <wtf/StdLibExtras.h> +#include <wtf/text/StringConcatenate.h> + +using namespace WebCore; + +namespace WebKit { + +static String windowsVersion() +{ + String osVersion; + OSVERSIONINFO versionInfo = { 0 }; + versionInfo.dwOSVersionInfoSize = sizeof(versionInfo); + ::GetVersionEx(&versionInfo); + + if (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { + if (versionInfo.dwMajorVersion == 4) { + if (versionInfo.dwMinorVersion == 0) + osVersion = "Windows 95"; + else if (versionInfo.dwMinorVersion == 10) + osVersion = "Windows 98"; + else if (versionInfo.dwMinorVersion == 90) + osVersion = "Windows 98; Win 9x 4.90"; + } + } else if (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) + osVersion = makeString("Windows NT ", String::number(versionInfo.dwMajorVersion), '.', String::number(versionInfo.dwMinorVersion)); + + if (!osVersion.length()) + osVersion = makeString("Windows ", String::number(versionInfo.dwMajorVersion), '.', String::number(versionInfo.dwMinorVersion)); + return osVersion; +} + +static String userVisibleWebKitVersionString() +{ + String versionStr = "420+"; + void* data = 0; + + struct LANGANDCODEPAGE { + WORD wLanguage; + WORD wCodePage; + } *lpTranslate; + + TCHAR path[MAX_PATH]; + ::GetModuleFileName(instanceHandle(), path, WTF_ARRAY_LENGTH(path)); + DWORD handle; + DWORD versionSize = ::GetFileVersionInfoSize(path, &handle); + if (!versionSize) + goto exit; + data = fastMalloc(versionSize); + if (!data) + goto exit; + if (!::GetFileVersionInfo(path, 0, versionSize, data)) + goto exit; + UINT cbTranslate; + if (!::VerQueryValue(data, TEXT("\\VarFileInfo\\Translation"), (LPVOID*)&lpTranslate, &cbTranslate)) + goto exit; + TCHAR key[256]; + _stprintf_s(key, WTF_ARRAY_LENGTH(key), TEXT("\\StringFileInfo\\%04x%04x\\ProductVersion"), lpTranslate[0].wLanguage, lpTranslate[0].wCodePage); + LPCTSTR productVersion; + UINT productVersionLength; + if (!::VerQueryValue(data, (LPTSTR)(LPCTSTR)key, (void**)&productVersion, &productVersionLength)) + goto exit; + versionStr = String(productVersion, productVersionLength - 1); + +exit: + if (data) + fastFree(data); + return versionStr; +} + +String WebPageProxy::standardUserAgent(const String& applicationNameForUserAgent) +{ + DEFINE_STATIC_LOCAL(String, osVersion, (windowsVersion())); + DEFINE_STATIC_LOCAL(String, webKitVersion, (userVisibleWebKitVersionString())); + + // FIXME: We should upate the user agent if the default language changes. + String language = defaultLanguage(); + + if (applicationNameForUserAgent.isEmpty()) + return makeString("Mozilla/5.0 (Windows; U; ", osVersion, "; ", language, ") AppleWebKit/", webKitVersion, " (KHTML, like Gecko)"); + return makeString("Mozilla/5.0 (Windows; U; ", osVersion, "; ", language, ") AppleWebKit/", webKitVersion, " (KHTML, like Gecko) ", applicationNameForUserAgent); +} + +} // namespace WebKit diff --git a/Source/WebKit2/UIProcess/win/WebPopupMenuProxyWin.cpp b/Source/WebKit2/UIProcess/win/WebPopupMenuProxyWin.cpp new file mode 100644 index 0000000..64b9fb5 --- /dev/null +++ b/Source/WebKit2/UIProcess/win/WebPopupMenuProxyWin.cpp @@ -0,0 +1,937 @@ +/* + * 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. + */ + +// NOTE: This implementation is very similar to the implementation of popups in WebCore::PopupMenuWin. +// We should try and factor out the common bits and share them. + +#include "WebPopupMenuProxyWin.h" + +#include "WebView.h" +#include <WebCore/WebCoreInstanceHandle.h> +#include <WebCore/ScrollbarTheme.h> +#include <WebCore/BitmapInfo.h> +#include <WebCore/PlatformMouseEvent.h> +#include <windowsx.h> + +using namespace WebCore; +using namespace std; + +namespace WebKit { + +static const LPCWSTR kWebKit2WebPopupMenuProxyWindowClassName = L"WebKit2WebPopupMenuProxyWindowClass"; + +static const int defaultAnimationDuration = 200; +static const int maxPopupHeight = 320; +static const int popupWindowBorderWidth = 1; +static const int separatorPadding = 4; +static const int separatorHeight = 1; + +// This is used from within our custom message pump when we want to send a +// message to the web view and not have our message stolen and sent to +// the popup window. +static const UINT WM_HOST_WINDOW_FIRST = WM_USER; +static const UINT WM_HOST_WINDOW_CHAR = WM_USER + WM_CHAR; +static const UINT WM_HOST_WINDOW_MOUSEMOVE = WM_USER + WM_MOUSEMOVE; + +static inline bool isASCIIPrintable(unsigned c) +{ + return c >= 0x20 && c <= 0x7E; +} + +static void translatePoint(LPARAM& lParam, HWND from, HWND to) +{ + POINT pt; + pt.x = static_cast<short>(GET_X_LPARAM(lParam)); + pt.y = static_cast<short>(GET_Y_LPARAM(lParam)); + ::MapWindowPoints(from, to, &pt, 1); + lParam = MAKELPARAM(pt.x, pt.y); +} + +LRESULT CALLBACK WebPopupMenuProxyWin::WebPopupMenuProxyWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + LONG_PTR longPtr = ::GetWindowLongPtr(hWnd, 0); + + if (WebPopupMenuProxyWin* popupMenuProxy = reinterpret_cast<WebPopupMenuProxyWin*>(longPtr)) + return popupMenuProxy->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 WebPopupMenuProxyWin::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + LRESULT lResult = 0; + bool handled = true; + + switch (message) { + case WM_MOUSEACTIVATE: + lResult = onMouseActivate(hWnd, message, wParam, lParam, handled); + break; + case WM_SIZE: + lResult = onSize(hWnd, message, wParam, lParam, handled); + break; + case WM_KEYDOWN: + lResult = onKeyDown(hWnd, message, wParam, lParam, handled); + break; + case WM_CHAR: + lResult = onChar(hWnd, message, wParam, lParam, handled); + break; + case WM_MOUSEMOVE: + lResult = onMouseMove(hWnd, message, wParam, lParam, handled); + break; + case WM_LBUTTONDOWN: + lResult = onLButtonDown(hWnd, message, wParam, lParam, handled); + break; + case WM_LBUTTONUP: + lResult = onLButtonUp(hWnd, message, wParam, lParam, handled); + break; + case WM_MOUSEWHEEL: + lResult = onMouseWheel(hWnd, message, wParam, lParam, handled); + break; + case WM_PAINT: + lResult = onPaint(hWnd, message, wParam, lParam, handled); + break; + case WM_PRINTCLIENT: + lResult = onPrintClient(hWnd, message, wParam, lParam, handled); + break; + default: + handled = false; + break; + } + + if (!handled) + lResult = ::DefWindowProc(hWnd, message, wParam, lParam); + + return lResult; +} + +bool WebPopupMenuProxyWin::registerWindowClass() +{ + static bool haveRegisteredWindowClass = false; + if (haveRegisteredWindowClass) + return true; + haveRegisteredWindowClass = true; + + WNDCLASSEX wcex; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_DROPSHADOW; + wcex.lpfnWndProc = WebPopupMenuProxyWin::WebPopupMenuProxyWndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = sizeof(WebPopupMenuProxyWin*); + wcex.hInstance = instanceHandle(); + wcex.hIcon = 0; + wcex.hCursor = ::LoadCursor(0, IDC_ARROW); + wcex.hbrBackground = 0; + wcex.lpszMenuName = 0; + wcex.lpszClassName = kWebKit2WebPopupMenuProxyWindowClassName; + wcex.hIconSm = 0; + + return !!::RegisterClassEx(&wcex); +} + +WebPopupMenuProxyWin::WebPopupMenuProxyWin(WebView* webView, WebPopupMenuProxy::Client* client) + : WebPopupMenuProxy(client) + , m_webView(webView) + , m_newSelectedIndex(0) + , m_popup(0) + , m_DC(0) + , m_bmp(0) + , m_itemHeight(0) + , m_scrollOffset(0) + , m_wheelDelta(0) + , m_focusedIndex(0) + , m_wasClicked(false) + , m_scrollbarCapturingMouse(false) + , m_showPopup(false) +{ +} + +WebPopupMenuProxyWin::~WebPopupMenuProxyWin() +{ + if (m_bmp) + ::DeleteObject(m_bmp); + if (m_DC) + ::DeleteDC(m_DC); + if (m_popup) + ::DestroyWindow(m_popup); + if (m_scrollbar) + m_scrollbar->setParent(0); +} + +void WebPopupMenuProxyWin::showPopupMenu(const IntRect& rect, const Vector<WebPopupItem>& items, const PlatformPopupMenuData& data, int32_t selectedIndex) +{ + m_items = items; + m_data = data; + m_newSelectedIndex = selectedIndex; + + calculatePositionAndSize(rect); + if (clientRect().isEmpty()) + return; + + HWND hostWindow = m_webView->window(); + + if (!m_scrollbar && visibleItems() < m_items.size()) { + m_scrollbar = Scrollbar::createNativeScrollbar(this, VerticalScrollbar, SmallScrollbar); + m_scrollbar->styleChanged(); + } + + if (!m_popup) { + registerWindowClass(); + + DWORD exStyle = WS_EX_LTRREADING; + + m_popup = ::CreateWindowEx(exStyle, kWebKit2WebPopupMenuProxyWindowClassName, TEXT("PopupMenu"), + WS_POPUP | WS_BORDER, + m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height(), + hostWindow, 0, instanceHandle(), this); + + if (!m_popup) + return; + } + + BOOL shouldAnimate = FALSE; + ::SystemParametersInfo(SPI_GETCOMBOBOXANIMATION, 0, &shouldAnimate, 0); + + if (shouldAnimate) { + RECT viewRect = {0}; + ::GetWindowRect(hostWindow, &viewRect); + + if (!::IsRectEmpty(&viewRect)) { + // Popups should slide into view away from the <select> box + // NOTE: This may have to change for Vista + DWORD slideDirection = (m_windowRect.y() < viewRect.top + rect.location().y()) ? AW_VER_NEGATIVE : AW_VER_POSITIVE; + + ::AnimateWindow(m_popup, defaultAnimationDuration, AW_SLIDE | slideDirection); + } + } else + ::ShowWindow(m_popup, SW_SHOWNOACTIVATE); + + + int index = selectedIndex; + if (index >= 0) + setFocusedIndex(index); + + m_showPopup = true; + + // Protect the popup menu in case its owner is destroyed while we're running the message pump. + RefPtr<WebPopupMenuProxyWin> protect(this); + + ::SetCapture(hostWindow); + + MSG msg; + HWND activeWindow; + + while (::GetMessage(&msg, 0, 0, 0)) { + switch (msg.message) { + case WM_HOST_WINDOW_MOUSEMOVE: + case WM_HOST_WINDOW_CHAR: + if (msg.hwnd == m_popup) { + // This message should be sent to the host window. + msg.hwnd = hostWindow; + msg.message -= WM_HOST_WINDOW_FIRST; + } + break; + + // Steal mouse messages. + case WM_NCMOUSEMOVE: + case WM_NCLBUTTONDOWN: + case WM_NCLBUTTONUP: + case WM_NCLBUTTONDBLCLK: + case WM_NCRBUTTONDOWN: + case WM_NCRBUTTONUP: + case WM_NCRBUTTONDBLCLK: + case WM_NCMBUTTONDOWN: + case WM_NCMBUTTONUP: + case WM_NCMBUTTONDBLCLK: + case WM_MOUSEWHEEL: + msg.hwnd = m_popup; + break; + + // These mouse messages use client coordinates so we need to convert them. + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_LBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_RBUTTONDBLCLK: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MBUTTONDBLCLK: { + // Translate the coordinate. + translatePoint(msg.lParam, msg.hwnd, m_popup); + msg.hwnd = m_popup; + break; + } + + // Steal all keyboard messages. + case WM_KEYDOWN: + case WM_KEYUP: + case WM_CHAR: + case WM_DEADCHAR: + case WM_SYSKEYUP: + case WM_SYSCHAR: + case WM_SYSDEADCHAR: + msg.hwnd = m_popup; + break; + } + + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + + if (!m_showPopup) + break; + activeWindow = ::GetActiveWindow(); + if (activeWindow != hostWindow && !::IsChild(activeWindow, hostWindow)) + break; + if (::GetCapture() != hostWindow) + break; + } + + if (::GetCapture() == hostWindow) + ::ReleaseCapture(); + + m_showPopup = false; + ::ShowWindow(m_popup, SW_HIDE); + + m_client->valueChangedForPopupMenu(this, m_newSelectedIndex); +} + +void WebPopupMenuProxyWin::hidePopupMenu() +{ + if (!m_showPopup) + return; + m_showPopup = false; + + ::ShowWindow(m_popup, SW_HIDE); + + // Post a WM_NULL message to wake up the message pump if necessary. + ::PostMessage(m_popup, WM_NULL, 0, 0); +} + +void WebPopupMenuProxyWin::calculatePositionAndSize(const IntRect& rect) +{ + // Convert the rect (which is in view cooridates) into screen coordinates. + IntRect rectInScreenCoords = rect; + POINT location(rectInScreenCoords .location()); + if (!::ClientToScreen(m_webView->window(), &location)) + return; + rectInScreenCoords.setLocation(location); + + int itemCount = m_items.size(); + m_itemHeight = m_data.m_itemHeight; + + int naturalHeight = m_itemHeight * itemCount; + int popupHeight = min(maxPopupHeight, naturalHeight); + + // The popup should show an integral number of items (i.e. no partial items should be visible) + popupHeight -= popupHeight % m_itemHeight; + + // Next determine its width + int popupWidth = m_data.m_popupWidth; + + if (naturalHeight > maxPopupHeight) { + // We need room for a scrollbar + popupWidth += ScrollbarTheme::nativeTheme()->scrollbarThickness(SmallScrollbar); + } + + popupHeight += 2 * popupWindowBorderWidth; + + // The popup should be at least as wide as the control on the page + popupWidth = max(rectInScreenCoords.width() - m_data.m_clientInsetLeft - m_data.m_clientInsetRight, popupWidth); + + // Always left-align items in the popup. This matches popup menus on the mac. + int popupX = rectInScreenCoords.x() + m_data.m_clientInsetLeft; + + IntRect popupRect(popupX, rectInScreenCoords.bottom(), popupWidth, popupHeight); + + // The popup needs to stay within the bounds of the screen and not overlap any toolbars + HMONITOR monitor = ::MonitorFromWindow(m_webView->window(), MONITOR_DEFAULTTOPRIMARY); + MONITORINFOEX monitorInfo; + monitorInfo.cbSize = sizeof(MONITORINFOEX); + ::GetMonitorInfo(monitor, &monitorInfo); + FloatRect screen = monitorInfo.rcWork; + + // Check that we don't go off the screen vertically + if (popupRect.bottom() > screen.height()) { + // The popup will go off the screen, so try placing it above the client + if (rectInScreenCoords.y() - popupRect.height() < 0) { + // The popup won't fit above, either, so place it whereever's bigger and resize it to fit + if ((rectInScreenCoords.y() + rectInScreenCoords.height() / 2) < (screen.height() / 2)) { + // Below is bigger + popupRect.setHeight(screen.height() - popupRect.y()); + } else { + // Above is bigger + popupRect.setY(0); + popupRect.setHeight(rectInScreenCoords.y()); + } + } else { + // The popup fits above, so reposition it + popupRect.setY(rectInScreenCoords.y() - popupRect.height()); + } + } + + // Check that we don't go off the screen horizontally + if (popupRect.x() < screen.x()) { + popupRect.setWidth(popupRect.width() - (screen.x() - popupRect.x())); + popupRect.setX(screen.x()); + } + + m_windowRect = popupRect; +} + +IntRect WebPopupMenuProxyWin::clientRect() const +{ + IntRect clientRect = m_windowRect; + clientRect.inflate(-popupWindowBorderWidth); + clientRect.setLocation(IntPoint(0, 0)); + return clientRect; +} + +void WebPopupMenuProxyWin::invalidateItem(int index) +{ + if (!m_popup) + return; + + IntRect damageRect(clientRect()); + damageRect.setY(m_itemHeight * (index - m_scrollOffset)); + damageRect.setHeight(m_itemHeight); + if (m_scrollbar) + damageRect.setWidth(damageRect.width() - m_scrollbar->frameRect().width()); + + RECT r = damageRect; + ::InvalidateRect(m_popup, &r, TRUE); +} + +// ScrollbarClient + +int WebPopupMenuProxyWin::scrollSize(ScrollbarOrientation orientation) const +{ + return ((orientation == VerticalScrollbar) && m_scrollbar) ? (m_scrollbar->totalSize() - m_scrollbar->visibleSize()) : 0; +} + +void WebPopupMenuProxyWin::setScrollOffsetFromAnimation(const IntPoint& offset) +{ + if (m_scrollbar) + m_scrollbar->setValue(offset.y(), Scrollbar::FromScrollAnimator); +} + +void WebPopupMenuProxyWin::valueChanged(Scrollbar* scrollBar) +{ + ASSERT(m_scrollbar); + + if (!m_popup) + return; + + int offset = scrollBar->value(); + + if (m_scrollOffset == offset) + return; + + int scrolledLines = m_scrollOffset - offset; + m_scrollOffset = offset; + + UINT flags = SW_INVALIDATE; + +#ifdef CAN_SET_SMOOTH_SCROLLING_DURATION + BOOL shouldSmoothScroll = FALSE; + ::SystemParametersInfo(SPI_GETLISTBOXSMOOTHSCROLLING, 0, &shouldSmoothScroll, 0); + if (shouldSmoothScroll) + flags |= MAKEWORD(SW_SMOOTHSCROLL, smoothScrollAnimationDuration); +#endif + + IntRect listRect = clientRect(); + if (m_scrollbar) + listRect.setWidth(listRect.width() - m_scrollbar->frameRect().width()); + RECT r = listRect; + ::ScrollWindowEx(m_popup, 0, scrolledLines * m_itemHeight, &r, 0, 0, 0, flags); + if (m_scrollbar) { + r = m_scrollbar->frameRect(); + ::InvalidateRect(m_popup, &r, TRUE); + } + ::UpdateWindow(m_popup); +} + +void WebPopupMenuProxyWin::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect) +{ + IntRect scrollRect = rect; + scrollRect.move(scrollbar->x(), scrollbar->y()); + RECT r = scrollRect; + ::InvalidateRect(m_popup, &r, false); +} + +// Message pump messages. + +LRESULT WebPopupMenuProxyWin::onMouseActivate(HWND hWnd, UINT message, WPARAM, LPARAM, bool& handled) +{ + handled = true; + return MA_NOACTIVATE; +} + +LRESULT WebPopupMenuProxyWin::onSize(HWND hWnd, UINT message, WPARAM, LPARAM lParam, bool& handled) +{ + handled = true; + if (!scrollbar()) + return 0; + + IntSize size(LOWORD(lParam), HIWORD(lParam)); + scrollbar()->setFrameRect(IntRect(size.width() - scrollbar()->width(), 0, scrollbar()->width(), size.height())); + + int visibleItems = this->visibleItems(); + scrollbar()->setEnabled(visibleItems < m_items.size()); + scrollbar()->setSteps(1, max(1, visibleItems - 1)); + scrollbar()->setProportion(visibleItems, m_items.size()); + return 0; +} + +LRESULT WebPopupMenuProxyWin::onKeyDown(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) +{ + handled = true; + + LRESULT lResult = 0; + switch (LOWORD(wParam)) { + case VK_DOWN: + case VK_RIGHT: + down(); + break; + case VK_UP: + case VK_LEFT: + up(); + break; + case VK_HOME: + focusFirst(); + break; + case VK_END: + focusLast(); + break; + case VK_PRIOR: + if (focusedIndex() != scrollOffset()) { + // Set the selection to the first visible item + int firstVisibleItem = scrollOffset(); + up(focusedIndex() - firstVisibleItem); + } else { + // The first visible item is selected, so move the selection back one page + up(visibleItems()); + } + break; + case VK_NEXT: { + int lastVisibleItem = scrollOffset() + visibleItems() - 1; + if (focusedIndex() != lastVisibleItem) { + // Set the selection to the last visible item + down(lastVisibleItem - focusedIndex()); + } else { + // The last visible item is selected, so move the selection forward one page + down(visibleItems()); + } + break; + } + case VK_TAB: + ::SendMessage(m_webView->window(), message, wParam, lParam); + hide(); + break; + case VK_ESCAPE: + hide(); + break; + default: + if (isASCIIPrintable(wParam)) { + // Send the keydown to the WebView so it can be used for type-to-select. + // Since we know that the virtual key is ASCII printable, it's OK to convert this to + // a WM_CHAR message. (We don't want to call TranslateMessage because that will post a + // WM_CHAR message that will be stolen and redirected to the popup HWND. + ::PostMessage(m_popup, WM_HOST_WINDOW_CHAR, wParam, lParam); + } else + lResult = 1; + break; + } + + return lResult; +} + +LRESULT WebPopupMenuProxyWin::onChar(HWND hWnd, UINT message, WPARAM wParam, LPARAM, bool& handled) +{ + handled = true; + + LRESULT lResult = 0; + int index; + switch (wParam) { + case 0x0D: // Enter/Return + hide(); + index = focusedIndex(); + ASSERT(index >= 0); + // FIXME: Do we need to send back the index right away? + m_newSelectedIndex = index; + break; + case 0x1B: // Escape + hide(); + break; + case 0x09: // TAB + case 0x08: // Backspace + case 0x0A: // Linefeed + default: // Character + lResult = 1; + break; + } + + return lResult; +} + +LRESULT WebPopupMenuProxyWin::onMouseMove(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) +{ + handled = true; + + IntPoint mousePoint(MAKEPOINTS(lParam)); + if (scrollbar()) { + IntRect scrollBarRect = scrollbar()->frameRect(); + if (scrollbarCapturingMouse() || scrollBarRect.contains(mousePoint)) { + // Put the point into coordinates relative to the scroll bar + mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y()); + PlatformMouseEvent event(hWnd, message, wParam, MAKELPARAM(mousePoint.x(), mousePoint.y())); + scrollbar()->mouseMoved(event); + return 0; + } + } + + BOOL shouldHotTrack = FALSE; + ::SystemParametersInfo(SPI_GETHOTTRACKING, 0, &shouldHotTrack, 0); + + RECT bounds; + ::GetClientRect(m_popup, &bounds); + if (!::PtInRect(&bounds, mousePoint) && !(wParam & MK_LBUTTON)) { + // When the mouse is not inside the popup menu and the left button isn't down, just + // repost the message to the web view. + + // Translate the coordinate. + translatePoint(lParam, m_popup, m_webView->window()); + + ::PostMessage(m_popup, WM_HOST_WINDOW_MOUSEMOVE, wParam, lParam); + return 0; + } + + if ((shouldHotTrack || wParam & MK_LBUTTON) && ::PtInRect(&bounds, mousePoint)) + setFocusedIndex(listIndexAtPoint(mousePoint), true); + + return 0; +} + +LRESULT WebPopupMenuProxyWin::onLButtonDown(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) +{ + handled = true; + + IntPoint mousePoint(MAKEPOINTS(lParam)); + if (scrollbar()) { + IntRect scrollBarRect = scrollbar()->frameRect(); + if (scrollBarRect.contains(mousePoint)) { + // Put the point into coordinates relative to the scroll bar + mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y()); + PlatformMouseEvent event(hWnd, message, wParam, MAKELPARAM(mousePoint.x(), mousePoint.y())); + scrollbar()->mouseDown(event); + setScrollbarCapturingMouse(true); + return 0; + } + } + + // If the mouse is inside the window, update the focused index. Otherwise, + // hide the popup. + RECT bounds; + ::GetClientRect(m_popup, &bounds); + if (::PtInRect(&bounds, mousePoint)) + setFocusedIndex(listIndexAtPoint(mousePoint), true); + else + hide(); + + return 0; +} + + +LRESULT WebPopupMenuProxyWin::onLButtonUp(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool& handled) +{ + handled = true; + + IntPoint mousePoint(MAKEPOINTS(lParam)); + if (scrollbar()) { + IntRect scrollBarRect = scrollbar()->frameRect(); + if (scrollbarCapturingMouse() || scrollBarRect.contains(mousePoint)) { + setScrollbarCapturingMouse(false); + // Put the point into coordinates relative to the scroll bar + mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y()); + PlatformMouseEvent event(hWnd, message, wParam, MAKELPARAM(mousePoint.x(), mousePoint.y())); + scrollbar()->mouseUp(); + // FIXME: This is a hack to work around Scrollbar not invalidating correctly when it doesn't have a parent widget + RECT r = scrollBarRect; + ::InvalidateRect(m_popup, &r, TRUE); + return 0; + } + } + // Only hide the popup if the mouse is inside the popup window. + RECT bounds; + ::GetClientRect(m_popup, &bounds); + if (::PtInRect(&bounds, mousePoint)) { + hide(); + int index = focusedIndex(); + if (index >= 0) { + // FIXME: Do we need to send back the index right away? + m_newSelectedIndex = index; + } + } + + return 0; +} + +LRESULT WebPopupMenuProxyWin::onMouseWheel(HWND hWnd, UINT message, WPARAM wParam, LPARAM, bool& handled) +{ + handled = true; + + if (!scrollbar()) + return 0; + + int i = 0; + for (incrementWheelDelta(GET_WHEEL_DELTA_WPARAM(wParam)); abs(wheelDelta()) >= WHEEL_DELTA; reduceWheelDelta(WHEEL_DELTA)) { + if (wheelDelta() > 0) + ++i; + else + --i; + } + scrollbar()->scroll(i > 0 ? ScrollUp : ScrollDown, ScrollByLine, abs(i)); + return 0; +} + +LRESULT WebPopupMenuProxyWin::onPaint(HWND hWnd, UINT message, WPARAM, LPARAM, bool& handled) +{ + handled = true; + + PAINTSTRUCT paintStruct; + ::BeginPaint(m_popup, &paintStruct); + paint(paintStruct.rcPaint, paintStruct.hdc); + ::EndPaint(m_popup, &paintStruct); + + return 0; +} + +LRESULT WebPopupMenuProxyWin::onPrintClient(HWND hWnd, UINT, WPARAM wParam, LPARAM, bool& handled) +{ + handled = true; + + HDC hdc = reinterpret_cast<HDC>(wParam); + paint(clientRect(), hdc); + + return 0; +} + +bool WebPopupMenuProxyWin::down(unsigned lines) +{ + int size = m_items.size(); + + int lastSelectableIndex, selectedListIndex; + lastSelectableIndex = selectedListIndex = focusedIndex(); + for (int i = selectedListIndex + 1; i >= 0 && i < size; ++i) { + if (m_items[i].m_isEnabled) { + lastSelectableIndex = i; + if (i >= selectedListIndex + (int)lines) + break; + } + } + + return setFocusedIndex(lastSelectableIndex); +} + +bool WebPopupMenuProxyWin::up(unsigned lines) +{ + int size = m_items.size(); + + int lastSelectableIndex, selectedListIndex; + lastSelectableIndex = selectedListIndex = focusedIndex(); + for (int i = selectedListIndex - 1; i >= 0 && i < size; --i) { + if (m_items[i].m_isEnabled) { + lastSelectableIndex = i; + if (i <= selectedListIndex - (int)lines) + break; + } + } + + return setFocusedIndex(lastSelectableIndex); +} + +void WebPopupMenuProxyWin::paint(const IntRect& damageRect, HDC hdc) +{ + if (!m_popup) + return; + + if (!m_DC) { + m_DC = ::CreateCompatibleDC(::GetDC(m_popup)); + if (!m_DC) + return; + } + + if (m_bmp) { + bool keepBitmap = false; + BITMAP bitmap; + if (::GetObject(m_bmp, sizeof(bitmap), &bitmap)) + keepBitmap = bitmap.bmWidth == clientRect().width() && bitmap.bmHeight == clientRect().height(); + if (!keepBitmap) { + ::DeleteObject(m_bmp); + m_bmp = 0; + } + } + + if (!m_bmp) { + BitmapInfo bitmapInfo = BitmapInfo::createBottomUp(clientRect().size()); + void* pixels = 0; + m_bmp = ::CreateDIBSection(m_DC, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0); + if (!m_bmp) + return; + ::SelectObject(m_DC, m_bmp); + } + + GraphicsContext context(m_DC); + + IntRect translatedDamageRect = damageRect; + translatedDamageRect.move(IntSize(0, m_scrollOffset * m_itemHeight)); + m_data.m_notSelectedBackingStore->paint(context, damageRect.location(), translatedDamageRect); + + IntRect selectedIndexRectInBackingStore(0, focusedIndex() * m_itemHeight, m_data.m_selectedBackingStore->size().width(), m_itemHeight); + IntPoint selectedIndexDstPoint = selectedIndexRectInBackingStore.location(); + selectedIndexDstPoint.move(0, -m_scrollOffset * m_itemHeight); + + m_data.m_selectedBackingStore->paint(context, selectedIndexDstPoint, selectedIndexRectInBackingStore); + + if (m_scrollbar) + m_scrollbar->paint(&context, damageRect); + + HDC localDC = hdc ? hdc : ::GetDC(m_popup); + + ::BitBlt(localDC, damageRect.x(), damageRect.y(), damageRect.width(), damageRect.height(), m_DC, damageRect.x(), damageRect.y(), SRCCOPY); + + if (!hdc) + ::ReleaseDC(m_popup, localDC); +} + +bool WebPopupMenuProxyWin::setFocusedIndex(int i, bool hotTracking) +{ + if (i < 0 || i >= m_items.size() || i == focusedIndex()) + return false; + + if (!m_items[i].m_isEnabled) + return false; + + invalidateItem(focusedIndex()); + invalidateItem(i); + + m_focusedIndex = i; + + if (!hotTracking) + m_client->setTextFromItemForPopupMenu(this, i); + + if (!scrollToRevealSelection()) + ::UpdateWindow(m_popup); + + return true; +} + +int WebPopupMenuProxyWin::visibleItems() const +{ + return clientRect().height() / m_itemHeight; +} + +int WebPopupMenuProxyWin::listIndexAtPoint(const IntPoint& point) const +{ + return m_scrollOffset + point.y() / m_itemHeight; +} + +int WebPopupMenuProxyWin::focusedIndex() const +{ + return m_focusedIndex; +} + +void WebPopupMenuProxyWin::focusFirst() +{ + int size = m_items.size(); + + for (int i = 0; i < size; ++i) { + if (m_items[i].m_isEnabled) { + setFocusedIndex(i); + break; + } + } +} + +void WebPopupMenuProxyWin::focusLast() +{ + int size = m_items.size(); + + for (int i = size - 1; i > 0; --i) { + if (m_items[i].m_isEnabled) { + setFocusedIndex(i); + break; + } + } +} + + +void WebPopupMenuProxyWin::incrementWheelDelta(int delta) +{ + m_wheelDelta += delta; +} + +void WebPopupMenuProxyWin::reduceWheelDelta(int delta) +{ + ASSERT(delta >= 0); + ASSERT(delta <= abs(m_wheelDelta)); + + if (m_wheelDelta > 0) + m_wheelDelta -= delta; + else if (m_wheelDelta < 0) + m_wheelDelta += delta; + else + return; +} + +bool WebPopupMenuProxyWin::scrollToRevealSelection() +{ + if (!m_scrollbar) + return false; + + int index = focusedIndex(); + + if (index < m_scrollOffset) { + m_scrollbar->setValue(index, Scrollbar::NotFromScrollAnimator); + return true; + } + + if (index >= m_scrollOffset + visibleItems()) { + m_scrollbar->setValue(index - visibleItems() + 1, Scrollbar::NotFromScrollAnimator); + return true; + } + + return false; +} + +} // namespace WebKit diff --git a/Source/WebKit2/UIProcess/win/WebPopupMenuProxyWin.h b/Source/WebKit2/UIProcess/win/WebPopupMenuProxyWin.h new file mode 100644 index 0000000..7a53c2c --- /dev/null +++ b/Source/WebKit2/UIProcess/win/WebPopupMenuProxyWin.h @@ -0,0 +1,136 @@ +/* + * 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. + */ + +#ifndef WebPopupMenuProxyWin_h +#define WebPopupMenuProxyWin_h + +#include "PlatformPopupMenuData.h" +#include "WebPopupItem.h" +#include "WebPopupMenuProxy.h" +#include <WebCore/Scrollbar.h> +#include <WebCore/ScrollbarClient.h> + +typedef struct HWND__* HWND; +typedef struct HDC__* HDC; +typedef struct HBITMAP__* HBITMAP; + +namespace WebKit { + +class WebView; + +class WebPopupMenuProxyWin : public WebPopupMenuProxy, private WebCore::ScrollbarClient { +public: + static PassRefPtr<WebPopupMenuProxyWin> create(WebView* webView, WebPopupMenuProxy::Client* client) + { + return adoptRef(new WebPopupMenuProxyWin(webView, client)); + } + ~WebPopupMenuProxyWin(); + + virtual void showPopupMenu(const WebCore::IntRect&, const Vector<WebPopupItem>&, const PlatformPopupMenuData&, int32_t selectedIndex); + virtual void hidePopupMenu(); + + void hide() { hidePopupMenu(); } + +private: + WebPopupMenuProxyWin(WebView*, WebPopupMenuProxy::Client*); + + WebCore::Scrollbar* scrollbar() const { return m_scrollbar.get(); } + + // ScrollBarClient + virtual int scrollSize(WebCore::ScrollbarOrientation orientation) const; + virtual void setScrollOffsetFromAnimation(const WebCore::IntPoint&); + virtual void valueChanged(WebCore::Scrollbar*); + virtual void invalidateScrollbarRect(WebCore::Scrollbar*, const WebCore::IntRect&); + virtual bool isActive() const { return true; } + virtual bool scrollbarCornerPresent() const { return false; } + + static bool registerWindowClass(); + static LRESULT CALLBACK WebPopupMenuProxyWndProc(HWND, UINT, WPARAM, LPARAM); + LRESULT wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + + // Message pump messages. + LRESULT onMouseActivate(HWND, UINT message, WPARAM, LPARAM, bool& handled); + LRESULT onSize(HWND, UINT message, WPARAM, LPARAM, bool& handled); + LRESULT onKeyDown(HWND, UINT message, WPARAM, LPARAM, bool& handled); + LRESULT onChar(HWND, UINT message, WPARAM, LPARAM, bool& handled); + LRESULT onMouseMove(HWND, UINT message, WPARAM, LPARAM, bool& handled); + LRESULT onLButtonDown(HWND, UINT message, WPARAM, LPARAM, bool& handled); + LRESULT onLButtonUp(HWND, UINT message, WPARAM, LPARAM, bool& handled); + LRESULT onMouseWheel(HWND, UINT message, WPARAM, LPARAM, bool& handled); + LRESULT onPaint(HWND, UINT message, WPARAM, LPARAM, bool& handled); + LRESULT onPrintClient(HWND, UINT message, WPARAM, LPARAM, bool& handled); + + void calculatePositionAndSize(const WebCore::IntRect&); + WebCore::IntRect clientRect() const; + void invalidateItem(int index); + + int itemHeight() const { return m_itemHeight; } + const WebCore::IntRect& windowRect() const { return m_windowRect; } + int wheelDelta() const { return m_wheelDelta; } + void setWasClicked(bool b = true) { m_wasClicked = b; } + bool wasClicked() const { return m_wasClicked; } + void setScrollOffset(int offset) { m_scrollOffset = offset; } + int scrollOffset() const { return m_scrollOffset; } + bool scrollbarCapturingMouse() const { return m_scrollbarCapturingMouse; } + void setScrollbarCapturingMouse(bool b) { m_scrollbarCapturingMouse = b; } + + + bool up(unsigned lines = 1); + bool down(unsigned lines = 1); + + void paint(const WebCore::IntRect& damageRect, HDC = 0); + int visibleItems() const; + int listIndexAtPoint(const WebCore::IntPoint&) const; + bool setFocusedIndex(int index, bool hotTracking = false); + int focusedIndex() const; + void focusFirst(); + void focusLast(); + bool scrollToRevealSelection(); + void incrementWheelDelta(int delta); + void reduceWheelDelta(int delta); + + WebView* m_webView; + Vector<WebPopupItem> m_items; + PlatformPopupMenuData m_data; + int m_newSelectedIndex; + + RefPtr<WebCore::Scrollbar> m_scrollbar; + HWND m_popup; + HDC m_DC; + HBITMAP m_bmp; + WebCore::IntRect m_windowRect; + + int m_itemHeight; + int m_scrollOffset; + int m_wheelDelta; + int m_focusedIndex; + bool m_wasClicked; + bool m_scrollbarCapturingMouse; + bool m_showPopup; +}; + +} // namespace WebKit + +#endif // WebPopupMenuProxyWin_h diff --git a/Source/WebKit2/UIProcess/win/WebView.cpp b/Source/WebKit2/UIProcess/win/WebView.cpp new file mode 100644 index 0000000..4b3992c --- /dev/null +++ b/Source/WebKit2/UIProcess/win/WebView.cpp @@ -0,0 +1,1085 @@ +/* + * 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 "FindIndicator.h" +#include "LayerBackedDrawingAreaProxy.h" +#include "Logging.h" +#include "RunLoop.h" +#include "NativeWebKeyboardEvent.h" +#include "WebContext.h" +#include "WebContextMenuProxyWin.h" +#include "WebEditCommandProxy.h" +#include "WebEventFactory.h" +#include "WebPageProxy.h" +#include "WebPopupMenuProxyWin.h" +#include <Commctrl.h> +#include <WebCore/Cursor.h> +#include <WebCore/FloatRect.h> +#include <WebCore/IntRect.h> +#include <WebCore/SoftLinking.h> +#include <WebCore/WebCoreInstanceHandle.h> +#include <WebCore/WindowMessageBroadcaster.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)) +}; + +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_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_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) +{ + registerWebViewWindowClass(); + + m_page = context->createWebPage(this, pageGroup); + + m_window = ::CreateWindowEx(0, kWebKit2WebViewWindowClassName, 0, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + rect.top, rect.left, rect.right - rect.left, rect.bottom - rect.top, parentWindow ? parentWindow : HWND_MESSAGE, 0, instanceHandle(), this); + ASSERT(::IsWindow(m_window)); + + m_page->initializeWebPage(); + + ::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::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) +{ + WebMouseEvent mouseEvent = WebEventFactory::createWebMouseEvent(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::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; +} + +LRESULT WebView::onPaintEvent(HWND hWnd, UINT message, WPARAM, LPARAM, bool& handled) +{ + PAINTSTRUCT paintStruct; + HDC hdc = ::BeginPaint(m_window, &paintStruct); + + if (m_page->isValid() && m_page->drawingArea() && m_page->drawingArea()->paint(IntRect(paintStruct.rcPaint), hdc)) + m_page->didDraw(); + else { + // Mac checks WebPageProxy::drawsBackground and + // WebPageProxy::drawsTransparentBackground here, but those are always false on Windows + // currently (see <http://webkit.org/b/52009>). + ::FillRect(hdc, &paintStruct.rcPaint, reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1)); + } + + ::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); + IntRect rect = winRect; + + m_page->drawingArea()->paint(rect, hdc); + + 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->drawingArea()) + 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->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: Is WM_SHOWWINDOW sent when ShowWindow is called on an ancestor of our window? + if (!lParam) { + m_isVisible = wParam; + m_page->viewStateDidChange(WebPageProxy::ViewIsVisible); + + handled = true; + } + + 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); +} + +void WebView::close() +{ + setParentWindow(0); + m_page->close(); +} + +// PageClient + +PassOwnPtr<DrawingAreaProxy> WebView::createDrawingAreaProxy() +{ + 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); +} + +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::processDidCrash() +{ + updateNativeCursor(); + ::InvalidateRect(m_window, 0, TRUE); +} + +void WebView::didRelaunchProcess() +{ + updateNativeCursor(); + ::InvalidateRect(m_window, 0, TRUE); +} + +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); +} + +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::setViewportArguments(const WebCore::ViewportArguments&) +{ +} + +void WebView::registerEditCommand(PassRefPtr<WebEditCommandProxy>, WebPageProxy::UndoOrRedo) +{ +} + +void WebView::clearAllEditCommands() +{ +} + +void WebView::setEditCommandState(const String&, bool, int) +{ +} + +FloatRect WebView::convertToDeviceSpace(const FloatRect& 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.bottom(); + form.rcArea.top = caret.y(); + form.rcArea.bottom = caret.bottom(); + form.rcArea.left = caret.x(); + form.rcArea.right = caret.right(); + 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->selectionState().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->selectionState().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->selectionState().hasComposition) + m_page->confirmComposition(String()); + + if (m_inIMEComposition) + m_inIMEComposition--; + + return true; +} + +LRESULT WebView::onIMERequestCharPosition(IMECHARPOSITION* charPos) +{ + if (charPos->dwCharPos && !m_page->selectionState().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->selectionState().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::didNotHandleKeyEvent(const NativeWebKeyboardEvent& event) +{ + // Calling ::DefWindowProcW will ensure that pressing the Alt key will generate a WM_SYSCOMMAND + // event, e.g. See <http://webkit.org/b/47671>. + ::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>, bool fadeOut) +{ + // FIXME: Implement. +} + +void WebView::didCommitLoadForMainFrame(bool useCustomRepresentation) +{ +} + +void WebView::didFinishLoadingDataForCustomRepresentation(const CoreIPC::DataReference&) +{ +} + +double WebView::customRepresentationZoomFactor() +{ + return 1; +} + +void WebView::setCustomRepresentationZoomFactor(double) +{ +} + +void WebView::setIsInWindow(bool isInWindow) +{ + m_isInWindow = isInWindow; + m_page->viewStateDidChange(WebPageProxy::ViewIsInWindow); +} + +#if USE(ACCELERATED_COMPOSITING) + +void WebView::pageDidEnterAcceleratedCompositing() +{ + switchToDrawingAreaTypeIfNecessary(DrawingAreaInfo::LayerBacked); +} + +void WebView::pageDidLeaveAcceleratedCompositing() +{ + switchToDrawingAreaTypeIfNecessary(DrawingAreaInfo::ChunkedUpdate); +} + +void WebView::switchToDrawingAreaTypeIfNecessary(DrawingAreaInfo::Type type) +{ + DrawingAreaInfo::Type existingDrawingAreaType = m_page->drawingArea() ? m_page->drawingArea()->info().type : DrawingAreaInfo::None; + if (existingDrawingAreaType == type) + return; + + OwnPtr<DrawingAreaProxy> newDrawingArea; + switch (type) { + case DrawingAreaInfo::None: + break; + case DrawingAreaInfo::ChunkedUpdate: + newDrawingArea = ChunkedUpdateDrawingAreaProxy::create(this, m_page.get()); + break; + case DrawingAreaInfo::LayerBacked: + newDrawingArea = LayerBackedDrawingAreaProxy::create(this, m_page.get()); + break; + } + + if (m_page->drawingArea()) + newDrawingArea->setSize(m_page->drawingArea()->size()); + + m_page->drawingArea()->detachCompositingContext(); + m_page->setDrawingArea(newDrawingArea.release()); +} + +#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; + } +} + +} // namespace WebKit diff --git a/Source/WebKit2/UIProcess/win/WebView.h b/Source/WebKit2/UIProcess/win/WebView.h new file mode 100644 index 0000000..c80334e --- /dev/null +++ b/Source/WebKit2/UIProcess/win/WebView.h @@ -0,0 +1,170 @@ +/* + * 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. + */ + +#ifndef WebView_h +#define WebView_h + +#include "APIObject.h" +#include "PageClient.h" +#include "WebPageProxy.h" +#include <WebCore/WindowMessageListener.h> +#include <wtf/Forward.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> + +namespace WebKit { + +class DrawingAreaProxy; + +class WebView : public APIObject, public PageClient, WebCore::WindowMessageListener { +public: + static PassRefPtr<WebView> create(RECT rect, WebContext* context, WebPageGroup* pageGroup, HWND parentWindow) + { + return adoptRef(new WebView(rect, context, pageGroup, parentWindow)); + } + ~WebView(); + + HWND window() const { return m_window; } + void setParentWindow(HWND); + void windowAncestryDidChange(); + void setIsInWindow(bool); + void setOverrideCursor(HCURSOR overrideCursor); + void setInitialFocus(bool forward); + + WebPageProxy* page() const { return m_page.get(); } + +private: + WebView(RECT, WebContext*, WebPageGroup*, HWND parentWindow); + + virtual Type type() const { return TypeView; } + + static bool registerWebViewWindowClass(); + static LRESULT CALLBACK WebViewWndProc(HWND, UINT, WPARAM, LPARAM); + LRESULT wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + + void setWasActivatedByMouseEvent(bool flag) { m_wasActivatedByMouseEvent = flag; } + LRESULT onMouseEvent(HWND hWnd, UINT message, WPARAM, LPARAM, bool& handled); + LRESULT onWheelEvent(HWND hWnd, UINT message, WPARAM, LPARAM, bool& handled); + LRESULT onKeyEvent(HWND hWnd, UINT message, WPARAM, LPARAM, bool& handled); + LRESULT onPaintEvent(HWND hWnd, UINT message, WPARAM, LPARAM, bool& handled); + LRESULT onPrintClientEvent(HWND hWnd, UINT message, WPARAM, LPARAM, bool& handled); + LRESULT onSizeEvent(HWND hWnd, UINT message, WPARAM, LPARAM, bool& handled); + LRESULT onWindowPositionChangedEvent(HWND hWnd, UINT message, WPARAM, LPARAM, bool& handled); + LRESULT onSetFocusEvent(HWND hWnd, UINT message, WPARAM, LPARAM, bool& handled); + LRESULT onKillFocusEvent(HWND hWnd, UINT message, WPARAM, LPARAM, bool& handled); + LRESULT onTimerEvent(HWND hWnd, UINT message, WPARAM, LPARAM, bool& handled); + LRESULT onShowWindowEvent(HWND hWnd, UINT message, WPARAM, LPARAM, bool& handled); + LRESULT onSetCursor(HWND hWnd, UINT message, WPARAM, LPARAM, bool& handled); + bool onIMEStartComposition(); + bool onIMEComposition(LPARAM); + bool onIMEEndComposition(); + LRESULT onIMERequest(WPARAM, LPARAM); + bool onIMESelect(WPARAM, LPARAM); + bool onIMESetContext(WPARAM, LPARAM); + void resetIME(); + void setInputMethodState(bool); + HIMC getIMMContext(); + void prepareCandidateWindow(HIMC); + LRESULT onIMERequestCharPosition(IMECHARPOSITION*); + LRESULT onIMERequestReconvertString(RECONVERTSTRING*); + + void updateActiveState(); + void updateActiveStateSoon(); + + void initializeToolTipWindow(); + + void startTrackingMouseLeave(); + void stopTrackingMouseLeave(); + + void close(); + + HCURSOR cursorToShow() const; + void updateNativeCursor(); + + // PageClient + virtual PassOwnPtr<DrawingAreaProxy> createDrawingAreaProxy(); + virtual void setViewNeedsDisplay(const WebCore::IntRect&); + virtual void displayView(); + + virtual WebCore::IntSize viewSize(); + virtual bool isViewWindowActive(); + virtual bool isViewFocused(); + virtual bool isViewVisible(); + virtual bool isViewInWindow(); + virtual void processDidCrash(); + virtual void didRelaunchProcess(); + virtual void takeFocus(bool direction); + virtual void toolTipChanged(const WTF::String&, const WTF::String&); + virtual void setCursor(const WebCore::Cursor&); + virtual void setViewportArguments(const WebCore::ViewportArguments&); + virtual void registerEditCommand(PassRefPtr<WebEditCommandProxy>, WebPageProxy::UndoOrRedo); + virtual void clearAllEditCommands(); + virtual void setEditCommandState(const WTF::String&, bool, int); + virtual WebCore::FloatRect convertToDeviceSpace(const WebCore::FloatRect&); + virtual WebCore::FloatRect convertToUserSpace(const WebCore::FloatRect&); + virtual void didNotHandleKeyEvent(const NativeWebKeyboardEvent&); + virtual void compositionSelectionChanged(bool); + virtual PassRefPtr<WebPopupMenuProxy> createPopupMenuProxy(WebPageProxy*); + virtual PassRefPtr<WebContextMenuProxy> createContextMenuProxy(WebPageProxy*); + virtual void setFindIndicator(PassRefPtr<FindIndicator>, bool fadeOut); + +#if USE(ACCELERATED_COMPOSITING) + virtual void pageDidEnterAcceleratedCompositing(); + virtual void pageDidLeaveAcceleratedCompositing(); + void switchToDrawingAreaTypeIfNecessary(DrawingAreaInfo::Type); +#endif + + void didCommitLoadForMainFrame(bool useCustomRepresentation); + void didFinishLoadingDataForCustomRepresentation(const CoreIPC::DataReference&); + virtual double customRepresentationZoomFactor(); + virtual void setCustomRepresentationZoomFactor(double); + + virtual HWND nativeWindow(); + + // WebCore::WindowMessageListener + virtual void windowReceivedMessage(HWND, UINT message, WPARAM, LPARAM); + + HWND m_window; + HWND m_topLevelParentWindow; + HWND m_toolTipWindow; + + HCURSOR m_lastCursorSet; + HCURSOR m_webCoreCursor; + HCURSOR m_overrideCursor; + + bool m_isInWindow; + bool m_isVisible; + bool m_wasActivatedByMouseEvent; + bool m_trackingMouseLeave; + bool m_isBeingDestroyed; + + RefPtr<WebPageProxy> m_page; + + unsigned m_inIMEComposition; +}; + +} // namespace WebKit + +#endif // WebView_h |