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