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

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

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

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

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

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

namespace WebCore {

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

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

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

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

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

    populate();

118 119 120
    showContextMenu(event);
}

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

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

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

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

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

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

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

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

    m_hitTestResult = result;

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

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

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

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

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

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

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

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

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

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

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

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

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

523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
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());
577
#if PLATFORM(MAC)
578 579 580 581 582 583
    ContextMenuItem correctSpelling(CheckableActionType, ContextMenuItemTagCorrectSpellingAutomatically, 
        contextMenuItemTagCorrectSpellingAutomatically());
#endif

    appendItem(showSpellingPanel, &spellingAndGrammarMenu);
    appendItem(checkSpelling, &spellingAndGrammarMenu);
584
#if PLATFORM(MAC)
585 586 587 588
    appendItem(*separatorItem(), &spellingAndGrammarMenu);
#endif
    appendItem(checkAsYouType, &spellingAndGrammarMenu);
    appendItem(grammarWithSpelling, &spellingAndGrammarMenu);
589
#if PLATFORM(MAC)
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615
    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
 
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 642 643 644 645 646 647
#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
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 676 677 678 679 680 681

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

682
#if PLATFORM(MAC)
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 720 721 722 723 724 725

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.
726
    for (TextIterator it(frame->selection().toNormalizedRange().get()); !it.atEnd(); it.advance()) {
727 728
        int length = it.length();
        for (int i = 0; i < length; ++i)
729
            if (!(category(it.characterAt(i)) & (Separator_Space | Separator_Line | Separator_Paragraph)))
730 731 732 733 734
                return true;
    }
    return false;
}

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

741 742 743 744 745 746
#if PLATFORM(MAC)
#define SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 1
#else
#define SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 0
#endif

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

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

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

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

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

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

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

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

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

889 890 891 892 893
#if !PLATFORM(GTK)
                    appendItem(SearchWebItem, m_contextMenu.get());
                    appendItem(*separatorItem(), m_contextMenu.get());
#endif
                }
894

895 896 897
                appendItem(CopyItem, m_contextMenu.get());
#if PLATFORM(MAC)
                appendItem(*separatorItem(), m_contextMenu.get());
898

899 900 901 902 903 904 905 906
                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
907

908 909
                // In GTK+ unavailable items are not hidden but insensitive.
#if PLATFORM(GTK)
910 911 912 913 914
                appendItem(BackItem, m_contextMenu.get());
                appendItem(ForwardItem, m_contextMenu.get());
                appendItem(StopItem, m_contextMenu.get());
                appendItem(ReloadItem, m_contextMenu.get());
#else
915 916 917 918 919 920 921 922
                if (frame->page() && frame->page()->backForward()->canGoBackOrForward(-1))
                    appendItem(BackItem, m_contextMenu.get());

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

                // use isLoadingInAPISense rather than isLoading because Stop/Reload are
                // intended to match WebKit's API, not WebCore's internal notion of loading status
923
                if (loader.documentLoader()->isLoadingInAPISense())
924 925 926
                    appendItem(StopItem, m_contextMenu.get());
                else
                    appendItem(ReloadItem, m_contextMenu.get());
927
#endif
928 929 930 931
#if ENABLE(INSPECTOR)
                }
#endif

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

            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());
                    }
995 996 997 998 999
                    appendItem(*separatorItem(), m_contextMenu.get());
                }
            }
        }

1000
        FrameLoader& loader = frame->loader();
1001 1002
        KURL linkURL = m_hitTestResult.absoluteLinkURL();
        if (!linkURL.isEmpty()) {
1003
            if (loader.client().canHandleRequest(ResourceRequest(linkURL))) {
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
                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)
1014
            String selectedString = frame->displayStringModifiedByEncoding(frame->editor().selectedText());
1015 1016 1017 1018 1019
            ContextMenuItem LookUpInDictionaryItem(ActionType, ContextMenuItemTagLookUpInDictionary, contextMenuItemTagLookUpInDictionary(selectedString));

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

1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031
#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());
#if PLATFORM(GTK)
        appendItem(DeleteItem, m_contextMenu.get());
        appendItem(*separatorItem(), m_contextMenu.get());
1032
#endif
1033
#if PLATFORM(GTK) || PLATFORM(QT) || PLATFORM(EFL)
1034 1035 1036 1037 1038
        appendItem(SelectAllItem, m_contextMenu.get());
#endif

        if (!inPasswordField) {
#if !PLATFORM(GTK)
1039
            appendItem(*separatorItem(), m_contextMenu.get());
1040 1041 1042 1043 1044
            ContextMenuItem SpellingAndGrammarMenuItem(SubmenuType, ContextMenuItemTagSpellingMenu, 
                contextMenuItemTagSpellingMenu());
            createAndAppendSpellingAndGrammarSubMenu(SpellingAndGrammarMenuItem);
            appendItem(SpellingAndGrammarMenuItem, m_contextMenu.get());
#endif
1045
#if PLATFORM(MAC)
1046 1047 1048 1049 1050 1051 1052 1053 1054 1055
            ContextMenuItem substitutionsMenuItem(SubmenuType, ContextMenuItemTagSubstitutionsMenu, 
                contextMenuItemTagSubstitutionsMenu());
            createAndAppendSubstitutionsSubMenu(substitutionsMenuItem);
            appendItem(substitutionsMenuItem, m_contextMenu.get());
            ContextMenuItem transformationsMenuItem(SubmenuType, ContextMenuItemTagTransformationsMenu, 
                contextMenuItemTagTransformationsMenu());
            createAndAppendTransformationsSubMenu(transformationsMenuItem);
            appendItem(transformationsMenuItem, m_contextMenu.get());
#endif
#if PLATFORM(GTK)
1056
            bool shouldShowFontMenu = frame->editor().canEditRichly();
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068