ContextMenuController.cpp 59.3 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
#include "FrameLoadRequest.h"
49
#include "FrameLoader.h"
50
#include "FrameLoaderClient.h"
51
#include "FrameSelection.h"
52
#include "HTMLFormElement.h"
aroben's avatar
aroben committed
53 54
#include "HitTestRequest.h"
#include "HitTestResult.h"
55
#include "InspectorController.h"
56
#include "LocalizedStrings.h"
darin@apple.com's avatar
darin@apple.com committed
57
#include "MainFrame.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"
bdakin's avatar
bdakin committed
63 64
#include "ReplaceSelectionCommand.h"
#include "ResourceRequest.h"
65
#include "Settings.h"
darin's avatar
darin committed
66
#include "TextIterator.h"
67
#include "TypingCommand.h"
68
#include "UserTypingGestureIndicator.h"
tristan's avatar
qt:  
tristan committed
69
#include "WindowFeatures.h"
bdakin's avatar
bdakin committed
70
#include "markup.h"
71
#include <wtf/unicode/CharacterNames.h>
72 73
#include <wtf/unicode/Unicode.h>

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

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

namespace WebCore {

83
ContextMenuController::ContextMenuController(Page& page, ContextMenuClient& client)
84 85
    : m_page(page)
    , m_client(client)
aroben's avatar
aroben committed
86 87 88 89 90
{
}

ContextMenuController::~ContextMenuController()
{
91
    m_client.contextMenuDestroyed();
92 93
}

bdakin's avatar
bdakin committed
94 95
void ContextMenuController::clearContextMenu()
{
96
    m_contextMenu.clear();
97 98 99
    if (m_menuProvider)
        m_menuProvider->contextMenuCleared();
    m_menuProvider = 0;
bdakin's avatar
bdakin committed
100 101
}

aroben's avatar
aroben committed
102 103
void ContextMenuController::handleContextMenuEvent(Event* event)
{
104
    m_contextMenu = createContextMenu(event);
105
    if (!m_contextMenu)
adele's avatar
adele committed
106
        return;
107 108 109

    populate();

110 111 112
    showContextMenu(event);
}

113 114 115 116 117
static PassOwnPtr<ContextMenuItem> separatorItem()
{
    return adoptPtr(new ContextMenuItem(SeparatorType, ContextMenuItemTagNoAction, String()));
}

118
void ContextMenuController::showContextMenu(Event* event, PassRefPtr<ContextMenuProvider> menuProvider)
119
{
120
    m_menuProvider = menuProvider;
121

122
    m_contextMenu = createContextMenu(event);
123 124 125 126
    if (!m_contextMenu) {
        clearContextMenu();
        return;
    }
127 128

    m_menuProvider->populateContextMenu(m_contextMenu.get());
129 130 131 132
    if (m_hitTestResult.isSelected()) {
        appendItem(*separatorItem(), m_contextMenu.get());
        populate();
    }
133 134 135
    showContextMenu(event);
}

136
PassOwnPtr<ContextMenu> ContextMenuController::createContextMenu(Event* event)
137
{
138 139
    ASSERT(event);
    
140
    if (!event->isMouseEvent())
141
        return nullptr;
142

aroben's avatar
aroben committed
143
    MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
144
    HitTestResult result(mouseEvent->absoluteLocation());
aroben's avatar
aroben committed
145

146
    if (Frame* frame = event->target()->toNode()->document().frame())
147
        result = frame->eventHandler().hitTestResultAtPoint(mouseEvent->absoluteLocation());
148

bdakin's avatar
bdakin committed
149
    if (!result.innerNonSharedNode())
150
        return nullptr;
151 152 153

    m_hitTestResult = result;

154
    return adoptPtr(new ContextMenu);
155
}
aroben's avatar
aroben committed
156

157 158
void ContextMenuController::showContextMenu(Event* event)
{
bolsinga@apple.com's avatar
bolsinga@apple.com committed
159
#if ENABLE(INSPECTOR)
160
    if (m_page.inspectorController()->enabled())
161
        addInspectElementItem();
bolsinga@apple.com's avatar
bolsinga@apple.com committed
162
#endif
163 164

#if USE(CROSS_PLATFORM_CONTEXT_MENUS)
165
    m_contextMenu = m_client.customizeMenu(m_contextMenu.release());
166
#else
167
    PlatformMenuDescription customMenu = m_client.getCustomMenuFromDefaultItems(m_contextMenu.get());
bdakin's avatar
bdakin committed
168
    m_contextMenu->setPlatformDescription(customMenu);
169
#endif
aroben's avatar
aroben committed
170
    event->setDefaultHandled();
aroben's avatar
aroben committed
171 172
}

darin@apple.com's avatar
darin@apple.com committed
173
static void openNewWindow(const URL& urlToLoad, Frame* frame)
bdakin's avatar
bdakin committed
174
{
tristan's avatar
qt:  
tristan committed
175
    if (Page* oldPage = frame->page()) {
176
        FrameLoadRequest request(frame->document()->securityOrigin(), ResourceRequest(urlToLoad, frame->loader().outgoingReferrer()));
177
        Page* newPage = oldPage;
178
        if (frame->settings().supportsMultipleWindows()) {
179
            newPage = oldPage->chrome().createWindow(frame, request, WindowFeatures(), NavigationAction(request.resourceRequest()));
180 181
            if (!newPage)
                return;
182
            newPage->chrome().show();
183
        }
184
        newPage->mainFrame().loader().loadFrameRequest(request, false, false, 0, 0, MaybeSendReferrer);
tristan's avatar
qt:  
tristan committed
185
    }
bdakin's avatar
bdakin committed
186 187
}

188 189 190 191
#if PLATFORM(GTK)
static void insertUnicodeCharacter(UChar character, Frame* frame)
{
    String text(&character, 1);
192
    if (!frame->editor().shouldInsertText(text, frame->selection().toNormalizedRange().get(), EditorInsertActionTyped))
193 194 195 196 197 198
        return;

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

aroben's avatar
aroben committed
199
void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item)
bdakin's avatar
bdakin committed
200
{
201
    ASSERT(item->type() == ActionType || item->type() == CheckableActionType);
aroben's avatar
aroben committed
202 203

    if (item->action() >= ContextMenuItemBaseApplicationTag) {
204
        m_client.contextMenuItemSelected(item, m_contextMenu.get());
aroben's avatar
aroben committed
205 206 207
        return;
    }

208
    if (item->action() >= ContextMenuItemBaseCustomTag) {
209 210
        ASSERT(m_menuProvider);
        m_menuProvider->contextMenuItemSelected(item);
211 212 213
        return;
    }

214
    Frame* frame = m_hitTestResult.innerNonSharedNode()->document().frame();
bdakin's avatar
bdakin committed
215 216
    if (!frame)
        return;
217

aroben's avatar
aroben committed
218
    switch (item->action()) {
219
    case ContextMenuItemTagOpenLinkInNewWindow:
220
        openNewWindow(m_hitTestResult.absoluteLinkURL(), frame);
221 222
        break;
    case ContextMenuItemTagDownloadLinkToDisk:
223
        // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709)
224
        m_client.downloadURL(m_hitTestResult.absoluteLinkURL());
225 226
        break;
    case ContextMenuItemTagCopyLinkToClipboard:
227
        frame->editor().copyURL(m_hitTestResult.absoluteLinkURL(), m_hitTestResult.textContent());
228 229
        break;
    case ContextMenuItemTagOpenImageInNewWindow:
230
        openNewWindow(m_hitTestResult.absoluteImageURL(), frame);
231 232
        break;
    case ContextMenuItemTagDownloadImageToDisk:
233
        // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709)
234
        m_client.downloadURL(m_hitTestResult.absoluteImageURL());
235 236 237 238
        break;
    case ContextMenuItemTagCopyImageToClipboard:
        // FIXME: The Pasteboard class is not written yet
        // For now, call into the client. This is temporary!
239
        frame->editor().copyImage(m_hitTestResult);
240
        break;
241
#if PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL)
242
    case ContextMenuItemTagCopyImageUrlToClipboard:
243
        frame->editor().copyURL(m_hitTestResult.absoluteImageURL(), m_hitTestResult.textContent());
244 245
        break;
#endif
246
    case ContextMenuItemTagOpenMediaInNewWindow:
247
        openNewWindow(m_hitTestResult.absoluteMediaURL(), frame);
248
        break;
249 250
    case ContextMenuItemTagDownloadMediaToDisk:
        // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709)
251
        m_client.downloadURL(m_hitTestResult.absoluteMediaURL());
252
        break;
253
    case ContextMenuItemTagCopyMediaLinkToClipboard:
254
        frame->editor().copyURL(m_hitTestResult.absoluteMediaURL(), m_hitTestResult.textContent());
255 256
        break;
    case ContextMenuItemTagToggleMediaControls:
257
        m_hitTestResult.toggleMediaControlsDisplay();
258 259
        break;
    case ContextMenuItemTagToggleMediaLoop:
260
        m_hitTestResult.toggleMediaLoopPlayback();
261
        break;
262 263 264
    case ContextMenuItemTagToggleVideoFullscreen:
        m_hitTestResult.toggleMediaFullscreenState();
        break;
265
    case ContextMenuItemTagEnterVideoFullscreen:
266
        m_hitTestResult.enterFullscreenForVideo();
267 268
        break;
    case ContextMenuItemTagMediaPlayPause:
269
        m_hitTestResult.toggleMediaPlayState();
270 271
        break;
    case ContextMenuItemTagMediaMute:
272
        m_hitTestResult.toggleMediaMuteState();
273
        break;
274
    case ContextMenuItemTagOpenFrameInNewWindow: {
275
        DocumentLoader* loader = frame->loader().documentLoader();
276 277 278 279 280 281 282
        if (!loader->unreachableURL().isEmpty())
            openNewWindow(loader->unreachableURL(), frame);
        else
            openNewWindow(loader->url(), frame);
        break;
    }
    case ContextMenuItemTagCopy:
283
        frame->editor().copy();
284 285
        break;
    case ContextMenuItemTagGoBack:
286
        if (Page* page = frame->page())
287
            page->backForward().goBackOrForward(-1);
288 289
        break;
    case ContextMenuItemTagGoForward:
290
        if (Page* page = frame->page())
291
            page->backForward().goBackOrForward(1);
292 293
        break;
    case ContextMenuItemTagStop:
294
        frame->loader().stop();
295 296
        break;
    case ContextMenuItemTagReload:
297
        frame->loader().reload();
298 299
        break;
    case ContextMenuItemTagCut:
300
        frame->editor().command("Cut").execute();
301 302
        break;
    case ContextMenuItemTagPaste:
303
        frame->editor().command("Paste").execute();
304
        break;
305
#if PLATFORM(GTK)
306
    case ContextMenuItemTagDelete:
307
        frame->editor().performDelete();
308
        break;
309 310 311 312 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
    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;
339
#endif
340
#if PLATFORM(GTK) || PLATFORM(QT) || PLATFORM(EFL)
341
    case ContextMenuItemTagSelectAll:
342
        frame->editor().command("SelectAll").execute();
343
        break;
344
#endif
345
    case ContextMenuItemTagSpellingGuess: {
346 347
        FrameSelection& frameSelection = frame->selection();
        if (frame->editor().shouldInsertText(item->title(), frameSelection.toNormalizedRange().get(), EditorInsertActionPasted)) {
348 349
            ReplaceSelectionCommand::CommandOptions replaceOptions = ReplaceSelectionCommand::MatchStyle | ReplaceSelectionCommand::PreventNesting;

350
            if (frame->editor().behavior().shouldAllowSpellingSuggestionsWithoutSelection()) {
351 352
                ASSERT(frameSelection.isCaretOrRange());
                VisibleSelection wordSelection(frameSelection.base());
353
                wordSelection.expandUsingGranularity(WordGranularity);
354
                frameSelection.setSelection(wordSelection);
355
            } else {
356
                ASSERT(frame->editor().selectedText().length());
357 358 359
                replaceOptions |= ReplaceSelectionCommand::SelectReplacement;
            }

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

515 516 517 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
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());
    ContextMenuItem grammarWithSpelling(CheckableActionType, ContextMenuItemTagCheckGrammarWithSpelling, 
        contextMenuItemTagCheckGrammarWithSpelling());
569
#if PLATFORM(MAC)
570 571 572 573 574 575
    ContextMenuItem correctSpelling(CheckableActionType, ContextMenuItemTagCorrectSpellingAutomatically, 
        contextMenuItemTagCorrectSpellingAutomatically());
#endif

    appendItem(showSpellingPanel, &spellingAndGrammarMenu);
    appendItem(checkSpelling, &spellingAndGrammarMenu);
576
#if PLATFORM(MAC)
577 578 579 580
    appendItem(*separatorItem(), &spellingAndGrammarMenu);
#endif
    appendItem(checkAsYouType, &spellingAndGrammarMenu);
    appendItem(grammarWithSpelling, &spellingAndGrammarMenu);
581
#if PLATFORM(MAC)
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
    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
 
608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
#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
640 641 642 643 644 645 646 647 648 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

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

674
#if PLATFORM(MAC)
675 676 677 678 679 680 681 682 683 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

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.
718
    for (TextIterator it(frame->selection().toNormalizedRange().get()); !it.atEnd(); it.advance()) {
719 720
        int length = it.length();
        for (int i = 0; i < length; ++i)
721
            if (!(category(it.characterAt(i)) & (Separator_Space | Separator_Line | Separator_Paragraph)))
722 723 724 725 726
                return true;
    }
    return false;
}

727 728 729 730 731 732
#if PLATFORM(MAC)
#define SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 1
#else
#define SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 0
#endif

733 734 735 736 737 738
#if PLATFORM(MAC)
#define SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 1
#else
#define SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 0
#endif

739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
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());
754
#if PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL)
755 756 757
    ContextMenuItem CopyImageUrlItem(ActionType, ContextMenuItemTagCopyImageUrlToClipboard, 
        contextMenuItemTagCopyImageUrlToClipboard());
#endif
758
    ContextMenuItem OpenMediaInNewWindowItem(ActionType, ContextMenuItemTagOpenMediaInNewWindow, String());
759 760
    ContextMenuItem DownloadMediaItem(ActionType, ContextMenuItemTagDownloadMediaToDisk, String());
    ContextMenuItem CopyMediaLinkItem(ActionType, ContextMenuItemTagCopyMediaLinkToClipboard, String());
761 762 763 764
    ContextMenuItem MediaPlayPause(ActionType, ContextMenuItemTagMediaPlayPause, 
        contextMenuItemTagMediaPlay());
    ContextMenuItem MediaMute(ActionType, ContextMenuItemTagMediaMute, 
        contextMenuItemTagMediaMute());
765 766 767 768
#if SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS
    ContextMenuItem ToggleMediaControls(ActionType, ContextMenuItemTagToggleMediaControls,
        contextMenuItemTagHideMediaControls());
#else
769 770
    ContextMenuItem ToggleMediaControls(CheckableActionType, ContextMenuItemTagToggleMediaControls, 
        contextMenuItemTagToggleMediaControls());
771
#endif
772 773
    ContextMenuItem ToggleMediaLoop(CheckableActionType, ContextMenuItemTagToggleMediaLoop, 
        contextMenuItemTagToggleMediaLoop());
774 775 776
    ContextMenuItem EnterVideoFullscreen(ActionType, ContextMenuItemTagEnterVideoFullscreen,
        contextMenuItemTagEnterVideoFullscreen());
    ContextMenuItem ToggleVideoFullscreen(ActionType, ContextMenuItemTagToggleVideoFullscreen,
777 778 779 780 781 782 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());
    ContextMenuItem IgnoreGrammarItem(ActionType, ContextMenuItemTagIgnoreGrammar, 
        contextMenuItemTagIgnoreGrammar());
    ContextMenuItem CutItem(ActionType, ContextMenuItemTagCut, contextMenuItemTagCut());
    ContextMenuItem PasteItem(ActionType, ContextMenuItemTagPaste, contextMenuItemTagPaste());
#if PLATFORM(GTK)
    ContextMenuItem DeleteItem(ActionType, ContextMenuItemTagDelete, contextMenuItemTagDelete());
804
#endif
805
#if PLATFORM(GTK) || PLATFORM(QT) || PLATFORM(EFL)
806 807 808 809 810 811 812
    ContextMenuItem SelectAllItem(ActionType, ContextMenuItemTagSelectAll, contextMenuItemTagSelectAll());
#endif

    Node* node = m_hitTestResult.innerNonSharedNode();
    if (!node)
        return;
#if PLATFORM(GTK)
813
    if (!m_hitTestResult.isContentEditable() && (node->isElementNode() && toElement(node)->isFormControlElement()))
814 815
        return;
#endif
816
    Frame* frame = node->document().frame();
817 818 819 820
    if (!frame)
        return;

    if (!m_hitTestResult.isContentEditable()) {
821
        FrameLoader& loader = frame->loader();
darin@apple.com's avatar
darin@apple.com committed
822
        URL linkURL = m_hitTestResult.absoluteLinkURL();
823
        if (!linkURL.isEmpty()) {
824
            if (loader.client().canHandleRequest(ResourceRequest(linkURL))) {
825 826 827 828
                appendItem(OpenLinkItem, m_contextMenu.get());
                appendItem(OpenLinkInNewWindowItem, m_contextMenu.get());
                appendItem(DownloadFileItem, m_contextMenu.get());
            }
829 830 831 832
#if PLATFORM(QT)
            if (m_hitTestResult.isSelected()) 
                appendItem(CopyItem, m_contextMenu.get());
#endif
833 834 835
            appendItem(CopyLinkItem, m_contextMenu.get());
        }

darin@apple.com's avatar
darin@apple.com committed
836
        URL imageURL = m_hitTestResult.absoluteImageURL();
837 838 839 840 841 842 843 844
        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());
845
#if PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL)
846 847
            appendItem(CopyImageUrlItem, m_contextMenu.get());
#endif
848 849
        }

darin@apple.com's avatar
darin@apple.com committed
850
        URL mediaURL = m_hitTestResult.absoluteMediaURL();
851 852 853 854 855 856 857 858
        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());
