Home | History | Annotate | Download | only in web
      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 
     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 
     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
     87 PassRefPtr<PopupContainer> PopupContainer::create(PopupMenuClient* client, bool deviceSupportsTouch)
     88 {
     89     return adoptRef(new PopupContainer(client, deviceSupportsTouch));
     90 }
     91 
     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 
     99 PopupContainer::~PopupContainer()
    100 {
    101     if (m_listBox->parent())
    102         m_listBox->setParent(0);
    103 }
    104 
    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 
    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 
    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 
    229 void PopupContainer::hidePopup()
    230 {
    231     m_listBox->abandon();
    232 }
    233 
    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 
    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 
    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 
    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 
    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 
    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 
    301 bool PopupContainer::handleTouchEvent(const PlatformTouchEvent&)
    302 {
    303     return false;
    304 }
    305 
    306 // FIXME: Refactor this code to share functionality with
    307 // EventHandler::handleGestureEvent.
    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 
    337 bool PopupContainer::handleKeyEvent(const PlatformKeyboardEvent& event)
    338 {
    339     UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
    340     return m_listBox->handleKeyEvent(event);
    341 }
    342 
    343 void PopupContainer::hide()
    344 {
    345     m_listBox->abandon();
    346 }
    347 
    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 
    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 
    382 bool PopupContainer::isInterestedInEventForKey(int keyCode)
    383 {
    384     return m_listBox->isInterestedInEventForKey(keyCode);
    385 }
    386 
    387 ChromeClient& PopupContainer::chromeClient()
    388 {
    389     return m_frameView->frame().page()->chrome().client();
    390 }
    391 
    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 
    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 
    444 inline bool PopupContainer::isRTL() const
    445 {
    446     return m_listBox->m_popupClient->menuStyle().textDirection() == RTL;
    447 }
    448 
    449 int PopupContainer::selectedIndex() const
    450 {
    451     return m_listBox->selectedIndex();
    452 }
    453 
    454 int PopupContainer::menuItemHeight() const
    455 {
    456     return m_listBox->getRowHeight(0);
    457 }
    458 
    459 int PopupContainer::menuItemFontSize() const
    460 {
    461     return m_listBox->getRowFont(0).fontDescription().computedSize();
    462 }
    463 
    464 PopupMenuStyle PopupContainer::menuStyle() const
    465 {
    466     return m_listBox->m_popupClient->menuStyle();
    467 }
    468 
    469 const WTF::Vector<PopupItem*>& PopupContainer:: popupData() const
    470 {
    471     return m_listBox->items();
    472 }
    473 
    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 
    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 
    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 
    533 void PopupContainer::invalidateRect(const IntRect& rect)
    534 {
    535     if (HostWindow* h = hostWindow())
    536         h->invalidateContentsAndRootView(rect);
    537 }
    538 
    539 HostWindow* PopupContainer::hostWindow() const
    540 {
    541     return const_cast<PopupContainerClient*>(m_client);
    542 }
    543 
    544 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 
    551 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