This source file includes following definitions.
- constructRelativeMouseEvent
- constructRelativeWheelEvent
- create
- m_popupOpen
- layoutAndCalculateWidgetRectInternal
- layoutAndCalculateWidgetRect
- showPopup
- hidePopup
- notifyPopupHidden
- fitToListBox
- handleMouseDownEvent
- handleMouseMoveEvent
- handleMouseReleaseEvent
- handleWheelEvent
- handleTouchEvent
- handleGestureEvent
- handleKeyEvent
- hide
- paint
- paintBorder
- isInterestedInEventForKey
- chromeClient
- showInRect
- refresh
- isRTL
- selectedIndex
- menuItemHeight
- menuItemFontSize
- menuStyle
- popupData
- getSelectedItemToolTip
- popupOpened
- getPopupMenuInfo
#include "config.h"
#include "PopupContainer.h"
#include "WebPopupMenuImpl.h"
#include "WebPopupMenuInfo.h"
#include "WebPopupType.h"
#include "WebViewClient.h"
#include "WebViewImpl.h"
#include "core/dom/Document.h"
#include "core/frame/FrameView.h"
#include "core/frame/LocalFrame.h"
#include "core/page/Chrome.h"
#include "core/page/ChromeClient.h"
#include "core/page/Page.h"
#include "platform/PlatformGestureEvent.h"
#include "platform/PlatformKeyboardEvent.h"
#include "platform/PlatformMouseEvent.h"
#include "platform/PlatformScreen.h"
#include "platform/PlatformTouchEvent.h"
#include "platform/PlatformWheelEvent.h"
#include "platform/PopupMenuClient.h"
#include "platform/UserGestureIndicator.h"
#include "platform/geometry/IntRect.h"
#include "platform/graphics/GraphicsContext.h"
#include "platform/scroll/FramelessScrollViewClient.h"
#include <limits>
namespace blink {
using namespace WebCore;
static const int borderSize = 1;
static PlatformMouseEvent constructRelativeMouseEvent(const PlatformMouseEvent& e, FramelessScrollView* parent, FramelessScrollView* child)
{
IntPoint pos = parent->convertSelfToChild(child, e.position());
PlatformMouseEvent relativeEvent = e;
IntPoint& relativePos = const_cast<IntPoint&>(relativeEvent.position());
relativePos.setX(pos.x());
relativePos.setY(pos.y());
return relativeEvent;
}
static PlatformWheelEvent constructRelativeWheelEvent(const PlatformWheelEvent& e, FramelessScrollView* parent, FramelessScrollView* child)
{
IntPoint pos = parent->convertSelfToChild(child, e.position());
PlatformWheelEvent relativeEvent = e;
IntPoint& relativePos = const_cast<IntPoint&>(relativeEvent.position());
relativePos.setX(pos.x());
relativePos.setY(pos.y());
return relativeEvent;
}
PassRefPtr<PopupContainer> PopupContainer::create(PopupMenuClient* client, bool deviceSupportsTouch)
{
return adoptRef(new PopupContainer(client, deviceSupportsTouch));
}
PopupContainer::PopupContainer(PopupMenuClient* client, bool deviceSupportsTouch)
: m_listBox(PopupListBox::create(client, deviceSupportsTouch))
, m_popupOpen(false)
{
setScrollbarModes(ScrollbarAlwaysOff, ScrollbarAlwaysOff);
}
PopupContainer::~PopupContainer()
{
if (m_listBox && m_listBox->parent())
removeChild(m_listBox.get());
}
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)
{
ASSERT(listBox);
if (windowRect.x() >= screen.x() && windowRect.maxX() <= screen.maxX() && (widgetRectInScreen.x() < screen.x() || widgetRectInScreen.maxX() > screen.maxX())) {
IntRect inverseWidgetRectInScreen = widgetRectInScreen;
inverseWidgetRectInScreen.setX(inverseWidgetRectInScreen.x() + (isRTL ? -rtlOffset : rtlOffset));
inverseWidgetRectInScreen.setY(inverseWidgetRectInScreen.y() + (isRTL ? -verticalOffset : verticalOffset));
IntRect enclosingScreen = enclosingIntRect(screen);
unsigned originalCutoff = std::max(enclosingScreen.x() - widgetRectInScreen.x(), 0) + std::max(widgetRectInScreen.maxX() - enclosingScreen.maxX(), 0);
unsigned inverseCutoff = std::max(enclosingScreen.x() - inverseWidgetRectInScreen.x(), 0) + std::max(inverseWidgetRectInScreen.maxX() - enclosingScreen.maxX(), 0);
if (inverseCutoff < originalCutoff)
widgetRectInScreen = inverseWidgetRectInScreen;
if (widgetRectInScreen.x() < screen.x()) {
widgetRectInScreen.setWidth(widgetRectInScreen.maxX() - screen.x());
widgetRectInScreen.setX(screen.x());
listBox->setMaxWidthAndLayout(std::max(widgetRectInScreen.width() - borderSize * 2, 0));
} else if (widgetRectInScreen.maxX() > screen.maxX()) {
widgetRectInScreen.setWidth(screen.maxX() - widgetRectInScreen.x());
listBox->setMaxWidthAndLayout(std::max(widgetRectInScreen.width() - borderSize * 2, 0));
}
}
if (widgetRectInScreen.maxY() > static_cast<int>(screen.maxY())) {
if (widgetRectInScreen.y() - widgetRectInScreen.height() - targetControlHeight - transformOffset.height() > 0) {
widgetRectInScreen.move(-transformOffset.width(), -(widgetRectInScreen.height() + targetControlHeight + transformOffset.height()));
} else {
int spaceAbove = widgetRectInScreen.y() - targetControlHeight + transformOffset.height();
int spaceBelow = screen.maxY() - widgetRectInScreen.y();
if (spaceAbove > spaceBelow)
listBox->setMaxHeight(spaceAbove);
else
listBox->setMaxHeight(spaceBelow);
listBox->layout();
needToResizeView = true;
widgetRectInScreen.setHeight(listBox->popupContentHeight() + borderSize * 2);
if (spaceAbove > spaceBelow)
widgetRectInScreen.move(-transformOffset.width(), -(widgetRectInScreen.height() + targetControlHeight + transformOffset.height()));
}
}
return widgetRectInScreen;
}
IntRect PopupContainer::layoutAndCalculateWidgetRect(int targetControlHeight, const IntSize& transformOffset, const IntPoint& popupInitialCoordinate)
{
m_listBox->setMaxHeight(PopupListBox::defaultMaxHeight);
m_listBox->setMaxWidth(std::numeric_limits<int>::max());
m_listBox->layout();
fitToListBox();
bool isRTL = this->isRTL();
int rtlOffset = m_controlPosition.p2().x() - m_controlPosition.p1().x() - (m_listBox->width() + borderSize * 2);
int rightOffset = isRTL ? rtlOffset : 0;
int verticalOffset = - m_controlPosition.p4().y() + m_controlPosition.p3().y();
int verticalForRTLOffset = isRTL ? verticalOffset : 0;
IntSize targetSize(m_listBox->width() + borderSize * 2, m_listBox->height() + borderSize * 2);
IntRect widgetRectInScreen;
FloatRect screen = screenAvailableRect(m_frameView.get());
float pageScaleFactor = m_frameView->frame().page()->pageScaleFactor();
int popupX = round((popupInitialCoordinate.x() + rightOffset) * pageScaleFactor);
int popupY = round((popupInitialCoordinate.y() + verticalForRTLOffset) * pageScaleFactor);
widgetRectInScreen = chromeClient().rootViewToScreen(IntRect(popupX, popupY, targetSize.width(), targetSize.height()));
FloatRect windowRect = chromeClient().windowRect();
bool needToResizeView = false;
widgetRectInScreen = layoutAndCalculateWidgetRectInternal(widgetRectInScreen, targetControlHeight, windowRect, screen, isRTL, rtlOffset, verticalOffset, transformOffset, m_listBox.get(), needToResizeView);
if (needToResizeView)
fitToListBox();
return widgetRectInScreen;
}
void PopupContainer::showPopup(FrameView* view)
{
m_frameView = view;
listBox()->m_focusedElement = m_frameView->frame().document()->focusedElement();
IntSize transformOffset(m_controlPosition.p4().x() - m_controlPosition.p1().x(), m_controlPosition.p4().y() - m_controlPosition.p1().y() - m_controlSize.height());
popupOpened(layoutAndCalculateWidgetRect(m_controlSize.height(), transformOffset, roundedIntPoint(m_controlPosition.p4())));
m_popupOpen = true;
if (!m_listBox->parent())
addChild(m_listBox.get());
m_listBox->setVerticalScrollbarMode(ScrollbarAuto);
m_listBox->scrollToRevealSelection();
invalidate();
}
void PopupContainer::hidePopup()
{
listBox()->abandon();
}
void PopupContainer::notifyPopupHidden()
{
if (!m_popupOpen)
return;
m_popupOpen = false;
WebViewImpl::fromPage(m_frameView->frame().page())->popupClosed(this);
}
void PopupContainer::fitToListBox()
{
m_listBox->move(borderSize, borderSize);
resize(m_listBox->width() + borderSize * 2, m_listBox->height() + borderSize * 2);
invalidate();
}
bool PopupContainer::handleMouseDownEvent(const PlatformMouseEvent& event)
{
UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
return m_listBox->handleMouseDownEvent(
constructRelativeMouseEvent(event, this, m_listBox.get()));
}
bool PopupContainer::handleMouseMoveEvent(const PlatformMouseEvent& event)
{
UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
return m_listBox->handleMouseMoveEvent(
constructRelativeMouseEvent(event, this, m_listBox.get()));
}
bool PopupContainer::handleMouseReleaseEvent(const PlatformMouseEvent& event)
{
RefPtr<PopupContainer> protect(this);
UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
return m_listBox->handleMouseReleaseEvent(
constructRelativeMouseEvent(event, this, m_listBox.get()));
}
bool PopupContainer::handleWheelEvent(const PlatformWheelEvent& event)
{
UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
return m_listBox->handleWheelEvent(
constructRelativeWheelEvent(event, this, m_listBox.get()));
}
bool PopupContainer::handleTouchEvent(const PlatformTouchEvent&)
{
return false;
}
bool PopupContainer::handleGestureEvent(const PlatformGestureEvent& gestureEvent)
{
switch (gestureEvent.type()) {
case PlatformEvent::GestureTap: {
PlatformMouseEvent fakeMouseMove(gestureEvent.position(), gestureEvent.globalPosition(), NoButton, PlatformEvent::MouseMoved, 1, gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey(), gestureEvent.timestamp());
PlatformMouseEvent fakeMouseDown(gestureEvent.position(), gestureEvent.globalPosition(), LeftButton, PlatformEvent::MousePressed, 1, gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey(), gestureEvent.timestamp());
PlatformMouseEvent fakeMouseUp(gestureEvent.position(), gestureEvent.globalPosition(), LeftButton, PlatformEvent::MouseReleased, 1, gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey(), gestureEvent.timestamp());
handleMouseDownEvent(fakeMouseDown);
handleMouseReleaseEvent(fakeMouseUp);
return true;
}
case PlatformEvent::GestureScrollUpdate:
case PlatformEvent::GestureScrollUpdateWithoutPropagation: {
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());
handleWheelEvent(syntheticWheelEvent);
return true;
}
case PlatformEvent::GestureScrollBegin:
case PlatformEvent::GestureScrollEnd:
case PlatformEvent::GestureTapDown:
case PlatformEvent::GestureShowPress:
break;
default:
ASSERT_NOT_REACHED();
}
return false;
}
bool PopupContainer::handleKeyEvent(const PlatformKeyboardEvent& event)
{
UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
return m_listBox->handleKeyEvent(event);
}
void PopupContainer::hide()
{
m_listBox->abandon();
}
void PopupContainer::paint(GraphicsContext* gc, const IntRect& rect)
{
IntRect r = intersection(rect, frameRect());
int tx = x();
int ty = y();
r.move(-tx, -ty);
gc->translate(static_cast<float>(tx), static_cast<float>(ty));
m_listBox->paint(gc, r);
gc->translate(-static_cast<float>(tx), -static_cast<float>(ty));
paintBorder(gc, rect);
}
void PopupContainer::paintBorder(GraphicsContext* gc, const IntRect& rect)
{
Color borderColor(127, 157, 185);
gc->setStrokeStyle(NoStroke);
gc->setFillColor(borderColor);
int tx = x();
int ty = y();
gc->drawRect(IntRect(tx, ty, width(), borderSize));
gc->drawRect(IntRect(tx, ty, borderSize, height()));
gc->drawRect(IntRect(tx, ty + height() - borderSize, width(), borderSize));
gc->drawRect(IntRect(tx + width() - borderSize, ty, borderSize, height()));
}
bool PopupContainer::isInterestedInEventForKey(int keyCode)
{
return m_listBox->isInterestedInEventForKey(keyCode);
}
ChromeClient& PopupContainer::chromeClient()
{
return m_frameView->frame().page()->chrome().client();
}
void PopupContainer::showInRect(const FloatQuad& controlPosition, const IntSize& controlSize, FrameView* v, int index)
{
listBox()->setBaseWidth(max(controlSize.width() - borderSize * 2, 0));
listBox()->updateFromElement();
m_controlPosition.setP1(v->contentsToWindow(IntPoint(controlPosition.p1().x(), controlPosition.p1().y())));
m_controlPosition.setP2(v->contentsToWindow(IntPoint(controlPosition.p2().x(), controlPosition.p2().y())));
m_controlPosition.setP3(v->contentsToWindow(IntPoint(controlPosition.p3().x(), controlPosition.p3().y())));
m_controlPosition.setP4(v->contentsToWindow(IntPoint(controlPosition.p4().x(), controlPosition.p4().y())));
m_controlSize = controlSize;
setFrameRect(IntRect(IntPoint(), controlSize));
showPopup(v);
}
IntRect PopupContainer::refresh(const IntRect& targetControlRect)
{
listBox()->setBaseWidth(max(m_controlSize.width() - borderSize * 2, 0));
listBox()->updateFromElement();
IntPoint locationInWindow = m_frameView->contentsToWindow(targetControlRect.location());
locationInWindow.move(0, targetControlRect.height());
IntRect widgetRectInScreen = layoutAndCalculateWidgetRect(targetControlRect.height(), IntSize(), locationInWindow);
if (size() != widgetRectInScreen.size())
resize(widgetRectInScreen.size());
invalidate();
return widgetRectInScreen;
}
inline bool PopupContainer::isRTL() const
{
return m_listBox->m_popupClient->menuStyle().textDirection() == RTL;
}
int PopupContainer::selectedIndex() const
{
return m_listBox->selectedIndex();
}
int PopupContainer::menuItemHeight() const
{
return m_listBox->getRowHeight(0);
}
int PopupContainer::menuItemFontSize() const
{
return m_listBox->getRowFont(0).fontDescription().computedSize();
}
PopupMenuStyle PopupContainer::menuStyle() const
{
return m_listBox->m_popupClient->menuStyle();
}
const WTF::Vector<PopupItem*>& PopupContainer:: popupData() const
{
return m_listBox->items();
}
String PopupContainer::getSelectedItemToolTip()
{
return listBox()->m_popupClient->itemToolTip(listBox()->m_selectedIndex);
}
void PopupContainer::popupOpened(const IntRect& bounds)
{
WebViewImpl* webView = WebViewImpl::fromPage(m_frameView->frame().page());
if (!webView->client())
return;
WebWidget* webwidget = webView->client()->createPopupMenu(WebPopupTypeSelect);
if (!webwidget)
return;
webView->popupOpened(this);
toWebPopupMenuImpl(webwidget)->initialize(this, bounds);
}
void PopupContainer::getPopupMenuInfo(WebPopupMenuInfo* info)
{
const Vector<PopupItem*>& inputItems = popupData();
WebVector<WebMenuItemInfo> outputItems(inputItems.size());
for (size_t i = 0; i < inputItems.size(); ++i) {
const PopupItem& inputItem = *inputItems[i];
WebMenuItemInfo& outputItem = outputItems[i];
outputItem.label = inputItem.label;
outputItem.enabled = inputItem.enabled;
if (inputItem.textDirection == WebCore::RTL)
outputItem.textDirection = WebTextDirectionRightToLeft;
else
outputItem.textDirection = WebTextDirectionLeftToRight;
outputItem.hasTextDirectionOverride = inputItem.hasTextDirectionOverride;
switch (inputItem.type) {
case PopupItem::TypeOption:
outputItem.type = WebMenuItemInfo::Option;
break;
case PopupItem::TypeGroup:
outputItem.type = WebMenuItemInfo::Group;
break;
case PopupItem::TypeSeparator:
outputItem.type = WebMenuItemInfo::Separator;
break;
}
}
info->itemHeight = menuItemHeight();
info->itemFontSize = menuItemFontSize();
info->selectedIndex = selectedIndex();
info->items.swap(outputItems);
info->rightAligned = menuStyle().textDirection() == RTL;
}
}