Roll out r27641 since the same bug was already fixed by r27568.
[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         if (Page* newPage = oldPage->chrome()->createWindow(frame,
114                 FrameLoadRequest(ResourceRequest(urlToLoad, frame->loader()->outgoingReferrer())), features))
115             newPage->chrome()->show();
116     }
117 }
118
119 void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item)
120 {
121     ASSERT(item->type() == ActionType);
122
123     if (item->action() >= ContextMenuItemBaseApplicationTag) {
124         m_client->contextMenuItemSelected(item, m_contextMenu.get());
125         return;
126     }
127
128     HitTestResult result = m_contextMenu->hitTestResult();
129     Frame* frame = result.innerNonSharedNode()->document()->frame();
130     if (!frame)
131         return;
132     
133     switch (item->action()) {
134         case ContextMenuItemTagOpenLinkInNewWindow: 
135             openNewWindow(result.absoluteLinkURL(), frame);
136             break;
137         case ContextMenuItemTagDownloadLinkToDisk:
138             // FIXME: Some day we should be able to do this from within WebCore.
139             m_client->downloadURL(result.absoluteLinkURL());
140             break;
141         case ContextMenuItemTagCopyLinkToClipboard:
142             frame->editor()->copyURL(result.absoluteLinkURL(), result.textContent());
143             break;
144         case ContextMenuItemTagOpenImageInNewWindow:
145             openNewWindow(result.absoluteImageURL(), frame);
146             break;
147         case ContextMenuItemTagDownloadImageToDisk:
148             // FIXME: Some day we should be able to do this from within WebCore.
149             m_client->downloadURL(result.absoluteImageURL());
150             break;
151         case ContextMenuItemTagCopyImageToClipboard:
152             // FIXME: The Pasteboard class is not written yet
153             // For now, call into the client. This is temporary!
154             frame->editor()->copyImage(result);
155             break;
156         case ContextMenuItemTagOpenFrameInNewWindow: {
157             KURL url = frame->loader()->documentLoader()->unreachableURL();
158             if (frame && url.isEmpty())
159                 url = frame->loader()->documentLoader()->URL();
160             openNewWindow(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         case ContextMenuItemTagSpellingGuess:
185             ASSERT(frame->selectedText().length());
186             if (frame->editor()->shouldInsertText(item->title(), frame->selectionController()->toRange().get(),
187                 EditorInsertActionPasted)) {
188                 Document* document = frame->document();
189                 RefPtr<ReplaceSelectionCommand> command =
190                     new ReplaceSelectionCommand(document, createFragmentFromMarkup(document, item->title(), ""),
191                                                                                    true, false, true);
192                 applyCommand(command);
193                 frame->revealSelection(RenderLayer::gAlignToEdgeIfNeeded);
194             }
195             break;
196         case ContextMenuItemTagIgnoreSpelling:
197             frame->editor()->ignoreSpelling();
198             break;
199         case ContextMenuItemTagLearnSpelling:
200             frame->editor()->learnSpelling();
201             break;
202         case ContextMenuItemTagSearchWeb:
203             m_client->searchWithGoogle(frame);
204             break;
205         case ContextMenuItemTagLookUpInDictionary:
206             // FIXME: Some day we may be able to do this from within WebCore.
207             m_client->lookUpInDictionary(frame);
208             break;
209         case ContextMenuItemTagOpenLink:
210             if (Frame* targetFrame = result.targetFrame())
211                 targetFrame->loader()->load(FrameLoadRequest(ResourceRequest(result.absoluteLinkURL(), 
212                     frame->loader()->outgoingReferrer())), false, true, 0, 0, HashMap<String, String>());
213             else
214                 openNewWindow(result.absoluteLinkURL(), frame);
215             break;
216         case ContextMenuItemTagBold:
217             frame->editor()->execCommand("ToggleBold");
218             break;
219         case ContextMenuItemTagItalic:
220             frame->editor()->execCommand("ToggleItalic");
221             break;
222         case ContextMenuItemTagUnderline:
223             frame->editor()->toggleUnderline();
224             break;
225         case ContextMenuItemTagOutline:
226             // We actually never enable this because CSS does not have a way to specify an outline font,
227             // which may make this difficult to implement. Maybe a special case of text-shadow?
228             break;
229         case ContextMenuItemTagStartSpeaking: {
230             ExceptionCode ec;
231             RefPtr<Range> selectedRange = frame->selectionController()->toRange();
232             if (!selectedRange || selectedRange->collapsed(ec)) {
233                 Document* document = result.innerNonSharedNode()->document();
234                 selectedRange = document->createRange();
235                 selectedRange->selectNode(document->documentElement(), ec);
236             }
237             m_client->speak(plainText(selectedRange.get()));
238             break;
239         }
240         case ContextMenuItemTagStopSpeaking:
241             m_client->stopSpeaking();
242             break;
243         case ContextMenuItemTagDefaultDirection:
244             frame->editor()->setBaseWritingDirection("inherit");
245             break;
246         case ContextMenuItemTagLeftToRight:
247             frame->editor()->setBaseWritingDirection("ltr");
248             break;
249         case ContextMenuItemTagRightToLeft:
250             frame->editor()->setBaseWritingDirection("rtl");
251             break;
252 #if PLATFORM(MAC)
253         case ContextMenuItemTagSearchInSpotlight:
254             m_client->searchWithSpotlight();
255             break;
256 #endif
257         case ContextMenuItemTagShowSpellingPanel:
258             frame->editor()->showSpellingGuessPanel();
259             break;
260         case ContextMenuItemTagCheckSpelling:
261             frame->editor()->advanceToNextMisspelling();
262             break;
263         case ContextMenuItemTagCheckSpellingWhileTyping:
264             frame->editor()->toggleContinuousSpellChecking();
265             break;
266 #ifndef BUILDING_ON_TIGER
267         case ContextMenuItemTagCheckGrammarWithSpelling:
268             frame->editor()->toggleGrammarChecking();
269             break;
270 #endif
271 #if PLATFORM(MAC)
272         case ContextMenuItemTagShowFonts:
273             frame->editor()->showFontPanel();
274             break;
275         case ContextMenuItemTagStyles:
276             frame->editor()->showStylesPanel();
277             break;
278         case ContextMenuItemTagShowColors:
279             frame->editor()->showColorPanel();
280             break;
281 #endif
282         case ContextMenuItemTagInspectElement:
283             if (Page* page = frame->page())
284                 page->inspectorController()->inspect(result.innerNonSharedNode());
285             break;
286         default:
287             break;
288     }
289 }
290
291 } // namespace WebCore