chrome_render_widget_host_view_mac_delegate.mm revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
1d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen// Use of this source code is governed by a BSD-style license that can be
3d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen// found in the LICENSE file.
4d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
5d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#import "chrome/browser/renderer_host/chrome_render_widget_host_view_mac_delegate.h"
6d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
7d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include <cmath>
8d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
9d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "base/prefs/pref_service.h"
10d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "base/strings/sys_string_conversions.h"
11d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/browser/devtools/devtools_window.h"
12d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/browser/profiles/profile.h"
13d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#import "chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.h"
14d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/browser/spellchecker/spellcheck_platform_mac.h"
15d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/browser/ui/browser.h"
16d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/browser/ui/browser_commands.h"
17d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/browser/ui/browser_finder.h"
18d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#import "chrome/browser/ui/cocoa/browser_window_controller.h"
19d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#import "chrome/browser/ui/cocoa/view_id_util.h"
20d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/common/pref_names.h"
21d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/common/spellcheck_messages.h"
22d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "chrome/common/url_constants.h"
23d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "content/public/browser/render_process_host.h"
24d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#include "content/public/browser/render_view_host.h"
25d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen#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- (void)gotWheelEventConsumed:(BOOL)consumed {
110  [historySwiper_ gotWheelEventConsumed:consumed];
111}
112
113// NSWindow events.
114
115- (void)beginGestureWithEvent:(NSEvent*)event {
116  [historySwiper_ beginGestureWithEvent:event];
117}
118
119- (void)endGestureWithEvent:(NSEvent*)event {
120  [historySwiper_ endGestureWithEvent:event];
121}
122
123// This is a low level API which provides touches associated with an event.
124// It is used in conjunction with gestures to determine finger placement
125// on the trackpad.
126- (void)touchesMovedWithEvent:(NSEvent*)event {
127  [historySwiper_ touchesMovedWithEvent:event];
128}
129
130- (void)touchesBeganWithEvent:(NSEvent*)event {
131  [historySwiper_ touchesBeganWithEvent:event];
132}
133
134- (void)touchesCancelledWithEvent:(NSEvent*)event {
135  [historySwiper_ touchesCancelledWithEvent:event];
136}
137
138- (void)touchesEndedWithEvent:(NSEvent*)event {
139  [historySwiper_ touchesEndedWithEvent:event];
140}
141
142- (BOOL)canRubberbandLeft:(NSView*)view {
143  return [historySwiper_ canRubberbandLeft:view];
144}
145
146- (BOOL)canRubberbandRight:(NSView*)view {
147  return [historySwiper_ canRubberbandRight:view];
148}
149
150// HistorySwiperDelegate methods
151
152- (BOOL)shouldAllowHistorySwiping {
153  if (!renderWidgetHost_ || !renderWidgetHost_->IsRenderView())
154    return NO;
155  if (DevToolsWindow::IsDevToolsWindow(
156      RenderViewHost::From(renderWidgetHost_))) {
157    return NO;
158  }
159
160  return YES;
161}
162
163- (NSView*)viewThatWantsHistoryOverlay {
164  return renderWidgetHost_->GetView()->GetNativeView();
165}
166
167- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item
168                      isValidItem:(BOOL*)valid {
169  SEL action = [item action];
170
171  // For now, this action is always enabled for render view;
172  // this is sub-optimal.
173  // TODO(suzhe): Plumb the "can*" methods up from WebCore.
174  if (action == @selector(checkSpelling:)) {
175    *valid = renderWidgetHost_->IsRenderView();
176    return YES;
177  }
178
179  // TODO(groby): Clarify who sends this and if toggleContinuousSpellChecking:
180  // is still necessary.
181  if (action == @selector(toggleContinuousSpellChecking:)) {
182    if ([(id)item respondsToSelector:@selector(setState:)]) {
183      content::RenderProcessHost* host = renderWidgetHost_->GetProcess();
184      Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext());
185      DCHECK(profile);
186      spellcheckChecked_ =
187          profile->GetPrefs()->GetBoolean(prefs::kEnableContinuousSpellcheck);
188      NSCellStateValue checkedState =
189          spellcheckChecked_ ? NSOnState : NSOffState;
190      [(id)item setState:checkedState];
191    }
192    *valid = spellcheckEnabled_;
193    return YES;
194  }
195
196  return NO;
197}
198
199// Spellchecking methods
200// The next five methods are implemented here since this class is the first
201// responder for anything in the browser.
202
203// This message is sent whenever the user specifies that a word should be
204// changed from the spellChecker.
205- (void)changeSpelling:(id)sender {
206  // Grab the currently selected word from the spell panel, as this is the word
207  // that we want to replace the selected word in the text with.
208  NSString* newWord = [[sender selectedCell] stringValue];
209  if (newWord != nil) {
210    content::WebContents* webContents =
211        content::WebContents::FromRenderViewHost(
212            RenderViewHost::From(renderWidgetHost_));
213    webContents->Replace(base::SysNSStringToUTF16(newWord));
214  }
215}
216
217// This message is sent by NSSpellChecker whenever the next word should be
218// advanced to, either after a correction or clicking the "Find Next" button.
219// This isn't documented anywhere useful, like in NSSpellProtocol.h with the
220// other spelling panel methods. This is probably because Apple assumes that the
221// the spelling panel will be used with an NSText, which will automatically
222// catch this and advance to the next word for you. Thanks Apple.
223// This is also called from the Edit -> Spelling -> Check Spelling menu item.
224- (void)checkSpelling:(id)sender {
225  renderWidgetHost_->Send(new SpellCheckMsg_AdvanceToNextMisspelling(
226      renderWidgetHost_->GetRoutingID()));
227}
228
229// This message is sent by the spelling panel whenever a word is ignored.
230- (void)ignoreSpelling:(id)sender {
231  // Ideally, we would ask the current RenderView for its tag, but that would
232  // mean making a blocking IPC call from the browser. Instead,
233  // spellcheck_mac::CheckSpelling remembers the last tag and
234  // spellcheck_mac::IgnoreWord assumes that is the correct tag.
235  NSString* wordToIgnore = [sender stringValue];
236  if (wordToIgnore != nil)
237    spellcheck_mac::IgnoreWord(base::SysNSStringToUTF16(wordToIgnore));
238}
239
240- (void)showGuessPanel:(id)sender {
241  renderWidgetHost_->Send(new SpellCheckMsg_ToggleSpellPanel(
242      renderWidgetHost_->GetRoutingID(),
243      spellcheck_mac::SpellingPanelVisible()));
244}
245
246- (void)toggleContinuousSpellChecking:(id)sender {
247  content::RenderProcessHost* host = renderWidgetHost_->GetProcess();
248  Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext());
249  DCHECK(profile);
250  PrefService* pref = profile->GetPrefs();
251  pref->SetBoolean(prefs::kEnableContinuousSpellcheck,
252                   !pref->GetBoolean(prefs::kEnableContinuousSpellcheck));
253}
254
255- (void)spellCheckEnabled:(BOOL)enabled checked:(BOOL)checked {
256  spellcheckEnabled_ = enabled;
257  spellcheckChecked_ = checked;
258}
259
260// END Spellchecking methods
261
262@end
263