Home | History | Annotate | Download | only in renderer_host
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #import "chrome/browser/renderer_host/chrome_render_widget_host_view_mac_delegate.h"
      6 
      7 #include <cmath>
      8 
      9 #include "base/prefs/pref_service.h"
     10 #include "base/strings/sys_string_conversions.h"
     11 #include "chrome/browser/devtools/devtools_window.h"
     12 #include "chrome/browser/profiles/profile.h"
     13 #import "chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.h"
     14 #include "chrome/browser/spellchecker/spellcheck_platform_mac.h"
     15 #include "chrome/browser/ui/browser.h"
     16 #include "chrome/browser/ui/browser_commands.h"
     17 #include "chrome/browser/ui/browser_finder.h"
     18 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
     19 #import "chrome/browser/ui/cocoa/view_id_util.h"
     20 #include "chrome/common/pref_names.h"
     21 #include "chrome/common/spellcheck_messages.h"
     22 #include "chrome/common/url_constants.h"
     23 #include "content/public/browser/render_process_host.h"
     24 #include "content/public/browser/render_view_host.h"
     25 #include "content/public/browser/render_widget_host.h"
     26 #include "content/public/browser/render_widget_host_view.h"
     27 #include "content/public/browser/web_contents.h"
     28 #include "content/public/browser/web_contents_observer.h"
     29 
     30 using content::RenderViewHost;
     31 
     32 @interface ChromeRenderWidgetHostViewMacDelegate () <HistorySwiperDelegate>
     33 - (void)spellCheckEnabled:(BOOL)enabled checked:(BOOL)checked;
     34 @end
     35 
     36 namespace ChromeRenderWidgetHostViewMacDelegateInternal {
     37 
     38 // Filters the message sent by the renderer to know if spellchecking is enabled
     39 // or not for the currently focused element.
     40 class SpellCheckObserver : public content::WebContentsObserver {
     41  public:
     42   SpellCheckObserver(
     43       RenderViewHost* host,
     44       ChromeRenderWidgetHostViewMacDelegate* view_delegate)
     45       : content::WebContentsObserver(
     46             content::WebContents::FromRenderViewHost(host)),
     47         view_delegate_(view_delegate) {
     48   }
     49 
     50   virtual ~SpellCheckObserver() {
     51   }
     52 
     53  private:
     54   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
     55     bool handled = true;
     56     IPC_BEGIN_MESSAGE_MAP(SpellCheckObserver, message)
     57       IPC_MESSAGE_HANDLER(SpellCheckHostMsg_ToggleSpellCheck,
     58                           OnToggleSpellCheck)
     59       IPC_MESSAGE_UNHANDLED(handled = false)
     60     IPC_END_MESSAGE_MAP()
     61     return handled;
     62   }
     63 
     64   void OnToggleSpellCheck(bool enabled, bool checked) {
     65     [view_delegate_ spellCheckEnabled:enabled checked:checked];
     66   }
     67 
     68   ChromeRenderWidgetHostViewMacDelegate* view_delegate_;
     69 };
     70 
     71 }  // namespace ChromeRenderWidgetHostViewMacDelegateInternal
     72 
     73 @implementation ChromeRenderWidgetHostViewMacDelegate
     74 
     75 - (id)initWithRenderWidgetHost:(content::RenderWidgetHost*)renderWidgetHost {
     76   self = [super init];
     77   if (self) {
     78     renderWidgetHost_ = renderWidgetHost;
     79     NSView* nativeView = renderWidgetHost_->GetView()->GetNativeView();
     80     view_id_util::SetID(nativeView, VIEW_ID_TAB_CONTAINER);
     81 
     82     if (renderWidgetHost_->IsRenderView()) {
     83       spellingObserver_.reset(
     84           new ChromeRenderWidgetHostViewMacDelegateInternal::SpellCheckObserver(
     85               RenderViewHost::From(renderWidgetHost_), self));
     86     }
     87 
     88     historySwiper_.reset([[HistorySwiper alloc] initWithDelegate:self]);
     89   }
     90   return self;
     91 }
     92 
     93 - (void)dealloc {
     94   [historySwiper_ setDelegate:nil];
     95   [super dealloc];
     96 }
     97 
     98 - (void)viewGone:(NSView*)view {
     99   view_id_util::UnsetID(view);
    100 }
    101 
    102 // Handle an event. All incoming key and mouse events flow through this
    103 // delegate method if implemented. Return YES if the event is fully handled, or
    104 // NO if normal processing should take place.
    105 - (BOOL)handleEvent:(NSEvent*)event {
    106   return [historySwiper_ handleEvent:event];
    107 }
    108 
    109 // NSWindow events.
    110 
    111 - (void)beginGestureWithEvent:(NSEvent*)event {
    112   [historySwiper_ beginGestureWithEvent:event];
    113 }
    114 
    115 - (void)endGestureWithEvent:(NSEvent*)event {
    116   [historySwiper_ endGestureWithEvent:event];
    117 }
    118 
    119 // This is a low level API which provides touches associated with an event.
    120 // It is used in conjunction with gestures to determine finger placement
    121 // on the trackpad.
    122 - (void)touchesMovedWithEvent:(NSEvent*)event {
    123   [historySwiper_ touchesMovedWithEvent:event];
    124 }
    125 
    126 - (void)touchesBeganWithEvent:(NSEvent*)event {
    127   [historySwiper_ touchesBeganWithEvent:event];
    128 }
    129 
    130 - (void)touchesCancelledWithEvent:(NSEvent*)event {
    131   [historySwiper_ touchesCancelledWithEvent:event];
    132 }
    133 
    134 - (void)touchesEndedWithEvent:(NSEvent*)event {
    135   [historySwiper_ touchesEndedWithEvent:event];
    136 }
    137 
    138 - (BOOL)canRubberbandLeft:(NSView*)view {
    139   return [historySwiper_ canRubberbandLeft:view];
    140 }
    141 
    142 - (BOOL)canRubberbandRight:(NSView*)view {
    143   return [historySwiper_ canRubberbandRight:view];
    144 }
    145 
    146 // HistorySwiperDelegate methods
    147 
    148 - (BOOL)shouldAllowHistorySwiping {
    149   if (!renderWidgetHost_ || !renderWidgetHost_->IsRenderView())
    150     return NO;
    151   content::WebContents* webContents = content::WebContents::FromRenderViewHost(
    152       RenderViewHost::From(renderWidgetHost_));
    153   if (webContents && DevToolsWindow::IsDevToolsWindow(webContents)) {
    154     return NO;
    155   }
    156 
    157   return YES;
    158 }
    159 
    160 - (NSView*)viewThatWantsHistoryOverlay {
    161   return renderWidgetHost_->GetView()->GetNativeView();
    162 }
    163 
    164 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item
    165                       isValidItem:(BOOL*)valid {
    166   SEL action = [item action];
    167 
    168   // For now, this action is always enabled for render view;
    169   // this is sub-optimal.
    170   // TODO(suzhe): Plumb the "can*" methods up from WebCore.
    171   if (action == @selector(checkSpelling:)) {
    172     *valid = renderWidgetHost_->IsRenderView();
    173     return YES;
    174   }
    175 
    176   // TODO(groby): Clarify who sends this and if toggleContinuousSpellChecking:
    177   // is still necessary.
    178   if (action == @selector(toggleContinuousSpellChecking:)) {
    179     if ([(id)item respondsToSelector:@selector(setState:)]) {
    180       content::RenderProcessHost* host = renderWidgetHost_->GetProcess();
    181       Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext());
    182       DCHECK(profile);
    183       spellcheckChecked_ =
    184           profile->GetPrefs()->GetBoolean(prefs::kEnableContinuousSpellcheck);
    185       NSCellStateValue checkedState =
    186           spellcheckChecked_ ? NSOnState : NSOffState;
    187       [(id)item setState:checkedState];
    188     }
    189     *valid = spellcheckEnabled_;
    190     return YES;
    191   }
    192 
    193   return NO;
    194 }
    195 
    196 - (void)rendererHandledWheelEvent:(const blink::WebMouseWheelEvent&)event
    197                          consumed:(BOOL)consumed {
    198   [historySwiper_ rendererHandledWheelEvent:event consumed:consumed];
    199 }
    200 
    201 // Spellchecking methods
    202 // The next five methods are implemented here since this class is the first
    203 // responder for anything in the browser.
    204 
    205 // This message is sent whenever the user specifies that a word should be
    206 // changed from the spellChecker.
    207 - (void)changeSpelling:(id)sender {
    208   // Grab the currently selected word from the spell panel, as this is the word
    209   // that we want to replace the selected word in the text with.
    210   NSString* newWord = [[sender selectedCell] stringValue];
    211   if (newWord != nil) {
    212     content::WebContents* webContents =
    213         content::WebContents::FromRenderViewHost(
    214             RenderViewHost::From(renderWidgetHost_));
    215     webContents->Replace(base::SysNSStringToUTF16(newWord));
    216   }
    217 }
    218 
    219 // This message is sent by NSSpellChecker whenever the next word should be
    220 // advanced to, either after a correction or clicking the "Find Next" button.
    221 // This isn't documented anywhere useful, like in NSSpellProtocol.h with the
    222 // other spelling panel methods. This is probably because Apple assumes that the
    223 // the spelling panel will be used with an NSText, which will automatically
    224 // catch this and advance to the next word for you. Thanks Apple.
    225 // This is also called from the Edit -> Spelling -> Check Spelling menu item.
    226 - (void)checkSpelling:(id)sender {
    227   renderWidgetHost_->Send(new SpellCheckMsg_AdvanceToNextMisspelling(
    228       renderWidgetHost_->GetRoutingID()));
    229 }
    230 
    231 // This message is sent by the spelling panel whenever a word is ignored.
    232 - (void)ignoreSpelling:(id)sender {
    233   // Ideally, we would ask the current RenderView for its tag, but that would
    234   // mean making a blocking IPC call from the browser. Instead,
    235   // spellcheck_mac::CheckSpelling remembers the last tag and
    236   // spellcheck_mac::IgnoreWord assumes that is the correct tag.
    237   NSString* wordToIgnore = [sender stringValue];
    238   if (wordToIgnore != nil)
    239     spellcheck_mac::IgnoreWord(base::SysNSStringToUTF16(wordToIgnore));
    240 }
    241 
    242 - (void)showGuessPanel:(id)sender {
    243   renderWidgetHost_->Send(new SpellCheckMsg_ToggleSpellPanel(
    244       renderWidgetHost_->GetRoutingID(),
    245       spellcheck_mac::SpellingPanelVisible()));
    246 }
    247 
    248 - (void)toggleContinuousSpellChecking:(id)sender {
    249   content::RenderProcessHost* host = renderWidgetHost_->GetProcess();
    250   Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext());
    251   DCHECK(profile);
    252   PrefService* pref = profile->GetPrefs();
    253   pref->SetBoolean(prefs::kEnableContinuousSpellcheck,
    254                    !pref->GetBoolean(prefs::kEnableContinuousSpellcheck));
    255 }
    256 
    257 - (void)spellCheckEnabled:(BOOL)enabled checked:(BOOL)checked {
    258   spellcheckEnabled_ = enabled;
    259   spellcheckChecked_ = checked;
    260 }
    261 
    262 // END Spellchecking methods
    263 
    264 @end
    265