• 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 "public/web/WebPopupMenuInfo.h"
51 #include "public/web/WebPopupType.h"
52 #include "public/web/WebViewClient.h"
53 #include "web/PopupContainerClient.h"
54 #include "web/WebPopupMenuImpl.h"
55 #include "web/WebViewImpl.h"
56 #include <limits>
57 
58 namespace blink {
59 
60 static const int borderSize = 1;
61 
constructRelativeMouseEvent(const PlatformMouseEvent & e,PopupContainer * parent,PopupListBox * child)62 static PlatformMouseEvent constructRelativeMouseEvent(const PlatformMouseEvent& e, PopupContainer* parent, PopupListBox* child)
63 {
64     IntPoint pos = parent->convertSelfToChild(child, e.position());
65 
66     // FIXME: This is a horrible hack since PlatformMouseEvent has no setters for x/y.
67     PlatformMouseEvent relativeEvent = e;
68     IntPoint& relativePos = const_cast<IntPoint&>(relativeEvent.position());
69     relativePos.setX(pos.x());
70     relativePos.setY(pos.y());
71     return relativeEvent;
72 }
73 
constructRelativeWheelEvent(const PlatformWheelEvent & e,PopupContainer * parent,PopupListBox * child)74 static PlatformWheelEvent constructRelativeWheelEvent(const PlatformWheelEvent& e, PopupContainer* parent, PopupListBox* child)
75 {
76     IntPoint pos = parent->convertSelfToChild(child, e.position());
77 
78     // FIXME: This is a horrible hack since PlatformWheelEvent has no setters for x/y.
79     PlatformWheelEvent relativeEvent = e;
80     IntPoint& relativePos = const_cast<IntPoint&>(relativeEvent.position());
81     relativePos.setX(pos.x());
82     relativePos.setY(pos.y());
83     return relativeEvent;
84 }
85 
86 // static
create(PopupMenuClient * client,bool deviceSupportsTouch)87 PassRefPtr<PopupContainer> PopupContainer::create(PopupMenuClient* client, bool deviceSupportsTouch)
88 {
89     return adoptRef(new PopupContainer(client, deviceSupportsTouch));
90 }
91 
PopupContainer(PopupMenuClient * client,bool deviceSupportsTouch)92 PopupContainer::PopupContainer(PopupMenuClient* client, bool deviceSupportsTouch)
93     : m_listBox(PopupListBox::create(client, deviceSupportsTouch, this))
94     , m_popupOpen(false)
95     , m_client(0)
96 {
97 }
98 
~PopupContainer()99 PopupContainer::~PopupContainer()
100 {
101     if (m_listBox->parent())
102         m_listBox->setParent(0);
103 }
104 
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)105 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)
106 {
107     ASSERT(listBox);
108     if (windowRect.x() >= screen.x() && windowRect.maxX() <= screen.maxX() && (widgetRectInScreen.x() < screen.x() || widgetRectInScreen.maxX() > screen.maxX())) {
109         // First, inverse the popup alignment if it does not fit the screen -
110         // this might fix things (or make them better).
111         IntRect inverseWidgetRectInScreen = widgetRectInScreen;
112         inverseWidgetRectInScreen.setX(inverseWidgetRectInScreen.x() + (isRTL ? -rtlOffset : rtlOffset));
113         inverseWidgetRectInScreen.setY(inverseWidgetRectInScreen.y() + (isRTL ? -verticalOffset : verticalOffset));
114         IntRect enclosingScreen = enclosingIntRect(screen);
115         unsigned originalCutoff = std::max(enclosingScreen.x() - widgetRectInScreen.x(), 0) + std::max(widgetRectInScreen.maxX() - enclosingScreen.maxX(), 0);
116         unsigned inverseCutoff = std::max(enclosingScreen.x() - inverseWidgetRectInScreen.x(), 0) + std::max(inverseWidgetRectInScreen.maxX() - enclosingScreen.maxX(), 0);
117 
118         // Accept the inverse popup alignment if the trimmed content gets
119         // shorter than that in the original alignment case.
120         if (inverseCutoff < originalCutoff)
121             widgetRectInScreen = inverseWidgetRectInScreen;
122 
123         if (widgetRectInScreen.x() < screen.x()) {
124             widgetRectInScreen.setWidth(widgetRectInScreen.maxX() - screen.x());
125             widgetRectInScreen.setX(screen.x());
126             listBox->setMaxWidthAndLayout(std::max(widgetRectInScreen.width() - borderSize * 2, 0));
127         } else if (widgetRectInScreen.maxX() > screen.maxX()) {
128             widgetRectInScreen.setWidth(screen.maxX() - widgetRectInScreen.x());
129             listBox->setMaxWidthAndLayout(std::max(widgetRectInScreen.width() - borderSize * 2, 0));
130         }
131     }
132 
133     // Calculate Y axis size.
134     if (widgetRectInScreen.maxY() > static_cast<int>(screen.maxY())) {
135         if (widgetRectInScreen.y() - widgetRectInScreen.height() - targetControlHeight - transformOffset.height() > 0) {
136             // There is enough room to open upwards.
137             widgetRectInScreen.move(-transformOffset.width(), -(widgetRectInScreen.height() + targetControlHeight + transformOffset.height()));
138         } else {
139             // Figure whether upwards or downwards has more room and set the
140             // maximum number of items.
141             int spaceAbove = widgetRectInScreen.y() - targetControlHeight + transformOffset.height();
142             int spaceBelow = screen.maxY() - widgetRectInScreen.y();
143             if (spaceAbove > spaceBelow)
144                 listBox->setMaxHeight(spaceAbove);
145             else
146                 listBox->setMaxHeight(spaceBelow);
147             listBox->layout();
148             needToResizeView = true;
149             widgetRectInScreen.setHeight(listBox->popupContentHeight() + borderSize * 2);
150             // Move WebWidget upwards if necessary.
151             if (spaceAbove > spaceBelow)
152                 widgetRectInScreen.move(-transformOffset.width(), -(widgetRectInScreen.height() + targetControlHeight + transformOffset.height()));
153         }
154     }
155     return widgetRectInScreen;
156 }
157 
layoutAndCalculateWidgetRect(int targetControlHeight,const IntSize & transformOffset,const IntPoint & popupInitialCoordinate)158 IntRect PopupContainer::layoutAndCalculateWidgetRect(int targetControlHeight, const IntSize& transformOffset, const IntPoint& popupInitialCoordinate)
159 {
160     // Reset the max width and height to their default values, they will be
161     // recomputed below if necessary.
162     m_listBox->setMaxHeight(PopupListBox::defaultMaxHeight);
163     m_listBox->setMaxWidth(std::numeric_limits<int>::max());
164 
165     // Lay everything out to figure out our preferred size, then tell the view's
166     // WidgetClient about it. It should assign us a client.
167     m_listBox->layout();
168     fitToListBox();
169     bool isRTL = this->isRTL();
170 
171     // Compute the starting x-axis for a normal RTL or right-aligned LTR
172     // dropdown. For those, the right edge of dropdown box should be aligned
173     // with the right edge of <select>/<input> element box, and the dropdown box
174     // should be expanded to the left if more space is needed.
175     // m_originalFrameRect.width() is the width of the target <select>/<input>
176     // element.
177     int rtlOffset = m_controlPosition.p2().x() - m_controlPosition.p1().x() - (m_listBox->width() + borderSize * 2);
178     int rightOffset = isRTL ? rtlOffset : 0;
179 
180     // Compute the y-axis offset between the bottom left and bottom right
181     // points. If the <select>/<input> is transformed, they are not the same.
182     int verticalOffset = - m_controlPosition.p4().y() + m_controlPosition.p3().y();
183     int verticalForRTLOffset = isRTL ? verticalOffset : 0;
184 
185     // Assume m_listBox size is already calculated.
186     IntSize targetSize(m_listBox->width() + borderSize * 2, m_listBox->height() + borderSize * 2);
187 
188     IntRect widgetRectInScreen;
189     // If the popup would extend past the bottom of the screen, open upwards
190     // instead.
191     FloatRect screen = screenAvailableRect(m_frameView.get());
192     // Use popupInitialCoordinate.x() + rightOffset because RTL position
193     // needs to be considered.
194     float pageScaleFactor = m_frameView->frame().page()->pageScaleFactor();
195     int popupX = round((popupInitialCoordinate.x() + rightOffset) * pageScaleFactor);
196     int popupY = round((popupInitialCoordinate.y() + verticalForRTLOffset) * pageScaleFactor);
197     widgetRectInScreen = chromeClient().rootViewToScreen(IntRect(popupX, popupY, targetSize.width(), targetSize.height()));
198 
199     // If we have multiple screens and the browser rect is in one screen, we
200     // have to clip the window width to the screen width.
201     // When clipping, we also need to set a maximum width for the list box.
202     FloatRect windowRect = chromeClient().windowRect();
203 
204     bool needToResizeView = false;
205     widgetRectInScreen = layoutAndCalculateWidgetRectInternal(widgetRectInScreen, targetControlHeight, windowRect, screen, isRTL, rtlOffset, verticalOffset, transformOffset, m_listBox.get(), needToResizeView);
206     if (needToResizeView)
207         fitToListBox();
208 
209     return widgetRectInScreen;
210 }
211 
showPopup(FrameView * view)212 void PopupContainer::showPopup(FrameView* view)
213 {
214     m_frameView = view;
215     m_listBox->m_focusedElement = m_frameView->frame().document()->focusedElement();
216 
217     IntSize transformOffset(m_controlPosition.p4().x() - m_controlPosition.p1().x(), m_controlPosition.p4().y() - m_controlPosition.p1().y() - m_controlSize.height());
218     popupOpened(layoutAndCalculateWidgetRect(m_controlSize.height(), transformOffset, roundedIntPoint(m_controlPosition.p4())));
219     m_popupOpen = true;
220 
221     if (!m_listBox->parent())
222         m_listBox->setParent(this);
223 
224     m_listBox->scrollToRevealSelection();
225 
226     invalidate();
227 }
228 
hidePopup()229 void PopupContainer::hidePopup()
230 {
231     m_listBox->abandon();
232 }
233 
notifyPopupHidden()234 void PopupContainer::notifyPopupHidden()
235 {
236     if (!m_popupOpen)
237         return;
238     m_popupOpen = false;
239 
240     // With Oilpan, we cannot assume that the FrameView's LocalFrame's
241     // page is still available, as the LocalFrame itself may have been
242     // detached from its FrameHost by now.
243     //
244     // So, if a popup menu is left in an open/shown state when
245     // finalized, the PopupMenu implementation of this container's
246     // listbox will hide itself when destructed, delivering the
247     // notifyPopupHidden() notification in the process & ending up here.
248     // If the LocalFrame has been detached already -- done when its
249     // HTMLFrameOwnerElement frame owner is detached as part of being
250     // torn down -- the connection to the FrameHost has been snipped &
251     // there's no page. Hence the null check.
252     //
253     // In a non-Oilpan setting, the RenderMenuList that controls/owns
254     // the PopupMenuChromium object and this PopupContainer is torn
255     // down and destructed before the frame and frame owner, hence the
256     // page will always be available in that setting and this will
257     // not be an issue.
258     if (WebViewImpl* webView = WebViewImpl::fromPage(m_frameView->frame().page()))
259         webView->popupClosed(this);
260 }
261 
fitToListBox()262 void PopupContainer::fitToListBox()
263 {
264     // Place the listbox within our border.
265     m_listBox->move(borderSize, borderSize);
266 
267     // Size ourselves to contain listbox + border.
268     resize(m_listBox->width() + borderSize * 2, m_listBox->height() + borderSize * 2);
269     invalidate();
270 }
271 
handleMouseDownEvent(const PlatformMouseEvent & event)272 bool PopupContainer::handleMouseDownEvent(const PlatformMouseEvent& event)
273 {
274     UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
275     return m_listBox->handleMouseDownEvent(
276         constructRelativeMouseEvent(event, this, m_listBox.get()));
277 }
278 
handleMouseMoveEvent(const PlatformMouseEvent & event)279 bool PopupContainer::handleMouseMoveEvent(const PlatformMouseEvent& event)
280 {
281     UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
282     return m_listBox->handleMouseMoveEvent(
283         constructRelativeMouseEvent(event, this, m_listBox.get()));
284 }
285 
handleMouseReleaseEvent(const PlatformMouseEvent & event)286 bool PopupContainer::handleMouseReleaseEvent(const PlatformMouseEvent& event)
287 {
288     RefPtr<PopupContainer> protect(this);
289     UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
290     return m_listBox->handleMouseReleaseEvent(
291         constructRelativeMouseEvent(event, this, m_listBox.get()));
292 }
293 
handleWheelEvent(const PlatformWheelEvent & event)294 bool PopupContainer::handleWheelEvent(const PlatformWheelEvent& event)
295 {
296     UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
297     return m_listBox->handleWheelEvent(
298         constructRelativeWheelEvent(event, this, m_listBox.get()));
299 }
300 
handleTouchEvent(const PlatformTouchEvent &)301 bool PopupContainer::handleTouchEvent(const PlatformTouchEvent&)
302 {
303     return false;
304 }
305 
306 // FIXME: Refactor this code to share functionality with
307 // EventHandler::handleGestureEvent.
handleGestureEvent(const PlatformGestureEvent & gestureEvent)308 bool PopupContainer::handleGestureEvent(const PlatformGestureEvent& gestureEvent)
309 {
310     switch (gestureEvent.type()) {
311     case PlatformEvent::GestureTap: {
312         PlatformMouseEvent fakeMouseMove(gestureEvent.position(), gestureEvent.globalPosition(), NoButton, PlatformEvent::MouseMoved, /* clickCount */ 1, gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey(), PlatformMouseEvent::FromTouch, gestureEvent.timestamp());
313         PlatformMouseEvent fakeMouseDown(gestureEvent.position(), gestureEvent.globalPosition(), LeftButton, PlatformEvent::MousePressed, /* clickCount */ 1, gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey(), PlatformMouseEvent::FromTouch, gestureEvent.timestamp());
314         PlatformMouseEvent fakeMouseUp(gestureEvent.position(), gestureEvent.globalPosition(), LeftButton, PlatformEvent::MouseReleased, /* clickCount */ 1, gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey(), PlatformMouseEvent::FromTouch, gestureEvent.timestamp());
315         // handleMouseMoveEvent(fakeMouseMove);
316         handleMouseDownEvent(fakeMouseDown);
317         handleMouseReleaseEvent(fakeMouseUp);
318         return true;
319     }
320     case PlatformEvent::GestureScrollUpdate:
321     case PlatformEvent::GestureScrollUpdateWithoutPropagation: {
322         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());
323         handleWheelEvent(syntheticWheelEvent);
324         return true;
325     }
326     case PlatformEvent::GestureScrollBegin:
327     case PlatformEvent::GestureScrollEnd:
328     case PlatformEvent::GestureTapDown:
329     case PlatformEvent::GestureShowPress:
330         break;
331     default:
332         ASSERT_NOT_REACHED();
333     }
334     return false;
335 }
336 
handleKeyEvent(const PlatformKeyboardEvent & event)337 bool PopupContainer::handleKeyEvent(const PlatformKeyboardEvent& event)
338 {
339     UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
340     return m_listBox->handleKeyEvent(event);
341 }
342 
hide()343 void PopupContainer::hide()
344 {
345     m_listBox->abandon();
346 }
347 
paint(GraphicsContext * gc,const IntRect & rect)348 void PopupContainer::paint(GraphicsContext* gc, const IntRect& rect)
349 {
350     // Adjust coords for scrolled frame.
351     IntRect r = intersection(rect, frameRect());
352     int tx = x();
353     int ty = y();
354 
355     r.move(-tx, -ty);
356 
357     gc->translate(static_cast<float>(tx), static_cast<float>(ty));
358     m_listBox->paint(gc, r);
359     gc->translate(-static_cast<float>(tx), -static_cast<float>(ty));
360 
361     paintBorder(gc, rect);
362 }
363 
paintBorder(GraphicsContext * gc,const IntRect & rect)364 void PopupContainer::paintBorder(GraphicsContext* gc, const IntRect& rect)
365 {
366     // FIXME: Where do we get the border color from?
367     Color borderColor(127, 157, 185);
368 
369     gc->setStrokeStyle(NoStroke);
370     gc->setFillColor(borderColor);
371 
372     int tx = x();
373     int ty = y();
374 
375     // top, left, bottom, right
376     gc->drawRect(IntRect(tx, ty, width(), borderSize));
377     gc->drawRect(IntRect(tx, ty, borderSize, height()));
378     gc->drawRect(IntRect(tx, ty + height() - borderSize, width(), borderSize));
379     gc->drawRect(IntRect(tx + width() - borderSize, ty, borderSize, height()));
380 }
381 
isInterestedInEventForKey(int keyCode)382 bool PopupContainer::isInterestedInEventForKey(int keyCode)
383 {
384     return m_listBox->isInterestedInEventForKey(keyCode);
385 }
386 
chromeClient()387 ChromeClient& PopupContainer::chromeClient()
388 {
389     return m_frameView->frame().page()->chrome().client();
390 }
391 
showInRect(const FloatQuad & controlPosition,const IntSize & controlSize,FrameView * v,int index)392 void PopupContainer::showInRect(const FloatQuad& controlPosition, const IntSize& controlSize, FrameView* v, int index)
393 {
394     // The controlSize is the size of the select box. It's usually larger than
395     // we need. Subtract border size so that usually the container will be
396     // displayed exactly the same width as the select box.
397     m_listBox->setBaseWidth(max(controlSize.width() - borderSize * 2, 0));
398 
399     m_listBox->updateFromElement();
400 
401     // We set the selected item in updateFromElement(), and disregard the
402     // index passed into this function (same as Webkit's PopupMenuWin.cpp)
403     // FIXME: make sure this is correct, and add an assertion.
404     // ASSERT(popupWindow(popup)->listBox()->selectedIndex() == index);
405 
406     // Save and convert the controlPosition to main window coords. Each point is converted separately
407     // to window coordinates because the control could be in a transformed webview and then each point
408     // would be transformed by a different delta.
409     m_controlPosition.setP1(v->contentsToWindow(IntPoint(controlPosition.p1().x(), controlPosition.p1().y())));
410     m_controlPosition.setP2(v->contentsToWindow(IntPoint(controlPosition.p2().x(), controlPosition.p2().y())));
411     m_controlPosition.setP3(v->contentsToWindow(IntPoint(controlPosition.p3().x(), controlPosition.p3().y())));
412     m_controlPosition.setP4(v->contentsToWindow(IntPoint(controlPosition.p4().x(), controlPosition.p4().y())));
413 
414     m_controlSize = controlSize;
415 
416     // Position at (0, 0) since the frameRect().location() is relative to the
417     // parent WebWidget.
418     setFrameRect(IntRect(IntPoint(), controlSize));
419     showPopup(v);
420 }
421 
refresh(const IntRect & targetControlRect)422 IntRect PopupContainer::refresh(const IntRect& targetControlRect)
423 {
424     m_listBox->setBaseWidth(max(m_controlSize.width() - borderSize * 2, 0));
425     m_listBox->updateFromElement();
426 
427     IntPoint locationInWindow = m_frameView->contentsToWindow(targetControlRect.location());
428 
429     // Move it below the select widget.
430     locationInWindow.move(0, targetControlRect.height());
431 
432     IntRect widgetRectInScreen = layoutAndCalculateWidgetRect(targetControlRect.height(), IntSize(), locationInWindow);
433 
434     // Reset the size (which can be set to the PopupListBox size in
435     // layoutAndGetRTLOffset(), exceeding the available widget rectangle.)
436     if (size() != widgetRectInScreen.size())
437         resize(widgetRectInScreen.size());
438 
439     invalidate();
440 
441     return widgetRectInScreen;
442 }
443 
isRTL() const444 inline bool PopupContainer::isRTL() const
445 {
446     return m_listBox->m_popupClient->menuStyle().textDirection() == RTL;
447 }
448 
selectedIndex() const449 int PopupContainer::selectedIndex() const
450 {
451     return m_listBox->selectedIndex();
452 }
453 
menuItemHeight() const454 int PopupContainer::menuItemHeight() const
455 {
456     return m_listBox->getRowHeight(0);
457 }
458 
menuItemFontSize() const459 int PopupContainer::menuItemFontSize() const
460 {
461     return m_listBox->getRowFont(0).fontDescription().computedSize();
462 }
463 
menuStyle() const464 PopupMenuStyle PopupContainer::menuStyle() const
465 {
466     return m_listBox->m_popupClient->menuStyle();
467 }
468 
popupData() const469 const WTF::Vector<PopupItem*>& PopupContainer:: popupData() const
470 {
471     return m_listBox->items();
472 }
473 
getSelectedItemToolTip()474 String PopupContainer::getSelectedItemToolTip()
475 {
476     // We cannot use m_popupClient->selectedIndex() to choose tooltip message,
477     // because the selectedIndex() might return final selected index, not
478     // hovering selection.
479     return m_listBox->m_popupClient->itemToolTip(m_listBox->m_selectedIndex);
480 }
481 
popupOpened(const IntRect & bounds)482 void PopupContainer::popupOpened(const IntRect& bounds)
483 {
484     WebViewImpl* webView = WebViewImpl::fromPage(m_frameView->frame().page());
485     if (!webView->client())
486         return;
487 
488     WebWidget* webwidget = webView->client()->createPopupMenu(WebPopupTypeSelect);
489     if (!webwidget)
490         return;
491     // We only notify when the WebView has to handle the popup, as when
492     // the popup is handled externally, the fact that a popup is showing is
493     // transparent to the WebView.
494     webView->popupOpened(this);
495     toWebPopupMenuImpl(webwidget)->initialize(this, bounds);
496 }
497 
getPopupMenuInfo(WebPopupMenuInfo * info)498 void PopupContainer::getPopupMenuInfo(WebPopupMenuInfo* info)
499 {
500     const Vector<PopupItem*>& inputItems = popupData();
501 
502     WebVector<WebMenuItemInfo> outputItems(inputItems.size());
503 
504     for (size_t i = 0; i < inputItems.size(); ++i) {
505         const PopupItem& inputItem = *inputItems[i];
506         WebMenuItemInfo& outputItem = outputItems[i];
507 
508         outputItem.label = inputItem.label;
509         outputItem.enabled = inputItem.enabled;
510         outputItem.textDirection = toWebTextDirection(inputItem.textDirection);
511         outputItem.hasTextDirectionOverride = inputItem.hasTextDirectionOverride;
512 
513         switch (inputItem.type) {
514         case PopupItem::TypeOption:
515             outputItem.type = WebMenuItemInfo::Option;
516             break;
517         case PopupItem::TypeGroup:
518             outputItem.type = WebMenuItemInfo::Group;
519             break;
520         case PopupItem::TypeSeparator:
521             outputItem.type = WebMenuItemInfo::Separator;
522             break;
523         }
524     }
525 
526     info->itemHeight = menuItemHeight();
527     info->itemFontSize = menuItemFontSize();
528     info->selectedIndex = selectedIndex();
529     info->items.swap(outputItems);
530     info->rightAligned = menuStyle().textDirection() == RTL;
531 }
532 
invalidateRect(const IntRect & rect)533 void PopupContainer::invalidateRect(const IntRect& rect)
534 {
535     if (HostWindow* h = hostWindow())
536         h->invalidateContentsAndRootView(rect);
537 }
538 
hostWindow() const539 HostWindow* PopupContainer::hostWindow() const
540 {
541     return const_cast<PopupContainerClient*>(m_client);
542 }
543 
convertChildToSelf(const Widget * child,const IntPoint & point) const544 IntPoint PopupContainer::convertChildToSelf(const Widget* child, const IntPoint& point) const
545 {
546     IntPoint newPoint = point;
547     newPoint.moveBy(child->location());
548     return newPoint;
549 }
550 
convertSelfToChild(const Widget * child,const IntPoint & point) const551 IntPoint PopupContainer::convertSelfToChild(const Widget* child, const IntPoint& point) const
552 {
553     IntPoint newPoint = point;
554     newPoint.moveBy(-child->location());
555     return newPoint;
556 }
557 
558 } // namespace blink
559