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