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

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

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

darin@apple.com's avatar
darin@apple.com committed
32
#include "BackForwardController.h"
aroben's avatar
aroben committed
33
#include "Chrome.h"
aroben's avatar
aroben committed
34 35
#include "ContextMenu.h"
#include "ContextMenuClient.h"
36
#include "ContextMenuItem.h"
37
#include "ContextMenuProvider.h"
bdakin's avatar
bdakin committed
38 39 40 41 42
#include "Document.h"
#include "DocumentFragment.h"
#include "DocumentLoader.h"
#include "Editor.h"
#include "EditorClient.h"
aroben's avatar
aroben committed
43
#include "Event.h"
bdakin's avatar
bdakin committed
44
#include "EventHandler.h"
aroben's avatar
aroben committed
45
#include "EventNames.h"
46
#include "ExceptionCodePlaceholder.h"
darin@apple.com's avatar
darin@apple.com committed
47
#include "FormState.h"
bdakin's avatar
bdakin committed
48 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
ContextMenuController::ContextMenuController(Page& page, ContextMenuClient& client)
85 86
    : m_page(page)
    , m_client(client)
aroben's avatar
aroben committed
87 88 89 90 91
{
}

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

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

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

    populate();

111 112 113
    showContextMenu(event);
}

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

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

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

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

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

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

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

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

    m_hitTestResult = result;

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

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

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

174
static void openNewWindow(const KURL& urlToLoad, Frame* frame)
bdakin's avatar
bdakin committed
175
{
tristan's avatar
qt:  
tristan committed
176
    if (Page* oldPage = frame->page()) {
177
        FrameLoadRequest request(frame->document()->securityOrigin(), ResourceRequest(urlToLoad, frame->loader().outgoingReferrer()));
178
        Page* newPage = oldPage;
179
        if (frame->settings().supportsMultipleWindows()) {
180
            newPage = oldPage->chrome().createWindow(frame, request, WindowFeatures(), NavigationAction(request.resourceRequest()));
181 182
            if (!newPage)
                return;
183
            newPage->chrome().show();
184
        }
185
        newPage->mainFrame().loader().loadFrameRequest(request, false, false, 0, 0, MaybeSendReferrer);
tristan's avatar
qt:  
tristan committed
186
    }
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 196 197 198 199
        return;

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

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

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

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

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

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

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

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

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

    appendItem(showSpellingPanel, &spellingAndGrammarMenu);
    appendItem(checkSpelling, &spellingAndGrammarMenu);
577
#if PLATFORM(MAC)
578 579 580 581
    appendItem(*separatorItem(), &spellingAndGrammarMenu);
#endif
    appendItem(checkAsYouType, &spellingAndGrammarMenu);
    appendItem(grammarWithSpelling, &spellingAndGrammarMenu);
582
#if PLATFORM(MAC)
583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
    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
 
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
#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
641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674

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

675
#if PLATFORM(MAC)
676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718

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

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

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

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

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

    if (!m_hitTestResult.isContentEditable()) {
822
        FrameLoader& loader = frame->loader();
823 824
        KURL linkURL = m_hitTestResult.absoluteLinkURL();
        if (!linkURL.isEmpty()) {
825
            if (loader.client().canHandleRequest(ResourceRequest(linkURL))) {
826 827 828 829
                appendItem(OpenLinkItem, m_contextMenu.get());
                appendItem(OpenLinkInNewWindowItem, m_contextMenu.get());
                appendItem(DownloadFileItem, m_contextMenu.get());
            }
830 831 832 833
#if PLATFORM(QT)
            if (m_hitTestResult.isSelected()) 
                appendItem(CopyItem, m_contextMenu.get());
#endif
834 835 836 837 838 839 840 841 842 843 844 845
            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());
846
#if PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL)
847 848
            appendItem(CopyImageUrlItem, m_contextMenu.get());
#endif
849 850 851 852 853 854 855 856 857 858 859
        }

        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());
860 861 862
#if SUPPORTS_TOGGLE_VIDEO_FULLSCREEN
            appendItem(ToggleVideoFullscreen, m_contextMenu.get());
#else
863
            appendItem(EnterVideoFullscreen, m_contextMenu.get());
864
#endif
865 866 867
            appendItem(*separatorItem(), m_contextMenu.get());
            appendItem(CopyMediaLinkItem, m_contextMenu.get());
            appendItem(OpenMediaInNewWindowItem, m_contextMenu.get());
868
            if (loader.client().canHandleRequest(ResourceRequest(mediaURL)))
869
                appendItem(DownloadMediaItem, m_contextMenu.get());
870 871 872 873 874 875
        }

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

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

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

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

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

901 902
                // In GTK+ unavailable items are not hidden but insensitive.
#if PLATFORM(GTK)
903 904 905 906 907
                appendItem(BackItem, m_contextMenu.get());
                appendItem(ForwardItem, m_contextMenu.get());
                appendItem(StopItem, m_contextMenu.get());
                appendItem(ReloadItem, m_contextMenu.get());