ContextMenuController.cpp 59.6 KB
Newer Older
aroben's avatar
aroben committed
1
/*
darin's avatar
darin committed
2
 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
3
 * Copyright (C) 2010 Igalia S.L
aroben's avatar
aroben committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#include "config.h"
#include "ContextMenuController.h"

bolsinga@apple.com's avatar
bolsinga@apple.com committed
30 31
#if ENABLE(CONTEXT_MENUS)

darin@apple.com's avatar
darin@apple.com committed
32
#include "BackForwardController.h"
aroben's avatar
aroben committed
33
#include "Chrome.h"
aroben's avatar
aroben committed
34 35
#include "ContextMenu.h"
#include "ContextMenuClient.h"
36
#include "ContextMenuItem.h"
37
#include "ContextMenuProvider.h"
bdakin's avatar
bdakin committed
38 39 40 41 42
#include "Document.h"
#include "DocumentFragment.h"
#include "DocumentLoader.h"
#include "Editor.h"
#include "EditorClient.h"
aroben's avatar
aroben committed
43
#include "Event.h"
bdakin's avatar
bdakin committed
44
#include "EventHandler.h"
aroben's avatar
aroben committed
45
#include "EventNames.h"
46
#include "ExceptionCodePlaceholder.h"
darin@apple.com's avatar
darin@apple.com committed
47
#include "FormState.h"
bdakin's avatar
bdakin committed
48 49
#include "Frame.h"
#include "FrameLoadRequest.h"
50
#include "FrameLoader.h"
51
#include "FrameLoaderClient.h"
52
#include "FrameSelection.h"
53
#include "HTMLFormElement.h"
aroben's avatar
aroben committed
54 55
#include "HitTestRequest.h"
#include "HitTestResult.h"
56
#include "InspectorController.h"
57
#include "LocalizedStrings.h"
aroben's avatar
aroben committed
58
#include "MouseEvent.h"
59
#include "NavigationAction.h"
aroben's avatar
aroben committed
60
#include "Node.h"
bdakin's avatar
bdakin committed
61
#include "Page.h"
62
#include "PlatformEvent.h"
aroben's avatar
aroben committed
63
#include "RenderObject.h"
bdakin's avatar
bdakin committed
64 65
#include "ReplaceSelectionCommand.h"
#include "ResourceRequest.h"
66
#include "Settings.h"
darin's avatar
darin committed
67
#include "TextIterator.h"
68
#include "TypingCommand.h"
69
#include "UserTypingGestureIndicator.h"
tristan's avatar
qt:  
tristan committed
70
#include "WindowFeatures.h"
bdakin's avatar
bdakin committed
71
#include "markup.h"
72
#include <wtf/unicode/CharacterNames.h>
73 74
#include <wtf/unicode/Unicode.h>

75 76 77 78
#if PLATFORM(GTK)
#include <wtf/gobject/GOwnPtr.h>
#endif

79 80
using namespace WTF;
using namespace Unicode;
aroben's avatar
aroben committed
81 82 83

namespace WebCore {

84 85 86
ContextMenuController::ContextMenuController(Page* page, ContextMenuClient* client)
    : m_page(page)
    , m_client(client)
aroben's avatar
aroben committed
87
{
88 89
    ASSERT_ARG(page, page);
    ASSERT_ARG(client, client);
aroben's avatar
aroben committed
90 91 92 93
}

ContextMenuController::~ContextMenuController()
{
ggaren's avatar
ggaren committed
94
    m_client->contextMenuDestroyed();
aroben's avatar
aroben committed
95 96
}

97 98 99 100 101
PassOwnPtr<ContextMenuController> ContextMenuController::create(Page* page, ContextMenuClient* client)
{
    return adoptPtr(new ContextMenuController(page, client));
}

bdakin's avatar
bdakin committed
102 103
void ContextMenuController::clearContextMenu()
{
104
    m_contextMenu.clear();
105 106 107
    if (m_menuProvider)
        m_menuProvider->contextMenuCleared();
    m_menuProvider = 0;
bdakin's avatar
bdakin committed
108 109
}

aroben's avatar
aroben committed
110 111
void ContextMenuController::handleContextMenuEvent(Event* event)
{
112
    m_contextMenu = createContextMenu(event);
113
    if (!m_contextMenu)
adele's avatar
adele committed
114
        return;
115 116 117

    populate();

118 119 120
    showContextMenu(event);
}

121 122 123 124 125
static PassOwnPtr<ContextMenuItem> separatorItem()
{
    return adoptPtr(new ContextMenuItem(SeparatorType, ContextMenuItemTagNoAction, String()));
}

126
void ContextMenuController::showContextMenu(Event* event, PassRefPtr<ContextMenuProvider> menuProvider)
127
{
128
    m_menuProvider = menuProvider;
129

130
    m_contextMenu = createContextMenu(event);
131 132 133 134
    if (!m_contextMenu) {
        clearContextMenu();
        return;
    }
135 136

    m_menuProvider->populateContextMenu(m_contextMenu.get());
137 138 139 140
    if (m_hitTestResult.isSelected()) {
        appendItem(*separatorItem(), m_contextMenu.get());
        populate();
    }
141 142 143
    showContextMenu(event);
}

144
PassOwnPtr<ContextMenu> ContextMenuController::createContextMenu(Event* event)
145
{
146 147
    ASSERT(event);
    
148
    if (!event->isMouseEvent())
149
        return nullptr;
150

aroben's avatar
aroben committed
151
    MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
152
    HitTestResult result(mouseEvent->absoluteLocation());
aroben's avatar
aroben committed
153

154
    if (Frame* frame = event->target()->toNode()->document()->frame())
155
        result = frame->eventHandler()->hitTestResultAtPoint(mouseEvent->absoluteLocation());
156

bdakin's avatar
bdakin committed
157
    if (!result.innerNonSharedNode())
158
        return nullptr;
159 160 161

    m_hitTestResult = result;

162
    return adoptPtr(new ContextMenu);
163
}
aroben's avatar
aroben committed
164

165 166
void ContextMenuController::showContextMenu(Event* event)
{
bolsinga@apple.com's avatar
bolsinga@apple.com committed
167
#if ENABLE(INSPECTOR)
thatcher's avatar
thatcher committed
168
    if (m_page->inspectorController()->enabled())
169
        addInspectElementItem();
bolsinga@apple.com's avatar
bolsinga@apple.com committed
170
#endif
171 172 173 174

#if USE(CROSS_PLATFORM_CONTEXT_MENUS)
    m_contextMenu = m_client->customizeMenu(m_contextMenu.release());
#else
bdakin's avatar
bdakin committed
175 176
    PlatformMenuDescription customMenu = m_client->getCustomMenuFromDefaultItems(m_contextMenu.get());
    m_contextMenu->setPlatformDescription(customMenu);
177
#endif
aroben's avatar
aroben committed
178
    event->setDefaultHandled();
aroben's avatar
aroben committed
179 180
}

181
static void openNewWindow(const KURL& urlToLoad, Frame* frame)
bdakin's avatar
bdakin committed
182
{
tristan's avatar
qt:  
tristan committed
183
    if (Page* oldPage = frame->page()) {
184
        FrameLoadRequest request(frame->document()->securityOrigin(), ResourceRequest(urlToLoad, frame->loader()->outgoingReferrer()));
185 186
        Page* newPage = oldPage;
        if (!frame->settings() || frame->settings()->supportsMultipleWindows()) {
187
            newPage = oldPage->chrome().createWindow(frame, request, WindowFeatures(), NavigationAction(request.resourceRequest()));
188 189
            if (!newPage)
                return;
190
            newPage->chrome().show();
191
        }
192
        newPage->mainFrame()->loader()->loadFrameRequest(request, false, false, 0, 0, MaybeSendReferrer);
tristan's avatar
qt:  
tristan committed
193
    }
bdakin's avatar
bdakin committed
194 195
}

196 197 198 199
#if PLATFORM(GTK)
static void insertUnicodeCharacter(UChar character, Frame* frame)
{
    String text(&character, 1);
200
    if (!frame->editor().shouldInsertText(text, frame->selection()->toNormalizedRange().get(), EditorInsertActionTyped))
201 202 203 204 205 206
        return;

    TypingCommand::insertText(frame->document(), text, 0, TypingCommand::TextCompositionNone);
}
#endif

aroben's avatar
aroben committed
207
void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item)
bdakin's avatar
bdakin committed
208
{
209
    ASSERT(item->type() == ActionType || item->type() == CheckableActionType);
aroben's avatar
aroben committed
210 211

    if (item->action() >= ContextMenuItemBaseApplicationTag) {
bdakin's avatar
bdakin committed
212
        m_client->contextMenuItemSelected(item, m_contextMenu.get());
aroben's avatar
aroben committed
213 214 215
        return;
    }

216
    if (item->action() >= ContextMenuItemBaseCustomTag) {
217 218
        ASSERT(m_menuProvider);
        m_menuProvider->contextMenuItemSelected(item);
219 220 221
        return;
    }

222
    Frame* frame = m_hitTestResult.innerNonSharedNode()->document()->frame();
bdakin's avatar
bdakin committed
223 224
    if (!frame)
        return;
225

aroben's avatar
aroben committed
226
    switch (item->action()) {
227
    case ContextMenuItemTagOpenLinkInNewWindow:
228
        openNewWindow(m_hitTestResult.absoluteLinkURL(), frame);
229 230 231
        break;
    case ContextMenuItemTagDownloadLinkToDisk:
        // FIXME: Some day we should be able to do this from within WebCore.
232
        m_client->downloadURL(m_hitTestResult.absoluteLinkURL());
233 234
        break;
    case ContextMenuItemTagCopyLinkToClipboard:
235
        frame->editor().copyURL(m_hitTestResult.absoluteLinkURL(), m_hitTestResult.textContent());
236 237
        break;
    case ContextMenuItemTagOpenImageInNewWindow:
238
        openNewWindow(m_hitTestResult.absoluteImageURL(), frame);
239 240 241
        break;
    case ContextMenuItemTagDownloadImageToDisk:
        // FIXME: Some day we should be able to do this from within WebCore.
242
        m_client->downloadURL(m_hitTestResult.absoluteImageURL());
243 244 245 246
        break;
    case ContextMenuItemTagCopyImageToClipboard:
        // FIXME: The Pasteboard class is not written yet
        // 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 253
        break;
#endif
254
    case ContextMenuItemTagOpenMediaInNewWindow:
255
        openNewWindow(m_hitTestResult.absoluteMediaURL(), frame);
256 257
        break;
    case ContextMenuItemTagCopyMediaLinkToClipboard:
258
        frame->editor().copyURL(m_hitTestResult.absoluteMediaURL(), m_hitTestResult.textContent());
259 260
        break;
    case ContextMenuItemTagToggleMediaControls:
261
        m_hitTestResult.toggleMediaControlsDisplay();
262 263
        break;
    case ContextMenuItemTagToggleMediaLoop:
264
        m_hitTestResult.toggleMediaLoopPlayback();
265
        break;
266 267 268
    case ContextMenuItemTagToggleVideoFullscreen:
        m_hitTestResult.toggleMediaFullscreenState();
        break;
269
    case ContextMenuItemTagEnterVideoFullscreen:
270
        m_hitTestResult.enterFullscreenForVideo();
271 272
        break;
    case ContextMenuItemTagMediaPlayPause:
273
        m_hitTestResult.toggleMediaPlayState();
274 275
        break;
    case ContextMenuItemTagMediaMute:
276
        m_hitTestResult.toggleMediaMuteState();
277
        break;
278 279 280 281 282 283 284 285 286
    case ContextMenuItemTagOpenFrameInNewWindow: {
        DocumentLoader* loader = frame->loader()->documentLoader();
        if (!loader->unreachableURL().isEmpty())
            openNewWindow(loader->unreachableURL(), frame);
        else
            openNewWindow(loader->url(), frame);
        break;
    }
    case ContextMenuItemTagCopy:
287
        frame->editor().copy();
288 289
        break;
    case ContextMenuItemTagGoBack:
290
        if (Page* page = frame->page())
291
            page->backForward()->goBackOrForward(-1);
292 293
        break;
    case ContextMenuItemTagGoForward:
294
        if (Page* page = frame->page())
295
            page->backForward()->goBackOrForward(1);
296 297 298 299 300 301 302 303
        break;
    case ContextMenuItemTagStop:
        frame->loader()->stop();
        break;
    case ContextMenuItemTagReload:
        frame->loader()->reload();
        break;
    case ContextMenuItemTagCut:
304
        frame->editor().command("Cut").execute();
305 306
        break;
    case ContextMenuItemTagPaste:
307
        frame->editor().command("Paste").execute();
308
        break;
309
#if PLATFORM(GTK)
310
    case ContextMenuItemTagDelete:
311
        frame->editor().performDelete();
312
        break;
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
    case ContextMenuItemTagUnicodeInsertLRMMark:
        insertUnicodeCharacter(leftToRightMark, frame);
        break;
    case ContextMenuItemTagUnicodeInsertRLMMark:
        insertUnicodeCharacter(rightToLeftMark, frame);
        break;
    case ContextMenuItemTagUnicodeInsertLREMark:
        insertUnicodeCharacter(leftToRightEmbed, frame);
        break;
    case ContextMenuItemTagUnicodeInsertRLEMark:
        insertUnicodeCharacter(rightToLeftEmbed, frame);
        break;
    case ContextMenuItemTagUnicodeInsertLROMark:
        insertUnicodeCharacter(leftToRightOverride, frame);
        break;
    case ContextMenuItemTagUnicodeInsertRLOMark:
        insertUnicodeCharacter(rightToLeftOverride, frame);
        break;
    case ContextMenuItemTagUnicodeInsertPDFMark:
        insertUnicodeCharacter(popDirectionalFormatting, frame);
        break;
    case ContextMenuItemTagUnicodeInsertZWSMark:
        insertUnicodeCharacter(zeroWidthSpace, frame);
        break;
    case ContextMenuItemTagUnicodeInsertZWJMark:
        insertUnicodeCharacter(zeroWidthJoiner, frame);
        break;
    case ContextMenuItemTagUnicodeInsertZWNJMark:
        insertUnicodeCharacter(zeroWidthNonJoiner, frame);
        break;
343
#endif
344
#if PLATFORM(GTK) || PLATFORM(QT) || PLATFORM(EFL)
345
    case ContextMenuItemTagSelectAll:
346
        frame->editor().command("SelectAll").execute();
347
        break;
348
#endif
349 350
    case ContextMenuItemTagSpellingGuess: {
        FrameSelection* frameSelection = frame->selection();
351
        if (frame->editor().shouldInsertText(item->title(), frameSelection->toNormalizedRange().get(), EditorInsertActionPasted)) {
352
            Document* document = frame->document();
353 354
            ReplaceSelectionCommand::CommandOptions replaceOptions = ReplaceSelectionCommand::MatchStyle | ReplaceSelectionCommand::PreventNesting;

355
            if (frame->editor().behavior().shouldAllowSpellingSuggestionsWithoutSelection()) {
356 357 358 359 360
                ASSERT(frameSelection->isCaretOrRange());
                VisibleSelection wordSelection(frameSelection->base());
                wordSelection.expandUsingGranularity(WordGranularity);
                frameSelection->setSelection(wordSelection);
            } else {
361
                ASSERT(frame->editor().selectedText().length());
362 363 364 365
                replaceOptions |= ReplaceSelectionCommand::SelectReplacement;
            }

            RefPtr<ReplaceSelectionCommand> command = ReplaceSelectionCommand::create(document, createFragmentFromMarkup(document, item->title(), ""), replaceOptions);
366
            applyCommand(command);
367
            frameSelection->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
bdakin's avatar
bdakin committed
368
        }
369
        break;
370
    }
371
    case ContextMenuItemTagIgnoreSpelling:
372
        frame->editor().ignoreSpelling();
373 374
        break;
    case ContextMenuItemTagLearnSpelling:
375
        frame->editor().learnSpelling();
376 377 378 379 380 381 382 383 384
        break;
    case ContextMenuItemTagSearchWeb:
        m_client->searchWithGoogle(frame);
        break;
    case ContextMenuItemTagLookUpInDictionary:
        // FIXME: Some day we may be able to do this from within WebCore.
        m_client->lookUpInDictionary(frame);
        break;
    case ContextMenuItemTagOpenLink:
385
        if (Frame* targetFrame = m_hitTestResult.targetFrame())
386
            targetFrame->loader()->loadFrameRequest(FrameLoadRequest(frame->document()->securityOrigin(), ResourceRequest(m_hitTestResult.absoluteLinkURL(), frame->loader()->outgoingReferrer())), false, false, 0, 0, MaybeSendReferrer);
387
        else
388
            openNewWindow(m_hitTestResult.absoluteLinkURL(), frame);
389
        break;
390 391 392
    case ContextMenuItemTagOpenLinkInThisWindow:
        frame->loader()->loadFrameRequest(FrameLoadRequest(frame->document()->securityOrigin(), ResourceRequest(m_hitTestResult.absoluteLinkURL(), frame->loader()->outgoingReferrer())), false, false, 0, 0, MaybeSendReferrer);
        break;
393
    case ContextMenuItemTagBold:
394
        frame->editor().command("ToggleBold").execute();
395 396
        break;
    case ContextMenuItemTagItalic:
397
        frame->editor().command("ToggleItalic").execute();
398 399
        break;
    case ContextMenuItemTagUnderline:
400
        frame->editor().toggleUnderline();
401 402 403 404 405 406 407
        break;
    case ContextMenuItemTagOutline:
        // We actually never enable this because CSS does not have a way to specify an outline font,
        // which may make this difficult to implement. Maybe a special case of text-shadow?
        break;
    case ContextMenuItemTagStartSpeaking: {
        RefPtr<Range> selectedRange = frame->selection()->toNormalizedRange();
408
        if (!selectedRange || selectedRange->collapsed(IGNORE_EXCEPTION)) {
409
            Document* document = m_hitTestResult.innerNonSharedNode()->document();
410
            selectedRange = document->createRange();
411
            selectedRange->selectNode(document->documentElement(), IGNORE_EXCEPTION);
412 413 414 415 416 417 418 419
        }
        m_client->speak(plainText(selectedRange.get()));
        break;
    }
    case ContextMenuItemTagStopSpeaking:
        m_client->stopSpeaking();
        break;
    case ContextMenuItemTagDefaultDirection:
420
        frame->editor().setBaseWritingDirection(NaturalWritingDirection);
421 422
        break;
    case ContextMenuItemTagLeftToRight:
423
        frame->editor().setBaseWritingDirection(LeftToRightWritingDirection);
424 425
        break;
    case ContextMenuItemTagRightToLeft:
426
        frame->editor().setBaseWritingDirection(RightToLeftWritingDirection);
427 428
        break;
    case ContextMenuItemTagTextDirectionDefault:
429
        frame->editor().command("MakeTextWritingDirectionNatural").execute();
430 431
        break;
    case ContextMenuItemTagTextDirectionLeftToRight:
432
        frame->editor().command("MakeTextWritingDirectionLeftToRight").execute();
433 434
        break;
    case ContextMenuItemTagTextDirectionRightToLeft:
435
        frame->editor().command("MakeTextWritingDirectionRightToLeft").execute();
436
        break;
bdakin's avatar
bdakin committed
437
#if PLATFORM(MAC)
438 439 440
    case ContextMenuItemTagSearchInSpotlight:
        m_client->searchWithSpotlight();
        break;
441
#endif
442
    case ContextMenuItemTagShowSpellingPanel:
443
        frame->editor().showSpellingGuessPanel();
444 445
        break;
    case ContextMenuItemTagCheckSpelling:
446
        frame->editor().advanceToNextMisspelling();
447 448
        break;
    case ContextMenuItemTagCheckSpellingWhileTyping:
449
        frame->editor().toggleContinuousSpellChecking();
450
        break;
451
#if USE(GRAMMAR_CHECKING)
452
    case ContextMenuItemTagCheckGrammarWithSpelling:
453
        frame->editor().toggleGrammarChecking();
454
        break;
455
#endif
456
#if PLATFORM(MAC)
457
    case ContextMenuItemTagShowFonts:
458
        frame->editor().showFontPanel();
459 460
        break;
    case ContextMenuItemTagStyles:
461
        frame->editor().showStylesPanel();
462 463
        break;
    case ContextMenuItemTagShowColors:
464
        frame->editor().showColorPanel();
465
        break;
adele@apple.com's avatar
adele@apple.com committed
466
#endif
467
#if USE(APPKIT)
468
    case ContextMenuItemTagMakeUpperCase:
469
        frame->editor().uppercaseWord();
470 471
        break;
    case ContextMenuItemTagMakeLowerCase:
472
        frame->editor().lowercaseWord();
473 474
        break;
    case ContextMenuItemTagCapitalize:
475
        frame->editor().capitalizeWord();
476
        break;
477 478
#endif
#if PLATFORM(MAC)
479
    case ContextMenuItemTagChangeBack:
480
        frame->editor().changeBackToReplacedString(m_hitTestResult.replacedString());
481 482 483
        break;
#endif
#if USE(AUTOMATIC_TEXT_REPLACEMENT)
484
    case ContextMenuItemTagShowSubstitutions:
485
        frame->editor().showSubstitutionsPanel();
486 487
        break;
    case ContextMenuItemTagSmartCopyPaste:
488
        frame->editor().toggleSmartInsertDelete();
489 490
        break;
    case ContextMenuItemTagSmartQuotes:
491
        frame->editor().toggleAutomaticQuoteSubstitution();
492 493
        break;
    case ContextMenuItemTagSmartDashes:
494
        frame->editor().toggleAutomaticDashSubstitution();
495 496
        break;
    case ContextMenuItemTagSmartLinks:
497
        frame->editor().toggleAutomaticLinkDetection();
498 499
        break;
    case ContextMenuItemTagTextReplacement:
500
        frame->editor().toggleAutomaticTextReplacement();
501 502
        break;
    case ContextMenuItemTagCorrectSpellingAutomatically:
503
        frame->editor().toggleAutomaticSpellingCorrection();
504
        break;
bdakin's avatar
bdakin committed
505
#endif
bolsinga@apple.com's avatar
bolsinga@apple.com committed
506
#if ENABLE(INSPECTOR)
507 508
    case ContextMenuItemTagInspectElement:
        if (Page* page = frame->page())
509
            page->inspectorController()->inspect(m_hitTestResult.innerNonSharedNode());
510
        break;
bolsinga@apple.com's avatar
bolsinga@apple.com committed
511
#endif
512
    case ContextMenuItemTagDictationAlternative:
513
        frame->editor().applyDictationAlternativelternative(item->title());
514
        break;
515 516
    default:
        break;
bdakin's avatar
bdakin committed
517
    }
aroben's avatar
aroben committed
518 519
}

520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571
void ContextMenuController::appendItem(ContextMenuItem& menuItem, ContextMenu* parentMenu)
{
    checkOrEnableIfNeeded(menuItem);
    if (parentMenu)
        parentMenu->appendItem(menuItem);
}

void ContextMenuController::createAndAppendFontSubMenu(ContextMenuItem& fontMenuItem)
{
    ContextMenu fontMenu;

#if PLATFORM(MAC)
    ContextMenuItem showFonts(ActionType, ContextMenuItemTagShowFonts, contextMenuItemTagShowFonts());
#endif
    ContextMenuItem bold(CheckableActionType, ContextMenuItemTagBold, contextMenuItemTagBold());
    ContextMenuItem italic(CheckableActionType, ContextMenuItemTagItalic, contextMenuItemTagItalic());
    ContextMenuItem underline(CheckableActionType, ContextMenuItemTagUnderline, contextMenuItemTagUnderline());
    ContextMenuItem outline(ActionType, ContextMenuItemTagOutline, contextMenuItemTagOutline());
#if PLATFORM(MAC)
    ContextMenuItem styles(ActionType, ContextMenuItemTagStyles, contextMenuItemTagStyles());
    ContextMenuItem showColors(ActionType, ContextMenuItemTagShowColors, contextMenuItemTagShowColors());
#endif

#if PLATFORM(MAC)
    appendItem(showFonts, &fontMenu);
#endif
    appendItem(bold, &fontMenu);
    appendItem(italic, &fontMenu);
    appendItem(underline, &fontMenu);
    appendItem(outline, &fontMenu);
#if PLATFORM(MAC)
    appendItem(styles, &fontMenu);
    appendItem(*separatorItem(), &fontMenu);
    appendItem(showColors, &fontMenu);
#endif

    fontMenuItem.setSubMenu(&fontMenu);
}


#if !PLATFORM(GTK)

void ContextMenuController::createAndAppendSpellingAndGrammarSubMenu(ContextMenuItem& spellingAndGrammarMenuItem)
{
    ContextMenu spellingAndGrammarMenu;

    ContextMenuItem showSpellingPanel(ActionType, ContextMenuItemTagShowSpellingPanel, 
        contextMenuItemTagShowSpellingPanel(true));
    ContextMenuItem checkSpelling(ActionType, ContextMenuItemTagCheckSpelling, 
        contextMenuItemTagCheckSpelling());
    ContextMenuItem checkAsYouType(CheckableActionType, ContextMenuItemTagCheckSpellingWhileTyping, 
        contextMenuItemTagCheckSpellingWhileTyping());
572
#if USE(GRAMMAR_CHECKING)
573 574
    ContextMenuItem grammarWithSpelling(CheckableActionType, ContextMenuItemTagCheckGrammarWithSpelling, 
        contextMenuItemTagCheckGrammarWithSpelling());
575
#endif
576
#if PLATFORM(MAC)
577 578 579 580 581 582
    ContextMenuItem correctSpelling(CheckableActionType, ContextMenuItemTagCorrectSpellingAutomatically, 
        contextMenuItemTagCorrectSpellingAutomatically());
#endif

    appendItem(showSpellingPanel, &spellingAndGrammarMenu);
    appendItem(checkSpelling, &spellingAndGrammarMenu);
583
#if PLATFORM(MAC)
584 585 586
    appendItem(*separatorItem(), &spellingAndGrammarMenu);
#endif
    appendItem(checkAsYouType, &spellingAndGrammarMenu);
587
#if USE(GRAMMAR_CHECKING)
588
    appendItem(grammarWithSpelling, &spellingAndGrammarMenu);
589
#endif
590
#if PLATFORM(MAC)
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
    appendItem(correctSpelling, &spellingAndGrammarMenu);
#endif

    spellingAndGrammarMenuItem.setSubMenu(&spellingAndGrammarMenu);
}

#endif // !PLATFORM(GTK)


#if PLATFORM(MAC)

void ContextMenuController::createAndAppendSpeechSubMenu(ContextMenuItem& speechMenuItem)
{
    ContextMenu speechMenu;

    ContextMenuItem start(ActionType, ContextMenuItemTagStartSpeaking, contextMenuItemTagStartSpeaking());
    ContextMenuItem stop(ActionType, ContextMenuItemTagStopSpeaking, contextMenuItemTagStopSpeaking());

    appendItem(start, &speechMenu);
    appendItem(stop, &speechMenu);

    speechMenuItem.setSubMenu(&speechMenu);
}

#endif
 
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648
#if PLATFORM(GTK)

void ContextMenuController::createAndAppendUnicodeSubMenu(ContextMenuItem& unicodeMenuItem)
{
    ContextMenu unicodeMenu;

    ContextMenuItem leftToRightMarkMenuItem(ActionType, ContextMenuItemTagUnicodeInsertLRMMark, contextMenuItemTagUnicodeInsertLRMMark());
    ContextMenuItem rightToLeftMarkMenuItem(ActionType, ContextMenuItemTagUnicodeInsertRLMMark, contextMenuItemTagUnicodeInsertRLMMark());
    ContextMenuItem leftToRightEmbedMenuItem(ActionType, ContextMenuItemTagUnicodeInsertLREMark, contextMenuItemTagUnicodeInsertLREMark());
    ContextMenuItem rightToLeftEmbedMenuItem(ActionType, ContextMenuItemTagUnicodeInsertRLEMark, contextMenuItemTagUnicodeInsertRLEMark());
    ContextMenuItem leftToRightOverrideMenuItem(ActionType, ContextMenuItemTagUnicodeInsertLROMark, contextMenuItemTagUnicodeInsertLROMark());
    ContextMenuItem rightToLeftOverrideMenuItem(ActionType, ContextMenuItemTagUnicodeInsertRLOMark, contextMenuItemTagUnicodeInsertRLOMark());
    ContextMenuItem popDirectionalFormattingMenuItem(ActionType, ContextMenuItemTagUnicodeInsertPDFMark, contextMenuItemTagUnicodeInsertPDFMark());
    ContextMenuItem zeroWidthSpaceMenuItem(ActionType, ContextMenuItemTagUnicodeInsertZWSMark, contextMenuItemTagUnicodeInsertZWSMark());
    ContextMenuItem zeroWidthJoinerMenuItem(ActionType, ContextMenuItemTagUnicodeInsertZWJMark, contextMenuItemTagUnicodeInsertZWJMark());
    ContextMenuItem zeroWidthNonJoinerMenuItem(ActionType, ContextMenuItemTagUnicodeInsertZWNJMark, contextMenuItemTagUnicodeInsertZWNJMark());

    appendItem(leftToRightMarkMenuItem, &unicodeMenu);
    appendItem(rightToLeftMarkMenuItem, &unicodeMenu);
    appendItem(leftToRightEmbedMenuItem, &unicodeMenu);
    appendItem(rightToLeftEmbedMenuItem, &unicodeMenu);
    appendItem(leftToRightOverrideMenuItem, &unicodeMenu);
    appendItem(rightToLeftOverrideMenuItem, &unicodeMenu);
    appendItem(popDirectionalFormattingMenuItem, &unicodeMenu);
    appendItem(zeroWidthSpaceMenuItem, &unicodeMenu);
    appendItem(zeroWidthJoinerMenuItem, &unicodeMenu);
    appendItem(zeroWidthNonJoinerMenuItem, &unicodeMenu);

    unicodeMenuItem.setSubMenu(&unicodeMenu);
}

#else
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682

void ContextMenuController::createAndAppendWritingDirectionSubMenu(ContextMenuItem& writingDirectionMenuItem)
{
    ContextMenu writingDirectionMenu;

    ContextMenuItem defaultItem(ActionType, ContextMenuItemTagDefaultDirection, 
        contextMenuItemTagDefaultDirection());
    ContextMenuItem ltr(CheckableActionType, ContextMenuItemTagLeftToRight, contextMenuItemTagLeftToRight());
    ContextMenuItem rtl(CheckableActionType, ContextMenuItemTagRightToLeft, contextMenuItemTagRightToLeft());

    appendItem(defaultItem, &writingDirectionMenu);
    appendItem(ltr, &writingDirectionMenu);
    appendItem(rtl, &writingDirectionMenu);

    writingDirectionMenuItem.setSubMenu(&writingDirectionMenu);
}

void ContextMenuController::createAndAppendTextDirectionSubMenu(ContextMenuItem& textDirectionMenuItem)
{
    ContextMenu textDirectionMenu;

    ContextMenuItem defaultItem(ActionType, ContextMenuItemTagTextDirectionDefault, contextMenuItemTagDefaultDirection());
    ContextMenuItem ltr(CheckableActionType, ContextMenuItemTagTextDirectionLeftToRight, contextMenuItemTagLeftToRight());
    ContextMenuItem rtl(CheckableActionType, ContextMenuItemTagTextDirectionRightToLeft, contextMenuItemTagRightToLeft());

    appendItem(defaultItem, &textDirectionMenu);
    appendItem(ltr, &textDirectionMenu);
    appendItem(rtl, &textDirectionMenu);

    textDirectionMenuItem.setSubMenu(&textDirectionMenu);
}

#endif

683
#if PLATFORM(MAC)
684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729

void ContextMenuController::createAndAppendSubstitutionsSubMenu(ContextMenuItem& substitutionsMenuItem)
{
    ContextMenu substitutionsMenu;

    ContextMenuItem showSubstitutions(ActionType, ContextMenuItemTagShowSubstitutions, contextMenuItemTagShowSubstitutions(true));
    ContextMenuItem smartCopyPaste(CheckableActionType, ContextMenuItemTagSmartCopyPaste, contextMenuItemTagSmartCopyPaste());
    ContextMenuItem smartQuotes(CheckableActionType, ContextMenuItemTagSmartQuotes, contextMenuItemTagSmartQuotes());
    ContextMenuItem smartDashes(CheckableActionType, ContextMenuItemTagSmartDashes, contextMenuItemTagSmartDashes());
    ContextMenuItem smartLinks(CheckableActionType, ContextMenuItemTagSmartLinks, contextMenuItemTagSmartLinks());
    ContextMenuItem textReplacement(CheckableActionType, ContextMenuItemTagTextReplacement, contextMenuItemTagTextReplacement());

    appendItem(showSubstitutions, &substitutionsMenu);
    appendItem(*separatorItem(), &substitutionsMenu);
    appendItem(smartCopyPaste, &substitutionsMenu);
    appendItem(smartQuotes, &substitutionsMenu);
    appendItem(smartDashes, &substitutionsMenu);
    appendItem(smartLinks, &substitutionsMenu);
    appendItem(textReplacement, &substitutionsMenu);

    substitutionsMenuItem.setSubMenu(&substitutionsMenu);
}

void ContextMenuController::createAndAppendTransformationsSubMenu(ContextMenuItem& transformationsMenuItem)
{
    ContextMenu transformationsMenu;

    ContextMenuItem makeUpperCase(ActionType, ContextMenuItemTagMakeUpperCase, contextMenuItemTagMakeUpperCase());
    ContextMenuItem makeLowerCase(ActionType, ContextMenuItemTagMakeLowerCase, contextMenuItemTagMakeLowerCase());
    ContextMenuItem capitalize(ActionType, ContextMenuItemTagCapitalize, contextMenuItemTagCapitalize());

    appendItem(makeUpperCase, &transformationsMenu);
    appendItem(makeLowerCase, &transformationsMenu);
    appendItem(capitalize, &transformationsMenu);

    transformationsMenuItem.setSubMenu(&transformationsMenu);
}

#endif

static bool selectionContainsPossibleWord(Frame* frame)
{
    // Current algorithm: look for a character that's not just a separator.
    for (TextIterator it(frame->selection()->toNormalizedRange().get()); !it.atEnd(); it.advance()) {
        int length = it.length();
        for (int i = 0; i < length; ++i)
730
            if (!(category(it.characterAt(i)) & (Separator_Space | Separator_Line | Separator_Paragraph)))
731 732 733 734 735
                return true;
    }
    return false;
}

736
#if PLATFORM(MAC)
737
#if __MAC_OS_X_VERSION_MIN_REQUIRED == 1060
738 739 740 741 742 743
#define INCLUDE_SPOTLIGHT_CONTEXT_MENU_ITEM 1
#else
#define INCLUDE_SPOTLIGHT_CONTEXT_MENU_ITEM 0
#endif
#endif

744 745 746 747 748 749
#if PLATFORM(MAC)
#define SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 1
#else
#define SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 0
#endif

750 751 752 753 754 755 756 757 758 759 760 761 762 763 764
void ContextMenuController::populate()
{
    ContextMenuItem OpenLinkItem(ActionType, ContextMenuItemTagOpenLink, contextMenuItemTagOpenLink());
    ContextMenuItem OpenLinkInNewWindowItem(ActionType, ContextMenuItemTagOpenLinkInNewWindow, 
        contextMenuItemTagOpenLinkInNewWindow());
    ContextMenuItem DownloadFileItem(ActionType, ContextMenuItemTagDownloadLinkToDisk, 
        contextMenuItemTagDownloadLinkToDisk());
    ContextMenuItem CopyLinkItem(ActionType, ContextMenuItemTagCopyLinkToClipboard, 
        contextMenuItemTagCopyLinkToClipboard());
    ContextMenuItem OpenImageInNewWindowItem(ActionType, ContextMenuItemTagOpenImageInNewWindow, 
        contextMenuItemTagOpenImageInNewWindow());
    ContextMenuItem DownloadImageItem(ActionType, ContextMenuItemTagDownloadImageToDisk, 
        contextMenuItemTagDownloadImageToDisk());
    ContextMenuItem CopyImageItem(ActionType, ContextMenuItemTagCopyImageToClipboard, 
        contextMenuItemTagCopyImageToClipboard());
765
#if PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL)
766 767 768
    ContextMenuItem CopyImageUrlItem(ActionType, ContextMenuItemTagCopyImageUrlToClipboard, 
        contextMenuItemTagCopyImageUrlToClipboard());
#endif
769 770 771 772 773 774 775 776 777 778 779
    ContextMenuItem OpenMediaInNewWindowItem(ActionType, ContextMenuItemTagOpenMediaInNewWindow, String());
    ContextMenuItem CopyMediaLinkItem(ActionType, ContextMenuItemTagCopyMediaLinkToClipboard, 
        String());
    ContextMenuItem MediaPlayPause(ActionType, ContextMenuItemTagMediaPlayPause, 
        contextMenuItemTagMediaPlay());
    ContextMenuItem MediaMute(ActionType, ContextMenuItemTagMediaMute, 
        contextMenuItemTagMediaMute());
    ContextMenuItem ToggleMediaControls(CheckableActionType, ContextMenuItemTagToggleMediaControls, 
        contextMenuItemTagToggleMediaControls());
    ContextMenuItem ToggleMediaLoop(CheckableActionType, ContextMenuItemTagToggleMediaLoop, 
        contextMenuItemTagToggleMediaLoop());
780 781 782
    ContextMenuItem EnterVideoFullscreen(ActionType, ContextMenuItemTagEnterVideoFullscreen,
        contextMenuItemTagEnterVideoFullscreen());
    ContextMenuItem ToggleVideoFullscreen(ActionType, ContextMenuItemTagToggleVideoFullscreen,
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803
        contextMenuItemTagEnterVideoFullscreen());
#if PLATFORM(MAC)
    ContextMenuItem SearchSpotlightItem(ActionType, ContextMenuItemTagSearchInSpotlight, 
        contextMenuItemTagSearchInSpotlight());
#endif
#if !PLATFORM(GTK)
    ContextMenuItem SearchWebItem(ActionType, ContextMenuItemTagSearchWeb, contextMenuItemTagSearchWeb());
#endif
    ContextMenuItem CopyItem(ActionType, ContextMenuItemTagCopy, contextMenuItemTagCopy());
    ContextMenuItem BackItem(ActionType, ContextMenuItemTagGoBack, contextMenuItemTagGoBack());
    ContextMenuItem ForwardItem(ActionType, ContextMenuItemTagGoForward,  contextMenuItemTagGoForward());
    ContextMenuItem StopItem(ActionType, ContextMenuItemTagStop, contextMenuItemTagStop());
    ContextMenuItem ReloadItem(ActionType, ContextMenuItemTagReload, contextMenuItemTagReload());
    ContextMenuItem OpenFrameItem(ActionType, ContextMenuItemTagOpenFrameInNewWindow, 
        contextMenuItemTagOpenFrameInNewWindow());
    ContextMenuItem NoGuessesItem(ActionType, ContextMenuItemTagNoGuessesFound, 
        contextMenuItemTagNoGuessesFound());
    ContextMenuItem IgnoreSpellingItem(ActionType, ContextMenuItemTagIgnoreSpelling, 
        contextMenuItemTagIgnoreSpelling());
    ContextMenuItem LearnSpellingItem(ActionType, ContextMenuItemTagLearnSpelling, 
        contextMenuItemTagLearnSpelling());
804
#if USE(GRAMMAR_CHECKING)
805 806
    ContextMenuItem IgnoreGrammarItem(ActionType, ContextMenuItemTagIgnoreGrammar, 
        contextMenuItemTagIgnoreGrammar());
807
#endif
808 809 810 811
    ContextMenuItem CutItem(ActionType, ContextMenuItemTagCut, contextMenuItemTagCut());
    ContextMenuItem PasteItem(ActionType, ContextMenuItemTagPaste, contextMenuItemTagPaste());
#if PLATFORM(GTK)
    ContextMenuItem DeleteItem(ActionType, ContextMenuItemTagDelete, contextMenuItemTagDelete());
812
#endif
813
#if PLATFORM(GTK) || PLATFORM(QT) || PLATFORM(EFL)
814 815 816 817 818 819 820
    ContextMenuItem SelectAllItem(ActionType, ContextMenuItemTagSelectAll, contextMenuItemTagSelectAll());
#endif

    Node* node = m_hitTestResult.innerNonSharedNode();
    if (!node)
        return;
#if PLATFORM(GTK)
821
    if (!m_hitTestResult.isContentEditable() && (node->isElementNode() && toElement(node)->isFormControlElement()))
822 823 824 825 826 827 828 829 830 831
        return;
#endif
    Frame* frame = node->document()->frame();
    if (!frame)
        return;

    if (!m_hitTestResult.isContentEditable()) {
        FrameLoader* loader = frame->loader();
        KURL linkURL = m_hitTestResult.absoluteLinkURL();
        if (!linkURL.isEmpty()) {
832
            if (loader->client()->canHandleRequest(ResourceRequest(linkURL))) {
833 834 835 836
                appendItem(OpenLinkItem, m_contextMenu.get());
                appendItem(OpenLinkInNewWindowItem, m_contextMenu.get());
                appendItem(DownloadFileItem, m_contextMenu.get());
            }
837 838 839 840
#if PLATFORM(QT)
            if (m_hitTestResult.isSelected()) 
                appendItem(CopyItem, m_contextMenu.get());
#endif
841 842 843 844 845 846 847 848 849 850 851 852
            appendItem(CopyLinkItem, m_contextMenu.get());
        }

        KURL imageURL = m_hitTestResult.absoluteImageURL();
        if (!imageURL.isEmpty()) {
            if (!linkURL.isEmpty())
                appendItem(*separatorItem(), m_contextMenu.get());

            appendItem(OpenImageInNewWindowItem, m_contextMenu.get());
            appendItem(DownloadImageItem, m_contextMenu.get());
            if (imageURL.isLocalFile() || m_hitTestResult.image())
                appendItem(CopyImageItem, m_contextMenu.get());
853
#if PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL)
854 855
            appendItem(CopyImageUrlItem, m_contextMenu.get());
#endif
856 857 858 859 860 861 862 863 864 865 866
        }

        KURL mediaURL = m_hitTestResult.absoluteMediaURL();
        if (!mediaURL.isEmpty()) {
            if (!linkURL.isEmpty() || !imageURL.isEmpty())
                appendItem(*separatorItem(), m_contextMenu.get());

            appendItem(MediaPlayPause, m_contextMenu.get());
            appendItem(MediaMute, m_contextMenu.get());
            appendItem(ToggleMediaControls, m_contextMenu.get());
            appendItem(ToggleMediaLoop, m_contextMenu.get());
867 868 869
#if SUPPORTS_TOGGLE_VIDEO_FULLSCREEN
            appendItem(ToggleVideoFullscreen, m_contextMenu.get());
#else
870
            appendItem(EnterVideoFullscreen, m_contextMenu.get());
871
#endif
872 873 874 875 876 877 878 879 880
            appendItem(*separatorItem(), m_contextMenu.get());
            appendItem(CopyMediaLinkItem, m_contextMenu.get());
            appendItem(OpenMediaInNewWindowItem, m_contextMenu.get());
        }

        if (imageURL.isEmpty() && linkURL.isEmpty() && mediaURL.isEmpty()) {
            if (m_hitTestResult.isSelected()) {
                if (selectionContainsPossibleWord(frame)) {
#if PLATFORM(MAC)
881
                    String selectedString = frame->displayStringModifiedByEncoding(frame->editor().selectedText());
882 883 884
                    ContextMenuItem LookUpInDictionaryItem(ActionType, ContextMenuItemTagLookUpInDictionary, contextMenuItemTagLookUpInDictionary(selectedString));

#if INCLUDE_SPOTLIGHT_CONTEXT_MENU_ITEM
885
                    appendItem(SearchSpotlightItem, m_contextMenu.get());
886 887 888
#else
                    appendItem(LookUpInDictionaryItem, m_contextMenu.get());
#endif
889
#endif
890

891 892 893 894
#if !PLATFORM(GTK)
                    appendItem(SearchWebItem, m_contextMenu.get());
                    appendItem(*separatorItem(), m_contextMenu.get());
#endif
895 896 897

#if PLATFORM(MAC) && INCLUDE_SPOTLIGHT_CONTEXT_MENU_ITEM
                    appendItem(LookUpInDictionaryItem, m_contextMenu.get());
898 899 900
                    appendItem(*separatorItem(), m_contextMenu.get());
#endif
                }
901

902 903 904
                appendItem(CopyItem, m_contextMenu.get());
#if PLATFORM(MAC)
                appendItem(*separatorItem(), m_contextMenu.get());
905

906 907 908 909 910 911 912 913
                ContextMenuItem SpeechMenuItem(SubmenuType, ContextMenuItemTagSpeechMenu, contextMenuItemTagSpeechMenu());
                createAndAppendSpeechSubMenu(SpeechMenuItem);
                appendItem(SpeechMenuItem, m_contextMenu.get());
#endif                
            } else {
#if ENABLE(INSPECTOR)
                if (!(frame->page() && frame->page()->inspectorController()->hasInspectorFrontendClient())) {
#endif
914

915 916
                // In GTK+ unavailable items are not hidden but insensitive.
#if PLATFORM(GTK)
917 918 919 920 921
                appendItem(BackItem, m_contextMenu.get());
                appendItem(ForwardItem, m_contextMenu.get());
                appendItem(StopItem, m_contextMenu.get());
                appendItem(ReloadItem, m_contextMenu.get());
#else
922 923 924 925 926 927 928 929 930 931 932 933
                if (frame->page() && frame->page()->backForward()->canGoBackOrForward(-1))
                    appendItem(BackItem, m_contextMenu.get());

                if (frame->page() && frame->page()->backForward()->canGoBackOrForward(1))
                    appendItem(ForwardItem, m_contextMenu.get());

                // use isLoadingInAPISense rather than isLoading because Stop/Reload are
                // intended to match WebKit's API, not WebCore's internal notion of loading status
                if (loader->documentLoader()->isLoadingInAPISense())
                    appendItem(StopItem, m_contextMenu.get());
                else
                    appendItem(ReloadItem, m_contextMenu.get());
934
#endif
935 936 937 938 939 940 941 942 943
#if ENABLE(INSPECTOR)
                }
#endif

                if (frame->page() && frame != frame->page()->mainFrame())
                    appendItem(OpenFrameItem, m_contextMenu.get());
            }
        }
    } else { // Make an editing context menu
944
        FrameSelection* selection = frame->selection();
945
        bool inPasswordField = selection->isInPasswordField();
946 947
        if (!inPasswordField) {
            bool haveContextMenuItemsForMisspellingOrGrammer = false;
948
            bool spellCheckingEnabled = frame->editor().isSpellCheckingEnabledFor(node);
949 950 951 952 953
            if (spellCheckingEnabled) {
                // Consider adding spelling-related or grammar-related context menu items (never both, since a single selected range
                // is never considered a misspelling and bad grammar at the same time)
                bool misspelling;
                bool badGrammar;
954
                Vector<String> guesses = frame->editor().guessesForMisspelledOrUngrammatical(misspelling, badGrammar);
955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971
                if (misspelling || badGrammar) {
                    size_t size = guesses.size();
                    if (!size) {
                        // If there's bad grammar but no suggestions (e.g., repeated word), just leave off the suggestions
                        // list and trailing separator rather than adding a "No Guesses Found" item (matches AppKit)
                        if (misspelling) {
                            appendItem(NoGuessesItem, m_contextMenu.get());
                            appendItem(*separatorItem(), m_contextMenu.get());
                        }
                    } else {
                        for (unsigned i = 0; i < size; i++) {
                            const String &guess = guesses[i];
                            if (!guess.isEmpty()) {
                                ContextMenuItem item(ActionType, ContextMenuItemTagSpellingGuess, guess);
                                appendItem(item, m_contextMenu.get());
                            }
                        }
972 973
                        appendItem(*separatorItem(), m_contextMenu.get());
                    }
974 975 976
                    if (misspelling) {
                        appendItem(IgnoreSpellingItem, m_contextMenu.get());
                        appendItem(LearnSpellingItem, m_contextMenu.get());
977 978 979
                    }
#if USE(GRAMMAR_CHECKING)
                    else
980
                        appendItem(IgnoreGrammarItem, m_contextMenu.get());
981 982
#endif

983 984
                    appendItem(*separatorItem(), m_contextMenu.get());
                    haveContextMenuItemsForMisspellingOrGrammer = true;
985
#if PLATFORM(MAC)
986
                } else {
987 988 989 990 991 992 993
                    // If the string was autocorrected, generate a contextual menu item allowing it to be changed back.
                    String replacedString = m_hitTestResult.replacedString();
                    if (!replacedString.isEmpty()) {
                        ContextMenuItem item(ActionType, ContextMenuItemTagChangeBack, contextMenuItemTagChangeBack(replacedString));
                        appendItem(item, m_contextMenu.get());
                        appendItem(*separatorItem(), m_contextMenu.get());
                        haveContextMenuItemsForMisspellingOrGrammer = true;
994
                    }
995
#endif
996
                }
997 998 999 1000 1001 1002 1003 1004 1005 1006
            }

            if (!haveContextMenuItemsForMisspellingOrGrammer) {
                // Spelling and grammar checking is mutually exclusive with dictation alternatives.
                Vector<String> dictationAlternatives = m_hitTestResult.dictationAlternatives();
                if (!dictationAlternatives.isEmpty()) {
                    for (size_t i = 0; i < dictationAlternatives.size(); ++i) {
                        ContextMenuItem item(ActionType, ContextMenuItemTagDictationAlternative, dictationAlternatives[i]);
                        appendItem(item, m_contextMenu.get());
                    }
1007 1008 1009 1010 1011 1012 1013 1014
                    appendItem(*separatorItem(), m_contextMenu.get());
                }
            }
        }

        FrameLoader* loader = frame->loader();
        KURL linkURL = m_hitTestResult.absoluteLinkURL();
        if (!linkURL.isEmpty()) {
1015
            if (loader->client()->canHandleRequest(ResourceRequest(linkURL))) {
1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
                appendItem(OpenLinkItem, m_contextMenu.get());
                appendItem(OpenLinkInNewWindowItem, m_contextMenu.get());
                appendItem(DownloadFileItem, m_contextMenu.get());
            }
            appendItem(CopyLinkItem, m_contextMenu.get());
            appendItem(*separatorItem(), m_contextMenu.get());
        }

        if (m_hitTestResult.isSelected() && !inPasswordField && selectionContainsPossibleWord(frame)) {
#if PLATFORM(MAC)
1026
            String selectedString = frame->displayStringModifiedByEncoding(frame->editor().selectedText());
1027 1028 1029
            ContextMenuItem LookUpInDictionaryItem(ActionType, ContextMenuItemTagLookUpInDictionary, contextMenuItemTagLookUpInDictionary(selectedString));

#if INCLUDE_SPOTLIGHT_CONTEXT_MENU_ITEM
1030
            appendItem(SearchSpotlightItem, m_contextMenu.get());
1031 1032 1033
#else
            appendItem(LookUpInDictionaryItem, m_contextMenu.get());
#endif
1034
#endif
1035

1036 1037 1038 1039
#if !PLATFORM(GTK)
            appendItem(SearchWebItem, m_contextMenu.get());
            appendItem(*separatorItem(), m_contextMenu.get());
#endif
1040 1041 1042

#if PLATFORM(MAC) && INCLUDE_SPOTLIGHT_CONTEXT_MENU_ITEM
            appendItem(LookUpInDictionaryItem, m_contextMenu.get());
1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
            appendItem(*separatorItem(), m_contextMenu.get());
#endif
        }

        appendItem(CutItem, m_contextMenu.get());
        appendItem(CopyItem, m_contextMenu.get());
        appendItem(PasteItem, m_contextMenu.get());
#if PLATFORM(GTK)
        appendItem(DeleteItem, m_contextMenu.get());
        appendItem(*separatorItem(), m_contextMenu.get());
1053
#endif
1054
#if PLATFORM(GTK) || PLATFORM(QT) || PLATFORM(EFL)
1055 1056 1057 1058 1059
        appendItem(SelectAllItem, m_contextMenu.get());
#endif

        if (!inPasswordField) {
#if !PLATFORM(GTK)
1060
            appendItem(*separatorItem(), m_contextMenu.get());
1061 1062 1063 1064 1065
            ContextMenuItem SpellingAndGrammarMenuItem(SubmenuType, ContextMenuItemTagSpellingMenu, 
                contextMenuItemTagSpellingMenu());
            createAndAppendSpellingAndGrammarSubMenu(SpellingAndGrammarMenuItem);
            appendItem(SpellingAndGrammarMenuItem, m_contextMenu.get());
#endif
1066
#if PLATFORM(MAC)
1067 1068 1069 1070 1071 1072 1073 1074 1075 1076
            ContextMenuItem substitutionsMenuItem(SubmenuType, ContextMenuItemTagSubstitutionsMenu, 
                contextMenuItemTagSubstitutionsMenu());
            createAndAppendSubstitutionsSubMenu(substitutionsMenuItem);
            appendItem(substitutionsMenuItem, m_contextMenu.get());
            ContextMenuItem transformationsMenuItem(SubmenuType, ContextMenuItemTagTransformationsMenu, 
                contextMenuItemTagTransformationsMenu());
            createAndAppendTransformationsSubMenu(transformationsMenuItem);
            appendItem(transformationsMenuItem, m_contextMenu.get());
#endif
#if PLATFORM(GTK)
1077
            bool shouldShowFontMenu = frame->editor().canEditRichly();
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
#else
            bool shouldShowFontMenu = true;
#endif
            if (shouldShowFontMenu) {
                ContextMenuItem FontMenuItem(SubmenuType, ContextMenuItemTagFontMenu, 
                    contextMenuItemTagFontMenu());
                createAndAppendFontSubMenu(FontMenuItem);
                appendItem(FontMenuItem, m_contextMenu.get());
            }
#if PLATFORM(MAC)
            ContextMenuItem SpeechMenuItem(SubmenuType, ContextMenuItemTagSpeechMenu, contextMenuItemTagSpeechMenu());
            createAndAppendSpeechSubMenu(SpeechMenuItem);
            appendItem(SpeechMenuItem, m_contextMenu.get());
#endif