WebCore/page/ContextMenuController.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
       
     3  *
       
     4  * Redistribution and use in source and binary forms, with or without
       
     5  * modification, are permitted provided that the following conditions
       
     6  * are met:
       
     7  * 1. Redistributions of source code must retain the above copyright
       
     8  *    notice, this list of conditions and the following disclaimer.
       
     9  * 2. Redistributions in binary form must reproduce the above copyright
       
    10  *    notice, this list of conditions and the following disclaimer in the
       
    11  *    documentation and/or other materials provided with the distribution.
       
    12  *
       
    13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
       
    14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
       
    15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
       
    16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
       
    17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       
    18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
       
    19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
       
    20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
       
    21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
       
    23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
       
    24  */
       
    25 
       
    26 #include "config.h"
       
    27 #include "ContextMenuController.h"
       
    28 
       
    29 #if ENABLE(CONTEXT_MENUS)
       
    30 
       
    31 #include "Chrome.h"
       
    32 #include "ContextMenu.h"
       
    33 #include "ContextMenuClient.h"
       
    34 #include "ContextMenuProvider.h"
       
    35 #include "Document.h"
       
    36 #include "DocumentFragment.h"
       
    37 #include "DocumentLoader.h"
       
    38 #include "Editor.h"
       
    39 #include "EditorClient.h"
       
    40 #include "Event.h"
       
    41 #include "EventHandler.h"
       
    42 #include "EventNames.h"
       
    43 #include "FormState.h"
       
    44 #include "Frame.h"
       
    45 #include "FrameLoadRequest.h"
       
    46 #include "FrameLoader.h"
       
    47 #include "HTMLFormElement.h"
       
    48 #include "HitTestRequest.h"
       
    49 #include "HitTestResult.h"
       
    50 #include "InspectorController.h"
       
    51 #include "MouseEvent.h"
       
    52 #include "Node.h"
       
    53 #include "Page.h"
       
    54 #include "RenderLayer.h"
       
    55 #include "RenderObject.h"
       
    56 #include "ReplaceSelectionCommand.h"
       
    57 #include "ResourceRequest.h"
       
    58 #include "SelectionController.h"
       
    59 #include "Settings.h"
       
    60 #include "TextIterator.h"
       
    61 #include "WindowFeatures.h"
       
    62 #include "markup.h"
       
    63 
       
    64 namespace WebCore {
       
    65 
       
    66 ContextMenuController::ContextMenuController(Page* page, ContextMenuClient* client)
       
    67     : m_page(page)
       
    68     , m_client(client)
       
    69     , m_contextMenu(0)
       
    70 {
       
    71     ASSERT_ARG(page, page);
       
    72     ASSERT_ARG(client, client);
       
    73 }
       
    74 
       
    75 ContextMenuController::~ContextMenuController()
       
    76 {
       
    77     m_client->contextMenuDestroyed();
       
    78 }
       
    79 
       
    80 void ContextMenuController::clearContextMenu()
       
    81 {
       
    82     m_contextMenu.set(0);
       
    83     if (m_menuProvider)
       
    84         m_menuProvider->contextMenuCleared();
       
    85     m_menuProvider = 0;
       
    86 }
       
    87 
       
    88 void ContextMenuController::handleContextMenuEvent(Event* event)
       
    89 {
       
    90     m_contextMenu.set(createContextMenu(event));
       
    91     if (!m_contextMenu)
       
    92         return;
       
    93     m_contextMenu->populate();
       
    94     showContextMenu(event);
       
    95 }
       
    96 
       
    97 void ContextMenuController::showContextMenu(Event* event, PassRefPtr<ContextMenuProvider> menuProvider)
       
    98 {
       
    99     m_menuProvider = menuProvider;
       
   100 
       
   101     m_contextMenu.set(createContextMenu(event));
       
   102     if (!m_contextMenu) {
       
   103         clearContextMenu();
       
   104         return;
       
   105     }
       
   106 
       
   107     m_menuProvider->populateContextMenu(m_contextMenu.get());
       
   108     showContextMenu(event);
       
   109 }
       
   110 
       
   111 ContextMenu* ContextMenuController::createContextMenu(Event* event)
       
   112 {
       
   113    if (!event->isMouseEvent())
       
   114         return 0;
       
   115     MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
       
   116     HitTestResult result(mouseEvent->absoluteLocation());
       
   117 
       
   118     if (Frame* frame = event->target()->toNode()->document()->frame())
       
   119         result = frame->eventHandler()->hitTestResultAtPoint(mouseEvent->absoluteLocation(), false);
       
   120 
       
   121     if (!result.innerNonSharedNode())
       
   122         return 0;
       
   123     return new ContextMenu(result);
       
   124 }
       
   125 
       
   126 void ContextMenuController::showContextMenu(Event* event)
       
   127 {
       
   128 #if ENABLE(INSPECTOR)
       
   129     if (m_page->inspectorController()->enabled())
       
   130         m_contextMenu->addInspectElementItem();
       
   131 #endif
       
   132     PlatformMenuDescription customMenu = m_client->getCustomMenuFromDefaultItems(m_contextMenu.get());
       
   133     m_contextMenu->setPlatformDescription(customMenu);
       
   134     event->setDefaultHandled();
       
   135 }
       
   136 
       
   137 static void openNewWindow(const KURL& urlToLoad, Frame* frame)
       
   138 {
       
   139     if (Page* oldPage = frame->page()) {
       
   140         WindowFeatures features;
       
   141         if (Page* newPage = oldPage->chrome()->createWindow(frame, FrameLoadRequest(ResourceRequest(urlToLoad, frame->loader()->outgoingReferrer())), features))
       
   142             newPage->chrome()->show();
       
   143     }
       
   144 }
       
   145 
       
   146 void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item)
       
   147 {
       
   148     ASSERT(item->type() == ActionType || item->type() == CheckableActionType);
       
   149 
       
   150     if (item->action() >= ContextMenuItemBaseApplicationTag) {
       
   151         m_client->contextMenuItemSelected(item, m_contextMenu.get());
       
   152         return;
       
   153     }
       
   154 
       
   155     if (item->action() >= ContextMenuItemBaseCustomTag) {
       
   156         ASSERT(m_menuProvider);
       
   157         m_menuProvider->contextMenuItemSelected(item);
       
   158         return;
       
   159     }
       
   160 
       
   161     HitTestResult result = m_contextMenu->hitTestResult();
       
   162     Frame* frame = result.innerNonSharedNode()->document()->frame();
       
   163     if (!frame)
       
   164         return;
       
   165 
       
   166     switch (item->action()) {
       
   167     case ContextMenuItemTagOpenLinkInNewWindow:
       
   168         openNewWindow(result.absoluteLinkURL(), frame);
       
   169         break;
       
   170     case ContextMenuItemTagDownloadLinkToDisk:
       
   171         // FIXME: Some day we should be able to do this from within WebCore.
       
   172         m_client->downloadURL(result.absoluteLinkURL());
       
   173         break;
       
   174     case ContextMenuItemTagCopyLinkToClipboard:
       
   175         frame->editor()->copyURL(result.absoluteLinkURL(), result.textContent());
       
   176         break;
       
   177     case ContextMenuItemTagOpenImageInNewWindow:
       
   178         openNewWindow(result.absoluteImageURL(), frame);
       
   179         break;
       
   180     case ContextMenuItemTagDownloadImageToDisk:
       
   181         // FIXME: Some day we should be able to do this from within WebCore.
       
   182         m_client->downloadURL(result.absoluteImageURL());
       
   183         break;
       
   184     case ContextMenuItemTagCopyImageToClipboard:
       
   185         // FIXME: The Pasteboard class is not written yet
       
   186         // For now, call into the client. This is temporary!
       
   187         frame->editor()->copyImage(result);
       
   188         break;
       
   189     case ContextMenuItemTagOpenFrameInNewWindow: {
       
   190         DocumentLoader* loader = frame->loader()->documentLoader();
       
   191         if (!loader->unreachableURL().isEmpty())
       
   192             openNewWindow(loader->unreachableURL(), frame);
       
   193         else
       
   194             openNewWindow(loader->url(), frame);
       
   195         break;
       
   196     }
       
   197     case ContextMenuItemTagCopy:
       
   198         frame->editor()->copy();
       
   199         break;
       
   200     case ContextMenuItemTagGoBack:
       
   201         if (Page* page = frame->page())
       
   202             page->goBackOrForward(-1);
       
   203         break;
       
   204     case ContextMenuItemTagGoForward:
       
   205         if (Page* page = frame->page())
       
   206             page->goBackOrForward(1);
       
   207         break;
       
   208     case ContextMenuItemTagStop:
       
   209         frame->loader()->stop();
       
   210         break;
       
   211     case ContextMenuItemTagReload:
       
   212         frame->loader()->reload();
       
   213         break;
       
   214     case ContextMenuItemTagCut:
       
   215         frame->editor()->cut();
       
   216         break;
       
   217     case ContextMenuItemTagPaste:
       
   218         frame->editor()->paste();
       
   219         break;
       
   220 #if PLATFORM(GTK)
       
   221     case ContextMenuItemTagDelete:
       
   222         frame->editor()->performDelete();
       
   223         break;
       
   224     case ContextMenuItemTagSelectAll:
       
   225         frame->editor()->command("SelectAll").execute();
       
   226         break;
       
   227 #endif
       
   228     case ContextMenuItemTagSpellingGuess:
       
   229         ASSERT(frame->selectedText().length());
       
   230         if (frame->editor()->shouldInsertText(item->title(), frame->selection()->toNormalizedRange().get(), EditorInsertActionPasted)) {
       
   231             Document* document = frame->document();
       
   232             RefPtr<ReplaceSelectionCommand> command = ReplaceSelectionCommand::create(document, createFragmentFromMarkup(document, item->title(), ""), true, false, true);
       
   233             applyCommand(command);
       
   234             frame->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
       
   235         }
       
   236         break;
       
   237     case ContextMenuItemTagIgnoreSpelling:
       
   238         frame->editor()->ignoreSpelling();
       
   239         break;
       
   240     case ContextMenuItemTagLearnSpelling:
       
   241         frame->editor()->learnSpelling();
       
   242         break;
       
   243     case ContextMenuItemTagSearchWeb:
       
   244         m_client->searchWithGoogle(frame);
       
   245         break;
       
   246     case ContextMenuItemTagLookUpInDictionary:
       
   247         // FIXME: Some day we may be able to do this from within WebCore.
       
   248         m_client->lookUpInDictionary(frame);
       
   249         break;
       
   250     case ContextMenuItemTagOpenLink:
       
   251         if (Frame* targetFrame = result.targetFrame())
       
   252             targetFrame->loader()->loadFrameRequest(FrameLoadRequest(ResourceRequest(result.absoluteLinkURL(), frame->loader()->outgoingReferrer())), false, false, 0, 0, SendReferrer);
       
   253         else
       
   254             openNewWindow(result.absoluteLinkURL(), frame);
       
   255         break;
       
   256     case ContextMenuItemTagBold:
       
   257         frame->editor()->command("ToggleBold").execute();
       
   258         break;
       
   259     case ContextMenuItemTagItalic:
       
   260         frame->editor()->command("ToggleItalic").execute();
       
   261         break;
       
   262     case ContextMenuItemTagUnderline:
       
   263         frame->editor()->toggleUnderline();
       
   264         break;
       
   265     case ContextMenuItemTagOutline:
       
   266         // We actually never enable this because CSS does not have a way to specify an outline font,
       
   267         // which may make this difficult to implement. Maybe a special case of text-shadow?
       
   268         break;
       
   269     case ContextMenuItemTagStartSpeaking: {
       
   270         ExceptionCode ec;
       
   271         RefPtr<Range> selectedRange = frame->selection()->toNormalizedRange();
       
   272         if (!selectedRange || selectedRange->collapsed(ec)) {
       
   273             Document* document = result.innerNonSharedNode()->document();
       
   274             selectedRange = document->createRange();
       
   275             selectedRange->selectNode(document->documentElement(), ec);
       
   276         }
       
   277         m_client->speak(plainText(selectedRange.get()));
       
   278         break;
       
   279     }
       
   280     case ContextMenuItemTagStopSpeaking:
       
   281         m_client->stopSpeaking();
       
   282         break;
       
   283     case ContextMenuItemTagDefaultDirection:
       
   284         frame->editor()->setBaseWritingDirection(NaturalWritingDirection);
       
   285         break;
       
   286     case ContextMenuItemTagLeftToRight:
       
   287         frame->editor()->setBaseWritingDirection(LeftToRightWritingDirection);
       
   288         break;
       
   289     case ContextMenuItemTagRightToLeft:
       
   290         frame->editor()->setBaseWritingDirection(RightToLeftWritingDirection);
       
   291         break;
       
   292     case ContextMenuItemTagTextDirectionDefault:
       
   293         frame->editor()->command("MakeTextWritingDirectionNatural").execute();
       
   294         break;
       
   295     case ContextMenuItemTagTextDirectionLeftToRight:
       
   296         frame->editor()->command("MakeTextWritingDirectionLeftToRight").execute();
       
   297         break;
       
   298     case ContextMenuItemTagTextDirectionRightToLeft:
       
   299         frame->editor()->command("MakeTextWritingDirectionRightToLeft").execute();
       
   300         break;
       
   301 #if PLATFORM(MAC)
       
   302     case ContextMenuItemTagSearchInSpotlight:
       
   303         m_client->searchWithSpotlight();
       
   304         break;
       
   305 #endif
       
   306     case ContextMenuItemTagShowSpellingPanel:
       
   307         frame->editor()->showSpellingGuessPanel();
       
   308         break;
       
   309     case ContextMenuItemTagCheckSpelling:
       
   310         frame->editor()->advanceToNextMisspelling();
       
   311         break;
       
   312     case ContextMenuItemTagCheckSpellingWhileTyping:
       
   313         frame->editor()->toggleContinuousSpellChecking();
       
   314         break;
       
   315 #ifndef BUILDING_ON_TIGER
       
   316     case ContextMenuItemTagCheckGrammarWithSpelling:
       
   317         frame->editor()->toggleGrammarChecking();
       
   318         break;
       
   319 #endif
       
   320 #if PLATFORM(MAC)
       
   321     case ContextMenuItemTagShowFonts:
       
   322         frame->editor()->showFontPanel();
       
   323         break;
       
   324     case ContextMenuItemTagStyles:
       
   325         frame->editor()->showStylesPanel();
       
   326         break;
       
   327     case ContextMenuItemTagShowColors:
       
   328         frame->editor()->showColorPanel();
       
   329         break;
       
   330 #endif
       
   331 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
       
   332     case ContextMenuItemTagMakeUpperCase:
       
   333         frame->editor()->uppercaseWord();
       
   334         break;
       
   335     case ContextMenuItemTagMakeLowerCase:
       
   336         frame->editor()->lowercaseWord();
       
   337         break;
       
   338     case ContextMenuItemTagCapitalize:
       
   339         frame->editor()->capitalizeWord();
       
   340         break;
       
   341     case ContextMenuItemTagShowSubstitutions:
       
   342         frame->editor()->showSubstitutionsPanel();
       
   343         break;
       
   344     case ContextMenuItemTagSmartCopyPaste:
       
   345         frame->editor()->toggleSmartInsertDelete();
       
   346         break;
       
   347     case ContextMenuItemTagSmartQuotes:
       
   348         frame->editor()->toggleAutomaticQuoteSubstitution();
       
   349         break;
       
   350     case ContextMenuItemTagSmartDashes:
       
   351         frame->editor()->toggleAutomaticDashSubstitution();
       
   352         break;
       
   353     case ContextMenuItemTagSmartLinks:
       
   354         frame->editor()->toggleAutomaticLinkDetection();
       
   355         break;
       
   356     case ContextMenuItemTagTextReplacement:
       
   357         frame->editor()->toggleAutomaticTextReplacement();
       
   358         break;
       
   359     case ContextMenuItemTagCorrectSpellingAutomatically:
       
   360         frame->editor()->toggleAutomaticSpellingCorrection();
       
   361         break;
       
   362     case ContextMenuItemTagChangeBack:
       
   363         frame->editor()->changeBackToReplacedString(result.replacedString());
       
   364         break;
       
   365 #endif
       
   366 #if ENABLE(INSPECTOR)
       
   367     case ContextMenuItemTagInspectElement:
       
   368         if (Page* page = frame->page())
       
   369             page->inspectorController()->inspect(result.innerNonSharedNode());
       
   370         break;
       
   371 #endif
       
   372     default:
       
   373         break;
       
   374     }
       
   375 }
       
   376 
       
   377 } // namespace WebCore
       
   378 
       
   379 #endif // ENABLE(CONTEXT_MENUS)