d511beaf0c80fb0fbaa7d486e06bdab07097f517
[WebKit-https.git] / Source / WebCore / page / ContextMenuController.cpp
1 /*
2  * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
3  * Copyright (C) 2010 Igalia S.L
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28 #include "ContextMenuController.h"
29
30 #if ENABLE(CONTEXT_MENUS)
31
32 #include "BackForwardController.h"
33 #include "Chrome.h"
34 #include "ContextMenu.h"
35 #include "ContextMenuClient.h"
36 #include "ContextMenuItem.h"
37 #include "ContextMenuProvider.h"
38 #include "Document.h"
39 #include "DocumentFragment.h"
40 #include "DocumentLoader.h"
41 #include "Editor.h"
42 #include "EditorClient.h"
43 #include "Event.h"
44 #include "EventHandler.h"
45 #include "EventNames.h"
46 #include "ExceptionCodePlaceholder.h"
47 #include "FormState.h"
48 #include "FrameLoadRequest.h"
49 #include "FrameLoader.h"
50 #include "FrameLoaderClient.h"
51 #include "FrameSelection.h"
52 #include "HTMLFormControlElement.h"
53 #include "HTMLFormElement.h"
54 #include "HitTestRequest.h"
55 #include "HitTestResult.h"
56 #include "InspectorController.h"
57 #include "LocalizedStrings.h"
58 #include "MainFrame.h"
59 #include "MouseEvent.h"
60 #include "NavigationAction.h"
61 #include "Node.h"
62 #include "Page.h"
63 #include "PlatformEvent.h"
64 #include "RenderImage.h"
65 #include "ReplaceSelectionCommand.h"
66 #include "ResourceRequest.h"
67 #include "Settings.h"
68 #include "TextIterator.h"
69 #include "TypingCommand.h"
70 #include "UserTypingGestureIndicator.h"
71 #include "WindowFeatures.h"
72 #include "markup.h"
73 #include <wtf/unicode/CharacterNames.h>
74
75 using namespace WTF;
76 using namespace Unicode;
77
78 namespace WebCore {
79
80 ContextMenuController::ContextMenuController(Page& page, ContextMenuClient& client)
81     : m_page(page)
82     , m_client(client)
83 {
84 }
85
86 ContextMenuController::~ContextMenuController()
87 {
88     m_client.contextMenuDestroyed();
89 }
90
91 void ContextMenuController::clearContextMenu()
92 {
93     m_contextMenu = nullptr;
94     if (m_menuProvider)
95         m_menuProvider->contextMenuCleared();
96     m_menuProvider = 0;
97 }
98
99 void ContextMenuController::handleContextMenuEvent(Event* event)
100 {
101     m_contextMenu = maybeCreateContextMenu(event);
102     if (!m_contextMenu)
103         return;
104
105     populate();
106
107     showContextMenu(event);
108 }
109
110 static std::unique_ptr<ContextMenuItem> separatorItem()
111 {
112     return std::unique_ptr<ContextMenuItem>(new ContextMenuItem(SeparatorType, ContextMenuItemTagNoAction, String()));
113 }
114
115 void ContextMenuController::showContextMenu(Event* event, PassRefPtr<ContextMenuProvider> menuProvider)
116 {
117     m_menuProvider = menuProvider;
118
119     m_contextMenu = maybeCreateContextMenu(event);
120     if (!m_contextMenu) {
121         clearContextMenu();
122         return;
123     }
124
125     m_menuProvider->populateContextMenu(m_contextMenu.get());
126     if (m_context.hitTestResult().isSelected()) {
127         appendItem(*separatorItem(), m_contextMenu.get());
128         populate();
129     }
130     showContextMenu(event);
131 }
132
133 #if ENABLE(SERVICE_CONTROLS)
134 static Image* imageFromImageElementNode(Node& node)
135 {
136     RenderObject* renderer = node.renderer();
137     if (!is<RenderImage>(renderer))
138         return nullptr;
139     CachedImage* image = downcast<RenderImage>(*renderer).cachedImage();
140     if (!image || image->errorOccurred())
141         return nullptr;
142
143     return image->imageForRenderer(renderer);
144 }
145 #endif
146
147 std::unique_ptr<ContextMenu> ContextMenuController::maybeCreateContextMenu(Event* event)
148 {
149     ASSERT(event);
150     
151     if (!is<MouseEvent>(*event))
152         return nullptr;
153
154     MouseEvent& mouseEvent = downcast<MouseEvent>(*event);
155     HitTestResult result(mouseEvent.absoluteLocation());
156
157     Node* node = event->target()->toNode();
158     if (Frame* frame = node->document().frame())
159         result = frame->eventHandler().hitTestResultAtPoint(mouseEvent.absoluteLocation());
160     
161     if (!result.innerNonSharedNode())
162         return nullptr;
163
164     m_context = ContextMenuContext(result);
165
166 #if ENABLE(SERVICE_CONTROLS)
167     if (node->isImageControlsButtonElement()) {
168         if (Image* image = imageFromImageElementNode(*result.innerNonSharedNode()))
169             m_context.setControlledImage(image);
170
171         // FIXME: If we couldn't get the image then we shouldn't try to show the image controls menu for it.
172         return nullptr;
173     }
174 #endif
175
176     return std::unique_ptr<ContextMenu>(new ContextMenu);
177 }
178
179 void ContextMenuController::showContextMenu(Event* event)
180 {
181     if (m_page.inspectorController().enabled())
182         addInspectElementItem();
183
184 #if USE(CROSS_PLATFORM_CONTEXT_MENUS)
185     m_contextMenu = m_client.customizeMenu(WTF::move(m_contextMenu));
186 #else
187     PlatformMenuDescription customMenu = m_client.getCustomMenuFromDefaultItems(m_contextMenu.get());
188     m_contextMenu->setPlatformDescription(customMenu);
189 #endif
190     event->setDefaultHandled();
191 }
192
193 static void openNewWindow(const URL& urlToLoad, Frame* frame)
194 {
195     Page* oldPage = frame->page();
196     if (!oldPage)
197         return;
198     
199     FrameLoadRequest request(frame->document()->securityOrigin(), ResourceRequest(urlToLoad, frame->loader().outgoingReferrer()));
200
201     Page* newPage = oldPage->chrome().createWindow(frame, request, WindowFeatures(), NavigationAction(request.resourceRequest()));
202     if (!newPage)
203         return;
204     newPage->chrome().show();
205     newPage->mainFrame().loader().loadFrameRequest(request, LockHistory::No, LockBackForwardList::No, 0, 0, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Suppress);
206 }
207
208 #if PLATFORM(GTK)
209 static void insertUnicodeCharacter(UChar character, Frame* frame)
210 {
211     String text(&character, 1);
212     if (!frame->editor().shouldInsertText(text, frame->selection().toNormalizedRange().get(), EditorInsertActionTyped))
213         return;
214
215     ASSERT(frame->document());
216     TypingCommand::insertText(*frame->document(), text, 0, TypingCommand::TextCompositionNone);
217 }
218 #endif
219
220 void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item)
221 {
222     ASSERT(item->type() == ActionType || item->type() == CheckableActionType);
223
224     if (item->action() >= ContextMenuItemBaseApplicationTag) {
225         m_client.contextMenuItemSelected(item, m_contextMenu.get());
226         return;
227     }
228
229     if (item->action() >= ContextMenuItemBaseCustomTag) {
230         ASSERT(m_menuProvider);
231         m_menuProvider->contextMenuItemSelected(item);
232         return;
233     }
234
235     Frame* frame = m_context.hitTestResult().innerNonSharedNode()->document().frame();
236     if (!frame)
237         return;
238
239     switch (item->action()) {
240     case ContextMenuItemTagOpenLinkInNewWindow:
241         openNewWindow(m_context.hitTestResult().absoluteLinkURL(), frame);
242         break;
243     case ContextMenuItemTagDownloadLinkToDisk:
244         // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709)
245         m_client.downloadURL(m_context.hitTestResult().absoluteLinkURL());
246         break;
247     case ContextMenuItemTagCopyLinkToClipboard:
248         frame->editor().copyURL(m_context.hitTestResult().absoluteLinkURL(), m_context.hitTestResult().textContent());
249         break;
250     case ContextMenuItemTagOpenImageInNewWindow:
251         openNewWindow(m_context.hitTestResult().absoluteImageURL(), frame);
252         break;
253     case ContextMenuItemTagDownloadImageToDisk:
254         // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709)
255         m_client.downloadURL(m_context.hitTestResult().absoluteImageURL());
256         break;
257     case ContextMenuItemTagCopyImageToClipboard:
258         // FIXME: The Pasteboard class is not written yet
259         // For now, call into the client. This is temporary!
260         frame->editor().copyImage(m_context.hitTestResult());
261         break;
262 #if PLATFORM(GTK) || PLATFORM(EFL)
263     case ContextMenuItemTagCopyImageUrlToClipboard:
264         frame->editor().copyURL(m_context.hitTestResult().absoluteImageURL(), m_context.hitTestResult().textContent());
265         break;
266 #endif
267     case ContextMenuItemTagOpenMediaInNewWindow:
268         openNewWindow(m_context.hitTestResult().absoluteMediaURL(), frame);
269         break;
270     case ContextMenuItemTagDownloadMediaToDisk:
271         // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709)
272         m_client.downloadURL(m_context.hitTestResult().absoluteMediaURL());
273         break;
274     case ContextMenuItemTagCopyMediaLinkToClipboard:
275         frame->editor().copyURL(m_context.hitTestResult().absoluteMediaURL(), m_context.hitTestResult().textContent());
276         break;
277     case ContextMenuItemTagToggleMediaControls:
278         m_context.hitTestResult().toggleMediaControlsDisplay();
279         break;
280     case ContextMenuItemTagToggleMediaLoop:
281         m_context.hitTestResult().toggleMediaLoopPlayback();
282         break;
283     case ContextMenuItemTagToggleVideoFullscreen:
284         m_context.hitTestResult().toggleMediaFullscreenState();
285         break;
286     case ContextMenuItemTagEnterVideoFullscreen:
287         m_context.hitTestResult().enterFullscreenForVideo();
288         break;
289     case ContextMenuItemTagMediaPlayPause:
290         m_context.hitTestResult().toggleMediaPlayState();
291         break;
292     case ContextMenuItemTagMediaMute:
293         m_context.hitTestResult().toggleMediaMuteState();
294         break;
295     case ContextMenuItemTagOpenFrameInNewWindow: {
296         DocumentLoader* loader = frame->loader().documentLoader();
297         if (!loader->unreachableURL().isEmpty())
298             openNewWindow(loader->unreachableURL(), frame);
299         else
300             openNewWindow(loader->url(), frame);
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 #endif
361 #if PLATFORM(GTK) || PLATFORM(EFL)
362     case ContextMenuItemTagSelectAll:
363         frame->editor().command("SelectAll").execute();
364         break;
365 #endif
366     case ContextMenuItemTagSpellingGuess: {
367         VisibleSelection selection = frame->selection().selection();
368         if (frame->editor().shouldInsertText(item->title(), selection.toNormalizedRange().get(), EditorInsertActionPasted)) {
369             ReplaceSelectionCommand::CommandOptions replaceOptions = ReplaceSelectionCommand::MatchStyle | ReplaceSelectionCommand::PreventNesting;
370
371             if (frame->editor().behavior().shouldAllowSpellingSuggestionsWithoutSelection()) {
372                 ASSERT(selection.isCaretOrRange());
373                 VisibleSelection wordSelection(selection.base());
374                 wordSelection.expandUsingGranularity(WordGranularity);
375                 frame->selection().setSelection(wordSelection);
376             } else {
377                 ASSERT(frame->editor().selectedText().length());
378                 replaceOptions |= ReplaceSelectionCommand::SelectReplacement;
379             }
380
381             Document* document = frame->document();
382             ASSERT(document);
383             RefPtr<ReplaceSelectionCommand> command = ReplaceSelectionCommand::create(*document, createFragmentFromMarkup(*document, item->title(), ""), replaceOptions);
384             applyCommand(command);
385             frame->selection().revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
386         }
387         break;
388     }
389     case ContextMenuItemTagIgnoreSpelling:
390         frame->editor().ignoreSpelling();
391         break;
392     case ContextMenuItemTagLearnSpelling:
393         frame->editor().learnSpelling();
394         break;
395     case ContextMenuItemTagSearchWeb:
396         m_client.searchWithGoogle(frame);
397         break;
398     case ContextMenuItemTagLookUpInDictionary:
399         // FIXME: Some day we may be able to do this from within WebCore.
400         m_client.lookUpInDictionary(frame);
401         break;
402     case ContextMenuItemTagOpenLink:
403         if (Frame* targetFrame = m_context.hitTestResult().targetFrame())
404             targetFrame->loader().loadFrameRequest(FrameLoadRequest(frame->document()->securityOrigin(), ResourceRequest(m_context.hitTestResult().absoluteLinkURL(), frame->loader().outgoingReferrer())), LockHistory::No, LockBackForwardList::No, 0, 0, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Suppress);
405         else
406             openNewWindow(m_context.hitTestResult().absoluteLinkURL(), frame);
407         break;
408     case ContextMenuItemTagOpenLinkInThisWindow:
409         frame->loader().loadFrameRequest(FrameLoadRequest(frame->document()->securityOrigin(), ResourceRequest(m_context.hitTestResult().absoluteLinkURL(), frame->loader().outgoingReferrer())), LockHistory::No, LockBackForwardList::No, 0, 0, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Suppress);
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(IGNORE_EXCEPTION)) {
427             Document& document = m_context.hitTestResult().innerNonSharedNode()->document();
428             selectedRange = document.createRange();
429             selectedRange->selectNode(document.documentElement(), IGNORE_EXCEPTION);
430         }
431         m_client.speak(plainText(selectedRange.get()));
432         break;
433     }
434     case ContextMenuItemTagStopSpeaking:
435         m_client.stopSpeaking();
436         break;
437     case ContextMenuItemTagDefaultDirection:
438         frame->editor().setBaseWritingDirection(NaturalWritingDirection);
439         break;
440     case ContextMenuItemTagLeftToRight:
441         frame->editor().setBaseWritingDirection(LeftToRightWritingDirection);
442         break;
443     case ContextMenuItemTagRightToLeft:
444         frame->editor().setBaseWritingDirection(RightToLeftWritingDirection);
445         break;
446     case ContextMenuItemTagTextDirectionDefault:
447         frame->editor().command("MakeTextWritingDirectionNatural").execute();
448         break;
449     case ContextMenuItemTagTextDirectionLeftToRight:
450         frame->editor().command("MakeTextWritingDirectionLeftToRight").execute();
451         break;
452     case ContextMenuItemTagTextDirectionRightToLeft:
453         frame->editor().command("MakeTextWritingDirectionRightToLeft").execute();
454         break;
455 #if PLATFORM(COCOA)
456     case ContextMenuItemTagSearchInSpotlight:
457         m_client.searchWithSpotlight();
458         break;
459 #endif
460     case ContextMenuItemTagShowSpellingPanel:
461         frame->editor().showSpellingGuessPanel();
462         break;
463     case ContextMenuItemTagCheckSpelling:
464         frame->editor().advanceToNextMisspelling();
465         break;
466     case ContextMenuItemTagCheckSpellingWhileTyping:
467         frame->editor().toggleContinuousSpellChecking();
468         break;
469     case ContextMenuItemTagCheckGrammarWithSpelling:
470         frame->editor().toggleGrammarChecking();
471         break;
472 #if PLATFORM(COCOA)
473     case ContextMenuItemTagShowFonts:
474         frame->editor().showFontPanel();
475         break;
476     case ContextMenuItemTagStyles:
477         frame->editor().showStylesPanel();
478         break;
479     case ContextMenuItemTagShowColors:
480         frame->editor().showColorPanel();
481         break;
482 #endif
483 #if USE(APPKIT)
484     case ContextMenuItemTagMakeUpperCase:
485         frame->editor().uppercaseWord();
486         break;
487     case ContextMenuItemTagMakeLowerCase:
488         frame->editor().lowercaseWord();
489         break;
490     case ContextMenuItemTagCapitalize:
491         frame->editor().capitalizeWord();
492         break;
493 #endif
494 #if PLATFORM(COCOA)
495     case ContextMenuItemTagChangeBack:
496         frame->editor().changeBackToReplacedString(m_context.hitTestResult().replacedString());
497         break;
498 #endif
499 #if USE(AUTOMATIC_TEXT_REPLACEMENT)
500     case ContextMenuItemTagShowSubstitutions:
501         frame->editor().showSubstitutionsPanel();
502         break;
503     case ContextMenuItemTagSmartCopyPaste:
504         frame->editor().toggleSmartInsertDelete();
505         break;
506     case ContextMenuItemTagSmartQuotes:
507         frame->editor().toggleAutomaticQuoteSubstitution();
508         break;
509     case ContextMenuItemTagSmartDashes:
510         frame->editor().toggleAutomaticDashSubstitution();
511         break;
512     case ContextMenuItemTagSmartLinks:
513         frame->editor().toggleAutomaticLinkDetection();
514         break;
515     case ContextMenuItemTagTextReplacement:
516         frame->editor().toggleAutomaticTextReplacement();
517         break;
518     case ContextMenuItemTagCorrectSpellingAutomatically:
519         frame->editor().toggleAutomaticSpellingCorrection();
520         break;
521 #endif
522     case ContextMenuItemTagInspectElement:
523         if (Page* page = frame->page())
524             page->inspectorController().inspect(m_context.hitTestResult().innerNonSharedNode());
525         break;
526     case ContextMenuItemTagDictationAlternative:
527         frame->editor().applyDictationAlternativelternative(item->title());
528         break;
529     default:
530         break;
531     }
532 }
533
534 void ContextMenuController::appendItem(ContextMenuItem& menuItem, ContextMenu* parentMenu)
535 {
536     checkOrEnableIfNeeded(menuItem);
537     if (parentMenu)
538         parentMenu->appendItem(menuItem);
539 }
540
541 void ContextMenuController::createAndAppendFontSubMenu(ContextMenuItem& fontMenuItem)
542 {
543     ContextMenu fontMenu;
544
545 #if PLATFORM(COCOA)
546     ContextMenuItem showFonts(ActionType, ContextMenuItemTagShowFonts, contextMenuItemTagShowFonts());
547 #endif
548     ContextMenuItem bold(CheckableActionType, ContextMenuItemTagBold, contextMenuItemTagBold());
549     ContextMenuItem italic(CheckableActionType, ContextMenuItemTagItalic, contextMenuItemTagItalic());
550     ContextMenuItem underline(CheckableActionType, ContextMenuItemTagUnderline, contextMenuItemTagUnderline());
551     ContextMenuItem outline(ActionType, ContextMenuItemTagOutline, contextMenuItemTagOutline());
552 #if PLATFORM(COCOA)
553     ContextMenuItem styles(ActionType, ContextMenuItemTagStyles, contextMenuItemTagStyles());
554     ContextMenuItem showColors(ActionType, ContextMenuItemTagShowColors, contextMenuItemTagShowColors());
555 #endif
556
557 #if PLATFORM(COCOA)
558     appendItem(showFonts, &fontMenu);
559 #endif
560     appendItem(bold, &fontMenu);
561     appendItem(italic, &fontMenu);
562     appendItem(underline, &fontMenu);
563     appendItem(outline, &fontMenu);
564 #if PLATFORM(COCOA)
565     appendItem(styles, &fontMenu);
566     appendItem(*separatorItem(), &fontMenu);
567     appendItem(showColors, &fontMenu);
568 #endif
569
570     fontMenuItem.setSubMenu(&fontMenu);
571 }
572
573
574 #if !PLATFORM(GTK)
575
576 void ContextMenuController::createAndAppendSpellingAndGrammarSubMenu(ContextMenuItem& spellingAndGrammarMenuItem)
577 {
578     ContextMenu spellingAndGrammarMenu;
579
580     ContextMenuItem showSpellingPanel(ActionType, ContextMenuItemTagShowSpellingPanel, 
581         contextMenuItemTagShowSpellingPanel(true));
582     ContextMenuItem checkSpelling(ActionType, ContextMenuItemTagCheckSpelling, 
583         contextMenuItemTagCheckSpelling());
584     ContextMenuItem checkAsYouType(CheckableActionType, ContextMenuItemTagCheckSpellingWhileTyping, 
585         contextMenuItemTagCheckSpellingWhileTyping());
586     ContextMenuItem grammarWithSpelling(CheckableActionType, ContextMenuItemTagCheckGrammarWithSpelling, 
587         contextMenuItemTagCheckGrammarWithSpelling());
588 #if PLATFORM(COCOA)
589     ContextMenuItem correctSpelling(CheckableActionType, ContextMenuItemTagCorrectSpellingAutomatically, 
590         contextMenuItemTagCorrectSpellingAutomatically());
591 #endif
592
593     appendItem(showSpellingPanel, &spellingAndGrammarMenu);
594     appendItem(checkSpelling, &spellingAndGrammarMenu);
595 #if PLATFORM(COCOA)
596     appendItem(*separatorItem(), &spellingAndGrammarMenu);
597 #endif
598     appendItem(checkAsYouType, &spellingAndGrammarMenu);
599     appendItem(grammarWithSpelling, &spellingAndGrammarMenu);
600 #if PLATFORM(COCOA)
601     appendItem(correctSpelling, &spellingAndGrammarMenu);
602 #endif
603
604     spellingAndGrammarMenuItem.setSubMenu(&spellingAndGrammarMenu);
605 }
606
607 #endif // !PLATFORM(GTK)
608
609
610 #if PLATFORM(COCOA)
611
612 void ContextMenuController::createAndAppendSpeechSubMenu(ContextMenuItem& speechMenuItem)
613 {
614     ContextMenu speechMenu;
615
616     ContextMenuItem start(ActionType, ContextMenuItemTagStartSpeaking, contextMenuItemTagStartSpeaking());
617     ContextMenuItem stop(ActionType, ContextMenuItemTagStopSpeaking, contextMenuItemTagStopSpeaking());
618
619     appendItem(start, &speechMenu);
620     appendItem(stop, &speechMenu);
621
622     speechMenuItem.setSubMenu(&speechMenu);
623 }
624
625 #endif
626  
627 #if PLATFORM(GTK)
628
629 void ContextMenuController::createAndAppendUnicodeSubMenu(ContextMenuItem& unicodeMenuItem)
630 {
631     ContextMenu unicodeMenu;
632
633     ContextMenuItem leftToRightMarkMenuItem(ActionType, ContextMenuItemTagUnicodeInsertLRMMark, contextMenuItemTagUnicodeInsertLRMMark());
634     ContextMenuItem rightToLeftMarkMenuItem(ActionType, ContextMenuItemTagUnicodeInsertRLMMark, contextMenuItemTagUnicodeInsertRLMMark());
635     ContextMenuItem leftToRightEmbedMenuItem(ActionType, ContextMenuItemTagUnicodeInsertLREMark, contextMenuItemTagUnicodeInsertLREMark());
636     ContextMenuItem rightToLeftEmbedMenuItem(ActionType, ContextMenuItemTagUnicodeInsertRLEMark, contextMenuItemTagUnicodeInsertRLEMark());
637     ContextMenuItem leftToRightOverrideMenuItem(ActionType, ContextMenuItemTagUnicodeInsertLROMark, contextMenuItemTagUnicodeInsertLROMark());
638     ContextMenuItem rightToLeftOverrideMenuItem(ActionType, ContextMenuItemTagUnicodeInsertRLOMark, contextMenuItemTagUnicodeInsertRLOMark());
639     ContextMenuItem popDirectionalFormattingMenuItem(ActionType, ContextMenuItemTagUnicodeInsertPDFMark, contextMenuItemTagUnicodeInsertPDFMark());
640     ContextMenuItem zeroWidthSpaceMenuItem(ActionType, ContextMenuItemTagUnicodeInsertZWSMark, contextMenuItemTagUnicodeInsertZWSMark());
641     ContextMenuItem zeroWidthJoinerMenuItem(ActionType, ContextMenuItemTagUnicodeInsertZWJMark, contextMenuItemTagUnicodeInsertZWJMark());
642     ContextMenuItem zeroWidthNonJoinerMenuItem(ActionType, ContextMenuItemTagUnicodeInsertZWNJMark, contextMenuItemTagUnicodeInsertZWNJMark());
643
644     appendItem(leftToRightMarkMenuItem, &unicodeMenu);
645     appendItem(rightToLeftMarkMenuItem, &unicodeMenu);
646     appendItem(leftToRightEmbedMenuItem, &unicodeMenu);
647     appendItem(rightToLeftEmbedMenuItem, &unicodeMenu);
648     appendItem(leftToRightOverrideMenuItem, &unicodeMenu);
649     appendItem(rightToLeftOverrideMenuItem, &unicodeMenu);
650     appendItem(popDirectionalFormattingMenuItem, &unicodeMenu);
651     appendItem(zeroWidthSpaceMenuItem, &unicodeMenu);
652     appendItem(zeroWidthJoinerMenuItem, &unicodeMenu);
653     appendItem(zeroWidthNonJoinerMenuItem, &unicodeMenu);
654
655     unicodeMenuItem.setSubMenu(&unicodeMenu);
656 }
657
658 #else
659
660 void ContextMenuController::createAndAppendWritingDirectionSubMenu(ContextMenuItem& writingDirectionMenuItem)
661 {
662     ContextMenu writingDirectionMenu;
663
664     ContextMenuItem defaultItem(ActionType, ContextMenuItemTagDefaultDirection, 
665         contextMenuItemTagDefaultDirection());
666     ContextMenuItem ltr(CheckableActionType, ContextMenuItemTagLeftToRight, contextMenuItemTagLeftToRight());
667     ContextMenuItem rtl(CheckableActionType, ContextMenuItemTagRightToLeft, contextMenuItemTagRightToLeft());
668
669     appendItem(defaultItem, &writingDirectionMenu);
670     appendItem(ltr, &writingDirectionMenu);
671     appendItem(rtl, &writingDirectionMenu);
672
673     writingDirectionMenuItem.setSubMenu(&writingDirectionMenu);
674 }
675
676 void ContextMenuController::createAndAppendTextDirectionSubMenu(ContextMenuItem& textDirectionMenuItem)
677 {
678     ContextMenu textDirectionMenu;
679
680     ContextMenuItem defaultItem(ActionType, ContextMenuItemTagTextDirectionDefault, contextMenuItemTagDefaultDirection());
681     ContextMenuItem ltr(CheckableActionType, ContextMenuItemTagTextDirectionLeftToRight, contextMenuItemTagLeftToRight());
682     ContextMenuItem rtl(CheckableActionType, ContextMenuItemTagTextDirectionRightToLeft, contextMenuItemTagRightToLeft());
683
684     appendItem(defaultItem, &textDirectionMenu);
685     appendItem(ltr, &textDirectionMenu);
686     appendItem(rtl, &textDirectionMenu);
687
688     textDirectionMenuItem.setSubMenu(&textDirectionMenu);
689 }
690
691 #endif
692
693 #if PLATFORM(COCOA)
694
695 void ContextMenuController::createAndAppendSubstitutionsSubMenu(ContextMenuItem& substitutionsMenuItem)
696 {
697     ContextMenu substitutionsMenu;
698
699     ContextMenuItem showSubstitutions(ActionType, ContextMenuItemTagShowSubstitutions, contextMenuItemTagShowSubstitutions(true));
700     ContextMenuItem smartCopyPaste(CheckableActionType, ContextMenuItemTagSmartCopyPaste, contextMenuItemTagSmartCopyPaste());
701     ContextMenuItem smartQuotes(CheckableActionType, ContextMenuItemTagSmartQuotes, contextMenuItemTagSmartQuotes());
702     ContextMenuItem smartDashes(CheckableActionType, ContextMenuItemTagSmartDashes, contextMenuItemTagSmartDashes());
703     ContextMenuItem smartLinks(CheckableActionType, ContextMenuItemTagSmartLinks, contextMenuItemTagSmartLinks());
704     ContextMenuItem textReplacement(CheckableActionType, ContextMenuItemTagTextReplacement, contextMenuItemTagTextReplacement());
705
706     appendItem(showSubstitutions, &substitutionsMenu);
707     appendItem(*separatorItem(), &substitutionsMenu);
708     appendItem(smartCopyPaste, &substitutionsMenu);
709     appendItem(smartQuotes, &substitutionsMenu);
710     appendItem(smartDashes, &substitutionsMenu);
711     appendItem(smartLinks, &substitutionsMenu);
712     appendItem(textReplacement, &substitutionsMenu);
713
714     substitutionsMenuItem.setSubMenu(&substitutionsMenu);
715 }
716
717 void ContextMenuController::createAndAppendTransformationsSubMenu(ContextMenuItem& transformationsMenuItem)
718 {
719     ContextMenu transformationsMenu;
720
721     ContextMenuItem makeUpperCase(ActionType, ContextMenuItemTagMakeUpperCase, contextMenuItemTagMakeUpperCase());
722     ContextMenuItem makeLowerCase(ActionType, ContextMenuItemTagMakeLowerCase, contextMenuItemTagMakeLowerCase());
723     ContextMenuItem capitalize(ActionType, ContextMenuItemTagCapitalize, contextMenuItemTagCapitalize());
724
725     appendItem(makeUpperCase, &transformationsMenu);
726     appendItem(makeLowerCase, &transformationsMenu);
727     appendItem(capitalize, &transformationsMenu);
728
729     transformationsMenuItem.setSubMenu(&transformationsMenu);
730 }
731
732 #endif
733
734 static bool selectionContainsPossibleWord(Frame* frame)
735 {
736     // Current algorithm: look for a character that's not just a separator.
737     for (TextIterator it(frame->selection().toNormalizedRange().get()); !it.atEnd(); it.advance()) {
738         int length = it.text().length();
739         for (int i = 0; i < length; ++i) {
740             if (!(U_GET_GC_MASK(it.text()[i]) & U_GC_Z_MASK))
741                 return true;
742         }
743     }
744     return false;
745 }
746
747 #if PLATFORM(COCOA)
748 #define SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 1
749 #else
750 #define SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 0
751 #endif
752
753 #if PLATFORM(COCOA)
754 #define SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 1
755 #else
756 #define SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 0
757 #endif
758
759 void ContextMenuController::populate()
760 {
761     ContextMenuItem OpenLinkItem(ActionType, ContextMenuItemTagOpenLink, contextMenuItemTagOpenLink());
762     ContextMenuItem OpenLinkInNewWindowItem(ActionType, ContextMenuItemTagOpenLinkInNewWindow, 
763         contextMenuItemTagOpenLinkInNewWindow());
764     ContextMenuItem DownloadFileItem(ActionType, ContextMenuItemTagDownloadLinkToDisk, 
765         contextMenuItemTagDownloadLinkToDisk());
766     ContextMenuItem CopyLinkItem(ActionType, ContextMenuItemTagCopyLinkToClipboard, 
767         contextMenuItemTagCopyLinkToClipboard());
768     ContextMenuItem OpenImageInNewWindowItem(ActionType, ContextMenuItemTagOpenImageInNewWindow, 
769         contextMenuItemTagOpenImageInNewWindow());
770     ContextMenuItem DownloadImageItem(ActionType, ContextMenuItemTagDownloadImageToDisk, 
771         contextMenuItemTagDownloadImageToDisk());
772     ContextMenuItem CopyImageItem(ActionType, ContextMenuItemTagCopyImageToClipboard, 
773         contextMenuItemTagCopyImageToClipboard());
774 #if PLATFORM(GTK) || PLATFORM(EFL)
775     ContextMenuItem CopyImageUrlItem(ActionType, ContextMenuItemTagCopyImageUrlToClipboard, 
776         contextMenuItemTagCopyImageUrlToClipboard());
777 #endif
778     ContextMenuItem OpenMediaInNewWindowItem(ActionType, ContextMenuItemTagOpenMediaInNewWindow, String());
779     ContextMenuItem DownloadMediaItem(ActionType, ContextMenuItemTagDownloadMediaToDisk, String());
780     ContextMenuItem CopyMediaLinkItem(ActionType, ContextMenuItemTagCopyMediaLinkToClipboard, String());
781     ContextMenuItem MediaPlayPause(ActionType, ContextMenuItemTagMediaPlayPause, 
782         contextMenuItemTagMediaPlay());
783     ContextMenuItem MediaMute(ActionType, ContextMenuItemTagMediaMute, 
784         contextMenuItemTagMediaMute());
785 #if SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS
786     ContextMenuItem ToggleMediaControls(ActionType, ContextMenuItemTagToggleMediaControls,
787         contextMenuItemTagHideMediaControls());
788 #else
789     ContextMenuItem ToggleMediaControls(CheckableActionType, ContextMenuItemTagToggleMediaControls, 
790         contextMenuItemTagToggleMediaControls());
791 #endif
792     ContextMenuItem ToggleMediaLoop(CheckableActionType, ContextMenuItemTagToggleMediaLoop, 
793         contextMenuItemTagToggleMediaLoop());
794     ContextMenuItem EnterVideoFullscreen(ActionType, ContextMenuItemTagEnterVideoFullscreen,
795         contextMenuItemTagEnterVideoFullscreen());
796     ContextMenuItem ToggleVideoFullscreen(ActionType, ContextMenuItemTagToggleVideoFullscreen,
797         contextMenuItemTagEnterVideoFullscreen());
798 #if PLATFORM(COCOA)
799     ContextMenuItem SearchSpotlightItem(ActionType, ContextMenuItemTagSearchInSpotlight, 
800         contextMenuItemTagSearchInSpotlight());
801 #endif
802 #if !PLATFORM(GTK)
803     ContextMenuItem SearchWebItem(ActionType, ContextMenuItemTagSearchWeb, contextMenuItemTagSearchWeb());
804 #endif
805     ContextMenuItem CopyItem(ActionType, ContextMenuItemTagCopy, contextMenuItemTagCopy());
806     ContextMenuItem BackItem(ActionType, ContextMenuItemTagGoBack, contextMenuItemTagGoBack());
807     ContextMenuItem ForwardItem(ActionType, ContextMenuItemTagGoForward,  contextMenuItemTagGoForward());
808     ContextMenuItem StopItem(ActionType, ContextMenuItemTagStop, contextMenuItemTagStop());
809     ContextMenuItem ReloadItem(ActionType, ContextMenuItemTagReload, contextMenuItemTagReload());
810     ContextMenuItem OpenFrameItem(ActionType, ContextMenuItemTagOpenFrameInNewWindow, 
811         contextMenuItemTagOpenFrameInNewWindow());
812     ContextMenuItem NoGuessesItem(ActionType, ContextMenuItemTagNoGuessesFound, 
813         contextMenuItemTagNoGuessesFound());
814     ContextMenuItem IgnoreSpellingItem(ActionType, ContextMenuItemTagIgnoreSpelling, 
815         contextMenuItemTagIgnoreSpelling());
816     ContextMenuItem LearnSpellingItem(ActionType, ContextMenuItemTagLearnSpelling, 
817         contextMenuItemTagLearnSpelling());
818     ContextMenuItem IgnoreGrammarItem(ActionType, ContextMenuItemTagIgnoreGrammar, 
819         contextMenuItemTagIgnoreGrammar());
820     ContextMenuItem CutItem(ActionType, ContextMenuItemTagCut, contextMenuItemTagCut());
821     ContextMenuItem PasteItem(ActionType, ContextMenuItemTagPaste, contextMenuItemTagPaste());
822 #if PLATFORM(GTK)
823     ContextMenuItem DeleteItem(ActionType, ContextMenuItemTagDelete, contextMenuItemTagDelete());
824 #endif
825 #if PLATFORM(GTK) || PLATFORM(EFL)
826     ContextMenuItem SelectAllItem(ActionType, ContextMenuItemTagSelectAll, contextMenuItemTagSelectAll());
827 #endif
828
829     Node* node = m_context.hitTestResult().innerNonSharedNode();
830     if (!node)
831         return;
832 #if PLATFORM(GTK)
833     if (!m_context.hitTestResult().isContentEditable() && is<HTMLFormControlElement>(*node))
834         return;
835 #endif
836     Frame* frame = node->document().frame();
837     if (!frame)
838         return;
839
840 #if ENABLE(SERVICE_CONTROLS)
841     // The default image control menu gets populated solely by the platform.
842     if (m_context.controlledImage())
843         return;
844 #endif
845
846     if (!m_context.hitTestResult().isContentEditable()) {
847         FrameLoader& loader = frame->loader();
848         URL linkURL = m_context.hitTestResult().absoluteLinkURL();
849         if (!linkURL.isEmpty()) {
850             if (loader.client().canHandleRequest(ResourceRequest(linkURL))) {
851                 appendItem(OpenLinkItem, m_contextMenu.get());
852                 appendItem(OpenLinkInNewWindowItem, m_contextMenu.get());
853                 appendItem(DownloadFileItem, m_contextMenu.get());
854             }
855             appendItem(CopyLinkItem, m_contextMenu.get());
856         }
857
858         URL imageURL = m_context.hitTestResult().absoluteImageURL();
859         if (!imageURL.isEmpty()) {
860             if (!linkURL.isEmpty())
861                 appendItem(*separatorItem(), m_contextMenu.get());
862
863             appendItem(OpenImageInNewWindowItem, m_contextMenu.get());
864             appendItem(DownloadImageItem, m_contextMenu.get());
865             if (imageURL.isLocalFile() || m_context.hitTestResult().image())
866                 appendItem(CopyImageItem, m_contextMenu.get());
867 #if PLATFORM(GTK) || PLATFORM(EFL)
868             appendItem(CopyImageUrlItem, m_contextMenu.get());
869 #endif
870         }
871
872         URL mediaURL = m_context.hitTestResult().absoluteMediaURL();
873         if (!mediaURL.isEmpty()) {
874             if (!linkURL.isEmpty() || !imageURL.isEmpty())
875                 appendItem(*separatorItem(), m_contextMenu.get());
876
877             appendItem(MediaPlayPause, m_contextMenu.get());
878             appendItem(MediaMute, m_contextMenu.get());
879             appendItem(ToggleMediaControls, m_contextMenu.get());
880             appendItem(ToggleMediaLoop, m_contextMenu.get());
881 #if SUPPORTS_TOGGLE_VIDEO_FULLSCREEN
882             appendItem(ToggleVideoFullscreen, m_contextMenu.get());
883 #else
884             appendItem(EnterVideoFullscreen, m_contextMenu.get());
885 #endif
886             appendItem(*separatorItem(), m_contextMenu.get());
887             appendItem(CopyMediaLinkItem, m_contextMenu.get());
888             appendItem(OpenMediaInNewWindowItem, m_contextMenu.get());
889             if (m_context.hitTestResult().isDownloadableMedia() && loader.client().canHandleRequest(ResourceRequest(mediaURL)))
890                 appendItem(DownloadMediaItem, m_contextMenu.get());
891         }
892
893         if (imageURL.isEmpty() && linkURL.isEmpty() && mediaURL.isEmpty()) {
894             if (m_context.hitTestResult().isSelected()) {
895                 String selectedString;
896                 if (selectionContainsPossibleWord(frame)) {
897 #if PLATFORM(COCOA)
898                     selectedString = frame->displayStringModifiedByEncoding(frame->editor().selectedText());
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                 if (!selectedString.isEmpty() && ContextMenuItem::supportsShareMenu()) {
915                     ContextMenuItem ShareItem(m_client.shareSelectedTextMenuItem(selectedString));
916                     appendItem(ShareItem, m_contextMenu.get());
917                     appendItem(*separatorItem(), m_contextMenu.get());
918
919                     m_context.setSelectedText(selectedString);
920                 }
921
922                 ContextMenuItem SpeechMenuItem(SubmenuType, ContextMenuItemTagSpeechMenu, contextMenuItemTagSpeechMenu());
923                 createAndAppendSpeechSubMenu(SpeechMenuItem);
924                 appendItem(SpeechMenuItem, m_contextMenu.get());
925 #endif                
926             } else {
927                 if (!(frame->page() && (frame->page()->inspectorController().hasInspectorFrontendClient() || frame->page()->inspectorController().hasRemoteFrontend()))) {
928
929                 // In GTK+ unavailable items are not hidden but insensitive.
930 #if PLATFORM(GTK)
931                 appendItem(BackItem, m_contextMenu.get());
932                 appendItem(ForwardItem, m_contextMenu.get());
933                 appendItem(StopItem, m_contextMenu.get());
934                 appendItem(ReloadItem, m_contextMenu.get());
935 #else
936                 if (frame->page() && frame->page()->backForward().canGoBackOrForward(-1))
937                     appendItem(BackItem, m_contextMenu.get());
938
939                 if (frame->page() && frame->page()->backForward().canGoBackOrForward(1))
940                     appendItem(ForwardItem, m_contextMenu.get());
941
942                 // use isLoadingInAPISense rather than isLoading because Stop/Reload are
943                 // intended to match WebKit's API, not WebCore's internal notion of loading status
944                 if (loader.documentLoader()->isLoadingInAPISense())
945                     appendItem(StopItem, m_contextMenu.get());
946                 else
947                     appendItem(ReloadItem, m_contextMenu.get());
948 #endif
949                 }
950
951                 if (frame->page() && !frame->isMainFrame())
952                     appendItem(OpenFrameItem, m_contextMenu.get());
953             }
954         }
955     } else { // Make an editing context menu
956         bool inPasswordField = frame->selection().selection().isInPasswordField();
957         if (!inPasswordField) {
958             bool haveContextMenuItemsForMisspellingOrGrammer = false;
959             bool spellCheckingEnabled = frame->editor().isSpellCheckingEnabledFor(node);
960             if (spellCheckingEnabled) {
961                 // Consider adding spelling-related or grammar-related context menu items (never both, since a single selected range
962                 // is never considered a misspelling and bad grammar at the same time)
963                 bool misspelling;
964                 bool badGrammar;
965                 Vector<String> guesses = frame->editor().guessesForMisspelledOrUngrammatical(misspelling, badGrammar);
966                 if (misspelling || badGrammar) {
967                     size_t size = guesses.size();
968                     if (!size) {
969                         // If there's bad grammar but no suggestions (e.g., repeated word), just leave off the suggestions
970                         // list and trailing separator rather than adding a "No Guesses Found" item (matches AppKit)
971                         if (misspelling) {
972                             appendItem(NoGuessesItem, m_contextMenu.get());
973                             appendItem(*separatorItem(), m_contextMenu.get());
974                         }
975                     } else {
976                         for (unsigned i = 0; i < size; i++) {
977                             const String &guess = guesses[i];
978                             if (!guess.isEmpty()) {
979                                 ContextMenuItem item(ActionType, ContextMenuItemTagSpellingGuess, guess);
980                                 appendItem(item, m_contextMenu.get());
981                             }
982                         }
983                         appendItem(*separatorItem(), m_contextMenu.get());
984                     }
985                     if (misspelling) {
986                         appendItem(IgnoreSpellingItem, m_contextMenu.get());
987                         appendItem(LearnSpellingItem, m_contextMenu.get());
988                     } else
989                         appendItem(IgnoreGrammarItem, m_contextMenu.get());
990                     appendItem(*separatorItem(), m_contextMenu.get());
991                     haveContextMenuItemsForMisspellingOrGrammer = true;
992 #if PLATFORM(COCOA)
993                 } else {
994                     // If the string was autocorrected, generate a contextual menu item allowing it to be changed back.
995                     String replacedString = m_context.hitTestResult().replacedString();
996                     if (!replacedString.isEmpty()) {
997                         ContextMenuItem item(ActionType, ContextMenuItemTagChangeBack, contextMenuItemTagChangeBack(replacedString));
998                         appendItem(item, m_contextMenu.get());
999                         appendItem(*separatorItem(), m_contextMenu.get());
1000                         haveContextMenuItemsForMisspellingOrGrammer = true;
1001                     }
1002 #endif
1003                 }
1004             }
1005
1006             if (!haveContextMenuItemsForMisspellingOrGrammer) {
1007                 // Spelling and grammar checking is mutually exclusive with dictation alternatives.
1008                 Vector<String> dictationAlternatives = m_context.hitTestResult().dictationAlternatives();
1009                 if (!dictationAlternatives.isEmpty()) {
1010                     for (size_t i = 0; i < dictationAlternatives.size(); ++i) {
1011                         ContextMenuItem item(ActionType, ContextMenuItemTagDictationAlternative, dictationAlternatives[i]);
1012                         appendItem(item, m_contextMenu.get());
1013                     }
1014                     appendItem(*separatorItem(), m_contextMenu.get());
1015                 }
1016             }
1017         }
1018
1019         FrameLoader& loader = frame->loader();
1020         URL linkURL = m_context.hitTestResult().absoluteLinkURL();
1021         if (!linkURL.isEmpty()) {
1022             if (loader.client().canHandleRequest(ResourceRequest(linkURL))) {
1023                 appendItem(OpenLinkItem, m_contextMenu.get());
1024                 appendItem(OpenLinkInNewWindowItem, m_contextMenu.get());
1025                 appendItem(DownloadFileItem, m_contextMenu.get());
1026             }
1027             appendItem(CopyLinkItem, m_contextMenu.get());
1028             appendItem(*separatorItem(), m_contextMenu.get());
1029         }
1030
1031         if (m_context.hitTestResult().isSelected() && !inPasswordField && selectionContainsPossibleWord(frame)) {
1032 #if PLATFORM(COCOA)
1033             String selectedString = frame->displayStringModifiedByEncoding(frame->editor().selectedText());
1034             ContextMenuItem LookUpInDictionaryItem(ActionType, ContextMenuItemTagLookUpInDictionary, contextMenuItemTagLookUpInDictionary(selectedString));
1035
1036             appendItem(LookUpInDictionaryItem, m_contextMenu.get());
1037 #endif
1038
1039 #if !PLATFORM(GTK)
1040             appendItem(SearchWebItem, m_contextMenu.get());
1041             appendItem(*separatorItem(), m_contextMenu.get());
1042 #endif
1043         }
1044
1045         appendItem(CutItem, m_contextMenu.get());
1046         appendItem(CopyItem, m_contextMenu.get());
1047         appendItem(PasteItem, m_contextMenu.get());
1048 #if PLATFORM(GTK)
1049         appendItem(DeleteItem, m_contextMenu.get());
1050         appendItem(*separatorItem(), m_contextMenu.get());
1051 #endif
1052 #if PLATFORM(GTK) || PLATFORM(EFL)
1053         appendItem(SelectAllItem, 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 }
1116
1117 void ContextMenuController::addInspectElementItem()
1118 {
1119     Node* node = m_context.hitTestResult().innerNonSharedNode();
1120     if (!node)
1121         return;
1122
1123     Frame* frame = node->document().frame();
1124     if (!frame)
1125         return;
1126
1127     Page* page = frame->page();
1128     if (!page)
1129         return;
1130
1131     ContextMenuItem InspectElementItem(ActionType, ContextMenuItemTagInspectElement, contextMenuItemTagInspectElement());
1132 #if USE(CROSS_PLATFORM_CONTEXT_MENUS)
1133     if (m_contextMenu && !m_contextMenu->items().isEmpty())
1134 #else
1135     if (m_contextMenu && m_contextMenu->itemCount())
1136 #endif
1137         appendItem(*separatorItem(), m_contextMenu.get());
1138     appendItem(InspectElementItem, m_contextMenu.get());
1139 }
1140
1141 void ContextMenuController::checkOrEnableIfNeeded(ContextMenuItem& item) const
1142 {
1143     if (item.type() == SeparatorType)
1144         return;
1145     
1146     Frame* frame = m_context.hitTestResult().innerNonSharedNode()->document().frame();
1147     if (!frame)
1148         return;
1149
1150     // Custom items already have proper checked and enabled values.
1151     if (ContextMenuItemBaseCustomTag <= item.action() && item.action() <= ContextMenuItemLastCustomTag)
1152         return;
1153
1154     bool shouldEnable = true;
1155     bool shouldCheck = false; 
1156
1157     switch (item.action()) {
1158         case ContextMenuItemTagCheckSpelling:
1159             shouldEnable = frame->editor().canEdit();
1160             break;
1161         case ContextMenuItemTagDefaultDirection:
1162             shouldCheck = false;
1163             shouldEnable = false;
1164             break;
1165         case ContextMenuItemTagLeftToRight:
1166         case ContextMenuItemTagRightToLeft: {
1167             String direction = item.action() == ContextMenuItemTagLeftToRight ? "ltr" : "rtl";
1168             shouldCheck = frame->editor().selectionHasStyle(CSSPropertyDirection, direction) != FalseTriState;
1169             shouldEnable = true;
1170             break;
1171         }
1172         case ContextMenuItemTagTextDirectionDefault: {
1173             Editor::Command command = frame->editor().command("MakeTextWritingDirectionNatural");
1174             shouldCheck = command.state() == TrueTriState;
1175             shouldEnable = command.isEnabled();
1176             break;
1177         }
1178         case ContextMenuItemTagTextDirectionLeftToRight: {
1179             Editor::Command command = frame->editor().command("MakeTextWritingDirectionLeftToRight");
1180             shouldCheck = command.state() == TrueTriState;
1181             shouldEnable = command.isEnabled();
1182             break;
1183         }
1184         case ContextMenuItemTagTextDirectionRightToLeft: {
1185             Editor::Command command = frame->editor().command("MakeTextWritingDirectionRightToLeft");
1186             shouldCheck = command.state() == TrueTriState;
1187             shouldEnable = command.isEnabled();
1188             break;
1189         }
1190         case ContextMenuItemTagCopy:
1191             shouldEnable = frame->editor().canDHTMLCopy() || frame->editor().canCopy();
1192             break;
1193         case ContextMenuItemTagCut:
1194             shouldEnable = frame->editor().canDHTMLCut() || frame->editor().canCut();
1195             break;
1196         case ContextMenuItemTagIgnoreSpelling:
1197         case ContextMenuItemTagLearnSpelling:
1198             shouldEnable = frame->selection().isRange();
1199             break;
1200         case ContextMenuItemTagPaste:
1201             shouldEnable = frame->editor().canDHTMLPaste() || frame->editor().canPaste();
1202             break;
1203 #if PLATFORM(GTK)
1204         case ContextMenuItemTagDelete:
1205             shouldEnable = frame->editor().canDelete();
1206             break;
1207         case ContextMenuItemTagInputMethods:
1208         case ContextMenuItemTagUnicode:
1209         case ContextMenuItemTagUnicodeInsertLRMMark:
1210         case ContextMenuItemTagUnicodeInsertRLMMark:
1211         case ContextMenuItemTagUnicodeInsertLREMark:
1212         case ContextMenuItemTagUnicodeInsertRLEMark:
1213         case ContextMenuItemTagUnicodeInsertLROMark:
1214         case ContextMenuItemTagUnicodeInsertRLOMark:
1215         case ContextMenuItemTagUnicodeInsertPDFMark:
1216         case ContextMenuItemTagUnicodeInsertZWSMark:
1217         case ContextMenuItemTagUnicodeInsertZWJMark:
1218         case ContextMenuItemTagUnicodeInsertZWNJMark:
1219             shouldEnable = true;
1220             break;
1221 #endif
1222 #if PLATFORM(GTK) || PLATFORM(EFL)
1223         case ContextMenuItemTagSelectAll:
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 ContextMenuItemTagOpenLinkInThisWindow:
1334         case ContextMenuItemTagDownloadLinkToDisk:
1335         case ContextMenuItemTagCopyLinkToClipboard:
1336         case ContextMenuItemTagOpenImageInNewWindow:
1337         case ContextMenuItemTagDownloadImageToDisk:
1338         case ContextMenuItemTagCopyImageToClipboard:
1339 #if PLATFORM(GTK) || PLATFORM(EFL)
1340         case ContextMenuItemTagCopyImageUrlToClipboard:
1341 #endif
1342             break;
1343         case ContextMenuItemTagOpenMediaInNewWindow:
1344             if (m_context.hitTestResult().mediaIsVideo())
1345                 item.setTitle(contextMenuItemTagOpenVideoInNewWindow());
1346             else
1347                 item.setTitle(contextMenuItemTagOpenAudioInNewWindow());
1348             break;
1349         case ContextMenuItemTagDownloadMediaToDisk:
1350             if (m_context.hitTestResult().mediaIsVideo())
1351                 item.setTitle(contextMenuItemTagDownloadVideoToDisk());
1352             else
1353                 item.setTitle(contextMenuItemTagDownloadAudioToDisk());
1354             break;
1355         case ContextMenuItemTagCopyMediaLinkToClipboard:
1356             if (m_context.hitTestResult().mediaIsVideo())
1357                 item.setTitle(contextMenuItemTagCopyVideoLinkToClipboard());
1358             else
1359                 item.setTitle(contextMenuItemTagCopyAudioLinkToClipboard());
1360             break;
1361         case ContextMenuItemTagToggleMediaControls:
1362 #if SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS
1363             item.setTitle(m_context.hitTestResult().mediaControlsEnabled() ? contextMenuItemTagHideMediaControls() : contextMenuItemTagShowMediaControls());
1364 #else
1365             shouldCheck = m_context.hitTestResult().mediaControlsEnabled();
1366 #endif
1367             break;
1368         case ContextMenuItemTagToggleMediaLoop:
1369             shouldCheck = m_context.hitTestResult().mediaLoopEnabled();
1370             break;
1371         case ContextMenuItemTagToggleVideoFullscreen:
1372 #if SUPPORTS_TOGGLE_VIDEO_FULLSCREEN
1373             item.setTitle(m_context.hitTestResult().mediaIsInFullscreen() ? contextMenuItemTagExitVideoFullscreen() : contextMenuItemTagEnterVideoFullscreen());
1374             break;
1375 #endif
1376         case ContextMenuItemTagEnterVideoFullscreen:
1377             shouldEnable = m_context.hitTestResult().mediaSupportsFullscreen();
1378             break;
1379         case ContextMenuItemTagOpenFrameInNewWindow:
1380         case ContextMenuItemTagSpellingGuess:
1381         case ContextMenuItemTagOther:
1382         case ContextMenuItemTagSearchInSpotlight:
1383         case ContextMenuItemTagSearchWeb:
1384         case ContextMenuItemTagOpenWithDefaultApplication:
1385         case ContextMenuItemPDFActualSize:
1386         case ContextMenuItemPDFZoomIn:
1387         case ContextMenuItemPDFZoomOut:
1388         case ContextMenuItemPDFAutoSize:
1389         case ContextMenuItemPDFSinglePage:
1390         case ContextMenuItemPDFFacingPages:
1391         case ContextMenuItemPDFContinuous:
1392         case ContextMenuItemPDFNextPage:
1393         case ContextMenuItemPDFPreviousPage:
1394         case ContextMenuItemTagOpenLink:
1395         case ContextMenuItemTagIgnoreGrammar:
1396         case ContextMenuItemTagSpellingMenu:
1397         case ContextMenuItemTagShowFonts:
1398         case ContextMenuItemTagStyles:
1399         case ContextMenuItemTagShowColors:
1400         case ContextMenuItemTagSpeechMenu:
1401         case ContextMenuItemTagStartSpeaking:
1402         case ContextMenuItemTagWritingDirectionMenu:
1403         case ContextMenuItemTagTextDirectionMenu:
1404         case ContextMenuItemTagPDFSinglePageScrolling:
1405         case ContextMenuItemTagPDFFacingPagesScrolling:
1406         case ContextMenuItemTagInspectElement:
1407         case ContextMenuItemBaseCustomTag:
1408         case ContextMenuItemCustomTagNoAction:
1409         case ContextMenuItemLastCustomTag:
1410         case ContextMenuItemBaseApplicationTag:
1411         case ContextMenuItemTagDictationAlternative:
1412         case ContextMenuItemTagShareMenu:
1413             break;
1414         case ContextMenuItemTagMediaPlayPause:
1415             if (m_context.hitTestResult().mediaPlaying())
1416                 item.setTitle(contextMenuItemTagMediaPause());
1417             else
1418                 item.setTitle(contextMenuItemTagMediaPlay());
1419             break;
1420         case ContextMenuItemTagMediaMute:
1421             shouldEnable = m_context.hitTestResult().mediaHasAudio();
1422             shouldCheck = shouldEnable &&  m_context.hitTestResult().mediaMuted();
1423             break;
1424     }
1425
1426     item.setChecked(shouldCheck);
1427     item.setEnabled(shouldEnable);
1428 }
1429
1430 #if USE(ACCESSIBILITY_CONTEXT_MENUS)
1431 void ContextMenuController::showContextMenuAt(Frame* frame, const IntPoint& clickPoint)
1432 {
1433     clearContextMenu();
1434     
1435     // Simulate a click in the middle of the accessibility object.
1436     PlatformMouseEvent mouseEvent(clickPoint, clickPoint, RightButton, PlatformEvent::MousePressed, 1, false, false, false, false, currentTime());
1437     frame->eventHandler().handleMousePressEvent(mouseEvent);
1438     bool handled = frame->eventHandler().sendContextMenuEvent(mouseEvent);
1439     if (handled)
1440         m_client.showContextMenu();
1441 }
1442 #endif
1443
1444 #if ENABLE(SERVICE_CONTROLS)
1445 void ContextMenuController::showImageControlsMenu(Event* event)
1446 {
1447     clearContextMenu();
1448     handleContextMenuEvent(event);
1449     m_client.showContextMenu();
1450 }
1451 #endif
1452
1453 } // namespace WebCore
1454
1455 #endif // ENABLE(CONTEXT_MENUS)