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