1 /*
2 * Copyright (C) 2006, 2007 Apple 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
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "ContextMenuController.h"
28
29 #include "Chrome.h"
30 #include "ContextMenu.h"
31 #include "ContextMenuClient.h"
32 #include "Document.h"
33 #include "DocumentFragment.h"
34 #include "DocumentLoader.h"
35 #include "Editor.h"
36 #include "EditorClient.h"
37 #include "Event.h"
38 #include "EventHandler.h"
39 #include "EventNames.h"
40 #include "FormState.h"
41 #include "Frame.h"
42 #include "FrameLoadRequest.h"
43 #include "FrameLoader.h"
44 #include "HTMLFormElement.h"
45 #include "HitTestRequest.h"
46 #include "HitTestResult.h"
47 #include "InspectorController.h"
48 #include "MouseEvent.h"
49 #include "Node.h"
50 #include "Page.h"
51 #include "RenderLayer.h"
52 #include "RenderObject.h"
53 #include "ReplaceSelectionCommand.h"
54 #include "ResourceRequest.h"
55 #include "SelectionController.h"
56 #include "Settings.h"
57 #include "TextIterator.h"
58 #include "WindowFeatures.h"
59 #include "markup.h"
60
61 namespace WebCore {
62
ContextMenuController(Page * page,ContextMenuClient * client)63 ContextMenuController::ContextMenuController(Page* page, ContextMenuClient* client)
64 : m_page(page)
65 , m_client(client)
66 , m_contextMenu(0)
67 {
68 ASSERT_ARG(page, page);
69 ASSERT_ARG(client, client);
70 }
71
~ContextMenuController()72 ContextMenuController::~ContextMenuController()
73 {
74 m_client->contextMenuDestroyed();
75 }
76
clearContextMenu()77 void ContextMenuController::clearContextMenu()
78 {
79 m_contextMenu.set(0);
80 }
81
handleContextMenuEvent(Event * event)82 void ContextMenuController::handleContextMenuEvent(Event* event)
83 {
84 ASSERT(event->type() == eventNames().contextmenuEvent);
85 if (!event->isMouseEvent())
86 return;
87 MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
88 HitTestResult result(mouseEvent->absoluteLocation());
89
90 if (Frame* frame = event->target()->toNode()->document()->frame())
91 result = frame->eventHandler()->hitTestResultAtPoint(mouseEvent->absoluteLocation(), false);
92
93 if (!result.innerNonSharedNode())
94 return;
95
96 m_contextMenu.set(new ContextMenu(result));
97 m_contextMenu->populate();
98 if (m_page->inspectorController()->enabled())
99 m_contextMenu->addInspectElementItem();
100
101 PlatformMenuDescription customMenu = m_client->getCustomMenuFromDefaultItems(m_contextMenu.get());
102 m_contextMenu->setPlatformDescription(customMenu);
103
104 event->setDefaultHandled();
105 }
106
openNewWindow(const KURL & urlToLoad,Frame * frame)107 static void openNewWindow(const KURL& urlToLoad, Frame* frame)
108 {
109 if (Page* oldPage = frame->page()) {
110 WindowFeatures features;
111 if (Page* newPage = oldPage->chrome()->createWindow(frame, FrameLoadRequest(ResourceRequest(urlToLoad, frame->loader()->outgoingReferrer())), features))
112 newPage->chrome()->show();
113 }
114 }
115
contextMenuItemSelected(ContextMenuItem * item)116 void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item)
117 {
118 ASSERT(item->type() == ActionType || item->type() == CheckableActionType);
119
120 if (item->action() >= ContextMenuItemBaseApplicationTag) {
121 m_client->contextMenuItemSelected(item, m_contextMenu.get());
122 return;
123 }
124
125 HitTestResult result = m_contextMenu->hitTestResult();
126 Frame* frame = result.innerNonSharedNode()->document()->frame();
127 if (!frame)
128 return;
129
130 switch (item->action()) {
131 case ContextMenuItemTagOpenLinkInNewWindow:
132 openNewWindow(result.absoluteLinkURL(), frame);
133 break;
134 case ContextMenuItemTagDownloadLinkToDisk:
135 // FIXME: Some day we should be able to do this from within WebCore.
136 m_client->downloadURL(result.absoluteLinkURL());
137 break;
138 case ContextMenuItemTagCopyLinkToClipboard:
139 frame->editor()->copyURL(result.absoluteLinkURL(), result.textContent());
140 break;
141 case ContextMenuItemTagOpenImageInNewWindow:
142 openNewWindow(result.absoluteImageURL(), frame);
143 break;
144 case ContextMenuItemTagDownloadImageToDisk:
145 // FIXME: Some day we should be able to do this from within WebCore.
146 m_client->downloadURL(result.absoluteImageURL());
147 break;
148 case ContextMenuItemTagCopyImageToClipboard:
149 // FIXME: The Pasteboard class is not written yet
150 // For now, call into the client. This is temporary!
151 frame->editor()->copyImage(result);
152 break;
153 case ContextMenuItemTagOpenFrameInNewWindow: {
154 DocumentLoader* loader = frame->loader()->documentLoader();
155 if (!loader->unreachableURL().isEmpty())
156 openNewWindow(loader->unreachableURL(), frame);
157 else
158 openNewWindow(loader->url(), frame);
159 break;
160 }
161 case ContextMenuItemTagCopy:
162 frame->editor()->copy();
163 break;
164 case ContextMenuItemTagGoBack:
165 frame->loader()->goBackOrForward(-1);
166 break;
167 case ContextMenuItemTagGoForward:
168 frame->loader()->goBackOrForward(1);
169 break;
170 case ContextMenuItemTagStop:
171 frame->loader()->stop();
172 break;
173 case ContextMenuItemTagReload:
174 frame->loader()->reload();
175 break;
176 case ContextMenuItemTagCut:
177 frame->editor()->cut();
178 break;
179 case ContextMenuItemTagPaste:
180 frame->editor()->paste();
181 break;
182 #if PLATFORM(GTK)
183 case ContextMenuItemTagDelete:
184 frame->editor()->performDelete();
185 break;
186 case ContextMenuItemTagSelectAll:
187 frame->editor()->command("SelectAll").execute();
188 break;
189 #endif
190 case ContextMenuItemTagSpellingGuess:
191 ASSERT(frame->selectedText().length());
192 if (frame->editor()->shouldInsertText(item->title(), frame->selection()->toNormalizedRange().get(), EditorInsertActionPasted)) {
193 Document* document = frame->document();
194 RefPtr<ReplaceSelectionCommand> command = ReplaceSelectionCommand::create(document, createFragmentFromMarkup(document, item->title(), ""), true, false, true);
195 applyCommand(command);
196 frame->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
197 }
198 break;
199 case ContextMenuItemTagIgnoreSpelling:
200 frame->editor()->ignoreSpelling();
201 break;
202 case ContextMenuItemTagLearnSpelling:
203 frame->editor()->learnSpelling();
204 break;
205 case ContextMenuItemTagSearchWeb:
206 m_client->searchWithGoogle(frame);
207 break;
208 case ContextMenuItemTagLookUpInDictionary:
209 // FIXME: Some day we may be able to do this from within WebCore.
210 m_client->lookUpInDictionary(frame);
211 break;
212 case ContextMenuItemTagOpenLink:
213 if (Frame* targetFrame = result.targetFrame())
214 targetFrame->loader()->loadFrameRequest(FrameLoadRequest(ResourceRequest(result.absoluteLinkURL(), frame->loader()->outgoingReferrer())), false, false, 0, 0);
215 else
216 openNewWindow(result.absoluteLinkURL(), frame);
217 break;
218 case ContextMenuItemTagBold:
219 frame->editor()->command("ToggleBold").execute();
220 break;
221 case ContextMenuItemTagItalic:
222 frame->editor()->command("ToggleItalic").execute();
223 break;
224 case ContextMenuItemTagUnderline:
225 frame->editor()->toggleUnderline();
226 break;
227 case ContextMenuItemTagOutline:
228 // We actually never enable this because CSS does not have a way to specify an outline font,
229 // which may make this difficult to implement. Maybe a special case of text-shadow?
230 break;
231 case ContextMenuItemTagStartSpeaking: {
232 ExceptionCode ec;
233 RefPtr<Range> selectedRange = frame->selection()->toNormalizedRange();
234 if (!selectedRange || selectedRange->collapsed(ec)) {
235 Document* document = result.innerNonSharedNode()->document();
236 selectedRange = document->createRange();
237 selectedRange->selectNode(document->documentElement(), ec);
238 }
239 m_client->speak(plainText(selectedRange.get()));
240 break;
241 }
242 case ContextMenuItemTagStopSpeaking:
243 m_client->stopSpeaking();
244 break;
245 case ContextMenuItemTagDefaultDirection:
246 frame->editor()->setBaseWritingDirection(NaturalWritingDirection);
247 break;
248 case ContextMenuItemTagLeftToRight:
249 frame->editor()->setBaseWritingDirection(LeftToRightWritingDirection);
250 break;
251 case ContextMenuItemTagRightToLeft:
252 frame->editor()->setBaseWritingDirection(RightToLeftWritingDirection);
253 break;
254 case ContextMenuItemTagTextDirectionDefault:
255 frame->editor()->command("MakeTextWritingDirectionNatural").execute();
256 break;
257 case ContextMenuItemTagTextDirectionLeftToRight:
258 frame->editor()->command("MakeTextWritingDirectionLeftToRight").execute();
259 break;
260 case ContextMenuItemTagTextDirectionRightToLeft:
261 frame->editor()->command("MakeTextWritingDirectionRightToLeft").execute();
262 break;
263 #if PLATFORM(MAC)
264 case ContextMenuItemTagSearchInSpotlight:
265 m_client->searchWithSpotlight();
266 break;
267 #endif
268 case ContextMenuItemTagShowSpellingPanel:
269 frame->editor()->showSpellingGuessPanel();
270 break;
271 case ContextMenuItemTagCheckSpelling:
272 frame->editor()->advanceToNextMisspelling();
273 break;
274 case ContextMenuItemTagCheckSpellingWhileTyping:
275 frame->editor()->toggleContinuousSpellChecking();
276 break;
277 #ifndef BUILDING_ON_TIGER
278 case ContextMenuItemTagCheckGrammarWithSpelling:
279 frame->editor()->toggleGrammarChecking();
280 break;
281 #endif
282 #if PLATFORM(MAC)
283 case ContextMenuItemTagShowFonts:
284 frame->editor()->showFontPanel();
285 break;
286 case ContextMenuItemTagStyles:
287 frame->editor()->showStylesPanel();
288 break;
289 case ContextMenuItemTagShowColors:
290 frame->editor()->showColorPanel();
291 break;
292 #endif
293 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
294 case ContextMenuItemTagMakeUpperCase:
295 frame->editor()->uppercaseWord();
296 break;
297 case ContextMenuItemTagMakeLowerCase:
298 frame->editor()->lowercaseWord();
299 break;
300 case ContextMenuItemTagCapitalize:
301 frame->editor()->capitalizeWord();
302 break;
303 case ContextMenuItemTagShowSubstitutions:
304 frame->editor()->showSubstitutionsPanel();
305 break;
306 case ContextMenuItemTagSmartCopyPaste:
307 frame->editor()->toggleSmartInsertDelete();
308 break;
309 case ContextMenuItemTagSmartQuotes:
310 frame->editor()->toggleAutomaticQuoteSubstitution();
311 break;
312 case ContextMenuItemTagSmartDashes:
313 frame->editor()->toggleAutomaticDashSubstitution();
314 break;
315 case ContextMenuItemTagSmartLinks:
316 frame->editor()->toggleAutomaticLinkDetection();
317 break;
318 case ContextMenuItemTagTextReplacement:
319 frame->editor()->toggleAutomaticTextReplacement();
320 break;
321 case ContextMenuItemTagCorrectSpellingAutomatically:
322 frame->editor()->toggleAutomaticSpellingCorrection();
323 break;
324 case ContextMenuItemTagChangeBack:
325 frame->editor()->changeBackToReplacedString(result.replacedString());
326 break;
327 #endif
328 case ContextMenuItemTagInspectElement:
329 if (Page* page = frame->page())
330 page->inspectorController()->inspect(result.innerNonSharedNode());
331 break;
332 default:
333 break;
334 }
335 }
336
337 } // namespace WebCore
338