Expose the "Share" menu for links, images, and media.
[WebKit-https.git] / Source / WebCore / page / ContextMenuController.cpp
1 /*
2  * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
3  * Copyright (C) 2010 Igalia S.L
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28 #include "ContextMenuController.h"
29
30 #if ENABLE(CONTEXT_MENUS)
31
32 #include "BackForwardController.h"
33 #include "Chrome.h"
34 #include "ContextMenu.h"
35 #include "ContextMenuClient.h"
36 #include "ContextMenuItem.h"
37 #include "ContextMenuProvider.h"
38 #include "Document.h"
39 #include "DocumentFragment.h"
40 #include "DocumentLoader.h"
41 #include "Editor.h"
42 #include "EditorClient.h"
43 #include "Event.h"
44 #include "EventHandler.h"
45 #include "EventNames.h"
46 #include "ExceptionCodePlaceholder.h"
47 #include "FormState.h"
48 #include "FrameLoadRequest.h"
49 #include "FrameLoader.h"
50 #include "FrameLoaderClient.h"
51 #include "FrameSelection.h"
52 #include "HTMLFormControlElement.h"
53 #include "HTMLFormElement.h"
54 #include "HitTestRequest.h"
55 #include "HitTestResult.h"
56 #include "InspectorController.h"
57 #include "LocalizedStrings.h"
58 #include "MainFrame.h"
59 #include "MouseEvent.h"
60 #include "NavigationAction.h"
61 #include "Node.h"
62 #include "Page.h"
63 #include "PlatformEvent.h"
64 #include "RenderImage.h"
65 #include "ReplaceSelectionCommand.h"
66 #include "ResourceRequest.h"
67 #include "Settings.h"
68 #include "TextIterator.h"
69 #include "TypingCommand.h"
70 #include "UserTypingGestureIndicator.h"
71 #include "WindowFeatures.h"
72 #include "markup.h"
73 #include <wtf/unicode/CharacterNames.h>
74
75 using namespace WTF;
76 using namespace Unicode;
77
78 namespace WebCore {
79
80 ContextMenuController::ContextMenuController(Page& page, ContextMenuClient& client)
81     : m_page(page)
82     , m_client(client)
83 {
84 }
85
86 ContextMenuController::~ContextMenuController()
87 {
88     m_client.contextMenuDestroyed();
89 }
90
91 void ContextMenuController::clearContextMenu()
92 {
93     m_contextMenu = nullptr;
94     if (m_menuProvider)
95         m_menuProvider->contextMenuCleared();
96     m_menuProvider = 0;
97 }
98
99 void ContextMenuController::handleContextMenuEvent(Event* event)
100 {
101     m_contextMenu = maybeCreateContextMenu(event);
102     if (!m_contextMenu)
103         return;
104
105     populate();
106
107     showContextMenu(event);
108 }
109
110 static std::unique_ptr<ContextMenuItem> separatorItem()
111 {
112     return std::unique_ptr<ContextMenuItem>(new ContextMenuItem(SeparatorType, ContextMenuItemTagNoAction, String()));
113 }
114
115 void ContextMenuController::showContextMenu(Event* event, PassRefPtr<ContextMenuProvider> menuProvider)
116 {
117     m_menuProvider = menuProvider;
118
119     m_contextMenu = maybeCreateContextMenu(event);
120     if (!m_contextMenu) {
121         clearContextMenu();
122         return;
123     }
124
125     m_menuProvider->populateContextMenu(m_contextMenu.get());
126     if (m_context.hitTestResult().isSelected()) {
127         appendItem(*separatorItem(), m_contextMenu.get());
128         populate();
129     }
130     showContextMenu(event);
131 }
132
133 #if ENABLE(SERVICE_CONTROLS)
134 static Image* imageFromImageElementNode(Node& node)
135 {
136     RenderObject* renderer = node.renderer();
137     if (!is<RenderImage>(renderer))
138         return nullptr;
139     CachedImage* image = downcast<RenderImage>(*renderer).cachedImage();
140     if (!image || image->errorOccurred())
141         return nullptr;
142
143     return image->imageForRenderer(renderer);
144 }
145 #endif
146
147 std::unique_ptr<ContextMenu> ContextMenuController::maybeCreateContextMenu(Event* event)
148 {
149     ASSERT(event);
150     
151     if (!is<MouseEvent>(*event))
152         return nullptr;
153
154     MouseEvent& mouseEvent = downcast<MouseEvent>(*event);
155     HitTestResult result(mouseEvent.absoluteLocation());
156
157     Node* node = event->target()->toNode();
158     if (Frame* frame = node->document().frame())
159         result = frame->eventHandler().hitTestResultAtPoint(mouseEvent.absoluteLocation());
160     
161     if (!result.innerNonSharedNode())
162         return nullptr;
163
164     m_context = ContextMenuContext(result);
165
166 #if ENABLE(SERVICE_CONTROLS)
167     if (node->isImageControlsButtonElement()) {
168         if (Image* image = imageFromImageElementNode(*result.innerNonSharedNode()))
169             m_context.setControlledImage(image);
170
171         // FIXME: If we couldn't get the image then we shouldn't try to show the image controls menu for it.
172         return nullptr;
173     }
174 #endif
175
176     return std::unique_ptr<ContextMenu>(new ContextMenu);
177 }
178
179 void ContextMenuController::showContextMenu(Event* event)
180 {
181     if (m_page.inspectorController().enabled())
182         addInspectElementItem();
183
184 #if USE(CROSS_PLATFORM_CONTEXT_MENUS)
185     m_contextMenu = m_client.customizeMenu(WTF::move(m_contextMenu));
186 #else
187     PlatformMenuDescription customMenu = m_client.getCustomMenuFromDefaultItems(m_contextMenu.get());
188     m_contextMenu->setPlatformDescription(customMenu);
189 #endif
190     event->setDefaultHandled();
191 }
192
193 static void openNewWindow(const URL& urlToLoad, Frame* frame)
194 {
195     Page* oldPage = frame->page();
196     if (!oldPage)
197         return;
198     
199     FrameLoadRequest request(frame->document()->securityOrigin(), ResourceRequest(urlToLoad, frame->loader().outgoingReferrer()));
200
201     Page* newPage = oldPage->chrome().createWindow(frame, request, WindowFeatures(), NavigationAction(request.resourceRequest()));
202     if (!newPage)
203         return;
204     newPage->chrome().show();
205     newPage->mainFrame().loader().loadFrameRequest(request, LockHistory::No, LockBackForwardList::No, 0, 0, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Suppress);
206 }
207
208 #if PLATFORM(GTK)
209 static void insertUnicodeCharacter(UChar character, Frame* frame)
210 {
211     String text(&character, 1);
212     if (!frame->editor().shouldInsertText(text, frame->selection().toNormalizedRange().get(), EditorInsertActionTyped))
213         return;
214
215     ASSERT(frame->document());
216     TypingCommand::insertText(*frame->document(), text, 0, TypingCommand::TextCompositionNone);
217 }
218 #endif
219
220 void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item)
221 {
222     ASSERT(item->type() == ActionType || item->type() == CheckableActionType);
223
224     if (item->action() >= ContextMenuItemBaseApplicationTag) {
225         m_client.contextMenuItemSelected(item, m_contextMenu.get());
226         return;
227     }
228
229     if (item->action() >= ContextMenuItemBaseCustomTag) {
230         ASSERT(m_menuProvider);
231         m_menuProvider->contextMenuItemSelected(item);
232         return;
233     }
234
235     Frame* frame = m_context.hitTestResult().innerNonSharedNode()->document().frame();
236     if (!frame)
237         return;
238
239     switch (item->action()) {
240     case ContextMenuItemTagOpenLinkInNewWindow:
241         openNewWindow(m_context.hitTestResult().absoluteLinkURL(), frame);
242         break;
243     case ContextMenuItemTagDownloadLinkToDisk:
244         // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709)
245         m_client.downloadURL(m_context.hitTestResult().absoluteLinkURL());
246         break;
247     case ContextMenuItemTagCopyLinkToClipboard:
248         frame->editor().copyURL(m_context.hitTestResult().absoluteLinkURL(), m_context.hitTestResult().textContent());
249         break;
250     case ContextMenuItemTagOpenImageInNewWindow:
251         openNewWindow(m_context.hitTestResult().absoluteImageURL(), frame);
252         break;
253     case ContextMenuItemTagDownloadImageToDisk:
254         // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709)
255         m_client.downloadURL(m_context.hitTestResult().absoluteImageURL());
256         break;
257     case ContextMenuItemTagCopyImageToClipboard:
258         // FIXME: The Pasteboard class is not written yet
259         // For now, call into the client. This is temporary!
260         frame->editor().copyImage(m_context.hitTestResult());
261         break;
262 #if PLATFORM(GTK) || PLATFORM(EFL)
263     case ContextMenuItemTagCopyImageUrlToClipboard:
264         frame->editor().copyURL(m_context.hitTestResult().absoluteImageURL(), m_context.hitTestResult().textContent());
265         break;
266 #endif
267     case ContextMenuItemTagOpenMediaInNewWindow:
268         openNewWindow(m_context.hitTestResult().absoluteMediaURL(), frame);
269         break;
270     case ContextMenuItemTagDownloadMediaToDisk:
271         // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709)
272         m_client.downloadURL(m_context.hitTestResult().absoluteMediaURL());
273         break;
274     case ContextMenuItemTagCopyMediaLinkToClipboard:
275         frame->editor().copyURL(m_context.hitTestResult().absoluteMediaURL(), m_context.hitTestResult().textContent());
276         break;
277     case ContextMenuItemTagToggleMediaControls:
278         m_context.hitTestResult().toggleMediaControlsDisplay();
279         break;
280     case ContextMenuItemTagToggleMediaLoop:
281         m_context.hitTestResult().toggleMediaLoopPlayback();
282         break;
283     case ContextMenuItemTagToggleVideoFullscreen:
284         m_context.hitTestResult().toggleMediaFullscreenState();
285         break;
286     case ContextMenuItemTagEnterVideoFullscreen:
287         m_context.hitTestResult().enterFullscreenForVideo();
288         break;
289     case ContextMenuItemTagMediaPlayPause:
290         m_context.hitTestResult().toggleMediaPlayState();
291         break;
292     case ContextMenuItemTagMediaMute:
293         m_context.hitTestResult().toggleMediaMuteState();
294         break;
295     case ContextMenuItemTagOpenFrameInNewWindow: {
296         DocumentLoader* loader = frame->loader().documentLoader();
297         if (!loader->unreachableURL().isEmpty())
298             openNewWindow(loader->unreachableURL(), frame);
299         else
300             openNewWindow(loader->url(), frame);
301         break;
302     }
303     case ContextMenuItemTagCopy:
304         frame->editor().copy();
305         break;
306     case ContextMenuItemTagGoBack:
307         if (Page* page = frame->page())
308             page->backForward().goBackOrForward(-1);
309         break;
310     case ContextMenuItemTagGoForward:
311         if (Page* page = frame->page())
312             page->backForward().goBackOrForward(1);
313         break;
314     case ContextMenuItemTagStop:
315         frame->loader().stop();
316         break;
317     case ContextMenuItemTagReload:
318         frame->loader().reload();
319         break;
320     case ContextMenuItemTagCut:
321         frame->editor().command("Cut").execute();
322         break;
323     case ContextMenuItemTagPaste:
324         frame->editor().command("Paste").execute();
325         break;
326 #if PLATFORM(GTK)
327     case ContextMenuItemTagDelete:
328         frame->editor().performDelete();
329         break;
330     case ContextMenuItemTagUnicodeInsertLRMMark:
331         insertUnicodeCharacter(leftToRightMark, frame);
332         break;
333     case ContextMenuItemTagUnicodeInsertRLMMark:
334         insertUnicodeCharacter(rightToLeftMark, frame);
335         break;
336     case ContextMenuItemTagUnicodeInsertLREMark:
337         insertUnicodeCharacter(leftToRightEmbed, frame);
338         break;
339     case ContextMenuItemTagUnicodeInsertRLEMark:
340         insertUnicodeCharacter(rightToLeftEmbed, frame);
341         break;
342     case ContextMenuItemTagUnicodeInsertLROMark:
343         insertUnicodeCharacter(leftToRightOverride, frame);
344         break;
345     case ContextMenuItemTagUnicodeInsertRLOMark:
346         insertUnicodeCharacter(rightToLeftOverride, frame);
347         break;
348     case ContextMenuItemTagUnicodeInsertPDFMark:
349         insertUnicodeCharacter(popDirectionalFormatting, frame);
350         break;
351     case ContextMenuItemTagUnicodeInsertZWSMark:
352         insertUnicodeCharacter(zeroWidthSpace, frame);
353         break;
354     case ContextMenuItemTagUnicodeInsertZWJMark:
355         insertUnicodeCharacter(zeroWidthJoiner, frame);
356         break;
357     case ContextMenuItemTagUnicodeInsertZWNJMark:
358         insertUnicodeCharacter(zeroWidthNonJoiner, frame);
359         break;
360 #endif
361 #if PLATFORM(GTK) || PLATFORM(EFL)
362     case ContextMenuItemTagSelectAll:
363         frame->editor().command("SelectAll").execute();
364         break;
365 #endif
366     case ContextMenuItemTagSpellingGuess: {
367         VisibleSelection selection = frame->selection().selection();
368         if (frame->editor().shouldInsertText(item->title(), selection.toNormalizedRange().get(), EditorInsertActionPasted)) {
369             ReplaceSelectionCommand::CommandOptions replaceOptions = ReplaceSelectionCommand::MatchStyle | ReplaceSelectionCommand::PreventNesting;
370
371             if (frame->editor().behavior().shouldAllowSpellingSuggestionsWithoutSelection()) {
372                 ASSERT(selection.isCaretOrRange());
373                 VisibleSelection wordSelection(selection.base());
374                 wordSelection.expandUsingGranularity(WordGranularity);
375                 frame->selection().setSelection(wordSelection);
376             } else {
377                 ASSERT(frame->editor().selectedText().length());
378                 replaceOptions |= ReplaceSelectionCommand::SelectReplacement;
379             }
380
381             Document* document = frame->document();
382             ASSERT(document);
383             RefPtr<ReplaceSelectionCommand> command = ReplaceSelectionCommand::create(*document, createFragmentFromMarkup(*document, item->title(), ""), replaceOptions);
384             applyCommand(command);
385             frame->selection().revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
386         }
387         break;
388     }
389     case ContextMenuItemTagIgnoreSpelling:
390         frame->editor().ignoreSpelling();
391         break;
392     case ContextMenuItemTagLearnSpelling:
393         frame->editor().learnSpelling();
394         break;
395     case ContextMenuItemTagSearchWeb:
396         m_client.searchWithGoogle(frame);
397         break;
398     case ContextMenuItemTagLookUpInDictionary:
399         // FIXME: Some day we may be able to do this from within WebCore.
400         m_client.lookUpInDictionary(frame);
401         break;
402     case ContextMenuItemTagOpenLink:
403         if (Frame* targetFrame = m_context.hitTestResult().targetFrame())
404             targetFrame->loader().loadFrameRequest(FrameLoadRequest(frame->document()->securityOrigin(), ResourceRequest(m_context.hitTestResult().absoluteLinkURL(), frame->loader().outgoingReferrer())), LockHistory::No, LockBackForwardList::No, 0, 0, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Suppress);
405         else
406             openNewWindow(m_context.hitTestResult().absoluteLinkURL(), frame);
407         break;
408     case ContextMenuItemTagOpenLinkInThisWindow:
409         frame->loader().loadFrameRequest(FrameLoadRequest(frame->document()->securityOrigin(), ResourceRequest(m_context.hitTestResult().absoluteLinkURL(), frame->loader().outgoingReferrer())), LockHistory::No, LockBackForwardList::No, 0, 0, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Suppress);
410         break;
411     case ContextMenuItemTagBold:
412         frame->editor().command("ToggleBold").execute();
413         break;
414     case ContextMenuItemTagItalic:
415         frame->editor().command("ToggleItalic").execute();
416         break;
417     case ContextMenuItemTagUnderline:
418         frame->editor().toggleUnderline();
419         break;
420     case ContextMenuItemTagOutline:
421         // We actually never enable this because CSS does not have a way to specify an outline font,
422         // which may make this difficult to implement. Maybe a special case of text-shadow?
423         break;
424     case ContextMenuItemTagStartSpeaking: {
425         RefPtr<Range> selectedRange = frame->selection().toNormalizedRange();
426         if (!selectedRange || selectedRange->collapsed(IGNORE_EXCEPTION)) {
427             Document& document = m_context.hitTestResult().innerNonSharedNode()->document();
428             selectedRange = document.createRange();
429             selectedRange->selectNode(document.documentElement(), IGNORE_EXCEPTION);
430         }
431         m_client.speak(plainText(selectedRange.get()));
432         break;
433     }
434     case ContextMenuItemTagStopSpeaking:
435         m_client.stopSpeaking();
436         break;
437     case ContextMenuItemTagDefaultDirection:
438         frame->editor().setBaseWritingDirection(NaturalWritingDirection);
439         break;
440     case ContextMenuItemTagLeftToRight:
441         frame->editor().setBaseWritingDirection(LeftToRightWritingDirection);
442         break;
443     case ContextMenuItemTagRightToLeft:
444         frame->editor().setBaseWritingDirection(RightToLeftWritingDirection);
445         break;
446     case ContextMenuItemTagTextDirectionDefault:
447         frame->editor().command("MakeTextWritingDirectionNatural").execute();
448         break;
449     case ContextMenuItemTagTextDirectionLeftToRight:
450         frame->editor().command("MakeTextWritingDirectionLeftToRight").execute();
451         break;
452     case ContextMenuItemTagTextDirectionRightToLeft:
453         frame->editor().command("MakeTextWritingDirectionRightToLeft").execute();
454         break;
455 #if PLATFORM(COCOA)
456     case ContextMenuItemTagSearchInSpotlight:
457         m_client.searchWithSpotlight();
458         break;
459 #endif
460     case ContextMenuItemTagShowSpellingPanel:
461         frame->editor().showSpellingGuessPanel();
462         break;
463     case ContextMenuItemTagCheckSpelling:
464         frame->editor().advanceToNextMisspelling();
465         break;
466     case ContextMenuItemTagCheckSpellingWhileTyping:
467         frame->editor().toggleContinuousSpellChecking();
468         break;
469     case ContextMenuItemTagCheckGrammarWithSpelling:
470         frame->editor().toggleGrammarChecking();
471         break;
472 #if PLATFORM(COCOA)
473     case ContextMenuItemTagShowFonts:
474         frame->editor().showFontPanel();
475         break;
476     case ContextMenuItemTagStyles:
477         frame->editor().showStylesPanel();
478         break;
479     case ContextMenuItemTagShowColors:
480         frame->editor().showColorPanel();
481         break;
482 #endif
483 #if USE(APPKIT)
484     case ContextMenuItemTagMakeUpperCase:
485         frame->editor().uppercaseWord();
486         break;
487     case ContextMenuItemTagMakeLowerCase:
488         frame->editor().lowercaseWord();
489         break;
490     case ContextMenuItemTagCapitalize:
491         frame->editor().capitalizeWord();
492         break;
493 #endif
494 #if PLATFORM(COCOA)
495     case ContextMenuItemTagChangeBack:
496         frame->editor().changeBackToReplacedString(m_context.hitTestResult().replacedString());
497         break;
498 #endif
499 #if USE(AUTOMATIC_TEXT_REPLACEMENT)
500     case ContextMenuItemTagShowSubstitutions:
501         frame->editor().showSubstitutionsPanel();
502         break;
503     case ContextMenuItemTagSmartCopyPaste:
504         frame->editor().toggleSmartInsertDelete();
505         break;
506     case ContextMenuItemTagSmartQuotes:
507         frame->editor().toggleAutomaticQuoteSubstitution();
508         break;
509     case ContextMenuItemTagSmartDashes:
510         frame->editor().toggleAutomaticDashSubstitution();
511         break;
512     case ContextMenuItemTagSmartLinks:
513         frame->editor().toggleAutomaticLinkDetection();
514         break;
515     case ContextMenuItemTagTextReplacement:
516         frame->editor().toggleAutomaticTextReplacement();
517         break;
518     case ContextMenuItemTagCorrectSpellingAutomatically:
519         frame->editor().toggleAutomaticSpellingCorrection();
520         break;
521 #endif
522     case ContextMenuItemTagInspectElement:
523         if (Page* page = frame->page())
524             page->inspectorController().inspect(m_context.hitTestResult().innerNonSharedNode());
525         break;
526     case ContextMenuItemTagDictationAlternative:
527         frame->editor().applyDictationAlternativelternative(item->title());
528         break;
529     default:
530         break;
531     }
532 }
533
534 void ContextMenuController::appendItem(ContextMenuItem& menuItem, ContextMenu* parentMenu)
535 {
536     checkOrEnableIfNeeded(menuItem);
537     if (parentMenu)
538         parentMenu->appendItem(menuItem);
539 }
540
541 void ContextMenuController::createAndAppendFontSubMenu(ContextMenuItem& fontMenuItem)
542 {
543     ContextMenu fontMenu;
544
545 #if PLATFORM(COCOA)
546     ContextMenuItem showFonts(ActionType, ContextMenuItemTagShowFonts, contextMenuItemTagShowFonts());
547 #endif
548     ContextMenuItem bold(CheckableActionType, ContextMenuItemTagBold, contextMenuItemTagBold());
549     ContextMenuItem italic(CheckableActionType, ContextMenuItemTagItalic, contextMenuItemTagItalic());
550     ContextMenuItem underline(CheckableActionType, ContextMenuItemTagUnderline, contextMenuItemTagUnderline());
551     ContextMenuItem outline(ActionType, ContextMenuItemTagOutline, contextMenuItemTagOutline());
552 #if PLATFORM(COCOA)
553     ContextMenuItem styles(ActionType, ContextMenuItemTagStyles, contextMenuItemTagStyles());
554     ContextMenuItem showColors(ActionType, ContextMenuItemTagShowColors, contextMenuItemTagShowColors());
555 #endif
556
557 #if PLATFORM(COCOA)
558     appendItem(showFonts, &fontMenu);
559 #endif
560     appendItem(bold, &fontMenu);
561     appendItem(italic, &fontMenu);
562     appendItem(underline, &fontMenu);
563     appendItem(outline, &fontMenu);
564 #if PLATFORM(COCOA)
565     appendItem(styles, &fontMenu);
566     appendItem(*separatorItem(), &fontMenu);
567     appendItem(showColors, &fontMenu);
568 #endif
569
570     fontMenuItem.setSubMenu(&fontMenu);
571 }
572
573
574 #if !PLATFORM(GTK)
575
576 void ContextMenuController::createAndAppendSpellingAndGrammarSubMenu(ContextMenuItem& spellingAndGrammarMenuItem)
577 {
578     ContextMenu spellingAndGrammarMenu;
579
580     ContextMenuItem showSpellingPanel(ActionType, ContextMenuItemTagShowSpellingPanel, 
581         contextMenuItemTagShowSpellingPanel(true));
582     ContextMenuItem checkSpelling(ActionType, ContextMenuItemTagCheckSpelling, 
583         contextMenuItemTagCheckSpelling());
584     ContextMenuItem checkAsYouType(CheckableActionType, ContextMenuItemTagCheckSpellingWhileTyping, 
585         contextMenuItemTagCheckSpellingWhileTyping());
586     ContextMenuItem grammarWithSpelling(CheckableActionType, ContextMenuItemTagCheckGrammarWithSpelling, 
587         contextMenuItemTagCheckGrammarWithSpelling());
588 #if PLATFORM(COCOA)
589     ContextMenuItem correctSpelling(CheckableActionType, ContextMenuItemTagCorrectSpellingAutomatically, 
590         contextMenuItemTagCorrectSpellingAutomatically());
591 #endif
592
593     appendItem(showSpellingPanel, &spellingAndGrammarMenu);
594     appendItem(checkSpelling, &spellingAndGrammarMenu);
595 #if PLATFORM(COCOA)
596     appendItem(*separatorItem(), &spellingAndGrammarMenu);
597 #endif
598     appendItem(checkAsYouType, &spellingAndGrammarMenu);
599     appendItem(grammarWithSpelling, &spellingAndGrammarMenu);
600 #if PLATFORM(COCOA)
601     appendItem(correctSpelling, &spellingAndGrammarMenu);
602 #endif
603
604     spellingAndGrammarMenuItem.setSubMenu(&spellingAndGrammarMenu);
605 }
606
607 #endif // !PLATFORM(GTK)
608
609
610 #if PLATFORM(COCOA)
611
612 void ContextMenuController::createAndAppendSpeechSubMenu(ContextMenuItem& speechMenuItem)
613 {
614     ContextMenu speechMenu;
615
616     ContextMenuItem start(ActionType, ContextMenuItemTagStartSpeaking, contextMenuItemTagStartSpeaking());
617     ContextMenuItem stop(ActionType, ContextMenuItemTagStopSpeaking, contextMenuItemTagStopSpeaking());
618
619     appendItem(start, &speechMenu);
620     appendItem(stop, &speechMenu);
621
622     speechMenuItem.setSubMenu(&speechMenu);
623 }
624
625 #endif
626  
627 #if PLATFORM(GTK)
628
629 void ContextMenuController::createAndAppendUnicodeSubMenu(ContextMenuItem& unicodeMenuItem)
630 {
631     ContextMenu unicodeMenu;
632
633     ContextMenuItem leftToRightMarkMenuItem(ActionType, ContextMenuItemTagUnicodeInsertLRMMark, contextMenuItemTagUnicodeInsertLRMMark());
634     ContextMenuItem rightToLeftMarkMenuItem(ActionType, ContextMenuItemTagUnicodeInsertRLMMark, contextMenuItemTagUnicodeInsertRLMMark());
635     ContextMenuItem leftToRightEmbedMenuItem(ActionType, ContextMenuItemTagUnicodeInsertLREMark, contextMenuItemTagUnicodeInsertLREMark());
636     ContextMenuItem rightToLeftEmbedMenuItem(ActionType, ContextMenuItemTagUnicodeInsertRLEMark, contextMenuItemTagUnicodeInsertRLEMark());
637     ContextMenuItem leftToRightOverrideMenuItem(ActionType, ContextMenuItemTagUnicodeInsertLROMark, contextMenuItemTagUnicodeInsertLROMark());
638     ContextMenuItem rightToLeftOverrideMenuItem(ActionType, ContextMenuItemTagUnicodeInsertRLOMark, contextMenuItemTagUnicodeInsertRLOMark());
639     ContextMenuItem popDirectionalFormattingMenuItem(ActionType, ContextMenuItemTagUnicodeInsertPDFMark, contextMenuItemTagUnicodeInsertPDFMark());
640     ContextMenuItem zeroWidthSpaceMenuItem(ActionType, ContextMenuItemTagUnicodeInsertZWSMark, contextMenuItemTagUnicodeInsertZWSMark());
641     ContextMenuItem zeroWidthJoinerMenuItem(ActionType, ContextMenuItemTagUnicodeInsertZWJMark, contextMenuItemTagUnicodeInsertZWJMark());
642     ContextMenuItem zeroWidthNonJoinerMenuItem(ActionType, ContextMenuItemTagUnicodeInsertZWNJMark, contextMenuItemTagUnicodeInsertZWNJMark());
643
644     appendItem(leftToRightMarkMenuItem, &unicodeMenu);
645     appendItem(rightToLeftMarkMenuItem, &unicodeMenu);
646     appendItem(leftToRightEmbedMenuItem, &unicodeMenu);
647     appendItem(rightToLeftEmbedMenuItem, &unicodeMenu);
648     appendItem(leftToRightOverrideMenuItem, &unicodeMenu);
649     appendItem(rightToLeftOverrideMenuItem, &unicodeMenu);
650     appendItem(popDirectionalFormattingMenuItem, &unicodeMenu);
651     appendItem(zeroWidthSpaceMenuItem, &unicodeMenu);
652     appendItem(zeroWidthJoinerMenuItem, &unicodeMenu);
653     appendItem(zeroWidthNonJoinerMenuItem, &unicodeMenu);
654
655     unicodeMenuItem.setSubMenu(&unicodeMenu);
656 }
657
658 #else
659
660 void ContextMenuController::createAndAppendWritingDirectionSubMenu(ContextMenuItem& writingDirectionMenuItem)
661 {
662     ContextMenu writingDirectionMenu;
663
664     ContextMenuItem defaultItem(ActionType, ContextMenuItemTagDefaultDirection, 
665         contextMenuItemTagDefaultDirection());
666     ContextMenuItem ltr(CheckableActionType, ContextMenuItemTagLeftToRight, contextMenuItemTagLeftToRight());
667     ContextMenuItem rtl(CheckableActionType, ContextMenuItemTagRightToLeft, contextMenuItemTagRightToLeft());
668
669     appendItem(defaultItem, &writingDirectionMenu);
670     appendItem(ltr, &writingDirectionMenu);
671     appendItem(rtl, &writingDirectionMenu);
672
673     writingDirectionMenuItem.setSubMenu(&writingDirectionMenu);
674 }
675
676 void ContextMenuController::createAndAppendTextDirectionSubMenu(ContextMenuItem& textDirectionMenuItem)
677 {
678     ContextMenu textDirectionMenu;
679
680     ContextMenuItem defaultItem(ActionType, ContextMenuItemTagTextDirectionDefault, contextMenuItemTagDefaultDirection());
681     ContextMenuItem ltr(CheckableActionType, ContextMenuItemTagTextDirectionLeftToRight, contextMenuItemTagLeftToRight());
682     ContextMenuItem rtl(CheckableActionType, ContextMenuItemTagTextDirectionRightToLeft, contextMenuItemTagRightToLeft());
683
684     appendItem(defaultItem, &textDirectionMenu);
685     appendItem(ltr, &textDirectionMenu);
686     appendItem(rtl, &textDirectionMenu);
687
688     textDirectionMenuItem.setSubMenu(&textDirectionMenu);
689 }
690
691 #endif
692
693 #if PLATFORM(COCOA)
694
695 void ContextMenuController::createAndAppendSubstitutionsSubMenu(ContextMenuItem& substitutionsMenuItem)
696 {
697     ContextMenu substitutionsMenu;
698
699     ContextMenuItem showSubstitutions(ActionType, ContextMenuItemTagShowSubstitutions, contextMenuItemTagShowSubstitutions(true));
700     ContextMenuItem smartCopyPaste(CheckableActionType, ContextMenuItemTagSmartCopyPaste, contextMenuItemTagSmartCopyPaste());
701     ContextMenuItem smartQuotes(CheckableActionType, ContextMenuItemTagSmartQuotes, contextMenuItemTagSmartQuotes());
702     ContextMenuItem smartDashes(CheckableActionType, ContextMenuItemTagSmartDashes, contextMenuItemTagSmartDashes());
703     ContextMenuItem smartLinks(CheckableActionType, ContextMenuItemTagSmartLinks, contextMenuItemTagSmartLinks());
704     ContextMenuItem textReplacement(CheckableActionType, ContextMenuItemTagTextReplacement, contextMenuItemTagTextReplacement());
705
706     appendItem(showSubstitutions, &substitutionsMenu);
707     appendItem(*separatorItem(), &substitutionsMenu);
708     appendItem(smartCopyPaste, &substitutionsMenu);
709     appendItem(smartQuotes, &substitutionsMenu);
710     appendItem(smartDashes, &substitutionsMenu);
711     appendItem(smartLinks, &substitutionsMenu);
712     appendItem(textReplacement, &substitutionsMenu);
713
714     substitutionsMenuItem.setSubMenu(&substitutionsMenu);
715 }
716
717 void ContextMenuController::createAndAppendTransformationsSubMenu(ContextMenuItem& transformationsMenuItem)
718 {
719     ContextMenu transformationsMenu;
720
721     ContextMenuItem makeUpperCase(ActionType, ContextMenuItemTagMakeUpperCase, contextMenuItemTagMakeUpperCase());
722     ContextMenuItem makeLowerCase(ActionType, ContextMenuItemTagMakeLowerCase, contextMenuItemTagMakeLowerCase());
723     ContextMenuItem capitalize(ActionType, ContextMenuItemTagCapitalize, contextMenuItemTagCapitalize());
724
725     appendItem(makeUpperCase, &transformationsMenu);
726     appendItem(makeLowerCase, &transformationsMenu);
727     appendItem(capitalize, &transformationsMenu);
728
729     transformationsMenuItem.setSubMenu(&transformationsMenu);
730 }
731
732 #endif
733
734 #if PLATFORM(COCOA)
735 #define SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 1
736 #else
737 #define SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 0
738 #endif
739
740 #if PLATFORM(COCOA)
741 #define SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 1
742 #else
743 #define SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 0
744 #endif
745
746 void ContextMenuController::populate()
747 {
748     ContextMenuItem OpenLinkItem(ActionType, ContextMenuItemTagOpenLink, contextMenuItemTagOpenLink());
749     ContextMenuItem OpenLinkInNewWindowItem(ActionType, ContextMenuItemTagOpenLinkInNewWindow, 
750         contextMenuItemTagOpenLinkInNewWindow());
751     ContextMenuItem DownloadFileItem(ActionType, ContextMenuItemTagDownloadLinkToDisk, 
752         contextMenuItemTagDownloadLinkToDisk());
753     ContextMenuItem CopyLinkItem(ActionType, ContextMenuItemTagCopyLinkToClipboard, 
754         contextMenuItemTagCopyLinkToClipboard());
755     ContextMenuItem OpenImageInNewWindowItem(ActionType, ContextMenuItemTagOpenImageInNewWindow, 
756         contextMenuItemTagOpenImageInNewWindow());
757     ContextMenuItem DownloadImageItem(ActionType, ContextMenuItemTagDownloadImageToDisk, 
758         contextMenuItemTagDownloadImageToDisk());
759     ContextMenuItem CopyImageItem(ActionType, ContextMenuItemTagCopyImageToClipboard, 
760         contextMenuItemTagCopyImageToClipboard());
761 #if PLATFORM(GTK) || PLATFORM(EFL)
762     ContextMenuItem CopyImageUrlItem(ActionType, ContextMenuItemTagCopyImageUrlToClipboard, 
763         contextMenuItemTagCopyImageUrlToClipboard());
764 #endif
765     ContextMenuItem OpenMediaInNewWindowItem(ActionType, ContextMenuItemTagOpenMediaInNewWindow, String());
766     ContextMenuItem DownloadMediaItem(ActionType, ContextMenuItemTagDownloadMediaToDisk, String());
767     ContextMenuItem CopyMediaLinkItem(ActionType, ContextMenuItemTagCopyMediaLinkToClipboard, String());
768     ContextMenuItem MediaPlayPause(ActionType, ContextMenuItemTagMediaPlayPause, 
769         contextMenuItemTagMediaPlay());
770     ContextMenuItem MediaMute(ActionType, ContextMenuItemTagMediaMute, 
771         contextMenuItemTagMediaMute());
772 #if SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS
773     ContextMenuItem ToggleMediaControls(ActionType, ContextMenuItemTagToggleMediaControls,
774         contextMenuItemTagHideMediaControls());
775 #else
776     ContextMenuItem ToggleMediaControls(CheckableActionType, ContextMenuItemTagToggleMediaControls, 
777         contextMenuItemTagToggleMediaControls());
778 #endif
779     ContextMenuItem ToggleMediaLoop(CheckableActionType, ContextMenuItemTagToggleMediaLoop, 
780         contextMenuItemTagToggleMediaLoop());
781     ContextMenuItem EnterVideoFullscreen(ActionType, ContextMenuItemTagEnterVideoFullscreen,
782         contextMenuItemTagEnterVideoFullscreen());
783     ContextMenuItem ToggleVideoFullscreen(ActionType, ContextMenuItemTagToggleVideoFullscreen,
784         contextMenuItemTagEnterVideoFullscreen());
785 #if PLATFORM(COCOA)
786     ContextMenuItem SearchSpotlightItem(ActionType, ContextMenuItemTagSearchInSpotlight, 
787         contextMenuItemTagSearchInSpotlight());
788 #endif
789 #if !PLATFORM(GTK)
790     ContextMenuItem SearchWebItem(ActionType, ContextMenuItemTagSearchWeb, contextMenuItemTagSearchWeb());
791 #endif
792     ContextMenuItem CopyItem(ActionType, ContextMenuItemTagCopy, contextMenuItemTagCopy());
793     ContextMenuItem BackItem(ActionType, ContextMenuItemTagGoBack, contextMenuItemTagGoBack());
794     ContextMenuItem ForwardItem(ActionType, ContextMenuItemTagGoForward,  contextMenuItemTagGoForward());
795     ContextMenuItem StopItem(ActionType, ContextMenuItemTagStop, contextMenuItemTagStop());
796     ContextMenuItem ReloadItem(ActionType, ContextMenuItemTagReload, contextMenuItemTagReload());
797     ContextMenuItem OpenFrameItem(ActionType, ContextMenuItemTagOpenFrameInNewWindow, 
798         contextMenuItemTagOpenFrameInNewWindow());
799     ContextMenuItem NoGuessesItem(ActionType, ContextMenuItemTagNoGuessesFound, 
800         contextMenuItemTagNoGuessesFound());
801     ContextMenuItem IgnoreSpellingItem(ActionType, ContextMenuItemTagIgnoreSpelling, 
802         contextMenuItemTagIgnoreSpelling());
803     ContextMenuItem LearnSpellingItem(ActionType, ContextMenuItemTagLearnSpelling, 
804         contextMenuItemTagLearnSpelling());
805     ContextMenuItem IgnoreGrammarItem(ActionType, ContextMenuItemTagIgnoreGrammar, 
806         contextMenuItemTagIgnoreGrammar());
807     ContextMenuItem CutItem(ActionType, ContextMenuItemTagCut, contextMenuItemTagCut());
808     ContextMenuItem PasteItem(ActionType, ContextMenuItemTagPaste, contextMenuItemTagPaste());
809 #if PLATFORM(GTK)
810     ContextMenuItem DeleteItem(ActionType, ContextMenuItemTagDelete, contextMenuItemTagDelete());
811 #endif
812 #if PLATFORM(GTK) || PLATFORM(EFL)
813     ContextMenuItem SelectAllItem(ActionType, ContextMenuItemTagSelectAll, contextMenuItemTagSelectAll());
814 #endif
815
816     ContextMenuItem ShareMenuItem = m_client.shareMenuItem(m_context.hitTestResult());
817
818     Node* node = m_context.hitTestResult().innerNonSharedNode();
819     if (!node)
820         return;
821 #if PLATFORM(GTK)
822     if (!m_context.hitTestResult().isContentEditable() && is<HTMLFormControlElement>(*node))
823         return;
824 #endif
825     Frame* frame = node->document().frame();
826     if (!frame)
827         return;
828
829 #if ENABLE(SERVICE_CONTROLS)
830     // The default image control menu gets populated solely by the platform.
831     if (m_context.controlledImage())
832         return;
833 #endif
834
835     if (!m_context.hitTestResult().isContentEditable()) {
836         String selectedString = m_context.hitTestResult().selectedText();
837         m_context.setSelectedText(selectedString);
838
839         FrameLoader& loader = frame->loader();
840         URL linkURL = m_context.hitTestResult().absoluteLinkURL();
841         if (!linkURL.isEmpty()) {
842             if (loader.client().canHandleRequest(ResourceRequest(linkURL))) {
843                 appendItem(OpenLinkItem, m_contextMenu.get());
844                 appendItem(OpenLinkInNewWindowItem, m_contextMenu.get());
845                 appendItem(DownloadFileItem, m_contextMenu.get());
846             }
847             appendItem(CopyLinkItem, m_contextMenu.get());
848         }
849
850         URL imageURL = m_context.hitTestResult().absoluteImageURL();
851         if (!imageURL.isEmpty()) {
852             if (!linkURL.isEmpty())
853                 appendItem(*separatorItem(), m_contextMenu.get());
854
855             appendItem(OpenImageInNewWindowItem, m_contextMenu.get());
856             appendItem(DownloadImageItem, m_contextMenu.get());
857             if (imageURL.isLocalFile() || m_context.hitTestResult().image())
858                 appendItem(CopyImageItem, m_contextMenu.get());
859 #if PLATFORM(GTK) || PLATFORM(EFL)
860             appendItem(CopyImageUrlItem, m_contextMenu.get());
861 #endif
862         }
863
864         URL mediaURL = m_context.hitTestResult().absoluteMediaURL();
865         if (!mediaURL.isEmpty()) {
866             if (!linkURL.isEmpty() || !imageURL.isEmpty())
867                 appendItem(*separatorItem(), m_contextMenu.get());
868
869             appendItem(MediaPlayPause, m_contextMenu.get());
870             appendItem(MediaMute, m_contextMenu.get());
871             appendItem(ToggleMediaControls, m_contextMenu.get());
872             appendItem(ToggleMediaLoop, m_contextMenu.get());
873 #if SUPPORTS_TOGGLE_VIDEO_FULLSCREEN
874             appendItem(ToggleVideoFullscreen, m_contextMenu.get());
875 #else
876             appendItem(EnterVideoFullscreen, m_contextMenu.get());
877 #endif
878             appendItem(*separatorItem(), m_contextMenu.get());
879             appendItem(CopyMediaLinkItem, m_contextMenu.get());
880             appendItem(OpenMediaInNewWindowItem, m_contextMenu.get());
881             if (m_context.hitTestResult().isDownloadableMedia() && loader.client().canHandleRequest(ResourceRequest(mediaURL)))
882                 appendItem(DownloadMediaItem, m_contextMenu.get());
883         }
884
885         if (imageURL.isEmpty() && linkURL.isEmpty() && mediaURL.isEmpty()) {
886             if (m_context.hitTestResult().isSelected()) {
887                 if (!selectedString.isEmpty()) {
888 #if PLATFORM(COCOA)
889                     ContextMenuItem LookUpInDictionaryItem(ActionType, ContextMenuItemTagLookUpInDictionary, contextMenuItemTagLookUpInDictionary(selectedString));
890
891                     appendItem(LookUpInDictionaryItem, m_contextMenu.get());
892 #endif
893
894 #if !PLATFORM(GTK)
895                     appendItem(SearchWebItem, m_contextMenu.get());
896                     appendItem(*separatorItem(), m_contextMenu.get());
897 #endif
898                 }
899
900                 appendItem(CopyItem, m_contextMenu.get());
901 #if PLATFORM(COCOA)
902                 appendItem(*separatorItem(), m_contextMenu.get());
903
904                 if (!ShareMenuItem.isNull()) {
905                     appendItem(ShareMenuItem, m_contextMenu.get());
906                     appendItem(*separatorItem(), m_contextMenu.get());
907                 }
908
909                 ContextMenuItem SpeechMenuItem(SubmenuType, ContextMenuItemTagSpeechMenu, contextMenuItemTagSpeechMenu());
910                 createAndAppendSpeechSubMenu(SpeechMenuItem);
911                 appendItem(SpeechMenuItem, m_contextMenu.get());
912 #endif                
913             } else {
914                 if (!(frame->page() && (frame->page()->inspectorController().hasInspectorFrontendClient() || frame->page()->inspectorController().hasRemoteFrontend()))) {
915
916                 // In GTK+ unavailable items are not hidden but insensitive.
917 #if PLATFORM(GTK)
918                 appendItem(BackItem, m_contextMenu.get());
919                 appendItem(ForwardItem, m_contextMenu.get());
920                 appendItem(StopItem, m_contextMenu.get());
921                 appendItem(ReloadItem, m_contextMenu.get());
922 #else
923                 if (frame->page() && frame->page()->backForward().canGoBackOrForward(-1))
924                     appendItem(BackItem, m_contextMenu.get());
925
926                 if (frame->page() && frame->page()->backForward().canGoBackOrForward(1))
927                     appendItem(ForwardItem, m_contextMenu.get());
928
929                 // use isLoadingInAPISense rather than isLoading because Stop/Reload are
930                 // intended to match WebKit's API, not WebCore's internal notion of loading status
931                 if (loader.documentLoader()->isLoadingInAPISense())
932                     appendItem(StopItem, m_contextMenu.get());
933                 else
934                     appendItem(ReloadItem, m_contextMenu.get());
935 #endif
936                 }
937
938                 if (frame->page() && !frame->isMainFrame())
939                     appendItem(OpenFrameItem, m_contextMenu.get());
940
941                 if (!ShareMenuItem.isNull()) {
942                     appendItem(*separatorItem(), m_contextMenu.get());
943                     appendItem(ShareMenuItem, m_contextMenu.get());
944                 }
945             }
946         } else if (!ShareMenuItem.isNull()) {
947             appendItem(*separatorItem(), m_contextMenu.get());
948             appendItem(ShareMenuItem, m_contextMenu.get());
949         }
950     } else { // Make an editing context menu
951         bool inPasswordField = frame->selection().selection().isInPasswordField();
952         if (!inPasswordField) {
953             bool haveContextMenuItemsForMisspellingOrGrammer = false;
954             bool spellCheckingEnabled = frame->editor().isSpellCheckingEnabledFor(node);
955             if (spellCheckingEnabled) {
956                 // Consider adding spelling-related or grammar-related context menu items (never both, since a single selected range
957                 // is never considered a misspelling and bad grammar at the same time)
958                 bool misspelling;
959                 bool badGrammar;
960                 Vector<String> guesses = frame->editor().guessesForMisspelledOrUngrammatical(misspelling, badGrammar);
961                 if (misspelling || badGrammar) {
962                     size_t size = guesses.size();
963                     if (!size) {
964                         // If there's bad grammar but no suggestions (e.g., repeated word), just leave off the suggestions
965                         // list and trailing separator rather than adding a "No Guesses Found" item (matches AppKit)
966                         if (misspelling) {
967                             appendItem(NoGuessesItem, m_contextMenu.get());
968                             appendItem(*separatorItem(), m_contextMenu.get());
969                         }
970                     } else {
971                         for (unsigned i = 0; i < size; i++) {
972                             const String &guess = guesses[i];
973                             if (!guess.isEmpty()) {
974                                 ContextMenuItem item(ActionType, ContextMenuItemTagSpellingGuess, guess);
975                                 appendItem(item, m_contextMenu.get());
976                             }
977                         }
978                         appendItem(*separatorItem(), m_contextMenu.get());
979                     }
980                     if (misspelling) {
981                         appendItem(IgnoreSpellingItem, m_contextMenu.get());
982                         appendItem(LearnSpellingItem, m_contextMenu.get());
983                     } else
984                         appendItem(IgnoreGrammarItem, m_contextMenu.get());
985                     appendItem(*separatorItem(), m_contextMenu.get());
986                     haveContextMenuItemsForMisspellingOrGrammer = true;
987 #if PLATFORM(COCOA)
988                 } else {
989                     // If the string was autocorrected, generate a contextual menu item allowing it to be changed back.
990                     String replacedString = m_context.hitTestResult().replacedString();
991                     if (!replacedString.isEmpty()) {
992                         ContextMenuItem item(ActionType, ContextMenuItemTagChangeBack, contextMenuItemTagChangeBack(replacedString));
993                         appendItem(item, m_contextMenu.get());
994                         appendItem(*separatorItem(), m_contextMenu.get());
995                         haveContextMenuItemsForMisspellingOrGrammer = true;
996                     }
997 #endif
998                 }
999             }
1000
1001             if (!haveContextMenuItemsForMisspellingOrGrammer) {
1002                 // Spelling and grammar checking is mutually exclusive with dictation alternatives.
1003                 Vector<String> dictationAlternatives = m_context.hitTestResult().dictationAlternatives();
1004                 if (!dictationAlternatives.isEmpty()) {
1005                     for (size_t i = 0; i < dictationAlternatives.size(); ++i) {
1006                         ContextMenuItem item(ActionType, ContextMenuItemTagDictationAlternative, dictationAlternatives[i]);
1007                         appendItem(item, m_contextMenu.get());
1008                     }
1009                     appendItem(*separatorItem(), m_contextMenu.get());
1010                 }
1011             }
1012         }
1013
1014         FrameLoader& loader = frame->loader();
1015         URL linkURL = m_context.hitTestResult().absoluteLinkURL();
1016         if (!linkURL.isEmpty()) {
1017             if (loader.client().canHandleRequest(ResourceRequest(linkURL))) {
1018                 appendItem(OpenLinkItem, m_contextMenu.get());
1019                 appendItem(OpenLinkInNewWindowItem, m_contextMenu.get());
1020                 appendItem(DownloadFileItem, m_contextMenu.get());
1021             }
1022             appendItem(CopyLinkItem, m_contextMenu.get());
1023             appendItem(*separatorItem(), m_contextMenu.get());
1024         }
1025
1026         String selectedText = m_context.hitTestResult().selectedText();
1027         if (m_context.hitTestResult().isSelected() && !inPasswordField && !selectedText.isEmpty()) {
1028 #if PLATFORM(COCOA)
1029             ContextMenuItem LookUpInDictionaryItem(ActionType, ContextMenuItemTagLookUpInDictionary, contextMenuItemTagLookUpInDictionary(selectedText));
1030
1031             appendItem(LookUpInDictionaryItem, m_contextMenu.get());
1032 #endif
1033
1034 #if !PLATFORM(GTK)
1035             appendItem(SearchWebItem, m_contextMenu.get());
1036             appendItem(*separatorItem(), m_contextMenu.get());
1037 #endif
1038         }
1039
1040         appendItem(CutItem, m_contextMenu.get());
1041         appendItem(CopyItem, m_contextMenu.get());
1042         appendItem(PasteItem, m_contextMenu.get());
1043 #if PLATFORM(GTK)
1044         appendItem(DeleteItem, m_contextMenu.get());
1045         appendItem(*separatorItem(), m_contextMenu.get());
1046 #endif
1047 #if PLATFORM(GTK) || PLATFORM(EFL)
1048         appendItem(SelectAllItem, m_contextMenu.get());
1049 #endif
1050
1051         if (!inPasswordField) {
1052 #if !PLATFORM(GTK)
1053             appendItem(*separatorItem(), m_contextMenu.get());
1054             ContextMenuItem SpellingAndGrammarMenuItem(SubmenuType, ContextMenuItemTagSpellingMenu, 
1055                 contextMenuItemTagSpellingMenu());
1056             createAndAppendSpellingAndGrammarSubMenu(SpellingAndGrammarMenuItem);
1057             appendItem(SpellingAndGrammarMenuItem, m_contextMenu.get());
1058 #endif
1059 #if PLATFORM(COCOA)
1060             ContextMenuItem substitutionsMenuItem(SubmenuType, ContextMenuItemTagSubstitutionsMenu, 
1061                 contextMenuItemTagSubstitutionsMenu());
1062             createAndAppendSubstitutionsSubMenu(substitutionsMenuItem);
1063             appendItem(substitutionsMenuItem, m_contextMenu.get());
1064             ContextMenuItem transformationsMenuItem(SubmenuType, ContextMenuItemTagTransformationsMenu, 
1065                 contextMenuItemTagTransformationsMenu());
1066             createAndAppendTransformationsSubMenu(transformationsMenuItem);
1067             appendItem(transformationsMenuItem, m_contextMenu.get());
1068 #endif
1069 #if PLATFORM(GTK)
1070             bool shouldShowFontMenu = frame->editor().canEditRichly();
1071 #else
1072             bool shouldShowFontMenu = true;
1073 #endif
1074             if (shouldShowFontMenu) {
1075                 ContextMenuItem FontMenuItem(SubmenuType, ContextMenuItemTagFontMenu, 
1076                     contextMenuItemTagFontMenu());
1077                 createAndAppendFontSubMenu(FontMenuItem);
1078                 appendItem(FontMenuItem, m_contextMenu.get());
1079             }
1080 #if PLATFORM(COCOA)
1081             ContextMenuItem SpeechMenuItem(SubmenuType, ContextMenuItemTagSpeechMenu, contextMenuItemTagSpeechMenu());
1082             createAndAppendSpeechSubMenu(SpeechMenuItem);
1083             appendItem(SpeechMenuItem, m_contextMenu.get());
1084 #endif
1085 #if PLATFORM(GTK)
1086             EditorClient* client = frame->editor().client();
1087             if (client && client->shouldShowUnicodeMenu()) {
1088                 ContextMenuItem UnicodeMenuItem(SubmenuType, ContextMenuItemTagUnicode, contextMenuItemTagUnicode());
1089                 createAndAppendUnicodeSubMenu(UnicodeMenuItem);
1090                 appendItem(*separatorItem(), m_contextMenu.get());
1091                 appendItem(UnicodeMenuItem, m_contextMenu.get());
1092             }
1093 #else
1094             ContextMenuItem WritingDirectionMenuItem(SubmenuType, ContextMenuItemTagWritingDirectionMenu, 
1095                 contextMenuItemTagWritingDirectionMenu());
1096             createAndAppendWritingDirectionSubMenu(WritingDirectionMenuItem);
1097             appendItem(WritingDirectionMenuItem, m_contextMenu.get());
1098             if (Page* page = frame->page()) {
1099                 bool includeTextDirectionSubmenu = page->settings().textDirectionSubmenuInclusionBehavior() == TextDirectionSubmenuAlwaysIncluded
1100                     || (page->settings().textDirectionSubmenuInclusionBehavior() == TextDirectionSubmenuAutomaticallyIncluded && frame->editor().hasBidiSelection());
1101                 if (includeTextDirectionSubmenu) {
1102                     ContextMenuItem TextDirectionMenuItem(SubmenuType, ContextMenuItemTagTextDirectionMenu, contextMenuItemTagTextDirectionMenu());
1103                     createAndAppendTextDirectionSubMenu(TextDirectionMenuItem);
1104                     appendItem(TextDirectionMenuItem, m_contextMenu.get());
1105                 }
1106             }
1107 #endif
1108         }
1109
1110         if (!ShareMenuItem.isNull()) {
1111             appendItem(*separatorItem(), m_contextMenu.get());
1112             appendItem(ShareMenuItem, m_contextMenu.get());
1113         }
1114     }
1115 }
1116
1117 void ContextMenuController::addInspectElementItem()
1118 {
1119     Node* node = m_context.hitTestResult().innerNonSharedNode();
1120     if (!node)
1121         return;
1122
1123     Frame* frame = node->document().frame();
1124     if (!frame)
1125         return;
1126
1127     Page* page = frame->page();
1128     if (!page)
1129         return;
1130
1131     ContextMenuItem InspectElementItem(ActionType, ContextMenuItemTagInspectElement, contextMenuItemTagInspectElement());
1132 #if USE(CROSS_PLATFORM_CONTEXT_MENUS)
1133     if (m_contextMenu && !m_contextMenu->items().isEmpty())
1134 #else
1135     if (m_contextMenu && m_contextMenu->itemCount())
1136 #endif
1137         appendItem(*separatorItem(), m_contextMenu.get());
1138     appendItem(InspectElementItem, m_contextMenu.get());
1139 }
1140
1141 void ContextMenuController::checkOrEnableIfNeeded(ContextMenuItem& item) const
1142 {
1143     if (item.type() == SeparatorType)
1144         return;
1145     
1146     Frame* frame = m_context.hitTestResult().innerNonSharedNode()->document().frame();
1147     if (!frame)
1148         return;
1149
1150     // Custom items already have proper checked and enabled values.
1151     if (ContextMenuItemBaseCustomTag <= item.action() && item.action() <= ContextMenuItemLastCustomTag)
1152         return;
1153
1154     bool shouldEnable = true;
1155     bool shouldCheck = false; 
1156
1157     switch (item.action()) {
1158         case ContextMenuItemTagCheckSpelling:
1159             shouldEnable = frame->editor().canEdit();
1160             break;
1161         case ContextMenuItemTagDefaultDirection:
1162             shouldCheck = false;
1163             shouldEnable = false;
1164             break;
1165         case ContextMenuItemTagLeftToRight:
1166         case ContextMenuItemTagRightToLeft: {
1167             String direction = item.action() == ContextMenuItemTagLeftToRight ? "ltr" : "rtl";
1168             shouldCheck = frame->editor().selectionHasStyle(CSSPropertyDirection, direction) != FalseTriState;
1169             shouldEnable = true;
1170             break;
1171         }
1172         case ContextMenuItemTagTextDirectionDefault: {
1173             Editor::Command command = frame->editor().command("MakeTextWritingDirectionNatural");
1174             shouldCheck = command.state() == TrueTriState;
1175             shouldEnable = command.isEnabled();
1176             break;
1177         }
1178         case ContextMenuItemTagTextDirectionLeftToRight: {
1179             Editor::Command command = frame->editor().command("MakeTextWritingDirectionLeftToRight");
1180             shouldCheck = command.state() == TrueTriState;
1181             shouldEnable = command.isEnabled();
1182             break;
1183         }
1184         case ContextMenuItemTagTextDirectionRightToLeft: {
1185             Editor::Command command = frame->editor().command("MakeTextWritingDirectionRightToLeft");
1186             shouldCheck = command.state() == TrueTriState;
1187             shouldEnable = command.isEnabled();
1188             break;
1189         }
1190         case ContextMenuItemTagCopy:
1191             shouldEnable = frame->editor().canDHTMLCopy() || frame->editor().canCopy();
1192             break;
1193         case ContextMenuItemTagCut:
1194             shouldEnable = frame->editor().canDHTMLCut() || frame->editor().canCut();
1195             break;
1196         case ContextMenuItemTagIgnoreSpelling:
1197         case ContextMenuItemTagLearnSpelling:
1198             shouldEnable = frame->selection().isRange();
1199             break;
1200         case ContextMenuItemTagPaste:
1201             shouldEnable = frame->editor().canDHTMLPaste() || frame->editor().canPaste();
1202             break;
1203 #if PLATFORM(GTK)
1204         case ContextMenuItemTagDelete:
1205             shouldEnable = frame->editor().canDelete();
1206             break;
1207         case ContextMenuItemTagInputMethods:
1208         case ContextMenuItemTagUnicode:
1209         case ContextMenuItemTagUnicodeInsertLRMMark:
1210         case ContextMenuItemTagUnicodeInsertRLMMark:
1211         case ContextMenuItemTagUnicodeInsertLREMark:
1212         case ContextMenuItemTagUnicodeInsertRLEMark:
1213         case ContextMenuItemTagUnicodeInsertLROMark:
1214         case ContextMenuItemTagUnicodeInsertRLOMark:
1215         case ContextMenuItemTagUnicodeInsertPDFMark:
1216         case ContextMenuItemTagUnicodeInsertZWSMark:
1217         case ContextMenuItemTagUnicodeInsertZWJMark:
1218         case ContextMenuItemTagUnicodeInsertZWNJMark:
1219             shouldEnable = true;
1220             break;
1221 #endif
1222 #if PLATFORM(GTK) || PLATFORM(EFL)
1223         case ContextMenuItemTagSelectAll:
1224             shouldEnable = true;
1225             break;
1226 #endif
1227         case ContextMenuItemTagUnderline: {
1228             shouldCheck = frame->editor().selectionHasStyle(CSSPropertyWebkitTextDecorationsInEffect, "underline") != FalseTriState;
1229             shouldEnable = frame->editor().canEditRichly();
1230             break;
1231         }
1232         case ContextMenuItemTagLookUpInDictionary:
1233             shouldEnable = frame->selection().isRange();
1234             break;
1235         case ContextMenuItemTagCheckGrammarWithSpelling:
1236             if (frame->editor().isGrammarCheckingEnabled())
1237                 shouldCheck = true;
1238             shouldEnable = true;
1239             break;
1240         case ContextMenuItemTagItalic: {
1241             shouldCheck = frame->editor().selectionHasStyle(CSSPropertyFontStyle, "italic") != FalseTriState;
1242             shouldEnable = frame->editor().canEditRichly();
1243             break;
1244         }
1245         case ContextMenuItemTagBold: {
1246             shouldCheck = frame->editor().selectionHasStyle(CSSPropertyFontWeight, "bold") != FalseTriState;
1247             shouldEnable = frame->editor().canEditRichly();
1248             break;
1249         }
1250         case ContextMenuItemTagOutline:
1251             shouldEnable = false;
1252             break;
1253         case ContextMenuItemTagShowSpellingPanel:
1254             if (frame->editor().spellingPanelIsShowing())
1255                 item.setTitle(contextMenuItemTagShowSpellingPanel(false));
1256             else
1257                 item.setTitle(contextMenuItemTagShowSpellingPanel(true));
1258             shouldEnable = frame->editor().canEdit();
1259             break;
1260         case ContextMenuItemTagNoGuessesFound:
1261             shouldEnable = false;
1262             break;
1263         case ContextMenuItemTagCheckSpellingWhileTyping:
1264             shouldCheck = frame->editor().isContinuousSpellCheckingEnabled();
1265             break;
1266 #if PLATFORM(COCOA)
1267         case ContextMenuItemTagSubstitutionsMenu:
1268         case ContextMenuItemTagTransformationsMenu:
1269             break;
1270         case ContextMenuItemTagShowSubstitutions:
1271             if (frame->editor().substitutionsPanelIsShowing())
1272                 item.setTitle(contextMenuItemTagShowSubstitutions(false));
1273             else
1274                 item.setTitle(contextMenuItemTagShowSubstitutions(true));
1275             shouldEnable = frame->editor().canEdit();
1276             break;
1277         case ContextMenuItemTagMakeUpperCase:
1278         case ContextMenuItemTagMakeLowerCase:
1279         case ContextMenuItemTagCapitalize:
1280         case ContextMenuItemTagChangeBack:
1281             shouldEnable = frame->editor().canEdit();
1282             break;
1283         case ContextMenuItemTagCorrectSpellingAutomatically:
1284             shouldCheck = frame->editor().isAutomaticSpellingCorrectionEnabled();
1285             break;
1286         case ContextMenuItemTagSmartCopyPaste:
1287             shouldCheck = frame->editor().smartInsertDeleteEnabled();
1288             break;
1289         case ContextMenuItemTagSmartQuotes:
1290             shouldCheck = frame->editor().isAutomaticQuoteSubstitutionEnabled();
1291             break;
1292         case ContextMenuItemTagSmartDashes:
1293             shouldCheck = frame->editor().isAutomaticDashSubstitutionEnabled();
1294             break;
1295         case ContextMenuItemTagSmartLinks:
1296             shouldCheck = frame->editor().isAutomaticLinkDetectionEnabled();
1297             break;
1298         case ContextMenuItemTagTextReplacement:
1299             shouldCheck = frame->editor().isAutomaticTextReplacementEnabled();
1300             break;
1301         case ContextMenuItemTagStopSpeaking:
1302             shouldEnable = m_client.isSpeaking();
1303             break;
1304 #else // PLATFORM(COCOA) ends here
1305         case ContextMenuItemTagStopSpeaking:
1306             break;
1307 #endif
1308 #if PLATFORM(GTK)
1309         case ContextMenuItemTagGoBack:
1310             shouldEnable = frame->page() && frame->page()->backForward().canGoBackOrForward(-1);
1311             break;
1312         case ContextMenuItemTagGoForward:
1313             shouldEnable = frame->page() && frame->page()->backForward().canGoBackOrForward(1);
1314             break;
1315         case ContextMenuItemTagStop:
1316             shouldEnable = frame->loader().documentLoader()->isLoadingInAPISense();
1317             break;
1318         case ContextMenuItemTagReload:
1319             shouldEnable = !frame->loader().documentLoader()->isLoadingInAPISense();
1320             break;
1321         case ContextMenuItemTagFontMenu:
1322             shouldEnable = frame->editor().canEditRichly();
1323             break;
1324 #else
1325         case ContextMenuItemTagGoBack:
1326         case ContextMenuItemTagGoForward:
1327         case ContextMenuItemTagStop:
1328         case ContextMenuItemTagReload:
1329         case ContextMenuItemTagFontMenu:
1330 #endif
1331         case ContextMenuItemTagNoAction:
1332         case ContextMenuItemTagOpenLinkInNewWindow:
1333         case ContextMenuItemTagOpenLinkInThisWindow:
1334         case ContextMenuItemTagDownloadLinkToDisk:
1335         case ContextMenuItemTagCopyLinkToClipboard:
1336         case ContextMenuItemTagOpenImageInNewWindow:
1337         case ContextMenuItemTagDownloadImageToDisk:
1338         case ContextMenuItemTagCopyImageToClipboard:
1339 #if PLATFORM(GTK) || PLATFORM(EFL)
1340         case ContextMenuItemTagCopyImageUrlToClipboard:
1341 #endif
1342             break;
1343         case ContextMenuItemTagOpenMediaInNewWindow:
1344             if (m_context.hitTestResult().mediaIsVideo())
1345                 item.setTitle(contextMenuItemTagOpenVideoInNewWindow());
1346             else
1347                 item.setTitle(contextMenuItemTagOpenAudioInNewWindow());
1348             break;
1349         case ContextMenuItemTagDownloadMediaToDisk:
1350             if (m_context.hitTestResult().mediaIsVideo())
1351                 item.setTitle(contextMenuItemTagDownloadVideoToDisk());
1352             else
1353                 item.setTitle(contextMenuItemTagDownloadAudioToDisk());
1354             break;
1355         case ContextMenuItemTagCopyMediaLinkToClipboard:
1356             if (m_context.hitTestResult().mediaIsVideo())
1357                 item.setTitle(contextMenuItemTagCopyVideoLinkToClipboard());
1358             else
1359                 item.setTitle(contextMenuItemTagCopyAudioLinkToClipboard());
1360             break;
1361         case ContextMenuItemTagToggleMediaControls:
1362 #if SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS
1363             item.setTitle(m_context.hitTestResult().mediaControlsEnabled() ? contextMenuItemTagHideMediaControls() : contextMenuItemTagShowMediaControls());
1364 #else
1365             shouldCheck = m_context.hitTestResult().mediaControlsEnabled();
1366 #endif
1367             break;
1368         case ContextMenuItemTagToggleMediaLoop:
1369             shouldCheck = m_context.hitTestResult().mediaLoopEnabled();
1370             break;
1371         case ContextMenuItemTagToggleVideoFullscreen:
1372 #if SUPPORTS_TOGGLE_VIDEO_FULLSCREEN
1373             item.setTitle(m_context.hitTestResult().mediaIsInFullscreen() ? contextMenuItemTagExitVideoFullscreen() : contextMenuItemTagEnterVideoFullscreen());
1374             break;
1375 #endif
1376         case ContextMenuItemTagEnterVideoFullscreen:
1377             shouldEnable = m_context.hitTestResult().mediaSupportsFullscreen();
1378             break;
1379         case ContextMenuItemTagOpenFrameInNewWindow:
1380         case ContextMenuItemTagSpellingGuess:
1381         case ContextMenuItemTagOther:
1382         case ContextMenuItemTagSearchInSpotlight:
1383         case ContextMenuItemTagSearchWeb:
1384         case ContextMenuItemTagOpenWithDefaultApplication:
1385         case ContextMenuItemPDFActualSize:
1386         case ContextMenuItemPDFZoomIn:
1387         case ContextMenuItemPDFZoomOut:
1388         case ContextMenuItemPDFAutoSize:
1389         case ContextMenuItemPDFSinglePage:
1390         case ContextMenuItemPDFFacingPages:
1391         case ContextMenuItemPDFContinuous:
1392         case ContextMenuItemPDFNextPage:
1393         case ContextMenuItemPDFPreviousPage:
1394         case ContextMenuItemTagOpenLink:
1395         case ContextMenuItemTagIgnoreGrammar:
1396         case ContextMenuItemTagSpellingMenu:
1397         case ContextMenuItemTagShowFonts:
1398         case ContextMenuItemTagStyles:
1399         case ContextMenuItemTagShowColors:
1400         case ContextMenuItemTagSpeechMenu:
1401         case ContextMenuItemTagStartSpeaking:
1402         case ContextMenuItemTagWritingDirectionMenu:
1403         case ContextMenuItemTagTextDirectionMenu:
1404         case ContextMenuItemTagPDFSinglePageScrolling:
1405         case ContextMenuItemTagPDFFacingPagesScrolling:
1406         case ContextMenuItemTagInspectElement:
1407         case ContextMenuItemBaseCustomTag:
1408         case ContextMenuItemCustomTagNoAction:
1409         case ContextMenuItemLastCustomTag:
1410         case ContextMenuItemBaseApplicationTag:
1411         case ContextMenuItemTagDictationAlternative:
1412         case ContextMenuItemTagShareMenu:
1413             break;
1414         case ContextMenuItemTagMediaPlayPause:
1415             if (m_context.hitTestResult().mediaPlaying())
1416                 item.setTitle(contextMenuItemTagMediaPause());
1417             else
1418                 item.setTitle(contextMenuItemTagMediaPlay());
1419             break;
1420         case ContextMenuItemTagMediaMute:
1421             shouldEnable = m_context.hitTestResult().mediaHasAudio();
1422             shouldCheck = shouldEnable &&  m_context.hitTestResult().mediaMuted();
1423             break;
1424     }
1425
1426     item.setChecked(shouldCheck);
1427     item.setEnabled(shouldEnable);
1428 }
1429
1430 #if USE(ACCESSIBILITY_CONTEXT_MENUS)
1431 void ContextMenuController::showContextMenuAt(Frame* frame, const IntPoint& clickPoint)
1432 {
1433     clearContextMenu();
1434     
1435     // Simulate a click in the middle of the accessibility object.
1436     PlatformMouseEvent mouseEvent(clickPoint, clickPoint, RightButton, PlatformEvent::MousePressed, 1, false, false, false, false, currentTime());
1437     frame->eventHandler().handleMousePressEvent(mouseEvent);
1438     bool handled = frame->eventHandler().sendContextMenuEvent(mouseEvent);
1439     if (handled)
1440         m_client.showContextMenu();
1441 }
1442 #endif
1443
1444 #if ENABLE(SERVICE_CONTROLS)
1445 void ContextMenuController::showImageControlsMenu(Event* event)
1446 {
1447     clearContextMenu();
1448     handleContextMenuEvent(event);
1449     m_client.showContextMenu();
1450 }
1451 #endif
1452
1453 } // namespace WebCore
1454
1455 #endif // ENABLE(CONTEXT_MENUS)