chrome_render_widget_host_view_mac_delegate.mm revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#import "chrome/browser/renderer_host/chrome_render_widget_host_view_mac_delegate.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <cmath>
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/prefs/pref_service.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/strings/sys_string_conversions.h"
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/devtools/devtools_window.h"
1290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/profiles/profile.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#import "chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/spellchecker/spellcheck_platform_mac.h"
15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "chrome/browser/ui/browser.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_commands.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser_finder.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#import "chrome/browser/ui/cocoa/browser_window_controller.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#import "chrome/browser/ui/cocoa/view_id_util.h"
20c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/common/pref_names.h"
21c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/common/spellcheck_messages.h"
22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/common/url_constants.h"
23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "content/public/browser/render_process_host.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_view_host.h"
256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "content/public/browser/render_widget_host.h"
2690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "content/public/browser/render_widget_host_view.h"
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/public/browser/web_contents.h"
286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "content/public/browser/web_contents_observer.h"
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::RenderViewHost;
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)@interface ChromeRenderWidgetHostViewMacDelegate () <HistorySwiperDelegate>
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)- (void)spellCheckEnabled:(BOOL)enabled checked:(BOOL)checked;
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)@end
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace ChromeRenderWidgetHostViewMacDelegateInternal {
37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Filters the message sent by the renderer to know if spellchecking is enabled
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// or not for the currently focused element.
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class SpellCheckObserver : public content::WebContentsObserver {
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) public:
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SpellCheckObserver(
43a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      RenderViewHost* host,
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      ChromeRenderWidgetHostViewMacDelegate* view_delegate)
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      : content::WebContentsObserver(
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            content::WebContents::FromRenderViewHost(host)),
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        view_delegate_(view_delegate) {
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
49a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual ~SpellCheckObserver() {
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
53f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private:
54f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool handled = true;
56f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    IPC_BEGIN_MESSAGE_MAP(SpellCheckObserver, message)
57f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      IPC_MESSAGE_HANDLER(SpellCheckHostMsg_ToggleSpellCheck,
58f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                          OnToggleSpellCheck)
591e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      IPC_MESSAGE_UNHANDLED(handled = false)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IPC_END_MESSAGE_MAP()
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return handled;
62a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void OnToggleSpellCheck(bool enabled, bool checked) {
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    [view_delegate_ spellCheckEnabled:enabled checked:checked];
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
68f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  ChromeRenderWidgetHostViewMacDelegate* view_delegate_;
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}  // namespace ChromeRenderWidgetHostViewMacDelegateInternal
72f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)@implementation ChromeRenderWidgetHostViewMacDelegate
74a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)- (id)initWithRenderWidgetHost:(content::RenderWidgetHost*)renderWidgetHost {
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  self = [super init];
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (self) {
78558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    renderWidgetHost_ = renderWidgetHost;
79f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    NSView* nativeView = renderWidgetHost_->GetView()->GetNativeView();
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    view_id_util::SetID(nativeView, VIEW_ID_TAB_CONTAINER);
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
82010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    if (renderWidgetHost_->IsRenderView()) {
83f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      spellingObserver_.reset(
84f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          new ChromeRenderWidgetHostViewMacDelegateInternal::SpellCheckObserver(
856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)              RenderViewHost::From(renderWidgetHost_), self));
866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    }
876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    historySwiper_.reset([[HistorySwiper alloc] initWithDelegate:self]);
8903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  }
9003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  return self;
9103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
927dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)- (void)dealloc {
94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  [historySwiper_ setDelegate:nil];
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  [super dealloc];
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)- (void)viewGone:(NSView*)view {
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  view_id_util::UnsetID(view);
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  [self autorelease];
10190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
10290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Handle an event. All incoming key and mouse events flow through this
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// delegate method if implemented. Return YES if the event is fully handled, or
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// NO if normal processing should take place.
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)- (BOOL)handleEvent:(NSEvent*)event {
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return [historySwiper_ handleEvent:event];
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
109f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
110f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Notification that a wheel event was unhandled.
111a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)- (void)gotUnhandledWheelEvent {
112558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch  [historySwiper_ gotUnhandledWheelEvent];
113f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
11590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Notification of scroll offset pinning.
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)- (void)scrollOffsetPinnedToLeft:(BOOL)left toRight:(BOOL)right {
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  [historySwiper_ scrollOffsetPinnedToLeft:left toRight:right];
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Notification of whether the view has a horizontal scrollbar.
121- (void)setHasHorizontalScrollbar:(BOOL)has_horizontal_scrollbar {
122  [historySwiper_ setHasHorizontalScrollbar:has_horizontal_scrollbar];
123}
124
125// NSWindow events.
126
127- (void)beginGestureWithEvent:(NSEvent*)event {
128  [historySwiper_ beginGestureWithEvent:event];
129}
130
131- (void)endGestureWithEvent:(NSEvent*)event {
132  [historySwiper_ endGestureWithEvent:event];
133}
134
135// This is a low level API which provides touches associated with an event.
136// It is used in conjunction with gestures to determine finger placement
137// on the trackpad.
138- (void)touchesMovedWithEvent:(NSEvent*)event {
139  [historySwiper_ touchesMovedWithEvent:event];
140}
141
142- (void)touchesBeganWithEvent:(NSEvent*)event {
143  [historySwiper_ touchesBeganWithEvent:event];
144}
145
146- (void)touchesCancelledWithEvent:(NSEvent*)event {
147  [historySwiper_ touchesCancelledWithEvent:event];
148}
149
150- (void)touchesEndedWithEvent:(NSEvent*)event {
151  [historySwiper_ touchesEndedWithEvent:event];
152}
153
154// HistorySwiperDelegate methods
155
156- (BOOL)shouldAllowHistorySwiping {
157  if (!renderWidgetHost_ || !renderWidgetHost_->IsRenderView())
158    return NO;
159  if (DevToolsWindow::IsDevToolsWindow(
160      RenderViewHost::From(renderWidgetHost_))) {
161    return NO;
162  }
163
164  return YES;
165}
166
167- (NSView*)viewThatWantsHistoryOverlay {
168  return renderWidgetHost_->GetView()->GetNativeView();
169}
170
171- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item
172                      isValidItem:(BOOL*)valid {
173  SEL action = [item action];
174
175  // For now, this action is always enabled for render view;
176  // this is sub-optimal.
177  // TODO(suzhe): Plumb the "can*" methods up from WebCore.
178  if (action == @selector(checkSpelling:)) {
179    *valid = renderWidgetHost_->IsRenderView();
180    return YES;
181  }
182
183  // TODO(groby): Clarify who sends this and if toggleContinuousSpellChecking:
184  // is still necessary.
185  if (action == @selector(toggleContinuousSpellChecking:)) {
186    if ([(id)item respondsToSelector:@selector(setState:)]) {
187      content::RenderProcessHost* host = renderWidgetHost_->GetProcess();
188      Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext());
189      DCHECK(profile);
190      spellcheckChecked_ =
191          profile->GetPrefs()->GetBoolean(prefs::kEnableContinuousSpellcheck);
192      NSCellStateValue checkedState =
193          spellcheckChecked_ ? NSOnState : NSOffState;
194      [(id)item setState:checkedState];
195    }
196    *valid = spellcheckEnabled_;
197    return YES;
198  }
199
200  return NO;
201}
202
203// Spellchecking methods
204// The next five methods are implemented here since this class is the first
205// responder for anything in the browser.
206
207// This message is sent whenever the user specifies that a word should be
208// changed from the spellChecker.
209- (void)changeSpelling:(id)sender {
210  // Grab the currently selected word from the spell panel, as this is the word
211  // that we want to replace the selected word in the text with.
212  NSString* newWord = [[sender selectedCell] stringValue];
213  if (newWord != nil) {
214    renderWidgetHost_->Replace(base::SysNSStringToUTF16(newWord));
215  }
216}
217
218// This message is sent by NSSpellChecker whenever the next word should be
219// advanced to, either after a correction or clicking the "Find Next" button.
220// This isn't documented anywhere useful, like in NSSpellProtocol.h with the
221// other spelling panel methods. This is probably because Apple assumes that the
222// the spelling panel will be used with an NSText, which will automatically
223// catch this and advance to the next word for you. Thanks Apple.
224// This is also called from the Edit -> Spelling -> Check Spelling menu item.
225- (void)checkSpelling:(id)sender {
226  renderWidgetHost_->Send(new SpellCheckMsg_AdvanceToNextMisspelling(
227      renderWidgetHost_->GetRoutingID()));
228}
229
230// This message is sent by the spelling panel whenever a word is ignored.
231- (void)ignoreSpelling:(id)sender {
232  // Ideally, we would ask the current RenderView for its tag, but that would
233  // mean making a blocking IPC call from the browser. Instead,
234  // spellcheck_mac::CheckSpelling remembers the last tag and
235  // spellcheck_mac::IgnoreWord assumes that is the correct tag.
236  NSString* wordToIgnore = [sender stringValue];
237  if (wordToIgnore != nil)
238    spellcheck_mac::IgnoreWord(base::SysNSStringToUTF16(wordToIgnore));
239}
240
241- (void)showGuessPanel:(id)sender {
242  renderWidgetHost_->Send(new SpellCheckMsg_ToggleSpellPanel(
243      renderWidgetHost_->GetRoutingID(),
244      spellcheck_mac::SpellingPanelVisible()));
245}
246
247- (void)toggleContinuousSpellChecking:(id)sender {
248  content::RenderProcessHost* host = renderWidgetHost_->GetProcess();
249  Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext());
250  DCHECK(profile);
251  PrefService* pref = profile->GetPrefs();
252  pref->SetBoolean(prefs::kEnableContinuousSpellcheck,
253                   !pref->GetBoolean(prefs::kEnableContinuousSpellcheck));
254}
255
256- (void)spellCheckEnabled:(BOOL)enabled checked:(BOOL)checked {
257  spellcheckEnabled_ = enabled;
258  spellcheckChecked_ = checked;
259}
260
261// END Spellchecking methods
262
263@end
264