RenderTheme does not need to be per-page
[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()
1101 {
1102 }
1103
1104 HRESULT AccessiblePopupMenu::QueryInterface(_In_ REFIID riid, _COM_Outptr_ void** ppvObject)
1105 {
1106     if (!ppvObject)
1107         return E_POINTER;
1108     if (IsEqualGUID(riid, __uuidof(IAccessible)))
1109         *ppvObject = static_cast<IAccessible*>(this);
1110     else if (IsEqualGUID(riid, __uuidof(IDispatch)))
1111         *ppvObject = static_cast<IAccessible*>(this);
1112     else if (IsEqualGUID(riid, __uuidof(IUnknown)))
1113         *ppvObject = static_cast<IAccessible*>(this);
1114     else {
1115         *ppvObject = nullptr;
1116         return E_NOINTERFACE;
1117     }
1118     AddRef();
1119     return S_OK;
1120 }
1121
1122 ULONG AccessiblePopupMenu::AddRef()
1123 {
1124     return ++m_refCount;
1125 }
1126
1127 ULONG AccessiblePopupMenu::Release()
1128 {
1129     int refCount = --m_refCount;
1130     if (!refCount)
1131         delete this;
1132     return refCount;
1133 }
1134
1135 HRESULT AccessiblePopupMenu::GetTypeInfoCount(_Out_ UINT* count)
1136 {
1137     if (!count)
1138         return E_POINTER;
1139     *count = 0;
1140     notImplemented();
1141     return E_NOTIMPL;
1142 }
1143
1144 HRESULT AccessiblePopupMenu::GetTypeInfo(UINT, LCID, _COM_Outptr_opt_ ITypeInfo** ppTInfo)
1145 {
1146     if (!ppTInfo)
1147         return E_POINTER;
1148     *ppTInfo = nullptr;
1149     notImplemented();
1150     return E_NOTIMPL;
1151 }
1152
1153 HRESULT AccessiblePopupMenu::GetIDsOfNames(_In_ REFIID, __in_ecount(cNames) LPOLESTR*, UINT cNames, LCID, __out_ecount_full(cNames) DISPID*)
1154 {
1155     notImplemented();
1156     return E_NOTIMPL;
1157 }
1158
1159 HRESULT AccessiblePopupMenu::Invoke(DISPID, REFIID, LCID, WORD, DISPPARAMS*, VARIANT*, EXCEPINFO*, UINT*)
1160 {
1161     notImplemented();
1162     return E_NOTIMPL;
1163 }
1164
1165 HRESULT AccessiblePopupMenu::get_accParent(_COM_Outptr_opt_ IDispatch** parent)
1166 {
1167     if (!parent)
1168         return E_POINTER;
1169     *parent = nullptr;
1170     notImplemented();
1171     return E_NOTIMPL;
1172 }
1173
1174 HRESULT AccessiblePopupMenu::get_accChildCount(_Out_ long* count)
1175 {
1176     if (!count)
1177         return E_POINTER;
1178
1179     *count = m_popupMenu.visibleItems();
1180     return S_OK;
1181 }
1182
1183 HRESULT AccessiblePopupMenu::get_accChild(VARIANT vChild, _COM_Outptr_opt_ IDispatch** ppChild)
1184 {
1185     if (!ppChild)
1186         return E_POINTER;
1187
1188     *ppChild = nullptr;
1189
1190     if (vChild.vt != VT_I4)
1191         return E_INVALIDARG;
1192
1193     notImplemented();
1194     return S_FALSE;
1195 }
1196
1197 HRESULT AccessiblePopupMenu::get_accName(VARIANT vChild, __deref_out_opt BSTR* name)
1198 {
1199     return get_accValue(vChild, name);
1200 }
1201
1202 HRESULT AccessiblePopupMenu::get_accValue(VARIANT vChild, __deref_out_opt BSTR* value)
1203 {
1204     if (!value)
1205         return E_POINTER;
1206
1207     *value = nullptr;
1208
1209     if (vChild.vt != VT_I4)
1210         return E_INVALIDARG;
1211
1212     int index = vChild.lVal - 1;
1213
1214     if (index < 0)
1215         return E_INVALIDARG;
1216
1217     BString itemText(m_popupMenu.client()->itemText(index));
1218     *value = itemText.release();
1219
1220     return S_OK;
1221 }
1222
1223 HRESULT AccessiblePopupMenu::get_accDescription(VARIANT, __deref_out_opt BSTR*)
1224 {
1225     notImplemented();
1226     return E_NOTIMPL;
1227 }
1228
1229 HRESULT AccessiblePopupMenu::get_accRole(VARIANT vChild, _Out_ VARIANT* pvRole)
1230 {
1231     if (!pvRole)
1232         return E_POINTER;
1233     if (vChild.vt != VT_I4)
1234         return E_INVALIDARG;
1235
1236     // Scrollbar parts are encoded as negative values.
1237     if (vChild.lVal < 0) {
1238         V_VT(pvRole) = VT_I4;
1239         V_I4(pvRole) = ROLE_SYSTEM_SCROLLBAR;
1240     } else {
1241         V_VT(pvRole) = VT_I4;
1242         V_I4(pvRole) = ROLE_SYSTEM_LISTITEM;
1243     }
1244
1245     return S_OK;
1246 }
1247
1248 HRESULT AccessiblePopupMenu::get_accState(VARIANT vChild, _Out_ VARIANT* pvState)
1249 {
1250     if (!pvState)
1251         return E_POINTER;
1252
1253     if (vChild.vt != VT_I4)
1254         return E_INVALIDARG;
1255
1256     V_VT(pvState) = VT_I4;
1257     V_I4(pvState) = 0; // STATE_SYSTEM_NORMAL
1258     
1259     return S_OK;
1260 }
1261
1262 HRESULT AccessiblePopupMenu::get_accHelp(VARIANT vChild, __deref_out_opt BSTR* helpText)
1263 {
1264     notImplemented();
1265     return E_NOTIMPL;
1266 }
1267
1268 HRESULT AccessiblePopupMenu::get_accKeyboardShortcut(VARIANT vChild, __deref_out_opt BSTR*)
1269 {
1270     notImplemented();
1271     return E_NOTIMPL;
1272 }
1273
1274 HRESULT AccessiblePopupMenu::get_accFocus(_Out_ VARIANT* pvFocusedChild)
1275 {
1276     notImplemented();
1277     return E_NOTIMPL;
1278 }
1279
1280 HRESULT AccessiblePopupMenu::get_accSelection(_Out_ VARIANT* pvSelectedChild)
1281 {
1282     notImplemented();
1283     return E_NOTIMPL;
1284 }
1285
1286 HRESULT AccessiblePopupMenu::get_accDefaultAction(VARIANT vChild, __deref_out_opt BSTR* actionDescription)
1287 {
1288     notImplemented();
1289     return E_NOTIMPL;
1290 }
1291
1292 HRESULT AccessiblePopupMenu::accSelect(long selectionFlags, VARIANT vChild)
1293 {
1294     notImplemented();
1295     return E_NOTIMPL;
1296 }
1297
1298 HRESULT AccessiblePopupMenu::accLocation(_Out_ long* left, _Out_ long* top, _Out_ long* width, _Out_ long* height, VARIANT vChild)
1299 {
1300     if (!left || !top || !width || !height)
1301         return E_POINTER;
1302
1303     if (vChild.vt != VT_I4)
1304         return E_INVALIDARG;
1305
1306     const IntRect& windowRect = m_popupMenu.windowRect();
1307
1308     // Scrollbar parts are encoded as negative values.
1309     if (vChild.lVal < 0) {
1310         if (!m_popupMenu.scrollbar())
1311             return E_FAIL;
1312
1313         Scrollbar& scrollbar = *m_popupMenu.scrollbar();
1314         WebCore::ScrollbarPart part = static_cast<WebCore::ScrollbarPart>(-vChild.lVal);
1315
1316         ScrollbarThemeWin& theme = static_cast<ScrollbarThemeWin&>(scrollbar.theme());
1317
1318         IntRect partRect;
1319
1320         switch (part) {
1321         case BackTrackPart:
1322         case BackButtonStartPart:
1323             partRect = theme.backButtonRect(scrollbar, WebCore::BackTrackPart);
1324             break;
1325         case ThumbPart:
1326             partRect = theme.thumbRect(scrollbar);
1327             break;
1328         case ForwardTrackPart:
1329         case ForwardButtonEndPart:
1330             partRect = theme.forwardButtonRect(scrollbar, WebCore::ForwardTrackPart);
1331             break;
1332         case ScrollbarBGPart:
1333             partRect = theme.trackRect(scrollbar);
1334             break;
1335         default:
1336             return E_FAIL;
1337         }
1338
1339         partRect.move(windowRect.x(), windowRect.y());
1340
1341         *left = partRect.x();
1342         *top = partRect.y();
1343         *width = partRect.width();
1344         *height = partRect.height();
1345
1346         return S_OK;
1347     }
1348
1349     int index = vChild.lVal - 1;
1350
1351     if (index < 0)
1352         return E_INVALIDARG;
1353
1354     *left = windowRect.x();
1355     *top = windowRect.y() + (index - m_popupMenu.m_scrollOffset) * m_popupMenu.itemHeight();
1356     *width = windowRect.width();
1357     *height = m_popupMenu.itemHeight();
1358
1359     return S_OK;
1360 }
1361
1362 HRESULT AccessiblePopupMenu::accNavigate(long direction, VARIANT vFromChild, _Out_ VARIANT* pvNavigatedTo)
1363 {
1364     notImplemented();
1365     return E_NOTIMPL;
1366 }
1367
1368 HRESULT AccessiblePopupMenu::accHitTest(long x, long y, _Out_ VARIANT* pvChildAtPoint)
1369 {
1370     if (!pvChildAtPoint)
1371         return E_POINTER;
1372
1373     ::VariantInit(pvChildAtPoint);
1374
1375     IntRect windowRect = m_popupMenu.windowRect();
1376
1377     IntPoint pt(x - windowRect.x(), y - windowRect.y());
1378
1379     IntRect scrollRect;
1380
1381     if (m_popupMenu.scrollbar())
1382         scrollRect = m_popupMenu.scrollbar()->frameRect();
1383
1384     if (m_popupMenu.scrollbar() && scrollRect.contains(pt)) {
1385         Scrollbar& scrollbar = *m_popupMenu.scrollbar();
1386
1387         pt.move(-scrollRect.x(), -scrollRect.y());
1388
1389         WebCore::ScrollbarPart part = scrollbar.theme().hitTest(scrollbar, pt);
1390
1391         V_VT(pvChildAtPoint) = VT_I4;
1392         V_I4(pvChildAtPoint) = -part; // Scrollbar parts are encoded as negative, to avoid mixup with item indexes.
1393         return S_OK;
1394     }
1395
1396     int index = m_popupMenu.listIndexAtPoint(pt);
1397
1398     if (index < 0) {
1399         V_VT(pvChildAtPoint) = VT_EMPTY;
1400         return S_OK;
1401     }
1402
1403     V_VT(pvChildAtPoint) = VT_I4;
1404     V_I4(pvChildAtPoint) = index + 1; // CHILDID_SELF is 0, need to add 1.
1405
1406     return S_OK;
1407 }
1408
1409 HRESULT AccessiblePopupMenu::accDoDefaultAction(VARIANT vChild)
1410 {
1411     notImplemented();
1412     return E_NOTIMPL;
1413 }
1414
1415 HRESULT AccessiblePopupMenu::put_accName(VARIANT, BSTR)
1416 {
1417     notImplemented();
1418     return E_NOTIMPL;
1419 }
1420
1421 HRESULT AccessiblePopupMenu::put_accValue(VARIANT, BSTR)
1422 {
1423     notImplemented();
1424     return E_NOTIMPL;
1425 }
1426
1427 HRESULT AccessiblePopupMenu::get_accHelpTopic(BSTR* helpFile, VARIANT, _Out_ long* topicID)
1428 {
1429     notImplemented();
1430     return E_NOTIMPL;
1431 }
1432
1433 }