• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2011, Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "config.h"
32 #include "web/PopupContainer.h"
33 
34 #include "core/dom/Document.h"
35 #include "core/frame/FrameView.h"
36 #include "core/frame/LocalFrame.h"
37 #include "core/page/Chrome.h"
38 #include "core/page/ChromeClient.h"
39 #include "core/page/Page.h"
40 #include "platform/PlatformGestureEvent.h"
41 #include "platform/PlatformKeyboardEvent.h"
42 #include "platform/PlatformMouseEvent.h"
43 #include "platform/PlatformScreen.h"
44 #include "platform/PlatformTouchEvent.h"
45 #include "platform/PlatformWheelEvent.h"
46 #include "platform/PopupMenuClient.h"
47 #include "platform/UserGestureIndicator.h"
48 #include "platform/geometry/IntRect.h"
49 #include "platform/graphics/GraphicsContext.h"
50 #include "platform/scroll/FramelessScrollViewClient.h"
51 #include "public/web/WebPopupMenuInfo.h"
52 #include "public/web/WebPopupType.h"
53 #include "public/web/WebViewClient.h"
54 #include "web/WebPopupMenuImpl.h"
55 #include "web/WebViewImpl.h"
56 #include <limits>
57 
58 namespace blink {
59 
60 using namespace WebCore;
61 
62 static const int borderSize = 1;
63 
constructRelativeMouseEvent(const PlatformMouseEvent & e,FramelessScrollView * parent,FramelessScrollView * child)64 static PlatformMouseEvent constructRelativeMouseEvent(const PlatformMouseEvent& e, FramelessScrollView* parent, FramelessScrollView* child)
65 {
66     IntPoint pos = parent->convertSelfToChild(child, e.position());
67 
68     // FIXME: This is a horrible hack since PlatformMouseEvent has no setters for x/y.
69     PlatformMouseEvent relativeEvent = e;
70     IntPoint& relativePos = const_cast<IntPoint&>(relativeEvent.position());
71     relativePos.setX(pos.x());
72     relativePos.setY(pos.y());
73     return relativeEvent;
74 }
75 
constructRelativeWheelEvent(const PlatformWheelEvent & e,FramelessScrollView * parent,FramelessScrollView * child)76 static PlatformWheelEvent constructRelativeWheelEvent(const PlatformWheelEvent& e, FramelessScrollView* parent, FramelessScrollView* child)
77 {
78     IntPoint pos = parent->convertSelfToChild(child, e.position());
79 
80     // FIXME: This is a horrible hack since PlatformWheelEvent has no setters for x/y.
81     PlatformWheelEvent relativeEvent = e;
82     IntPoint& relativePos = const_cast<IntPoint&>(relativeEvent.position());
83     relativePos.setX(pos.x());
84     relativePos.setY(pos.y());
85     return relativeEvent;
86 }
87 
88 // static
create(PopupMenuClient * client,bool deviceSupportsTouch)89 PassRefPtr<PopupContainer> PopupContainer::create(PopupMenuClient* client, bool deviceSupportsTouch)
90 {
91     return adoptRef(new PopupContainer(client, deviceSupportsTouch));
92 }
93 
PopupContainer(PopupMenuClient * client,bool deviceSupportsTouch)94 PopupContainer::PopupContainer(PopupMenuClient* client, bool deviceSupportsTouch)
95     : m_listBox(PopupListBox::create(client, deviceSupportsTouch))
96     , m_popupOpen(false)
97 {
98     setScrollbarModes(ScrollbarAlwaysOff, ScrollbarAlwaysOff);
99 }
100 
~PopupContainer()101 PopupContainer::~PopupContainer()
102 {
103     if (m_listBox && m_listBox->parent())
104         removeChild(m_listBox.get());
105 }
106 
layoutAndCalculateWidgetRectInternal(IntRect widgetRectInScreen,int targetControlHeight,const FloatRect & windowRect,const FloatRect & screen,bool isRTL,const int rtlOffset,const int verticalOffset,const IntSize & transformOffset,PopupContent * listBox,bool & needToResizeView)107 IntRect PopupContainer::layoutAndCalculateWidgetRectInternal(IntRect widgetRectInScreen, int targetControlHeight, const FloatRect& windowRect, const FloatRect& screen, bool isRTL, const int rtlOffset, const int verticalOffset, const IntSize& transformOffset, PopupContent* listBox, bool& needToResizeView)
108 {
109     ASSERT(listBox);
110     if (windowRect.x() >= screen.x() && windowRect.maxX() <= screen.maxX() && (widgetRectInScreen.x() < screen.x() || widgetRectInScreen.maxX() > screen.maxX())) {
111         // First, inverse the popup alignment if it does not fit the screen -
112         // this might fix things (or make them better).
113         IntRect inverseWidgetRectInScreen = widgetRectInScreen;
114         inverseWidgetRectInScreen.setX(inverseWidgetRectInScreen.x() + (isRTL ? -rtlOffset : rtlOffset));
115         inverseWidgetRectInScreen.setY(inverseWidgetRectInScreen.y() + (isRTL ? -verticalOffset : verticalOffset));
116         IntRect enclosingScreen = enclosingIntRect(screen);
117         unsigned originalCutoff = std::max(enclosingScreen.x() - widgetRectInScreen.x(), 0) + std::max(widgetRectInScreen.maxX() - enclosingScreen.maxX(), 0);
118         unsigned inverseCutoff = std::max(enclosingScreen.x() - inverseWidgetRectInScreen.x(), 0) + std::max(inverseWidgetRectInScreen.maxX() - enclosingScreen.maxX(), 0);
119 
120         // Accept the inverse popup alignment if the trimmed content gets
121         // shorter than that in the original alignment case.
122         if (inverseCutoff < originalCutoff)
123             widgetRectInScreen = inverseWidgetRectInScreen;
124 
125         if (widgetRectInScreen.x() < screen.x()) {
126             widgetRectInScreen.setWidth(widgetRectInScreen.maxX() - screen.x());
127             widgetRectInScreen.setX(screen.x());
128             listBox->setMaxWidthAndLayout(std::max(widgetRectInScreen.width() - borderSize * 2, 0));
129         } else if (widgetRectInScreen.maxX() > screen.maxX()) {
130             widgetRectInScreen.setWidth(screen.maxX() - widgetRectInScreen.x());
131             listBox->setMaxWidthAndLayout(std::max(widgetRectInScreen.width() - borderSize * 2, 0));
132         }
133     }
134 
135     // Calculate Y axis size.
136     if (widgetRectInScreen.maxY() > static_cast<int>(screen.maxY())) {
137         if (widgetRectInScreen.y() - widgetRectInScreen.height() - targetControlHeight - transformOffset.height() > 0) {
138             // There is enough room to open upwards.
139             widgetRectInScreen.move(-transformOffset.width(), -(widgetRectInScreen.height() + targetControlHeight + transformOffset.height()));
140         } else {
141             // Figure whether upwards or downwards has more room and set the
142             // maximum number of items.
143             int spaceAbove = widgetRectInScreen.y() - targetControlHeight + transformOffset.height();
144             int spaceBelow = screen.maxY() - widgetRectInScreen.y();
145             if (spaceAbove > spaceBelow)
146                 listBox->setMaxHeight(spaceAbove);
147             else
148                 listBox->setMaxHeight(spaceBelow);
149             listBox->layout();
150             needToResizeView = true;
151             widgetRectInScreen.setHeight(listBox->popupContentHeight() + borderSize * 2);
152             // Move WebWidget upwards if necessary.
153             if (spaceAbove > spaceBelow)
154                 widgetRectInScreen.move(-transformOffset.width(), -(widgetRectInScreen.height() + targetControlHeight + transformOffset.height()));
155         }
156     }
157     return widgetRectInScreen;
158 }
159 
layoutAndCalculateWidgetRect(int targetControlHeight,const IntSize & transformOffset,const IntPoint & popupInitialCoordinate)160 IntRect PopupContainer::layoutAndCalculateWidgetRect(int targetControlHeight, const IntSize& transformOffset, const IntPoint& popupInitialCoordinate)
161 {
162     // Reset the max width and height to their default values, they will be
163     // recomputed below if necessary.
164     m_listBox->setMaxHeight(PopupListBox::defaultMaxHeight);
165     m_listBox->setMaxWidth(std::numeric_limits<int>::max());
166 
167     // Lay everything out to figure out our preferred size, then tell the view's
168     // WidgetClient about it. It should assign us a client.
169     m_listBox->layout();
170     fitToListBox();
171     bool isRTL = this->isRTL();
172 
173     // Compute the starting x-axis for a normal RTL or right-aligned LTR
174     // dropdown. For those, the right edge of dropdown box should be aligned
175     // with the right edge of <select>/<input> element box, and the dropdown box
176     // should be expanded to the left if more space is needed.
177     // m_originalFrameRect.width() is the width of the target <select>/<input>
178     // element.
179     int rtlOffset = m_controlPosition.p2().x() - m_controlPosition.p1().x() - (m_listBox->width() + borderSize * 2);
180     int rightOffset = isRTL ? rtlOffset : 0;
181 
182     // Compute the y-axis offset between the bottom left and bottom right
183     // points. If the <select>/<input> is transformed, they are not the same.
184     int verticalOffset = - m_controlPosition.p4().y() + m_controlPosition.p3().y();
185     int verticalForRTLOffset = isRTL ? verticalOffset : 0;
186 
187     // Assume m_listBox size is already calculated.
188     IntSize targetSize(m_listBox->width() + borderSize * 2, m_listBox->height() + borderSize * 2);
189 
190     IntRect widgetRectInScreen;
191     // If the popup would extend past the bottom of the screen, open upwards
192     // instead.
193     FloatRect screen = screenAvailableRect(m_frameView.get());
194     // Use popupInitialCoordinate.x() + rightOffset because RTL position
195     // needs to be considered.
196     float pageScaleFactor = m_frameView->frame().page()->pageScaleFactor();
197     int popupX = round((popupInitialCoordinate.x() + rightOffset) * pageScaleFactor);
198     int popupY = round((popupInitialCoordinate.y() + verticalForRTLOffset) * pageScaleFactor);
199     widgetRectInScreen = chromeClient().rootViewToScreen(IntRect(popupX, popupY, targetSize.width(), targetSize.height()));
200 
201     // If we have multiple screens and the browser rect is in one screen, we
202     // have to clip the window width to the screen width.
203     // When clipping, we also need to set a maximum width for the list box.
204     FloatRect windowRect = chromeClient().windowRect();
205 
206     bool needToResizeView = false;
207     widgetRectInScreen = layoutAndCalculateWidgetRectInternal(widgetRectInScreen, targetControlHeight, windowRect, screen, isRTL, rtlOffset, verticalOffset, transformOffset, m_listBox.get(), needToResizeView);
208     if (needToResizeView)
209         fitToListBox();
210 
211     return widgetRectInScreen;
212 }
213 
showPopup(FrameView * view)214 void PopupContainer::showPopup(FrameView* view)
215 {
216     m_frameView = view;
217     listBox()->m_focusedElement = m_frameView->frame().document()->focusedElement();
218 
219     IntSize transformOffset(m_controlPosition.p4().x() - m_controlPosition.p1().x(), m_controlPosition.p4().y() - m_controlPosition.p1().y() - m_controlSize.height());
220     popupOpened(layoutAndCalculateWidgetRect(m_controlSize.height(), transformOffset, roundedIntPoint(m_controlPosition.p4())));
221     m_popupOpen = true;
222 
223     if (!m_listBox->parent())
224         addChild(m_listBox.get());
225 
226     // Enable scrollbars after the listbox is inserted into the hierarchy,
227     // so it has a proper WidgetClient.
228     m_listBox->setVerticalScrollbarMode(ScrollbarAuto);
229 
230     m_listBox->scrollToRevealSelection();
231 
232     invalidate();
233 }
234 
hidePopup()235 void PopupContainer::hidePopup()
236 {
237     listBox()->abandon();
238 }
239 
notifyPopupHidden()240 void PopupContainer::notifyPopupHidden()
241 {
242     if (!m_popupOpen)
243         return;
244     m_popupOpen = false;
245     WebViewImpl::fromPage(m_frameView->frame().page())->popupClosed(this);
246 }
247 
fitToListBox()248 void PopupContainer::fitToListBox()
249 {
250     // Place the listbox within our border.
251     m_listBox->move(borderSize, borderSize);
252 
253     // Size ourselves to contain listbox + border.
254     resize(m_listBox->width() + borderSize * 2, m_listBox->height() + borderSize * 2);
255     invalidate();
256 }
257 
handleMouseDownEvent(const PlatformMouseEvent & event)258 bool PopupContainer::handleMouseDownEvent(const PlatformMouseEvent& event)
259 {
260     UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
261     return m_listBox->handleMouseDownEvent(
262         constructRelativeMouseEvent(event, this, m_listBox.get()));
263 }
264 
handleMouseMoveEvent(const PlatformMouseEvent & event)265 bool PopupContainer::handleMouseMoveEvent(const PlatformMouseEvent& event)
266 {
267     UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
268     return m_listBox->handleMouseMoveEvent(
269         constructRelativeMouseEvent(event, this, m_listBox.get()));
270 }
271 
handleMouseReleaseEvent(const PlatformMouseEvent & event)272 bool PopupContainer::handleMouseReleaseEvent(const PlatformMouseEvent& event)
273 {
274     RefPtr<PopupContainer> protect(this);
275     UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
276     return m_listBox->handleMouseReleaseEvent(
277         constructRelativeMouseEvent(event, this, m_listBox.get()));
278 }
279 
handleWheelEvent(const PlatformWheelEvent & event)280 bool PopupContainer::handleWheelEvent(const PlatformWheelEvent& event)
281 {
282     UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
283     return m_listBox->handleWheelEvent(
284         constructRelativeWheelEvent(event, this, m_listBox.get()));
285 }
286 
handleTouchEvent(const PlatformTouchEvent &)287 bool PopupContainer::handleTouchEvent(const PlatformTouchEvent&)
288 {
289     return false;
290 }
291 
292 // FIXME: Refactor this code to share functionality with
293 // EventHandler::handleGestureEvent.
handleGestureEvent(const PlatformGestureEvent & gestureEvent)294 bool PopupContainer::handleGestureEvent(const PlatformGestureEvent& gestureEvent)
295 {
296     switch (gestureEvent.type()) {
297     case PlatformEvent::GestureTap: {
298         PlatformMouseEvent fakeMouseMove(gestureEvent.position(), gestureEvent.globalPosition(), NoButton, PlatformEvent::MouseMoved, /* clickCount */ 1, gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey(), gestureEvent.timestamp());
299         PlatformMouseEvent fakeMouseDown(gestureEvent.position(), gestureEvent.globalPosition(), LeftButton, PlatformEvent::MousePressed, /* clickCount */ 1, gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey(), gestureEvent.timestamp());
300         PlatformMouseEvent fakeMouseUp(gestureEvent.position(), gestureEvent.globalPosition(), LeftButton, PlatformEvent::MouseReleased, /* clickCount */ 1, gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey(), gestureEvent.timestamp());
301         // handleMouseMoveEvent(fakeMouseMove);
302         handleMouseDownEvent(fakeMouseDown);
303         handleMouseReleaseEvent(fakeMouseUp);
304         return true;
305     }
306     case PlatformEvent::GestureScrollUpdate:
307     case PlatformEvent::GestureScrollUpdateWithoutPropagation: {
308         PlatformWheelEvent syntheticWheelEvent(gestureEvent.position(), gestureEvent.globalPosition(), gestureEvent.deltaX(), gestureEvent.deltaY(), gestureEvent.deltaX() / 120.0f, gestureEvent.deltaY() / 120.0f, ScrollByPixelWheelEvent, gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey());
309         handleWheelEvent(syntheticWheelEvent);
310         return true;
311     }
312     case PlatformEvent::GestureScrollBegin:
313     case PlatformEvent::GestureScrollEnd:
314     case PlatformEvent::GestureTapDown:
315     case PlatformEvent::GestureShowPress:
316         break;
317     default:
318         ASSERT_NOT_REACHED();
319     }
320     return false;
321 }
322 
handleKeyEvent(const PlatformKeyboardEvent & event)323 bool PopupContainer::handleKeyEvent(const PlatformKeyboardEvent& event)
324 {
325     UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
326     return m_listBox->handleKeyEvent(event);
327 }
328 
hide()329 void PopupContainer::hide()
330 {
331     m_listBox->abandon();
332 }
333 
paint(GraphicsContext * gc,const IntRect & rect)334 void PopupContainer::paint(GraphicsContext* gc, const IntRect& rect)
335 {
336     // Adjust coords for scrolled frame.
337     IntRect r = intersection(rect, frameRect());
338     int tx = x();
339     int ty = y();
340 
341     r.move(-tx, -ty);
342 
343     gc->translate(static_cast<float>(tx), static_cast<float>(ty));
344     m_listBox->paint(gc, r);
345     gc->translate(-static_cast<float>(tx), -static_cast<float>(ty));
346 
347     paintBorder(gc, rect);
348 }
349 
paintBorder(GraphicsContext * gc,const IntRect & rect)350 void PopupContainer::paintBorder(GraphicsContext* gc, const IntRect& rect)
351 {
352     // FIXME: Where do we get the border color from?
353     Color borderColor(127, 157, 185);
354 
355     gc->setStrokeStyle(NoStroke);
356     gc->setFillColor(borderColor);
357 
358     int tx = x();
359     int ty = y();
360 
361     // top, left, bottom, right
362     gc->drawRect(IntRect(tx, ty, width(), borderSize));
363     gc->drawRect(IntRect(tx, ty, borderSize, height()));
364     gc->drawRect(IntRect(tx, ty + height() - borderSize, width(), borderSize));
365     gc->drawRect(IntRect(tx + width() - borderSize, ty, borderSize, height()));
366 }
367 
isInterestedInEventForKey(int keyCode)368 bool PopupContainer::isInterestedInEventForKey(int keyCode)
369 {
370     return m_listBox->isInterestedInEventForKey(keyCode);
371 }
372 
chromeClient()373 ChromeClient& PopupContainer::chromeClient()
374 {
375     return m_frameView->frame().page()->chrome().client();
376 }
377 
showInRect(const FloatQuad & controlPosition,const IntSize & controlSize,FrameView * v,int index)378 void PopupContainer::showInRect(const FloatQuad& controlPosition, const IntSize& controlSize, FrameView* v, int index)
379 {
380     // The controlSize is the size of the select box. It's usually larger than
381     // we need. Subtract border size so that usually the container will be
382     // displayed exactly the same width as the select box.
383     listBox()->setBaseWidth(max(controlSize.width() - borderSize * 2, 0));
384 
385     listBox()->updateFromElement();
386 
387     // We set the selected item in updateFromElement(), and disregard the
388     // index passed into this function (same as Webkit's PopupMenuWin.cpp)
389     // FIXME: make sure this is correct, and add an assertion.
390     // ASSERT(popupWindow(popup)->listBox()->selectedIndex() == index);
391 
392     // Save and convert the controlPosition to main window coords. Each point is converted separately
393     // to window coordinates because the control could be in a transformed webview and then each point
394     // would be transformed by a different delta.
395     m_controlPosition.setP1(v->contentsToWindow(IntPoint(controlPosition.p1().x(), controlPosition.p1().y())));
396     m_controlPosition.setP2(v->contentsToWindow(IntPoint(controlPosition.p2().x(), controlPosition.p2().y())));
397     m_controlPosition.setP3(v->contentsToWindow(IntPoint(controlPosition.p3().x(), controlPosition.p3().y())));
398     m_controlPosition.setP4(v->contentsToWindow(IntPoint(controlPosition.p4().x(), controlPosition.p4().y())));
399 
400     m_controlSize = controlSize;
401 
402     // Position at (0, 0) since the frameRect().location() is relative to the
403     // parent WebWidget.
404     setFrameRect(IntRect(IntPoint(), controlSize));
405     showPopup(v);
406 }
407 
refresh(const IntRect & targetControlRect)408 IntRect PopupContainer::refresh(const IntRect& targetControlRect)
409 {
410     listBox()->setBaseWidth(max(m_controlSize.width() - borderSize * 2, 0));
411     listBox()->updateFromElement();
412 
413     IntPoint locationInWindow = m_frameView->contentsToWindow(targetControlRect.location());
414 
415     // Move it below the select widget.
416     locationInWindow.move(0, targetControlRect.height());
417 
418     IntRect widgetRectInScreen = layoutAndCalculateWidgetRect(targetControlRect.height(), IntSize(), locationInWindow);
419 
420     // Reset the size (which can be set to the PopupListBox size in
421     // layoutAndGetRTLOffset(), exceeding the available widget rectangle.)
422     if (size() != widgetRectInScreen.size())
423         resize(widgetRectInScreen.size());
424 
425     invalidate();
426 
427     return widgetRectInScreen;
428 }
429 
isRTL() const430 inline bool PopupContainer::isRTL() const
431 {
432     return m_listBox->m_popupClient->menuStyle().textDirection() == RTL;
433 }
434 
selectedIndex() const435 int PopupContainer::selectedIndex() const
436 {
437     return m_listBox->selectedIndex();
438 }
439 
menuItemHeight() const440 int PopupContainer::menuItemHeight() const
441 {
442     return m_listBox->getRowHeight(0);
443 }
444 
menuItemFontSize() const445 int PopupContainer::menuItemFontSize() const
446 {
447     return m_listBox->getRowFont(0).fontDescription().computedSize();
448 }
449 
menuStyle() const450 PopupMenuStyle PopupContainer::menuStyle() const
451 {
452     return m_listBox->m_popupClient->menuStyle();
453 }
454 
popupData() const455 const WTF::Vector<PopupItem*>& PopupContainer:: popupData() const
456 {
457     return m_listBox->items();
458 }
459 
getSelectedItemToolTip()460 String PopupContainer::getSelectedItemToolTip()
461 {
462     // We cannot use m_popupClient->selectedIndex() to choose tooltip message,
463     // because the selectedIndex() might return final selected index, not
464     // hovering selection.
465     return listBox()->m_popupClient->itemToolTip(listBox()->m_selectedIndex);
466 }
467 
popupOpened(const IntRect & bounds)468 void PopupContainer::popupOpened(const IntRect& bounds)
469 {
470     WebViewImpl* webView = WebViewImpl::fromPage(m_frameView->frame().page());
471     if (!webView->client())
472         return;
473 
474     WebWidget* webwidget = webView->client()->createPopupMenu(WebPopupTypeSelect);
475     if (!webwidget)
476         return;
477     // We only notify when the WebView has to handle the popup, as when
478     // the popup is handled externally, the fact that a popup is showing is
479     // transparent to the WebView.
480     webView->popupOpened(this);
481     toWebPopupMenuImpl(webwidget)->initialize(this, bounds);
482 }
483 
getPopupMenuInfo(WebPopupMenuInfo * info)484 void PopupContainer::getPopupMenuInfo(WebPopupMenuInfo* info)
485 {
486     const Vector<PopupItem*>& inputItems = popupData();
487 
488     WebVector<WebMenuItemInfo> outputItems(inputItems.size());
489 
490     for (size_t i = 0; i < inputItems.size(); ++i) {
491         const PopupItem& inputItem = *inputItems[i];
492         WebMenuItemInfo& outputItem = outputItems[i];
493 
494         outputItem.label = inputItem.label;
495         outputItem.enabled = inputItem.enabled;
496         if (inputItem.textDirection == WebCore::RTL)
497             outputItem.textDirection = WebTextDirectionRightToLeft;
498         else
499             outputItem.textDirection = WebTextDirectionLeftToRight;
500         outputItem.hasTextDirectionOverride = inputItem.hasTextDirectionOverride;
501 
502         switch (inputItem.type) {
503         case PopupItem::TypeOption:
504             outputItem.type = WebMenuItemInfo::Option;
505             break;
506         case PopupItem::TypeGroup:
507             outputItem.type = WebMenuItemInfo::Group;
508             break;
509         case PopupItem::TypeSeparator:
510             outputItem.type = WebMenuItemInfo::Separator;
511             break;
512         }
513     }
514 
515     info->itemHeight = menuItemHeight();
516     info->itemFontSize = menuItemFontSize();
517     info->selectedIndex = selectedIndex();
518     info->items.swap(outputItems);
519     info->rightAligned = menuStyle().textDirection() == RTL;
520 }
521 
522 } // namespace blink
523