chrome_render_widget_host_view_mac_delegate.mm revision 0529e5d033099cbfc42635f6f6183833b09dff6e
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// Notification that a wheel event was unhandled. 110- (void)gotUnhandledWheelEvent { 111 [historySwiper_ gotUnhandledWheelEvent]; 112} 113 114// Notification of scroll offset pinning. 115- (void)scrollOffsetPinnedToLeft:(BOOL)left toRight:(BOOL)right { 116 [historySwiper_ scrollOffsetPinnedToLeft:left toRight:right]; 117} 118 119// Notification of whether the view has a horizontal scrollbar. 120- (void)setHasHorizontalScrollbar:(BOOL)has_horizontal_scrollbar { 121 [historySwiper_ setHasHorizontalScrollbar:has_horizontal_scrollbar]; 122} 123 124// NSWindow events. 125 126- (void)beginGestureWithEvent:(NSEvent*)event { 127 [historySwiper_ beginGestureWithEvent:event]; 128} 129 130- (void)endGestureWithEvent:(NSEvent*)event { 131 [historySwiper_ endGestureWithEvent:event]; 132} 133 134// This is a low level API which provides touches associated with an event. 135// It is used in conjunction with gestures to determine finger placement 136// on the trackpad. 137- (void)touchesMovedWithEvent:(NSEvent*)event { 138 [historySwiper_ touchesMovedWithEvent:event]; 139} 140 141- (void)touchesBeganWithEvent:(NSEvent*)event { 142 [historySwiper_ touchesBeganWithEvent:event]; 143} 144 145- (void)touchesCancelledWithEvent:(NSEvent*)event { 146 [historySwiper_ touchesCancelledWithEvent:event]; 147} 148 149- (void)touchesEndedWithEvent:(NSEvent*)event { 150 [historySwiper_ touchesEndedWithEvent:event]; 151} 152 153- (BOOL)canRubberbandLeft:(NSView*)view { 154 return [historySwiper_ canRubberbandLeft:view]; 155} 156 157- (BOOL)canRubberbandRight:(NSView*)view { 158 return [historySwiper_ canRubberbandRight:view]; 159} 160 161// HistorySwiperDelegate methods 162 163- (BOOL)shouldAllowHistorySwiping { 164 if (!renderWidgetHost_ || !renderWidgetHost_->IsRenderView()) 165 return NO; 166 if (DevToolsWindow::IsDevToolsWindow( 167 RenderViewHost::From(renderWidgetHost_))) { 168 return NO; 169 } 170 171 return YES; 172} 173 174- (NSView*)viewThatWantsHistoryOverlay { 175 return renderWidgetHost_->GetView()->GetNativeView(); 176} 177 178- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item 179 isValidItem:(BOOL*)valid { 180 SEL action = [item action]; 181 182 // For now, this action is always enabled for render view; 183 // this is sub-optimal. 184 // TODO(suzhe): Plumb the "can*" methods up from WebCore. 185 if (action == @selector(checkSpelling:)) { 186 *valid = renderWidgetHost_->IsRenderView(); 187 return YES; 188 } 189 190 // TODO(groby): Clarify who sends this and if toggleContinuousSpellChecking: 191 // is still necessary. 192 if (action == @selector(toggleContinuousSpellChecking:)) { 193 if ([(id)item respondsToSelector:@selector(setState:)]) { 194 content::RenderProcessHost* host = renderWidgetHost_->GetProcess(); 195 Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext()); 196 DCHECK(profile); 197 spellcheckChecked_ = 198 profile->GetPrefs()->GetBoolean(prefs::kEnableContinuousSpellcheck); 199 NSCellStateValue checkedState = 200 spellcheckChecked_ ? NSOnState : NSOffState; 201 [(id)item setState:checkedState]; 202 } 203 *valid = spellcheckEnabled_; 204 return YES; 205 } 206 207 return NO; 208} 209 210// Spellchecking methods 211// The next five methods are implemented here since this class is the first 212// responder for anything in the browser. 213 214// This message is sent whenever the user specifies that a word should be 215// changed from the spellChecker. 216- (void)changeSpelling:(id)sender { 217 // Grab the currently selected word from the spell panel, as this is the word 218 // that we want to replace the selected word in the text with. 219 NSString* newWord = [[sender selectedCell] stringValue]; 220 if (newWord != nil) { 221 content::WebContents* webContents = 222 content::WebContents::FromRenderViewHost( 223 RenderViewHost::From(renderWidgetHost_)); 224 webContents->Replace(base::SysNSStringToUTF16(newWord)); 225 } 226} 227 228// This message is sent by NSSpellChecker whenever the next word should be 229// advanced to, either after a correction or clicking the "Find Next" button. 230// This isn't documented anywhere useful, like in NSSpellProtocol.h with the 231// other spelling panel methods. This is probably because Apple assumes that the 232// the spelling panel will be used with an NSText, which will automatically 233// catch this and advance to the next word for you. Thanks Apple. 234// This is also called from the Edit -> Spelling -> Check Spelling menu item. 235- (void)checkSpelling:(id)sender { 236 renderWidgetHost_->Send(new SpellCheckMsg_AdvanceToNextMisspelling( 237 renderWidgetHost_->GetRoutingID())); 238} 239 240// This message is sent by the spelling panel whenever a word is ignored. 241- (void)ignoreSpelling:(id)sender { 242 // Ideally, we would ask the current RenderView for its tag, but that would 243 // mean making a blocking IPC call from the browser. Instead, 244 // spellcheck_mac::CheckSpelling remembers the last tag and 245 // spellcheck_mac::IgnoreWord assumes that is the correct tag. 246 NSString* wordToIgnore = [sender stringValue]; 247 if (wordToIgnore != nil) 248 spellcheck_mac::IgnoreWord(base::SysNSStringToUTF16(wordToIgnore)); 249} 250 251- (void)showGuessPanel:(id)sender { 252 renderWidgetHost_->Send(new SpellCheckMsg_ToggleSpellPanel( 253 renderWidgetHost_->GetRoutingID(), 254 spellcheck_mac::SpellingPanelVisible())); 255} 256 257- (void)toggleContinuousSpellChecking:(id)sender { 258 content::RenderProcessHost* host = renderWidgetHost_->GetProcess(); 259 Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext()); 260 DCHECK(profile); 261 PrefService* pref = profile->GetPrefs(); 262 pref->SetBoolean(prefs::kEnableContinuousSpellcheck, 263 !pref->GetBoolean(prefs::kEnableContinuousSpellcheck)); 264} 265 266- (void)spellCheckEnabled:(BOOL)enabled checked:(BOOL)checked { 267 spellcheckEnabled_ = enabled; 268 spellcheckChecked_ = checked; 269} 270 271// END Spellchecking methods 272 273@end 274