summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/win/PopupMenuWin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/platform/win/PopupMenuWin.cpp')
-rw-r--r--Source/WebCore/platform/win/PopupMenuWin.cpp1422
1 files changed, 1422 insertions, 0 deletions
diff --git a/Source/WebCore/platform/win/PopupMenuWin.cpp b/Source/WebCore/platform/win/PopupMenuWin.cpp
new file mode 100644
index 000000000..6a0adbe85
--- /dev/null
+++ b/Source/WebCore/platform/win/PopupMenuWin.cpp
@@ -0,0 +1,1422 @@
+/*
+ * Copyright (C) 2006-2008, 2011, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2007-2009 Torch Mobile Inc.
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "PopupMenuWin.h"
+
+#include "BString.h"
+#include "BitmapInfo.h"
+#include "Document.h"
+#include "FloatRect.h"
+#include "Font.h"
+#include "FontSelector.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "HTMLNames.h"
+#include "HWndDC.h"
+#include "HostWindow.h"
+#include "LengthFunctions.h"
+#include "NotImplemented.h"
+#include "Page.h"
+#include "PlatformMouseEvent.h"
+#include "PlatformScreen.h"
+#include "RenderMenuList.h"
+#include "RenderTheme.h"
+#include "RenderView.h"
+#include "Scrollbar.h"
+#include "ScrollbarTheme.h"
+#include "ScrollbarThemeWin.h"
+#include "TextRun.h"
+#include "WebCoreInstanceHandle.h"
+#include <wtf/WindowsExtras.h>
+
+#include <windows.h>
+#include <windowsx.h>
+
+#define HIGH_BIT_MASK_SHORT 0x8000
+
+using std::min;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+// Default Window animation duration in milliseconds
+static const int defaultAnimationDuration = 200;
+// Maximum height of a popup window
+static const int maxPopupHeight = 320;
+
+const int optionSpacingMiddle = 1;
+const int popupWindowBorderWidth = 1;
+
+static LPCWSTR kPopupWindowClassName = L"PopupWindowClass";
+
+// 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;
+
+// FIXME: Remove this as soon as practical.
+static inline bool isASCIIPrintable(unsigned c)
+{
+ return c >= 0x20 && c <= 0x7E;
+}
+
+static void translatePoint(LPARAM& lParam, HWND from, HWND to)
+{
+ POINT pt;
+ pt.x = (short)GET_X_LPARAM(lParam);
+ pt.y = (short)GET_Y_LPARAM(lParam);
+ ::MapWindowPoints(from, to, &pt, 1);
+ lParam = MAKELPARAM(pt.x, pt.y);
+}
+
+static FloatRect monitorFromHwnd(HWND hwnd)
+{
+ HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
+ MONITORINFOEX monitorInfo;
+ monitorInfo.cbSize = sizeof(MONITORINFOEX);
+ GetMonitorInfo(monitor, &monitorInfo);
+ return monitorInfo.rcWork;
+}
+
+PopupMenuWin::PopupMenuWin(PopupMenuClient* client)
+ : m_popupClient(client)
+{
+}
+
+PopupMenuWin::~PopupMenuWin()
+{
+ if (m_popup)
+ ::DestroyWindow(m_popup);
+ if (m_scrollbar)
+ m_scrollbar->setParent(0);
+}
+
+void PopupMenuWin::disconnectClient()
+{
+ m_popupClient = 0;
+}
+
+LPCWSTR PopupMenuWin::popupClassName()
+{
+ return kPopupWindowClassName;
+}
+
+void PopupMenuWin::show(const IntRect& r, FrameView* view, int index)
+{
+ calculatePositionAndSize(r, view);
+ if (clientRect().isEmpty())
+ return;
+
+ HWND hostWindow = view->hostWindow()->platformPageClient();
+
+ if (!m_scrollbar && visibleItems() < client()->listSize()) {
+ // We need a scroll bar
+ m_scrollbar = client()->createScrollbar(*this, VerticalScrollbar, SmallScrollbar);
+ m_scrollbar->styleChanged();
+ }
+
+ // We need to reposition the popup window to its final coordinates.
+ // Before calling this, the popup hwnd is currently the size of and at the location of the menu list client so it needs to be updated.
+ ::MoveWindow(m_popup, m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height(), false);
+
+ // Determine whether we should animate our popups
+ // Note: Must use 'BOOL' and 'FALSE' instead of 'bool' and 'false' to avoid stack corruption with SystemParametersInfo
+ BOOL shouldAnimate = FALSE;
+
+ if (client()) {
+ int index = client()->selectedIndex();
+ if (index >= 0)
+ setFocusedIndex(index);
+ }
+
+ if (!::SystemParametersInfo(SPI_GETCOMBOBOXANIMATION, 0, &shouldAnimate, 0))
+ shouldAnimate = FALSE;
+
+ if (shouldAnimate) {
+ RECT viewRect = {0};
+ ::GetWindowRect(hostWindow, &viewRect);
+ if (!::IsRectEmpty(&viewRect))
+ ::AnimateWindow(m_popup, defaultAnimationDuration, AW_BLEND);
+ } else
+ ::ShowWindow(m_popup, SW_SHOWNOACTIVATE);
+
+ m_showPopup = true;
+
+ // Protect the popup menu in case its owner is destroyed while we're running the message pump.
+ RefPtr<PopupMenu> 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_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ case WM_SYSCHAR:
+ case WM_SYSDEADCHAR:
+ msg.hwnd = m_popup;
+ break;
+ }
+
+ ::TranslateMessage(&msg);
+ ::DispatchMessage(&msg);
+
+ if (!m_popupClient)
+ break;
+
+ if (!m_showPopup)
+ break;
+ activeWindow = ::GetActiveWindow();
+ if (activeWindow != hostWindow && !::IsChild(activeWindow, hostWindow))
+ break;
+ if (::GetCapture() != hostWindow)
+ break;
+ }
+
+ if (::GetCapture() == hostWindow)
+ ::ReleaseCapture();
+
+ // We're done, hide the popup if necessary.
+ hide();
+}
+
+void PopupMenuWin::hide()
+{
+ if (!m_showPopup)
+ return;
+
+ m_showPopup = false;
+
+ ::ShowWindow(m_popup, SW_HIDE);
+
+ if (client())
+ client()->popupDidHide();
+
+ // Post a WM_NULL message to wake up the message pump if necessary.
+ ::PostMessage(m_popup, WM_NULL, 0, 0);
+}
+
+// The screen that the popup is placed on should be whichever one the popup menu button lies on.
+// We fake an hwnd (here we use the popup's hwnd) on top of the button which we can then use to determine the screen.
+// We can then proceed with our final position/size calculations.
+void PopupMenuWin::calculatePositionAndSize(const IntRect& r, FrameView* v)
+{
+ // First get the screen coordinates of the popup menu client.
+ HWND hostWindow = v->hostWindow()->platformPageClient();
+ IntRect absoluteBounds = ((RenderMenuList*)m_popupClient)->absoluteBoundingBoxRect();
+ IntRect absoluteScreenCoords(v->contentsToWindow(absoluteBounds.location()), absoluteBounds.size());
+ POINT absoluteLocation(absoluteScreenCoords.location());
+ if (!::ClientToScreen(hostWindow, &absoluteLocation))
+ return;
+ absoluteScreenCoords.setLocation(absoluteLocation);
+
+ // Now set the popup menu's location temporarily to these coordinates so we can determine which screen the popup should lie on.
+ // We create or move m_popup as necessary.
+ if (!m_popup) {
+ registerClass();
+ DWORD exStyle = WS_EX_LTRREADING;
+ m_popup = ::CreateWindowExW(exStyle, kPopupWindowClassName, L"PopupMenu",
+ WS_POPUP | WS_BORDER,
+ absoluteScreenCoords.x(), absoluteScreenCoords.y(), absoluteScreenCoords.width(), absoluteScreenCoords.height(),
+ hostWindow, 0, WebCore::instanceHandle(), this);
+
+ if (!m_popup)
+ return;
+ } else
+ ::MoveWindow(m_popup, absoluteScreenCoords.x(), absoluteScreenCoords.y(), absoluteScreenCoords.width(), absoluteScreenCoords.height(), false);
+
+ FloatRect screen = monitorFromHwnd(m_popup);
+
+ // Now we determine the actual location and measurements of the popup itself.
+ // r is in absolute document coordinates, but we want to be in screen coordinates.
+
+ // First, move to WebView coordinates
+ IntRect rScreenCoords(v->contentsToWindow(r.location()), r.size());
+ if (Page* page = v->frame().page())
+ rScreenCoords.scale(page->deviceScaleFactor());
+
+ // Then, translate to screen coordinates
+ POINT location(rScreenCoords.location());
+ if (!::ClientToScreen(hostWindow, &location))
+ return;
+
+ rScreenCoords.setLocation(location);
+
+ // First, determine the popup's height
+ int itemCount = client()->listSize();
+ m_itemHeight = client()->menuStyle().font().fontMetrics().height() + optionSpacingMiddle;
+ int naturalHeight = m_itemHeight * itemCount;
+ int popupHeight = std::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 = 0;
+ for (int i = 0; i < itemCount; ++i) {
+ String text = client()->itemText(i);
+ if (text.isEmpty())
+ continue;
+
+ FontCascade itemFont = client()->menuStyle().font();
+ if (client()->itemIsLabel(i)) {
+ auto d = itemFont.fontDescription();
+ d.setWeight(d.bolderWeight());
+ itemFont = FontCascade(d, itemFont.letterSpacing(), itemFont.wordSpacing());
+ itemFont.update(m_popupClient->fontSelector());
+ }
+
+ popupWidth = std::max(popupWidth, static_cast<int>(ceilf(itemFont.width(TextRun(text)))));
+ }
+
+ if (naturalHeight > maxPopupHeight)
+ // We need room for a scrollbar
+ popupWidth += ScrollbarTheme::theme().scrollbarThickness(SmallScrollbar);
+
+ // Add padding to align the popup text with the <select> text
+ popupWidth += std::max<int>(0, client()->clientPaddingRight() - client()->clientInsetRight()) + std::max<int>(0, client()->clientPaddingLeft() - client()->clientInsetLeft());
+
+ // Leave room for the border
+ popupWidth += 2 * popupWindowBorderWidth;
+ popupHeight += 2 * popupWindowBorderWidth;
+
+ // The popup should be at least as wide as the control on the page
+ popupWidth = std::max(rScreenCoords.width() - client()->clientInsetLeft() - client()->clientInsetRight(), popupWidth);
+
+ // Always left-align items in the popup. This matches popup menus on the mac.
+ int popupX = rScreenCoords.x() + client()->clientInsetLeft();
+
+ IntRect popupRect(popupX, rScreenCoords.maxY(), popupWidth, popupHeight);
+
+ // Check that we don't go off the screen vertically
+ if (popupRect.maxY() > screen.height()) {
+ // The popup will go off the screen, so try placing it above the client
+ if (rScreenCoords.y() - popupRect.height() < 0) {
+ // The popup won't fit above, either, so place it whereever's bigger and resize it to fit
+ if ((rScreenCoords.y() + rScreenCoords.height() / 2) < (screen.height() / 2)) {
+ // Below is bigger
+ popupRect.setHeight(screen.height() - popupRect.y());
+ } else {
+ // Above is bigger
+ popupRect.setY(0);
+ popupRect.setHeight(rScreenCoords.y());
+ }
+ } else {
+ // The popup fits above, so reposition it
+ popupRect.setY(rScreenCoords.y() - popupRect.height());
+ }
+ }
+
+ // Check that we don't go off the screen horizontally
+ if (popupRect.x() + popupRect.width() > screen.width() + screen.x())
+ popupRect.setX(screen.x() + screen.width() - popupRect.width());
+ if (popupRect.x() < screen.x())
+ popupRect.setX(screen.x());
+
+ m_windowRect = popupRect;
+ return;
+}
+
+bool PopupMenuWin::setFocusedIndex(int i, bool hotTracking)
+{
+ if (i < 0 || i >= client()->listSize() || i == focusedIndex())
+ return false;
+
+ if (!client()->itemIsEnabled(i))
+ return false;
+
+ invalidateItem(focusedIndex());
+ invalidateItem(i);
+
+ m_focusedIndex = i;
+
+ if (!hotTracking)
+ client()->setTextFromItem(i);
+
+ if (!scrollToRevealSelection())
+ ::UpdateWindow(m_popup);
+
+ return true;
+}
+
+int PopupMenuWin::visibleItems() const
+{
+ return clientRect().height() / m_itemHeight;
+}
+
+int PopupMenuWin::listIndexAtPoint(const IntPoint& point) const
+{
+ return m_scrollOffset + point.y() / m_itemHeight;
+}
+
+int PopupMenuWin::focusedIndex() const
+{
+ return m_focusedIndex;
+}
+
+void PopupMenuWin::focusFirst()
+{
+ if (!client())
+ return;
+
+ int size = client()->listSize();
+
+ for (int i = 0; i < size; ++i)
+ if (client()->itemIsEnabled(i)) {
+ setFocusedIndex(i);
+ break;
+ }
+}
+
+void PopupMenuWin::focusLast()
+{
+ if (!client())
+ return;
+
+ int size = client()->listSize();
+
+ for (int i = size - 1; i > 0; --i)
+ if (client()->itemIsEnabled(i)) {
+ setFocusedIndex(i);
+ break;
+ }
+}
+
+bool PopupMenuWin::down(unsigned lines)
+{
+ if (!client())
+ return false;
+
+ int size = client()->listSize();
+
+ int lastSelectableIndex, selectedListIndex;
+ lastSelectableIndex = selectedListIndex = focusedIndex();
+ for (int i = selectedListIndex + 1; i >= 0 && i < size; ++i)
+ if (client()->itemIsEnabled(i)) {
+ lastSelectableIndex = i;
+ if (i >= selectedListIndex + (int)lines)
+ break;
+ }
+
+ return setFocusedIndex(lastSelectableIndex);
+}
+
+bool PopupMenuWin::up(unsigned lines)
+{
+ if (!client())
+ return false;
+
+ int size = client()->listSize();
+
+ int lastSelectableIndex, selectedListIndex;
+ lastSelectableIndex = selectedListIndex = focusedIndex();
+ for (int i = selectedListIndex - 1; i >= 0 && i < size; --i)
+ if (client()->itemIsEnabled(i)) {
+ lastSelectableIndex = i;
+ if (i <= selectedListIndex - (int)lines)
+ break;
+ }
+
+ return setFocusedIndex(lastSelectableIndex);
+}
+
+void PopupMenuWin::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);
+}
+
+IntRect PopupMenuWin::clientRect() const
+{
+ IntRect clientRect = m_windowRect;
+ clientRect.inflate(-popupWindowBorderWidth);
+ clientRect.setLocation(IntPoint(0, 0));
+ return clientRect;
+}
+
+void PopupMenuWin::incrementWheelDelta(int delta)
+{
+ m_wheelDelta += delta;
+}
+
+void PopupMenuWin::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 PopupMenuWin::scrollToRevealSelection()
+{
+ if (!m_scrollbar)
+ return false;
+
+ int index = focusedIndex();
+
+ if (index < m_scrollOffset) {
+ ScrollableArea::scrollToOffsetWithoutAnimation(VerticalScrollbar, index);
+ return true;
+ }
+
+ if (index >= m_scrollOffset + visibleItems()) {
+ ScrollableArea::scrollToOffsetWithoutAnimation(VerticalScrollbar, index - visibleItems() + 1);
+ return true;
+ }
+
+ return false;
+}
+
+void PopupMenuWin::updateFromElement()
+{
+ if (!m_popup)
+ return;
+
+ m_focusedIndex = client()->selectedIndex();
+
+ ::InvalidateRect(m_popup, 0, TRUE);
+ if (!scrollToRevealSelection())
+ ::UpdateWindow(m_popup);
+}
+
+const int separatorPadding = 4;
+const int separatorHeight = 1;
+void PopupMenuWin::paint(const IntRect& damageRect, HDC hdc)
+{
+ if (!m_popup)
+ return;
+
+ if (!m_DC) {
+ m_DC = adoptGDIObject(::CreateCompatibleDC(HWndDC(m_popup)));
+ if (!m_DC)
+ return;
+ }
+
+ if (m_bmp) {
+ bool keepBitmap = false;
+ BITMAP bitmap;
+ if (::GetObject(m_bmp.get(), sizeof(bitmap), &bitmap))
+ keepBitmap = bitmap.bmWidth == clientRect().width()
+ && bitmap.bmHeight == clientRect().height();
+ if (!keepBitmap)
+ m_bmp.clear();
+ }
+ if (!m_bmp) {
+ BitmapInfo bitmapInfo = BitmapInfo::createBottomUp(clientRect().size());
+ void* pixels = 0;
+ m_bmp = adoptGDIObject(::CreateDIBSection(m_DC.get(), &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0));
+ if (!m_bmp)
+ return;
+
+ ::SelectObject(m_DC.get(), m_bmp.get());
+ }
+
+ GraphicsContext context(m_DC.get());
+
+ int itemCount = client()->listSize();
+
+ // listRect is the damageRect translated into the coordinates of the entire menu list (which is itemCount * m_itemHeight pixels tall)
+ IntRect listRect = damageRect;
+ listRect.move(IntSize(0, m_scrollOffset * m_itemHeight));
+
+ for (int y = listRect.y(); y < listRect.maxY(); y += m_itemHeight) {
+ int index = y / m_itemHeight;
+
+ Color optionBackgroundColor, optionTextColor;
+ PopupMenuStyle itemStyle = client()->itemStyle(index);
+ if (index == focusedIndex()) {
+ optionBackgroundColor = RenderTheme::defaultTheme()->activeListBoxSelectionBackgroundColor();
+ optionTextColor = RenderTheme::defaultTheme()->activeListBoxSelectionForegroundColor();
+ } else {
+ optionBackgroundColor = itemStyle.backgroundColor();
+ optionTextColor = itemStyle.foregroundColor();
+ }
+
+ // itemRect is in client coordinates
+ IntRect itemRect(0, (index - m_scrollOffset) * m_itemHeight, damageRect.width(), m_itemHeight);
+
+ // Draw the background for this menu item
+ if (itemStyle.isVisible())
+ context.fillRect(itemRect, optionBackgroundColor);
+
+ if (client()->itemIsSeparator(index)) {
+ IntRect separatorRect(itemRect.x() + separatorPadding, itemRect.y() + (itemRect.height() - separatorHeight) / 2, itemRect.width() - 2 * separatorPadding, separatorHeight);
+ context.fillRect(separatorRect, optionTextColor);
+ continue;
+ }
+
+ String itemText = client()->itemText(index);
+
+ TextRun textRun(itemText, 0, 0, AllowTrailingExpansion, itemStyle.textDirection(), itemStyle.hasTextDirectionOverride());
+ context.setFillColor(optionTextColor);
+
+ FontCascade itemFont = client()->menuStyle().font();
+ if (client()->itemIsLabel(index)) {
+ auto d = itemFont.fontDescription();
+ d.setWeight(d.bolderWeight());
+ itemFont = FontCascade(d, itemFont.letterSpacing(), itemFont.wordSpacing());
+ itemFont.update(m_popupClient->fontSelector());
+ }
+
+ // Draw the item text
+ if (itemStyle.isVisible()) {
+ int textX = 0;
+ if (client()->menuStyle().textDirection() == LTR) {
+ textX = std::max<int>(0, client()->clientPaddingLeft() - client()->clientInsetLeft());
+ if (RenderTheme::defaultTheme()->popupOptionSupportsTextIndent())
+ textX += minimumIntValueForLength(itemStyle.textIndent(), itemRect.width());
+ } else {
+ textX = itemRect.width() - client()->menuStyle().font().width(textRun);
+ textX = std::min<int>(textX, textX - client()->clientPaddingRight() + client()->clientInsetRight());
+ if (RenderTheme::defaultTheme()->popupOptionSupportsTextIndent())
+ textX -= minimumIntValueForLength(itemStyle.textIndent(), itemRect.width());
+ }
+ int textY = itemRect.y() + itemFont.fontMetrics().ascent() + (itemRect.height() - itemFont.fontMetrics().height()) / 2;
+ context.drawBidiText(itemFont, textRun, IntPoint(textX, textY));
+ }
+ }
+
+ if (m_scrollbar)
+ m_scrollbar->paint(context, damageRect);
+
+ HWndDC hWndDC;
+ HDC localDC = hdc ? hdc : hWndDC.setHWnd(m_popup);
+
+ ::BitBlt(localDC, damageRect.x(), damageRect.y(), damageRect.width(), damageRect.height(), m_DC.get(), damageRect.x(), damageRect.y(), SRCCOPY);
+}
+
+int PopupMenuWin::scrollSize(ScrollbarOrientation orientation) const
+{
+ return ((orientation == VerticalScrollbar) && m_scrollbar) ? (m_scrollbar->totalSize() - m_scrollbar->visibleSize()) : 0;
+}
+
+int PopupMenuWin::scrollOffset(ScrollbarOrientation) const
+{
+ return m_scrollOffset;
+}
+
+void PopupMenuWin::setScrollOffset(const IntPoint& offset)
+{
+ scrollTo(offset.y());
+}
+
+void PopupMenuWin::scrollTo(int offset)
+{
+ ASSERT(m_scrollbar);
+
+ if (!m_popup)
+ return;
+
+ 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 PopupMenuWin::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
+{
+ IntRect scrollRect = rect;
+ scrollRect.move(scrollbar->x(), scrollbar->y());
+ RECT r = scrollRect;
+ ::InvalidateRect(m_popup, &r, false);
+}
+
+IntSize PopupMenuWin::visibleSize() const
+{
+ return IntSize(m_windowRect.width(), m_scrollbar ? m_scrollbar->visibleSize() : m_windowRect.height());
+}
+
+IntSize PopupMenuWin::contentsSize() const
+{
+ return m_windowRect.size();
+}
+
+IntRect PopupMenuWin::scrollableAreaBoundingBox(bool*) const
+{
+ return m_windowRect;
+}
+
+bool PopupMenuWin::onGetObject(WPARAM wParam, LPARAM lParam, LRESULT& lResult)
+{
+ lResult = 0;
+
+ if (static_cast<LONG>(lParam) != OBJID_CLIENT)
+ return false;
+
+ if (!m_accessiblePopupMenu)
+ m_accessiblePopupMenu = new AccessiblePopupMenu(*this);
+
+ static HMODULE accessibilityLib = nullptr;
+ if (!accessibilityLib) {
+ if (!(accessibilityLib = ::LoadLibraryW(L"oleacc.dll")))
+ return false;
+ }
+
+ static LPFNLRESULTFROMOBJECT procPtr = reinterpret_cast<LPFNLRESULTFROMOBJECT>(::GetProcAddress(accessibilityLib, "LresultFromObject"));
+ if (!procPtr)
+ return false;
+
+ // LresultFromObject returns a reference to the accessible object, stored
+ // in an LRESULT. If this call is not successful, Windows will handle the
+ // request through DefWindowProc.
+ return SUCCEEDED(lResult = procPtr(__uuidof(IAccessible), wParam, m_accessiblePopupMenu.get()));
+}
+
+void PopupMenuWin::registerClass()
+{
+ static bool haveRegisteredWindowClass = false;
+
+ if (haveRegisteredWindowClass)
+ return;
+
+ WNDCLASSEX wcex;
+ wcex.cbSize = sizeof(WNDCLASSEX);
+ wcex.hIconSm = 0;
+ wcex.style = CS_DROPSHADOW;
+
+ wcex.lpfnWndProc = PopupMenuWndProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = sizeof(PopupMenu*); // For the PopupMenu pointer
+ wcex.hInstance = WebCore::instanceHandle();
+ wcex.hIcon = 0;
+ wcex.hCursor = LoadCursor(0, IDC_ARROW);
+ wcex.hbrBackground = 0;
+ wcex.lpszMenuName = 0;
+ wcex.lpszClassName = kPopupWindowClassName;
+
+ haveRegisteredWindowClass = true;
+
+ RegisterClassEx(&wcex);
+}
+
+
+LRESULT CALLBACK PopupMenuWin::PopupMenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ if (PopupMenuWin* popup = static_cast<PopupMenuWin*>(getWindowPointer(hWnd, 0)))
+ return popup->wndProc(hWnd, message, wParam, lParam);
+
+ if (message == WM_CREATE) {
+ LPCREATESTRUCT createStruct = reinterpret_cast<LPCREATESTRUCT>(lParam);
+
+ // Associate the PopupMenu with the window.
+ setWindowPointer(hWnd, 0, createStruct->lpCreateParams);
+ return 0;
+ }
+
+ return ::DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+const int smoothScrollAnimationDuration = 5000;
+
+LRESULT PopupMenuWin::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ LRESULT lResult = 0;
+
+ switch (message) {
+ case WM_MOUSEACTIVATE:
+ return MA_NOACTIVATE;
+ case WM_SIZE: {
+ if (!scrollbar())
+ break;
+
+ 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 < client()->listSize());
+ scrollbar()->setSteps(1, std::max(1, visibleItems - 1));
+ scrollbar()->setProportion(visibleItems, client()->listSize());
+
+ break;
+ }
+ case WM_SYSKEYDOWN:
+ case WM_KEYDOWN: {
+ if (!client())
+ break;
+
+ bool altKeyPressed = GetKeyState(VK_MENU) & HIGH_BIT_MASK_SHORT;
+ bool ctrlKeyPressed = GetKeyState(VK_CONTROL) & HIGH_BIT_MASK_SHORT;
+
+ lResult = 0;
+ switch (LOWORD(wParam)) {
+ case VK_F4: {
+ if (!altKeyPressed && !ctrlKeyPressed) {
+ int index = focusedIndex();
+ ASSERT(index >= 0);
+ client()->valueChanged(index);
+ hide();
+ }
+ break;
+ }
+ case VK_DOWN:
+ if (altKeyPressed) {
+ int index = focusedIndex();
+ ASSERT(index >= 0);
+ client()->valueChanged(index);
+ hide();
+ } else
+ down();
+ break;
+ case VK_RIGHT:
+ down();
+ break;
+ case VK_UP:
+ if (altKeyPressed) {
+ int index = focusedIndex();
+ ASSERT(index >= 0);
+ client()->valueChanged(index);
+ hide();
+ } else
+ up();
+ break;
+ 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(client()->hostWindow()->platformPageClient(), 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;
+ }
+ break;
+ }
+ case WM_CHAR: {
+ if (!client())
+ break;
+
+ lResult = 0;
+ int index;
+ switch (wParam) {
+ case 0x0D: // Enter/Return
+ hide();
+ index = focusedIndex();
+ ASSERT(index >= 0);
+ client()->valueChanged(index);
+ break;
+ case 0x1B: // Escape
+ hide();
+ break;
+ case 0x09: // TAB
+ case 0x08: // Backspace
+ case 0x0A: // Linefeed
+ default: // Character
+ lResult = 1;
+ break;
+ }
+ break;
+ }
+ case WM_MOUSEMOVE: {
+ 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);
+ break;
+ }
+ }
+
+ BOOL shouldHotTrack = FALSE;
+ if (!::SystemParametersInfo(SPI_GETHOTTRACKING, 0, &shouldHotTrack, 0))
+ shouldHotTrack = FALSE;
+
+ RECT bounds;
+ GetClientRect(popupHandle(), &bounds);
+ if (!::PtInRect(&bounds, mousePoint) && !(wParam & MK_LBUTTON) && client()) {
+ // 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, client()->hostWindow()->platformPageClient());
+
+ ::PostMessage(m_popup, WM_HOST_WINDOW_MOUSEMOVE, wParam, lParam);
+ break;
+ }
+
+ if ((shouldHotTrack || wParam & MK_LBUTTON) && ::PtInRect(&bounds, mousePoint)) {
+ setFocusedIndex(listIndexAtPoint(mousePoint), true);
+ m_hoveredIndex = listIndexAtPoint(mousePoint);
+ }
+
+ break;
+ }
+ case WM_LBUTTONDOWN: {
+ 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);
+ break;
+ }
+ }
+
+ // 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);
+ m_hoveredIndex = listIndexAtPoint(mousePoint);
+ }
+ else
+ hide();
+ break;
+ }
+ case WM_LBUTTONUP: {
+ 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(event);
+ // FIXME: This is a hack to work around Scrollbar not invalidating correctly when it doesn't have a parent widget
+ RECT r = scrollBarRect;
+ ::InvalidateRect(popupHandle(), &r, TRUE);
+ break;
+ }
+ }
+ // Only hide the popup if the mouse is inside the popup window.
+ RECT bounds;
+ GetClientRect(popupHandle(), &bounds);
+ if (client() && ::PtInRect(&bounds, mousePoint)) {
+ hide();
+ int index = m_hoveredIndex;
+ if (!client()->itemIsEnabled(index))
+ index = client()->selectedIndex();
+ if (index >= 0)
+ client()->valueChanged(index);
+ }
+ break;
+ }
+
+ case WM_MOUSEWHEEL: {
+ if (!scrollbar())
+ break;
+
+ int i = 0;
+ for (incrementWheelDelta(GET_WHEEL_DELTA_WPARAM(wParam)); abs(wheelDelta()) >= WHEEL_DELTA; reduceWheelDelta(WHEEL_DELTA)) {
+ if (wheelDelta() > 0)
+ ++i;
+ else
+ --i;
+ }
+
+ ScrollableArea::scroll(i > 0 ? ScrollUp : ScrollDown, ScrollByLine, abs(i));
+ break;
+ }
+
+ case WM_PAINT: {
+ PAINTSTRUCT paintInfo;
+ ::BeginPaint(popupHandle(), &paintInfo);
+ paint(paintInfo.rcPaint, paintInfo.hdc);
+ ::EndPaint(popupHandle(), &paintInfo);
+ lResult = 0;
+ break;
+ }
+ case WM_PRINTCLIENT:
+ paint(clientRect(), (HDC)wParam);
+ break;
+ case WM_GETOBJECT:
+ onGetObject(wParam, lParam, lResult);
+ break;
+ default:
+ lResult = DefWindowProc(hWnd, message, wParam, lParam);
+ }
+
+ return lResult;
+}
+
+AccessiblePopupMenu::AccessiblePopupMenu(const PopupMenuWin& popupMenu)
+ : m_popupMenu(popupMenu)
+{
+}
+
+AccessiblePopupMenu::~AccessiblePopupMenu()
+{
+}
+
+HRESULT AccessiblePopupMenu::QueryInterface(_In_ REFIID riid, _COM_Outptr_ void** ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+ if (IsEqualGUID(riid, __uuidof(IAccessible)))
+ *ppvObject = static_cast<IAccessible*>(this);
+ else if (IsEqualGUID(riid, __uuidof(IDispatch)))
+ *ppvObject = static_cast<IAccessible*>(this);
+ else if (IsEqualGUID(riid, __uuidof(IUnknown)))
+ *ppvObject = static_cast<IAccessible*>(this);
+ else {
+ *ppvObject = nullptr;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+ULONG AccessiblePopupMenu::AddRef()
+{
+ return ++m_refCount;
+}
+
+ULONG AccessiblePopupMenu::Release()
+{
+ int refCount = --m_refCount;
+ if (!refCount)
+ delete this;
+ return refCount;
+}
+
+HRESULT AccessiblePopupMenu::GetTypeInfoCount(_Out_ UINT* count)
+{
+ if (!count)
+ return E_POINTER;
+ *count = 0;
+ notImplemented();
+ return E_NOTIMPL;
+}
+
+HRESULT AccessiblePopupMenu::GetTypeInfo(UINT, LCID, _COM_Outptr_opt_ ITypeInfo** ppTInfo)
+{
+ if (!ppTInfo)
+ return E_POINTER;
+ *ppTInfo = nullptr;
+ notImplemented();
+ return E_NOTIMPL;
+}
+
+HRESULT AccessiblePopupMenu::GetIDsOfNames(_In_ REFIID, __in_ecount(cNames) LPOLESTR*, UINT cNames, LCID, __out_ecount_full(cNames) DISPID*)
+{
+ notImplemented();
+ return E_NOTIMPL;
+}
+
+HRESULT AccessiblePopupMenu::Invoke(DISPID, REFIID, LCID, WORD, DISPPARAMS*, VARIANT*, EXCEPINFO*, UINT*)
+{
+ notImplemented();
+ return E_NOTIMPL;
+}
+
+HRESULT AccessiblePopupMenu::get_accParent(_COM_Outptr_opt_ IDispatch** parent)
+{
+ if (!parent)
+ return E_POINTER;
+ *parent = nullptr;
+ notImplemented();
+ return E_NOTIMPL;
+}
+
+HRESULT AccessiblePopupMenu::get_accChildCount(_Out_ long* count)
+{
+ if (!count)
+ return E_POINTER;
+
+ *count = m_popupMenu.visibleItems();
+ return S_OK;
+}
+
+HRESULT AccessiblePopupMenu::get_accChild(VARIANT vChild, _COM_Outptr_opt_ IDispatch** ppChild)
+{
+ if (!ppChild)
+ return E_POINTER;
+
+ *ppChild = nullptr;
+
+ if (vChild.vt != VT_I4)
+ return E_INVALIDARG;
+
+ notImplemented();
+ return S_FALSE;
+}
+
+HRESULT AccessiblePopupMenu::get_accName(VARIANT vChild, __deref_out_opt BSTR* name)
+{
+ return get_accValue(vChild, name);
+}
+
+HRESULT AccessiblePopupMenu::get_accValue(VARIANT vChild, __deref_out_opt BSTR* value)
+{
+ if (!value)
+ return E_POINTER;
+
+ *value = nullptr;
+
+ if (vChild.vt != VT_I4)
+ return E_INVALIDARG;
+
+ int index = vChild.lVal - 1;
+
+ if (index < 0)
+ return E_INVALIDARG;
+
+ BString itemText(m_popupMenu.client()->itemText(index));
+ *value = itemText.release();
+
+ return S_OK;
+}
+
+HRESULT AccessiblePopupMenu::get_accDescription(VARIANT, __deref_out_opt BSTR*)
+{
+ notImplemented();
+ return E_NOTIMPL;
+}
+
+HRESULT AccessiblePopupMenu::get_accRole(VARIANT vChild, _Out_ VARIANT* pvRole)
+{
+ if (!pvRole)
+ return E_POINTER;
+ if (vChild.vt != VT_I4)
+ return E_INVALIDARG;
+
+ // Scrollbar parts are encoded as negative values.
+ if (vChild.lVal < 0) {
+ V_VT(pvRole) = VT_I4;
+ V_I4(pvRole) = ROLE_SYSTEM_SCROLLBAR;
+ } else {
+ V_VT(pvRole) = VT_I4;
+ V_I4(pvRole) = ROLE_SYSTEM_LISTITEM;
+ }
+
+ return S_OK;
+}
+
+HRESULT AccessiblePopupMenu::get_accState(VARIANT vChild, _Out_ VARIANT* pvState)
+{
+ if (!pvState)
+ return E_POINTER;
+
+ if (vChild.vt != VT_I4)
+ return E_INVALIDARG;
+
+ V_VT(pvState) = VT_I4;
+ V_I4(pvState) = 0; // STATE_SYSTEM_NORMAL
+
+ return S_OK;
+}
+
+HRESULT AccessiblePopupMenu::get_accHelp(VARIANT vChild, __deref_out_opt BSTR* helpText)
+{
+ notImplemented();
+ return E_NOTIMPL;
+}
+
+HRESULT AccessiblePopupMenu::get_accKeyboardShortcut(VARIANT vChild, __deref_out_opt BSTR*)
+{
+ notImplemented();
+ return E_NOTIMPL;
+}
+
+HRESULT AccessiblePopupMenu::get_accFocus(_Out_ VARIANT* pvFocusedChild)
+{
+ notImplemented();
+ return E_NOTIMPL;
+}
+
+HRESULT AccessiblePopupMenu::get_accSelection(_Out_ VARIANT* pvSelectedChild)
+{
+ notImplemented();
+ return E_NOTIMPL;
+}
+
+HRESULT AccessiblePopupMenu::get_accDefaultAction(VARIANT vChild, __deref_out_opt BSTR* actionDescription)
+{
+ notImplemented();
+ return E_NOTIMPL;
+}
+
+HRESULT AccessiblePopupMenu::accSelect(long selectionFlags, VARIANT vChild)
+{
+ notImplemented();
+ return E_NOTIMPL;
+}
+
+HRESULT AccessiblePopupMenu::accLocation(_Out_ long* left, _Out_ long* top, _Out_ long* width, _Out_ long* height, VARIANT vChild)
+{
+ if (!left || !top || !width || !height)
+ return E_POINTER;
+
+ if (vChild.vt != VT_I4)
+ return E_INVALIDARG;
+
+ const IntRect& windowRect = m_popupMenu.windowRect();
+
+ // Scrollbar parts are encoded as negative values.
+ if (vChild.lVal < 0) {
+ if (!m_popupMenu.scrollbar())
+ return E_FAIL;
+
+ Scrollbar& scrollbar = *m_popupMenu.scrollbar();
+ WebCore::ScrollbarPart part = static_cast<WebCore::ScrollbarPart>(-vChild.lVal);
+
+ ScrollbarThemeWin& theme = static_cast<ScrollbarThemeWin&>(scrollbar.theme());
+
+ IntRect partRect;
+
+ switch (part) {
+ case BackTrackPart:
+ case BackButtonStartPart:
+ partRect = theme.backButtonRect(scrollbar, WebCore::BackTrackPart);
+ break;
+ case ThumbPart:
+ partRect = theme.thumbRect(scrollbar);
+ break;
+ case ForwardTrackPart:
+ case ForwardButtonEndPart:
+ partRect = theme.forwardButtonRect(scrollbar, WebCore::ForwardTrackPart);
+ break;
+ case ScrollbarBGPart:
+ partRect = theme.trackRect(scrollbar);
+ break;
+ default:
+ return E_FAIL;
+ }
+
+ partRect.move(windowRect.x(), windowRect.y());
+
+ *left = partRect.x();
+ *top = partRect.y();
+ *width = partRect.width();
+ *height = partRect.height();
+
+ return S_OK;
+ }
+
+ int index = vChild.lVal - 1;
+
+ if (index < 0)
+ return E_INVALIDARG;
+
+ *left = windowRect.x();
+ *top = windowRect.y() + (index - m_popupMenu.m_scrollOffset) * m_popupMenu.itemHeight();
+ *width = windowRect.width();
+ *height = m_popupMenu.itemHeight();
+
+ return S_OK;
+}
+
+HRESULT AccessiblePopupMenu::accNavigate(long direction, VARIANT vFromChild, _Out_ VARIANT* pvNavigatedTo)
+{
+ notImplemented();
+ return E_NOTIMPL;
+}
+
+HRESULT AccessiblePopupMenu::accHitTest(long x, long y, _Out_ VARIANT* pvChildAtPoint)
+{
+ if (!pvChildAtPoint)
+ return E_POINTER;
+
+ ::VariantInit(pvChildAtPoint);
+
+ IntRect windowRect = m_popupMenu.windowRect();
+
+ IntPoint pt(x - windowRect.x(), y - windowRect.y());
+
+ IntRect scrollRect;
+
+ if (m_popupMenu.scrollbar())
+ scrollRect = m_popupMenu.scrollbar()->frameRect();
+
+ if (m_popupMenu.scrollbar() && scrollRect.contains(pt)) {
+ Scrollbar& scrollbar = *m_popupMenu.scrollbar();
+
+ pt.move(-scrollRect.x(), -scrollRect.y());
+
+ WebCore::ScrollbarPart part = scrollbar.theme().hitTest(scrollbar, pt);
+
+ V_VT(pvChildAtPoint) = VT_I4;
+ V_I4(pvChildAtPoint) = -part; // Scrollbar parts are encoded as negative, to avoid mixup with item indexes.
+ return S_OK;
+ }
+
+ int index = m_popupMenu.listIndexAtPoint(pt);
+
+ if (index < 0) {
+ V_VT(pvChildAtPoint) = VT_EMPTY;
+ return S_OK;
+ }
+
+ V_VT(pvChildAtPoint) = VT_I4;
+ V_I4(pvChildAtPoint) = index + 1; // CHILDID_SELF is 0, need to add 1.
+
+ return S_OK;
+}
+
+HRESULT AccessiblePopupMenu::accDoDefaultAction(VARIANT vChild)
+{
+ notImplemented();
+ return E_NOTIMPL;
+}
+
+HRESULT AccessiblePopupMenu::put_accName(VARIANT, BSTR)
+{
+ notImplemented();
+ return E_NOTIMPL;
+}
+
+HRESULT AccessiblePopupMenu::put_accValue(VARIANT, BSTR)
+{
+ notImplemented();
+ return E_NOTIMPL;
+}
+
+HRESULT AccessiblePopupMenu::get_accHelpTopic(BSTR* helpFile, VARIANT, _Out_ long* topicID)
+{
+ notImplemented();
+ return E_NOTIMPL;
+}
+
+}