ed56de6a3e34e850ebda6b53fc0308c48ba60654
[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 using namespace EventNames;
62
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
72 ContextMenuController::~ContextMenuController()
73 {
74     m_client->contextMenuDestroyed();
75 }
76
77 void ContextMenuController::clearContextMenu()
78 {
79     m_contextMenu.set(0);
80 }
81
82 void ContextMenuController::handleContextMenuEvent(Event* event)
83 {
84     ASSERT(event->type() == contextmenuEvent);
85     if (!event->isMouseEvent())
86         return;
87     MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
88     IntPoint point = IntPoint(mouseEvent->pageX(), mouseEvent->pageY());
89     HitTestResult result(point);
90
91     if (Frame* frame = event->target()->toNode()->document()->frame())
92         result = frame->eventHandler()->hitTestResultAtPoint(point, false);
93     
94     if (!result.innerNonSharedNode())
95         return;
96
97     m_contextMenu.set(new ContextMenu(result));
98     m_contextMenu->populate();
99     if (m_page->inspectorController()->enabled())
100         m_contextMenu->addInspectElementItem();
101
102     PlatformMenuDescription customMenu = m_client->getCustomMenuFromDefaultItems(m_contextMenu.get());
103     m_contextMenu->setPlatformDescription(customMenu);
104
105     event->setDefaultHandled();
106 }
107
108 static void openNewWindow(const KURL& urlToLoad, Frame* frame)
109 {
110     if (Page* oldPage = frame->page()) {
111         WindowFeatures features;
112         if (Page* newPage = oldPage->chrome()->createWindow(frame,
113                 FrameLoadRequest(ResourceRequest(urlToLoad, frame->loader()->outgoingReferrer())), features))
114             newPage->chrome()->show();
115     }
116 }
117
118 void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item)
119 {
120     ASSERT(item->type() == ActionType || item->type() == CheckableActionType);
121
122     if (item->action() >= ContextMenuItemBaseApplicationTag) {
123         m_client->contextMenuItemSelected(item, m_contextMenu.get());
124         return;
125     }
126
127     HitTestResult result = m_contextMenu->hitTestResult();
128     Frame* frame = result.innerNonSharedNode()->document()->frame();
129     if (!frame)
130         return;
131     
132     switch (item->action()) {
133         case ContextMenuItemTagOpenLinkInNewWindow: 
134             openNewWindow(result.absoluteLinkURL(), frame);
135             break;
136         case ContextMenuItemTagDownloadLinkToDisk:
137             // FIXME: Some day we should be able to do this from within WebCore.
138             m_client->downloadURL(result.absoluteLinkURL());
139             break;
140         case ContextMenuItemTagCopyLinkToClipboard:
141             frame->editor()->copyURL(result.absoluteLinkURL(), result.textContent());
142             break;
143         case ContextMenuItemTagOpenImageInNewWindow:
144             openNewWindow(result.absoluteImageURL(), frame);
145             break;
146         case ContextMenuItemTagDownloadImageToDisk:
147             // FIXME: Some day we should be able to do this from within WebCore.
148             m_client->downloadURL(result.absoluteImageURL());
149             break;
150         case ContextMenuItemTagCopyImageToClipboard:
151             // FIXME: The Pasteboard class is not written yet
152             // For now, call into the client. This is temporary!
153             frame->editor()->copyImage(result);
154             break;
155         case ContextMenuItemTagOpenFrameInNewWindow: {
156             DocumentLoader* loader = frame->loader()->documentLoader();
157             if (!loader->unreachableURL().isEmpty())
158                 openNewWindow(loader->unreachableURL(), frame);
159             else
160                 openNewWindow(loader->url(), frame);
161             break;
162         }
163         case ContextMenuItemTagCopy:
164             frame->editor()->copy();
165             break;
166         case ContextMenuItemTagGoBack:
167             frame->loader()->goBackOrForward(-1);
168             break;
169         case ContextMenuItemTagGoForward:
170             frame->loader()->goBackOrForward(1);
171             break;
172         case ContextMenuItemTagStop:
173             frame->loader()->stop();
174             break;
175         case ContextMenuItemTagReload:
176             frame->loader()->reload();
177             break;
178         case ContextMenuItemTagCut:
179             frame->editor()->cut();
180             break;
181         case ContextMenuItemTagPaste:
182             frame->editor()->paste();
183             break;
184 #if PLATFORM(GTK)
185         case ContextMenuItemTagDelete:
186             frame->editor()->performDelete();
187             break;
188         case ContextMenuItemTagSelectAll:
189             frame->editor()->command("SelectAll").execute();
190             break;
191 #endif
192         case ContextMenuItemTagSpellingGuess:
193             ASSERT(frame->selectedText().length());
194             if (frame->editor()->shouldInsertText(item->title(), frame->selection()->toRange().get(),
195                 EditorInsertActionPasted)) {
196                 Document* document = frame->document();
197                 RefPtr<ReplaceSelectionCommand> command =
198                     ReplaceSelectionCommand::create(document, createFragmentFromMarkup(document, item->title(), ""),
199                                                                                    true, false, true);
200                 applyCommand(command);
201                 frame->revealSelection(RenderLayer::gAlignToEdgeIfNeeded);
202             }
203             break;
204         case ContextMenuItemTagIgnoreSpelling:
205             frame->editor()->ignoreSpelling();
206             break;
207         case ContextMenuItemTagLearnSpelling:
208             frame->editor()->learnSpelling();
209             break;
210         case ContextMenuItemTagSearchWeb:
211             m_client->searchWithGoogle(frame);
212             break;
213         case ContextMenuItemTagLookUpInDictionary:
214             // FIXME: Some day we may be able to do this from within WebCore.
215             m_client->lookUpInDictionary(frame);
216             break;
217         case ContextMenuItemTagOpenLink:
218             if (Frame* targetFrame = result.targetFrame())
219                 targetFrame->loader()->load(FrameLoadRequest(ResourceRequest(result.absoluteLinkURL(), 
220                     frame->loader()->outgoingReferrer())), false, true, 0, 0, HashMap<String, String>());
221             else
222                 openNewWindow(result.absoluteLinkURL(), frame);
223             break;
224         case ContextMenuItemTagBold:
225             frame->editor()->command("ToggleBold").execute();
226             break;
227         case ContextMenuItemTagItalic:
228             frame->editor()->command("ToggleItalic").execute();
229             break;
230         case ContextMenuItemTagUnderline:
231             frame->editor()->toggleUnderline();
232             break;
233         case ContextMenuItemTagOutline:
234             // We actually never enable this because CSS does not have a way to specify an outline font,
235             // which may make this difficult to implement. Maybe a special case of text-shadow?
236             break;
237         case ContextMenuItemTagStartSpeaking: {
238             ExceptionCode ec;
239             RefPtr<Range> selectedRange = frame->selection()->toRange();
240             if (!selectedRange || selectedRange->collapsed(ec)) {
241                 Document* document = result.innerNonSharedNode()->document();
242                 selectedRange = document->createRange();
243                 selectedRange->selectNode(document->documentElement(), ec);
244             }
245             m_client->speak(plainText(selectedRange.get()));
246             break;
247         }
248         case ContextMenuItemTagStopSpeaking:
249             m_client->stopSpeaking();
250             break;
251         case ContextMenuItemTagDefaultDirection:
252             frame->setSelectionBaseWritingDirection(NaturalWritingDirection);
253             break;
254         case ContextMenuItemTagLeftToRight:
255             frame->setSelectionBaseWritingDirection(LeftToRightWritingDirection);
256             break;
257         case ContextMenuItemTagRightToLeft:
258             frame->setSelectionBaseWritingDirection(RightToLeftWritingDirection);
259             break;
260 #if PLATFORM(MAC)
261         case ContextMenuItemTagSearchInSpotlight:
262             m_client->searchWithSpotlight();
263             break;
264 #endif
265         case ContextMenuItemTagShowSpellingPanel:
266             frame->editor()->showSpellingGuessPanel();
267             break;
268         case ContextMenuItemTagCheckSpelling:
269             frame->editor()->advanceToNextMisspelling();
270             break;
271         case ContextMenuItemTagCheckSpellingWhileTyping:
272             frame->editor()->toggleContinuousSpellChecking();
273             break;
274 #ifndef BUILDING_ON_TIGER
275         case ContextMenuItemTagCheckGrammarWithSpelling:
276             frame->editor()->toggleGrammarChecking();
277             break;
278 #endif
279 #if PLATFORM(MAC)
280         case ContextMenuItemTagShowFonts:
281             frame->editor()->showFontPanel();
282             break;
283         case ContextMenuItemTagStyles:
284             frame->editor()->showStylesPanel();
285             break;
286         case ContextMenuItemTagShowColors:
287             frame->editor()->showColorPanel();
288             break;
289 #endif
290         case ContextMenuItemTagInspectElement:
291             if (Page* page = frame->page())
292                 page->inspectorController()->inspect(result.innerNonSharedNode());
293             break;
294         default:
295             break;
296     }
297 }
298
299 } // namespace WebCore