blob: 1db2c1833bcfaca8462e35f606f50175b6a4f511 [file] [log] [blame]
initial.commitf5b16fe2008-07-27 00:20:511// Copyright 2008, Google Inc.
2// 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#include "config.h"
31
32#pragma warning(push, 0)
33#include "ContextMenu.h"
34#include "Document.h"
35#include "DocumentLoader.h"
36#include "Editor.h"
37#include "FrameLoader.h"
38#include "FrameView.h"
39#include "HitTestResult.h"
40#include "KURL.h"
41#include "Widget.h"
42#pragma warning(pop)
43#undef LOG
44
45#include "webkit/glue/context_menu_client_impl.h"
46
47#include "base/string_util.h"
48#include "webkit/glue/context_node_types.h"
49#include "webkit/glue/glue_util.h"
50#include "webkit/glue/webdocumentloader_impl.h"
51#include "webkit/glue/webview_impl.h"
52
53#include "base/word_iterator.h"
54
55// Helper function to determine whether text is a single word or a sentence.
56static bool IsASingleWord(const std::wstring& text) {
57 WordIterator iter(text, WordIterator::BREAK_WORD);
58 int word_count = 0;
59 if (!iter.Init()) return false;
60 while (iter.Advance()) {
61 if (iter.IsWord()) {
62 word_count++;
63 if (word_count > 1) // More than one word.
64 return false;
65 }
66 }
67
68 // Check for 0 words.
69 if (!word_count)
70 return false;
71
72 // Has a single word.
73 return true;
74}
75
76// Helper function to get misspelled word on which context menu
77// is to be evolked. This function also sets the word on which context menu
78// has been evoked to be the selected word, as required.
79static std::wstring GetMisspelledWord(WebCore::ContextMenu* default_menu,
80 WebCore::Frame* selected_frame) {
81 std::wstring misspelled_word_string;
82
83 // First select from selectedText to check for multiple word selection.
84 misspelled_word_string = CollapseWhitespace(
85 webkit_glue::StringToStdWString(selected_frame->selectedText()),
86 false);
87
88 // Don't provide suggestions for multiple words.
89 if (!misspelled_word_string.empty() &&
90 !IsASingleWord(misspelled_word_string))
91 return L"";
92
93 // Expand around the click to see if we clicked a word.
94 WebCore::Selection selection;
95 WebCore::VisiblePosition pos(default_menu->hitTestResult().innerNode()->
96 renderer()->positionForPoint(default_menu->hitTestResult().
97 localPoint()));
98
99 if (pos.isNotNull()) {
100 selection = WebCore::Selection(pos);
101 selection.expandUsingGranularity(WebCore::WordGranularity);
102 }
103
104 if (selection.isRange()) {
105 selected_frame->setSelectionGranularity(WebCore::WordGranularity);
106 }
107
108 if (selected_frame->shouldChangeSelection(selection))
109 selected_frame->selectionController()->setSelection(selection);
110
111 misspelled_word_string = CollapseWhitespace(
112 webkit_glue::StringToStdWString(selected_frame->selectedText()),
113 false);
114
115 return misspelled_word_string;
116}
117
118ContextMenuClientImpl::~ContextMenuClientImpl() {
119}
120
121void ContextMenuClientImpl::contextMenuDestroyed() {
122 delete this;
123}
124
125// Figure out the URL of a page or subframe. Returns |page_type| as the type,
126// which indicates page or subframe, or ContextNode::NONE if the URL could not
127// be determined for some reason.
128static ContextNode::Type GetTypeAndURLFromFrame(WebCore::Frame* frame,
129 GURL* url,
130 ContextNode::Type page_type) {
131 ContextNode::Type type = ContextNode::NONE;
132 if (frame) {
133 WebCore::DocumentLoader* dl = frame->loader()->documentLoader();
134 if (dl) {
135 WebDataSource* ds = static_cast<WebDocumentLoaderImpl*>(dl)->
136 GetDataSource();
137 if (ds) {
138 type = page_type;
139 *url = ds->HasUnreachableURL() ? ds->GetUnreachableURL()
140 : ds->GetRequest().GetURL();
141 }
142 }
143 }
144 return type;
145}
146
147WebCore::PlatformMenuDescription
148 ContextMenuClientImpl::getCustomMenuFromDefaultItems(
149 WebCore::ContextMenu* default_menu) {
150 // Displaying the context menu in this function is a big hack as we don't
151 // have context, i.e. whether this is being invoked via a script or in
152 // response to user input (Mouse event WM_RBUTTONDOWN,
153 // Keyboard events KeyVK_APPS, Shift+F10). Check if this is being invoked
154 // in response to the above input events before popping up the context menu.
155 if (!webview_->context_menu_allowed())
156 return NULL;
157
158 WebCore::HitTestResult r = default_menu->hitTestResult();
159 WebCore::Frame* selected_frame = r.innerNonSharedNode()->document()->frame();
160
161 WebCore::IntPoint menu_point =
162 selected_frame->view()->contentsToWindow(r.point());
163
164 ContextNode::Type type = ContextNode::NONE;
165
166 // Links, Images and Image-Links take preference over all else.
167 WebCore::KURL link_url = r.absoluteLinkURL();
168 std::wstring link_url_string;
169 if (!link_url.isEmpty()) {
170 type = ContextNode::LINK;
171 }
172 WebCore::KURL image_url = r.absoluteImageURL();
173 std::wstring image_url_string;
174 if (!image_url.isEmpty()) {
175 type = ContextNode::IMAGE;
176 }
177 if (!image_url.isEmpty() && !link_url.isEmpty())
178 type = ContextNode::IMAGE_LINK;
179
180 // If it's not a link, an image or an image link, show a selection menu or a
181 // more generic page menu.
182 std::wstring selection_text_string;
183 std::wstring misspelled_word_string;
184 GURL frame_url;
185 GURL page_url;
186
[email protected]1a3ebe12008-08-21 02:58:09187 std::string frame_encoding;
initial.commitf5b16fe2008-07-27 00:20:51188 // Send the frame and page URLs in any case.
189 ContextNode::Type frame_type = ContextNode::NONE;
190 ContextNode::Type page_type =
191 GetTypeAndURLFromFrame(webview_->main_frame()->frame(),
192 &page_url,
193 ContextNode::PAGE);
194 if (selected_frame != webview_->main_frame()->frame()) {
195 frame_type = GetTypeAndURLFromFrame(selected_frame,
196 &frame_url,
197 ContextNode::FRAME);
[email protected]1a3ebe12008-08-21 02:58:09198 frame_encoding = WideToUTF8(
199 webkit_glue::StringToStdWString(selected_frame->loader()->encoding()));
initial.commitf5b16fe2008-07-27 00:20:51200 }
201
202 if (type == ContextNode::NONE) {
203 if (r.isContentEditable()) {
204 type = ContextNode::EDITABLE;
205 if (webview_->FocusedFrameNeedsSpellchecking()) {
206 misspelled_word_string = GetMisspelledWord(default_menu,
207 selected_frame);
208 }
209 } else if (r.isSelected()) {
210 type = ContextNode::SELECTION;
211 selection_text_string =
212 CollapseWhitespace(
213 webkit_glue::StringToStdWString(selected_frame->selectedText()),
214 false);
215 } else if (selected_frame != webview_->main_frame()->frame()) {
216 type = frame_type;
217 } else {
218 type = page_type;
219 }
220 }
221
222 int edit_flags = ContextNode::CAN_DO_NONE;
223 if (webview_->GetFocusedWebCoreFrame()->editor()->canUndo())
224 edit_flags |= ContextNode::CAN_UNDO;
225 if (webview_->GetFocusedWebCoreFrame()->editor()->canRedo())
226 edit_flags |= ContextNode::CAN_REDO;
227 if (webview_->GetFocusedWebCoreFrame()->editor()->canCut())
228 edit_flags |= ContextNode::CAN_CUT;
229 if (webview_->GetFocusedWebCoreFrame()->editor()->canCopy())
230 edit_flags |= ContextNode::CAN_COPY;
231 if (webview_->GetFocusedWebCoreFrame()->editor()->canPaste())
232 edit_flags |= ContextNode::CAN_PASTE;
233 if (webview_->GetFocusedWebCoreFrame()->editor()->canDelete())
234 edit_flags |= ContextNode::CAN_DELETE;
235 // We can always select all...
236 edit_flags |= ContextNode::CAN_SELECT_ALL;
237
238 WebViewDelegate* d = webview_->delegate();
239 if (d) {
240 d->ShowContextMenu(webview_,
241 type,
242 menu_point.x(),
243 menu_point.y(),
244 webkit_glue::KURLToGURL(link_url),
245 webkit_glue::KURLToGURL(image_url),
246 page_url,
247 frame_url,
248 selection_text_string,
249 misspelled_word_string,
[email protected]72c94f32008-08-18 21:24:29250 edit_flags,
251 frame_encoding);
initial.commitf5b16fe2008-07-27 00:20:51252 }
253 return NULL;
254}
255
256void ContextMenuClientImpl::contextMenuItemSelected(
257 WebCore::ContextMenuItem*, const WebCore::ContextMenu*) {
258}
259
260void ContextMenuClientImpl::downloadURL(const WebCore::KURL&) {
261}
262
263void ContextMenuClientImpl::copyImageToClipboard(const WebCore::HitTestResult&) {
264}
265
266void ContextMenuClientImpl::searchWithGoogle(const WebCore::Frame*) {
267}
268
269void ContextMenuClientImpl::lookUpInDictionary(WebCore::Frame*) {
270}
271
272void ContextMenuClientImpl::speak(const WebCore::String&) {
273}
274
275void ContextMenuClientImpl::stopSpeaking() {
276}
277
278bool ContextMenuClientImpl::shouldIncludeInspectElementItem() {
279 return false; // TODO(jackson): Eventually include the inspector context menu item
280}