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