diff options
author | Ben Murdoch <benm@google.com> | 2011-06-02 12:07:03 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2011-06-10 10:47:21 +0100 |
commit | 2daae5fd11344eaa88a0d92b0f6d65f8d2255c00 (patch) | |
tree | e4964fbd1cb70599f7718ff03e50ea1dab33890b /Source/WebKit2/UIProcess/API/mac | |
parent | 87bdf0060a247bfbe668342b87e0874182e0ffa9 (diff) | |
download | external_webkit-2daae5fd11344eaa88a0d92b0f6d65f8d2255c00.zip external_webkit-2daae5fd11344eaa88a0d92b0f6d65f8d2255c00.tar.gz external_webkit-2daae5fd11344eaa88a0d92b0f6d65f8d2255c00.tar.bz2 |
Merge WebKit at r84325: Initial merge by git.
Change-Id: Ic1a909300ecc0a13ddc6b4e784371d2ac6e3d59b
Diffstat (limited to 'Source/WebKit2/UIProcess/API/mac')
-rw-r--r-- | Source/WebKit2/UIProcess/API/mac/PDFViewController.h | 5 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/API/mac/PDFViewController.mm | 221 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/API/mac/PageClientImpl.h | 17 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/API/mac/PageClientImpl.mm | 156 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/API/mac/WKTextInputWindowController.mm | 9 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/API/mac/WKView.h | 2 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/API/mac/WKView.mm | 660 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/API/mac/WKViewInternal.h | 19 |
8 files changed, 827 insertions, 262 deletions
diff --git a/Source/WebKit2/UIProcess/API/mac/PDFViewController.h b/Source/WebKit2/UIProcess/API/mac/PDFViewController.h index dc30f56..041aa7b 100644 --- a/Source/WebKit2/UIProcess/API/mac/PDFViewController.h +++ b/Source/WebKit2/UIProcess/API/mac/PDFViewController.h @@ -26,6 +26,7 @@ #ifndef PDFViewController_h #define PDFViewController_h +#include "WebFindOptions.h" #include <wtf/Forward.h> #include <wtf/Noncopyable.h> #include <wtf/PassOwnPtr.h> @@ -64,6 +65,10 @@ public: NSPrintOperation *makePrintOperation(NSPrintInfo *); void openPDFInFinder(); void savePDFToDownloadsFolder(); + void linkClicked(const String& url); + + void findString(const String&, FindOptions, unsigned maxMatchCount); + void countStringMatches(const String&, FindOptions, unsigned maxMatchCount); private: explicit PDFViewController(WKView *wkView); diff --git a/Source/WebKit2/UIProcess/API/mac/PDFViewController.mm b/Source/WebKit2/UIProcess/API/mac/PDFViewController.mm index 5c64000..436a08a 100644 --- a/Source/WebKit2/UIProcess/API/mac/PDFViewController.mm +++ b/Source/WebKit2/UIProcess/API/mac/PDFViewController.mm @@ -30,10 +30,12 @@ #import "WKAPICast.h" #import "WKView.h" #import "WebData.h" +#import "WebEventFactory.h" #import "WebPageGroup.h" #import "WebPageProxy.h" #import "WebPreferences.h" #import <PDFKit/PDFKit.h> +#import <WebCore/LocalizedStrings.h> #import <wtf/text/WTFString.h> // Redeclarations of PDFKit notifications. We can't use the API since we use a weak link to the framework. @@ -51,7 +53,51 @@ using namespace WebKit; @end extern "C" NSString *_NSPathForSystemFramework(NSString *framework); + +// MARK: C UTILITY FUNCTIONS + +static void _applicationInfoForMIMEType(NSString *type, NSString **name, NSImage **image) +{ + ASSERT(name); + ASSERT(image); + CFURLRef appURL = 0; + + OSStatus error = LSCopyApplicationForMIMEType((CFStringRef)type, kLSRolesAll, &appURL); + if (error != noErr) + return; + + NSString *appPath = [(NSURL *)appURL path]; + if (appURL) + CFRelease(appURL); + + *image = [[NSWorkspace sharedWorkspace] iconForFile:appPath]; + [*image setSize:NSMakeSize(16, 16)]; + + *name = [[NSFileManager defaultManager] displayNameAtPath:appPath]; +} + +// FIXME 4182876: We can eliminate this function in favor if -isEqual: if [PDFSelection isEqual:] is overridden +// to compare contents. +static BOOL _PDFSelectionsAreEqual(PDFSelection *selectionA, PDFSelection *selectionB) +{ + NSArray *aPages = [selectionA pages]; + NSArray *bPages = [selectionB pages]; + + if (![aPages isEqual:bPages]) + return NO; + + NSUInteger count = [aPages count]; + for (NSUInteger i = 0; i < count; ++i) { + NSRect aBounds = [selectionA boundsForPage:[aPages objectAtIndex:i]]; + NSRect bBounds = [selectionB boundsForPage:[bPages objectAtIndex:i]]; + if (!NSEqualRects(aBounds, bBounds)) + return NO; + } + + return YES; +} + @interface WKPDFView : NSView { PDFViewController* _pdfViewController; @@ -68,7 +114,7 @@ extern "C" NSString *_NSPathForSystemFramework(NSString *framework); - (void)setDocument:(PDFDocument *)pdfDocument; - (void)_applyPDFPreferences; - +- (PDFSelection *)_nextMatchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag fromSelection:(PDFSelection *)initialSelection startInSelection:(BOOL)startInSelection; @end @implementation WKPDFView @@ -159,6 +205,64 @@ extern "C" NSString *_NSPathForSystemFramework(NSString *framework); [self _updatePreferencesSoon]; } +- (void)_openWithFinder:(id)sender +{ + _pdfViewController->openPDFInFinder(); +} + +- (PDFSelection *)_nextMatchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag fromSelection:(PDFSelection *)initialSelection startInSelection:(BOOL)startInSelection +{ + if (![string length]) + return nil; + + int options = 0; + if (!forward) + options |= NSBackwardsSearch; + + if (!caseFlag) + options |= NSCaseInsensitiveSearch; + + PDFDocument *document = [_pdfView document]; + + PDFSelection *selectionForInitialSearch = [initialSelection copy]; + if (startInSelection) { + // Initially we want to include the selected text in the search. So we must modify the starting search + // selection to fit PDFDocument's search requirements: selection must have a length >= 1, begin before + // the current selection (if searching forwards) or after (if searching backwards). + int initialSelectionLength = [[initialSelection string] length]; + if (forward) { + [selectionForInitialSearch extendSelectionAtStart:1]; + [selectionForInitialSearch extendSelectionAtEnd:-initialSelectionLength]; + } else { + [selectionForInitialSearch extendSelectionAtEnd:1]; + [selectionForInitialSearch extendSelectionAtStart:-initialSelectionLength]; + } + } + PDFSelection *foundSelection = [document findString:string fromSelection:selectionForInitialSearch withOptions:options]; + [selectionForInitialSearch release]; + + // If we first searched in the selection, and we found the selection, search again from just past the selection + if (startInSelection && _PDFSelectionsAreEqual(foundSelection, initialSelection)) + foundSelection = [document findString:string fromSelection:initialSelection withOptions:options]; + + if (!foundSelection && wrapFlag) + foundSelection = [document findString:string fromSelection:nil withOptions:options]; + + return foundSelection; +} + +- (NSUInteger)_countMatches:(NSString *)string caseSensitive:(BOOL)caseFlag +{ + if (![string length]) + return 0; + + int options = caseFlag ? 0 : NSCaseInsensitiveSearch; + + return [[[_pdfView document] findString:string withOptions:options] count]; +} + +// MARK: NSView overrides + - (void)viewDidMoveToWindow { if (![self window]) @@ -181,7 +285,69 @@ extern "C" NSString *_NSPathForSystemFramework(NSString *framework); [notificationCenter removeObserver:self name:_webkit_PDFViewPageChangedNotification object:_pdfView]; } -// PDFView delegate methods +- (NSView *)hitTest:(NSPoint)point +{ + // Override hitTest so we can override menuForEvent. + NSEvent *event = [NSApp currentEvent]; + NSEventType type = [event type]; + if (type == NSRightMouseDown || (type == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask))) + return self; + + return [super hitTest:point]; +} + +- (NSMenu *)menuForEvent:(NSEvent *)theEvent +{ + NSMenu *menu = [[NSMenu alloc] initWithTitle:@""]; + + NSEnumerator *menuItemEnumerator = [[[_pdfView menuForEvent:theEvent] itemArray] objectEnumerator]; + while (NSMenuItem *item = [menuItemEnumerator nextObject]) { + NSMenuItem *itemCopy = [item copy]; + [menu addItem:itemCopy]; + [itemCopy release]; + + if ([item action] != @selector(copy:)) + continue; + + // Add in an "Open with <default PDF viewer>" item + NSString *appName = nil; + NSImage *appIcon = nil; + + _applicationInfoForMIMEType(@"application/pdf", &appName, &appIcon); + if (!appName) + appName = WEB_UI_STRING("Finder", "Default application name for Open With context menu"); + + // To match the PDFKit style, we'll add Open with Preview even when there's no document yet to view, and + // disable it using validateUserInterfaceItem. + NSString *title = [NSString stringWithFormat:WEB_UI_STRING("Open with %@", "context menu item for PDF"), appName]; + + item = [[NSMenuItem alloc] initWithTitle:title action:@selector(_openWithFinder:) keyEquivalent:@""]; + if (appIcon) + [item setImage:appIcon]; + [menu addItem:[NSMenuItem separatorItem]]; + [menu addItem:item]; + [item release]; + } + + return [menu autorelease]; +} + +// MARK: NSUserInterfaceValidations PROTOCOL IMPLEMENTATION + +- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item +{ + SEL action = [item action]; + if (action == @selector(_openWithFinder:)) + return [_pdfView document] != nil; + return YES; +} + +// MARK: PDFView delegate methods + +- (void)PDFViewWillClickOnLink:(PDFView *)sender withURL:(NSURL *)URL +{ + _pdfViewController->linkClicked([URL absoluteString]); +} - (void)PDFViewOpenPDFInNativeApplication:(PDFView *)sender { @@ -416,4 +582,55 @@ NSString *PDFViewController::pathToPDFOnDisk() return path; } +void PDFViewController::linkClicked(const String& url) +{ + NSEvent* nsEvent = [NSApp currentEvent]; + WebMouseEvent event; + switch ([nsEvent type]) { + case NSLeftMouseUp: + case NSRightMouseUp: + case NSOtherMouseUp: + event = WebEventFactory::createWebMouseEvent(nsEvent, m_pdfView); + default: + // For non mouse-clicks or for keyboard events, pass an empty WebMouseEvent + // through. The event is only used by the WebFrameLoaderClient to determine + // the modifier keys and which mouse button is down. These queries will be + // valid with an empty event. + break; + } + + page()->linkClicked(url, event); +} + +void PDFViewController::findString(const String& string, FindOptions options, unsigned maxMatchCount) +{ + BOOL forward = !(options & FindOptionsBackwards); + BOOL caseFlag = !(options & FindOptionsCaseInsensitive); + BOOL wrapFlag = options & FindOptionsWrapAround; + + PDFSelection *selection = [m_wkPDFView.get() _nextMatchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag fromSelection:[m_pdfView currentSelection] startInSelection:NO]; + NSUInteger matchCount = [m_wkPDFView.get() _countMatches:string caseSensitive:caseFlag]; + if (matchCount > maxMatchCount) + matchCount = maxMatchCount; + + if (!selection) { + page()->didFailToFindString(string); + return; + } + + [m_pdfView setCurrentSelection:selection]; + [m_pdfView scrollSelectionToVisible:nil]; + page()->didFindString(string, matchCount); +} + +void PDFViewController::countStringMatches(const String& string, FindOptions options, unsigned maxMatchCount) +{ + BOOL caseFlag = !(options & FindOptionsCaseInsensitive); + + NSUInteger matchCount = [m_wkPDFView.get() _countMatches:string caseSensitive:caseFlag]; + if (matchCount > maxMatchCount) + matchCount = maxMatchCount; + page()->didCountStringMatches(string, matchCount); +} + } // namespace WebKit diff --git a/Source/WebKit2/UIProcess/API/mac/PageClientImpl.h b/Source/WebKit2/UIProcess/API/mac/PageClientImpl.h index e217fc5..9e08a28 100644 --- a/Source/WebKit2/UIProcess/API/mac/PageClientImpl.h +++ b/Source/WebKit2/UIProcess/API/mac/PageClientImpl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010, 2011 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,8 +30,8 @@ #include "PageClient.h" #include <wtf/RetainPtr.h> +@class WKEditorUndoTargetObjC; @class WKView; -@class WebEditorUndoTargetObjC; namespace WebKit { @@ -63,16 +63,18 @@ private: virtual void processDidCrash(); virtual void pageClosed(); virtual void didRelaunchProcess(); - virtual void setFocus(bool focused); - virtual void takeFocus(bool direction); virtual void toolTipChanged(const String& oldToolTip, const String& newToolTip); virtual void setCursor(const WebCore::Cursor&); virtual void setViewportArguments(const WebCore::ViewportArguments&); virtual void registerEditCommand(PassRefPtr<WebEditCommandProxy>, WebPageProxy::UndoOrRedo); virtual void clearAllEditCommands(); - virtual void interceptKeyEvent(const NativeWebKeyboardEvent& event, Vector<WebCore::KeypressCommand>& commandName, uint32_t selectionStart, uint32_t selectionEnd, Vector<WebCore::CompositionUnderline>& underlines); + virtual bool canUndoRedo(WebPageProxy::UndoOrRedo); + virtual void executeUndoRedo(WebPageProxy::UndoOrRedo); + virtual bool interpretKeyEvent(const NativeWebKeyboardEvent&, Vector<WebCore::KeypressCommand>&); + virtual bool executeSavedCommandBySelector(const String& selector); virtual void setDragImage(const WebCore::IntPoint& clientPosition, PassRefPtr<ShareableBitmap> dragImage, bool isLinkDrag); + virtual void updateSecureInputState(); virtual WebCore::FloatRect convertToDeviceSpace(const WebCore::FloatRect&); virtual WebCore::FloatRect convertToUserSpace(const WebCore::FloatRect&); @@ -100,10 +102,13 @@ private: virtual double customRepresentationZoomFactor(); virtual void setCustomRepresentationZoomFactor(double); + virtual void findStringInCustomRepresentation(const String&, FindOptions, unsigned maxMatchCount); + virtual void countStringMatchesInCustomRepresentation(const String&, FindOptions, unsigned maxMatchCount); virtual void flashBackingStoreUpdates(const Vector<WebCore::IntRect>& updateRects); virtual void didPerformDictionaryLookup(const String&, double scaleFactor, const DictionaryPopupInfo&); + virtual void dismissDictionaryLookupPanel(); virtual void showCorrectionPanel(WebCore::CorrectionPanelInfo::PanelType, const WebCore::FloatRect& boundingBoxOfReplacedString, const String& replacedString, const String& replacementString, const Vector<String>& alternativeReplacementStrings); virtual void dismissCorrectionPanel(WebCore::ReasonForDismissingCorrectionPanel); @@ -113,7 +118,7 @@ private: virtual float userSpaceScaleFactor() const; WKView* m_wkView; - RetainPtr<WebEditorUndoTargetObjC> m_undoTarget; + RetainPtr<WKEditorUndoTargetObjC> m_undoTarget; #if !defined(BUILDING_ON_SNOW_LEOPARD) CorrectionPanel m_correctionPanel; #endif diff --git a/Source/WebKit2/UIProcess/API/mac/PageClientImpl.mm b/Source/WebKit2/UIProcess/API/mac/PageClientImpl.mm index 7a0d62d..e1888de 100644 --- a/Source/WebKit2/UIProcess/API/mac/PageClientImpl.mm +++ b/Source/WebKit2/UIProcess/API/mac/PageClientImpl.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010, 2011 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -52,20 +52,24 @@ @end using namespace WebCore; +using namespace WebKit; -@interface WebEditCommandObjC : NSObject +@interface WKEditCommandObjC : NSObject { - RefPtr<WebKit::WebEditCommandProxy> m_command; + RefPtr<WebEditCommandProxy> m_command; } +- (id)initWithWebEditCommandProxy:(PassRefPtr<WebEditCommandProxy>)command; +- (WebEditCommandProxy*)command; +@end -- (id)initWithWebEditCommandProxy:(PassRefPtr<WebKit::WebEditCommandProxy>)command; -- (WebKit::WebEditCommandProxy*)command; - +@interface WKEditorUndoTargetObjC : NSObject +- (void)undoEditing:(id)sender; +- (void)redoEditing:(id)sender; @end -@implementation WebEditCommandObjC +@implementation WKEditCommandObjC -- (id)initWithWebEditCommandProxy:(PassRefPtr<WebKit::WebEditCommandProxy>)command +- (id)initWithWebEditCommandProxy:(PassRefPtr<WebEditCommandProxy>)command { self = [super init]; if (!self) @@ -75,31 +79,24 @@ using namespace WebCore; return self; } -- (WebKit::WebEditCommandProxy*)command +- (WebEditCommandProxy*)command { return m_command.get(); } @end -@interface WebEditorUndoTargetObjC : NSObject - -- (void)undoEditing:(id)sender; -- (void)redoEditing:(id)sender; - -@end - -@implementation WebEditorUndoTargetObjC +@implementation WKEditorUndoTargetObjC - (void)undoEditing:(id)sender { - ASSERT([sender isKindOfClass:[WebEditCommandObjC class]]); + ASSERT([sender isKindOfClass:[WKEditCommandObjC class]]); [sender command]->unapply(); } - (void)redoEditing:(id)sender { - ASSERT([sender isKindOfClass:[WebEditCommandObjC class]]); + ASSERT([sender isKindOfClass:[WKEditCommandObjC class]]); [sender command]->reapply(); } @@ -119,7 +116,7 @@ PassOwnPtr<PageClientImpl> PageClientImpl::create(WKView* wkView) PageClientImpl::PageClientImpl(WKView* wkView) : m_wkView(wkView) - , m_undoTarget(AdoptNS, [[WebEditorUndoTargetObjC alloc] init]) + , m_undoTarget(AdoptNS, [[WKEditorUndoTargetObjC alloc] init]) { } @@ -157,7 +154,7 @@ IntSize PageClientImpl::viewSize() bool PageClientImpl::isViewWindowActive() { - return [[m_wkView window] isKeyWindow]; + return [[m_wkView window] isKeyWindow] || [NSApp keyWindow] == [m_wkView window]; } bool PageClientImpl::isViewFocused() @@ -170,6 +167,9 @@ bool PageClientImpl::isViewVisible() if (![m_wkView window]) return false; + if (![[m_wkView window] isVisible]) + return false; + if ([m_wkView isHiddenOrHasHiddenAncestor]) return false; @@ -196,20 +196,6 @@ void PageClientImpl::didRelaunchProcess() [m_wkView _didRelaunchProcess]; } -void PageClientImpl::setFocus(bool focused) -{ - if (focused) - [[m_wkView window] makeFirstResponder:m_wkView]; - else - // takeFocus in this context means take focus away from the WKView. - takeFocus(true); -} - -void PageClientImpl::takeFocus(bool direction) -{ - [m_wkView _takeFocus:direction]; -} - void PageClientImpl::toolTipChanged(const String& oldToolTip, const String& newToolTip) { [m_wkView _toolTipChangedFrom:nsStringFromWebCoreString(oldToolTip) to:nsStringFromWebCoreString(newToolTip)]; @@ -223,67 +209,19 @@ void PageClientImpl::setCursor(const WebCore::Cursor& cursor) void PageClientImpl::setViewportArguments(const WebCore::ViewportArguments&) { - -} - -static NSString* nameForEditAction(EditAction editAction) -{ - // FIXME: Use localized strings. - // FIXME: Move this to a platform independent location. - - switch (editAction) { - case EditActionUnspecified: return nil; - case EditActionSetColor: return @"Set Color"; - case EditActionSetBackgroundColor: return @"Set Background Color"; - case EditActionTurnOffKerning: return @"Turn Off Kerning"; - case EditActionTightenKerning: return @"Tighten Kerning"; - case EditActionLoosenKerning: return @"Loosen Kerning"; - case EditActionUseStandardKerning: return @"Use Standard Kerning"; - case EditActionTurnOffLigatures: return @"Turn Off Ligatures"; - case EditActionUseStandardLigatures: return @"Use Standard Ligatures"; - case EditActionUseAllLigatures: return @"Use All Ligatures"; - case EditActionRaiseBaseline: return @"Raise Baseline"; - case EditActionLowerBaseline: return @"Lower Baseline"; - case EditActionSetTraditionalCharacterShape: return @"Set Traditional Character Shape"; - case EditActionSetFont: return @"Set Font"; - case EditActionChangeAttributes: return @"Change Attributes"; - case EditActionAlignLeft: return @"Align Left"; - case EditActionAlignRight: return @"Align Right"; - case EditActionCenter: return @"Center"; - case EditActionJustify: return @"Justify"; - case EditActionSetWritingDirection: return @"Set Writing Direction"; - case EditActionSubscript: return @"Subscript"; - case EditActionSuperscript: return @"Superscript"; - case EditActionUnderline: return @"Underline"; - case EditActionOutline: return @"Outline"; - case EditActionUnscript: return @"Unscript"; - case EditActionDrag: return @"Drag"; - case EditActionCut: return @"Cut"; - case EditActionPaste: return @"Paste"; - case EditActionPasteFont: return @"Paste Font"; - case EditActionPasteRuler: return @"Paste Ruler"; - case EditActionTyping: return @"Typing"; - case EditActionCreateLink: return @"Create Link"; - case EditActionUnlink: return @"Unlink"; - case EditActionInsertList: return @"Insert List"; - case EditActionFormatBlock: return @"Formatting"; - case EditActionIndent: return @"Indent"; - case EditActionOutdent: return @"Outdent"; - } - return nil; } void PageClientImpl::registerEditCommand(PassRefPtr<WebEditCommandProxy> prpCommand, WebPageProxy::UndoOrRedo undoOrRedo) { RefPtr<WebEditCommandProxy> command = prpCommand; - RetainPtr<WebEditCommandObjC> commandObjC(AdoptNS, [[WebEditCommandObjC alloc] initWithWebEditCommandProxy:command]); - NSString *actionName = nameForEditAction(command->editAction()); + RetainPtr<WKEditCommandObjC> commandObjC(AdoptNS, [[WKEditCommandObjC alloc] initWithWebEditCommandProxy:command]); + String actionName = WebEditCommandProxy::nameForEditAction(command->editAction()); NSUndoManager *undoManager = [m_wkView undoManager]; [undoManager registerUndoWithTarget:m_undoTarget.get() selector:((undoOrRedo == WebPageProxy::Undo) ? @selector(undoEditing:) : @selector(redoEditing:)) object:commandObjC.get()]; - if (actionName) - [undoManager setActionName:actionName]; + if (!actionName.isEmpty()) + [undoManager setActionName:(NSString *)actionName]; } void PageClientImpl::clearAllEditCommands() @@ -291,10 +229,19 @@ void PageClientImpl::clearAllEditCommands() [[m_wkView undoManager] removeAllActionsWithTarget:m_undoTarget.get()]; } -void PageClientImpl::interceptKeyEvent(const NativeWebKeyboardEvent& event, Vector<WebCore::KeypressCommand>& commandsList, uint32_t selectionStart, uint32_t selectionEnd, Vector<WebCore::CompositionUnderline>& underlines) +bool PageClientImpl::canUndoRedo(WebPageProxy::UndoOrRedo undoOrRedo) { - commandsList = [m_wkView _interceptKeyEvent:event.nativeEvent()]; - [m_wkView _getTextInputState:selectionStart selectionEnd:selectionEnd underlines:underlines]; + return (undoOrRedo == WebPageProxy::Undo) ? [[m_wkView undoManager] canUndo] : [[m_wkView undoManager] canRedo]; +} + +void PageClientImpl::executeUndoRedo(WebPageProxy::UndoOrRedo undoOrRedo) +{ + return (undoOrRedo == WebPageProxy::Undo) ? [[m_wkView undoManager] undo] : [[m_wkView undoManager] redo]; +} + +bool PageClientImpl::interpretKeyEvent(const NativeWebKeyboardEvent& event, Vector<WebCore::KeypressCommand>& commands) +{ + return [m_wkView _interpretKeyEvent:event.nativeEvent() savingCommandsTo:commands]; } void PageClientImpl::setDragImage(const IntPoint& clientPosition, PassRefPtr<ShareableBitmap> dragImage, bool isLinkDrag) @@ -304,7 +251,12 @@ void PageClientImpl::setDragImage(const IntPoint& clientPosition, PassRefPtr<Sha [m_wkView _setDragImage:dragNSImage.get() at:clientPosition linkDrag:isLinkDrag]; } - + +void PageClientImpl::updateSecureInputState() +{ + [m_wkView _updateSecureInputState]; +} + FloatRect PageClientImpl::convertToDeviceSpace(const FloatRect& rect) { return [m_wkView _convertToDeviceSpace:rect]; @@ -408,6 +360,16 @@ void PageClientImpl::setCustomRepresentationZoomFactor(double zoomFactor) [m_wkView _setCustomRepresentationZoomFactor:zoomFactor]; } +void PageClientImpl::findStringInCustomRepresentation(const String& string, FindOptions options, unsigned maxMatchCount) +{ + [m_wkView _findStringInCustomRepresentation:string withFindOptions:options maxMatchCount:maxMatchCount]; +} + +void PageClientImpl::countStringMatchesInCustomRepresentation(const String& string, FindOptions options, unsigned maxMatchCount) +{ + [m_wkView _countStringMatchesInCustomRepresentation:string withFindOptions:options maxMatchCount:maxMatchCount]; +} + void PageClientImpl::flashBackingStoreUpdates(const Vector<IntRect>&) { notImplemented(); @@ -437,6 +399,13 @@ void PageClientImpl::didPerformDictionaryLookup(const String& text, double scale #endif } +void PageClientImpl::dismissDictionaryLookupPanel() +{ +#if !defined(BUILDING_ON_SNOW_LEOPARD) + WKHideWordDefinitionWindow(); +#endif +} + void PageClientImpl::showCorrectionPanel(CorrectionPanelInfo::PanelType type, const FloatRect& boundingBoxOfReplacedString, const String& replacedString, const String& replacementString, const Vector<String>& alternativeReplacementStrings) { #if !defined(BUILDING_ON_SNOW_LEOPARD) @@ -484,4 +453,9 @@ float PageClientImpl::userSpaceScaleFactor() const #endif } +bool PageClientImpl::executeSavedCommandBySelector(const String& selectorString) +{ + return [m_wkView _executeSavedCommandBySelector:NSSelectorFromString(selectorString)]; +} + } // namespace WebKit diff --git a/Source/WebKit2/UIProcess/API/mac/WKTextInputWindowController.mm b/Source/WebKit2/UIProcess/API/mac/WKTextInputWindowController.mm index 3b69a1d..2c9ceaa 100644 --- a/Source/WebKit2/UIProcess/API/mac/WKTextInputWindowController.mm +++ b/Source/WebKit2/UIProcess/API/mac/WKTextInputWindowController.mm @@ -86,7 +86,14 @@ BOOL hadMarkedText = [_inputTextView hasMarkedText]; *string = nil; - + + // Let TSM know that a bottom input window would be created for marked text. + EventRef carbonEvent = static_cast<EventRef>(const_cast<void*>([event eventRef])); + if (carbonEvent) { + Boolean ignorePAH = true; + SetEventParameter(carbonEvent, 'iPAH', typeBoolean, sizeof(ignorePAH), &ignorePAH); + } + if (![[_inputTextView inputContext] handleEvent:event]) return NO; diff --git a/Source/WebKit2/UIProcess/API/mac/WKView.h b/Source/WebKit2/UIProcess/API/mac/WKView.h index 8c1826c..098b3ca 100644 --- a/Source/WebKit2/UIProcess/API/mac/WKView.h +++ b/Source/WebKit2/UIProcess/API/mac/WKView.h @@ -29,7 +29,7 @@ @class WKViewData; WK_EXPORT -@interface WKView : NSView <NSTextInput> { +@interface WKView : NSView <NSTextInputClient> { WKViewData *_data; unsigned _frameSizeUpdatesDisabledCount; } diff --git a/Source/WebKit2/UIProcess/API/mac/WKView.mm b/Source/WebKit2/UIProcess/API/mac/WKView.mm index 05693ef..80345f7 100644 --- a/Source/WebKit2/UIProcess/API/mac/WKView.mm +++ b/Source/WebKit2/UIProcess/API/mac/WKView.mm @@ -26,14 +26,17 @@ #import "config.h" #import "WKView.h" +#import "AttributedString.h" #import "ChunkedUpdateDrawingAreaProxy.h" #import "DataReference.h" #import "DrawingAreaProxyImpl.h" +#import "EditorState.h" #import "FindIndicator.h" #import "FindIndicatorWindow.h" #import "LayerTreeContext.h" #import "Logging.h" #import "NativeWebKeyboardEvent.h" +#import "NativeWebMouseEvent.h" #import "PDFViewController.h" #import "PageClientImpl.h" #import "PasteboardTypes.h" @@ -69,24 +72,23 @@ #import <wtf/RefPtr.h> #import <wtf/RetainPtr.h> -@interface NSApplication (WebNSApplicationDetails) +@interface NSApplication (WKNSApplicationDetails) - (void)speakString:(NSString *)string; - (void)_setCurrentEvent:(NSEvent *)event; @end -@interface NSWindow (WebNSWindowDetails) +@interface NSObject (WKNSTextInputContextDetails) +- (BOOL)wantsToHandleMouseEvents; +- (BOOL)handleMouseEvent:(NSEvent *)event; +@end + +@interface NSWindow (WKNSWindowDetails) - (NSRect)_growBoxRect; - (id)_growBoxOwner; - (void)_setShowOpaqueGrowBoxForOwner:(id)owner; - (BOOL)_updateGrowBoxForWindowFrameChange; @end -extern "C" { - // Need to declare this attribute name because AppKit exports it but does not make it available in API or SPI headers. - // <rdar://problem/8631468> tracks the request to make it available. This code should be removed when the bug is closed. - extern NSString *NSTextInputReplacementRangeAttributeName; -} - using namespace WebKit; using namespace WebCore; @@ -98,6 +100,13 @@ typedef HashMap<String, ValidationVector> ValidationMap; } +struct WKViewInterpretKeyEventsParameters { + bool eventInterpretationHadSideEffects; + bool consumedByIM; + bool executingSavedKeypressCommands; + Vector<KeypressCommand>* commands; +}; + @interface WKViewData : NSObject { @public OwnPtr<PageClientImpl> _pageClient; @@ -122,18 +131,13 @@ typedef HashMap<String, ValidationVector> ValidationMap; // the application to distinguish the case of a new event from one // that has been already sent to WebCore. RetainPtr<NSEvent> _keyDownEventBeingResent; - bool _isInInterpretKeyEvents; - Vector<KeypressCommand> _commandsList; + WKViewInterpretKeyEventsParameters* _interpretKeyEventsParameters; NSSize _resizeScrollOffset; // The identifier of the plug-in we want to send complex text input to, or 0 if there is none. uint64_t _pluginComplexTextInputIdentifier; - Vector<CompositionUnderline> _underlines; - unsigned _selectionStart; - unsigned _selectionEnd; - bool _inBecomeFirstResponder; bool _inResignFirstResponder; NSEvent *_mouseDownEvent; @@ -150,15 +154,21 @@ typedef HashMap<String, ValidationVector> ValidationMap; BOOL _hasSpellCheckerDocumentTag; NSInteger _spellCheckerDocumentTag; + + BOOL _inSecureInputState; } @end -@implementation WKViewData +@interface WKResponderChainSink : NSResponder { + NSResponder *_lastResponderInChain; + bool _didReceiveUnhandledCommand; +} +- (id)initWithResponderChain:(NSResponder *)chain; +- (void)detach; +- (bool)didReceiveUnhandledCommand; @end -@interface NSObject (NSTextInputContextDetails) -- (BOOL)wantsToHandleMouseEvents; -- (BOOL)handleMouseEvent:(NSEvent *)event; +@implementation WKViewData @end @implementation WKView @@ -239,6 +249,8 @@ typedef HashMap<String, ValidationVector> ValidationMap; { _data->_page->close(); + ASSERT(!_data->_inSecureInputState); + [_data release]; _data = nil; @@ -282,7 +294,10 @@ typedef HashMap<String, ValidationVector> ValidationMap; NSSelectionDirection direction = [[self window] keyViewSelectionDirection]; _data->_inBecomeFirstResponder = true; + + [self _updateSecureInputState]; _data->_page->viewStateDidChange(WebPageProxy::ViewIsFocused); + _data->_inBecomeFirstResponder = false; if (direction != NSDirectSelection) @@ -294,7 +309,13 @@ typedef HashMap<String, ValidationVector> ValidationMap; - (BOOL)resignFirstResponder { _data->_inResignFirstResponder = true; + + if (_data->_inSecureInputState) { + DisableSecureEventInput(); + _data->_inSecureInputState = NO; + } _data->_page->viewStateDidChange(WebPageProxy::ViewIsFocused); + _data->_inResignFirstResponder = false; return YES; @@ -514,13 +535,13 @@ WEBCORE_COMMAND(yankAndSelect) - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType { - BOOL isValidSendType = !sendType || ([PasteboardTypes::forSelection() containsObject:sendType] && !_data->_page->selectionState().isNone); + BOOL isValidSendType = !sendType || ([PasteboardTypes::forSelection() containsObject:sendType] && !_data->_page->editorState().selectionIsNone); BOOL isValidReturnType = NO; if (!returnType) isValidReturnType = YES; - else if ([PasteboardTypes::forEditing() containsObject:returnType] && _data->_page->selectionState().isContentEditable) { + else if ([PasteboardTypes::forEditing() containsObject:returnType] && _data->_page->editorState().isContentEditable) { // We can insert strings in any editable context. We can insert other types, like images, only in rich edit contexts. - isValidReturnType = _data->_page->selectionState().isContentRichlyEditable || [returnType isEqualToString:NSStringPboardType]; + isValidReturnType = _data->_page->editorState().isContentRichlyEditable || [returnType isEqualToString:NSStringPboardType]; } if (isValidSendType && isValidReturnType) return self; @@ -604,12 +625,12 @@ static void validateCommandCallback(WKStringRef commandName, bool isEnabled, int if (action == @selector(showGuessPanel:)) { if (NSMenuItem *menuItem = ::menuItem(item)) - [menuItem setTitle:contextMenuItemTagShowSpellingPanel([[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible])]; - return _data->_page->selectionState().isContentEditable; + [menuItem setTitle:contextMenuItemTagShowSpellingPanel(![[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible])]; + return _data->_page->editorState().isContentEditable; } if (action == @selector(checkSpelling:) || action == @selector(changeSpelling:)) - return _data->_page->selectionState().isContentEditable; + return _data->_page->editorState().isContentEditable; if (action == @selector(toggleContinuousSpellChecking:)) { bool enabled = TextChecker::isContinuousSpellCheckingAllowed(); @@ -627,47 +648,47 @@ static void validateCommandCallback(WKStringRef commandName, bool isEnabled, int if (action == @selector(toggleAutomaticSpellingCorrection:)) { bool checked = TextChecker::state().isAutomaticSpellingCorrectionEnabled; [menuItem(item) setState:checked ? NSOnState : NSOffState]; - return _data->_page->selectionState().isContentEditable; + return _data->_page->editorState().isContentEditable; } if (action == @selector(orderFrontSubstitutionsPanel:)) { if (NSMenuItem *menuItem = ::menuItem(item)) - [menuItem setTitle:contextMenuItemTagShowSubstitutions([[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible])]; - return _data->_page->selectionState().isContentEditable; + [menuItem setTitle:contextMenuItemTagShowSubstitutions(![[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible])]; + return _data->_page->editorState().isContentEditable; } if (action == @selector(toggleSmartInsertDelete:)) { bool checked = _data->_page->isSmartInsertDeleteEnabled(); [menuItem(item) setState:checked ? NSOnState : NSOffState]; - return _data->_page->selectionState().isContentEditable; + return _data->_page->editorState().isContentEditable; } if (action == @selector(toggleAutomaticQuoteSubstitution:)) { bool checked = TextChecker::state().isAutomaticQuoteSubstitutionEnabled; [menuItem(item) setState:checked ? NSOnState : NSOffState]; - return _data->_page->selectionState().isContentEditable; + return _data->_page->editorState().isContentEditable; } if (action == @selector(toggleAutomaticDashSubstitution:)) { bool checked = TextChecker::state().isAutomaticDashSubstitutionEnabled; [menuItem(item) setState:checked ? NSOnState : NSOffState]; - return _data->_page->selectionState().isContentEditable; + return _data->_page->editorState().isContentEditable; } if (action == @selector(toggleAutomaticLinkDetection:)) { bool checked = TextChecker::state().isAutomaticLinkDetectionEnabled; [menuItem(item) setState:checked ? NSOnState : NSOffState]; - return _data->_page->selectionState().isContentEditable; + return _data->_page->editorState().isContentEditable; } if (action == @selector(toggleAutomaticTextReplacement:)) { bool checked = TextChecker::state().isAutomaticTextReplacementEnabled; [menuItem(item) setState:checked ? NSOnState : NSOffState]; - return _data->_page->selectionState().isContentEditable; + return _data->_page->editorState().isContentEditable; } if (action == @selector(uppercaseWord:) || action == @selector(lowercaseWord:) || action == @selector(capitalizeWord:)) - return _data->_page->selectionState().selectedRangeLength && _data->_page->selectionState().isContentEditable; + return _data->_page->editorState().selectionIsRange && _data->_page->editorState().isContentEditable; if (action == @selector(stopSpeaking:)) return [NSApp isSpeaking]; @@ -751,9 +772,6 @@ static void speakString(WKStringRef string, WKErrorRef error, void*) TextChecker::setContinuousSpellCheckingEnabled(spellCheckingEnabled); _data->_page->process()->updateTextCheckerState(); - - if (!spellCheckingEnabled) - _data->_page->unmarkAllMisspellings(); } - (BOOL)isGrammarCheckingEnabled @@ -768,9 +786,6 @@ static void speakString(WKStringRef string, WKErrorRef error, void*) TextChecker::setGrammarCheckingEnabled(flag); _data->_page->process()->updateTextCheckerState(); - - if (!flag) - _data->_page->unmarkAllBadGrammar(); } - (IBAction)toggleGrammarChecking:(id)sender @@ -779,9 +794,6 @@ static void speakString(WKStringRef string, WKErrorRef error, void*) TextChecker::setGrammarCheckingEnabled(grammarCheckingEnabled); _data->_page->process()->updateTextCheckerState(); - - if (!grammarCheckingEnabled) - _data->_page->unmarkAllBadGrammar(); } - (IBAction)toggleAutomaticSpellingCorrection:(id)sender @@ -927,6 +939,33 @@ static void speakString(WKStringRef string, WKErrorRef error, void*) _data->_mouseDownEvent = [event retain]; } +#define NATIVE_MOUSE_EVENT_HANDLER(Selector) \ + - (void)Selector:(NSEvent *)theEvent \ + { \ + if ([[self inputContext] handleEvent:theEvent]) { \ + LOG(TextInput, "%s was handled by text input context", String(#Selector).substring(0, String(#Selector).find("Internal")).ascii().data()); \ + return; \ + } \ + NativeWebMouseEvent webEvent(theEvent, self); \ + _data->_page->handleMouseEvent(webEvent); \ + } + +NATIVE_MOUSE_EVENT_HANDLER(mouseEntered) +NATIVE_MOUSE_EVENT_HANDLER(mouseExited) +NATIVE_MOUSE_EVENT_HANDLER(mouseMovedInternal) +NATIVE_MOUSE_EVENT_HANDLER(mouseDownInternal) +NATIVE_MOUSE_EVENT_HANDLER(mouseUpInternal) +NATIVE_MOUSE_EVENT_HANDLER(mouseDraggedInternal) +NATIVE_MOUSE_EVENT_HANDLER(otherMouseDown) +NATIVE_MOUSE_EVENT_HANDLER(otherMouseDragged) +NATIVE_MOUSE_EVENT_HANDLER(otherMouseMoved) +NATIVE_MOUSE_EVENT_HANDLER(otherMouseUp) +NATIVE_MOUSE_EVENT_HANDLER(rightMouseDown) +NATIVE_MOUSE_EVENT_HANDLER(rightMouseDragged) +NATIVE_MOUSE_EVENT_HANDLER(rightMouseUp) + +#undef NATIVE_MOUSE_EVENT_HANDLER + #define EVENT_HANDLER(Selector, Type) \ - (void)Selector:(NSEvent *)theEvent \ { \ @@ -934,28 +973,17 @@ static void speakString(WKStringRef string, WKErrorRef error, void*) _data->_page->handle##Type##Event(webEvent); \ } -EVENT_HANDLER(mouseEntered, Mouse) -EVENT_HANDLER(mouseExited, Mouse) -EVENT_HANDLER(mouseMoved, Mouse) -EVENT_HANDLER(otherMouseDown, Mouse) -EVENT_HANDLER(otherMouseDragged, Mouse) -EVENT_HANDLER(otherMouseMoved, Mouse) -EVENT_HANDLER(otherMouseUp, Mouse) -EVENT_HANDLER(rightMouseDown, Mouse) -EVENT_HANDLER(rightMouseDragged, Mouse) -EVENT_HANDLER(rightMouseMoved, Mouse) -EVENT_HANDLER(rightMouseUp, Mouse) EVENT_HANDLER(scrollWheel, Wheel) #undef EVENT_HANDLER -- (void)_mouseHandler:(NSEvent *)event +- (void)mouseMoved:(NSEvent *)event { - NSInputManager *currentInputManager = [NSInputManager currentInputManager]; - if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event]) + // When a view is first responder, it gets mouse moved events even when the mouse is outside its visible rect. + if (self == [[self window] firstResponder] && !NSPointInRect([self convertPoint:[event locationInWindow] fromView:nil], [self visibleRect])) return; - WebMouseEvent webEvent = WebEventFactory::createWebMouseEvent(event, self); - _data->_page->handleMouseEvent(webEvent); + + [self mouseMovedInternal:event]; } - (void)mouseDown:(NSEvent *)event @@ -963,20 +991,57 @@ EVENT_HANDLER(scrollWheel, Wheel) [self _setMouseDownEvent:event]; _data->_ignoringMouseDraggedEvents = NO; _data->_dragHasStarted = NO; - [self _mouseHandler:event]; + [self mouseDownInternal:event]; } - (void)mouseUp:(NSEvent *)event { [self _setMouseDownEvent:nil]; - [self _mouseHandler:event]; + [self mouseUpInternal:event]; } - (void)mouseDragged:(NSEvent *)event { if (_data->_ignoringMouseDraggedEvents) return; - [self _mouseHandler:event]; + [self mouseDraggedInternal:event]; +} + +- (BOOL)acceptsFirstMouse:(NSEvent *)event +{ + // There's a chance that responding to this event will run a nested event loop, and + // fetching a new event might release the old one. Retaining and then autoreleasing + // the current event prevents that from causing a problem inside WebKit or AppKit code. + [[event retain] autorelease]; + + if (![self hitTest:[event locationInWindow]]) + return NO; + + [self _setMouseDownEvent:event]; + bool result = _data->_page->acceptsFirstMouse([event eventNumber], WebEventFactory::createWebMouseEvent(event, self)); + [self _setMouseDownEvent:nil]; + return result; +} + +- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event +{ + // If this is the active window or we don't have a range selection, there is no need to perform additional checks + // and we can avoid making a synchronous call to the WebProcess. + if ([[self window] isKeyWindow] || _data->_page->editorState().selectionIsNone || !_data->_page->editorState().selectionIsRange) + return NO; + + // There's a chance that responding to this event will run a nested event loop, and + // fetching a new event might release the old one. Retaining and then autoreleasing + // the current event prevents that from causing a problem inside WebKit or AppKit code. + [[event retain] autorelease]; + + if (![self hitTest:[event locationInWindow]]) + return NO; + + [self _setMouseDownEvent:event]; + bool result = _data->_page->shouldDelayWindowOrderingForEvent(WebEventFactory::createWebMouseEvent(event, self)); + [self _setMouseDownEvent:nil]; + return result; } #if ENABLE(GESTURE_EVENTS) @@ -1018,52 +1083,75 @@ static const short kIOHIDEventTypeScroll = 6; { LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector)); - if (!_data->_isInInterpretKeyEvents) { + WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; + if (parameters) + parameters->consumedByIM = false; + + // As in insertText:replacementRange:, we assume that the call comes from an input method if there is marked text. + bool isFromInputMethod = _data->_page->editorState().hasComposition; + + if (parameters && !isFromInputMethod) + parameters->commands->append(KeypressCommand(NSStringFromSelector(selector))); + else { + // FIXME: Send the command to Editor synchronously and only send it along the + // responder chain if it's a selector that does not correspond to an editing command. [super doCommandBySelector:selector]; - return; } - if (selector != @selector(noop:)) - _data->_commandsList.append(KeypressCommand(commandNameForSelector(selector))); } - (void)insertText:(id)string { - BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString - - LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string); + // Unlike and NSTextInputClient variant with replacementRange, this NSResponder method is called when there is no input context, + // so text input processing isn't performed. We are not going to actually insert any text in that case, but saving an insertText + // command ensures that a keypress event is dispatched as appropriate. + [self insertText:string replacementRange:NSMakeRange(NSNotFound, 0)]; +} + +- (void)insertText:(id)string replacementRange:(NSRange)replacementRange +{ + BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; + ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]); + + if (replacementRange.location != NSNotFound) + LOG(TextInput, "insertText:\"%@\" replacementRange:(%u, %u)", isAttributedString ? [string string] : string, replacementRange.location, replacementRange.length); + else + LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string); + WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; + if (parameters) + parameters->consumedByIM = false; + NSString *text; - bool isFromInputMethod = _data->_page->selectionState().hasComposition; + bool isFromInputMethod = _data->_page->editorState().hasComposition; if (isAttributedString) { + // FIXME: We ignore most attributes from the string, so for example inserting from Character Palette loses font and glyph variation data. text = [string string]; - // We deal with the NSTextInputReplacementRangeAttributeName attribute from NSAttributedString here - // simply because it is used by at least one Input Method -- it corresonds to the kEventParamTextInputSendReplaceRange - // event in TSM. This behaviour matches that of -[WebHTMLView setMarkedText:selectedRange:] when it receives an - // NSAttributedString - NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:NULL inRange:NSMakeRange(0, [text length])]; - LOG(TextInput, "ReplacementRange: %@", rangeString); - if (rangeString) - isFromInputMethod = YES; } else text = string; + // insertText can be called for several reasons: + // - If it's from normal key event processing (including key bindings), we may need to save the action to perform it later. + // - If it's from an input method, then we should go ahead and insert the text now. We assume it's from the input method if we have marked text. + // FIXME: In theory, this could be wrong for some input methods, so we should try to find another way to determine if the call is from the input method. + // - If it's sent outside of keyboard event processing (e.g. from Character Viewer, or when confirming an inline input area with a mouse), + // then we also execute it immediately, as there will be no other chance. + if (parameters && !isFromInputMethod) { + ASSERT(replacementRange.location == NSNotFound); + parameters->commands->append(KeypressCommand("insertText:", text)); + return; + } + String eventText = text; - - // We'd need a different code path here if we wanted to be able to handle this - // outside of interpretKeyEvents. - ASSERT(_data->_isInInterpretKeyEvents); + eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore + bool eventHandled = _data->_page->insertText(eventText, replacementRange.location, NSMaxRange(replacementRange)); - if (!isFromInputMethod) - _data->_commandsList.append(KeypressCommand("insertText", text)); - else { - eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore - _data->_commandsList.append(KeypressCommand("insertText", eventText)); - } + if (parameters) + parameters->eventInterpretationHadSideEffects |= eventHandled; } - (BOOL)_handleStyleKeyEquivalent:(NSEvent *)event { - if (!_data->_page->selectionState().isContentEditable) + if (!_data->_page->editorState().isContentEditable) return NO; if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) != NSCommandKeyMask) @@ -1130,9 +1218,6 @@ static const short kIOHIDEventTypeScroll = 6; } } - _data->_underlines.clear(); - _data->_selectionStart = 0; - _data->_selectionEnd = 0; // We could be receiving a key down from AppKit if we have re-sent an event // that maps to an action that is currently unavailable (for example a copy when // there is no range selection). @@ -1144,37 +1229,108 @@ static const short kIOHIDEventTypeScroll = 6; _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, self)); } -- (NSTextInputContext *)inputContext { - if (_data->_pluginComplexTextInputIdentifier && !_data->_isInInterpretKeyEvents) +- (void)flagsChanged:(NSEvent *)theEvent +{ + // There's a chance that responding to this event will run a nested event loop, and + // fetching a new event might release the old one. Retaining and then autoreleasing + // the current event prevents that from causing a problem inside WebKit or AppKit code. + [[theEvent retain] autorelease]; + + unsigned short keyCode = [theEvent keyCode]; + + // Don't make an event from the num lock and function keys + if (!keyCode || keyCode == 10 || keyCode == 63) + return; + + _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, self)); +} + +- (void)_executeSavedKeypressCommands +{ + WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; + if (!parameters || parameters->commands->isEmpty()) + return; + + // We could be called again if the execution of one command triggers a call to selectedRange. + // In this case, the state is up to date, and we don't need to execute any more saved commands to return a result. + if (parameters->executingSavedKeypressCommands) + return; + + parameters->executingSavedKeypressCommands = true; + parameters->eventInterpretationHadSideEffects |= _data->_page->executeKeypressCommands(*parameters->commands); + parameters->commands->clear(); + parameters->executingSavedKeypressCommands = false; +} + +- (NSTextInputContext *)inputContext +{ + WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; + + if (_data->_pluginComplexTextInputIdentifier && !parameters) return [[WKTextInputWindowController sharedTextInputWindowController] inputContext]; + // Disable text input machinery when in non-editable content. An invisible inline input area affects performance, and can prevent Expose from working. + if (!_data->_page->editorState().isContentEditable) + return nil; + return [super inputContext]; } - (NSRange)selectedRange { - if (_data->_page->selectionState().isNone || !_data->_page->selectionState().isContentEditable) - return NSMakeRange(NSNotFound, 0); - - LOG(TextInput, "selectedRange -> (%u, %u)", _data->_page->selectionState().selectedRangeStart, _data->_page->selectionState().selectedRangeLength); - return NSMakeRange(_data->_page->selectionState().selectedRangeStart, _data->_page->selectionState().selectedRangeLength); + [self _executeSavedKeypressCommands]; + + uint64_t selectionStart; + uint64_t selectionLength; + _data->_page->getSelectedRange(selectionStart, selectionLength); + + NSRange result = NSMakeRange(selectionStart, selectionLength); + if (result.location == NSNotFound) + LOG(TextInput, "selectedRange -> (NSNotFound, %u)", result.length); + else + LOG(TextInput, "selectedRange -> (%u, %u)", result.location, result.length); + + return result; } - (BOOL)hasMarkedText { - LOG(TextInput, "hasMarkedText -> %u", _data->_page->selectionState().hasComposition); - return _data->_page->selectionState().hasComposition; + WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; + + BOOL result; + if (parameters) { + result = _data->_page->editorState().hasComposition; + if (result) { + // A saved command can confirm a composition, but it cannot start a new one. + [self _executeSavedKeypressCommands]; + result = _data->_page->editorState().hasComposition; + } + } else { + uint64_t location; + uint64_t length; + _data->_page->getMarkedRange(location, length); + result = location != NSNotFound; + } + + LOG(TextInput, "hasMarkedText -> %u", result); + return result; } - (void)unmarkText { + [self _executeSavedKeypressCommands]; + LOG(TextInput, "unmarkText"); - // We'd need a different code path here if we wanted to be able to handle this - // outside of interpretKeyEvents. - ASSERT(_data->_isInInterpretKeyEvents); + // Use pointer to get parameters passed to us by the caller of interpretKeyEvents. + WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; + + if (parameters) { + parameters->eventInterpretationHadSideEffects = true; + parameters->consumedByIM = false; + } - _data->_commandsList.append(KeypressCommand("unmarkText")); + _data->_page->confirmComposition(); } - (NSArray *)validAttributesForMarkedText @@ -1183,7 +1339,7 @@ static const short kIOHIDEventTypeScroll = 6; if (!validAttributes) { validAttributes = [[NSArray alloc] initWithObjects: NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName, - NSMarkedClauseSegmentAttributeName, NSTextInputReplacementRangeAttributeName, nil]; + NSMarkedClauseSegmentAttributeName, nil]; // NSText also supports the following attributes, but it's // hard to tell which are really required for text input to // work well; I have not seen any input method make use of them yet. @@ -1215,47 +1371,86 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde } } -- (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange +- (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange replacementRange:(NSRange)replacementRange { - BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString - + [self _executeSavedKeypressCommands]; + + BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; + ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]); + LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u)", isAttributedString ? [string string] : string, newSelRange.location, newSelRange.length); + + // Use pointer to get parameters passed to us by the caller of interpretKeyEvents. + WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; + + if (parameters) { + parameters->eventInterpretationHadSideEffects = true; + parameters->consumedByIM = false; + } - NSString *text = string; - + Vector<CompositionUnderline> underlines; + NSString *text; + if (isAttributedString) { + // FIXME: We ignore most attributes from the string, so an input method cannot specify e.g. a font or a glyph variation. text = [string string]; - extractUnderlines(string, _data->_underlines); + extractUnderlines(string, underlines); + } else + text = string; + + if (_data->_page->editorState().isInPasswordField) { + // In password fields, we only allow ASCII dead keys, and don't allow inline input, matching NSSecureTextInputField. + // Allowing ASCII dead keys is necessary to enable full Roman input when using a Vietnamese keyboard. + ASSERT(!_data->_page->editorState().hasComposition); + [[super inputContext] discardMarkedText]; // Inform the input method that we won't have an inline input area despite having been asked to. + if ([text length] == 1 && [[text decomposedStringWithCanonicalMapping] characterAtIndex:0] < 0x80) { + _data->_page->insertText(text, replacementRange.location, NSMaxRange(replacementRange)); + } else + NSBeep(); + return; } - - // We'd need a different code path here if we wanted to be able to handle this - // outside of interpretKeyEvents. - ASSERT(_data->_isInInterpretKeyEvents); - _data->_commandsList.append(KeypressCommand("setMarkedText", text)); - _data->_selectionStart = newSelRange.location; - _data->_selectionEnd = NSMaxRange(newSelRange); + _data->_page->setComposition(text, underlines, newSelRange.location, NSMaxRange(newSelRange), replacementRange.location, NSMaxRange(replacementRange)); } - (NSRange)markedRange { + [self _executeSavedKeypressCommands]; + uint64_t location; uint64_t length; - _data->_page->getMarkedRange(location, length); + LOG(TextInput, "markedRange -> (%u, %u)", location, length); return NSMakeRange(location, length); } -- (NSAttributedString *)attributedSubstringFromRange:(NSRange)nsRange +- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)nsRange actualRange:(NSRangePointer)actualRange { - // This is not implemented for now. Need to figure out how to serialize the attributed string across processes. - LOG(TextInput, "attributedSubstringFromRange"); - return nil; + [self _executeSavedKeypressCommands]; + + if (!_data->_page->editorState().isContentEditable) { + LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length); + return nil; + } + + if (_data->_page->editorState().isInPasswordField) + return nil; + + AttributedString result; + _data->_page->getAttributedSubstringFromRange(nsRange.location, NSMaxRange(nsRange), result); + + if (actualRange) + *actualRange = nsRange; + + LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> \"%@\"", nsRange.location, nsRange.length, [result.string.get() string]); + return [[result.string.get() retain] autorelease]; } - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint { + [self _executeSavedKeypressCommands]; + NSWindow *window = [self window]; if (window) @@ -1267,8 +1462,10 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde return result; } -- (NSRect)firstRectForCharacterRange:(NSRange)theRange +- (NSRect)firstRectForCharacterRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange { + [self _executeSavedKeypressCommands]; + // Just to match NSTextView's behavior. Regression tests cannot detect this; // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682 // (type something; try ranges (1, -1) and (2, -1). @@ -1281,7 +1478,10 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde NSWindow *window = [self window]; if (window) resultRect.origin = [window convertBaseToScreen:resultRect.origin]; - + + if (actualRange) + *actualRange = theRange; + LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (%f, %f, %f, %f)", theRange.location, theRange.length, resultRect.origin.x, resultRect.origin.y, resultRect.size.width, resultRect.size.height); return resultRect; } @@ -1318,7 +1518,7 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]); _data->_page->resetDragOperation(); - _data->_page->performDragControllerAction(DragControllerActionEntered, &dragData, [[draggingInfo draggingPasteboard] name]); + _data->_page->dragEntered(&dragData, [[draggingInfo draggingPasteboard] name]); return NSDragOperationCopy; } @@ -1327,7 +1527,7 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]); IntPoint global(globalPoint([draggingInfo draggingLocation], [self window])); DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]); - _data->_page->performDragControllerAction(DragControllerActionUpdated, &dragData, [[draggingInfo draggingPasteboard] name]); + _data->_page->dragUpdated(&dragData, [[draggingInfo draggingPasteboard] name]); return _data->_page->dragOperation(); } @@ -1336,7 +1536,7 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]); IntPoint global(globalPoint([draggingInfo draggingLocation], [self window])); DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]); - _data->_page->performDragControllerAction(DragControllerActionExited, &dragData, [[draggingInfo draggingPasteboard] name]); + _data->_page->dragExited(&dragData, [[draggingInfo draggingPasteboard] name]); _data->_page->resetDragOperation(); } @@ -1345,15 +1545,53 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde return YES; } +// FIXME: This code is more or less copied from Pasteboard::getBestURL. +// It would be nice to be able to share the code somehow. +static void maybeCreateSandboxExtensionFromPasteboard(NSPasteboard *pasteboard, SandboxExtension::Handle& sandboxExtensionHandle) +{ + NSArray *types = [pasteboard types]; + if (![types containsObject:NSFilenamesPboardType]) + return; + + NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType]; + if ([files count] != 1) + return; + + NSString *file = [files objectAtIndex:0]; + BOOL isDirectory; + if (![[NSFileManager defaultManager] fileExistsAtPath:file isDirectory:&isDirectory]) + return; + + if (isDirectory) + return; + + SandboxExtension::createHandle("/", SandboxExtension::ReadOnly, sandboxExtensionHandle); +} + - (BOOL)performDragOperation:(id <NSDraggingInfo>)draggingInfo { IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]); IntPoint global(globalPoint([draggingInfo draggingLocation], [self window])); DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]); - _data->_page->performDragControllerAction(DragControllerActionPerformDrag, &dragData, [[draggingInfo draggingPasteboard] name]); + + SandboxExtension::Handle sandboxExtensionHandle; + maybeCreateSandboxExtensionFromPasteboard([draggingInfo draggingPasteboard], sandboxExtensionHandle); + + _data->_page->performDrag(&dragData, [[draggingInfo draggingPasteboard] name], sandboxExtensionHandle); + return YES; } +// This code is needed to support drag and drop when the drag types cannot be matched. +// This is the case for elements that do not place content +// in the drag pasteboard automatically when the drag start (i.e. dragging a DIV element). +- (NSView *)_hitTest:(NSPoint *)point dragTypes:(NSSet *)types +{ + if ([[self superview] mouse:*point inRect:[self frame]]) + return self; + return nil; +} + - (void)_updateWindowVisibility { _data->_page->updateWindowIsVisible(![[self window] isMiniaturized]); @@ -1423,6 +1661,10 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde name:NSWindowDidMoveNotification object:window]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowFrameDidChange:) name:NSWindowDidResizeNotification object:window]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidOrderOffScreen:) + name:@"NSWindowDidOrderOffScreenNotification" object:window]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidOrderOnScreen:) + name:@"_NSWindowDidBecomeVisible" object:window]; } } @@ -1438,6 +1680,8 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window]; [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidMoveNotification object:window]; [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResizeNotification object:window]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:@"NSWindowDidOrderOffScreenNotification" object:window]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:@"_NSWindowDidBecomeVisible" object:window]; } - (void)viewWillMoveToWindow:(NSWindow *)window @@ -1492,15 +1736,19 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde - (void)_windowDidBecomeKey:(NSNotification *)notification { NSWindow *keyWindow = [notification object]; - if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet]) + if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet]) { + [self _updateSecureInputState]; _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive); + } } - (void)_windowDidResignKey:(NSNotification *)notification { NSWindow *formerKeyWindow = [notification object]; - if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) + if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) { + [self _updateSecureInputState]; _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive); + } } - (void)_windowDidMiniaturize:(NSNotification *)notification @@ -1518,6 +1766,16 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde [self _updateWindowAndViewFrames]; } +- (void)_windowDidOrderOffScreen:(NSNotification *)notification +{ + _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible); +} + +- (void)_windowDidOrderOnScreen:(NSNotification *)notification +{ + _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible); +} + static void drawPageBackground(CGContextRef context, WebPageProxy* page, const IntRect& rect) { if (!page->drawsBackground()) @@ -1667,6 +1925,7 @@ static void drawPageBackground(CGContextRef context, WebPageProxy* page, const I // NSPrintOperation takes ownership of the view. NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView:printingView.get()]; [printOperation setCanSpawnSeparateThread:YES]; + [printOperation setJobTitle:toImpl(frameRef)->title()]; printingView->_printOperation = printOperation; return printOperation; } @@ -1706,14 +1965,6 @@ static void drawPageBackground(CGContextRef context, WebPageProxy* page, const I [self setNeedsDisplay:YES]; } -- (void)_takeFocus:(BOOL)forward -{ - if (forward) - [[self window] selectKeyViewFollowingView:self]; - else - [[self window] selectKeyViewPrecedingView:self]; -} - - (void)_setCursor:(NSCursor *)cursor { if ([NSCursor currentCursor] == cursor) @@ -1744,27 +1995,34 @@ static void drawPageBackground(CGContextRef context, WebPageProxy* page, const I _data->_keyDownEventBeingResent = nullptr; } -- (Vector<KeypressCommand>&)_interceptKeyEvent:(NSEvent *)theEvent +- (BOOL)_interpretKeyEvent:(NSEvent *)event savingCommandsTo:(Vector<WebCore::KeypressCommand>&)commands { - ASSERT(!_data->_isInInterpretKeyEvents); + ASSERT(!_data->_interpretKeyEventsParameters); + ASSERT(commands.isEmpty()); - _data->_isInInterpretKeyEvents = true; - _data->_commandsList.clear(); + if ([event type] == NSFlagsChanged) + return NO; - // Calling interpretKeyEvents will trigger one or more calls to doCommandBySelector and insertText - // that will populate the commandsList vector. - [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; + WKViewInterpretKeyEventsParameters parameters; + parameters.eventInterpretationHadSideEffects = false; + parameters.executingSavedKeypressCommands = false; + // We assume that an input method has consumed the event, and only change this assumption if one of the NSTextInput methods is called. + // We assume the IM will *not* consume hotkey sequences. + parameters.consumedByIM = !([event modifierFlags] & NSCommandKeyMask); + parameters.commands = &commands; + _data->_interpretKeyEventsParameters = ¶meters; - _data->_isInInterpretKeyEvents = false; + [self interpretKeyEvents:[NSArray arrayWithObject:event]]; - return _data->_commandsList; -} + _data->_interpretKeyEventsParameters = 0; -- (void)_getTextInputState:(unsigned)start selectionEnd:(unsigned)end underlines:(Vector<CompositionUnderline>&)lines -{ - start = _data->_selectionStart; - end = _data->_selectionEnd; - lines = _data->_underlines; + // An input method may consume an event and not tell us (e.g. when displaying a candidate window), + // in which case we should not bubble the event up the DOM. + if (parameters.consumedByIM) + return YES; + + // If we have already executed all or some of the commands, the event is "handled". Note that there are additional checks on web process side. + return parameters.eventInterpretationHadSideEffects; } - (NSRect)_convertToDeviceSpace:(NSRect)rect @@ -2010,6 +2268,22 @@ static void drawPageBackground(CGContextRef context, WebPageProxy* page, const I _data->_pdfViewController->setZoomFactor(zoomFactor); } +- (void)_findStringInCustomRepresentation:(NSString *)string withFindOptions:(WebKit::FindOptions)options maxMatchCount:(NSUInteger)count +{ + if (!_data->_pdfViewController) + return; + + _data->_pdfViewController->findString(string, options, count); +} + +- (void)_countStringMatchesInCustomRepresentation:(NSString *)string withFindOptions:(WebKit::FindOptions)options maxMatchCount:(NSUInteger)count +{ + if (!_data->_pdfViewController) + return; + + _data->_pdfViewController->countStringMatches(string, options, count); +} + - (void)_setDragImage:(NSImage *)image at:(NSPoint)clientPoint linkDrag:(BOOL)linkDrag { // We need to prevent re-entering this call to avoid crashing in AppKit. @@ -2028,6 +2302,32 @@ static void drawPageBackground(CGContextRef context, WebPageProxy* page, const I _data->_dragHasStarted = NO; } +- (void)_updateSecureInputState +{ + if (![[self window] isKeyWindow] || ([[self window] firstResponder] != self && !_data->_inBecomeFirstResponder)) { + if (_data->_inSecureInputState) { + DisableSecureEventInput(); + _data->_inSecureInputState = NO; + } + return; + } + // WKView has a single input context for all editable areas (except for plug-ins). + NSTextInputContext *context = [super inputContext]; + bool isInPasswordField = _data->_page->editorState().isInPasswordField; + + if (isInPasswordField) { + if (!_data->_inSecureInputState) + EnableSecureEventInput(); + static NSArray *romanInputSources = [[NSArray alloc] initWithObjects:&NSAllRomanInputSourcesLocaleIdentifier count:1]; + [context setAllowedInputSourceLocales:romanInputSources]; + } else { + if (_data->_inSecureInputState) + DisableSecureEventInput(); + [context setAllowedInputSourceLocales:nil]; + } + _data->_inSecureInputState = isInPasswordField; +} + - (void)_setDrawingAreaSize:(NSSize)size { if (!_data->_page->drawingArea()) @@ -2053,6 +2353,16 @@ static void drawPageBackground(CGContextRef context, WebPageProxy* page, const I } #endif +- (bool)_executeSavedCommandBySelector:(SEL)selector +{ + // The sink does two things: 1) Tells us if the responder went unhandled, and + // 2) prevents any NSBeep; we don't ever want to beep here. + RetainPtr<WKResponderChainSink> sink(AdoptNS, [[WKResponderChainSink alloc] initWithResponderChain:self]); + [super doCommandBySelector:selector]; + [sink.get() detach]; + return ![sink.get() didReceiveUnhandledCommand]; +} + @end @implementation WKView (Private) @@ -2101,3 +2411,45 @@ static void drawPageBackground(CGContextRef context, WebPageProxy* page, const I @end +@implementation WKResponderChainSink + +- (id)initWithResponderChain:(NSResponder *)chain +{ + self = [super init]; + if (!self) + return nil; + _lastResponderInChain = chain; + while (NSResponder *next = [_lastResponderInChain nextResponder]) + _lastResponderInChain = next; + [_lastResponderInChain setNextResponder:self]; + return self; +} + +- (void)detach +{ + [_lastResponderInChain setNextResponder:nil]; + _lastResponderInChain = nil; +} + +- (bool)didReceiveUnhandledCommand +{ + return _didReceiveUnhandledCommand; +} + +- (void)noResponderFor:(SEL)selector +{ + _didReceiveUnhandledCommand = true; +} + +- (void)doCommandBySelector:(SEL)selector +{ + _didReceiveUnhandledCommand = true; +} + +- (BOOL)tryToPerform:(SEL)action with:(id)object +{ + _didReceiveUnhandledCommand = true; + return YES; +} + +@end diff --git a/Source/WebKit2/UIProcess/API/mac/WKViewInternal.h b/Source/WebKit2/UIProcess/API/mac/WKViewInternal.h index e4a40f7..82acdcf 100644 --- a/Source/WebKit2/UIProcess/API/mac/WKViewInternal.h +++ b/Source/WebKit2/UIProcess/API/mac/WKViewInternal.h @@ -24,22 +24,25 @@ */ #import "WKView.h" -#import <WebCore/Editor.h> -#import <WebCore/KeyboardEvent.h> +#import "WebFindOptions.h" +#import <wtf/Forward.h> +#import <wtf/Vector.h> namespace CoreIPC { class DataReference; } +namespace WebCore { + struct KeypressCommand; +} + namespace WebKit { class DrawingAreaProxy; class FindIndicator; class LayerTreeContext; } -#if ENABLE(FULLSCREEN_API) @class WKFullScreenWindowController; -#endif @interface WKView (Internal) - (PassOwnPtr<WebKit::DrawingAreaProxy>)_createDrawingAreaProxy; @@ -47,13 +50,12 @@ namespace WebKit { - (void)_processDidCrash; - (void)_pageClosed; - (void)_didRelaunchProcess; -- (void)_takeFocus:(BOOL)direction; - (void)_toolTipChangedFrom:(NSString *)oldToolTip to:(NSString *)newToolTip; - (void)_setCursor:(NSCursor *)cursor; - (void)_setUserInterfaceItemState:(NSString *)commandName enabled:(BOOL)isEnabled state:(int)newState; -- (Vector<WebCore::KeypressCommand>&)_interceptKeyEvent:(NSEvent *)theEvent; -- (void)_getTextInputState:(unsigned)start selectionEnd:(unsigned)end underlines:(Vector<WebCore::CompositionUnderline>&)lines; +- (BOOL)_interpretKeyEvent:(NSEvent *)theEvent savingCommandsTo:(Vector<WebCore::KeypressCommand>&)commands; - (void)_resendKeyDownEvent:(NSEvent *)event; +- (bool)_executeSavedCommandBySelector:(SEL)selector; - (NSRect)_convertToDeviceSpace:(NSRect)rect; - (NSRect)_convertToUserSpace:(NSRect)rect; - (void)_setFindIndicator:(PassRefPtr<WebKit::FindIndicator>)findIndicator fadeOut:(BOOL)fadeOut; @@ -68,7 +70,10 @@ namespace WebKit { - (void)_didFinishLoadingDataForCustomRepresentationWithSuggestedFilename:(const String&)suggestedFilename dataReference:(const CoreIPC::DataReference&)dataReference; - (double)_customRepresentationZoomFactor; - (void)_setCustomRepresentationZoomFactor:(double)zoomFactor; +- (void)_findStringInCustomRepresentation:(NSString *)string withFindOptions:(WebKit::FindOptions)options maxMatchCount:(NSUInteger)count; +- (void)_countStringMatchesInCustomRepresentation:(NSString *)string withFindOptions:(WebKit::FindOptions)options maxMatchCount:(NSUInteger)count; - (void)_setDragImage:(NSImage *)image at:(NSPoint)clientPoint linkDrag:(BOOL)linkDrag; +- (void)_updateSecureInputState; - (void)_setDrawingAreaSize:(NSSize)size; |