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