chrome_render_widget_host_view_mac_delegate.mm revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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
30using content::RenderViewHost;
31
32@interface ChromeRenderWidgetHostViewMacDelegate () <HistorySwiperDelegate>
33- (void)spellCheckEnabled:(BOOL)enabled checked:(BOOL)checked;
34@end
35
36namespace ChromeRenderWidgetHostViewMacDelegateInternal {
37
38// Filters the message sent by the renderer to know if spellchecking is enabled
39// or not for the currently focused element.
40class 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  if (DevToolsWindow::IsDevToolsWindow(
152      RenderViewHost::From(renderWidgetHost_))) {
153    return NO;
154  }
155
156  return YES;
157}
158
159- (NSView*)viewThatWantsHistoryOverlay {
160  return renderWidgetHost_->GetView()->GetNativeView();
161}
162
163- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item
164                      isValidItem:(BOOL*)valid {
165  SEL action = [item action];
166
167  // For now, this action is always enabled for render view;
168  // this is sub-optimal.
169  // TODO(suzhe): Plumb the "can*" methods up from WebCore.
170  if (action == @selector(checkSpelling:)) {
171    *valid = renderWidgetHost_->IsRenderView();
172    return YES;
173  }
174
175  // TODO(groby): Clarify who sends this and if toggleContinuousSpellChecking:
176  // is still necessary.
177  if (action == @selector(toggleContinuousSpellChecking:)) {
178    if ([(id)item respondsToSelector:@selector(setState:)]) {
179      content::RenderProcessHost* host = renderWidgetHost_->GetProcess();
180      Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext());
181      DCHECK(profile);
182      spellcheckChecked_ =
183          profile->GetPrefs()->GetBoolean(prefs::kEnableContinuousSpellcheck);
184      NSCellStateValue checkedState =
185          spellcheckChecked_ ? NSOnState : NSOffState;
186      [(id)item setState:checkedState];
187    }
188    *valid = spellcheckEnabled_;
189    return YES;
190  }
191
192  return NO;
193}
194
195- (void)rendererHandledWheelEvent:(const blink::WebMouseWheelEvent&)event
196                         consumed:(BOOL)consumed {
197  [historySwiper_ rendererHandledWheelEvent:event consumed:consumed];
198}
199
200// Spellchecking methods
201// The next five methods are implemented here since this class is the first
202// responder for anything in the browser.
203
204// This message is sent whenever the user specifies that a word should be
205// changed from the spellChecker.
206- (void)changeSpelling:(id)sender {
207  // Grab the currently selected word from the spell panel, as this is the word
208  // that we want to replace the selected word in the text with.
209  NSString* newWord = [[sender selectedCell] stringValue];
210  if (newWord != nil) {
211    content::WebContents* webContents =
212        content::WebContents::FromRenderViewHost(
213            RenderViewHost::From(renderWidgetHost_));
214    webContents->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