1/* 2 * Copyright (C) 2010, 2011 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#import "config.h" 27#import "PageClientImpl.h" 28 29#import "DataReference.h" 30#import "DictionaryPopupInfo.h" 31#import "FindIndicator.h" 32#import "NativeWebKeyboardEvent.h" 33#import "WKAPICast.h" 34#import "WKStringCF.h" 35#import "WKViewInternal.h" 36#import "WebContextMenuProxyMac.h" 37#import "WebEditCommandProxy.h" 38#import "WebPopupMenuProxyMac.h" 39#import <WebCore/Cursor.h> 40#import <WebCore/FloatRect.h> 41#import <WebCore/FoundationExtras.h> 42#import <WebCore/GraphicsContext.h> 43#import <WebCore/KeyboardEvent.h> 44#import <WebCore/NotImplemented.h> 45#import <wtf/PassOwnPtr.h> 46#import <wtf/text/CString.h> 47#import <wtf/text/WTFString.h> 48#import <WebKitSystemInterface.h> 49 50@interface NSApplication (WebNSApplicationDetails) 51- (NSCursor *)_cursorRectCursor; 52@end 53 54using namespace WebCore; 55using namespace WebKit; 56 57@interface WKEditCommandObjC : NSObject 58{ 59 RefPtr<WebEditCommandProxy> m_command; 60} 61- (id)initWithWebEditCommandProxy:(PassRefPtr<WebEditCommandProxy>)command; 62- (WebEditCommandProxy*)command; 63@end 64 65@interface WKEditorUndoTargetObjC : NSObject 66- (void)undoEditing:(id)sender; 67- (void)redoEditing:(id)sender; 68@end 69 70@implementation WKEditCommandObjC 71 72- (id)initWithWebEditCommandProxy:(PassRefPtr<WebEditCommandProxy>)command 73{ 74 self = [super init]; 75 if (!self) 76 return nil; 77 78 m_command = command; 79 return self; 80} 81 82- (WebEditCommandProxy*)command 83{ 84 return m_command.get(); 85} 86 87@end 88 89@implementation WKEditorUndoTargetObjC 90 91- (void)undoEditing:(id)sender 92{ 93 ASSERT([sender isKindOfClass:[WKEditCommandObjC class]]); 94 [sender command]->unapply(); 95} 96 97- (void)redoEditing:(id)sender 98{ 99 ASSERT([sender isKindOfClass:[WKEditCommandObjC class]]); 100 [sender command]->reapply(); 101} 102 103@end 104 105namespace WebKit { 106 107NSString* nsStringFromWebCoreString(const String& string) 108{ 109 return string.impl() ? HardAutorelease(WKStringCopyCFString(0, toAPI(string.impl()))) : @""; 110} 111 112PassOwnPtr<PageClientImpl> PageClientImpl::create(WKView* wkView) 113{ 114 return adoptPtr(new PageClientImpl(wkView)); 115} 116 117PageClientImpl::PageClientImpl(WKView* wkView) 118 : m_wkView(wkView) 119 , m_undoTarget(AdoptNS, [[WKEditorUndoTargetObjC alloc] init]) 120{ 121} 122 123PageClientImpl::~PageClientImpl() 124{ 125} 126 127PassOwnPtr<DrawingAreaProxy> PageClientImpl::createDrawingAreaProxy() 128{ 129 return [m_wkView _createDrawingAreaProxy]; 130} 131 132void PageClientImpl::setViewNeedsDisplay(const WebCore::IntRect& rect) 133{ 134 [m_wkView setNeedsDisplayInRect:rect]; 135} 136 137void PageClientImpl::displayView() 138{ 139 [m_wkView displayIfNeeded]; 140} 141 142void PageClientImpl::scrollView(const IntRect& scrollRect, const IntSize& scrollOffset) 143{ 144 NSRect clippedScrollRect = NSIntersectionRect(scrollRect, NSOffsetRect(scrollRect, -scrollOffset.width(), -scrollOffset.height())); 145 146 [m_wkView translateRectsNeedingDisplayInRect:clippedScrollRect by:scrollOffset]; 147 [m_wkView scrollRect:clippedScrollRect by:scrollOffset]; 148} 149 150IntSize PageClientImpl::viewSize() 151{ 152 return IntSize([m_wkView bounds].size); 153} 154 155bool PageClientImpl::isViewWindowActive() 156{ 157 return [[m_wkView window] isKeyWindow] || [NSApp keyWindow] == [m_wkView window]; 158} 159 160bool PageClientImpl::isViewFocused() 161{ 162 return [m_wkView _isFocused]; 163} 164 165bool PageClientImpl::isViewVisible() 166{ 167 if (![m_wkView window]) 168 return false; 169 170 if (![[m_wkView window] isVisible]) 171 return false; 172 173 if ([m_wkView isHiddenOrHasHiddenAncestor]) 174 return false; 175 176 return true; 177} 178 179bool PageClientImpl::isViewInWindow() 180{ 181 return [m_wkView window]; 182} 183 184void PageClientImpl::processDidCrash() 185{ 186 [m_wkView _processDidCrash]; 187} 188 189void PageClientImpl::pageClosed() 190{ 191 [m_wkView _pageClosed]; 192} 193 194void PageClientImpl::didRelaunchProcess() 195{ 196 [m_wkView _didRelaunchProcess]; 197} 198 199void PageClientImpl::toolTipChanged(const String& oldToolTip, const String& newToolTip) 200{ 201 [m_wkView _toolTipChangedFrom:nsStringFromWebCoreString(oldToolTip) to:nsStringFromWebCoreString(newToolTip)]; 202} 203 204void PageClientImpl::setCursor(const WebCore::Cursor& cursor) 205{ 206 if (![NSApp _cursorRectCursor]) 207 [m_wkView _setCursor:cursor.platformCursor()]; 208} 209 210void PageClientImpl::setViewportArguments(const WebCore::ViewportArguments&) 211{ 212} 213 214void PageClientImpl::registerEditCommand(PassRefPtr<WebEditCommandProxy> prpCommand, WebPageProxy::UndoOrRedo undoOrRedo) 215{ 216 RefPtr<WebEditCommandProxy> command = prpCommand; 217 218 RetainPtr<WKEditCommandObjC> commandObjC(AdoptNS, [[WKEditCommandObjC alloc] initWithWebEditCommandProxy:command]); 219 String actionName = WebEditCommandProxy::nameForEditAction(command->editAction()); 220 221 NSUndoManager *undoManager = [m_wkView undoManager]; 222 [undoManager registerUndoWithTarget:m_undoTarget.get() selector:((undoOrRedo == WebPageProxy::Undo) ? @selector(undoEditing:) : @selector(redoEditing:)) object:commandObjC.get()]; 223 if (!actionName.isEmpty()) 224 [undoManager setActionName:(NSString *)actionName]; 225} 226 227void PageClientImpl::clearAllEditCommands() 228{ 229 [[m_wkView undoManager] removeAllActionsWithTarget:m_undoTarget.get()]; 230} 231 232bool PageClientImpl::canUndoRedo(WebPageProxy::UndoOrRedo undoOrRedo) 233{ 234 return (undoOrRedo == WebPageProxy::Undo) ? [[m_wkView undoManager] canUndo] : [[m_wkView undoManager] canRedo]; 235} 236 237void PageClientImpl::executeUndoRedo(WebPageProxy::UndoOrRedo undoOrRedo) 238{ 239 return (undoOrRedo == WebPageProxy::Undo) ? [[m_wkView undoManager] undo] : [[m_wkView undoManager] redo]; 240} 241 242bool PageClientImpl::interpretKeyEvent(const NativeWebKeyboardEvent& event, Vector<WebCore::KeypressCommand>& commands) 243{ 244 return [m_wkView _interpretKeyEvent:event.nativeEvent() savingCommandsTo:commands]; 245} 246 247void PageClientImpl::setDragImage(const IntPoint& clientPosition, PassRefPtr<ShareableBitmap> dragImage, bool isLinkDrag) 248{ 249 RetainPtr<CGImageRef> dragCGImage = dragImage->makeCGImage(); 250 RetainPtr<NSImage> dragNSImage(AdoptNS, [[NSImage alloc] initWithCGImage:dragCGImage.get() size:dragImage->size()]); 251 252 [m_wkView _setDragImage:dragNSImage.get() at:clientPosition linkDrag:isLinkDrag]; 253} 254 255void PageClientImpl::updateSecureInputState() 256{ 257 [m_wkView _updateSecureInputState]; 258} 259 260FloatRect PageClientImpl::convertToDeviceSpace(const FloatRect& rect) 261{ 262 return [m_wkView _convertToDeviceSpace:rect]; 263} 264 265FloatRect PageClientImpl::convertToUserSpace(const FloatRect& rect) 266{ 267 return [m_wkView _convertToUserSpace:rect]; 268} 269 270IntRect PageClientImpl::windowToScreen(const IntRect& rect) 271{ 272 NSRect tempRect = rect; 273 tempRect = [m_wkView convertRect:tempRect toView:nil]; 274 tempRect.origin = [[m_wkView window] convertBaseToScreen:tempRect.origin]; 275 return enclosingIntRect(tempRect); 276} 277 278void PageClientImpl::doneWithKeyEvent(const NativeWebKeyboardEvent& event, bool wasEventHandled) 279{ 280 NSEvent* nativeEvent = event.nativeEvent(); 281 if ([nativeEvent type] != NSKeyDown) 282 return; 283 if (wasEventHandled) 284 [NSCursor setHiddenUntilMouseMoves:YES]; 285 else 286 [m_wkView _resendKeyDownEvent:nativeEvent]; 287} 288 289PassRefPtr<WebPopupMenuProxy> PageClientImpl::createPopupMenuProxy(WebPageProxy* page) 290{ 291 return WebPopupMenuProxyMac::create(m_wkView, page); 292} 293 294PassRefPtr<WebContextMenuProxy> PageClientImpl::createContextMenuProxy(WebPageProxy* page) 295{ 296 return WebContextMenuProxyMac::create(m_wkView, page); 297} 298 299void PageClientImpl::setFindIndicator(PassRefPtr<FindIndicator> findIndicator, bool fadeOut) 300{ 301 [m_wkView _setFindIndicator:findIndicator fadeOut:fadeOut]; 302} 303 304void PageClientImpl::accessibilityWebProcessTokenReceived(const CoreIPC::DataReference& data) 305{ 306 NSData* remoteToken = [NSData dataWithBytes:data.data() length:data.size()]; 307 [m_wkView _setAccessibilityWebProcessToken:remoteToken]; 308} 309 310#if USE(ACCELERATED_COMPOSITING) 311void PageClientImpl::enterAcceleratedCompositingMode(const LayerTreeContext& layerTreeContext) 312{ 313 [m_wkView _enterAcceleratedCompositingMode:layerTreeContext]; 314} 315 316void PageClientImpl::exitAcceleratedCompositingMode() 317{ 318 [m_wkView _exitAcceleratedCompositingMode]; 319} 320#endif // USE(ACCELERATED_COMPOSITING) 321 322void PageClientImpl::setComplexTextInputEnabled(uint64_t pluginComplexTextInputIdentifier, bool complexTextInputEnabled) 323{ 324 [m_wkView _setComplexTextInputEnabled:complexTextInputEnabled pluginComplexTextInputIdentifier:pluginComplexTextInputIdentifier]; 325} 326 327CGContextRef PageClientImpl::containingWindowGraphicsContext() 328{ 329 NSWindow *window = [m_wkView window]; 330 331 // Don't try to get the graphics context if the NSWindow doesn't have a window device. 332 if ([window windowNumber] <= 0) 333 return 0; 334 335 return static_cast<CGContextRef>([[window graphicsContext] graphicsPort]); 336} 337 338void PageClientImpl::didChangeScrollbarsForMainFrame() const 339{ 340 [m_wkView _didChangeScrollbarsForMainFrame]; 341} 342 343void PageClientImpl::didCommitLoadForMainFrame(bool useCustomRepresentation) 344{ 345 [m_wkView _setPageHasCustomRepresentation:useCustomRepresentation]; 346} 347 348void PageClientImpl::didFinishLoadingDataForCustomRepresentation(const String& suggestedFilename, const CoreIPC::DataReference& dataReference) 349{ 350 [m_wkView _didFinishLoadingDataForCustomRepresentationWithSuggestedFilename:suggestedFilename dataReference:dataReference]; 351} 352 353double PageClientImpl::customRepresentationZoomFactor() 354{ 355 return [m_wkView _customRepresentationZoomFactor]; 356} 357 358void PageClientImpl::setCustomRepresentationZoomFactor(double zoomFactor) 359{ 360 [m_wkView _setCustomRepresentationZoomFactor:zoomFactor]; 361} 362 363void PageClientImpl::findStringInCustomRepresentation(const String& string, FindOptions options, unsigned maxMatchCount) 364{ 365 [m_wkView _findStringInCustomRepresentation:string withFindOptions:options maxMatchCount:maxMatchCount]; 366} 367 368void PageClientImpl::countStringMatchesInCustomRepresentation(const String& string, FindOptions options, unsigned maxMatchCount) 369{ 370 [m_wkView _countStringMatchesInCustomRepresentation:string withFindOptions:options maxMatchCount:maxMatchCount]; 371} 372 373void PageClientImpl::flashBackingStoreUpdates(const Vector<IntRect>&) 374{ 375 notImplemented(); 376} 377 378void PageClientImpl::didPerformDictionaryLookup(const String& text, double scaleFactor, const DictionaryPopupInfo& dictionaryPopupInfo) 379{ 380 NSFontDescriptor *fontDescriptor = [NSFontDescriptor fontDescriptorWithFontAttributes:(NSDictionary *)dictionaryPopupInfo.fontInfo.fontAttributeDictionary.get()]; 381 NSFont *font = [NSFont fontWithDescriptor:fontDescriptor size:((scaleFactor != 1) ? [fontDescriptor pointSize] * scaleFactor : 0)]; 382 383 RetainPtr<NSMutableAttributedString> attributedString(AdoptNS, [[NSMutableAttributedString alloc] initWithString:nsStringFromWebCoreString(text)]); 384 [attributedString.get() addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, [attributedString.get() length])]; 385 386 NSPoint textBaselineOrigin = dictionaryPopupInfo.origin; 387 textBaselineOrigin.y += [font ascender]; 388 389#if !defined(BUILDING_ON_SNOW_LEOPARD) 390 // Convert to screen coordinates. 391 textBaselineOrigin = [m_wkView convertPoint:textBaselineOrigin toView:nil]; 392 textBaselineOrigin = [m_wkView.window convertRectToScreen:NSMakeRect(textBaselineOrigin.x, textBaselineOrigin.y, 0, 0)].origin; 393 394 WKShowWordDefinitionWindow(attributedString.get(), textBaselineOrigin, (NSDictionary *)dictionaryPopupInfo.options.get()); 395#else 396 // If the dictionary lookup is being triggered by a hot key, force the overlay style. 397 NSDictionary *options = (dictionaryPopupInfo.type == DictionaryPopupInfo::HotKey) ? [NSDictionary dictionaryWithObject:NSDefinitionPresentationTypeOverlay forKey:NSDefinitionPresentationTypeKey] : 0; 398 [m_wkView showDefinitionForAttributedString:attributedString.get() range:NSMakeRange(0, [attributedString.get() length]) options:options baselineOriginProvider:^(NSRange adjustedRange) { return (NSPoint)textBaselineOrigin; }]; 399#endif 400} 401 402void PageClientImpl::dismissDictionaryLookupPanel() 403{ 404#if !defined(BUILDING_ON_SNOW_LEOPARD) 405 WKHideWordDefinitionWindow(); 406#endif 407} 408 409void PageClientImpl::showCorrectionPanel(CorrectionPanelInfo::PanelType type, const FloatRect& boundingBoxOfReplacedString, const String& replacedString, const String& replacementString, const Vector<String>& alternativeReplacementStrings) 410{ 411#if !defined(BUILDING_ON_SNOW_LEOPARD) 412 if (!isViewVisible() || !isViewInWindow()) 413 return; 414 m_correctionPanel.show(m_wkView, type, boundingBoxOfReplacedString, replacedString, replacementString, alternativeReplacementStrings); 415#endif 416} 417 418void PageClientImpl::dismissCorrectionPanel(ReasonForDismissingCorrectionPanel reason) 419{ 420#if !defined(BUILDING_ON_SNOW_LEOPARD) 421 m_correctionPanel.dismiss(reason); 422#endif 423} 424 425String PageClientImpl::dismissCorrectionPanelSoon(WebCore::ReasonForDismissingCorrectionPanel reason) 426{ 427#if !defined(BUILDING_ON_SNOW_LEOPARD) 428 return m_correctionPanel.dismissSoon(reason); 429#else 430 return String(); 431#endif 432} 433 434void PageClientImpl::recordAutocorrectionResponse(EditorClient::AutocorrectionResponseType responseType, const String& replacedString, const String& replacementString) 435{ 436#if !defined(BUILDING_ON_SNOW_LEOPARD) 437 NSCorrectionResponse response = responseType == EditorClient::AutocorrectionReverted ? NSCorrectionResponseReverted : NSCorrectionResponseEdited; 438 CorrectionPanel::recordAutocorrectionResponse(m_wkView, response, replacedString, replacementString); 439#endif 440} 441 442float PageClientImpl::userSpaceScaleFactor() const 443{ 444 NSWindow *window = [m_wkView window]; 445#if !defined(BUILDING_ON_SNOW_LEOPARD) 446 if (window) 447 return [window backingScaleFactor]; 448 return [[NSScreen mainScreen] backingScaleFactor]; 449#else 450 if (window) 451 return [window userSpaceScaleFactor]; 452 return [[NSScreen mainScreen] userSpaceScaleFactor]; 453#endif 454} 455 456bool PageClientImpl::executeSavedCommandBySelector(const String& selectorString) 457{ 458 return [m_wkView _executeSavedCommandBySelector:NSSelectorFromString(selectorString)]; 459} 460 461} // namespace WebKit 462