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