ContextMenuController.cpp 59.1 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
{
175 176 177 178 179 180 181 182 183 184 185 186
    Page* oldPage = frame->page();
    if (!oldPage)
        return;
    
    FrameLoadRequest request(frame->document()->securityOrigin(), ResourceRequest(urlToLoad, frame->loader().outgoingReferrer()));
    Page* newPage = oldPage;

    newPage = oldPage->chrome().createWindow(frame, request, WindowFeatures(), NavigationAction(request.resourceRequest()));
    if (!newPage)
        return;
    newPage->chrome().show();
    newPage->mainFrame().loader().loadFrameRequest(request, false, false, 0, 0, MaybeSendReferrer);
bdakin's avatar
bdakin committed
187 188
}

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

196 197
    ASSERT(frame->document());
    TypingCommand::insertText(*frame->document(), text, 0, TypingCommand::TextCompositionNone);
198 199 200
}
#endif

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

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

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

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

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

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

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

    appendItem(showSpellingPanel, &spellingAndGrammarMenu);
    appendItem(checkSpelling, &spellingAndGrammarMenu);
578
#if PLATFORM(MAC)
579 580 581 582
    appendItem(*separatorItem(), &spellingAndGrammarMenu);
#endif
    appendItem(checkAsYouType, &spellingAndGrammarMenu);
    appendItem(grammarWithSpelling, &spellingAndGrammarMenu);
583
#if PLATFORM(MAC)
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
    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
 
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 640 641
#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
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 674 675

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

676
#if PLATFORM(MAC)
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 718 719

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.
720
    for (TextIterator it(frame->selection().toNormalizedRange().get()); !it.atEnd(); it.advance()) {
721
        int length = it.length();
722 723
        for (int i = 0; i < length; ++i) {
            if (!(U_GET_GC_MASK(it.characterAt(i)) & U_GC_Z_MASK))
724
                return true;
725
        }
726 727 728 729
    }
    return false;
}

730 731 732 733 734 735
#if PLATFORM(MAC)
#define SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 1
#else
#define SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 0
#endif

736 737 738 739 740 741
#if PLATFORM(MAC)
#define SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 1
#else
#define SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 0
#endif

742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
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());
757
#if PLATFORM(GTK) || PLATFORM(EFL)
758 759 760
    ContextMenuItem CopyImageUrlItem(ActionType, ContextMenuItemTagCopyImageUrlToClipboard, 
        contextMenuItemTagCopyImageUrlToClipboard());
#endif
761
    ContextMenuItem OpenMediaInNewWindowItem(ActionType, ContextMenuItemTagOpenMediaInNewWindow, String());
762 763
    ContextMenuItem DownloadMediaItem(ActionType, ContextMenuItemTagDownloadMediaToDisk, String());
    ContextMenuItem CopyMediaLinkItem(ActionType, ContextMenuItemTagCopyMediaLinkToClipboard, String());
764 765 766 767
    ContextMenuItem MediaPlayPause(ActionType, ContextMenuItemTagMediaPlayPause, 
        contextMenuItemTagMediaPlay());
    ContextMenuItem MediaMute(ActionType, ContextMenuItemTagMediaMute, 
        contextMenuItemTagMediaMute());
768 769 770 771
#if SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS
    ContextMenuItem ToggleMediaControls(ActionType, ContextMenuItemTagToggleMediaControls,
        contextMenuItemTagHideMediaControls());
#else
772 773
    ContextMenuItem ToggleMediaControls(CheckableActionType, ContextMenuItemTagToggleMediaControls, 
        contextMenuItemTagToggleMediaControls());
774
#endif
775 776
    ContextMenuItem ToggleMediaLoop(CheckableActionType, ContextMenuItemTagToggleMediaLoop, 
        contextMenuItemTagToggleMediaLoop());
777 778 779
    ContextMenuItem EnterVideoFullscreen(ActionType, ContextMenuItemTagEnterVideoFullscreen,
        contextMenuItemTagEnterVideoFullscreen());
    ContextMenuItem ToggleVideoFullscreen(ActionType, ContextMenuItemTagToggleVideoFullscreen,
780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806
        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());
807
#endif
808
#if PLATFORM(GTK) || PLATFORM(EFL)
809 810 811 812 813 814 815
    ContextMenuItem SelectAllItem(ActionType, ContextMenuItemTagSelectAll, contextMenuItemTagSelectAll());
#endif

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

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

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

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

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

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

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

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

890 891 892 893 894 895
                ContextMenuItem SpeechMenuItem(SubmenuType, ContextMenuItemTagSpeechMenu, contextMenuItemTagSpeechMenu());
                createAndAppendSpeechSubMenu(SpeechMenuItem);
                appendItem(SpeechMenuItem, m_contextMenu.get());
#endif                
            } else {
#if ENABLE(INSPECTOR)
896
                if (!(frame->page() && (frame->page()->inspectorController()->hasInspectorFrontendClient() || frame->page()->inspectorController()->hasRemoteFrontend()))) {
897
#endif
898

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

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

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

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

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

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

1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022
#if !PLATFORM(GTK)
            appendItem(SearchWebItem, m_contextMenu.get());
            appendItem(*separatorItem(), m_contextMenu.get());
#endif
        }

        appendItem(CutItem, m_contextMenu.get());
        appendItem(CopyItem, m_contextMenu.get());
        appendItem(PasteItem, m_contextMenu.get());