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 |