| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2009 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 "ContextMenuClientImpl.h" | |
| 33 | |
| 34 #include "ContextMenu.h" | |
| 35 #include "Document.h" | |
| 36 #include "DocumentLoader.h" | |
| 37 #include "Editor.h" | |
| 38 #include "EventHandler.h" | |
| 39 #include "FrameLoader.h" | |
| 40 #include "FrameView.h" | |
| 41 #include "HitTestResult.h" | |
| 42 #include "HTMLMediaElement.h" | |
| 43 #include "HTMLNames.h" | |
| 44 #include "KURL.h" | |
| 45 #include "MediaError.h" | |
| 46 #include "PlatformString.h" | |
| 47 #include "TextBreakIterator.h" | |
| 48 #include "Widget.h" | |
| 49 | |
| 50 #include "WebContextMenuData.h" | |
| 51 #include "WebDataSourceImpl.h" | |
| 52 #include "WebFrameImpl.h" | |
| 53 #include "WebPoint.h" | |
| 54 #include "WebString.h" | |
| 55 #include "WebURL.h" | |
| 56 #include "WebURLResponse.h" | |
| 57 #include "WebViewClient.h" | |
| 58 #include "WebViewImpl.h" | |
| 59 | |
| 60 using namespace WebCore; | |
| 61 | |
| 62 namespace WebKit { | |
| 63 | |
| 64 // Figure out the URL of a page or subframe. Returns |page_type| as the type, | |
| 65 // which indicates page or subframe, or ContextNodeType::NONE if the URL could not | |
| 66 // be determined for some reason. | |
| 67 static WebURL urlFromFrame(Frame* frame) | |
| 68 { | |
| 69 if (frame) { | |
| 70 DocumentLoader* dl = frame->loader()->documentLoader(); | |
| 71 if (dl) { | |
| 72 WebDataSource* ds = WebDataSourceImpl::fromDocumentLoader(dl); | |
| 73 if (ds) | |
| 74 return ds->hasUnreachableURL() ? ds->unreachableURL() : ds->request().url(); | |
| 75 } | |
| 76 } | |
| 77 return WebURL(); | |
| 78 } | |
| 79 | |
| 80 // Helper function to determine whether text is a single word or a sentence. | |
| 81 static bool isASingleWord(const String& text) | |
| 82 { | |
| 83 TextBreakIterator* it = characterBreakIterator(text.characters(), text.length()); | |
| 84 return it && textBreakNext(it) == TextBreakDone; | |
| 85 } | |
| 86 | |
| 87 // Helper function to get misspelled word on which context menu | |
| 88 // is to be evolked. This function also sets the word on which context menu | |
| 89 // has been evoked to be the selected word, as required. This function changes | |
| 90 // the selection only when there were no selected characters. | |
| 91 static String selectMisspelledWord(const ContextMenu* defaultMenu, Frame* selectedFrame) | |
| 92 { | |
| 93 // First select from selectedText to check for multiple word selection. | |
| 94 String misspelledWord = selectedFrame->selectedText().stripWhiteSpace(); | |
| 95 | |
| 96 // If some texts were already selected, we don't change the selection. | |
| 97 if (!misspelledWord.isEmpty()) { | |
| 98 // Don't provide suggestions for multiple words. | |
| 99 if (!isASingleWord(misspelledWord)) | |
| 100 return String(); | |
| 101 return misspelledWord; | |
| 102 } | |
| 103 | |
| 104 // Selection is empty, so change the selection to the word under the cursor. | |
| 105 HitTestResult hitTestResult = selectedFrame->eventHandler()-> | |
| 106 hitTestResultAtPoint(defaultMenu->hitTestResult().point(), true); | |
| 107 Node* innerNode = hitTestResult.innerNode(); | |
| 108 VisiblePosition pos(innerNode->renderer()->positionForPoint( | |
| 109 hitTestResult.localPoint())); | |
| 110 | |
| 111 VisibleSelection selection; | |
| 112 if (pos.isNotNull()) { | |
| 113 selection = VisibleSelection(pos); | |
| 114 selection.expandUsingGranularity(WordGranularity); | |
| 115 } | |
| 116 | |
| 117 if (selection.isRange()) | |
| 118 selectedFrame->setSelectionGranularity(WordGranularity); | |
| 119 | |
| 120 if (selectedFrame->shouldChangeSelection(selection)) | |
| 121 selectedFrame->selection()->setSelection(selection); | |
| 122 | |
| 123 misspelledWord = selectedFrame->selectedText().stripWhiteSpace(); | |
| 124 | |
| 125 // If misspelled word is still empty, then that portion should not be | |
| 126 // selected. Set the selection to that position only, and do not expand. | |
| 127 if (misspelledWord.isEmpty()) { | |
| 128 selection = VisibleSelection(pos); | |
| 129 selectedFrame->selection()->setSelection(selection); | |
| 130 } | |
| 131 | |
| 132 return misspelledWord; | |
| 133 } | |
| 134 | |
| 135 PlatformMenuDescription ContextMenuClientImpl::getCustomMenuFromDefaultItems( | |
| 136 ContextMenu* defaultMenu) | |
| 137 { | |
| 138 // Displaying the context menu in this function is a big hack as we don't | |
| 139 // have context, i.e. whether this is being invoked via a script or in | |
| 140 // response to user input (Mouse event WM_RBUTTONDOWN, | |
| 141 // Keyboard events KeyVK_APPS, Shift+F10). Check if this is being invoked | |
| 142 // in response to the above input events before popping up the context menu. | |
| 143 if (!m_webView->contextMenuAllowed()) | |
| 144 return 0; | |
| 145 | |
| 146 HitTestResult r = defaultMenu->hitTestResult(); | |
| 147 Frame* selectedFrame = r.innerNonSharedNode()->document()->frame(); | |
| 148 | |
| 149 WebContextMenuData data; | |
| 150 data.mousePosition = selectedFrame->view()->contentsToWindow(r.point()); | |
| 151 | |
| 152 // Links, Images, Media tags, and Image/Media-Links take preference over | |
| 153 // all else. | |
| 154 data.linkURL = r.absoluteLinkURL(); | |
| 155 | |
| 156 data.mediaType = WebContextMenuData::MediaTypeNone; | |
| 157 data.mediaFlags = WebContextMenuData::MediaNone; | |
| 158 | |
| 159 if (!r.absoluteImageURL().isEmpty()) { | |
| 160 data.srcURL = r.absoluteImageURL(); | |
| 161 data.mediaType = WebContextMenuData::MediaTypeImage; | |
| 162 } else if (!r.absoluteMediaURL().isEmpty()) { | |
| 163 data.srcURL = r.absoluteMediaURL(); | |
| 164 | |
| 165 // We know that if absoluteMediaURL() is not empty, then this | |
| 166 // is a media element. | |
| 167 HTMLMediaElement* mediaElement = | |
| 168 static_cast<HTMLMediaElement*>(r.innerNonSharedNode()); | |
| 169 if (mediaElement->hasTagName(HTMLNames::videoTag)) | |
| 170 data.mediaType = WebContextMenuData::MediaTypeVideo; | |
| 171 else if (mediaElement->hasTagName(HTMLNames::audioTag)) | |
| 172 data.mediaType = WebContextMenuData::MediaTypeAudio; | |
| 173 | |
| 174 if (mediaElement->error()) | |
| 175 data.mediaFlags |= WebContextMenuData::MediaInError; | |
| 176 if (mediaElement->paused()) | |
| 177 data.mediaFlags |= WebContextMenuData::MediaPaused; | |
| 178 if (mediaElement->muted()) | |
| 179 data.mediaFlags |= WebContextMenuData::MediaMuted; | |
| 180 if (mediaElement->loop()) | |
| 181 data.mediaFlags |= WebContextMenuData::MediaLoop; | |
| 182 if (mediaElement->supportsSave()) | |
| 183 data.mediaFlags |= WebContextMenuData::MediaCanSave; | |
| 184 if (mediaElement->hasAudio()) | |
| 185 data.mediaFlags |= WebContextMenuData::MediaHasAudio; | |
| 186 } | |
| 187 // If it's not a link, an image, a media element, or an image/media link, | |
| 188 // show a selection menu or a more generic page menu. | |
| 189 data.frameEncoding = selectedFrame->loader()->encoding(); | |
| 190 | |
| 191 // Send the frame and page URLs in any case. | |
| 192 data.pageURL = urlFromFrame(m_webView->mainFrameImpl()->frame()); | |
| 193 if (selectedFrame != m_webView->mainFrameImpl()->frame()) | |
| 194 data.frameURL = urlFromFrame(selectedFrame); | |
| 195 | |
| 196 if (r.isSelected()) | |
| 197 data.selectedText = selectedFrame->selectedText().stripWhiteSpace(); | |
| 198 | |
| 199 data.isEditable = false; | |
| 200 if (r.isContentEditable()) { | |
| 201 data.isEditable = true; | |
| 202 if (m_webView->focusedWebCoreFrame()->editor()->isContinuousSpellCheckingEnabled()) { | |
| 203 data.isSpellCheckingEnabled = true; | |
| 204 data.misspelledWord = selectMisspelledWord(defaultMenu, selectedFrame); | |
| 205 } | |
| 206 } | |
| 207 | |
| 208 // Now retrieve the security info. | |
| 209 DocumentLoader* dl = selectedFrame->loader()->documentLoader(); | |
| 210 WebDataSource* ds = WebDataSourceImpl::fromDocumentLoader(dl); | |
| 211 if (ds) | |
| 212 data.securityInfo = ds->response().securityInfo(); | |
| 213 | |
| 214 // Compute edit flags. | |
| 215 data.editFlags = WebContextMenuData::CanDoNone; | |
| 216 if (m_webView->focusedWebCoreFrame()->editor()->canUndo()) | |
| 217 data.editFlags |= WebContextMenuData::CanUndo; | |
| 218 if (m_webView->focusedWebCoreFrame()->editor()->canRedo()) | |
| 219 data.editFlags |= WebContextMenuData::CanRedo; | |
| 220 if (m_webView->focusedWebCoreFrame()->editor()->canCut()) | |
| 221 data.editFlags |= WebContextMenuData::CanCut; | |
| 222 if (m_webView->focusedWebCoreFrame()->editor()->canCopy()) | |
| 223 data.editFlags |= WebContextMenuData::CanCopy; | |
| 224 if (m_webView->focusedWebCoreFrame()->editor()->canPaste()) | |
| 225 data.editFlags |= WebContextMenuData::CanPaste; | |
| 226 if (m_webView->focusedWebCoreFrame()->editor()->canDelete()) | |
| 227 data.editFlags |= WebContextMenuData::CanDelete; | |
| 228 // We can always select all... | |
| 229 data.editFlags |= WebContextMenuData::CanSelectAll; | |
| 230 | |
| 231 WebFrame* selected_web_frame = WebFrameImpl::fromFrame(selectedFrame); | |
| 232 if (m_webView->client()) | |
| 233 m_webView->client()->showContextMenu(selected_web_frame, data); | |
| 234 | |
| 235 return 0; | |
| 236 } | |
| 237 | |
| 238 } // namespace WebKit | |
| OLD | NEW |