OLD | NEW |
| (Empty) |
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 "core/platform/chromium/PopupContainer.h" | |
33 | |
34 #include "core/dom/Document.h" | |
35 #include "core/dom/UserGestureIndicator.h" | |
36 #include "core/page/Chrome.h" | |
37 #include "core/page/ChromeClient.h" | |
38 #include "core/page/Frame.h" | |
39 #include "core/page/FrameView.h" | |
40 #include "core/page/Page.h" | |
41 #include "core/platform/PlatformGestureEvent.h" | |
42 #include "core/platform/PlatformKeyboardEvent.h" | |
43 #include "core/platform/PlatformMouseEvent.h" | |
44 #include "core/platform/PlatformScreen.h" | |
45 #include "core/platform/PlatformTouchEvent.h" | |
46 #include "core/platform/PlatformWheelEvent.h" | |
47 #include "core/platform/PopupMenuClient.h" | |
48 #include "core/platform/chromium/FramelessScrollView.h" | |
49 #include "core/platform/chromium/FramelessScrollViewClient.h" | |
50 #include "core/platform/chromium/PopupListBox.h" | |
51 #include "core/platform/graphics/GraphicsContext.h" | |
52 #include "core/platform/graphics/IntRect.h" | |
53 #include <limits> | |
54 | |
55 namespace WebCore { | |
56 | |
57 static const int borderSize = 1; | |
58 | |
59 static PlatformMouseEvent constructRelativeMouseEvent(const PlatformMouseEvent&
e, FramelessScrollView* parent, FramelessScrollView* child) | |
60 { | |
61 IntPoint pos = parent->convertSelfToChild(child, e.position()); | |
62 | |
63 // FIXME: This is a horrible hack since PlatformMouseEvent has no setters fo
r x/y. | |
64 PlatformMouseEvent relativeEvent = e; | |
65 IntPoint& relativePos = const_cast<IntPoint&>(relativeEvent.position()); | |
66 relativePos.setX(pos.x()); | |
67 relativePos.setY(pos.y()); | |
68 return relativeEvent; | |
69 } | |
70 | |
71 static PlatformWheelEvent constructRelativeWheelEvent(const PlatformWheelEvent&
e, FramelessScrollView* parent, FramelessScrollView* child) | |
72 { | |
73 IntPoint pos = parent->convertSelfToChild(child, e.position()); | |
74 | |
75 // FIXME: This is a horrible hack since PlatformWheelEvent has no setters fo
r x/y. | |
76 PlatformWheelEvent relativeEvent = e; | |
77 IntPoint& relativePos = const_cast<IntPoint&>(relativeEvent.position()); | |
78 relativePos.setX(pos.x()); | |
79 relativePos.setY(pos.y()); | |
80 return relativeEvent; | |
81 } | |
82 | |
83 // static | |
84 PassRefPtr<PopupContainer> PopupContainer::create(PopupMenuClient* client, Popup
Type popupType, const PopupContainerSettings& settings) | |
85 { | |
86 return adoptRef(new PopupContainer(client, popupType, settings)); | |
87 } | |
88 | |
89 PopupContainer::PopupContainer(PopupMenuClient* client, PopupType popupType, con
st PopupContainerSettings& settings) | |
90 : m_listBox(PopupListBox::create(client, settings)) | |
91 , m_settings(settings) | |
92 , m_popupType(popupType) | |
93 , m_popupOpen(false) | |
94 { | |
95 setScrollbarModes(ScrollbarAlwaysOff, ScrollbarAlwaysOff); | |
96 } | |
97 | |
98 PopupContainer::~PopupContainer() | |
99 { | |
100 if (m_listBox && m_listBox->parent()) | |
101 removeChild(m_listBox.get()); | |
102 } | |
103 | |
104 IntRect PopupContainer::layoutAndCalculateWidgetRectInternal(IntRect widgetRectI
nScreen, int targetControlHeight, const FloatRect& windowRect, const FloatRect&
screen, bool isRTL, const int rtlOffset, const int verticalOffset, const IntSize
& transformOffset, PopupContent* listBox, bool& needToResizeView) | |
105 { | |
106 ASSERT(listBox); | |
107 if (windowRect.x() >= screen.x() && windowRect.maxX() <= screen.maxX() && (w
idgetRectInScreen.x() < screen.x() || widgetRectInScreen.maxX() > screen.maxX())
) { | |
108 // First, inverse the popup alignment if it does not fit the screen - | |
109 // this might fix things (or make them better). | |
110 IntRect inverseWidgetRectInScreen = widgetRectInScreen; | |
111 inverseWidgetRectInScreen.setX(inverseWidgetRectInScreen.x() + (isRTL ?
-rtlOffset : rtlOffset)); | |
112 inverseWidgetRectInScreen.setY(inverseWidgetRectInScreen.y() + (isRTL ?
-verticalOffset : verticalOffset)); | |
113 IntRect enclosingScreen = enclosingIntRect(screen); | |
114 unsigned originalCutoff = std::max(enclosingScreen.x() - widgetRectInScr
een.x(), 0) + std::max(widgetRectInScreen.maxX() - enclosingScreen.maxX(), 0); | |
115 unsigned inverseCutoff = std::max(enclosingScreen.x() - inverseWidgetRec
tInScreen.x(), 0) + std::max(inverseWidgetRectInScreen.maxX() - enclosingScreen.
maxX(), 0); | |
116 | |
117 // Accept the inverse popup alignment if the trimmed content gets | |
118 // shorter than that in the original alignment case. | |
119 if (inverseCutoff < originalCutoff) | |
120 widgetRectInScreen = inverseWidgetRectInScreen; | |
121 | |
122 if (widgetRectInScreen.x() < screen.x()) { | |
123 widgetRectInScreen.setWidth(widgetRectInScreen.maxX() - screen.x()); | |
124 widgetRectInScreen.setX(screen.x()); | |
125 listBox->setMaxWidthAndLayout(std::max(widgetRectInScreen.width() -
borderSize * 2, 0)); | |
126 } else if (widgetRectInScreen.maxX() > screen.maxX()) { | |
127 widgetRectInScreen.setWidth(screen.maxX() - widgetRectInScreen.x()); | |
128 listBox->setMaxWidthAndLayout(std::max(widgetRectInScreen.width() -
borderSize * 2, 0)); | |
129 } | |
130 } | |
131 | |
132 // Calculate Y axis size. | |
133 if (widgetRectInScreen.maxY() > static_cast<int>(screen.maxY())) { | |
134 if (widgetRectInScreen.y() - widgetRectInScreen.height() - targetControl
Height - transformOffset.height() > 0) { | |
135 // There is enough room to open upwards. | |
136 widgetRectInScreen.move(-transformOffset.width(), -(widgetRectInScre
en.height() + targetControlHeight + transformOffset.height())); | |
137 } else { | |
138 // Figure whether upwards or downwards has more room and set the | |
139 // maximum number of items. | |
140 int spaceAbove = widgetRectInScreen.y() - targetControlHeight + tran
sformOffset.height(); | |
141 int spaceBelow = screen.maxY() - widgetRectInScreen.y(); | |
142 if (spaceAbove > spaceBelow) | |
143 listBox->setMaxHeight(spaceAbove); | |
144 else | |
145 listBox->setMaxHeight(spaceBelow); | |
146 listBox->layout(); | |
147 needToResizeView = true; | |
148 widgetRectInScreen.setHeight(listBox->popupContentHeight() + borderS
ize * 2); | |
149 // Move WebWidget upwards if necessary. | |
150 if (spaceAbove > spaceBelow) | |
151 widgetRectInScreen.move(-transformOffset.width(), -(widgetRectIn
Screen.height() + targetControlHeight + transformOffset.height())); | |
152 } | |
153 } | |
154 return widgetRectInScreen; | |
155 } | |
156 | |
157 IntRect PopupContainer::layoutAndCalculateWidgetRect(int targetControlHeight, co
nst IntSize& transformOffset, const IntPoint& popupInitialCoordinate) | |
158 { | |
159 // Reset the max width and height to their default values, they will be | |
160 // recomputed below if necessary. | |
161 m_listBox->setMaxHeight(PopupListBox::defaultMaxHeight); | |
162 m_listBox->setMaxWidth(std::numeric_limits<int>::max()); | |
163 | |
164 // Lay everything out to figure out our preferred size, then tell the view's | |
165 // WidgetClient about it. It should assign us a client. | |
166 m_listBox->layout(); | |
167 fitToListBox(); | |
168 bool isRTL = this->isRTL(); | |
169 | |
170 // Compute the starting x-axis for a normal RTL or right-aligned LTR | |
171 // dropdown. For those, the right edge of dropdown box should be aligned | |
172 // with the right edge of <select>/<input> element box, and the dropdown box | |
173 // should be expanded to the left if more space is needed. | |
174 // m_originalFrameRect.width() is the width of the target <select>/<input> | |
175 // element. | |
176 int rtlOffset = m_controlPosition.p2().x() - m_controlPosition.p1().x() - (m
_listBox->width() + borderSize * 2); | |
177 int rightOffset = isRTL ? rtlOffset : 0; | |
178 | |
179 // Compute the y-axis offset between the bottom left and bottom right | |
180 // points. If the <select>/<input> is transformed, they are not the same. | |
181 int verticalOffset = - m_controlPosition.p4().y() + m_controlPosition.p3().y
(); | |
182 int verticalForRTLOffset = isRTL ? verticalOffset : 0; | |
183 | |
184 // Assume m_listBox size is already calculated. | |
185 IntSize targetSize(m_listBox->width() + borderSize * 2, m_listBox->height()
+ borderSize * 2); | |
186 | |
187 IntRect widgetRectInScreen; | |
188 if (ChromeClient* client = chromeClient()) { | |
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 widgetRectInScreen = client->rootViewToScreen(IntRect(popupInitialCoordi
nate.x() + rightOffset, popupInitialCoordinate.y() + verticalForRTLOffset, targe
tSize.width(), targetSize.height())); | |
195 | |
196 // If we have multiple screens and the browser rect is in one screen, we | |
197 // have to clip the window width to the screen width. | |
198 // When clipping, we also need to set a maximum width for the list box. | |
199 FloatRect windowRect = client->windowRect(); | |
200 | |
201 bool needToResizeView = false; | |
202 widgetRectInScreen = layoutAndCalculateWidgetRectInternal(widgetRectInSc
reen, targetControlHeight, windowRect, screen, isRTL, rtlOffset, verticalOffset,
transformOffset, m_listBox.get(), needToResizeView); | |
203 if (needToResizeView) | |
204 fitToListBox(); | |
205 } | |
206 | |
207 return widgetRectInScreen; | |
208 } | |
209 | |
210 void PopupContainer::showPopup(FrameView* view) | |
211 { | |
212 m_frameView = view; | |
213 listBox()->m_focusedNode = m_frameView->frame()->document()->focusedNode(); | |
214 | |
215 if (ChromeClient* client = chromeClient()) { | |
216 IntSize transformOffset(m_controlPosition.p4().x() - m_controlPosition.p
1().x(), m_controlPosition.p4().y() - m_controlPosition.p1().y() - m_controlSize
.height()); | |
217 client->popupOpened(this, layoutAndCalculateWidgetRect(m_controlSize.hei
ght(), transformOffset, roundedIntPoint(m_controlPosition.p4())), false); | |
218 m_popupOpen = true; | |
219 } | |
220 | |
221 if (!m_listBox->parent()) | |
222 addChild(m_listBox.get()); | |
223 | |
224 // Enable scrollbars after the listbox is inserted into the hierarchy, | |
225 // so it has a proper WidgetClient. | |
226 m_listBox->setVerticalScrollbarMode(ScrollbarAuto); | |
227 | |
228 m_listBox->scrollToRevealSelection(); | |
229 | |
230 invalidate(); | |
231 } | |
232 | |
233 void PopupContainer::hidePopup() | |
234 { | |
235 listBox()->hidePopup(); | |
236 } | |
237 | |
238 void PopupContainer::notifyPopupHidden() | |
239 { | |
240 if (!m_popupOpen) | |
241 return; | |
242 m_popupOpen = false; | |
243 chromeClient()->popupClosed(this); | |
244 } | |
245 | |
246 void PopupContainer::fitToListBox() | |
247 { | |
248 // Place the listbox within our border. | |
249 m_listBox->move(borderSize, borderSize); | |
250 | |
251 // Size ourselves to contain listbox + border. | |
252 resize(m_listBox->width() + borderSize * 2, m_listBox->height() + borderSize
* 2); | |
253 invalidate(); | |
254 } | |
255 | |
256 bool PopupContainer::handleMouseDownEvent(const PlatformMouseEvent& event) | |
257 { | |
258 UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture); | |
259 return m_listBox->handleMouseDownEvent( | |
260 constructRelativeMouseEvent(event, this, m_listBox.get())); | |
261 } | |
262 | |
263 bool PopupContainer::handleMouseMoveEvent(const PlatformMouseEvent& event) | |
264 { | |
265 UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture); | |
266 return m_listBox->handleMouseMoveEvent( | |
267 constructRelativeMouseEvent(event, this, m_listBox.get())); | |
268 } | |
269 | |
270 bool PopupContainer::handleMouseReleaseEvent(const PlatformMouseEvent& event) | |
271 { | |
272 RefPtr<PopupContainer> protect(this); | |
273 UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture); | |
274 return m_listBox->handleMouseReleaseEvent( | |
275 constructRelativeMouseEvent(event, this, m_listBox.get())); | |
276 } | |
277 | |
278 bool PopupContainer::handleWheelEvent(const PlatformWheelEvent& event) | |
279 { | |
280 UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture); | |
281 return m_listBox->handleWheelEvent( | |
282 constructRelativeWheelEvent(event, this, m_listBox.get())); | |
283 } | |
284 | |
285 bool PopupContainer::handleTouchEvent(const PlatformTouchEvent&) | |
286 { | |
287 return false; | |
288 } | |
289 | |
290 // FIXME: Refactor this code to share functionality with | |
291 // EventHandler::handleGestureEvent. | |
292 bool PopupContainer::handleGestureEvent(const PlatformGestureEvent& gestureEvent
) | |
293 { | |
294 switch (gestureEvent.type()) { | |
295 case PlatformEvent::GestureTap: { | |
296 PlatformMouseEvent fakeMouseMove(gestureEvent.position(), gestureEvent.g
lobalPosition(), NoButton, PlatformEvent::MouseMoved, /* clickCount */ 1, gestur
eEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.m
etaKey(), gestureEvent.timestamp()); | |
297 PlatformMouseEvent fakeMouseDown(gestureEvent.position(), gestureEvent.g
lobalPosition(), LeftButton, PlatformEvent::MousePressed, /* clickCount */ 1, ge
stureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEve
nt.metaKey(), gestureEvent.timestamp()); | |
298 PlatformMouseEvent fakeMouseUp(gestureEvent.position(), gestureEvent.glo
balPosition(), LeftButton, PlatformEvent::MouseReleased, /* clickCount */ 1, ges
tureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEven
t.metaKey(), gestureEvent.timestamp()); | |
299 // handleMouseMoveEvent(fakeMouseMove); | |
300 handleMouseDownEvent(fakeMouseDown); | |
301 handleMouseReleaseEvent(fakeMouseUp); | |
302 return true; | |
303 } | |
304 case PlatformEvent::GestureScrollUpdate: | |
305 case PlatformEvent::GestureScrollUpdateWithoutPropagation: { | |
306 PlatformWheelEvent syntheticWheelEvent(gestureEvent.position(), gestureE
vent.globalPosition(), gestureEvent.deltaX(), gestureEvent.deltaY(), gestureEven
t.deltaX() / 120.0f, gestureEvent.deltaY() / 120.0f, ScrollByPixelWheelEvent, ge
stureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEve
nt.metaKey()); | |
307 handleWheelEvent(syntheticWheelEvent); | |
308 return true; | |
309 } | |
310 case PlatformEvent::GestureScrollBegin: | |
311 case PlatformEvent::GestureScrollEnd: | |
312 case PlatformEvent::GestureTapDown: | |
313 break; | |
314 default: | |
315 ASSERT_NOT_REACHED(); | |
316 } | |
317 return false; | |
318 } | |
319 | |
320 bool PopupContainer::handleKeyEvent(const PlatformKeyboardEvent& event) | |
321 { | |
322 UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture); | |
323 return m_listBox->handleKeyEvent(event); | |
324 } | |
325 | |
326 void PopupContainer::hide() | |
327 { | |
328 m_listBox->abandon(); | |
329 } | |
330 | |
331 void PopupContainer::paint(GraphicsContext* gc, const IntRect& rect) | |
332 { | |
333 // Adjust coords for scrolled frame. | |
334 IntRect r = intersection(rect, frameRect()); | |
335 int tx = x(); | |
336 int ty = y(); | |
337 | |
338 r.move(-tx, -ty); | |
339 | |
340 gc->translate(static_cast<float>(tx), static_cast<float>(ty)); | |
341 m_listBox->paint(gc, r); | |
342 gc->translate(-static_cast<float>(tx), -static_cast<float>(ty)); | |
343 | |
344 paintBorder(gc, rect); | |
345 } | |
346 | |
347 void PopupContainer::paintBorder(GraphicsContext* gc, const IntRect& rect) | |
348 { | |
349 // FIXME: Where do we get the border color from? | |
350 Color borderColor(127, 157, 185); | |
351 | |
352 gc->setStrokeStyle(NoStroke); | |
353 gc->setFillColor(borderColor, ColorSpaceDeviceRGB); | |
354 | |
355 int tx = x(); | |
356 int ty = y(); | |
357 | |
358 // top, left, bottom, right | |
359 gc->drawRect(IntRect(tx, ty, width(), borderSize)); | |
360 gc->drawRect(IntRect(tx, ty, borderSize, height())); | |
361 gc->drawRect(IntRect(tx, ty + height() - borderSize, width(), borderSize)); | |
362 gc->drawRect(IntRect(tx + width() - borderSize, ty, borderSize, height())); | |
363 } | |
364 | |
365 bool PopupContainer::isInterestedInEventForKey(int keyCode) | |
366 { | |
367 return m_listBox->isInterestedInEventForKey(keyCode); | |
368 } | |
369 | |
370 ChromeClient* PopupContainer::chromeClient() | |
371 { | |
372 return m_frameView->frame()->page()->chrome().client(); | |
373 } | |
374 | |
375 void PopupContainer::showInRect(const FloatQuad& controlPosition, const IntSize&
controlSize, FrameView* v, int index) | |
376 { | |
377 // The controlSize is the size of the select box. It's usually larger than | |
378 // we need. Subtract border size so that usually the container will be | |
379 // displayed exactly the same width as the select box. | |
380 listBox()->setBaseWidth(max(controlSize.width() - borderSize * 2, 0)); | |
381 | |
382 listBox()->updateFromElement(); | |
383 | |
384 // We set the selected item in updateFromElement(), and disregard the | |
385 // index passed into this function (same as Webkit's PopupMenuWin.cpp) | |
386 // FIXME: make sure this is correct, and add an assertion. | |
387 // ASSERT(popupWindow(popup)->listBox()->selectedIndex() == index); | |
388 | |
389 // Save and convert the controlPosition to main window coords. | |
390 m_controlPosition = controlPosition; | |
391 IntPoint delta = v->contentsToWindow(IntPoint()); | |
392 m_controlPosition.move(delta.x(), delta.y()); | |
393 m_controlSize = controlSize; | |
394 | |
395 // Position at (0, 0) since the frameRect().location() is relative to the | |
396 // parent WebWidget. | |
397 setFrameRect(IntRect(IntPoint(), controlSize)); | |
398 showPopup(v); | |
399 } | |
400 | |
401 IntRect PopupContainer::refresh(const IntRect& targetControlRect) | |
402 { | |
403 listBox()->setBaseWidth(max(m_controlSize.width() - borderSize * 2, 0)); | |
404 listBox()->updateFromElement(); | |
405 | |
406 IntPoint locationInWindow = m_frameView->contentsToWindow(targetControlRect.
location()); | |
407 | |
408 // Move it below the select widget. | |
409 locationInWindow.move(0, targetControlRect.height()); | |
410 | |
411 IntRect widgetRectInScreen = layoutAndCalculateWidgetRect(targetControlRect.
height(), IntSize(), locationInWindow); | |
412 | |
413 // Reset the size (which can be set to the PopupListBox size in | |
414 // layoutAndGetRTLOffset(), exceeding the available widget rectangle.) | |
415 if (size() != widgetRectInScreen.size()) | |
416 resize(widgetRectInScreen.size()); | |
417 | |
418 invalidate(); | |
419 | |
420 return widgetRectInScreen; | |
421 } | |
422 | |
423 inline bool PopupContainer::isRTL() const | |
424 { | |
425 return m_listBox->m_popupClient->menuStyle().textDirection() == RTL; | |
426 } | |
427 | |
428 int PopupContainer::selectedIndex() const | |
429 { | |
430 return m_listBox->selectedIndex(); | |
431 } | |
432 | |
433 int PopupContainer::menuItemHeight() const | |
434 { | |
435 return m_listBox->getRowHeight(0); | |
436 } | |
437 | |
438 int PopupContainer::menuItemFontSize() const | |
439 { | |
440 return m_listBox->getRowFont(0).size(); | |
441 } | |
442 | |
443 PopupMenuStyle PopupContainer::menuStyle() const | |
444 { | |
445 return m_listBox->m_popupClient->menuStyle(); | |
446 } | |
447 | |
448 const WTF::Vector<PopupItem*>& PopupContainer:: popupData() const | |
449 { | |
450 return m_listBox->items(); | |
451 } | |
452 | |
453 String PopupContainer::getSelectedItemToolTip() | |
454 { | |
455 // We cannot use m_popupClient->selectedIndex() to choose tooltip message, | |
456 // because the selectedIndex() might return final selected index, not | |
457 // hovering selection. | |
458 return listBox()->m_popupClient->itemToolTip(listBox()->m_selectedIndex); | |
459 } | |
460 | |
461 } // namespace WebCore | |
OLD | NEW |