8d7e51ace09e0dd8a13b0de88dd2cca4a68bb7ac
[WebKit-https.git] / WebCore / page / ContextMenuController.cpp
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 "Frame.h"
41 #include "FrameLoader.h"
42 #include "FrameLoadRequest.h"
43 #include "HitTestRequest.h"
44 #include "HitTestResult.h"
45 #include "InspectorController.h"
46 #include "MouseEvent.h"
47 #include "Node.h"
48 #include "Page.h"
49 #include "RenderLayer.h"
50 #include "RenderObject.h"
51 #include "ReplaceSelectionCommand.h"
52 #include "ResourceRequest.h"
53 #include "SelectionController.h"
54 #include "Settings.h"
55 #include "TextIterator.h"
56 #include "WindowFeatures.h"
57 #include "markup.h"
58
59 namespace WebCore {
60
61 ContextMenuController::ContextMenuController(Page* page, ContextMenuClient* client)
62     : m_page(page)
63     , m_client(client)
64     , m_contextMenu(0)
65 {
66     ASSERT_ARG(page, page);
67     ASSERT_ARG(client, client);
68 }
69
70 ContextMenuController::~ContextMenuController()
71 {
72     m_client->contextMenuDestroyed();
73 }
74
75 void ContextMenuController::clearContextMenu()
76 {
77     m_contextMenu.set(0);
78 }
79
80 void ContextMenuController::handleContextMenuEvent(Event* event)
81 {
82     ASSERT(event->type() == eventNames().contextmenuEvent);
83     if (!event->isMouseEvent())
84         return;
85     MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
86     IntPoint point = IntPoint(mouseEvent->pageX(), mouseEvent->pageY());
87     HitTestResult result(point);
88
89     if (Frame* frame = event->target()->toNode()->document()->frame())
90         result = frame->eventHandler()->hitTestResultAtPoint(point, false);
91     
92     if (!result.innerNonSharedNode())
93         return;
94
95     m_contextMenu.set(new ContextMenu(result));
96     m_contextMenu->populate();
97     if (m_page->inspectorController()->enabled())
98         m_contextMenu->addInspectElementItem();
99
100     PlatformMenuDescription customMenu = m_client->getCustomMenuFromDefaultItems(m_contextMenu.get());
101     m_contextMenu->setPlatformDescription(customMenu);
102
103     event->setDefaultHandled();
104 }
105
106 static void openNewWindow(const KURL& urlToLoad, Frame* frame)
107 {
108     if (Page* oldPage = frame->page()) {
109         WindowFeatures features;
110         if (Page* newPage = oldPage->chrome()->createWindow(frame,
111                 FrameLoadRequest(ResourceRequest(urlToLoad, frame->loader()->outgoingReferrer())), features))
112             newPage->chrome()->show();
113     }
114 }
115
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()->toRange().get(),
193                 EditorInsertActionPasted)) {
194                 Document* document = frame->document();
195                 RefPtr<ReplaceSelectionCommand> command =
196                     ReplaceSelectionCommand::create(document, createFragmentFromMarkup(document, item->title(), ""),
197                                                                                    true, false, true);
198                 applyCommand(command);
199                 frame->revealSelection(RenderLayer::gAlignToEdgeIfNeeded);
200             }
201             break;
202         case ContextMenuItemTagIgnoreSpelling:
203             frame->editor()->ignoreSpelling();
204             break;
205         case ContextMenuItemTagLearnSpelling:
206             frame->editor()->learnSpelling();
207             break;
208         case ContextMenuItemTagSearchWeb:
209             m_client->searchWithGoogle(frame);
210             break;
211         case ContextMenuItemTagLookUpInDictionary:
212             // FIXME: Some day we may be able to do this from within WebCore.
213             m_client->lookUpInDictionary(frame);
214             break;
215         case ContextMenuItemTagOpenLink:
216             if (Frame* targetFrame = result.targetFrame())
217                 targetFrame->loader()->loadFrameRequestWithFormAndValues(FrameLoadRequest(ResourceRequest(result.absoluteLinkURL(), 
218                     frame->loader()->outgoingReferrer())), false, 0, 0, HashMap<String, String>());
219             else
220                 openNewWindow(result.absoluteLinkURL(), frame);
221             break;
222         case ContextMenuItemTagBold:
223             frame->editor()->command("ToggleBold").execute();
224             break;
225         case ContextMenuItemTagItalic:
226             frame->editor()->command("ToggleItalic").execute();
227             break;
228         case ContextMenuItemTagUnderline:
229             frame->editor()->toggleUnderline();
230             break;
231         case ContextMenuItemTagOutline:
232             // We actually never enable this because CSS does not have a way to specify an outline font,
233             // which may make this difficult to implement. Maybe a special case of text-shadow?
234             break;
235         case ContextMenuItemTagStartSpeaking: {
236             ExceptionCode ec;
237             RefPtr<Range> selectedRange = frame->selection()->toRange();
238             if (!selectedRange || selectedRange->collapsed(ec)) {
239                 Document* document = result.innerNonSharedNode()->document();
240                 selectedRange = document->createRange();
241                 selectedRange->selectNode(document->documentElement(), ec);
242             }
243             m_client->speak(plainText(selectedRange.get()));
244             break;
245         }
246         case ContextMenuItemTagStopSpeaking:
247             m_client->stopSpeaking();
248             break;
249         case ContextMenuItemTagDefaultDirection:
250             frame->editor()->setBaseWritingDirection(NaturalWritingDirection);
251             break;
252         case ContextMenuItemTagLeftToRight:
253             frame->editor()->setBaseWritingDirection(LeftToRightWritingDirection);
254             break;
255         case ContextMenuItemTagRightToLeft:
256             frame->editor()->setBaseWritingDirection(RightToLeftWritingDirection);
257             break;
258         case ContextMenuItemTagTextDirectionDefault:
259             frame->editor()->command("MakeTextWritingDirectionNatural").execute();
260             break;
261         case ContextMenuItemTagTextDirectionLeftToRight:
262             frame->editor()->command("MakeTextWritingDirectionLeftToRight").execute();
263             break;
264         case ContextMenuItemTagTextDirectionRightToLeft:
265             frame->editor()->command("MakeTextWritingDirectionRightToLeft").execute();
266             break;
267 #if PLATFORM(MAC)
268         case ContextMenuItemTagSearchInSpotlight:
269             m_client->searchWithSpotlight();
270             break;
271 #endif
272         case ContextMenuItemTagShowSpellingPanel:
273             frame->editor()->showSpellingGuessPanel();
274             break;
275         case ContextMenuItemTagCheckSpelling:
276             frame->editor()->advanceToNextMisspelling();
277             break;
278         case ContextMenuItemTagCheckSpellingWhileTyping:
279             frame->editor()->toggleContinuousSpellChecking();
280             break;
281 #ifndef BUILDING_ON_TIGER
282         case ContextMenuItemTagCheckGrammarWithSpelling:
283             frame->editor()->toggleGrammarChecking();
284             break;
285 #endif
286 #if PLATFORM(MAC)
287         case ContextMenuItemTagShowFonts:
288             frame->editor()->showFontPanel();
289             break;
290         case ContextMenuItemTagStyles:
291             frame->editor()->showStylesPanel();
292             break;
293         case ContextMenuItemTagShowColors:
294             frame->editor()->showColorPanel();
295             break;
296 #endif
297         case ContextMenuItemTagInspectElement:
298             if (Page* page = frame->page())
299                 page->inspectorController()->inspect(result.innerNonSharedNode());
300             break;
301         default:
302             break;
303     }
304 }
305
306 } // namespace WebCore