Use a 1-byte enum class for TextDirection
[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(WTFMove(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(WTFMove(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     scrollToRevealSelection();
576 }
577
578 const int separatorPadding = 4;
579 const int separatorHeight = 1;
580 void PopupMenuWin::paint(const IntRect& damageRect, HDC hdc)
581 {
582     if (!m_popup)
583         return;
584
585     if (!m_DC) {
586         m_DC = adoptGDIObject(::CreateCompatibleDC(HWndDC(m_popup)));
587         if (!m_DC)
588             return;
589     }
590
591     if (m_bmp) {
592         bool keepBitmap = false;
593         BITMAP bitmap;
594         if (::GetObject(m_bmp.get(), sizeof(bitmap), &bitmap))
595             keepBitmap = bitmap.bmWidth == clientRect().width()
596                 && bitmap.bmHeight == clientRect().height();
597         if (!keepBitmap)
598             m_bmp.clear();
599     }
600     if (!m_bmp) {
601         BitmapInfo bitmapInfo = BitmapInfo::createBottomUp(clientRect().size());
602         void* pixels = 0;
603         m_bmp = adoptGDIObject(::CreateDIBSection(m_DC.get(), &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0));
604         if (!m_bmp)
605             return;
606
607         ::SelectObject(m_DC.get(), m_bmp.get());
608     }
609
610     GraphicsContext context(m_DC.get());
611
612     int itemCount = client()->listSize();
613
614     // listRect is the damageRect translated into the coordinates of the entire menu list (which is itemCount * m_itemHeight pixels tall)
615     IntRect listRect = damageRect;
616     listRect.move(IntSize(0, m_scrollOffset * m_itemHeight));
617
618     for (int y = listRect.y(); y < listRect.maxY(); y += m_itemHeight) {
619         int index = y / m_itemHeight;
620
621         Color optionBackgroundColor, optionTextColor;
622         PopupMenuStyle itemStyle = client()->itemStyle(index);
623         if (index == focusedIndex()) {
624             optionBackgroundColor = RenderTheme::singleton().activeListBoxSelectionBackgroundColor({ });
625             optionTextColor = RenderTheme::singleton().activeListBoxSelectionForegroundColor({ });
626         } else {
627             optionBackgroundColor = itemStyle.backgroundColor();
628             optionTextColor = itemStyle.foregroundColor();
629         }
630
631         // itemRect is in client coordinates
632         IntRect itemRect(0, (index - m_scrollOffset) * m_itemHeight, damageRect.width(), m_itemHeight);
633
634         // Draw the background for this menu item
635         if (itemStyle.isVisible())
636             context.fillRect(itemRect, optionBackgroundColor);
637
638         if (client()->itemIsSeparator(index)) {
639             IntRect separatorRect(itemRect.x() + separatorPadding, itemRect.y() + (itemRect.height() - separatorHeight) / 2, itemRect.width() - 2 * separatorPadding, separatorHeight);
640             context.fillRect(separatorRect, optionTextColor);
641             continue;
642         }
643
644         String itemText = client()->itemText(index);
645
646         TextRun textRun(itemText, 0, 0, AllowTrailingExpansion, itemStyle.textDirection(), itemStyle.hasTextDirectionOverride());
647         context.setFillColor(optionTextColor);
648
649         FontCascade itemFont = m_font;
650         if (client()->itemIsLabel(index)) {
651             auto d = itemFont.fontDescription();
652             d.setWeight(d.bolderWeight());
653             itemFont = FontCascade(WTFMove(d), itemFont.letterSpacing(), itemFont.wordSpacing());
654             itemFont.update(m_popupClient->fontSelector());
655         }
656         
657         // Draw the item text
658         if (itemStyle.isVisible()) {
659             int textX = 0;
660             if (client()->menuStyle().textDirection() == TextDirection::LTR) {
661                 textX = std::max<int>(0, client()->clientPaddingLeft() - client()->clientInsetLeft());
662                 if (RenderTheme::singleton().popupOptionSupportsTextIndent())
663                     textX += minimumIntValueForLength(itemStyle.textIndent(), itemRect.width());
664             } else {
665                 textX = itemRect.width() - client()->menuStyle().font().width(textRun);
666                 textX = std::min<int>(textX, textX - client()->clientPaddingRight() + client()->clientInsetRight());
667                 if (RenderTheme::singleton().popupOptionSupportsTextIndent())
668                     textX -= minimumIntValueForLength(itemStyle.textIndent(), itemRect.width());
669             }
670             int textY = itemRect.y() + itemFont.fontMetrics().ascent() + (itemRect.height() - itemFont.fontMetrics().height()) / 2;
671             context.drawBidiText(itemFont, textRun, IntPoint(textX, textY));
672         }
673     }
674
675     if (m_scrollbar)
676         m_scrollbar->paint(context, damageRect);
677
678     HWndDC hWndDC;
679     HDC localDC = hdc ? hdc : hWndDC.setHWnd(m_popup);
680
681     ::BitBlt(localDC, damageRect.x(), damageRect.y(), damageRect.width(), damageRect.height(), m_DC.get(), damageRect.x(), damageRect.y(), SRCCOPY);
682 }
683
684 int PopupMenuWin::scrollSize(ScrollbarOrientation orientation) const
685 {
686     return ((orientation == VerticalScrollbar) && m_scrollbar) ? (m_scrollbar->totalSize() - m_scrollbar->visibleSize()) : 0;
687 }
688
689 int PopupMenuWin::scrollOffset(ScrollbarOrientation) const
690 {
691     return m_scrollOffset;
692 }
693
694 void PopupMenuWin::setScrollOffset(const IntPoint& offset)
695 {
696     scrollTo(offset.y());
697 }
698
699 void PopupMenuWin::scrollTo(int offset)
700 {
701     ASSERT(m_scrollbar);
702
703     if (!m_popup)
704         return;
705
706     if (m_scrollOffset == offset)
707         return;
708
709     int scrolledLines = m_scrollOffset - offset;
710     m_scrollOffset = offset;
711
712     UINT flags = SW_INVALIDATE;
713
714 #ifdef CAN_SET_SMOOTH_SCROLLING_DURATION
715     BOOL shouldSmoothScroll = FALSE;
716     ::SystemParametersInfo(SPI_GETLISTBOXSMOOTHSCROLLING, 0, &shouldSmoothScroll, 0);
717     if (shouldSmoothScroll)
718         flags |= MAKEWORD(SW_SMOOTHSCROLL, smoothScrollAnimationDuration);
719 #endif
720
721     IntRect listRect = clientRect();
722     if (m_scrollbar)
723         listRect.setWidth(listRect.width() - m_scrollbar->frameRect().width());
724     RECT r = listRect;
725     ::ScrollWindowEx(m_popup, 0, scrolledLines * m_itemHeight, &r, 0, 0, 0, flags);
726     if (m_scrollbar) {
727         r = m_scrollbar->frameRect();
728         ::InvalidateRect(m_popup, &r, TRUE);
729     }
730     ::UpdateWindow(m_popup);
731 }
732
733 void PopupMenuWin::invalidateScrollbarRect(Scrollbar& scrollbar, const IntRect& rect)
734 {
735     IntRect scrollRect = rect;
736     scrollRect.move(scrollbar.x(), scrollbar.y());
737     RECT r = scrollRect;
738     ::InvalidateRect(m_popup, &r, false);
739 }
740
741 IntSize PopupMenuWin::visibleSize() const
742 {
743     return IntSize(m_windowRect.width(), m_scrollbar ? m_scrollbar->visibleSize() : m_windowRect.height());
744 }
745
746 IntSize PopupMenuWin::contentsSize() const
747 {
748     return IntSize(m_windowRect.width(), m_scrollbar ? m_scrollbar->totalSize() : m_windowRect.height());
749 }
750
751 IntRect PopupMenuWin::scrollableAreaBoundingBox(bool*) const
752 {
753     return m_windowRect;
754 }
755
756 bool PopupMenuWin::onGetObject(WPARAM wParam, LPARAM lParam, LRESULT& lResult)
757 {
758     lResult = 0;
759
760     if (static_cast<LONG>(lParam) != OBJID_CLIENT)
761         return false;
762
763     if (!m_accessiblePopupMenu)
764         m_accessiblePopupMenu = new AccessiblePopupMenu(*this);
765
766     static HMODULE accessibilityLib = nullptr;
767     if (!accessibilityLib) {
768         if (!(accessibilityLib = ::LoadLibraryW(L"oleacc.dll")))
769             return false;
770     }
771
772     static LPFNLRESULTFROMOBJECT procPtr = reinterpret_cast<LPFNLRESULTFROMOBJECT>(::GetProcAddress(accessibilityLib, "LresultFromObject"));
773     if (!procPtr)
774         return false;
775
776     // LresultFromObject returns a reference to the accessible object, stored
777     // in an LRESULT. If this call is not successful, Windows will handle the
778     // request through DefWindowProc.
779     return SUCCEEDED(lResult = procPtr(__uuidof(IAccessible), wParam, m_accessiblePopupMenu.get()));
780 }
781
782 void PopupMenuWin::registerClass()
783 {
784     static bool haveRegisteredWindowClass = false;
785
786     if (haveRegisteredWindowClass)
787         return;
788
789     WNDCLASSEX wcex;
790     wcex.cbSize = sizeof(WNDCLASSEX);
791     wcex.hIconSm        = 0;
792     wcex.style          = CS_DROPSHADOW;
793
794     wcex.lpfnWndProc    = PopupMenuWndProc;
795     wcex.cbClsExtra     = 0;
796     wcex.cbWndExtra     = sizeof(PopupMenu*); // For the PopupMenu pointer
797     wcex.hInstance      = WebCore::instanceHandle();
798     wcex.hIcon          = 0;
799     wcex.hCursor        = LoadCursor(0, IDC_ARROW);
800     wcex.hbrBackground  = 0;
801     wcex.lpszMenuName   = 0;
802     wcex.lpszClassName  = kPopupWindowClassName;
803
804     haveRegisteredWindowClass = true;
805
806     RegisterClassEx(&wcex);
807 }
808
809
810 LRESULT CALLBACK PopupMenuWin::PopupMenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
811 {
812     if (PopupMenuWin* popup = static_cast<PopupMenuWin*>(getWindowPointer(hWnd, 0)))
813         return popup->wndProc(hWnd, message, wParam, lParam);
814
815     if (message == WM_CREATE) {
816         LPCREATESTRUCT createStruct = reinterpret_cast<LPCREATESTRUCT>(lParam);
817
818         // Associate the PopupMenu with the window.
819         setWindowPointer(hWnd, 0, createStruct->lpCreateParams);
820         return 0;
821     }
822
823     return ::DefWindowProc(hWnd, message, wParam, lParam);
824 }
825
826 const int smoothScrollAnimationDuration = 5000;
827
828 LRESULT PopupMenuWin::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
829 {
830     LRESULT lResult = 0;
831
832     switch (message) {
833         case WM_MOUSEACTIVATE:
834             return MA_NOACTIVATE;
835         case WM_SIZE: {
836             if (!scrollbar())
837                 break;
838
839             IntSize size(LOWORD(lParam), HIWORD(lParam));
840             scrollbar()->setFrameRect(IntRect(size.width() - scrollbar()->width(), 0, scrollbar()->width(), size.height()));
841
842             int visibleItems = this->visibleItems();
843             scrollbar()->setEnabled(visibleItems < client()->listSize());
844             scrollbar()->setSteps(1, std::max(1, visibleItems - 1));
845             scrollbar()->setProportion(visibleItems, client()->listSize());
846
847             break;
848         }
849         case WM_SYSKEYDOWN:
850         case WM_KEYDOWN: {
851             if (!client())
852                 break;
853
854             bool altKeyPressed = GetKeyState(VK_MENU) & HIGH_BIT_MASK_SHORT;
855             bool ctrlKeyPressed = GetKeyState(VK_CONTROL) & HIGH_BIT_MASK_SHORT;
856
857             lResult = 0;
858             switch (LOWORD(wParam)) {
859                 case VK_F4: {
860                     if (!altKeyPressed && !ctrlKeyPressed) {
861                         int index = focusedIndex();
862                         ASSERT(index >= 0);
863                         client()->valueChanged(index);
864                         hide();
865                     }
866                     break;
867                 }
868                 case VK_DOWN:
869                     if (altKeyPressed) {
870                         int index = focusedIndex();
871                         ASSERT(index >= 0);
872                         client()->valueChanged(index);
873                         hide();
874                     } else
875                         down();
876                     break;
877                 case VK_RIGHT:
878                     down();
879                     break;
880                 case VK_UP:
881                     if (altKeyPressed) {
882                         int index = focusedIndex();
883                         ASSERT(index >= 0);
884                         client()->valueChanged(index);
885                         hide();
886                     } else
887                         up();
888                     break;
889                 case VK_LEFT:
890                     up();
891                     break;
892                 case VK_HOME:
893                     focusFirst();
894                     break;
895                 case VK_END:
896                     focusLast();
897                     break;
898                 case VK_PRIOR:
899                     if (focusedIndex() != scrollOffset()) {
900                         // Set the selection to the first visible item
901                         int firstVisibleItem = scrollOffset();
902                         up(focusedIndex() - firstVisibleItem);
903                     } else {
904                         // The first visible item is selected, so move the selection back one page
905                         up(visibleItems());
906                     }
907                     break;
908                 case VK_NEXT: {
909                     int lastVisibleItem = scrollOffset() + visibleItems() - 1;
910                     if (focusedIndex() != lastVisibleItem) {
911                         // Set the selection to the last visible item
912                         down(lastVisibleItem - focusedIndex());
913                     } else {
914                         // The last visible item is selected, so move the selection forward one page
915                         down(visibleItems());
916                     }
917                     break;
918                 }
919                 case VK_TAB:
920                     ::SendMessage(client()->hostWindow()->platformPageClient(), message, wParam, lParam);
921                     hide();
922                     break;
923                 case VK_ESCAPE:
924                     hide();
925                     break;
926                 default:
927                     if (isASCIIPrintable(wParam))
928                         // Send the keydown to the WebView so it can be used for type-to-select.
929                         // Since we know that the virtual key is ASCII printable, it's OK to convert this to
930                         // a WM_CHAR message. (We don't want to call TranslateMessage because that will post a
931                         // WM_CHAR message that will be stolen and redirected to the popup HWND.
932                         ::PostMessage(m_popup, WM_HOST_WINDOW_CHAR, wParam, lParam);
933                     else
934                         lResult = 1;
935                     break;
936             }
937             break;
938         }
939         case WM_CHAR: {
940             if (!client())
941                 break;
942
943             lResult = 0;
944             int index;
945             switch (wParam) {
946                 case 0x0D:   // Enter/Return
947                     hide();
948                     index = focusedIndex();
949                     ASSERT(index >= 0);
950                     client()->valueChanged(index);
951                     break;
952                 case 0x1B:   // Escape
953                     hide();
954                     break;
955                 case 0x09:   // TAB
956                 case 0x08:   // Backspace
957                 case 0x0A:   // Linefeed
958                 default:     // Character
959                     lResult = 1;
960                     break;
961             }
962             break;
963         }
964         case WM_MOUSEMOVE: {
965             IntPoint mousePoint(MAKEPOINTS(lParam));
966             if (scrollbar()) {
967                 IntRect scrollBarRect = scrollbar()->frameRect();
968                 if (scrollbarCapturingMouse() || scrollBarRect.contains(mousePoint)) {
969                     // Put the point into coordinates relative to the scroll bar
970                     mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y());
971                     PlatformMouseEvent event(hWnd, message, wParam, makeScaledPoint(mousePoint, m_scaleFactor));
972                     scrollbar()->mouseMoved(event);
973                     break;
974                 }
975             }
976
977             BOOL shouldHotTrack = FALSE;
978             if (!::SystemParametersInfo(SPI_GETHOTTRACKING, 0, &shouldHotTrack, 0))
979                 shouldHotTrack = FALSE;
980
981             RECT bounds;
982             GetClientRect(popupHandle(), &bounds);
983             if (!::PtInRect(&bounds, mousePoint) && !(wParam & MK_LBUTTON) && client()) {
984                 // When the mouse is not inside the popup menu and the left button isn't down, just
985                 // repost the message to the web view.
986
987                 // Translate the coordinate.
988                 translatePoint(lParam, m_popup, client()->hostWindow()->platformPageClient());
989
990                 ::PostMessage(m_popup, WM_HOST_WINDOW_MOUSEMOVE, wParam, lParam);
991                 break;
992             }
993
994             if ((shouldHotTrack || wParam & MK_LBUTTON) && ::PtInRect(&bounds, mousePoint)) {
995                 setFocusedIndex(listIndexAtPoint(mousePoint), true);
996                 m_hoveredIndex = listIndexAtPoint(mousePoint);
997             }
998
999             break;
1000         }
1001         case WM_LBUTTONDOWN: {
1002             IntPoint mousePoint(MAKEPOINTS(lParam));
1003             if (scrollbar()) {
1004                 IntRect scrollBarRect = scrollbar()->frameRect();
1005                 if (scrollBarRect.contains(mousePoint)) {
1006                     // Put the point into coordinates relative to the scroll bar
1007                     mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y());
1008                     PlatformMouseEvent event(hWnd, message, wParam, makeScaledPoint(mousePoint, m_scaleFactor));
1009                     scrollbar()->mouseDown(event);
1010                     setScrollbarCapturingMouse(true);
1011                     break;
1012                 }
1013             }
1014
1015             // If the mouse is inside the window, update the focused index. Otherwise, 
1016             // hide the popup.
1017             RECT bounds;
1018             GetClientRect(m_popup, &bounds);
1019             if (::PtInRect(&bounds, mousePoint)) {
1020                 setFocusedIndex(listIndexAtPoint(mousePoint), true);
1021                 m_hoveredIndex = listIndexAtPoint(mousePoint);
1022             }
1023             else
1024                 hide();
1025             break;
1026         }
1027         case WM_LBUTTONUP: {
1028             IntPoint mousePoint(MAKEPOINTS(lParam));
1029             if (scrollbar()) {
1030                 IntRect scrollBarRect = scrollbar()->frameRect();
1031                 if (scrollbarCapturingMouse() || scrollBarRect.contains(mousePoint)) {
1032                     setScrollbarCapturingMouse(false);
1033                     // Put the point into coordinates relative to the scroll bar
1034                     mousePoint.move(-scrollBarRect.x(), -scrollBarRect.y());
1035                     PlatformMouseEvent event(hWnd, message, wParam, makeScaledPoint(mousePoint, m_scaleFactor));
1036                     scrollbar()->mouseUp(event);
1037                     // FIXME: This is a hack to work around Scrollbar not invalidating correctly when it doesn't have a parent widget
1038                     RECT r = scrollBarRect;
1039                     ::InvalidateRect(popupHandle(), &r, TRUE);
1040                     break;
1041                 }
1042             }
1043             // Only hide the popup if the mouse is inside the popup window.
1044             RECT bounds;
1045             GetClientRect(popupHandle(), &bounds);
1046             if (client() && ::PtInRect(&bounds, mousePoint)) {
1047                 hide();
1048                 int index = m_hoveredIndex;
1049                 if (!client()->itemIsEnabled(index))
1050                     index = client()->selectedIndex();
1051                 if (index >= 0)
1052                     client()->valueChanged(index);
1053             }
1054             break;
1055         }
1056
1057         case WM_MOUSEWHEEL: {
1058             if (!scrollbar())
1059                 break;
1060
1061             int i = 0;
1062             for (incrementWheelDelta(GET_WHEEL_DELTA_WPARAM(wParam)); abs(wheelDelta()) >= WHEEL_DELTA; reduceWheelDelta(WHEEL_DELTA)) {
1063                 if (wheelDelta() > 0)
1064                     ++i;
1065                 else
1066                     --i;
1067             }
1068
1069             ScrollableArea::scroll(i > 0 ? ScrollUp : ScrollDown, ScrollByLine, abs(i));
1070             break;
1071         }
1072
1073         case WM_PAINT: {
1074             PAINTSTRUCT paintInfo;
1075             ::BeginPaint(popupHandle(), &paintInfo);
1076             paint(paintInfo.rcPaint, paintInfo.hdc);
1077             ::EndPaint(popupHandle(), &paintInfo);
1078             lResult = 0;
1079             break;
1080         }
1081         case WM_PRINTCLIENT:
1082             paint(clientRect(), (HDC)wParam);
1083             break;
1084         case WM_GETOBJECT:
1085             onGetObject(wParam, lParam, lResult);
1086             break;
1087         default:
1088             lResult = DefWindowProc(hWnd, message, wParam, lParam);
1089     }
1090
1091     return lResult;
1092 }
1093
1094 AccessiblePopupMenu::AccessiblePopupMenu(const PopupMenuWin& popupMenu)
1095     : m_popupMenu(popupMenu)
1096 {
1097 }
1098
1099 AccessiblePopupMenu::~AccessiblePopupMenu() = default;
1100
1101 HRESULT AccessiblePopupMenu::QueryInterface(_In_ REFIID riid, _COM_Outptr_ void** ppvObject)
1102 {
1103     if (!ppvObject)
1104         return E_POINTER;
1105     if (IsEqualGUID(riid, __uuidof(IAccessible)))
1106         *ppvObject = static_cast<IAccessible*>(this);
1107     else if (IsEqualGUID(riid, __uuidof(IDispatch)))
1108         *ppvObject = static_cast<IAccessible*>(this);
1109     else if (IsEqualGUID(riid, __uuidof(IUnknown)))
1110         *ppvObject = static_cast<IAccessible*>(this);
1111     else {
1112         *ppvObject = nullptr;
1113         return E_NOINTERFACE;
1114     }
1115     AddRef();
1116     return S_OK;
1117 }
1118
1119 ULONG AccessiblePopupMenu::AddRef()
1120 {
1121     return ++m_refCount;
1122 }
1123
1124 ULONG AccessiblePopupMenu::Release()
1125 {
1126     int refCount = --m_refCount;
1127     if (!refCount)
1128         delete this;
1129     return refCount;
1130 }
1131
1132 HRESULT AccessiblePopupMenu::GetTypeInfoCount(_Out_ UINT* count)
1133 {
1134     if (!count)
1135         return E_POINTER;
1136     *count = 0;
1137     notImplemented();
1138     return E_NOTIMPL;
1139 }
1140
1141 HRESULT AccessiblePopupMenu::GetTypeInfo(UINT, LCID, _COM_Outptr_opt_ ITypeInfo** ppTInfo)
1142 {
1143     if (!ppTInfo)
1144         return E_POINTER;
1145     *ppTInfo = nullptr;
1146     notImplemented();
1147     return E_NOTIMPL;
1148 }
1149
1150 HRESULT AccessiblePopupMenu::GetIDsOfNames(_In_ REFIID, __in_ecount(cNames) LPOLESTR*, UINT cNames, LCID, __out_ecount_full(cNames) DISPID*)
1151 {
1152     notImplemented();
1153     return E_NOTIMPL;
1154 }
1155
1156 HRESULT AccessiblePopupMenu::Invoke(DISPID, REFIID, LCID, WORD, DISPPARAMS*, VARIANT*, EXCEPINFO*, UINT*)
1157 {
1158     notImplemented();
1159     return E_NOTIMPL;
1160 }
1161
1162 HRESULT AccessiblePopupMenu::get_accParent(_COM_Outptr_opt_ IDispatch** parent)
1163 {
1164     if (!parent)
1165         return E_POINTER;
1166     *parent = nullptr;
1167     notImplemented();
1168     return E_NOTIMPL;
1169 }
1170
1171 HRESULT AccessiblePopupMenu::get_accChildCount(_Out_ long* count)
1172 {
1173     if (!count)
1174         return E_POINTER;
1175
1176     *count = m_popupMenu.visibleItems();
1177     return S_OK;
1178 }
1179
1180 HRESULT AccessiblePopupMenu::get_accChild(VARIANT vChild, _COM_Outptr_opt_ IDispatch** ppChild)
1181 {
1182     if (!ppChild)
1183         return E_POINTER;
1184
1185     *ppChild = nullptr;
1186
1187     if (vChild.vt != VT_I4)
1188         return E_INVALIDARG;
1189
1190     notImplemented();
1191     return S_FALSE;
1192 }
1193
1194 HRESULT AccessiblePopupMenu::get_accName(VARIANT vChild, __deref_out_opt BSTR* name)
1195 {
1196     return get_accValue(vChild, name);
1197 }
1198
1199 HRESULT AccessiblePopupMenu::get_accValue(VARIANT vChild, __deref_out_opt BSTR* value)
1200 {
1201     if (!value)
1202         return E_POINTER;
1203
1204     *value = nullptr;
1205
1206     if (vChild.vt != VT_I4)
1207         return E_INVALIDARG;
1208
1209     int index = vChild.lVal - 1;
1210
1211     if (index < 0)
1212         return E_INVALIDARG;
1213
1214     BString itemText(m_popupMenu.client()->itemText(index));
1215     *value = itemText.release();
1216
1217     return S_OK;
1218 }
1219
1220 HRESULT AccessiblePopupMenu::get_accDescription(VARIANT, __deref_out_opt BSTR*)
1221 {
1222     notImplemented();
1223     return E_NOTIMPL;
1224 }
1225
1226 HRESULT AccessiblePopupMenu::get_accRole(VARIANT vChild, _Out_ VARIANT* pvRole)
1227 {
1228     if (!pvRole)
1229         return E_POINTER;
1230     if (vChild.vt != VT_I4)
1231         return E_INVALIDARG;
1232
1233     // Scrollbar parts are encoded as negative values.
1234     if (vChild.lVal < 0) {
1235         V_VT(pvRole) = VT_I4;
1236         V_I4(pvRole) = ROLE_SYSTEM_SCROLLBAR;
1237     } else {
1238         V_VT(pvRole) = VT_I4;
1239         V_I4(pvRole) = ROLE_SYSTEM_LISTITEM;
1240     }
1241
1242     return S_OK;
1243 }
1244
1245 HRESULT AccessiblePopupMenu::get_accState(VARIANT vChild, _Out_ VARIANT* pvState)
1246 {
1247     if (!pvState)
1248         return E_POINTER;
1249
1250     if (vChild.vt != VT_I4)
1251         return E_INVALIDARG;
1252
1253     V_VT(pvState) = VT_I4;
1254     V_I4(pvState) = 0; // STATE_SYSTEM_NORMAL
1255     
1256     return S_OK;
1257 }
1258
1259 HRESULT AccessiblePopupMenu::get_accHelp(VARIANT vChild, __deref_out_opt BSTR* helpText)
1260 {
1261     notImplemented();
1262     return E_NOTIMPL;
1263 }
1264
1265 HRESULT AccessiblePopupMenu::get_accKeyboardShortcut(VARIANT vChild, __deref_out_opt BSTR*)
1266 {
1267     notImplemented();
1268     return E_NOTIMPL;
1269 }
1270
1271 HRESULT AccessiblePopupMenu::get_accFocus(_Out_ VARIANT* pvFocusedChild)
1272 {
1273     notImplemented();
1274     return E_NOTIMPL;
1275 }
1276
1277 HRESULT AccessiblePopupMenu::get_accSelection(_Out_ VARIANT* pvSelectedChild)
1278 {
1279     notImplemented();
1280     return E_NOTIMPL;
1281 }
1282
1283 HRESULT AccessiblePopupMenu::get_accDefaultAction(VARIANT vChild, __deref_out_opt BSTR* actionDescription)
1284 {
1285     notImplemented();
1286     return E_NOTIMPL;
1287 }
1288
1289 HRESULT AccessiblePopupMenu::accSelect(long selectionFlags, VARIANT vChild)
1290 {
1291     notImplemented();
1292     return E_NOTIMPL;
1293 }
1294
1295 HRESULT AccessiblePopupMenu::accLocation(_Out_ long* left, _Out_ long* top, _Out_ long* width, _Out_ long* height, VARIANT vChild)
1296 {
1297     if (!left || !top || !width || !height)
1298         return E_POINTER;
1299
1300     if (vChild.vt != VT_I4)
1301         return E_INVALIDARG;
1302
1303     const IntRect& windowRect = m_popupMenu.windowRect();
1304
1305     // Scrollbar parts are encoded as negative values.
1306     if (vChild.lVal < 0) {
1307         if (!m_popupMenu.scrollbar())
1308             return E_FAIL;
1309
1310         Scrollbar& scrollbar = *m_popupMenu.scrollbar();
1311         WebCore::ScrollbarPart part = static_cast<WebCore::ScrollbarPart>(-vChild.lVal);
1312
1313         ScrollbarThemeWin& theme = static_cast<ScrollbarThemeWin&>(scrollbar.theme());
1314
1315         IntRect partRect;
1316
1317         switch (part) {
1318         case BackTrackPart:
1319         case BackButtonStartPart:
1320             partRect = theme.backButtonRect(scrollbar, WebCore::BackTrackPart);
1321             break;
1322         case ThumbPart:
1323             partRect = theme.thumbRect(scrollbar);
1324             break;
1325         case ForwardTrackPart:
1326         case ForwardButtonEndPart:
1327             partRect = theme.forwardButtonRect(scrollbar, WebCore::ForwardTrackPart);
1328             break;
1329         case ScrollbarBGPart:
1330             partRect = theme.trackRect(scrollbar);
1331             break;
1332         default:
1333             return E_FAIL;
1334         }
1335
1336         partRect.move(windowRect.x(), windowRect.y());
1337
1338         *left = partRect.x();
1339         *top = partRect.y();
1340         *width = partRect.width();
1341         *height = partRect.height();
1342
1343         return S_OK;
1344     }
1345
1346     int index = vChild.lVal - 1;
1347
1348     if (index < 0)
1349         return E_INVALIDARG;
1350
1351     *left = windowRect.x();
1352     *top = windowRect.y() + (index - m_popupMenu.m_scrollOffset) * m_popupMenu.itemHeight();
1353     *width = windowRect.width();
1354     *height = m_popupMenu.itemHeight();
1355
1356     return S_OK;
1357 }
1358
1359 HRESULT AccessiblePopupMenu::accNavigate(long direction, VARIANT vFromChild, _Out_ VARIANT* pvNavigatedTo)
1360 {
1361     notImplemented();
1362     return E_NOTIMPL;
1363 }
1364
1365 HRESULT AccessiblePopupMenu::accHitTest(long x, long y, _Out_ VARIANT* pvChildAtPoint)
1366 {
1367     if (!pvChildAtPoint)
1368         return E_POINTER;
1369
1370     ::VariantInit(pvChildAtPoint);
1371
1372     IntRect windowRect = m_popupMenu.windowRect();
1373
1374     IntPoint pt(x - windowRect.x(), y - windowRect.y());
1375
1376     IntRect scrollRect;
1377
1378     if (m_popupMenu.scrollbar())
1379         scrollRect = m_popupMenu.scrollbar()->frameRect();
1380
1381     if (m_popupMenu.scrollbar() && scrollRect.contains(pt)) {
1382         Scrollbar& scrollbar = *m_popupMenu.scrollbar();
1383
1384         pt.move(-scrollRect.x(), -scrollRect.y());
1385
1386         WebCore::ScrollbarPart part = scrollbar.theme().hitTest(scrollbar, pt);
1387
1388         V_VT(pvChildAtPoint) = VT_I4;
1389         V_I4(pvChildAtPoint) = -part; // Scrollbar parts are encoded as negative, to avoid mixup with item indexes.
1390         return S_OK;
1391     }
1392
1393     int index = m_popupMenu.listIndexAtPoint(pt);
1394
1395     if (index < 0) {
1396         V_VT(pvChildAtPoint) = VT_EMPTY;
1397         return S_OK;
1398     }
1399
1400     V_VT(pvChildAtPoint) = VT_I4;
1401     V_I4(pvChildAtPoint) = index + 1; // CHILDID_SELF is 0, need to add 1.
1402
1403     return S_OK;
1404 }
1405
1406 HRESULT AccessiblePopupMenu::accDoDefaultAction(VARIANT vChild)
1407 {
1408     notImplemented();
1409     return E_NOTIMPL;
1410 }
1411
1412 HRESULT AccessiblePopupMenu::put_accName(VARIANT, BSTR)
1413 {
1414     notImplemented();
1415     return E_NOTIMPL;
1416 }
1417
1418 HRESULT AccessiblePopupMenu::put_accValue(VARIANT, BSTR)
1419 {
1420     notImplemented();
1421     return E_NOTIMPL;
1422 }
1423
1424 HRESULT AccessiblePopupMenu::get_accHelpTopic(BSTR* helpFile, VARIANT, _Out_ long* topicID)
1425 {
1426     notImplemented();
1427     return E_NOTIMPL;
1428 }
1429
1430 }