859 860 861
#if SUPPORTS_TOGGLE_VIDEO_FULLSCREEN
            appendItem(ToggleVideoFullscreen, m_contextMenu.get());
#else
862
            appendItem(EnterVideoFullscreen, m_contextMenu.get());
863
#endif
864 865 866
            appendItem(*separatorItem(), m_contextMenu.get());
            appendItem(CopyMediaLinkItem, m_contextMenu.get());
            appendItem(OpenMediaInNewWindowItem, m_contextMenu.get());
867
            if (loader.client().canHandleRequest(ResourceRequest(mediaURL)))
868
                appendItem(DownloadMediaItem, m_contextMenu.get());
869 870 871 872 873 874
        }

        if (imageURL.isEmpty() && linkURL.isEmpty() && mediaURL.isEmpty()) {
            if (m_hitTestResult.isSelected()) {
                if (selectionContainsPossibleWord(frame)) {
#if PLATFORM(MAC)
875
                    String selectedString = frame->displayStringModifiedByEncoding(frame->editor().selectedText());
876 877 878 879 880
                    ContextMenuItem LookUpInDictionaryItem(ActionType, ContextMenuItemTagLookUpInDictionary, contextMenuItemTagLookUpInDictionary(selectedString));

                    appendItem(LookUpInDictionaryItem, m_contextMenu.get());
#endif

881 882 883 884 885
#if !PLATFORM(GTK)
                    appendItem(SearchWebItem, m_contextMenu.get());
                    appendItem(*separatorItem(), m_contextMenu.get());
#endif
                }
886

887 888 889
                appendItem(CopyItem, m_contextMenu.get());
#if PLATFORM(MAC)
                appendItem(*separatorItem(), m_contextMenu.get());
890

891 892 893 894 895 896 897 898
                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
899

900 901
                // In GTK+ unavailable items are not hidden but insensitive.
#if PLATFORM(GTK)
902 903 904 905 906
                appendItem(BackItem, m_contextMenu.get());
                appendItem(ForwardItem, m_contextMenu.get());
                appendItem(StopItem, m_contextMenu.get());
                appendItem(ReloadItem, m_contextMenu.get());
#else
907
                if (frame->page() && frame->page()->backForward().canGoBackOrForward(-1))
908 909
                    appendItem(BackItem, m_contextMenu.get());

910
                if (frame->page() && frame->page()->backForward().canGoBackOrForward(1))
911 912 913 914
                    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
915
                if (loader.documentLoader()->isLoadingInAPISense())
916 917 918
                    appendItem(StopItem, m_contextMenu.get());
                else
                    appendItem(ReloadItem, m_contextMenu.get());
919
#endif
920 921 922 923
#if ENABLE(INSPECTOR)
                }
#endif

924
                if (frame->page() && !frame->isMainFrame())
925 926 927 928
                    appendItem(OpenFrameItem, m_contextMenu.get());
            }
        }
    } else { // Make an editing context menu
929
        bool inPasswordField = frame->selection().isInPasswordField();
930 931
        if (!inPasswordField) {
            bool haveContextMenuItemsForMisspellingOrGrammer = false;
932
            bool spellCheckingEnabled = frame->editor().isSpellCheckingEnabledFor(node);
933 934 935 936 937
            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;
938
                Vector<String> guesses = frame->editor().guessesForMisspelledOrUngrammatical(misspelling, badGrammar);
939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955
                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());
                            }
                        }
