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