• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #if ENABLE(CONTEXT_MENUS)
30 
31 #include "Chrome.h"
32 #include "ContextMenu.h"
33 #include "ContextMenuClient.h"
34 #include "ContextMenuProvider.h"
35 #include "Document.h"
36 #include "DocumentFragment.h"
37 #include "DocumentLoader.h"
38 #include "Editor.h"
39 #include "EditorClient.h"
40 #include "Event.h"
41 #include "EventHandler.h"
42 #include "EventNames.h"
43 #include "FormState.h"
44 #include "Frame.h"
45 #include "FrameLoadRequest.h"
46 #include "FrameLoader.h"
47 #include "HTMLFormElement.h"
48 #include "HitTestRequest.h"
49 #include "HitTestResult.h"
50 #include "InspectorController.h"
51 #include "MouseEvent.h"
52 #include "Node.h"
53 #include "Page.h"
54 #include "RenderLayer.h"
55 #include "RenderObject.h"
56 #include "ReplaceSelectionCommand.h"
57 #include "ResourceRequest.h"
58 #include "SelectionController.h"
59 #include "Settings.h"
60 #include "TextIterator.h"
61 #include "WindowFeatures.h"
62 #include "markup.h"
63 
64 namespace WebCore {
65 
ContextMenuController(Page * page,ContextMenuClient * client)66 ContextMenuController::ContextMenuController(Page* page, ContextMenuClient* client)
67     : m_page(page)
68     , m_client(client)
69     , m_contextMenu(0)
70 {
71     ASSERT_ARG(page, page);
72     ASSERT_ARG(client, client);
73 }
74 
~ContextMenuController()75 ContextMenuController::~ContextMenuController()
76 {
77     m_client->contextMenuDestroyed();
78 }
79 
clearContextMenu()80 void ContextMenuController::clearContextMenu()
81 {
82     m_contextMenu.set(0);
83     if (m_menuProvider)
84         m_menuProvider->contextMenuCleared();
85     m_menuProvider = 0;
86 }
87 
handleContextMenuEvent(Event * event)88 void ContextMenuController::handleContextMenuEvent(Event* event)
89 {
90     m_contextMenu.set(createContextMenu(event));
91     if (!m_contextMenu)
92         return;
93     m_contextMenu->populate();
94     showContextMenu(event);
95 }
96 
showContextMenu(Event * event,PassRefPtr<ContextMenuProvider> menuProvider)97 void ContextMenuController::showContextMenu(Event* event, PassRefPtr<ContextMenuProvider> menuProvider)
98 {
99     m_menuProvider = menuProvider;
100 
101     m_contextMenu.set(createContextMenu(event));
102     if (!m_contextMenu) {
103         clearContextMenu();
104         return;
105     }
106 
107     m_menuProvider->populateContextMenu(m_contextMenu.get());
108     showContextMenu(event);
109 }
110 
createContextMenu(Event * event)111 ContextMenu* ContextMenuController::createContextMenu(Event* event)
112 {
113    if (!event->isMouseEvent())
114         return 0;
115     MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
116     HitTestResult result(mouseEvent->absoluteLocation());
117 
118     if (Frame* frame = event->target()->toNode()->document()->frame())
119         result = frame->eventHandler()->hitTestResultAtPoint(mouseEvent->absoluteLocation(), false);
120 
121     if (!result.innerNonSharedNode())
122         return 0;
123     return new ContextMenu(result);
124 }
125 
showContextMenu(Event * event)126 void ContextMenuController::showContextMenu(Event* event)
127 {
128 #if ENABLE(INSPECTOR)
129     if (m_page->inspectorController()->enabled())
130         m_contextMenu->addInspectElementItem();
131 #endif
132     PlatformMenuDescription customMenu = m_client->getCustomMenuFromDefaultItems(m_contextMenu.get());
133     m_contextMenu->setPlatformDescription(customMenu);
134     event->setDefaultHandled();
135 }
136 
openNewWindow(const KURL & urlToLoad,Frame * frame)137 static void openNewWindow(const KURL& urlToLoad, Frame* frame)
138 {
139     if (Page* oldPage = frame->page()) {
140         WindowFeatures features;
141         if (Page* newPage = oldPage->chrome()->createWindow(frame, FrameLoadRequest(ResourceRequest(urlToLoad, frame->loader()->outgoingReferrer())), features))
142             newPage->chrome()->show();
143     }
144 }
145 
contextMenuItemSelected(ContextMenuItem * item)146 void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item)
147 {
148     ASSERT(item->type() == ActionType || item->type() == CheckableActionType);
149 
150     if (item->action() >= ContextMenuItemBaseApplicationTag) {
151         m_client->contextMenuItemSelected(item, m_contextMenu.get());
152         return;
153     }
154 
155     if (item->action() >= ContextMenuItemBaseCustomTag) {
156         ASSERT(m_menuProvider);
157         m_menuProvider->contextMenuItemSelected(item);
158         return;
159     }
160 
161     HitTestResult result = m_contextMenu->hitTestResult();
162     Frame* frame = result.innerNonSharedNode()->document()->frame();
163     if (!frame)
164         return;
165 
166     switch (item->action()) {
167     case ContextMenuItemTagOpenLinkInNewWindow:
168         openNewWindow(result.absoluteLinkURL(), frame);
169         break;
170     case ContextMenuItemTagDownloadLinkToDisk:
171         // FIXME: Some day we should be able to do this from within WebCore.
172         m_client->downloadURL(result.absoluteLinkURL());
173         break;
174     case ContextMenuItemTagCopyLinkToClipboard:
175         frame->editor()->copyURL(result.absoluteLinkURL(), result.textContent());
176         break;
177     case ContextMenuItemTagOpenImageInNewWindow:
178         openNewWindow(result.absoluteImageURL(), frame);
179         break;
180     case ContextMenuItemTagDownloadImageToDisk:
181         // FIXME: Some day we should be able to do this from within WebCore.
182         m_client->downloadURL(result.absoluteImageURL());
183         break;
184     case ContextMenuItemTagCopyImageToClipboard:
185         // FIXME: The Pasteboard class is not written yet
186         // For now, call into the client. This is temporary!
187         frame->editor()->copyImage(result);
188         break;
189     case ContextMenuItemTagOpenFrameInNewWindow: {
190         DocumentLoader* loader = frame->loader()->documentLoader();
191         if (!loader->unreachableURL().isEmpty())
192             openNewWindow(loader->unreachableURL(), frame);
193         else
194             openNewWindow(loader->url(), frame);
195         break;
196     }
197     case ContextMenuItemTagCopy:
198         frame->editor()->copy();
199         break;
200     case ContextMenuItemTagGoBack:
201         if (Page* page = frame->page())
202             page->goBackOrForward(-1);
203         break;
204     case ContextMenuItemTagGoForward:
205         if (Page* page = frame->page())
206             page->goBackOrForward(1);
207         break;
208     case ContextMenuItemTagStop:
209         frame->loader()->stop();
210         break;
211     case ContextMenuItemTagReload:
212         frame->loader()->reload();
213         break;
214     case ContextMenuItemTagCut:
215         frame->editor()->cut();
216         break;
217     case ContextMenuItemTagPaste:
218         frame->editor()->paste();
219         break;
220 #if PLATFORM(GTK)
221     case ContextMenuItemTagDelete:
222         frame->editor()->performDelete();
223         break;
224     case ContextMenuItemTagSelectAll:
225         frame->editor()->command("SelectAll").execute();
226         break;
227 #endif
228     case ContextMenuItemTagSpellingGuess:
229         ASSERT(frame->selectedText().length());
230         if (frame->editor()->shouldInsertText(item->title(), frame->selection()->toNormalizedRange().get(), EditorInsertActionPasted)) {
231             Document* document = frame->document();
232             RefPtr<ReplaceSelectionCommand> command = ReplaceSelectionCommand::create(document, createFragmentFromMarkup(document, item->title(), ""), true, false, true);
233             applyCommand(command);
234             frame->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
235         }
236         break;
237     case ContextMenuItemTagIgnoreSpelling:
238         frame->editor()->ignoreSpelling();
239         break;
240     case ContextMenuItemTagLearnSpelling:
241         frame->editor()->learnSpelling();
242         break;
243     case ContextMenuItemTagSearchWeb:
244         m_client->searchWithGoogle(frame);
245         break;
246     case ContextMenuItemTagLookUpInDictionary:
247         // FIXME: Some day we may be able to do this from within WebCore.
248         m_client->lookUpInDictionary(frame);
249         break;
250     case ContextMenuItemTagOpenLink:
251         if (Frame* targetFrame = result.targetFrame())
252             targetFrame->loader()->loadFrameRequest(FrameLoadRequest(ResourceRequest(result.absoluteLinkURL(), frame->loader()->outgoingReferrer())), false, false, 0, 0, SendReferrer);
253         else
254             openNewWindow(result.absoluteLinkURL(), frame);
255         break;
256     case ContextMenuItemTagBold:
257         frame->editor()->command("ToggleBold").execute();
258         break;
259     case ContextMenuItemTagItalic:
260         frame->editor()->command("ToggleItalic").execute();
261         break;
262     case ContextMenuItemTagUnderline:
263         frame->editor()->toggleUnderline();
264         break;
265     case ContextMenuItemTagOutline:
266         // We actually never enable this because CSS does not have a way to specify an outline font,
267         // which may make this difficult to implement. Maybe a special case of text-shadow?
268         break;
269     case ContextMenuItemTagStartSpeaking: {
270         ExceptionCode ec;
271         RefPtr<Range> selectedRange = frame->selection()->toNormalizedRange();
272         if (!selectedRange || selectedRange->collapsed(ec)) {
273             Document* document = result.innerNonSharedNode()->document();
274             selectedRange = document->createRange();
275             selectedRange->selectNode(document->documentElement(), ec);
276         }
277         m_client->speak(plainText(selectedRange.get()));
278         break;
279     }
280     case ContextMenuItemTagStopSpeaking:
281         m_client->stopSpeaking();
282         break;
283     case ContextMenuItemTagDefaultDirection:
284         frame->editor()->setBaseWritingDirection(NaturalWritingDirection);
285         break;
286     case ContextMenuItemTagLeftToRight:
287         frame->editor()->setBaseWritingDirection(LeftToRightWritingDirection);
288         break;
289     case ContextMenuItemTagRightToLeft:
290         frame->editor()->setBaseWritingDirection(RightToLeftWritingDirection);
291         break;
292     case ContextMenuItemTagTextDirectionDefault:
293         frame->editor()->command("MakeTextWritingDirectionNatural").execute();
294         break;
295     case ContextMenuItemTagTextDirectionLeftToRight:
296         frame->editor()->command("MakeTextWritingDirectionLeftToRight").execute();
297         break;
298     case ContextMenuItemTagTextDirectionRightToLeft:
299         frame->editor()->command("MakeTextWritingDirectionRightToLeft").execute();
300         break;
301 #if PLATFORM(MAC)
302     case ContextMenuItemTagSearchInSpotlight:
303         m_client->searchWithSpotlight();
304         break;
305 #endif
306     case ContextMenuItemTagShowSpellingPanel:
307         frame->editor()->showSpellingGuessPanel();
308         break;
309     case ContextMenuItemTagCheckSpelling:
310         frame->editor()->advanceToNextMisspelling();
311         break;
312     case ContextMenuItemTagCheckSpellingWhileTyping:
313         frame->editor()->toggleContinuousSpellChecking();
314         break;
315 #ifndef BUILDING_ON_TIGER
316     case ContextMenuItemTagCheckGrammarWithSpelling:
317         frame->editor()->toggleGrammarChecking();
318         break;
319 #endif
320 #if PLATFORM(MAC)
321     case ContextMenuItemTagShowFonts:
322         frame->editor()->showFontPanel();
323         break;
324     case ContextMenuItemTagStyles:
325         frame->editor()->showStylesPanel();
326         break;
327     case ContextMenuItemTagShowColors:
328         frame->editor()->showColorPanel();
329         break;
330 #endif
331 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
332     case ContextMenuItemTagMakeUpperCase:
333         frame->editor()->uppercaseWord();
334         break;
335     case ContextMenuItemTagMakeLowerCase:
336         frame->editor()->lowercaseWord();
337         break;
338     case ContextMenuItemTagCapitalize:
339         frame->editor()->capitalizeWord();
340         break;
341     case ContextMenuItemTagShowSubstitutions:
342         frame->editor()->showSubstitutionsPanel();
343         break;
344     case ContextMenuItemTagSmartCopyPaste:
345         frame->editor()->toggleSmartInsertDelete();
346         break;
347     case ContextMenuItemTagSmartQuotes:
348         frame->editor()->toggleAutomaticQuoteSubstitution();
349         break;
350     case ContextMenuItemTagSmartDashes:
351         frame->editor()->toggleAutomaticDashSubstitution();
352         break;
353     case ContextMenuItemTagSmartLinks:
354         frame->editor()->toggleAutomaticLinkDetection();
355         break;
356     case ContextMenuItemTagTextReplacement:
357         frame->editor()->toggleAutomaticTextReplacement();
358         break;
359     case ContextMenuItemTagCorrectSpellingAutomatically:
360         frame->editor()->toggleAutomaticSpellingCorrection();
361         break;
362     case ContextMenuItemTagChangeBack:
363         frame->editor()->changeBackToReplacedString(result.replacedString());
364         break;
365 #endif
366 #if ENABLE(INSPECTOR)
367     case ContextMenuItemTagInspectElement:
368         if (Page* page = frame->page())
369             page->inspectorController()->inspect(result.innerNonSharedNode());
370         break;
371 #endif
372     default:
373         break;
374     }
375 }
376 
377 } // namespace WebCore
378 
379 #endif // ENABLE(CONTEXT_MENUS)
380