956 957
                        appendItem(*separatorItem(), m_contextMenu.get());
                    }
958 959 960
                    if (misspelling) {
                        appendItem(IgnoreSpellingItem, m_contextMenu.get());
                        appendItem(LearnSpellingItem, m_contextMenu.get());
961
                    } else
962 963 964
                        appendItem(IgnoreGrammarItem, m_contextMenu.get());
                    appendItem(*separatorItem(), m_contextMenu.get());
                    haveContextMenuItemsForMisspellingOrGrammer = true;
965
#if PLATFORM(MAC)
966
                } else {
967 968 969 970 971 972 973
                    // 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;
974
                    }
975
#endif
976
                }
977 978 979 980 981 982 983 984 985 986
            }

            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());
                    }
987 988 989 990 991
                    appendItem(*separatorItem(), m_contextMenu.get());
                }
            }
        }

992
        FrameLoader& loader = frame->loader();
darin@apple.com's avatar
darin@apple.com committed
993
        URL linkURL = m_hitTestResult.absoluteLinkURL();
994
        if (!linkURL.isEmpty()) {
995
            if (loader.client().canHandleRequest(ResourceRequest(linkURL))) {
996 997 998 999 1000 1001 1002 1003 1004 1005
                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)
1006
            String selectedString = frame->displayStringModifiedByEncoding(frame->editor().selectedText());