2 * Copyright (C) 2010 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "web/ExternalPopupMenu.h"
34 #include "core/dom/NodeComputedStyle.h"
35 #include "core/frame/FrameHost.h"
36 #include "core/frame/FrameView.h"
37 #include "core/frame/LocalFrame.h"
38 #include "core/html/HTMLOptionElement.h"
39 #include "core/html/HTMLSelectElement.h"
40 #include "core/page/Page.h"
41 #include "core/style/ComputedStyle.h"
42 #include "platform/geometry/FloatQuad.h"
43 #include "platform/geometry/IntPoint.h"
44 #include "platform/text/TextDirection.h"
45 #include "public/platform/WebVector.h"
46 #include "public/web/WebExternalPopupMenu.h"
47 #include "public/web/WebFrameClient.h"
48 #include "public/web/WebMenuItemInfo.h"
49 #include "public/web/WebPopupMenuInfo.h"
50 #include "web/WebLocalFrameImpl.h"
51 #include "web/WebViewImpl.h"
55 ExternalPopupMenu::ExternalPopupMenu(LocalFrame
& frame
, HTMLSelectElement
& ownerElement
, WebViewImpl
& webView
)
56 : m_ownerElement(ownerElement
)
59 , m_dispatchEventTimer(this, &ExternalPopupMenu::dispatchEvent
)
60 , m_webExternalPopupMenu(0)
64 ExternalPopupMenu::~ExternalPopupMenu()
68 DEFINE_TRACE(ExternalPopupMenu
)
70 visitor
->trace(m_ownerElement
);
71 visitor
->trace(m_localFrame
);
72 PopupMenu::trace(visitor
);
75 void ExternalPopupMenu::show(const FloatQuad
& controlPosition
, const IntSize
&, int index
)
77 IntRect
rect(controlPosition
.enclosingBoundingBox());
78 // WebCore reuses the PopupMenu of an element.
79 // For simplicity, we do recreate the actual external popup everytime.
80 if (m_webExternalPopupMenu
) {
81 m_webExternalPopupMenu
->close();
82 m_webExternalPopupMenu
= 0;
85 WebPopupMenuInfo info
;
86 getPopupMenuInfo(info
, *m_ownerElement
);
87 if (info
.items
.isEmpty())
89 WebLocalFrameImpl
* webframe
= WebLocalFrameImpl::fromFrame(m_localFrame
.get());
90 m_webExternalPopupMenu
= webframe
->client()->createExternalPopupMenu(info
, this);
91 if (m_webExternalPopupMenu
) {
92 IntRect rectInViewport
= m_localFrame
->view()->soonToBeRemovedContentsToUnscaledViewport(rect
);
93 m_webExternalPopupMenu
->show(rectInViewport
);
95 const WebInputEvent
* currentEvent
= WebViewImpl::currentInputEvent();
96 if (currentEvent
&& currentEvent
->type
== WebInputEvent::MouseDown
) {
97 m_syntheticEvent
= adoptPtr(new WebMouseEvent
);
98 *m_syntheticEvent
= *static_cast<const WebMouseEvent
*>(currentEvent
);
99 m_syntheticEvent
->type
= WebInputEvent::MouseUp
;
100 m_dispatchEventTimer
.startOneShot(0, FROM_HERE
);
101 // FIXME: show() is asynchronous. If preparing a popup is slow and
102 // a user released the mouse button before showing the popup,
103 // mouseup and click events are correctly dispatched. Dispatching
104 // the synthetic mouseup event is redundant in this case.
108 // The client might refuse to create a popup (when there is already one pending to be shown for example).
113 void ExternalPopupMenu::dispatchEvent(Timer
<ExternalPopupMenu
>*)
115 m_webView
.handleInputEvent(*m_syntheticEvent
);
116 m_syntheticEvent
.clear();
119 void ExternalPopupMenu::hide()
122 m_ownerElement
->popupDidHide();
123 if (!m_webExternalPopupMenu
)
125 m_webExternalPopupMenu
->close();
126 m_webExternalPopupMenu
= 0;
129 void ExternalPopupMenu::updateFromElement()
133 void ExternalPopupMenu::disconnectClient()
136 m_ownerElement
= nullptr;
139 void ExternalPopupMenu::didChangeSelection(int index
)
143 void ExternalPopupMenu::didAcceptIndex(int index
)
145 // Calling methods on the HTMLSelectElement might lead to this object being
146 // derefed. This ensures it does not get deleted while we are running this
148 int popupMenuItemIndex
= toPopupMenuItemIndex(index
, *m_ownerElement
);
149 RefPtrWillBeRawPtr
<ExternalPopupMenu
> guard(this);
151 if (m_ownerElement
) {
152 m_ownerElement
->popupDidHide();
153 m_ownerElement
->valueChanged(popupMenuItemIndex
);
155 m_webExternalPopupMenu
= 0;
158 void ExternalPopupMenu::didAcceptIndices(const WebVector
<int>& indices
)
160 if (!m_ownerElement
) {
161 m_webExternalPopupMenu
= 0;
165 // Calling methods on the HTMLSelectElement might lead to this object being
166 // derefed. This ensures it does not get deleted while we are running this
168 RefPtrWillBeRawPtr
<ExternalPopupMenu
> protect(this);
169 RefPtrWillBeRawPtr
<HTMLSelectElement
> ownerElement(m_ownerElement
.get());
170 ownerElement
->popupDidHide();
172 if (indices
.size() == 0) {
173 ownerElement
->valueChanged(static_cast<unsigned>(-1));
175 for (size_t i
= 0; i
< indices
.size(); ++i
)
176 ownerElement
->listBoxSelectItem(toPopupMenuItemIndex(indices
[i
], *ownerElement
), (i
> 0), false, (i
== indices
.size() - 1));
179 m_webExternalPopupMenu
= 0;
182 void ExternalPopupMenu::didCancel()
184 // See comment in didAcceptIndex on why we need this.
185 RefPtrWillBeRawPtr
<ExternalPopupMenu
> guard(this);
188 m_ownerElement
->popupDidHide();
189 m_webExternalPopupMenu
= 0;
192 void ExternalPopupMenu::getPopupMenuInfo(WebPopupMenuInfo
& info
, HTMLSelectElement
& ownerElement
)
194 const WillBeHeapVector
<RawPtrWillBeMember
<HTMLElement
>>& listItems
= ownerElement
.listItems();
195 size_t itemCount
= listItems
.size();
197 Vector
<WebMenuItemInfo
> items(itemCount
);
198 for (size_t i
= 0; i
< itemCount
; ++i
) {
199 if (ownerElement
.itemIsDisplayNone(*listItems
[i
]))
202 Element
& itemElement
= *listItems
[i
];
203 WebMenuItemInfo
& popupItem
= items
[count
++];
204 popupItem
.label
= ownerElement
.itemText(itemElement
);
205 popupItem
.toolTip
= itemElement
.title();
206 popupItem
.checked
= false;
207 if (isHTMLHRElement(itemElement
)) {
208 popupItem
.type
= WebMenuItemInfo::Separator
;
209 } else if (isHTMLOptGroupElement(itemElement
)) {
210 popupItem
.type
= WebMenuItemInfo::Group
;
212 popupItem
.type
= WebMenuItemInfo::Option
;
213 popupItem
.checked
= toHTMLOptionElement(itemElement
).selected();
215 popupItem
.enabled
= !itemElement
.isDisabledFormControl();
216 const ComputedStyle
& style
= *ownerElement
.itemComputedStyle(itemElement
);
217 popupItem
.textDirection
= toWebTextDirection(style
.direction());
218 popupItem
.hasTextDirectionOverride
= isOverride(style
.unicodeBidi());
221 const ComputedStyle
& menuStyle
= ownerElement
.computedStyle() ? *ownerElement
.computedStyle() : *ownerElement
.ensureComputedStyle();
222 info
.itemHeight
= menuStyle
.font().fontMetrics().height();
223 info
.itemFontSize
= static_cast<int>(menuStyle
.font().fontDescription().computedSize());
224 info
.selectedIndex
= toExternalPopupMenuItemIndex(ownerElement
.optionToListIndex(ownerElement
.selectedIndex()), ownerElement
);
225 info
.rightAligned
= menuStyle
.direction() == RTL
;
226 info
.allowMultipleSelection
= ownerElement
.multiple();
227 if (count
< itemCount
)
233 int ExternalPopupMenu::toPopupMenuItemIndex(int externalPopupMenuItemIndex
, HTMLSelectElement
& ownerElement
)
235 if (externalPopupMenuItemIndex
< 0)
236 return externalPopupMenuItemIndex
;
238 int indexTracker
= 0;
239 const WillBeHeapVector
<RawPtrWillBeMember
<HTMLElement
>>& items
= ownerElement
.listItems();
240 for (int i
= 0; i
< static_cast<int>(items
.size()); ++i
) {
241 if (ownerElement
.itemIsDisplayNone(*items
[i
]))
243 if (indexTracker
++ == externalPopupMenuItemIndex
)
249 int ExternalPopupMenu::toExternalPopupMenuItemIndex(int popupMenuItemIndex
, HTMLSelectElement
& ownerElement
)
251 if (popupMenuItemIndex
< 0)
252 return popupMenuItemIndex
;
254 size_t indexTracker
= 0;
255 const WillBeHeapVector
<RawPtrWillBeMember
<HTMLElement
>>& items
= ownerElement
.listItems();
256 for (int i
= 0; i
< static_cast<int>(items
.size()); ++i
) {
257 if (ownerElement
.itemIsDisplayNone(*items
[i
]))
259 if (popupMenuItemIndex
== i
)