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