blob: 26bb5585b02cc6d1fb76bf83369451c1b7fea46f [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commitf5b16fe2008-07-27 00:20:514
5#include "config.h"
6
[email protected]fa419692008-10-16 21:46:147#include "base/compiler_specific.h"
8
9MSVC_PUSH_WARNING_LEVEL(0);
initial.commitf5b16fe2008-07-27 00:20:5110#include "ContextMenu.h"
11#include "Document.h"
12#include "DocumentLoader.h"
13#include "Editor.h"
[email protected]985f0e62008-10-29 01:05:0814#include "EventHandler.h"
initial.commitf5b16fe2008-07-27 00:20:5115#include "FrameLoader.h"
16#include "FrameView.h"
17#include "HitTestResult.h"
18#include "KURL.h"
19#include "Widget.h"
[email protected]fa419692008-10-16 21:46:1420MSVC_POP_WARNING();
initial.commitf5b16fe2008-07-27 00:20:5121#undef LOG
22
23#include "webkit/glue/context_menu_client_impl.h"
24
25#include "base/string_util.h"
[email protected]726985e22009-06-18 21:09:2826#include "webkit/api/public/WebURL.h"
27#include "webkit/api/public/WebURLResponse.h"
[email protected]e09ba552009-02-05 03:26:2928#include "webkit/glue/context_menu.h"
initial.commitf5b16fe2008-07-27 00:20:5129#include "webkit/glue/glue_util.h"
[email protected]2903f3b2009-03-13 16:30:5030#include "webkit/glue/webdatasource_impl.h"
initial.commitf5b16fe2008-07-27 00:20:5131#include "webkit/glue/webview_impl.h"
32
33#include "base/word_iterator.h"
34
[email protected]726985e22009-06-18 21:09:2835using WebKit::WebDataSource;
36
[email protected]985f0e62008-10-29 01:05:0837namespace {
38
initial.commitf5b16fe2008-07-27 00:20:5139// Helper function to determine whether text is a single word or a sentence.
[email protected]985f0e62008-10-29 01:05:0840bool IsASingleWord(const std::wstring& text) {
initial.commitf5b16fe2008-07-27 00:20:5141 WordIterator iter(text, WordIterator::BREAK_WORD);
42 int word_count = 0;
43 if (!iter.Init()) return false;
44 while (iter.Advance()) {
45 if (iter.IsWord()) {
46 word_count++;
47 if (word_count > 1) // More than one word.
48 return false;
49 }
50 }
[email protected]f0a51fb52009-03-05 12:46:3851
initial.commitf5b16fe2008-07-27 00:20:5152 // Check for 0 words.
53 if (!word_count)
54 return false;
[email protected]f0a51fb52009-03-05 12:46:3855
initial.commitf5b16fe2008-07-27 00:20:5156 // Has a single word.
57 return true;
58}
59
[email protected]f0a51fb52009-03-05 12:46:3860// Helper function to get misspelled word on which context menu
initial.commitf5b16fe2008-07-27 00:20:5161// is to be evolked. This function also sets the word on which context menu
[email protected]442a3a12009-04-23 18:14:0662// has been evoked to be the selected word, as required. This function changes
63// the selection only when there were no selected characters.
[email protected]985f0e62008-10-29 01:05:0864std::wstring GetMisspelledWord(const WebCore::ContextMenu* default_menu,
65 WebCore::Frame* selected_frame) {
initial.commitf5b16fe2008-07-27 00:20:5166 std::wstring misspelled_word_string;
67
68 // First select from selectedText to check for multiple word selection.
69 misspelled_word_string = CollapseWhitespace(
70 webkit_glue::StringToStdWString(selected_frame->selectedText()),
71 false);
72
[email protected]442a3a12009-04-23 18:14:0673 // If some texts were already selected, we don't change the selection.
74 if (!misspelled_word_string.empty()) {
75 // Don't provide suggestions for multiple words.
76 if (!IsASingleWord(misspelled_word_string))
77 return L"";
78 else
79 return misspelled_word_string;
80 }
initial.commitf5b16fe2008-07-27 00:20:5181
[email protected]3a500b352008-10-31 17:10:2182 WebCore::HitTestResult hit_test_result = selected_frame->eventHandler()->
83 hitTestResultAtPoint(default_menu->hitTestResult().point(), true);
84 WebCore::Node* inner_node = hit_test_result.innerNode();
85 WebCore::VisiblePosition pos(inner_node->renderer()->positionForPoint(
86 hit_test_result.localPoint()));
[email protected]985f0e62008-10-29 01:05:0887
[email protected]0f0981d2009-02-12 23:09:3588 WebCore::VisibleSelection selection;
initial.commitf5b16fe2008-07-27 00:20:5189 if (pos.isNotNull()) {
[email protected]0f0981d2009-02-12 23:09:3590 selection = WebCore::VisibleSelection(pos);
initial.commitf5b16fe2008-07-27 00:20:5191 selection.expandUsingGranularity(WebCore::WordGranularity);
92 }
[email protected]f0a51fb52009-03-05 12:46:3893
94 if (selection.isRange()) {
initial.commitf5b16fe2008-07-27 00:20:5195 selected_frame->setSelectionGranularity(WebCore::WordGranularity);
96 }
[email protected]f0a51fb52009-03-05 12:46:3897
initial.commitf5b16fe2008-07-27 00:20:5198 if (selected_frame->shouldChangeSelection(selection))
[email protected]de56f3782008-10-01 22:31:3599 selected_frame->selection()->setSelection(selection);
[email protected]f0a51fb52009-03-05 12:46:38100
initial.commitf5b16fe2008-07-27 00:20:51101 misspelled_word_string = CollapseWhitespace(
[email protected]f0a51fb52009-03-05 12:46:38102 webkit_glue::StringToStdWString(selected_frame->selectedText()),
initial.commitf5b16fe2008-07-27 00:20:51103 false);
[email protected]6d886b72009-02-11 23:30:11104
105 // If misspelled word is empty, then that portion should not be selected.
106 // Set the selection to that position only, and do not expand.
107 if (misspelled_word_string.empty()) {
[email protected]0f0981d2009-02-12 23:09:35108 selection = WebCore::VisibleSelection(pos);
[email protected]6d886b72009-02-11 23:30:11109 selected_frame->selection()->setSelection(selection);
110 }
111
initial.commitf5b16fe2008-07-27 00:20:51112 return misspelled_word_string;
113}
114
[email protected]985f0e62008-10-29 01:05:08115} // namespace
116
initial.commitf5b16fe2008-07-27 00:20:51117ContextMenuClientImpl::~ContextMenuClientImpl() {
118}
119
120void ContextMenuClientImpl::contextMenuDestroyed() {
121 delete this;
122}
123
124// Figure out the URL of a page or subframe. Returns |page_type| as the type,
125// which indicates page or subframe, or ContextNode::NONE if the URL could not
126// be determined for some reason.
[email protected]124646932009-01-28 18:39:02127static ContextNode GetTypeAndURLFromFrame(WebCore::Frame* frame,
128 GURL* url,
129 ContextNode page_node) {
130 ContextNode node;
initial.commitf5b16fe2008-07-27 00:20:51131 if (frame) {
132 WebCore::DocumentLoader* dl = frame->loader()->documentLoader();
133 if (dl) {
[email protected]2903f3b2009-03-13 16:30:50134 WebDataSource* ds = WebDataSourceImpl::FromLoader(dl);
initial.commitf5b16fe2008-07-27 00:20:51135 if (ds) {
[email protected]124646932009-01-28 18:39:02136 node = page_node;
[email protected]726985e22009-06-18 21:09:28137 *url = ds->hasUnreachableURL() ? ds->unreachableURL()
138 : ds->request().url();
initial.commitf5b16fe2008-07-27 00:20:51139 }
140 }
141 }
[email protected]124646932009-01-28 18:39:02142 return node;
initial.commitf5b16fe2008-07-27 00:20:51143}
144
145WebCore::PlatformMenuDescription
146 ContextMenuClientImpl::getCustomMenuFromDefaultItems(
147 WebCore::ContextMenu* default_menu) {
148 // Displaying the context menu in this function is a big hack as we don't
[email protected]f0a51fb52009-03-05 12:46:38149 // have context, i.e. whether this is being invoked via a script or in
initial.commitf5b16fe2008-07-27 00:20:51150 // response to user input (Mouse event WM_RBUTTONDOWN,
151 // Keyboard events KeyVK_APPS, Shift+F10). Check if this is being invoked
152 // in response to the above input events before popping up the context menu.
153 if (!webview_->context_menu_allowed())
154 return NULL;
155
156 WebCore::HitTestResult r = default_menu->hitTestResult();
157 WebCore::Frame* selected_frame = r.innerNonSharedNode()->document()->frame();
158
159 WebCore::IntPoint menu_point =
160 selected_frame->view()->contentsToWindow(r.point());
161
[email protected]124646932009-01-28 18:39:02162 ContextNode node;
initial.commitf5b16fe2008-07-27 00:20:51163
164 // Links, Images and Image-Links take preference over all else.
165 WebCore::KURL link_url = r.absoluteLinkURL();
initial.commitf5b16fe2008-07-27 00:20:51166 if (!link_url.isEmpty()) {
[email protected]124646932009-01-28 18:39:02167 node.type |= ContextNode::LINK;
initial.commitf5b16fe2008-07-27 00:20:51168 }
169 WebCore::KURL image_url = r.absoluteImageURL();
initial.commitf5b16fe2008-07-27 00:20:51170 if (!image_url.isEmpty()) {
[email protected]124646932009-01-28 18:39:02171 node.type |= ContextNode::IMAGE;
initial.commitf5b16fe2008-07-27 00:20:51172 }
initial.commitf5b16fe2008-07-27 00:20:51173
174 // If it's not a link, an image or an image link, show a selection menu or a
175 // more generic page menu.
176 std::wstring selection_text_string;
177 std::wstring misspelled_word_string;
178 GURL frame_url;
179 GURL page_url;
[email protected]6aa376b2008-09-23 18:49:52180 std::string security_info;
[email protected]f0a51fb52009-03-05 12:46:38181
[email protected]c9825a42009-05-01 22:51:50182 std::string frame_charset = WideToASCII(
183 webkit_glue::StringToStdWString(selected_frame->loader()->encoding()));
initial.commitf5b16fe2008-07-27 00:20:51184 // Send the frame and page URLs in any case.
[email protected]124646932009-01-28 18:39:02185 ContextNode frame_node = ContextNode(ContextNode::NONE);
186 ContextNode page_node =
initial.commitf5b16fe2008-07-27 00:20:51187 GetTypeAndURLFromFrame(webview_->main_frame()->frame(),
188 &page_url,
[email protected]124646932009-01-28 18:39:02189 ContextNode(ContextNode::PAGE));
initial.commitf5b16fe2008-07-27 00:20:51190 if (selected_frame != webview_->main_frame()->frame()) {
[email protected]124646932009-01-28 18:39:02191 frame_node = GetTypeAndURLFromFrame(selected_frame,
initial.commitf5b16fe2008-07-27 00:20:51192 &frame_url,
[email protected]124646932009-01-28 18:39:02193 ContextNode(ContextNode::FRAME));
initial.commitf5b16fe2008-07-27 00:20:51194 }
[email protected]124646932009-01-28 18:39:02195
196 if (r.isSelected()) {
197 node.type |= ContextNode::SELECTION;
198 selection_text_string = CollapseWhitespace(
199 webkit_glue::StringToStdWString(selected_frame->selectedText()),
200 false);
201 }
202
203 if (r.isContentEditable()) {
204 node.type |= ContextNode::EDITABLE;
205 if (webview_->GetFocusedWebCoreFrame()->editor()->
206 isContinuousSpellCheckingEnabled()) {
207 misspelled_word_string = GetMisspelledWord(default_menu,
208 selected_frame);
209 }
210 }
[email protected]f0a51fb52009-03-05 12:46:38211
[email protected]124646932009-01-28 18:39:02212 if (node.type == ContextNode::NONE) {
213 if (selected_frame != webview_->main_frame()->frame()) {
214 node = frame_node;
initial.commitf5b16fe2008-07-27 00:20:51215 } else {
[email protected]124646932009-01-28 18:39:02216 node = page_node;
initial.commitf5b16fe2008-07-27 00:20:51217 }
218 }
219
[email protected]6aa376b2008-09-23 18:49:52220 // Now retrieve the security info.
221 WebCore::DocumentLoader* dl = selected_frame->loader()->documentLoader();
[email protected]2903f3b2009-03-13 16:30:50222 WebDataSource* ds = WebDataSourceImpl::FromLoader(dl);
[email protected]726985e22009-06-18 21:09:28223 if (ds)
224 security_info = ds->response().securityInfo();
[email protected]6aa376b2008-09-23 18:49:52225
initial.commitf5b16fe2008-07-27 00:20:51226 int edit_flags = ContextNode::CAN_DO_NONE;
227 if (webview_->GetFocusedWebCoreFrame()->editor()->canUndo())
228 edit_flags |= ContextNode::CAN_UNDO;
229 if (webview_->GetFocusedWebCoreFrame()->editor()->canRedo())
230 edit_flags |= ContextNode::CAN_REDO;
231 if (webview_->GetFocusedWebCoreFrame()->editor()->canCut())
232 edit_flags |= ContextNode::CAN_CUT;
233 if (webview_->GetFocusedWebCoreFrame()->editor()->canCopy())
234 edit_flags |= ContextNode::CAN_COPY;
235 if (webview_->GetFocusedWebCoreFrame()->editor()->canPaste())
236 edit_flags |= ContextNode::CAN_PASTE;
237 if (webview_->GetFocusedWebCoreFrame()->editor()->canDelete())
238 edit_flags |= ContextNode::CAN_DELETE;
239 // We can always select all...
240 edit_flags |= ContextNode::CAN_SELECT_ALL;
241
242 WebViewDelegate* d = webview_->delegate();
243 if (d) {
244 d->ShowContextMenu(webview_,
[email protected]124646932009-01-28 18:39:02245 node,
initial.commitf5b16fe2008-07-27 00:20:51246 menu_point.x(),
247 menu_point.y(),
248 webkit_glue::KURLToGURL(link_url),
249 webkit_glue::KURLToGURL(image_url),
250 page_url,
251 frame_url,
252 selection_text_string,
253 misspelled_word_string,
[email protected]6aa376b2008-09-23 18:49:52254 edit_flags,
[email protected]c9825a42009-05-01 22:51:50255 security_info,
256 frame_charset);
initial.commitf5b16fe2008-07-27 00:20:51257 }
258 return NULL;
259}
260
261void ContextMenuClientImpl::contextMenuItemSelected(
262 WebCore::ContextMenuItem*, const WebCore::ContextMenu*) {
263}
264
265void ContextMenuClientImpl::downloadURL(const WebCore::KURL&) {
266}
267
268void ContextMenuClientImpl::copyImageToClipboard(const WebCore::HitTestResult&) {
269}
270
271void ContextMenuClientImpl::searchWithGoogle(const WebCore::Frame*) {
272}
273
274void ContextMenuClientImpl::lookUpInDictionary(WebCore::Frame*) {
275}
276
277void ContextMenuClientImpl::speak(const WebCore::String&) {
278}
279
280void ContextMenuClientImpl::stopSpeaking() {
281}
282
283bool ContextMenuClientImpl::shouldIncludeInspectElementItem() {
284 return false; // TODO(jackson): Eventually include the inspector context menu item
285}
license.botbf09a502008-08-24 00:55:55286
[email protected]7284c6502008-09-09 20:27:26287#if defined(OS_MACOSX)
288void ContextMenuClientImpl::searchWithSpotlight() {
289 // TODO(pinkerton): write this
290}
291#endif