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