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