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