Move URL from WebCore to WTF
[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 "Frame.h"
47 #include "FrameLoadRequest.h"
48 #include "FrameLoader.h"
49 #include "FrameLoaderClient.h"
50 #include "FrameSelection.h"
51 #include "HTMLFormControlElement.h"
52 #include "HTMLFormElement.h"
53 #include "HitTestRequest.h"
54 #include "HitTestResult.h"
55 #include "InspectorController.h"
56 #include "LocalizedStrings.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/WallTime.h>
72 #include <wtf/unicode/CharacterNames.h>
73
74
75 namespace WebCore {
76 using namespace WTF;
77 using namespace Unicode;
78
79 ContextMenuController::ContextMenuController(Page& page, ContextMenuClient& client)
80     : m_page(page)
81     , m_client(client)
82 {
83 }
84
85 ContextMenuController::~ContextMenuController()
86 {
87     m_client.contextMenuDestroyed();
88 }
89
90 void ContextMenuController::clearContextMenu()
91 {
92     m_contextMenu = nullptr;
93     if (m_menuProvider)
94         m_menuProvider->contextMenuCleared();
95     m_menuProvider = nullptr;
96 }
97
98 void ContextMenuController::handleContextMenuEvent(Event& event)
99 {
100     m_contextMenu = maybeCreateContextMenu(event);
101     if (!m_contextMenu)
102         return;
103
104     populate();
105
106     showContextMenu(event);
107 }
108
109 static std::unique_ptr<ContextMenuItem> separatorItem()
110 {
111     return std::unique_ptr<ContextMenuItem>(new ContextMenuItem(SeparatorType, ContextMenuItemTagNoAction, String()));
112 }
113
114 void ContextMenuController::showContextMenu(Event& event, ContextMenuProvider& provider)
115 {
116     m_menuProvider = &provider;
117
118     m_contextMenu = maybeCreateContextMenu(event);
119     if (!m_contextMenu) {
120         clearContextMenu();
121         return;
122     }
123
124     provider.populateContextMenu(m_contextMenu.get());
125     if (m_context.hitTestResult().isSelected()) {
126         appendItem(*separatorItem(), m_contextMenu.get());
127         populate();
128     }
129     showContextMenu(event);
130 }
131
132 #if ENABLE(SERVICE_CONTROLS)
133
134 static Image* imageFromImageElementNode(Node& node)
135 {
136     auto* renderer = node.renderer();
137     if (!is<RenderImage>(renderer))
138         return nullptr;
139     auto* image = downcast<RenderImage>(*renderer).cachedImage();
140     if (!image || image->errorOccurred())
141         return nullptr;
142     return image->imageForRenderer(renderer);
143 }
144
145 #endif
146
147 std::unique_ptr<ContextMenu> ContextMenuController::maybeCreateContextMenu(Event& event)
148 {
149     if (!is<MouseEvent>(event))
150         return nullptr;
151
152     auto& mouseEvent = downcast<MouseEvent>(event);
153     if (!is<Node>(mouseEvent.target()))
154         return nullptr;
155     auto& node = downcast<Node>(*mouseEvent.target());
156     auto* frame = node.document().frame();
157     if (!frame)
158         return nullptr;
159
160     auto result = frame->eventHandler().hitTestResultAtPoint(mouseEvent.absoluteLocation());
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 (auto* 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::make_unique<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 frameLoadRequest { *frame.document(), frame.document()->securityOrigin(), ResourceRequest(urlToLoad, frame.loader().outgoingReferrer()), { }, LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Suppress, shouldOpenExternalURLsPolicy, InitiatedByMainFrame::Unknown };
194
195     Page* newPage = oldPage->chrome().createWindow(frame, frameLoadRequest, { }, { *frame.document(), frameLoadRequest.resourceRequest(), frameLoadRequest.initiatedByMainFrame() });
196     if (!newPage)
197         return;
198     newPage->chrome().show();
199     newPage->mainFrame().loader().loadFrameRequest(WTFMove(frameLoadRequest), nullptr, { });
200 }
201
202 #if PLATFORM(GTK)
203
204 static void insertUnicodeCharacter(UChar character, Frame& frame)
205 {
206     String text(&character, 1);
207     if (!frame.editor().shouldInsertText(text, frame.selection().toNormalizedRange().get(), EditorInsertAction::Typed))
208         return;
209
210     ASSERT(frame.document());
211     TypingCommand::insertText(*frame.document(), text, 0, TypingCommand::TextCompositionNone);
212 }
213
214 #endif
215
216 void ContextMenuController::contextMenuItemSelected(ContextMenuAction action, const String& title)
217 {
218     if (action >= ContextMenuItemBaseCustomTag) {
219         ASSERT(m_menuProvider);
220         m_menuProvider->contextMenuItemSelected(action, title);
221         return;
222     }
223
224     Frame* frame = m_context.hitTestResult().innerNonSharedNode()->document().frame();
225     if (!frame)
226         return;
227
228     Ref<Frame> protector(*frame);
229
230     switch (action) {
231     case ContextMenuItemTagOpenLinkInNewWindow:
232         openNewWindow(m_context.hitTestResult().absoluteLinkURL(), *frame, ShouldOpenExternalURLsPolicy::ShouldAllowExternalSchemes);
233         break;
234     case ContextMenuItemTagDownloadLinkToDisk:
235         // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709)
236         m_client.downloadURL(m_context.hitTestResult().absoluteLinkURL());
237         break;
238     case ContextMenuItemTagCopyLinkToClipboard:
239         frame->editor().copyURL(m_context.hitTestResult().absoluteLinkURL(), m_context.hitTestResult().textContent());
240         break;
241     case ContextMenuItemTagOpenImageInNewWindow:
242         openNewWindow(m_context.hitTestResult().absoluteImageURL(), *frame, ShouldOpenExternalURLsPolicy::ShouldNotAllow);
243         break;
244     case ContextMenuItemTagDownloadImageToDisk:
245         // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709)
246         m_client.downloadURL(m_context.hitTestResult().absoluteImageURL());
247         break;
248     case ContextMenuItemTagCopyImageToClipboard:
249         // FIXME: The Pasteboard class is not written yet
250         // For now, call into the client. This is temporary!
251         frame->editor().copyImage(m_context.hitTestResult());
252         break;
253 #if PLATFORM(GTK)
254     case ContextMenuItemTagCopyImageUrlToClipboard:
255         frame->editor().copyURL(m_context.hitTestResult().absoluteImageURL(), m_context.hitTestResult().textContent());
256         break;
257 #endif
258     case ContextMenuItemTagOpenMediaInNewWindow:
259         openNewWindow(m_context.hitTestResult().absoluteMediaURL(), *frame, ShouldOpenExternalURLsPolicy::ShouldNotAllow);
260         break;
261     case ContextMenuItemTagDownloadMediaToDisk:
262         // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709)
263         m_client.downloadURL(m_context.hitTestResult().absoluteMediaURL());
264         break;
265     case ContextMenuItemTagCopyMediaLinkToClipboard:
266         frame->editor().copyURL(m_context.hitTestResult().absoluteMediaURL(), m_context.hitTestResult().textContent());
267         break;
268     case ContextMenuItemTagToggleMediaControls:
269         m_context.hitTestResult().toggleMediaControlsDisplay();
270         break;
271     case ContextMenuItemTagToggleMediaLoop:
272         m_context.hitTestResult().toggleMediaLoopPlayback();
273         break;
274     case ContextMenuItemTagToggleVideoFullscreen:
275         m_context.hitTestResult().toggleMediaFullscreenState();
276         break;
277     case ContextMenuItemTagEnterVideoFullscreen:
278         m_context.hitTestResult().enterFullscreenForVideo();
279         break;
280     case ContextMenuItemTagMediaPlayPause:
281         m_context.hitTestResult().toggleMediaPlayState();
282         break;
283     case ContextMenuItemTagMediaMute:
284         m_context.hitTestResult().toggleMediaMuteState();
285         break;
286     case ContextMenuItemTagToggleVideoEnhancedFullscreen:
287         m_context.hitTestResult().toggleEnhancedFullscreenForVideo();
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)
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(title, selection.toNormalizedRange().get(), EditorInsertAction::Pasted)) {
363             OptionSet<ReplaceSelectionCommand::CommandOption> 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.add(ReplaceSelectionCommand::SelectReplacement);
373             }
374
375             Document* document = frame->document();
376             ASSERT(document);
377             auto command = ReplaceSelectionCommand::create(*document, createFragmentFromMarkup(*document, title, emptyString()), replaceOptions);
378             command->apply();
379             frame->selection().revealSelection(SelectionRevealMode::Reveal, 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             ResourceRequest resourceRequest { m_context.hitTestResult().absoluteLinkURL(), frame->loader().outgoingReferrer() };
399             FrameLoadRequest frameLoadRequest { *frame->document(), frame->document()->securityOrigin(), resourceRequest, { }, LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Suppress, targetFrame->isMainFrame() ? ShouldOpenExternalURLsPolicy::ShouldAllow : ShouldOpenExternalURLsPolicy::ShouldNotAllow, InitiatedByMainFrame::Unknown };
400             targetFrame->loader().loadFrameRequest(WTFMove(frameLoadRequest), nullptr,  { });
401         } else
402             openNewWindow(m_context.hitTestResult().absoluteLinkURL(), *frame, ShouldOpenExternalURLsPolicy::ShouldAllow);
403         break;
404     case ContextMenuItemTagBold:
405         frame->editor().command("ToggleBold").execute();
406         break;
407     case ContextMenuItemTagItalic:
408         frame->editor().command("ToggleItalic").execute();
409         break;
410     case ContextMenuItemTagUnderline:
411         frame->editor().toggleUnderline();
412         break;
413     case ContextMenuItemTagOutline:
414         // We actually never enable this because CSS does not have a way to specify an outline font,
415         // which may make this difficult to implement. Maybe a special case of text-shadow?
416         break;
417     case ContextMenuItemTagStartSpeaking: {
418         RefPtr<Range> selectedRange = frame->selection().toNormalizedRange();
419         if (!selectedRange || selectedRange->collapsed()) {
420             auto& document = m_context.hitTestResult().innerNonSharedNode()->document();
421             selectedRange = document.createRange();
422             if (auto* element = document.documentElement())
423                 selectedRange->selectNode(*element);
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(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)
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(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)
780     ContextMenuItem ToggleVideoEnhancedFullscreen(ActionType, ContextMenuItemTagToggleVideoEnhancedFullscreen, contextMenuItemTagEnterVideoEnhancedFullscreen());
781 #endif
782 #if PLATFORM(COCOA)
783     ContextMenuItem SearchSpotlightItem(ActionType, ContextMenuItemTagSearchInSpotlight, 
784         contextMenuItemTagSearchInSpotlight());
785 #endif
786 #if !PLATFORM(GTK)
787     ContextMenuItem SearchWebItem(ActionType, ContextMenuItemTagSearchWeb, contextMenuItemTagSearchWeb());
788 #endif
789     ContextMenuItem CopyItem(ActionType, ContextMenuItemTagCopy, contextMenuItemTagCopy());
790     ContextMenuItem BackItem(ActionType, ContextMenuItemTagGoBack, contextMenuItemTagGoBack());
791     ContextMenuItem ForwardItem(ActionType, ContextMenuItemTagGoForward,  contextMenuItemTagGoForward());
792     ContextMenuItem StopItem(ActionType, ContextMenuItemTagStop, contextMenuItemTagStop());
793     ContextMenuItem ReloadItem(ActionType, ContextMenuItemTagReload, contextMenuItemTagReload());
794     ContextMenuItem OpenFrameItem(ActionType, ContextMenuItemTagOpenFrameInNewWindow, 
795         contextMenuItemTagOpenFrameInNewWindow());
796     ContextMenuItem NoGuessesItem(ActionType, ContextMenuItemTagNoGuessesFound, 
797         contextMenuItemTagNoGuessesFound());
798     ContextMenuItem IgnoreSpellingItem(ActionType, ContextMenuItemTagIgnoreSpelling, 
799         contextMenuItemTagIgnoreSpelling());
800     ContextMenuItem LearnSpellingItem(ActionType, ContextMenuItemTagLearnSpelling, 
801         contextMenuItemTagLearnSpelling());
802     ContextMenuItem IgnoreGrammarItem(ActionType, ContextMenuItemTagIgnoreGrammar, 
803         contextMenuItemTagIgnoreGrammar());
804     ContextMenuItem CutItem(ActionType, ContextMenuItemTagCut, contextMenuItemTagCut());
805     ContextMenuItem PasteItem(ActionType, ContextMenuItemTagPaste, contextMenuItemTagPaste());
806 #if PLATFORM(GTK)
807     ContextMenuItem DeleteItem(ActionType, ContextMenuItemTagDelete, contextMenuItemTagDelete());
808 #endif
809 #if PLATFORM(GTK)
810     ContextMenuItem SelectAllItem(ActionType, ContextMenuItemTagSelectAll, contextMenuItemTagSelectAll());
811 #endif
812
813 #if PLATFORM(GTK) || PLATFORM(WIN)
814     ContextMenuItem ShareMenuItem;
815 #else
816     ContextMenuItem ShareMenuItem(SubmenuType, ContextMenuItemTagShareMenu, emptyString());
817 #endif
818
819     Node* node = m_context.hitTestResult().innerNonSharedNode();
820     if (!node)
821         return;
822 #if PLATFORM(GTK)
823     if (!m_context.hitTestResult().isContentEditable() && is<HTMLFormControlElement>(*node))
824         return;
825 #endif
826     Frame* frame = node->document().frame();
827     if (!frame)
828         return;
829
830 #if ENABLE(SERVICE_CONTROLS)
831     // The default image control menu gets populated solely by the platform.
832     if (m_context.controlledImage())
833         return;
834 #endif
835
836     if (!m_context.hitTestResult().isContentEditable()) {
837         String selectedString = m_context.hitTestResult().selectedText();
838         m_context.setSelectedText(selectedString);
839
840         FrameLoader& loader = frame->loader();
841         URL linkURL = m_context.hitTestResult().absoluteLinkURL();
842         if (!linkURL.isEmpty()) {
843             if (loader.client().canHandleRequest(ResourceRequest(linkURL))) {
844                 appendItem(OpenLinkItem, m_contextMenu.get());
845                 appendItem(OpenLinkInNewWindowItem, m_contextMenu.get());
846                 appendItem(DownloadFileItem, m_contextMenu.get());
847             }
848             appendItem(CopyLinkItem, m_contextMenu.get());
849         }
850
851         URL imageURL = m_context.hitTestResult().absoluteImageURL();
852         if (!imageURL.isEmpty()) {
853             if (!linkURL.isEmpty())
854                 appendItem(*separatorItem(), m_contextMenu.get());
855
856             appendItem(OpenImageInNewWindowItem, m_contextMenu.get());
857             appendItem(DownloadImageItem, m_contextMenu.get());
858             if (imageURL.isLocalFile() || m_context.hitTestResult().image())
859                 appendItem(CopyImageItem, m_contextMenu.get());
860 #if PLATFORM(GTK)
861             appendItem(CopyImageUrlItem, m_contextMenu.get());
862 #endif
863         }
864
865         URL mediaURL = m_context.hitTestResult().absoluteMediaURL();
866         if (!mediaURL.isEmpty()) {
867             if (!linkURL.isEmpty() || !imageURL.isEmpty())
868                 appendItem(*separatorItem(), m_contextMenu.get());
869
870             appendItem(MediaPlayPause, m_contextMenu.get());
871             appendItem(MediaMute, m_contextMenu.get());
872             appendItem(ToggleMediaControls, m_contextMenu.get());
873             appendItem(ToggleMediaLoop, m_contextMenu.get());
874 #if SUPPORTS_TOGGLE_VIDEO_FULLSCREEN
875             appendItem(ToggleVideoFullscreen, m_contextMenu.get());
876 #else
877             appendItem(EnterVideoFullscreen, m_contextMenu.get());
878 #endif
879 #if PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)
880             appendItem(ToggleVideoEnhancedFullscreen, m_contextMenu.get());
881 #endif
882             appendItem(*separatorItem(), m_contextMenu.get());
883             appendItem(CopyMediaLinkItem, m_contextMenu.get());
884             appendItem(OpenMediaInNewWindowItem, m_contextMenu.get());
885             if (m_context.hitTestResult().isDownloadableMedia() && loader.client().canHandleRequest(ResourceRequest(mediaURL)))
886                 appendItem(DownloadMediaItem, m_contextMenu.get());
887         }
888
889         if (imageURL.isEmpty() && linkURL.isEmpty() && mediaURL.isEmpty()) {
890             if (m_context.hitTestResult().isSelected()) {
891                 if (!selectedString.isEmpty()) {
892 #if PLATFORM(COCOA)
893                     ContextMenuItem LookUpInDictionaryItem(ActionType, ContextMenuItemTagLookUpInDictionary, contextMenuItemTagLookUpInDictionary(selectedString));
894
895                     appendItem(LookUpInDictionaryItem, m_contextMenu.get());
896 #endif
897
898 #if !PLATFORM(GTK)
899                     appendItem(SearchWebItem, m_contextMenu.get());
900                     appendItem(*separatorItem(), m_contextMenu.get());
901 #endif
902                 }
903
904                 appendItem(CopyItem, m_contextMenu.get());
905 #if PLATFORM(COCOA)
906                 appendItem(*separatorItem(), m_contextMenu.get());
907
908                 appendItem(ShareMenuItem, m_contextMenu.get());
909                 appendItem(*separatorItem(), m_contextMenu.get());
910
911                 ContextMenuItem SpeechMenuItem(SubmenuType, ContextMenuItemTagSpeechMenu, contextMenuItemTagSpeechMenu());
912                 createAndAppendSpeechSubMenu(SpeechMenuItem);
913                 appendItem(SpeechMenuItem, m_contextMenu.get());
914 #endif                
915             } else {
916                 if (!(frame->page() && (frame->page()->inspectorController().inspectionLevel() > 0 || frame->page()->inspectorController().hasRemoteFrontend()))) {
917
918                 // In GTK+ unavailable items are not hidden but insensitive.
919 #if PLATFORM(GTK)
920                 appendItem(BackItem, m_contextMenu.get());
921                 appendItem(ForwardItem, m_contextMenu.get());
922                 appendItem(StopItem, m_contextMenu.get());
923                 appendItem(ReloadItem, m_contextMenu.get());
924 #else
925                 if (frame->page() && frame->page()->backForward().canGoBackOrForward(-1))
926                     appendItem(BackItem, m_contextMenu.get());
927
928                 if (frame->page() && frame->page()->backForward().canGoBackOrForward(1))
929                     appendItem(ForwardItem, m_contextMenu.get());
930
931                 // use isLoadingInAPISense rather than isLoading because Stop/Reload are
932                 // intended to match WebKit's API, not WebCore's internal notion of loading status
933                 if (loader.documentLoader()->isLoadingInAPISense())
934                     appendItem(StopItem, m_contextMenu.get());
935                 else
936                     appendItem(ReloadItem, m_contextMenu.get());
937 #endif
938                 }
939
940                 if (frame->page() && !frame->isMainFrame())
941                     appendItem(OpenFrameItem, m_contextMenu.get());
942
943                 if (!ShareMenuItem.isNull()) {
944                     appendItem(*separatorItem(), m_contextMenu.get());
945                     appendItem(ShareMenuItem, m_contextMenu.get());
946                 }
947             }
948         } else if (!ShareMenuItem.isNull()) {
949             appendItem(*separatorItem(), m_contextMenu.get());
950             appendItem(ShareMenuItem, m_contextMenu.get());
951         }
952     } else { // Make an editing context menu
953         bool inPasswordField = frame->selection().selection().isInPasswordField();
954         if (!inPasswordField) {
955             bool haveContextMenuItemsForMisspellingOrGrammer = false;
956             bool spellCheckingEnabled = frame->editor().isSpellCheckingEnabledFor(node);
957             if (spellCheckingEnabled) {
958                 // Consider adding spelling-related or grammar-related context menu items (never both, since a single selected range
959                 // is never considered a misspelling and bad grammar at the same time)
960                 bool misspelling;
961                 bool badGrammar;
962                 Vector<String> guesses = frame->editor().guessesForMisspelledOrUngrammatical(misspelling, badGrammar);
963                 if (misspelling || badGrammar) {
964                     if (guesses.isEmpty()) {
965                         // If there's bad grammar but no suggestions (e.g., repeated word), just leave off the suggestions
966                         // list and trailing separator rather than adding a "No Guesses Found" item (matches AppKit)
967                         if (misspelling) {
968                             appendItem(NoGuessesItem, m_contextMenu.get());
969                             appendItem(*separatorItem(), m_contextMenu.get());
970                         }
971                     } else {
972                         for (const auto& guess : guesses) {
973                             if (!guess.isEmpty()) {
974                                 ContextMenuItem item(ActionType, ContextMenuItemTagSpellingGuess, guess);
975                                 appendItem(item, m_contextMenu.get());
976                             }
977                         }
978                         appendItem(*separatorItem(), m_contextMenu.get());
979                     }
980                     if (misspelling) {
981                         appendItem(IgnoreSpellingItem, m_contextMenu.get());
982                         appendItem(LearnSpellingItem, m_contextMenu.get());
983                     } else
984                         appendItem(IgnoreGrammarItem, m_contextMenu.get());
985                     appendItem(*separatorItem(), m_contextMenu.get());
986                     haveContextMenuItemsForMisspellingOrGrammer = true;
987 #if PLATFORM(COCOA)
988                 } else {
989                     // If the string was autocorrected, generate a contextual menu item allowing it to be changed back.
990                     String replacedString = m_context.hitTestResult().replacedString();
991                     if (!replacedString.isEmpty()) {
992                         ContextMenuItem item(ActionType, ContextMenuItemTagChangeBack, contextMenuItemTagChangeBack(replacedString));
993                         appendItem(item, m_contextMenu.get());
994                         appendItem(*separatorItem(), m_contextMenu.get());
995                         haveContextMenuItemsForMisspellingOrGrammer = true;
996                     }
997 #endif
998                 }
999             }
1000
1001             if (!haveContextMenuItemsForMisspellingOrGrammer) {
1002                 // Spelling and grammar checking is mutually exclusive with dictation alternatives.
1003                 Vector<String> dictationAlternatives = m_context.hitTestResult().dictationAlternatives();
1004                 if (!dictationAlternatives.isEmpty()) {
1005                     for (auto& alternative : dictationAlternatives) {
1006                         ContextMenuItem item(ActionType, ContextMenuItemTagDictationAlternative, alternative);
1007                         appendItem(item, m_contextMenu.get());
1008                     }
1009                     appendItem(*separatorItem(), m_contextMenu.get());
1010                 }
1011             }
1012         }
1013
1014         FrameLoader& loader = frame->loader();
1015         URL linkURL = m_context.hitTestResult().absoluteLinkURL();
1016         if (!linkURL.isEmpty()) {
1017             if (loader.client().canHandleRequest(ResourceRequest(linkURL))) {
1018                 appendItem(OpenLinkItem, m_contextMenu.get());
1019                 appendItem(OpenLinkInNewWindowItem, m_contextMenu.get());
1020                 appendItem(DownloadFileItem, m_contextMenu.get());
1021             }
1022             appendItem(CopyLinkItem, m_contextMenu.get());
1023             appendItem(*separatorItem(), m_contextMenu.get());
1024         }
1025
1026         String selectedText = m_context.hitTestResult().selectedText();
1027         if (m_context.hitTestResult().isSelected() && !inPasswordField && !selectedText.isEmpty()) {
1028 #if PLATFORM(COCOA)
1029             ContextMenuItem LookUpInDictionaryItem(ActionType, ContextMenuItemTagLookUpInDictionary, contextMenuItemTagLookUpInDictionary(selectedText));
1030
1031             appendItem(LookUpInDictionaryItem, m_contextMenu.get());
1032 #endif
1033
1034 #if !PLATFORM(GTK)
1035             appendItem(SearchWebItem, m_contextMenu.get());
1036             appendItem(*separatorItem(), m_contextMenu.get());
1037 #endif
1038         }
1039
1040         appendItem(CutItem, m_contextMenu.get());
1041         appendItem(CopyItem, m_contextMenu.get());
1042         appendItem(PasteItem, m_contextMenu.get());
1043 #if PLATFORM(GTK)
1044         appendItem(DeleteItem, m_contextMenu.get());
1045         appendItem(*separatorItem(), m_contextMenu.get());
1046 #endif
1047 #if PLATFORM(GTK)
1048         appendItem(SelectAllItem, m_contextMenu.get());
1049 #endif
1050
1051         if (!inPasswordField) {
1052 #if !PLATFORM(GTK)
1053             appendItem(*separatorItem(), m_contextMenu.get());
1054             ContextMenuItem SpellingAndGrammarMenuItem(SubmenuType, ContextMenuItemTagSpellingMenu, 
1055                 contextMenuItemTagSpellingMenu());
1056             createAndAppendSpellingAndGrammarSubMenu(SpellingAndGrammarMenuItem);
1057             appendItem(SpellingAndGrammarMenuItem, m_contextMenu.get());
1058 #endif
1059 #if PLATFORM(COCOA)
1060             ContextMenuItem substitutionsMenuItem(SubmenuType, ContextMenuItemTagSubstitutionsMenu, 
1061                 contextMenuItemTagSubstitutionsMenu());
1062             createAndAppendSubstitutionsSubMenu(substitutionsMenuItem);
1063             appendItem(substitutionsMenuItem, m_contextMenu.get());
1064             ContextMenuItem transformationsMenuItem(SubmenuType, ContextMenuItemTagTransformationsMenu, 
1065                 contextMenuItemTagTransformationsMenu());
1066             createAndAppendTransformationsSubMenu(transformationsMenuItem);
1067             appendItem(transformationsMenuItem, m_contextMenu.get());
1068 #endif
1069 #if PLATFORM(GTK)
1070             bool shouldShowFontMenu = frame->editor().canEditRichly();
1071 #else
1072             bool shouldShowFontMenu = true;
1073 #endif
1074             if (shouldShowFontMenu) {
1075                 ContextMenuItem FontMenuItem(SubmenuType, ContextMenuItemTagFontMenu, 
1076                     contextMenuItemTagFontMenu());
1077                 createAndAppendFontSubMenu(FontMenuItem);
1078                 appendItem(FontMenuItem, m_contextMenu.get());
1079             }
1080 #if PLATFORM(COCOA)
1081             ContextMenuItem SpeechMenuItem(SubmenuType, ContextMenuItemTagSpeechMenu, contextMenuItemTagSpeechMenu());
1082             createAndAppendSpeechSubMenu(SpeechMenuItem);
1083             appendItem(SpeechMenuItem, m_contextMenu.get());
1084 #endif
1085 #if PLATFORM(GTK)
1086             EditorClient* client = frame->editor().client();
1087             if (client && client->shouldShowUnicodeMenu()) {
1088                 ContextMenuItem UnicodeMenuItem(SubmenuType, ContextMenuItemTagUnicode, contextMenuItemTagUnicode());
1089                 createAndAppendUnicodeSubMenu(UnicodeMenuItem);
1090                 appendItem(*separatorItem(), m_contextMenu.get());
1091                 appendItem(UnicodeMenuItem, m_contextMenu.get());
1092             }
1093 #else
1094             ContextMenuItem WritingDirectionMenuItem(SubmenuType, ContextMenuItemTagWritingDirectionMenu, 
1095                 contextMenuItemTagWritingDirectionMenu());
1096             createAndAppendWritingDirectionSubMenu(WritingDirectionMenuItem);
1097             appendItem(WritingDirectionMenuItem, m_contextMenu.get());
1098             if (Page* page = frame->page()) {
1099                 bool includeTextDirectionSubmenu = page->settings().textDirectionSubmenuInclusionBehavior() == TextDirectionSubmenuAlwaysIncluded
1100                     || (page->settings().textDirectionSubmenuInclusionBehavior() == TextDirectionSubmenuAutomaticallyIncluded && frame->editor().hasBidiSelection());
1101                 if (includeTextDirectionSubmenu) {
1102                     ContextMenuItem TextDirectionMenuItem(SubmenuType, ContextMenuItemTagTextDirectionMenu, contextMenuItemTagTextDirectionMenu());
1103                     createAndAppendTextDirectionSubMenu(TextDirectionMenuItem);
1104                     appendItem(TextDirectionMenuItem, m_contextMenu.get());
1105                 }
1106             }
1107 #endif
1108         }
1109
1110         if (!ShareMenuItem.isNull()) {
1111             appendItem(*separatorItem(), m_contextMenu.get());
1112             appendItem(ShareMenuItem, m_contextMenu.get());
1113         }
1114     }
1115 }
1116
1117 void ContextMenuController::addInspectElementItem()
1118 {
1119     Node* node = m_context.hitTestResult().innerNonSharedNode();
1120     if (!node)
1121         return;
1122
1123     Frame* frame = node->document().frame();
1124     if (!frame)
1125         return;
1126
1127     Page* page = frame->page();
1128     if (!page)
1129         return;
1130
1131     ContextMenuItem InspectElementItem(ActionType, ContextMenuItemTagInspectElement, contextMenuItemTagInspectElement());
1132     if (m_contextMenu && !m_contextMenu->items().isEmpty())
1133         appendItem(*separatorItem(), m_contextMenu.get());
1134     appendItem(InspectElementItem, m_contextMenu.get());
1135 }
1136
1137 void ContextMenuController::checkOrEnableIfNeeded(ContextMenuItem& item) const
1138 {
1139     if (item.type() == SeparatorType)
1140         return;
1141     
1142     Frame* frame = m_context.hitTestResult().innerNonSharedNode()->document().frame();
1143     if (!frame)
1144         return;
1145
1146     // Custom items already have proper checked and enabled values.
1147     if (ContextMenuItemBaseCustomTag <= item.action() && item.action() <= ContextMenuItemLastCustomTag)
1148         return;
1149
1150     bool shouldEnable = true;
1151     bool shouldCheck = false; 
1152
1153     switch (item.action()) {
1154         case ContextMenuItemTagCheckSpelling:
1155             shouldEnable = frame->editor().canEdit();
1156             break;
1157         case ContextMenuItemTagDefaultDirection:
1158             shouldCheck = false;
1159             shouldEnable = false;
1160             break;
1161         case ContextMenuItemTagLeftToRight:
1162         case ContextMenuItemTagRightToLeft: {
1163             String direction = item.action() == ContextMenuItemTagLeftToRight ? "ltr" : "rtl";
1164             shouldCheck = frame->editor().selectionHasStyle(CSSPropertyDirection, direction) != FalseTriState;
1165             shouldEnable = true;
1166             break;
1167         }
1168         case ContextMenuItemTagTextDirectionDefault: {
1169             Editor::Command command = frame->editor().command("MakeTextWritingDirectionNatural");
1170             shouldCheck = command.state() == TrueTriState;
1171             shouldEnable = command.isEnabled();
1172             break;
1173         }
1174         case ContextMenuItemTagTextDirectionLeftToRight: {
1175             Editor::Command command = frame->editor().command("MakeTextWritingDirectionLeftToRight");
1176             shouldCheck = command.state() == TrueTriState;
1177             shouldEnable = command.isEnabled();
1178             break;
1179         }
1180         case ContextMenuItemTagTextDirectionRightToLeft: {
1181             Editor::Command command = frame->editor().command("MakeTextWritingDirectionRightToLeft");
1182             shouldCheck = command.state() == TrueTriState;
1183             shouldEnable = command.isEnabled();
1184             break;
1185         }
1186         case ContextMenuItemTagCopy:
1187             shouldEnable = frame->editor().canDHTMLCopy() || frame->editor().canCopy();
1188             break;
1189         case ContextMenuItemTagCut:
1190             shouldEnable = frame->editor().canDHTMLCut() || frame->editor().canCut();
1191             break;
1192         case ContextMenuItemTagIgnoreSpelling:
1193         case ContextMenuItemTagLearnSpelling:
1194             shouldEnable = frame->selection().isRange();
1195             break;
1196         case ContextMenuItemTagPaste:
1197             shouldEnable = frame->editor().canDHTMLPaste() || frame->editor().canPaste();
1198             break;
1199 #if PLATFORM(GTK)
1200         case ContextMenuItemTagDelete:
1201             shouldEnable = frame->editor().canDelete();
1202             break;
1203         case ContextMenuItemTagInputMethods:
1204         case ContextMenuItemTagUnicode:
1205         case ContextMenuItemTagUnicodeInsertLRMMark:
1206         case ContextMenuItemTagUnicodeInsertRLMMark:
1207         case ContextMenuItemTagUnicodeInsertLREMark:
1208         case ContextMenuItemTagUnicodeInsertRLEMark:
1209         case ContextMenuItemTagUnicodeInsertLROMark:
1210         case ContextMenuItemTagUnicodeInsertRLOMark:
1211         case ContextMenuItemTagUnicodeInsertPDFMark:
1212         case ContextMenuItemTagUnicodeInsertZWSMark:
1213         case ContextMenuItemTagUnicodeInsertZWJMark:
1214         case ContextMenuItemTagUnicodeInsertZWNJMark:
1215             shouldEnable = true;
1216             break;
1217 #endif
1218 #if PLATFORM(GTK)
1219         case ContextMenuItemTagSelectAll:
1220             shouldEnable = true;
1221             break;
1222 #endif
1223         case ContextMenuItemTagUnderline: {
1224             shouldCheck = frame->editor().selectionHasStyle(CSSPropertyWebkitTextDecorationsInEffect, "underline") != FalseTriState;
1225             shouldEnable = frame->editor().canEditRichly();
1226             break;
1227         }
1228         case ContextMenuItemTagLookUpInDictionary:
1229             shouldEnable = frame->selection().isRange();
1230             break;
1231         case ContextMenuItemTagCheckGrammarWithSpelling:
1232             if (frame->editor().isGrammarCheckingEnabled())
1233                 shouldCheck = true;
1234             shouldEnable = true;
1235             break;
1236         case ContextMenuItemTagItalic: {
1237             shouldCheck = frame->editor().selectionHasStyle(CSSPropertyFontStyle, "italic") != FalseTriState;
1238             shouldEnable = frame->editor().canEditRichly();
1239             break;
1240         }
1241         case ContextMenuItemTagBold: {
1242             shouldCheck = frame->editor().selectionHasStyle(CSSPropertyFontWeight, "bold") != FalseTriState;
1243             shouldEnable = frame->editor().canEditRichly();
1244             break;
1245         }
1246         case ContextMenuItemTagOutline:
1247             shouldEnable = false;
1248             break;
1249         case ContextMenuItemTagShowSpellingPanel:
1250             if (frame->editor().spellingPanelIsShowing())
1251                 item.setTitle(contextMenuItemTagShowSpellingPanel(false));
1252             else
1253                 item.setTitle(contextMenuItemTagShowSpellingPanel(true));
1254             shouldEnable = frame->editor().canEdit();
1255             break;
1256         case ContextMenuItemTagNoGuessesFound:
1257             shouldEnable = false;
1258             break;
1259         case ContextMenuItemTagCheckSpellingWhileTyping:
1260             shouldCheck = frame->editor().isContinuousSpellCheckingEnabled();
1261             break;
1262 #if PLATFORM(COCOA)
1263         case ContextMenuItemTagSubstitutionsMenu:
1264         case ContextMenuItemTagTransformationsMenu:
1265             break;
1266         case ContextMenuItemTagShowSubstitutions:
1267             if (frame->editor().substitutionsPanelIsShowing())
1268                 item.setTitle(contextMenuItemTagShowSubstitutions(false));
1269             else
1270                 item.setTitle(contextMenuItemTagShowSubstitutions(true));
1271             shouldEnable = frame->editor().canEdit();
1272             break;
1273         case ContextMenuItemTagMakeUpperCase:
1274         case ContextMenuItemTagMakeLowerCase:
1275         case ContextMenuItemTagCapitalize:
1276         case ContextMenuItemTagChangeBack:
1277             shouldEnable = frame->editor().canEdit();
1278             break;
1279         case ContextMenuItemTagCorrectSpellingAutomatically:
1280             shouldCheck = frame->editor().isAutomaticSpellingCorrectionEnabled();
1281             break;
1282         case ContextMenuItemTagSmartCopyPaste:
1283             shouldCheck = frame->editor().smartInsertDeleteEnabled();
1284             break;
1285         case ContextMenuItemTagSmartQuotes:
1286             shouldCheck = frame->editor().isAutomaticQuoteSubstitutionEnabled();
1287             break;
1288         case ContextMenuItemTagSmartDashes:
1289             shouldCheck = frame->editor().isAutomaticDashSubstitutionEnabled();
1290             break;
1291         case ContextMenuItemTagSmartLinks:
1292             shouldCheck = frame->editor().isAutomaticLinkDetectionEnabled();
1293             break;
1294         case ContextMenuItemTagTextReplacement:
1295             shouldCheck = frame->editor().isAutomaticTextReplacementEnabled();
1296             break;
1297         case ContextMenuItemTagStopSpeaking:
1298             shouldEnable = m_client.isSpeaking();
1299             break;
1300 #else // PLATFORM(COCOA) ends here
1301         case ContextMenuItemTagStopSpeaking:
1302             break;
1303 #endif
1304 #if PLATFORM(GTK)
1305         case ContextMenuItemTagGoBack:
1306             shouldEnable = frame->page() && frame->page()->backForward().canGoBackOrForward(-1);
1307             break;
1308         case ContextMenuItemTagGoForward:
1309             shouldEnable = frame->page() && frame->page()->backForward().canGoBackOrForward(1);
1310             break;
1311         case ContextMenuItemTagStop:
1312             shouldEnable = frame->loader().documentLoader()->isLoadingInAPISense();
1313             break;
1314         case ContextMenuItemTagReload:
1315             shouldEnable = !frame->loader().documentLoader()->isLoadingInAPISense();
1316             break;
1317         case ContextMenuItemTagFontMenu:
1318             shouldEnable = frame->editor().canEditRichly();
1319             break;
1320 #else
1321         case ContextMenuItemTagGoBack:
1322         case ContextMenuItemTagGoForward:
1323         case ContextMenuItemTagStop:
1324         case ContextMenuItemTagReload:
1325         case ContextMenuItemTagFontMenu:
1326 #endif
1327         case ContextMenuItemTagNoAction:
1328         case ContextMenuItemTagOpenLinkInNewWindow:
1329         case ContextMenuItemTagDownloadLinkToDisk:
1330         case ContextMenuItemTagCopyLinkToClipboard:
1331         case ContextMenuItemTagOpenImageInNewWindow:
1332         case ContextMenuItemTagCopyImageToClipboard:
1333 #if PLATFORM(GTK)
1334         case ContextMenuItemTagCopyImageUrlToClipboard:
1335 #endif
1336             break;
1337         case ContextMenuItemTagDownloadImageToDisk:
1338 #if PLATFORM(MAC)
1339             if (WTF::protocolIs(m_context.hitTestResult().absoluteImageURL(), "file"))
1340                 shouldEnable = false;
1341 #endif
1342             break;
1343         case ContextMenuItemTagOpenMediaInNewWindow:
1344             if (m_context.hitTestResult().mediaIsVideo())
1345                 item.setTitle(contextMenuItemTagOpenVideoInNewWindow());
1346             else
1347                 item.setTitle(contextMenuItemTagOpenAudioInNewWindow());
1348             break;
1349         case ContextMenuItemTagDownloadMediaToDisk:
1350             if (m_context.hitTestResult().mediaIsVideo())
1351                 item.setTitle(contextMenuItemTagDownloadVideoToDisk());
1352             else
1353                 item.setTitle(contextMenuItemTagDownloadAudioToDisk());
1354             if (WTF::protocolIs(m_context.hitTestResult().absoluteImageURL(), "file"))
1355                 shouldEnable = false;
1356             break;
1357         case ContextMenuItemTagCopyMediaLinkToClipboard:
1358             if (m_context.hitTestResult().mediaIsVideo())
1359                 item.setTitle(contextMenuItemTagCopyVideoLinkToClipboard());
1360             else
1361                 item.setTitle(contextMenuItemTagCopyAudioLinkToClipboard());
1362             break;
1363         case ContextMenuItemTagToggleMediaControls:
1364 #if SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS
1365             item.setTitle(m_context.hitTestResult().mediaControlsEnabled() ? contextMenuItemTagHideMediaControls() : contextMenuItemTagShowMediaControls());
1366 #else
1367             shouldCheck = m_context.hitTestResult().mediaControlsEnabled();
1368 #endif
1369             break;
1370         case ContextMenuItemTagToggleMediaLoop:
1371             shouldCheck = m_context.hitTestResult().mediaLoopEnabled();
1372             break;
1373         case ContextMenuItemTagToggleVideoFullscreen:
1374 #if SUPPORTS_TOGGLE_VIDEO_FULLSCREEN
1375             item.setTitle(m_context.hitTestResult().mediaIsInFullscreen() ? contextMenuItemTagExitVideoFullscreen() : contextMenuItemTagEnterVideoFullscreen());
1376             break;
1377 #endif
1378         case ContextMenuItemTagEnterVideoFullscreen:
1379             shouldEnable = m_context.hitTestResult().mediaSupportsFullscreen();
1380             break;
1381         case ContextMenuItemTagToggleVideoEnhancedFullscreen:
1382 #if PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)
1383             item.setTitle(m_context.hitTestResult().mediaIsInEnhancedFullscreen() ? contextMenuItemTagExitVideoEnhancedFullscreen() : contextMenuItemTagEnterVideoEnhancedFullscreen());
1384 #endif
1385             shouldEnable = m_context.hitTestResult().mediaSupportsEnhancedFullscreen();
1386             break;
1387         case ContextMenuItemTagOpenFrameInNewWindow:
1388         case ContextMenuItemTagSpellingGuess:
1389         case ContextMenuItemTagOther:
1390         case ContextMenuItemTagSearchInSpotlight:
1391         case ContextMenuItemTagSearchWeb:
1392         case ContextMenuItemTagOpenWithDefaultApplication:
1393         case ContextMenuItemPDFActualSize:
1394         case ContextMenuItemPDFZoomIn:
1395         case ContextMenuItemPDFZoomOut:
1396         case ContextMenuItemPDFAutoSize:
1397         case ContextMenuItemPDFSinglePage:
1398         case ContextMenuItemPDFFacingPages:
1399         case ContextMenuItemPDFContinuous:
1400         case ContextMenuItemPDFNextPage:
1401         case ContextMenuItemPDFPreviousPage:
1402         case ContextMenuItemTagOpenLink:
1403         case ContextMenuItemTagIgnoreGrammar:
1404         case ContextMenuItemTagSpellingMenu:
1405         case ContextMenuItemTagShowFonts:
1406         case ContextMenuItemTagStyles:
1407         case ContextMenuItemTagShowColors:
1408         case ContextMenuItemTagSpeechMenu:
1409         case ContextMenuItemTagStartSpeaking:
1410         case ContextMenuItemTagWritingDirectionMenu:
1411         case ContextMenuItemTagTextDirectionMenu:
1412         case ContextMenuItemTagPDFSinglePageScrolling:
1413         case ContextMenuItemTagPDFFacingPagesScrolling:
1414         case ContextMenuItemTagInspectElement:
1415         case ContextMenuItemBaseCustomTag:
1416         case ContextMenuItemLastCustomTag:
1417         case ContextMenuItemBaseApplicationTag:
1418         case ContextMenuItemTagDictationAlternative:
1419         case ContextMenuItemTagShareMenu:
1420             break;
1421         case ContextMenuItemTagMediaPlayPause:
1422             if (m_context.hitTestResult().mediaPlaying())
1423                 item.setTitle(contextMenuItemTagMediaPause());
1424             else
1425                 item.setTitle(contextMenuItemTagMediaPlay());
1426             break;
1427         case ContextMenuItemTagMediaMute:
1428             shouldEnable = m_context.hitTestResult().mediaHasAudio();
1429             shouldCheck = shouldEnable &&  m_context.hitTestResult().mediaMuted();
1430             break;
1431     }
1432
1433     item.setChecked(shouldCheck);
1434     item.setEnabled(shouldEnable);
1435 }
1436
1437 #if USE(ACCESSIBILITY_CONTEXT_MENUS)
1438
1439 void ContextMenuController::showContextMenuAt(Frame& frame, const IntPoint& clickPoint)
1440 {
1441     clearContextMenu();
1442     
1443     // Simulate a click in the middle of the accessibility object.
1444     PlatformMouseEvent mouseEvent(clickPoint, clickPoint, RightButton, PlatformEvent::MousePressed, 1, false, false, false, false, WallTime::now(), ForceAtClick, NoTap);
1445     frame.eventHandler().handleMousePressEvent(mouseEvent);
1446     bool handled = frame.eventHandler().sendContextMenuEvent(mouseEvent);
1447     if (handled)
1448         m_client.showContextMenu();
1449 }
1450
1451 #endif
1452
1453 #if ENABLE(SERVICE_CONTROLS)
1454
1455 void ContextMenuController::showImageControlsMenu(Event& event)
1456 {
1457     clearContextMenu();
1458     handleContextMenuEvent(event);
1459     m_client.showContextMenu();
1460 }
1461
1462 #endif
1463
1464 } // namespace WebCore
1465
1466 #endif // ENABLE(CONTEXT_MENUS)