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