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