Use "= default" to denote default constructor or destructor
[WebKit-https.git] / Source / WebCore / platform / win / PopupMenuWin.cpp
1 /*
2  * Copyright (C) 2006-2008, 2011, 2015 Apple Inc. All rights reserved.
3  * Copyright (C) 2007-2009 Torch Mobile Inc.
4  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22
23 #include "config.h"
24 #include "PopupMenuWin.h"
25
26 #include "BString.h"
27 #include "BitmapInfo.h"
28 #include "Document.h"
29 #include "FloatRect.h"
30 #include "Font.h"
31 #include "FontSelector.h"
32 #include "Frame.h"
33 #include "FrameView.h"
34 #include "GDIUtilities.h"
35 #include "GraphicsContext.h"
36 #include "HTMLNames.h"
37 #include "HWndDC.h"
38 #include "HostWindow.h"
39 #include "LengthFunctions.h"
40 #include "NotImplemented.h"
41 #include "Page.h"
42 #include "PlatformMouseEvent.h"
43 #include "PlatformScreen.h"
44 #include "RenderMenuList.h"
45 #include "RenderTheme.h"
46 #include "RenderView.h"
47 #include "Scrollbar.h"
48 #include "ScrollbarTheme.h"
49 #include "ScrollbarThemeWin.h"
50 #include "TextRun.h"
51 #include "WebCoreInstanceHandle.h"
52 #include <wtf/WindowsExtras.h>
53
54 #include <windows.h>
55 #include <windowsx.h>
56
57 #define HIGH_BIT_MASK_SHORT 0x8000
58
59 using std::min;
60
61 namespace WebCore {
62
63 using namespace HTMLNames;
64
65 // Default Window animation duration in milliseconds
66 static const int defaultAnimationDuration = 200;
67 // Maximum height of a popup window
68 static const int maxPopupHeight = 320;
69
70 const int optionSpacingMiddle = 1;
71 const int popupWindowBorderWidth = 1;
72
73 static LPCWSTR kPopupWindowClassName = L"PopupWindowClass";
74
75 // This is used from within our custom message pump when we want to send a
76 // message to the web view and not have our message stolen and sent to
77 // the popup window.
78 static const UINT WM_HOST_WINDOW_FIRST = WM_USER;
79 static const UINT WM_HOST_WINDOW_CHAR = WM_USER + WM_CHAR; 
80 static const UINT WM_HOST_WINDOW_MOUSEMOVE = WM_USER + WM_MOUSEMOVE;
81
82 // FIXME: Remove this as soon as practical.
83 static inline bool isASCIIPrintable(unsigned c)
84 {
85     return c >= 0x20 && c <= 0x7E;
86 }
87
88 static void translatePoint(LPARAM& lParam, HWND from, HWND to)
89 {
90     POINT pt;
91     pt.x = (short)GET_X_LPARAM(lParam);
92     pt.y = (short)GET_Y_LPARAM(lParam);    
93     ::MapWindowPoints(from, to, &pt, 1);
94     lParam = MAKELPARAM(pt.x, pt.y);
95 }
96
97 static FloatRect monitorFromHwnd(HWND hwnd)
98 {
99     HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
100     MONITORINFOEX monitorInfo;
101     monitorInfo.cbSize = sizeof(MONITORINFOEX);
102     GetMonitorInfo(monitor, &monitorInfo);
103     return monitorInfo.rcWork;
104 }
105
106 PopupMenuWin::PopupMenuWin(PopupMenuClient* client)
107     : m_popupClient(client)
108 {
109 }
110
111 PopupMenuWin::~PopupMenuWin()
112 {
113     if (m_popup)
114         ::DestroyWindow(m_popup);
115     if (m_scrollbar)
116         m_scrollbar->setParent(0);
117 }
118
119 void PopupMenuWin::disconnectClient()
120 {
121     m_popupClient = 0;
122 }
123
124 LPCWSTR PopupMenuWin::popupClassName()
125 {
126     return kPopupWindowClassName;
127 }
128
129 void PopupMenuWin::show(const IntRect& r, FrameView* view, int index)
130 {
131     if (view && view->frame().page())
132         m_scaleFactor = view->frame().page()->deviceScaleFactor();
133
134     calculatePositionAndSize(r, view);
135     if (clientRect().isEmpty())
136         return;
137
138     HWND hostWindow = view->hostWindow()->platformPageClient();
139
140     if (!m_scrollbar && visibleItems() < client()->listSize()) {
141         // We need a scroll bar
142         m_scrollbar = client()->createScrollbar(*this, VerticalScrollbar, SmallScrollbar);
143         m_scrollbar->styleChanged();
144     }
145
146     // We need to reposition the popup window to its final coordinates.
147     // 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.
148     ::MoveWindow(m_popup, m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height(), false);
149
150     // Determine whether we should animate our popups
151     // Note: Must use 'BOOL' and 'FALSE' instead of 'bool' and 'false' to avoid stack corruption with SystemParametersInfo
152     BOOL shouldAnimate = FALSE;
153
154     if (client()) {
155         int index = client()->selectedIndex();
156         if (index >= 0)
157             setFocusedIndex(index);
158     }
159
160     if (!::SystemParametersInfo(SPI_GETCOMBOBOXANIMATION, 0, &shouldAnimate, 0))
161         shouldAnimate = FALSE;
162
163     if (shouldAnimate) {
164         RECT viewRect = {0};
165         ::GetWindowRect(hostWindow, &viewRect);
166         if (!::IsRectEmpty(&viewRect))
167             ::AnimateWindow(m_popup, defaultAnimationDuration, AW_BLEND);
168     } else
169         ::ShowWindow(m_popup, SW_SHOWNOACTIVATE);
170
171     m_showPopup = true;
172
173     // Protect the popup menu in case its owner is destroyed while we're running the message pump.
174     RefPtr<PopupMenu> protectedThis(this);
175
176     ::SetCapture(hostWindow);
177
178     MSG msg;
179     HWND activeWindow;
180
181     while (::GetMessage(&msg, 0, 0, 0)) {
182         switch (msg.message) {
183             case WM_HOST_WINDOW_MOUSEMOVE:
184             case WM_HOST_WINDOW_CHAR: 
185                 if (msg.hwnd == m_popup) {
186                     // This message should be sent to the host window.
187                     msg.hwnd = hostWindow;
188                     msg.message -= WM_HOST_WINDOW_FIRST;
189                 }
190                 break;
191
192             // Steal mouse messages.
193             case WM_NCMOUSEMOVE:
194             case WM_NCLBUTTONDOWN:
195             case WM_NCLBUTTONUP:
196             case WM_NCLBUTTONDBLCLK:
197             case WM_NCRBUTTONDOWN:
198             case WM_NCRBUTTONUP:
199             case WM_NCRBUTTONDBLCLK:
200             case WM_NCMBUTTONDOWN:
201             case WM_NCMBUTTONUP:
202             case WM_NCMBUTTONDBLCLK:
203             case WM_MOUSEWHEEL:
204                 msg.hwnd = m_popup;
205                 break;
206
207             // These mouse messages use client coordinates so we need to convert them.
208             case WM_MOUSEMOVE:
209             case WM_LBUTTONDOWN:
210             case WM_LBUTTONUP:
211             case WM_LBUTTONDBLCLK:
212             case WM_RBUTTONDOWN:
213             case WM_RBUTTONUP:
214             case WM_RBUTTONDBLCLK:
215             case WM_MBUTTONDOWN:
216             case WM_MBUTTONUP:
217             case WM_MBUTTONDBLCLK: {
218                 // Translate the coordinate.
219                 translatePoint(msg.lParam, msg.hwnd, m_popup);
220
221                 msg.hwnd = m_popup;
222                 break;
223             }
224
225             // Steal all keyboard messages.
226             case WM_KEYDOWN:
227             case WM_KEYUP:
228             case WM_CHAR:
229             case WM_DEADCHAR:
230             case WM_SYSKEYDOWN:
231             case WM_SYSKEYUP:
232             case WM_SYSCHAR:
233             case WM_SYSDEADCHAR:
234                 msg.hwnd = m_popup;
235                 break;
236         }
237
238         ::TranslateMessage(&msg);
239         ::DispatchMessage(&msg);
240
241         if (!m_popupClient)
242             break;
243
244         if (!m_showPopup)
245             break;
246         activeWindow = ::GetActiveWindow();
247         if (activeWindow != hostWindow && !::IsChild(activeWindow, hostWindow))
248             break;
249         if (::GetCapture() != hostWindow)
250             break;
251     }
252
253     if (::GetCapture() == hostWindow)
254         ::ReleaseCapture();
255
256     // We're done, hide the popup if necessary.
257     hide();
258 }
259
260 void PopupMenuWin::hide()
261 {
262     if (!m_showPopup)
263         return;
264
265     m_showPopup = false;
266
267     ::ShowWindow(m_popup, SW_HIDE);
268
269     if (client())
270         client()->popupDidHide();
271
272     // Post a WM_NULL message to wake up the message pump if necessary.
273     ::PostMessage(m_popup, WM_NULL, 0, 0);
274 }
275
276 // The screen that the popup is placed on should be whichever one the popup menu button lies on.
277 // 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.
278 // We can then proceed with our final position/size calculations.
279 void PopupMenuWin::calculatePositionAndSize(const IntRect& r, FrameView* v)
280 {
281     // First get the screen coordinates of the popup menu client.
282     HWND hostWindow = v->hostWindow()->platformPageClient();
283     IntRect absoluteBounds = ((RenderMenuList*)m_popupClient)->absoluteBoundingBoxRect();
284     IntRect absoluteScreenCoords(v->contentsToWindow(absoluteBounds.location()), absoluteBounds.size());
285     POINT absoluteLocation(absoluteScreenCoords.location());
286     if (!::ClientToScreen(hostWindow, &absoluteLocation))
287         return;
288     absoluteScreenCoords.setLocation(absoluteLocation);
289
290     // Now set the popup menu's location temporarily to these coordinates so we can determine which screen the popup should lie on.
291     // We create or move m_popup as necessary.
292     if (!m_popup) {
293         registerClass();
294         DWORD exStyle = WS_EX_LTRREADING;
295         m_popup = ::CreateWindowExW(exStyle, kPopupWindowClassName, L"PopupMenu",
296             WS_POPUP | WS_BORDER,
297             absoluteScreenCoords.x(), absoluteScreenCoords.y(), absoluteScreenCoords.width(), absoluteScreenCoords.height(),
298             hostWindow, 0, WebCore::instanceHandle(), this);
299
300         if (!m_popup)
301             return;
302     } else
303         ::MoveWindow(m_popup, absoluteScreenCoords.x(), absoluteScreenCoords.y(), absoluteScreenCoords.width(), absoluteScreenCoords.height(), false);
304
305     FloatRect screen = monitorFromHwnd(m_popup);
306     
307     // Now we determine the actual location and measurements of the popup itself.
308     // r is in absolute document coordinates, but we want to be in screen coordinates.
309
310     // First, move to WebView coordinates
311     IntRect rScreenCoords(v->contentsToWindow(r.location()), r.size());
312     if (Page* page = v->frame().page())
313         rScreenCoords.scale(page->deviceScaleFactor());
314
315     // Then, translate to screen coordinates
316     POINT location(rScreenCoords.location());
317     if (!::ClientToScreen(hostWindow, &location))
318         return;
319
320     rScreenCoords.setLocation(location);
321
322     m_font = client()->menuStyle().font();
323     auto d = m_font.fontDescription();
324     d.setComputedSize(d.computedSize() * m_scaleFactor);
325     m_font = FontCascade(d, m_font.letterSpacing(), m_font.wordSpacing());
326     m_font.update(m_popupClient->fontSelector());
327
328     // First, determine the popup's height
329     int itemCount = client()->listSize();
330     m_itemHeight = m_font.fontMetrics().height() + optionSpacingMiddle;
331
332     int naturalHeight = m_itemHeight * itemCount;
333     int popupHeight = std::min(maxPopupHeight, naturalHeight);
334     // The popup should show an integral number of items (i.e. no partial items should be visible)
335     popupHeight -= popupHeight % m_itemHeight;
336     
337     // Next determine its width
338     int popupWidth = 0;
339     for (int i = 0; i < itemCount; ++i) {
340         String text = client()->itemText(i);
341         if (text.isEmpty())
342             continue;
343
344         FontCascade itemFont = m_font;
345         if (client()->itemIsLabel(i)) {
346             auto d = itemFont.fontDescription();
347             d.setWeight(d.bolderWeight());
348             itemFont = FontCascade(d, itemFont.letterSpacing(), itemFont.wordSpacing());
349             itemFont.update(m_popupClient->fontSelector());
350         }
351
352         popupWidth = std::max(popupWidth, static_cast<int>(ceilf(itemFont.width(TextRun(text)))));
353     }
354
355     if (naturalHeight > maxPopupHeight)
356         // We need room for a scrollbar
357         popupWidth += ScrollbarTheme::theme().scrollbarThickness(SmallScrollbar);
358
359     // Add padding to align the popup text with the <select> text
360     popupWidth += std::max<int>(0, client()->clientPaddingRight() - client()->clientInsetRight()) + std::max<int>(0, client()->clientPaddingLeft() - client()->clientInsetLeft());
361
362     // Leave room for the border
363     popupWidth += 2 * popupWindowBorderWidth;
364     popupHeight += 2 * popupWindowBorderWidth;
365
366     // The popup should be at least as wide as the control on the page
367     popupWidth = std::max(rScreenCoords.width() - client()->clientInsetLeft() - client()->clientInsetRight(), popupWidth);
368
369     // Always left-align items in the popup.  This matches popup menus on the mac.
370     int popupX = rScreenCoords.x() + client()->clientInsetLeft();
371
372     IntRect popupRect(popupX, rScreenCoords.maxY(), popupWidth, popupHeight);
373
374     // Check that we don't go off the screen vertically
375     if (popupRect.maxY() > screen.height()) {
376         // The popup will go off the screen, so try placing it above the client
377         if (rScreenCoords.y() - popupRect.height() < 0) {
378             // The popup won't fit above, either, so place it whereever's bigger and resize it to fit
379             if ((rScreenCoords.y() + rScreenCoords.height() / 2) < (screen.height() / 2)) {
380                 // Below is bigger
381                 popupRect.setHeight(screen.height() - popupRect.y());
382             } else {
383                 // Above is bigger
384                 popupRect.setY(0);
385                 popupRect.setHeight(rScreenCoords.y());
386             }
387         } else {
388             // The popup fits above, so reposition it
389             popupRect.setY(rScreenCoords.y() - popupRect.height());
390         }
391     }
392
393     // Check that we don't go off the screen horizontally
394     if (popupRect.x() + popupRect.width() > screen.width() + screen.x())
395         popupRect.setX(screen.x() + screen.width() - popupRect.width());
396     if (popupRect.x() < screen.x())
397         popupRect.setX(screen.x());
398
399     m_windowRect = popupRect;
400     return;
401 }
402
403 bool PopupMenuWin::setFocusedIndex(int i, bool hotTracking)
404 {
405     if (i < 0 || i >= client()->listSize() || i == focusedIndex())
406         return false;
407
408     if (!client()->itemIsEnabled(i))
409         return false;
410
411     invalidateItem(focusedIndex());
412     invalidateItem(i);
413
414     m_focusedIndex = i;
415
416     if (!hotTracking)
417         client()->setTextFromItem(i);
418
419     if (!scrollToRevealSelection())
420         ::UpdateWindow(m_popup);
421
422     return true;
423 }
424
425 int PopupMenuWin::visibleItems() const
426 {
427     return clientRect().height() / m_itemHeight;
428 }
429
430 int PopupMenuWin::listIndexAtPoint(const IntPoint& point) const
431 {
432     return m_scrollOffset + point.y() / m_itemHeight;
433 }
434
435 int PopupMenuWin::focusedIndex() const
436 {
437     return m_focusedIndex;
438 }
439
440 void PopupMenuWin::focusFirst()
441 {
442     if (!client())
443         return;
444
445     int size = client()->listSize();
446
447     for (int i = 0; i < size; ++i)
448         if (client()->itemIsEnabled(i)) {
449             setFocusedIndex(i);
450             break;
451         }
452 }
453
454 void PopupMenuWin::focusLast()
455 {
456     if (!client())
457         return;
458
459     int size = client()->listSize();
460
461     for (int i = size - 1; i > 0; --i)
462         if (client()->itemIsEnabled(i)) {
463             setFocusedIndex(i);
464             break;
465         }
466 }
467
468 bool PopupMenuWin::down(unsigned lines)
469 {
470     if (!client())
471         return false;
472
473     int size = client()->listSize();
474
475     int lastSelectableIndex, selectedListIndex;
476     lastSelectableIndex = selectedListIndex = focusedIndex();
477     for (int i = selectedListIndex + 1; i >= 0 && i < size; ++i)
478         if (client()->itemIsEnabled(i)) {
479             lastSelectableIndex = i;
480             if (i >= selectedListIndex + (int)lines)
481                 break;
482         }
483
484     return setFocusedIndex(lastSelectableIndex);
485 }
486
487 bool PopupMenuWin::up(unsigned lines)
488 {
489     if (!client())
490         return false;
491
492     int size = client()->listSize();
493
494     int lastSelectableIndex, selectedListIndex;
495     lastSelectableIndex = selectedListIndex = focusedIndex();
496     for (int i = selectedListIndex - 1; i >= 0 && i < size; --i)
497         if (client()->itemIsEnabled(i)) {
498             lastSelectableIndex = i;
499             if (i <= selectedListIndex - (int)lines)
500                 break;
501         }
502
503     return setFocusedIndex(lastSelectableIndex);
504 }
505
506 void PopupMenuWin::invalidateItem(int index)
507 {
508     if (!m_popup)
509         return;
510
511     IntRect damageRect(clientRect());
512     damageRect.setY(m_itemHeight * (index - m_scrollOffset));
513     damageRect.setHeight(m_itemHeight);
514     if (m_scrollbar)
515         damageRect.setWidth(damageRect.width() - m_scrollbar->frameRect().width());
516
517     RECT r = damageRect;
518     ::InvalidateRect(m_popup, &r, TRUE);
519 }
520
521 IntRect PopupMenuWin::clientRect() const
522 {
523     IntRect clientRect = m_windowRect;
524     clientRect.inflate(-popupWindowBorderWidth);
525     clientRect.setLocation(IntPoint(0, 0));
526     return clientRect;
527 }
528
529 void PopupMenuWin::incrementWheelDelta(int delta)
530 {
531     m_wheelDelta += delta;
532 }
533
534 void PopupMenuWin::reduceWheelDelta(int delta)
535 {
536     ASSERT(delta >= 0);
537     ASSERT(delta <= abs(m_wheelDelta));
538
539     if (m_wheelDelta > 0)
540         m_wheelDelta -= delta;
541     else if (m_wheelDelta < 0)
542         m_wheelDelta += delta;
543     else
544         return;
545 }
546
547 bool PopupMenuWin::scrollToRevealSelection()
548 {
549     if (!m_scrollbar)
550         return false;
551
552     int index = focusedIndex();
553
554     if (index < m_scrollOffset) {
555         ScrollableArea::scrollToOffsetWithoutAnimation(VerticalScrollbar, index);
556         return true;
557     }
558
559     if (index >= m_scrollOffset + visibleItems()) {
560         ScrollableArea::scrollToOffsetWithoutAnimation(VerticalScrollbar, index - visibleItems() + 1);
561         return true;
562     }
563
564     return false;
565 }
566
567 void PopupMenuWin::updateFromElement()
568 {
569     if (!m_popup)
570         return;
571
572     m_focusedIndex = client()->selectedIndex();
573
574     ::InvalidateRect(m_popup, 0, TRUE);
575     if (!scrollToRevealSelection())
576         ::UpdateWindow(m_popup);
577 }
578
579 const int separatorPadding = 4;
580 const int separatorHeight = 1;
581 void PopupMenuWin::paint(const IntRect& damageRect, HDC hdc)
582 {
583     if (!m_popup)
584         return;
585
586     if (!m_DC) {
587         m_DC = adoptGDIObject(::CreateCompatibleDC(HWndDC(m_popup)));
588         if (!m_DC)
589             return;
590     }
591
592     if (m_bmp) {
593         bool keepBitmap = false;
594         BITMAP bitmap;
595         if (::GetObject(m_bmp.get(), sizeof(bitmap), &bitmap))
596             keepBitmap = bitmap.bmWidth == clientRect().width()
597                 && bitmap.bmHeight == clientRect().height();
598         if (!keepBitmap)
599             m_bmp.clear();
600     }
601     if (!m_bmp) {
602         BitmapInfo bitmapInfo = BitmapInfo::createBottomUp(clientRect().size());
603         void* pixels = 0;
604         m_bmp = adoptGDIObject(::CreateDIBSection(m_DC.get(), &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0));
605         if (!m_bmp)
606             return;
607
608         ::SelectObject(m_DC.get(), m_bmp.get());
609     }
610
611     GraphicsContext context(m_DC.get());
612
613     int itemCount = client()->listSize();
614
615     // listRect is the damageRect translated into the coordinates of the entire menu list (which is itemCount * m_itemHeight pixels tall)
616     IntRect listRect = damageRect;
617     listRect.move(IntSize(0, m_scrollOffset * m_itemHeight));
618
619     for (int y = listRect.y(); y < listRect.maxY(); y += m_itemHeight) {
620         int index = y / m_itemHeight;
621
622         Color optionBackgroundColor, optionTextColor;
623         PopupMenuStyle itemStyle = client()->itemStyle(index);
624         if (index == focusedIndex()) {
625             optionBackgroundColor = RenderTheme::singleton().activeListBoxSelectionBackgroundColor();
626             optionTextColor = RenderTheme::singleton().activeListBoxSelectionForegroundColor();
627         } else {
628             optionBackgroundColor = itemStyle.backgroundColor();
629             optionTextColor = itemStyle.foregroundColor();
630         }
631
632         // itemRect is in client coordinates
633         IntRect itemRect(0, (index - m_scrollOffset) * m_itemHeight, damageRect.width(), m_itemHeight);
634
635         // Draw the background for this menu item
636         if (itemStyle.isVisible())
637             context.fillRect(itemRect, optionBackgroundColor);
638
639         if (client()->itemIsSeparator(index)) {
640             IntRect separatorRect(itemRect.x() + separatorPadding, itemRect.y() + (itemRect.height() - separatorHeight) / 2, itemRect.width() - 2 * separatorPadding, separatorHeight);
641             context.fillRect(separatorRect, optionTextColor);
642             continue;
643         }
644
645         String itemText = client()->itemText(index);
646
647         TextRun textRun(itemText, 0, 0, AllowTrailingExpansion, itemStyle.textDirection(), itemStyle.hasTextDirectionOverride());
648         context.setFillColor(optionTextColor);
649
650         FontCascade itemFont = m_font;
651         if (client()->itemIsLabel(index)) {
652             auto d = itemFont.fontDescription();
653             d.setWeight(d.bolderWeight());
654             itemFont = FontCascade(d, itemFont.letterSpacing(), itemFont.wordSpacing());
655             itemFont.update(m_popupClient->fontSelector());
656         }
657         
658         // Draw the item text
659         if (itemStyle.isVisible()) {
660             int textX = 0;
661             if (client()->menuStyle().textDirection() == LTR) {
662                 textX = std::max<int>(0, client()->clientPaddingLeft() - client()->clientInsetLeft());
663                 if (RenderTheme::singleton().popupOptionSupportsTextIndent())
664                     textX += minimumIntValueForLength(itemStyle.textIndent(), itemRect.width());
665             } else {
666                 textX = itemRect.width() - client()->menuStyle().font().width(textRun);
667                 textX = std::min<int>(textX, textX - client()->clientPaddingRight() + client()->clientInsetRight());
668                 if (RenderTheme::singleton().popupOptionSupportsTextIndent())
669                     textX -= minimumIntValueForLength(itemStyle.textIndent(), itemRect.width());
670             }
671             int textY = itemRect.y() + itemFont.fontMetrics().ascent() + (itemRect.height() - itemFont.fontMetrics().height()) / 2;
672             context.drawBidiText(itemFont, textRun, IntPoint(textX, textY));
673         }
674     }
675
676     if (m_scrollbar)
677         m_scrollbar->paint(context, damageRect);
678
679     HWndDC hWndDC;
680     HDC localDC = hdc ? hdc : hWndDC.setHWnd(m_popup);
681
682     ::BitBlt(localDC, damageRect.x(), damageRect.y(), damageRect.width(), damageRect.height(), m_DC.get(), damageRect.x(), damageRect.y(), SRCCOPY);
683 }
684
685 int PopupMenuWin::scrollSize(ScrollbarOrientation orientation) const
686 {
687     return ((orientation == VerticalScrollbar) && m_scrollbar) ? (m_scrollbar->totalSize() - m_scrollbar->visibleSize()) : 0;
688 }
689
690 int PopupMenuWin::scrollOffset(ScrollbarOrientation) const
691 {
692     return m_scrollOffset;
693 }
694
695 void PopupMenuWin::setScrollOffset(const IntPoint& offset)
696 {
697     scrollTo(offset.y());
698 }
699
700 void PopupMenuWin::scrollTo(int offset)
701 {
702     ASSERT(m_scrollbar);
703
704     if (!m_popup)
705         return;
706
707     if (m_scrollOffset == offset)
708         return;
709
710     int scrolledLines = m_scrollOffset - offset;
711     m_scrollOffset = offset;
712
713     UINT flags = SW_INVALIDATE;
714
715 #ifdef CAN_SET_SMOOTH_SCROLLING_DURATION
716     BOOL shouldSmoothScroll = FALSE;
717     ::SystemParametersInfo(SPI_GETLISTBOXSMOOTHSCROLLING, 0, &shouldSmoothScroll, 0);
718     if (shouldSmoothScroll)
719         flags |= MAKEWORD(SW_SMOOTHSCROLL, smoothScrollAnimationDuration);
720 #endif
721
722     IntRect listRect = clientRect();
723     if (m_scrollbar)
724         listRect.setWidth(listRect.width() - m_scrollbar->frameRect().width());
725     RECT r = listRect;
726     ::ScrollWindowEx(m_popup, 0, scrolledLines * m_itemHeight, &r, 0, 0, 0, flags);
727     if (m_scrollbar) {
728         r = m_scrollbar->frameRect();
729         ::InvalidateRect(m_popup, &r, TRUE);
730     }
731     ::UpdateWindow(m_popup);
732 }
733
734 void PopupMenuWin::invalidateScrollbarRect(Scrollbar& scrollbar, const IntRect& rect)
735 {
736     IntRect scrollRect = rect;
737     scrollRect.move(scrollbar.x(), scrollbar.y());
738     RECT r = scrollRect;
739     ::InvalidateRect(m_popup, &r, false);
740 }
741
742 IntSize PopupMenuWin::visibleSize() const
743 {
744     return IntSize(m_windowRect.width(), m_scrollbar ? m_scrollbar->visibleSize() : m_windowRect.height());
745 }
746
747 IntSize PopupMenuWin::contentsSize() const
748 {
749     return IntSize(m_windowRect.width(), m_scrollbar ? m_scrollbar->totalSize() : m_windowRect.height());
750 }
751
752 IntRect PopupMenuWin::scrollableAreaBoundingBox(bool*) const
753 {
754     return m_windowRect;
755 }
756
757 bool PopupMenuWin::onGetObject(WPARAM wParam, LPARAM lParam, LRESULT& lResult)
758 {
759     lResult = 0;
760
761     if (static_cast<LONG>(lParam) != OBJID_CLIENT)
762         return false;
763
764     if (!m_accessiblePopupMenu)
765         m_accessiblePopupMenu = new AccessiblePopupMenu(*this);
766
767     static HMODULE accessibilityLib = nullptr;
768     if (!accessibilityLib) {
769         if (!(accessibilityLib = ::LoadLibraryW(L"oleacc.dll")))
770             return false;
771     }
772
773     static LPFNLRESULTFROMOBJECT procPtr = reinterpret_cast<LPFNLRESULTFROMOBJECT>(::GetProcAddress(accessibilityLib, "LresultFromObject"));
774     if (!procPtr)
775         return false;
776
777     // LresultFromObject returns a reference to the accessible object, stored
778     // in an LRESULT. If this call is not successful, Windows will handle the
779     // request through DefWindowProc.
780     return SUCCEEDED(lResult = procPtr(__uuidof(IAccessible), wParam, m_accessiblePopupMenu.get()));
781 }
782
783 void PopupMenuWin::registerClass()
784 {
785     static bool haveRegisteredWindowClass = false;
786
787     if (haveRegisteredWindowClass)
788         return;
789
790     WNDCLASSEX wcex;
791     wcex.cbSize = sizeof(WNDCLASSEX);
792     wcex.hIconSm        = 0;
793     wcex.style          = CS_DROPSHADOW;
794
795     wcex.lpfnWndProc    = PopupMenuWndProc;
796     wcex.cbClsExtra     = 0;
797     wcex.cbWndExtra     = sizeof(PopupMenu*); // For the PopupMenu pointer
798     wcex.hInstance      = WebCore::instanceHandle();
799     wcex.hIcon          = 0;
800     wcex.hCursor        = LoadCursor(0, IDC_ARROW);
801     wcex.hbrBackground  = 0;
802     wcex.lpszMenuName   = 0;
803     wcex.lpszClassName  = kPopupWindowClassName;
804
805     haveRegisteredWindowClass = true;
806
807     RegisterClassEx(&wcex);
808 }
809
810
811 LRESULT CALLBACK PopupMenuWin::PopupMenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
812 {
813     if (PopupMenuWin* popup = static_cast<PopupMenuWin*>(getWindowPointer(hWnd, 0)))
814         return popup->wndProc(hWnd, message, wParam, lParam);
815
816     if (message == WM_CREATE) {
817         LPCREATESTRUCT createStruct = reinterpret_cast<LPCREATESTRUCT>(lParam);
818
819         // Associate the PopupMenu with the window.
820         setWindowPointer(hWnd, 0, createStruct->lpCreateParams);
821         return 0;
822     }
823
824     return ::DefWindowProc(hWnd, message, wParam, lParam);
825 }
826
827 const int smoothScrollAnimationDuration = 5000;
828
829 LRESULT PopupMenuWin::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
830 {
831     LRESULT lResult = 0;
832
833     switch (message) {
834         case WM_MOUSEACTIVATE:
835             return MA_NOACTIVATE;
836         case WM_SIZE: {
837             if (!scrollbar())
838                 break;
839
840             IntSize size(LOWORD(lParam), HIWORD(lParam));
841             scrollbar()->setFrameRect(IntRect(size.width() - scrollbar()->width(), 0, scrollbar()->width(), size.height()));
842
843             int visibleItems = this->visibleItems();
844             scrollbar()->setEnabled(visibleItems < client()->listSize());
845             scrollbar()->setSteps(1, std::max(1, visibleItems - 1));
846             scrollbar()->setProportion(visibleItems, client()->listSize());
847
848             break;
849         }
850         case WM_SYSKEYDOWN:
851         case WM_KEYDOWN: {
852             if (!client())
853                 break;
854
855             bool altKeyPressed = GetKeyState(VK_MENU) & HIGH_BIT_MASK_SHORT;
856             bool ctrlKeyPressed = GetKeyState(VK_CONTROL) & HIGH_BIT_MASK_SHORT;
857
858             lResult = 0;
859             switch (LOWORD(wParam)) {
860                 case VK_F4: {
861                     if (!altKeyPressed && !ctrlKeyPressed) {
862                         int index = focusedIndex();
863                         ASSERT(index >= 0);
864                         client()->valueChanged(index);
865                         hide();
866                     }
867                     break;
868                 }
869                 case VK_DOWN:
870                     if (altKeyPressed) {
871                         int index = focusedIndex();
872                         ASSERT(index >= 0);
873                         client()->valueChanged(index);
874                         hide();
875                     } else
876                         down();
877                     break;
878                 case VK_RIGHT:
879                     down();
880                     break;
881                 case VK_UP:
882                     if (altKeyPressed) {
883                         int index = focusedIndex();
884                         ASSERT(index >= 0);
885                         client()->valueChanged(index);
886                         hide();
887                     } else
888                         up();
889                     break;
890                 case VK_LEFT:
891                     up();
892                     break;
893                 case VK_HOME:
894                     focusFirst();
895                     break;
896                 case VK_END:
897                     focusLast();
898                     break;
899                 case VK_PRIOR:
900                     if (focusedIndex() != scrollOffset()) {
901                         // Set the selection to the first visible item
902                         int firstVisibleItem = scrollOffset();
903                         up(focusedIndex() - firstVisibleItem);
904                     } else {
905                         // The first visible item is selected, so move the selection back one page
906                         up(visibleItems());
907                     }
908                     break;
909                 case VK_NEXT: {
910                     int lastVisibleItem = scrollOffset() + visibleItems() - 1;
911                     if (focusedIndex() != lastVisibleItem) {
912                         // Set the selection to the last visible item
913                         down(lastVisibleItem - focusedIndex());
914                     } else {
915                         // The last visible item is selected, so move the selection forward one page
916                         down(visibleItems());
917                     }
918                     break;
919                 }
920                 case VK_TAB:
921                     ::SendMessage(client()->hostWindow()->platformPageClient(), message, wParam, lParam);
922                     hide();
923                     break;
924                 case VK_ESCAPE:
925                     hide();
926                     break;
927                 default:
928                     if (isASCIIPrintable(wParam))
929                         // Send the keydown to the WebView so it can be used for type-to-select.
930                         // Since we know that the virtual key is ASCII printable, it's OK to convert this to
931                         // a WM_CHAR message. (We don't want to call TranslateMessage because that will post a
932                         // WM_CHAR message that will be stolen and redirected to the popup HWND.
933                         ::PostMessage(m_popup, WM_HOST_WINDOW_CHAR, wParam, lParam);
934                     else
935                         lResult = 1;
936                     break;
937             }
938             break;
939         }
940         case WM_CHAR: {
941             if (!client())
942                 break;
943
944             lResult = 0;
945             int index;
946             switch (wParam) {
947                 case 0x0D:   // Enter/Return
948                     hide();
949                     index = focusedIndex();
950                     ASSERT(index >= 0);
951                     client()->valueChanged(index);
952                     break;
953                 case 0x1B:   // Escape
954                     hide();
955                     break;
956                 case 0x09:   // TAB
957                 case 0x08:   // Backspace
958                 case 0x0A:   // Linefeed
959                 default:     // Character
960                     lResult = 1;
961                     break;
962             }
963             break;
964         }
965         case WM_MOUSEMOVE: {
966             IntPoint mousePoint(MAKEPOINTS(lParam));
967             if (scrollbar()) {
968                 IntRect scrollBarRect = scrollbar()->frameRect();
969                 if (scrollbarCapturingMouse() || scrollBarRect.contains(mousePoint)) {
970                     // Put the point into coordinates relative to the scroll bar
971                     mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y());
972                     PlatformMouseEvent event(hWnd, message, wParam, makeScaledPoint(mousePoint, m_scaleFactor));
973                     scrollbar()->mouseMoved(event);
974                     break;
975                 }
976             }
977
978             BOOL shouldHotTrack = FALSE;
979             if (!::SystemParametersInfo(SPI_GETHOTTRACKING, 0, &shouldHotTrack, 0))
980                 shouldHotTrack = FALSE;
981
982             RECT bounds;
983             GetClientRect(popupHandle(), &bounds);
984             if (!::PtInRect(&bounds, mousePoint) && !(wParam & MK_LBUTTON) && client()) {
985                 // When the mouse is not inside the popup menu and the left button isn't down, just
986                 // repost the message to the web view.
987
988                 // Translate the coordinate.
989                 translatePoint(lParam, m_popup, client()->hostWindow()->platformPageClient());
990
991                 ::PostMessage(m_popup, WM_HOST_WINDOW_MOUSEMOVE, wParam, lParam);
992                 break;
993             }
994
995             if ((shouldHotTrack || wParam & MK_LBUTTON) && ::PtInRect(&bounds, mousePoint)) {
996                 setFocusedIndex(listIndexAtPoint(mousePoint), true);
997                 m_hoveredIndex = listIndexAtPoint(mousePoint);
998             }
999
1000             break;
1001         }
1002         case WM_LBUTTONDOWN: {
1003             IntPoint mousePoint(MAKEPOINTS(lParam));
1004             if (scrollbar()) {
1005                 IntRect scrollBarRect = scrollbar()->frameRect();
1006                 if (scrollBarRect.contains(mousePoint)) {
1007                     // Put the point into coordinates relative to the scroll bar
1008                     mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y());
1009                     PlatformMouseEvent event(hWnd, message, wParam, makeScaledPoint(mousePoint, m_scaleFactor));
1010                     scrollbar()->mouseDown(event);
1011                     setScrollbarCapturingMouse(true);
1012                     break;
1013                 }
1014             }
1015
1016             // If the mouse is inside the window, update the focused index. Otherwise, 
1017             // hide the popup.
1018             RECT bounds;
1019             GetClientRect(m_popup, &bounds);
1020             if (::PtInRect(&bounds, mousePoint)) {
1021                 setFocusedIndex(listIndexAtPoint(mousePoint), true);
1022                 m_hoveredIndex = listIndexAtPoint(mousePoint);
1023             }
1024             else
1025                 hide();
1026             break;
1027         }
1028         case WM_LBUTTONUP: {
1029             IntPoint mousePoint(MAKEPOINTS(lParam));
1030             if (scrollbar()) {
1031                 IntRect scrollBarRect = scrollbar()->frameRect();
1032                 if (scrollbarCapturingMouse() || scrollBarRect.contains(mousePoint)) {
1033                     setScrollbarCapturingMouse(false);
1034                     // Put the point into coordinates relative to the scroll bar
1035                     mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y());
1036                     PlatformMouseEvent event(hWnd, message, wParam, makeScaledPoint(mousePoint, m_scaleFactor));
1037                     scrollbar()->mouseUp(event);
1038                     // FIXME: This is a hack to work around Scrollbar not invalidating correctly when it doesn't have a parent widget
1039                     RECT r = scrollBarRect;
1040                     ::InvalidateRect(popupHandle(), &r, TRUE);
1041                     break;
1042                 }
1043             }
1044             // Only hide the popup if the mouse is inside the popup window.
1045             RECT bounds;
1046             GetClientRect(popupHandle(), &bounds);
1047             if (client() && ::PtInRect(&bounds, mousePoint)) {
1048                 hide();
1049                 int index = m_hoveredIndex;
1050                 if (!client()->itemIsEnabled(index))
1051                     index = client()->selectedIndex();
1052                 if (index >= 0)
1053                     client()->valueChanged(index);
1054             }
1055             break;
1056         }
1057
1058         case WM_MOUSEWHEEL: {
1059             if (!scrollbar())
1060                 break;
1061
1062             int i = 0;
1063             for (incrementWheelDelta(GET_WHEEL_DELTA_WPARAM(wParam)); abs(wheelDelta()) >= WHEEL_DELTA; reduceWheelDelta(WHEEL_DELTA)) {
1064                 if (wheelDelta() > 0)
1065                     ++i;
1066                 else
1067                     --i;
1068             }
1069
1070             ScrollableArea::scroll(i > 0 ? ScrollUp : ScrollDown, ScrollByLine, abs(i));
1071             break;
1072         }
1073
1074         case WM_PAINT: {
1075             PAINTSTRUCT paintInfo;
1076             ::BeginPaint(popupHandle(), &paintInfo);
1077             paint(paintInfo.rcPaint, paintInfo.hdc);
1078             ::EndPaint(popupHandle(), &paintInfo);
1079             lResult = 0;
1080             break;
1081         }
1082         case WM_PRINTCLIENT:
1083             paint(clientRect(), (HDC)wParam);
1084             break;
1085         case WM_GETOBJECT:
1086             onGetObject(wParam, lParam, lResult);
1087             break;
1088         default:
1089             lResult = DefWindowProc(hWnd, message, wParam, lParam);
1090     }
1091
1092     return lResult;
1093 }
1094
1095 AccessiblePopupMenu::AccessiblePopupMenu(const PopupMenuWin& popupMenu)
1096     : m_popupMenu(popupMenu)
1097 {
1098 }
1099
1100 AccessiblePopupMenu::~AccessiblePopupMenu() = default;
1101
1102 HRESULT AccessiblePopupMenu::QueryInterface(_In_ REFIID riid, _COM_Outptr_ void** ppvObject)
1103 {
1104     if (!ppvObject)
1105         return E_POINTER;
1106     if (IsEqualGUID(riid, __uuidof(IAccessible)))
1107         *ppvObject = static_cast<IAccessible*>(this);
1108     else if (IsEqualGUID(riid, __uuidof(IDispatch)))
1109         *ppvObject = static_cast<IAccessible*>(this);
1110     else if (IsEqualGUID(riid, __uuidof(IUnknown)))
1111         *ppvObject = static_cast<IAccessible*>(this);
1112     else {
1113         *ppvObject = nullptr;
1114         return E_NOINTERFACE;
1115     }
1116     AddRef();
1117     return S_OK;
1118 }
1119
1120 ULONG AccessiblePopupMenu::AddRef()
1121 {
1122     return ++m_refCount;
1123 }
1124
1125 ULONG AccessiblePopupMenu::Release()
1126 {
1127     int refCount = --m_refCount;
1128     if (!refCount)
1129         delete this;
1130     return refCount;
1131 }
1132
1133 HRESULT AccessiblePopupMenu::GetTypeInfoCount(_Out_ UINT* count)
1134 {
1135     if (!count)
1136         return E_POINTER;
1137     *count = 0;
1138     notImplemented();
1139     return E_NOTIMPL;
1140 }
1141
1142 HRESULT AccessiblePopupMenu::GetTypeInfo(UINT, LCID, _COM_Outptr_opt_ ITypeInfo** ppTInfo)
1143 {
1144     if (!ppTInfo)
1145         return E_POINTER;
1146     *ppTInfo = nullptr;
1147     notImplemented();
1148     return E_NOTIMPL;
1149 }
1150
1151 HRESULT AccessiblePopupMenu::GetIDsOfNames(_In_ REFIID, __in_ecount(cNames) LPOLESTR*, UINT cNames, LCID, __out_ecount_full(cNames) DISPID*)
1152 {
1153     notImplemented();
1154     return E_NOTIMPL;
1155 }
1156
1157 HRESULT AccessiblePopupMenu::Invoke(DISPID, REFIID, LCID, WORD, DISPPARAMS*, VARIANT*, EXCEPINFO*, UINT*)
1158 {
1159     notImplemented();
1160     return E_NOTIMPL;
1161 }
1162
1163 HRESULT AccessiblePopupMenu::get_accParent(_COM_Outptr_opt_ IDispatch** parent)
1164 {
1165     if (!parent)
1166         return E_POINTER;
1167     *parent = nullptr;
1168     notImplemented();
1169     return E_NOTIMPL;
1170 }
1171
1172 HRESULT AccessiblePopupMenu::get_accChildCount(_Out_ long* count)
1173 {
1174     if (!count)
1175         return E_POINTER;
1176
1177     *count = m_popupMenu.visibleItems();
1178     return S_OK;
1179 }
1180
1181 HRESULT AccessiblePopupMenu::get_accChild(VARIANT vChild, _COM_Outptr_opt_ IDispatch** ppChild)
1182 {
1183     if (!ppChild)
1184         return E_POINTER;
1185
1186     *ppChild = nullptr;
1187
1188     if (vChild.vt != VT_I4)
1189         return E_INVALIDARG;
1190
1191     notImplemented();
1192     return S_FALSE;
1193 }
1194
1195 HRESULT AccessiblePopupMenu::get_accName(VARIANT vChild, __deref_out_opt BSTR* name)
1196 {
1197     return get_accValue(vChild, name);
1198 }
1199
1200 HRESULT AccessiblePopupMenu::get_accValue(VARIANT vChild, __deref_out_opt BSTR* value)
1201 {
1202     if (!value)
1203         return E_POINTER;
1204
1205     *value = nullptr;
1206
1207     if (vChild.vt != VT_I4)
1208         return E_INVALIDARG;
1209
1210     int index = vChild.lVal - 1;
1211
1212     if (index < 0)
1213         return E_INVALIDARG;
1214
1215     BString itemText(m_popupMenu.client()->itemText(index));
1216     *value = itemText.release();
1217
1218     return S_OK;
1219 }
1220
1221 HRESULT AccessiblePopupMenu::get_accDescription(VARIANT, __deref_out_opt BSTR*)
1222 {
1223     notImplemented();
1224     return E_NOTIMPL;
1225 }
1226
1227 HRESULT AccessiblePopupMenu::get_accRole(VARIANT vChild, _Out_ VARIANT* pvRole)
1228 {
1229     if (!pvRole)
1230         return E_POINTER;
1231     if (vChild.vt != VT_I4)
1232         return E_INVALIDARG;
1233
1234     // Scrollbar parts are encoded as negative values.
1235     if (vChild.lVal < 0) {
1236         V_VT(pvRole) = VT_I4;
1237         V_I4(pvRole) = ROLE_SYSTEM_SCROLLBAR;
1238     } else {
1239         V_VT(pvRole) = VT_I4;
1240         V_I4(pvRole) = ROLE_SYSTEM_LISTITEM;
1241     }
1242
1243     return S_OK;
1244 }
1245
1246 HRESULT AccessiblePopupMenu::get_accState(VARIANT vChild, _Out_ VARIANT* pvState)
1247 {
1248     if (!pvState)
1249         return E_POINTER;
1250
1251     if (vChild.vt != VT_I4)
1252         return E_INVALIDARG;
1253
1254     V_VT(pvState) = VT_I4;
1255     V_I4(pvState) = 0; // STATE_SYSTEM_NORMAL
1256     
1257     return S_OK;
1258 }
1259
1260 HRESULT AccessiblePopupMenu::get_accHelp(VARIANT vChild, __deref_out_opt BSTR* helpText)
1261 {
1262     notImplemented();
1263     return E_NOTIMPL;
1264 }
1265
1266 HRESULT AccessiblePopupMenu::get_accKeyboardShortcut(VARIANT vChild, __deref_out_opt BSTR*)
1267 {
1268     notImplemented();
1269     return E_NOTIMPL;
1270 }
1271
1272 HRESULT AccessiblePopupMenu::get_accFocus(_Out_ VARIANT* pvFocusedChild)
1273 {
1274     notImplemented();
1275     return E_NOTIMPL;
1276 }
1277
1278 HRESULT AccessiblePopupMenu::get_accSelection(_Out_ VARIANT* pvSelectedChild)
1279 {
1280     notImplemented();
1281     return E_NOTIMPL;
1282 }
1283
1284 HRESULT AccessiblePopupMenu::get_accDefaultAction(VARIANT vChild, __deref_out_opt BSTR* actionDescription)
1285 {
1286     notImplemented();
1287     return E_NOTIMPL;
1288 }
1289
1290 HRESULT AccessiblePopupMenu::accSelect(long selectionFlags, VARIANT vChild)
1291 {
1292     notImplemented();
1293     return E_NOTIMPL;
1294 }
1295
1296 HRESULT AccessiblePopupMenu::accLocation(_Out_ long* left, _Out_ long* top, _Out_ long* width, _Out_ long* height, VARIANT vChild)
1297 {
1298     if (!left || !top || !width || !height)
1299         return E_POINTER;
1300
1301     if (vChild.vt != VT_I4)
1302         return E_INVALIDARG;
1303
1304     const IntRect& windowRect = m_popupMenu.windowRect();
1305
1306     // Scrollbar parts are encoded as negative values.
1307     if (vChild.lVal < 0) {
1308         if (!m_popupMenu.scrollbar())
1309             return E_FAIL;
1310
1311         Scrollbar& scrollbar = *m_popupMenu.scrollbar();
1312         WebCore::ScrollbarPart part = static_cast<WebCore::ScrollbarPart>(-vChild.lVal);
1313
1314         ScrollbarThemeWin& theme = static_cast<ScrollbarThemeWin&>(scrollbar.theme());
1315
1316         IntRect partRect;
1317
1318         switch (part) {
1319         case BackTrackPart:
1320         case BackButtonStartPart:
1321             partRect = theme.backButtonRect(scrollbar, WebCore::BackTrackPart);
1322             break;
1323         case ThumbPart:
1324             partRect = theme.thumbRect(scrollbar);
1325             break;
1326         case ForwardTrackPart:
1327         case ForwardButtonEndPart:
1328             partRect = theme.forwardButtonRect(scrollbar, WebCore::ForwardTrackPart);
1329             break;
1330         case ScrollbarBGPart:
1331             partRect = theme.trackRect(scrollbar);
1332             break;
1333         default:
1334             return E_FAIL;
1335         }
1336
1337         partRect.move(windowRect.x(), windowRect.y());
1338
1339         *left = partRect.x();
1340         *top = partRect.y();
1341         *width = partRect.width();
1342         *height = partRect.height();
1343
1344         return S_OK;
1345     }
1346
1347     int index = vChild.lVal - 1;
1348
1349     if (index < 0)
1350         return E_INVALIDARG;
1351
1352     *left = windowRect.x();
1353     *top = windowRect.y() + (index - m_popupMenu.m_scrollOffset) * m_popupMenu.itemHeight();
1354     *width = windowRect.width();
1355     *height = m_popupMenu.itemHeight();
1356
1357     return S_OK;
1358 }
1359
1360 HRESULT AccessiblePopupMenu::accNavigate(long direction, VARIANT vFromChild, _Out_ VARIANT* pvNavigatedTo)
1361 {
1362     notImplemented();
1363     return E_NOTIMPL;
1364 }
1365
1366 HRESULT AccessiblePopupMenu::accHitTest(long x, long y, _Out_ VARIANT* pvChildAtPoint)
1367 {
1368     if (!pvChildAtPoint)
1369         return E_POINTER;
1370
1371     ::VariantInit(pvChildAtPoint);
1372
1373     IntRect windowRect = m_popupMenu.windowRect();
1374
1375     IntPoint pt(x - windowRect.x(), y - windowRect.y());
1376
1377     IntRect scrollRect;
1378
1379     if (m_popupMenu.scrollbar())
1380         scrollRect = m_popupMenu.scrollbar()->frameRect();
1381
1382     if (m_popupMenu.scrollbar() && scrollRect.contains(pt)) {
1383         Scrollbar& scrollbar = *m_popupMenu.scrollbar();
1384
1385         pt.move(-scrollRect.x(), -scrollRect.y());
1386
1387         WebCore::ScrollbarPart part = scrollbar.theme().hitTest(scrollbar, pt);
1388
1389         V_VT(pvChildAtPoint) = VT_I4;
1390         V_I4(pvChildAtPoint) = -part; // Scrollbar parts are encoded as negative, to avoid mixup with item indexes.
1391         return S_OK;
1392     }
1393
1394     int index = m_popupMenu.listIndexAtPoint(pt);
1395
1396     if (index < 0) {
1397         V_VT(pvChildAtPoint) = VT_EMPTY;
1398         return S_OK;
1399     }
1400
1401     V_VT(pvChildAtPoint) = VT_I4;
1402     V_I4(pvChildAtPoint) = index + 1; // CHILDID_SELF is 0, need to add 1.
1403
1404     return S_OK;
1405 }
1406
1407 HRESULT AccessiblePopupMenu::accDoDefaultAction(VARIANT vChild)
1408 {
1409     notImplemented();
1410     return E_NOTIMPL;
1411 }
1412
1413 HRESULT AccessiblePopupMenu::put_accName(VARIANT, BSTR)
1414 {
1415     notImplemented();
1416     return E_NOTIMPL;
1417 }
1418
1419 HRESULT AccessiblePopupMenu::put_accValue(VARIANT, BSTR)
1420 {
1421     notImplemented();
1422     return E_NOTIMPL;
1423 }
1424
1425 HRESULT AccessiblePopupMenu::get_accHelpTopic(BSTR* helpFile, VARIANT, _Out_ long* topicID)
1426 {
1427     notImplemented();
1428     return E_NOTIMPL;
1429 }
1430
1431 }