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
58namespace blink {
59
60static const int borderSize = 1;
61
62static 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
74static 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
87PassRefPtr<PopupContainer> PopupContainer::create(PopupMenuClient* client, bool deviceSupportsTouch)
88{
89    return adoptRef(new PopupContainer(client, deviceSupportsTouch));
90}
91
92PopupContainer::PopupContainer(PopupMenuClient* client, bool deviceSupportsTouch)
93    : m_listBox(PopupListBox::create(client, deviceSupportsTouch, this))
94    , m_popupOpen(false)
95    , m_client(0)
96{
97}
98
99PopupContainer::~PopupContainer()
100{
101    if (m_listBox->parent())
102        m_listBox->setParent(0);
103}
104
105IntRect 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
158IntRect 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
212void 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
229void PopupContainer::hidePopup()
230{
231    m_listBox->abandon();
232}
233
234void 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
262void 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
272bool PopupContainer::handleMouseDownEvent(const PlatformMouseEvent& event)
273{
274    UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
275    return m_listBox->handleMouseDownEvent(
276        constructRelativeMouseEvent(event, this, m_listBox.get()));
277}
278
279bool PopupContainer::handleMouseMoveEvent(const PlatformMouseEvent& event)
280{
281    UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
282    return m_listBox->handleMouseMoveEvent(
283        constructRelativeMouseEvent(event, this, m_listBox.get()));
284}
285
286bool 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
294bool PopupContainer::handleWheelEvent(const PlatformWheelEvent& event)
295{
296    UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
297    return m_listBox->handleWheelEvent(
298        constructRelativeWheelEvent(event, this, m_listBox.get()));
299}
300
301bool PopupContainer::handleTouchEvent(const PlatformTouchEvent&)
302{
303    return false;
304}
305
306// FIXME: Refactor this code to share functionality with
307// EventHandler::handleGestureEvent.
308bool 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
337bool PopupContainer::handleKeyEvent(const PlatformKeyboardEvent& event)
338{
339    UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
340    return m_listBox->handleKeyEvent(event);
341}
342
343void PopupContainer::hide()
344{
345    m_listBox->abandon();
346}
347
348void 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
364void 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
382bool PopupContainer::isInterestedInEventForKey(int keyCode)
383{
384    return m_listBox->isInterestedInEventForKey(keyCode);
385}
386
387ChromeClient& PopupContainer::chromeClient()
388{
389    return m_frameView->frame().page()->chrome().client();
390}
391
392void 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
422IntRect 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
444inline bool PopupContainer::isRTL() const
445{
446    return m_listBox->m_popupClient->menuStyle().textDirection() == RTL;
447}
448
449int PopupContainer::selectedIndex() const
450{
451    return m_listBox->selectedIndex();
452}
453
454int PopupContainer::menuItemHeight() const
455{
456    return m_listBox->getRowHeight(0);
457}
458
459int PopupContainer::menuItemFontSize() const
460{
461    return m_listBox->getRowFont(0).fontDescription().computedSize();
462}
463
464PopupMenuStyle PopupContainer::menuStyle() const
465{
466    return m_listBox->m_popupClient->menuStyle();
467}
468
469const WTF::Vector<PopupItem*>& PopupContainer:: popupData() const
470{
471    return m_listBox->items();
472}
473
474String 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
482void 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
498void 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
533void PopupContainer::invalidateRect(const IntRect& rect)
534{
535    if (HostWindow* h = hostWindow())
536        h->invalidateContentsAndRootView(rect);
537}
538
539HostWindow* PopupContainer::hostWindow() const
540{
541    return const_cast<PopupContainerClient*>(m_client);
542}
543
544IntPoint PopupContainer::convertChildToSelf(const Widget* child, const IntPoint& point) const
545{
546    IntPoint newPoint = point;
547    newPoint.moveBy(child->location());
548    return newPoint;
549}
550
551IntPoint 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