1 /* 2 * Copyright (C) 2010 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/ExternalPopupMenu.h" 33 34 #include "core/frame/FrameView.h" 35 #include "core/frame/LocalFrame.h" 36 #include "platform/PopupMenuClient.h" 37 #include "platform/geometry/FloatQuad.h" 38 #include "platform/geometry/IntPoint.h" 39 #include "platform/text/TextDirection.h" 40 #include "public/platform/WebVector.h" 41 #include "public/web/WebExternalPopupMenu.h" 42 #include "public/web/WebFrameClient.h" 43 #include "public/web/WebMenuItemInfo.h" 44 #include "public/web/WebPopupMenuInfo.h" 45 #include "web/WebLocalFrameImpl.h" 46 #include "web/WebViewImpl.h" 47 48 namespace blink { 49 50 ExternalPopupMenu::ExternalPopupMenu(LocalFrame& frame, PopupMenuClient* popupMenuClient, WebViewImpl& webView) 51 : m_popupMenuClient(popupMenuClient) 52 , m_localFrame(frame) 53 , m_webView(webView) 54 , m_dispatchEventTimer(this, &ExternalPopupMenu::dispatchEvent) 55 , m_webExternalPopupMenu(0) 56 { 57 } 58 59 ExternalPopupMenu::~ExternalPopupMenu() 60 { 61 } 62 63 void ExternalPopupMenu::trace(Visitor* visitor) 64 { 65 visitor->trace(m_localFrame); 66 PopupMenu::trace(visitor); 67 } 68 69 void ExternalPopupMenu::show(const FloatQuad& controlPosition, const IntSize&, int index) 70 { 71 IntRect rect(controlPosition.enclosingBoundingBox()); 72 // WebCore reuses the PopupMenu of an element. 73 // For simplicity, we do recreate the actual external popup everytime. 74 if (m_webExternalPopupMenu) { 75 m_webExternalPopupMenu->close(); 76 m_webExternalPopupMenu = 0; 77 } 78 79 WebPopupMenuInfo info; 80 getPopupMenuInfo(info, *m_popupMenuClient); 81 if (info.items.isEmpty()) 82 return; 83 WebLocalFrameImpl* webframe = WebLocalFrameImpl::fromFrame(m_localFrame.get()); 84 m_webExternalPopupMenu = webframe->client()->createExternalPopupMenu(info, this); 85 if (m_webExternalPopupMenu) { 86 m_webExternalPopupMenu->show(m_localFrame->view()->contentsToWindow(rect)); 87 #if OS(MACOSX) 88 const WebInputEvent* currentEvent = WebViewImpl::currentInputEvent(); 89 if (currentEvent && currentEvent->type == WebInputEvent::MouseDown) { 90 m_syntheticEvent = adoptPtr(new WebMouseEvent); 91 *m_syntheticEvent = *static_cast<const WebMouseEvent*>(currentEvent); 92 m_syntheticEvent->type = WebInputEvent::MouseUp; 93 m_dispatchEventTimer.startOneShot(0, FROM_HERE); 94 // FIXME: show() is asynchronous. If preparing a popup is slow and 95 // a user released the mouse button before showing the popup, 96 // mouseup and click events are correctly dispatched. Dispatching 97 // the synthetic mouseup event is redundant in this case. 98 } 99 #endif 100 } else { 101 // The client might refuse to create a popup (when there is already one pending to be shown for example). 102 didCancel(); 103 } 104 } 105 106 void ExternalPopupMenu::dispatchEvent(Timer<ExternalPopupMenu>*) 107 { 108 m_webView.handleInputEvent(*m_syntheticEvent); 109 m_syntheticEvent.clear(); 110 } 111 112 void ExternalPopupMenu::hide() 113 { 114 if (m_popupMenuClient) 115 m_popupMenuClient->popupDidHide(); 116 if (!m_webExternalPopupMenu) 117 return; 118 m_webExternalPopupMenu->close(); 119 m_webExternalPopupMenu = 0; 120 } 121 122 void ExternalPopupMenu::updateFromElement() 123 { 124 } 125 126 void ExternalPopupMenu::disconnectClient() 127 { 128 hide(); 129 m_popupMenuClient = 0; 130 } 131 132 void ExternalPopupMenu::didChangeSelection(int index) 133 { 134 if (m_popupMenuClient) 135 m_popupMenuClient->selectionChanged(toPopupMenuItemIndex(index, *m_popupMenuClient)); 136 } 137 138 void ExternalPopupMenu::didAcceptIndex(int index) 139 { 140 // Calling methods on the PopupMenuClient might lead to this object being 141 // derefed. This ensures it does not get deleted while we are running this 142 // method. 143 int popupMenuItemIndex = toPopupMenuItemIndex(index, *m_popupMenuClient); 144 RefPtrWillBeRawPtr<ExternalPopupMenu> guard(this); 145 146 if (m_popupMenuClient) { 147 m_popupMenuClient->popupDidHide(); 148 m_popupMenuClient->valueChanged(popupMenuItemIndex); 149 } 150 m_webExternalPopupMenu = 0; 151 } 152 153 void ExternalPopupMenu::didAcceptIndices(const WebVector<int>& indices) 154 { 155 if (!m_popupMenuClient) { 156 m_webExternalPopupMenu = 0; 157 return; 158 } 159 160 // Calling methods on the PopupMenuClient might lead to this object being 161 // derefed. This ensures it does not get deleted while we are running this 162 // method. 163 RefPtrWillBeRawPtr<ExternalPopupMenu> protect(this); 164 165 m_popupMenuClient->popupDidHide(); 166 167 if (!indices.size()) 168 m_popupMenuClient->valueChanged(static_cast<unsigned>(-1), true); 169 else { 170 for (size_t i = 0; i < indices.size(); ++i) 171 m_popupMenuClient->listBoxSelectItem(toPopupMenuItemIndex(indices[i], *m_popupMenuClient), (i > 0), false, (i == indices.size() - 1)); 172 } 173 174 m_webExternalPopupMenu = 0; 175 } 176 177 void ExternalPopupMenu::didCancel() 178 { 179 // See comment in didAcceptIndex on why we need this. 180 RefPtrWillBeRawPtr<ExternalPopupMenu> guard(this); 181 182 if (m_popupMenuClient) 183 m_popupMenuClient->popupDidHide(); 184 m_webExternalPopupMenu = 0; 185 } 186 187 void ExternalPopupMenu::getPopupMenuInfo(WebPopupMenuInfo& info, PopupMenuClient& popupMenuClient) 188 { 189 int itemCount = popupMenuClient.listSize(); 190 int count = 0; 191 Vector<WebMenuItemInfo> items(static_cast<size_t>(itemCount)); 192 for (int i = 0; i < itemCount; ++i) { 193 PopupMenuStyle style = popupMenuClient.itemStyle(i); 194 if (style.isDisplayNone()) 195 continue; 196 197 WebMenuItemInfo& popupItem = items[count++]; 198 popupItem.label = popupMenuClient.itemText(i); 199 popupItem.toolTip = popupMenuClient.itemToolTip(i); 200 if (popupMenuClient.itemIsSeparator(i)) 201 popupItem.type = WebMenuItemInfo::Separator; 202 else if (popupMenuClient.itemIsLabel(i)) 203 popupItem.type = WebMenuItemInfo::Group; 204 else 205 popupItem.type = WebMenuItemInfo::Option; 206 popupItem.enabled = popupMenuClient.itemIsEnabled(i); 207 popupItem.checked = popupMenuClient.itemIsSelected(i); 208 popupItem.textDirection = toWebTextDirection(style.textDirection()); 209 popupItem.hasTextDirectionOverride = style.hasTextDirectionOverride(); 210 } 211 212 info.itemHeight = popupMenuClient.menuStyle().font().fontMetrics().height(); 213 info.itemFontSize = static_cast<int>(popupMenuClient.menuStyle().font().fontDescription().computedSize()); 214 info.selectedIndex = toExternalPopupMenuItemIndex(popupMenuClient.selectedIndex(), popupMenuClient); 215 info.rightAligned = popupMenuClient.menuStyle().textDirection() == RTL; 216 info.allowMultipleSelection = popupMenuClient.multiple(); 217 if (count < itemCount) 218 items.shrink(count); 219 info.items = items; 220 221 } 222 223 int ExternalPopupMenu::toPopupMenuItemIndex(int externalPopupMenuItemIndex, PopupMenuClient& popupMenuClient) 224 { 225 if (externalPopupMenuItemIndex < 0) 226 return externalPopupMenuItemIndex; 227 228 int itemCount = popupMenuClient.listSize(); 229 int indexTracker = 0; 230 for (int i = 0; i < itemCount ; ++i) { 231 if (popupMenuClient.itemStyle(i).isDisplayNone()) 232 continue; 233 if (indexTracker++ == externalPopupMenuItemIndex) 234 return i; 235 } 236 return -1; 237 } 238 239 int ExternalPopupMenu::toExternalPopupMenuItemIndex(int popupMenuItemIndex, PopupMenuClient& popupMenuClient) 240 { 241 if (popupMenuItemIndex < 0) 242 return popupMenuItemIndex; 243 244 int itemCount = popupMenuClient.listSize(); 245 int indexTracker = 0; 246 for (int i = 0; i < itemCount; ++i) { 247 if (popupMenuClient.itemStyle(i).isDisplayNone()) 248 continue; 249 if (popupMenuItemIndex == i) 250 return indexTracker; 251 ++indexTracker; 252 } 253 return -1; 254 } 255 256 } // namespace blink 257