diff options
Diffstat (limited to 'Source/WebKit2/UIProcess/API/mac')
-rw-r--r-- | Source/WebKit2/UIProcess/API/mac/FindIndicatorWindow.h | 77 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/API/mac/FindIndicatorWindow.mm | 245 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/API/mac/PDFViewController.h | 74 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/API/mac/PDFViewController.mm | 297 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/API/mac/PageClientImpl.h | 103 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/API/mac/PageClientImpl.mm | 350 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/API/mac/WKTextInputWindowController.h | 44 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/API/mac/WKTextInputWindowController.mm | 156 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/API/mac/WKView.h | 47 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/API/mac/WKView.mm | 1795 | ||||
-rw-r--r-- | Source/WebKit2/UIProcess/API/mac/WKViewInternal.h | 66 |
11 files changed, 3254 insertions, 0 deletions
diff --git a/Source/WebKit2/UIProcess/API/mac/FindIndicatorWindow.h b/Source/WebKit2/UIProcess/API/mac/FindIndicatorWindow.h new file mode 100644 index 0000000..e8889b6 --- /dev/null +++ b/Source/WebKit2/UIProcess/API/mac/FindIndicatorWindow.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FindIndicatorWindow_h +#define FindIndicatorWindow_h + +#import <wtf/Noncopyable.h> +#import <wtf/PassOwnPtr.h> +#import <wtf/RefPtr.h> +#import <wtf/RetainPtr.h> +#import "RunLoop.h" +#import "WebKitSystemInterface.h" + +@class WKView; +@class WebFindIndicatorWindowAnimation; + +namespace WebKit { + +class FindIndicator; + +class FindIndicatorWindow { + WTF_MAKE_NONCOPYABLE(FindIndicatorWindow); + +public: + static PassOwnPtr<FindIndicatorWindow> create(WKView *); + ~FindIndicatorWindow(); + + void setFindIndicator(PassRefPtr<FindIndicator>, bool fadeOut); + +private: + explicit FindIndicatorWindow(WKView *); + void closeWindow(); + + void startFadeOutTimerFired(); + + void fadeOutAnimationCallback(double); + void fadeOutAnimationDidEnd(); + + void bounceAnimationCallback(double); + void bounceAnimationDidEnd(); + + WKView* m_wkView; + RefPtr<FindIndicator> m_findIndicator; + RetainPtr<NSWindow> m_findIndicatorWindow; + + WKWindowBounceAnimationContextRef m_bounceAnimationContext; + RetainPtr<WebFindIndicatorWindowAnimation> m_bounceAnimation; + + RunLoop::Timer<FindIndicatorWindow> m_startFadeOutTimer; + RetainPtr<WebFindIndicatorWindowAnimation> m_fadeOutAnimation; +}; + +} // namespace WebKit + +#endif // FindIndicatorWindow_h diff --git a/Source/WebKit2/UIProcess/API/mac/FindIndicatorWindow.mm b/Source/WebKit2/UIProcess/API/mac/FindIndicatorWindow.mm new file mode 100644 index 0000000..5b93a9e --- /dev/null +++ b/Source/WebKit2/UIProcess/API/mac/FindIndicatorWindow.mm @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "FindIndicatorWindow.h" + +#include "FindIndicator.h" +#include <WebCore/GraphicsContext.h> + +static const double bounceAnimationDuration = 0.12; +static const double timeBeforeFadeStarts = bounceAnimationDuration + 0.2; +static const double fadeOutAnimationDuration = 0.3; + +using namespace WebCore; + +@interface WebFindIndicatorView : NSView { + RefPtr<WebKit::FindIndicator> _findIndicator; +} + +- (id)_initWithFindIndicator:(PassRefPtr<WebKit::FindIndicator>)findIndicator; +@end + +@implementation WebFindIndicatorView + +- (id)_initWithFindIndicator:(PassRefPtr<WebKit::FindIndicator>)findIndicator +{ + if ((self = [super initWithFrame:NSZeroRect])) + _findIndicator = findIndicator; + + return self; +} + +- (void)drawRect:(NSRect)rect +{ + GraphicsContext graphicsContext(static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort])); + + _findIndicator->draw(graphicsContext, enclosingIntRect(rect)); +} + +- (BOOL)isFlipped +{ + return YES; +} + +@end + +@interface WebFindIndicatorWindowAnimation : NSAnimation<NSAnimationDelegate> { + WebKit::FindIndicatorWindow* _findIndicatorWindow; + void (WebKit::FindIndicatorWindow::*_animationProgressCallback)(double progress); + void (WebKit::FindIndicatorWindow::*_animationDidEndCallback)(); +} + +- (id)_initWithFindIndicatorWindow:(WebKit::FindIndicatorWindow *)findIndicatorWindow + animationDuration:(CFTimeInterval)duration + animationProgressCallback:(void (WebKit::FindIndicatorWindow::*)(double progress))animationProgressCallback + animationDidEndCallback:(void (WebKit::FindIndicatorWindow::*)())animationDidEndCallback; +@end + +@implementation WebFindIndicatorWindowAnimation + +- (id)_initWithFindIndicatorWindow:(WebKit::FindIndicatorWindow *)findIndicatorWindow + animationDuration:(CFTimeInterval)animationDuration + animationProgressCallback:(void (WebKit::FindIndicatorWindow::*)(double progress))animationProgressCallback + animationDidEndCallback:(void (WebKit::FindIndicatorWindow::*)())animationDidEndCallback +{ + if ((self = [super initWithDuration:animationDuration animationCurve:NSAnimationEaseInOut])) { + _findIndicatorWindow = findIndicatorWindow; + _animationProgressCallback = animationProgressCallback; + _animationDidEndCallback = animationDidEndCallback; + [self setDelegate:self]; + [self setAnimationBlockingMode:NSAnimationNonblocking]; + } + return self; +} + +- (void)setCurrentProgress:(NSAnimationProgress)progress +{ + (_findIndicatorWindow->*_animationProgressCallback)(progress); +} + +- (void)animationDidEnd:(NSAnimation *)animation +{ + ASSERT(animation == self); + + (_findIndicatorWindow->*_animationDidEndCallback)(); +} + +@end + +namespace WebKit { + +PassOwnPtr<FindIndicatorWindow> FindIndicatorWindow::create(WKView *wkView) +{ + return adoptPtr(new FindIndicatorWindow(wkView)); +} + +FindIndicatorWindow::FindIndicatorWindow(WKView *wkView) + : m_wkView(wkView) + , m_bounceAnimationContext(0) + , m_startFadeOutTimer(RunLoop::main(), this, &FindIndicatorWindow::startFadeOutTimerFired) +{ +} + +FindIndicatorWindow::~FindIndicatorWindow() +{ + closeWindow(); +} + +void FindIndicatorWindow::setFindIndicator(PassRefPtr<FindIndicator> findIndicator, bool fadeOut) +{ + if (m_findIndicator == findIndicator) + return; + + m_findIndicator = findIndicator; + + // Get rid of the old window. + closeWindow(); + + if (!m_findIndicator) + return; + + NSRect contentRect = m_findIndicator->frameRect(); + NSRect windowFrameRect = NSIntegralRect([m_wkView convertRect:contentRect toView:nil]); + windowFrameRect.origin = [[m_wkView window] convertBaseToScreen:windowFrameRect.origin]; + + NSRect windowContentRect = [NSWindow contentRectForFrameRect:windowFrameRect styleMask:NSBorderlessWindowMask]; + + m_findIndicatorWindow.adoptNS([[NSWindow alloc] initWithContentRect:windowContentRect + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:NO]); + + [m_findIndicatorWindow.get() setBackgroundColor:[NSColor clearColor]]; + [m_findIndicatorWindow.get() setOpaque:NO]; + [m_findIndicatorWindow.get() setIgnoresMouseEvents:YES]; + + RetainPtr<WebFindIndicatorView> findIndicatorView(AdoptNS, [[WebFindIndicatorView alloc] _initWithFindIndicator:m_findIndicator]); + [m_findIndicatorWindow.get() setContentView:findIndicatorView.get()]; + + [[m_wkView window] addChildWindow:m_findIndicatorWindow.get() ordered:NSWindowAbove]; + [m_findIndicatorWindow.get() setReleasedWhenClosed:NO]; + + // Start the bounce animation. + m_bounceAnimationContext = WKWindowBounceAnimationContextCreate(m_findIndicatorWindow.get()); + m_bounceAnimation.adoptNS([[WebFindIndicatorWindowAnimation alloc] _initWithFindIndicatorWindow:this + animationDuration:bounceAnimationDuration + animationProgressCallback:&FindIndicatorWindow::bounceAnimationCallback + animationDidEndCallback:&FindIndicatorWindow::bounceAnimationDidEnd]); + [m_bounceAnimation.get() startAnimation]; + + if (fadeOut) + m_startFadeOutTimer.startOneShot(timeBeforeFadeStarts); +} + +void FindIndicatorWindow::closeWindow() +{ + if (!m_findIndicatorWindow) + return; + + m_startFadeOutTimer.stop(); + + if (m_fadeOutAnimation) { + [m_fadeOutAnimation.get() stopAnimation]; + m_fadeOutAnimation = nullptr; + } + + if (m_bounceAnimation) { + [m_bounceAnimation.get() stopAnimation]; + m_bounceAnimation = nullptr; + } + + if (m_bounceAnimationContext) + WKWindowBounceAnimationContextDestroy(m_bounceAnimationContext); + + [[m_findIndicatorWindow.get() parentWindow] removeChildWindow:m_findIndicatorWindow.get()]; + [m_findIndicatorWindow.get() close]; + m_findIndicatorWindow = nullptr; +} + +void FindIndicatorWindow::startFadeOutTimerFired() +{ + ASSERT(!m_fadeOutAnimation); + + m_fadeOutAnimation.adoptNS([[WebFindIndicatorWindowAnimation alloc] _initWithFindIndicatorWindow:this + animationDuration:fadeOutAnimationDuration + animationProgressCallback:&FindIndicatorWindow::fadeOutAnimationCallback + animationDidEndCallback:&FindIndicatorWindow::fadeOutAnimationDidEnd]); + [m_fadeOutAnimation.get() startAnimation]; +} + +void FindIndicatorWindow::fadeOutAnimationCallback(double progress) +{ + ASSERT(m_fadeOutAnimation); + + [m_findIndicatorWindow.get() setAlphaValue:1.0 - progress]; +} + +void FindIndicatorWindow::fadeOutAnimationDidEnd() +{ + ASSERT(m_fadeOutAnimation); + ASSERT(m_findIndicatorWindow); + + closeWindow(); +} + +void FindIndicatorWindow::bounceAnimationCallback(double progress) +{ + ASSERT(m_bounceAnimation); + ASSERT(m_bounceAnimationContext); + + WKWindowBounceAnimationSetAnimationProgress(m_bounceAnimationContext, progress); +} + +void FindIndicatorWindow::bounceAnimationDidEnd() +{ + ASSERT(m_bounceAnimation); + ASSERT(m_bounceAnimationContext); + ASSERT(m_findIndicatorWindow); + + WKWindowBounceAnimationContextDestroy(m_bounceAnimationContext); + m_bounceAnimationContext = 0; +} + +} // namespace WebKit diff --git a/Source/WebKit2/UIProcess/API/mac/PDFViewController.h b/Source/WebKit2/UIProcess/API/mac/PDFViewController.h new file mode 100644 index 0000000..2c4a235 --- /dev/null +++ b/Source/WebKit2/UIProcess/API/mac/PDFViewController.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PDFViewController_h +#define PDFViewController_h + +#include <wtf/Forward.h> +#include <wtf/Noncopyable.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/RetainPtr.h> + +@class PDFView; +@class WKView; +@class WKPDFView; + +namespace CoreIPC { + class DataReference; +} + +namespace WebKit { + +class PDFViewController { + WTF_MAKE_NONCOPYABLE(PDFViewController); + +public: + static PassOwnPtr<PDFViewController> create(WKView *); + ~PDFViewController(); + + WKView* wkView() const { return m_wkView; } + void setPDFDocumentData(const String& mimeType, const CoreIPC::DataReference&); + + double zoomFactor() const; + void setZoomFactor(double); + + static Class pdfPreviewViewClass(); + + NSPrintOperation *makePrintOperation(NSPrintInfo *); + +private: + explicit PDFViewController(WKView *wkView); + + static Class pdfDocumentClass(); + static NSBundle* pdfKitBundle(); + + WKView* m_wkView; + RetainPtr<WKPDFView> m_wkPDFView; + PDFView* m_pdfView; +}; + +} // namespace WebKit + +#endif // PDFViewController_h diff --git a/Source/WebKit2/UIProcess/API/mac/PDFViewController.mm b/Source/WebKit2/UIProcess/API/mac/PDFViewController.mm new file mode 100644 index 0000000..a92c2d4 --- /dev/null +++ b/Source/WebKit2/UIProcess/API/mac/PDFViewController.mm @@ -0,0 +1,297 @@ +/* + * 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 + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "PDFViewController.h" + +#import "DataReference.h" +#import "WKAPICast.h" +#import "WKView.h" +#import "WebPageGroup.h" +#import "WebPageProxy.h" +#import "WebPreferences.h" +#import <PDFKit/PDFKit.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. +#define _webkit_PDFViewDisplayModeChangedNotification @"PDFViewDisplayModeChanged" +#define _webkit_PDFViewScaleChangedNotification @"PDFViewScaleChanged" +#define _webkit_PDFViewPageChangedNotification @"PDFViewChangedPage" + +using namespace WebKit; + +@class PDFDocument; +@class PDFView; + +@interface PDFDocument (PDFDocumentDetails) +- (NSPrintOperation *)getPrintOperationForPrintInfo:(NSPrintInfo *)printInfo autoRotate:(BOOL)doRotate; +@end + +extern "C" NSString *_NSPathForSystemFramework(NSString *framework); + +@interface WKPDFView : NSView +{ + PDFViewController* _pdfViewController; + + RetainPtr<NSView> _pdfPreviewView; + PDFView *_pdfView; + BOOL _ignoreScaleAndDisplayModeAndPageNotifications; + BOOL _willUpdatePreferencesSoon; +} + +- (id)initWithFrame:(NSRect)frame PDFViewController:(PDFViewController*)pdfViewController; +- (void)invalidate; +- (PDFView *)pdfView; +- (void)setDocument:(PDFDocument *)pdfDocument; + +- (void)_applyPDFPreferences; + +@end + +@implementation WKPDFView + +- (id)initWithFrame:(NSRect)frame PDFViewController:(PDFViewController*)pdfViewController +{ + if ((self = [super initWithFrame:frame])) { + _pdfViewController = pdfViewController; + + [self setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + + Class previewViewClass = PDFViewController::pdfPreviewViewClass(); + ASSERT(previewViewClass); + + _pdfPreviewView.adoptNS([[previewViewClass alloc] initWithFrame:frame]); + [_pdfPreviewView.get() setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [self addSubview:_pdfPreviewView.get()]; + + _pdfView = [_pdfPreviewView.get() performSelector:@selector(pdfView)]; + } + + return self; +} + +- (void)invalidate +{ + _pdfViewController = 0; +} + +- (PDFView *)pdfView +{ + return _pdfView; +} + +- (void)setDocument:(PDFDocument *)pdfDocument +{ + _ignoreScaleAndDisplayModeAndPageNotifications = YES; + [_pdfView setDocument:pdfDocument]; + [self _applyPDFPreferences]; + _ignoreScaleAndDisplayModeAndPageNotifications = NO; +} + +- (void)_applyPDFPreferences +{ + if (!_pdfViewController) + return; + + WebPreferences *preferences = toImpl([_pdfViewController->wkView() pageRef])->pageGroup()->preferences(); + + CGFloat scaleFactor = preferences->pdfScaleFactor(); + if (!scaleFactor) + [_pdfView setAutoScales:YES]; + else { + [_pdfView setAutoScales:NO]; + [_pdfView setScaleFactor:scaleFactor]; + } + [_pdfView setDisplayMode:preferences->pdfDisplayMode()]; +} + +- (void)_updatePreferences:(id)ignored +{ + _willUpdatePreferencesSoon = NO; + + if (!_pdfViewController) + return; + + WebPreferences* preferences = toImpl([_pdfViewController->wkView() pageRef])->pageGroup()->preferences(); + + CGFloat scaleFactor = [_pdfView autoScales] ? 0 : [_pdfView scaleFactor]; + preferences->setPDFScaleFactor(scaleFactor); + preferences->setPDFDisplayMode([_pdfView displayMode]); +} + +- (void)_updatePreferencesSoon +{ + if (_willUpdatePreferencesSoon) + return; + + [self performSelector:@selector(_updatePreferences:) withObject:nil afterDelay:0]; + _willUpdatePreferencesSoon = YES; +} + +- (void)_scaleOrDisplayModeOrPageChanged:(NSNotification *)notification +{ + ASSERT_ARG(notification, [notification object] == _pdfView); + if (!_ignoreScaleAndDisplayModeAndPageNotifications) + [self _updatePreferencesSoon]; +} + +- (void)viewDidMoveToWindow +{ + if (![self window]) + return; + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter addObserver:self selector:@selector(_scaleOrDisplayModeOrPageChanged:) name:_webkit_PDFViewScaleChangedNotification object:_pdfView]; + [notificationCenter addObserver:self selector:@selector(_scaleOrDisplayModeOrPageChanged:) name:_webkit_PDFViewDisplayModeChangedNotification object:_pdfView]; + [notificationCenter addObserver:self selector:@selector(_scaleOrDisplayModeOrPageChanged:) name:_webkit_PDFViewPageChangedNotification object:_pdfView]; +} + +- (void)viewWillMoveToWindow:(NSWindow *)newWindow +{ + if (![self window]) + return; + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter removeObserver:self name:_webkit_PDFViewScaleChangedNotification object:_pdfView]; + [notificationCenter removeObserver:self name:_webkit_PDFViewDisplayModeChangedNotification object:_pdfView]; + [notificationCenter removeObserver:self name:_webkit_PDFViewPageChangedNotification object:_pdfView]; +} + +@end + +namespace WebKit { + +PassOwnPtr<PDFViewController> PDFViewController::create(WKView *wkView) +{ + return adoptPtr(new PDFViewController(wkView)); +} + +PDFViewController::PDFViewController(WKView *wkView) + : m_wkView(wkView) + , m_wkPDFView(AdoptNS, [[WKPDFView alloc] initWithFrame:[m_wkView bounds] PDFViewController:this]) + , m_pdfView([m_wkPDFView.get() pdfView]) +{ + [m_wkView addSubview:m_wkPDFView.get()]; +} + +PDFViewController::~PDFViewController() +{ + [m_wkPDFView.get() removeFromSuperview]; + [m_wkPDFView.get() invalidate]; + m_wkPDFView = nullptr; +} + +static RetainPtr<CFDataRef> convertPostScriptDataSourceToPDF(const CoreIPC::DataReference& dataReference) +{ + // Convert PostScript to PDF using Quartz 2D API + // http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_ps_convert/chapter_16_section_1.html + + CGPSConverterCallbacks callbacks = { 0, 0, 0, 0, 0, 0, 0, 0 }; + RetainPtr<CGPSConverterRef> converter(AdoptCF, CGPSConverterCreate(0, &callbacks, 0)); + ASSERT(converter); + + RetainPtr<NSData> nsData(AdoptNS, [[NSData alloc] initWithBytesNoCopy:const_cast<uint8_t*>(dataReference.data()) length:dataReference.size() freeWhenDone:NO]); + + RetainPtr<CGDataProviderRef> provider(AdoptCF, CGDataProviderCreateWithCFData((CFDataRef)nsData.get())); + ASSERT(provider); + + RetainPtr<CFMutableDataRef> result(AdoptCF, CFDataCreateMutable(kCFAllocatorDefault, 0)); + ASSERT(result); + + RetainPtr<CGDataConsumerRef> consumer(AdoptCF, CGDataConsumerCreateWithCFData(result.get())); + ASSERT(consumer); + + CGPSConverterConvert(converter.get(), provider.get(), consumer.get(), 0); + + if (!result) + return 0; + + return result; +} + +void PDFViewController::setPDFDocumentData(const String& mimeType, const CoreIPC::DataReference& dataReference) +{ + RetainPtr<CFDataRef> data; + + if (equalIgnoringCase(mimeType, "application/postscript")) { + data = convertPostScriptDataSourceToPDF(dataReference); + if (!data) + return; + } else { + // Make sure to copy the data. + data.adoptCF(CFDataCreate(0, dataReference.data(), dataReference.size())); + } + + RetainPtr<PDFDocument> pdfDocument(AdoptNS, [[pdfDocumentClass() alloc] initWithData:(NSData *)data.get()]); + [m_wkPDFView.get() setDocument:pdfDocument.get()]; +} + +double PDFViewController::zoomFactor() const +{ + return [m_pdfView scaleFactor]; +} + +void PDFViewController::setZoomFactor(double zoomFactor) +{ + [m_pdfView setScaleFactor:zoomFactor]; +} + +Class PDFViewController::pdfDocumentClass() +{ + static Class pdfDocumentClass = [pdfKitBundle() classNamed:@"PDFDocument"]; + + return pdfDocumentClass; +} + +Class PDFViewController::pdfPreviewViewClass() +{ + static Class pdfPreviewViewClass = [pdfKitBundle() classNamed:@"PDFPreviewView"]; + + return pdfPreviewViewClass; +} + +NSBundle* PDFViewController::pdfKitBundle() +{ + static NSBundle *pdfKitBundle; + if (pdfKitBundle) + return pdfKitBundle; + + NSString *pdfKitPath = [_NSPathForSystemFramework(@"Quartz.framework") stringByAppendingString:@"/Frameworks/PDFKit.framework"]; + if (!pdfKitPath) { + LOG_ERROR("Couldn't find PDFKit.framework"); + return nil; + } + + pdfKitBundle = [NSBundle bundleWithPath:pdfKitPath]; + if (![pdfKitBundle load]) + LOG_ERROR("Couldn't load PDFKit.framework"); + return pdfKitBundle; +} + +NSPrintOperation *PDFViewController::makePrintOperation(NSPrintInfo *printInfo) +{ + return [[m_pdfView document] getPrintOperationForPrintInfo:printInfo autoRotate:YES]; +} + +} // namespace WebKit diff --git a/Source/WebKit2/UIProcess/API/mac/PageClientImpl.h b/Source/WebKit2/UIProcess/API/mac/PageClientImpl.h new file mode 100644 index 0000000..8322465 --- /dev/null +++ b/Source/WebKit2/UIProcess/API/mac/PageClientImpl.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PageClientImpl_h +#define PageClientImpl_h + +#include "PageClient.h" +#include <wtf/RetainPtr.h> + +@class WKView; +@class WebEditorUndoTargetObjC; + +namespace WebKit { + +class FindIndicatorWindow; + +// NOTE: This does not use String::operator NSString*() since that function +// expects to be called on the thread running WebCore. +NSString* nsStringFromWebCoreString(const String&); + +class PageClientImpl : public PageClient { +public: + static PassOwnPtr<PageClientImpl> create(WKView*); + virtual ~PageClientImpl(); + +private: + PageClientImpl(WKView*); + + virtual PassOwnPtr<DrawingAreaProxy> createDrawingAreaProxy(); + virtual void setViewNeedsDisplay(const WebCore::IntRect&); + virtual void displayView(); + + virtual WebCore::IntSize viewSize(); + virtual bool isViewWindowActive(); + virtual bool isViewFocused(); + virtual bool isViewVisible(); + virtual bool isViewInWindow(); + + virtual void processDidCrash(); + virtual void didRelaunchProcess(); + 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 setEditCommandState(const String& commandName, bool isEnabled, int state); + virtual void interceptKeyEvent(const NativeWebKeyboardEvent& event, Vector<WebCore::KeypressCommand>& commandName, uint32_t selectionStart, uint32_t selectionEnd, Vector<WebCore::CompositionUnderline>& underlines); + + virtual WebCore::FloatRect convertToDeviceSpace(const WebCore::FloatRect&); + virtual WebCore::FloatRect convertToUserSpace(const WebCore::FloatRect&); + + virtual void didNotHandleKeyEvent(const NativeWebKeyboardEvent&); + + virtual PassRefPtr<WebPopupMenuProxy> createPopupMenuProxy(WebPageProxy*); + virtual PassRefPtr<WebContextMenuProxy> createContextMenuProxy(WebPageProxy*); + + void setFindIndicator(PassRefPtr<FindIndicator>, bool fadeOut); + +#if USE(ACCELERATED_COMPOSITING) + virtual void pageDidEnterAcceleratedCompositing(); + virtual void pageDidLeaveAcceleratedCompositing(); +#endif + + virtual void accessibilityChildTokenReceived(const CoreIPC::DataReference&); + virtual void setComplexTextInputEnabled(uint64_t pluginComplexTextInputIdentifier, bool complexTextInputEnabled); + + virtual void didCommitLoadForMainFrame(bool useCustomRepresentation); + virtual void didFinishLoadingDataForCustomRepresentation(const CoreIPC::DataReference&); + + virtual double customRepresentationZoomFactor(); + virtual void setCustomRepresentationZoomFactor(double); + + WKView* m_wkView; + RetainPtr<WebEditorUndoTargetObjC> m_undoTarget; +}; + +} // namespace WebKit + +#endif // PageClientImpl_h diff --git a/Source/WebKit2/UIProcess/API/mac/PageClientImpl.mm b/Source/WebKit2/UIProcess/API/mac/PageClientImpl.mm new file mode 100644 index 0000000..c37b641 --- /dev/null +++ b/Source/WebKit2/UIProcess/API/mac/PageClientImpl.mm @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "NativeWebKeyboardEvent.h" +#import "PageClientImpl.h" + +#import "DataReference.h" +#import "FindIndicator.h" +#import "WKAPICast.h" +#import "WKStringCF.h" +#import "WKViewInternal.h" +#import "WebContextMenuProxyMac.h" +#import "WebEditCommandProxy.h" +#import "WebPopupMenuProxyMac.h" +#import <WebCore/Cursor.h> +#import <WebCore/FloatRect.h> +#import <WebCore/FoundationExtras.h> +#import <WebCore/KeyboardEvent.h> +#import <wtf/PassOwnPtr.h> +#import <wtf/text/CString.h> +#import <wtf/text/WTFString.h> + +using namespace WebCore; + +@interface WebEditCommandObjC : NSObject +{ + RefPtr<WebKit::WebEditCommandProxy> m_command; +} + +- (id)initWithWebEditCommandProxy:(PassRefPtr<WebKit::WebEditCommandProxy>)command; +- (WebKit::WebEditCommandProxy*)command; + +@end + +@implementation WebEditCommandObjC + +- (id)initWithWebEditCommandProxy:(PassRefPtr<WebKit::WebEditCommandProxy>)command +{ + self = [super init]; + if (!self) + return nil; + + m_command = command; + return self; +} + +- (WebKit::WebEditCommandProxy*)command +{ + return m_command.get(); +} + +@end + +@interface WebEditorUndoTargetObjC : NSObject + +- (void)undoEditing:(id)sender; +- (void)redoEditing:(id)sender; + +@end + +@implementation WebEditorUndoTargetObjC + +- (void)undoEditing:(id)sender +{ + ASSERT([sender isKindOfClass:[WebEditCommandObjC class]]); + [sender command]->unapply(); +} + +- (void)redoEditing:(id)sender +{ + ASSERT([sender isKindOfClass:[WebEditCommandObjC class]]); + [sender command]->reapply(); +} + +@end + +namespace WebKit { + +NSString* nsStringFromWebCoreString(const String& string) +{ + return string.impl() ? HardAutorelease(WKStringCopyCFString(0, toAPI(string.impl()))) : @""; +} + +PassOwnPtr<PageClientImpl> PageClientImpl::create(WKView* wkView) +{ + return adoptPtr(new PageClientImpl(wkView)); +} + +PageClientImpl::PageClientImpl(WKView* wkView) + : m_wkView(wkView) + , m_undoTarget(AdoptNS, [[WebEditorUndoTargetObjC alloc] init]) +{ +} + +PageClientImpl::~PageClientImpl() +{ +} + +PassOwnPtr<DrawingAreaProxy> PageClientImpl::createDrawingAreaProxy() +{ + return [m_wkView _createDrawingAreaProxy]; +} + +void PageClientImpl::setViewNeedsDisplay(const WebCore::IntRect& rect) +{ + [m_wkView setNeedsDisplayInRect:rect]; +} + +void PageClientImpl::displayView() +{ + [m_wkView displayIfNeeded]; +} + +IntSize PageClientImpl::viewSize() +{ + return IntSize([m_wkView bounds].size); +} + +bool PageClientImpl::isViewWindowActive() +{ + return [[m_wkView window] isKeyWindow]; +} + +bool PageClientImpl::isViewFocused() +{ + return [m_wkView _isFocused]; +} + +bool PageClientImpl::isViewVisible() +{ + if (![m_wkView window]) + return false; + + if ([m_wkView isHiddenOrHasHiddenAncestor]) + return false; + + return true; +} + +bool PageClientImpl::isViewInWindow() +{ + return [m_wkView window]; +} + +void PageClientImpl::processDidCrash() +{ + [m_wkView _processDidCrash]; +} + +void PageClientImpl::didRelaunchProcess() +{ + [m_wkView _didRelaunchProcess]; +} + +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)]; +} + +void PageClientImpl::setCursor(const WebCore::Cursor& cursor) +{ + [m_wkView _setCursor:cursor.platformCursor()]; +} + +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()); + + 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]; +} + +void PageClientImpl::clearAllEditCommands() +{ + [[m_wkView undoManager] removeAllActionsWithTarget:m_undoTarget.get()]; +} + +void PageClientImpl::setEditCommandState(const String& commandName, bool isEnabled, int newState) +{ + [m_wkView _setUserInterfaceItemState:nsStringFromWebCoreString(commandName) enabled:isEnabled state:newState]; +} + +void PageClientImpl::interceptKeyEvent(const NativeWebKeyboardEvent& event, Vector<WebCore::KeypressCommand>& commandsList, uint32_t selectionStart, uint32_t selectionEnd, Vector<WebCore::CompositionUnderline>& underlines) +{ + commandsList = [m_wkView _interceptKeyEvent:event.nativeEvent()]; + [m_wkView _getTextInputState:selectionStart selectionEnd:selectionEnd underlines:underlines]; +} + +FloatRect PageClientImpl::convertToDeviceSpace(const FloatRect& rect) +{ + return [m_wkView _convertToDeviceSpace:rect]; +} + +FloatRect PageClientImpl::convertToUserSpace(const FloatRect& rect) +{ + return [m_wkView _convertToUserSpace:rect]; +} + +void PageClientImpl::didNotHandleKeyEvent(const NativeWebKeyboardEvent& event) +{ + NSEvent* nativeEvent = event.nativeEvent(); + if ([nativeEvent type] == NSKeyDown) { + [m_wkView _setEventBeingResent:nativeEvent]; + [[NSApplication sharedApplication] sendEvent:nativeEvent]; + } +} + +PassRefPtr<WebPopupMenuProxy> PageClientImpl::createPopupMenuProxy(WebPageProxy* page) +{ + return WebPopupMenuProxyMac::create(m_wkView, page); +} + +PassRefPtr<WebContextMenuProxy> PageClientImpl::createContextMenuProxy(WebPageProxy* page) +{ + return WebContextMenuProxyMac::create(m_wkView, page); +} + +void PageClientImpl::setFindIndicator(PassRefPtr<FindIndicator> findIndicator, bool fadeOut) +{ + [m_wkView _setFindIndicator:findIndicator fadeOut:fadeOut]; +} + +void PageClientImpl::accessibilityChildTokenReceived(const CoreIPC::DataReference& data) +{ + NSData* remoteToken = [NSData dataWithBytes:data.data() length:data.size()]; + [m_wkView _setAccessibilityChildToken:remoteToken]; +} + +#if USE(ACCELERATED_COMPOSITING) +void PageClientImpl::pageDidEnterAcceleratedCompositing() +{ + [m_wkView _pageDidEnterAcceleratedCompositing]; +} + +void PageClientImpl::pageDidLeaveAcceleratedCompositing() +{ + [m_wkView _pageDidLeaveAcceleratedCompositing]; +} +#endif // USE(ACCELERATED_COMPOSITING) + +void PageClientImpl::setComplexTextInputEnabled(uint64_t pluginComplexTextInputIdentifier, bool complexTextInputEnabled) +{ + [m_wkView _setComplexTextInputEnabled:complexTextInputEnabled pluginComplexTextInputIdentifier:pluginComplexTextInputIdentifier]; +} + +void PageClientImpl::didCommitLoadForMainFrame(bool useCustomRepresentation) +{ + [m_wkView _setPageHasCustomRepresentation:useCustomRepresentation]; +} + +void PageClientImpl::didFinishLoadingDataForCustomRepresentation(const CoreIPC::DataReference& dataReference) +{ + [m_wkView _didFinishLoadingDataForCustomRepresentation:dataReference]; +} + +double PageClientImpl::customRepresentationZoomFactor() +{ + return [m_wkView _customRepresentationZoomFactor]; +} + +void PageClientImpl::setCustomRepresentationZoomFactor(double zoomFactor) +{ + [m_wkView _setCustomRepresentationZoomFactor:zoomFactor]; +} + +} // namespace WebKit diff --git a/Source/WebKit2/UIProcess/API/mac/WKTextInputWindowController.h b/Source/WebKit2/UIProcess/API/mac/WKTextInputWindowController.h new file mode 100644 index 0000000..b2ad3b0 --- /dev/null +++ b/Source/WebKit2/UIProcess/API/mac/WKTextInputWindowController.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2009, 2010 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WKTextInputWindowController_h +#define WKTextInputWindowController_h + +@class WKTextInputPanel; + +@interface WKTextInputWindowController : NSObject { + WKTextInputPanel *_panel; +} + ++ (WKTextInputWindowController *)sharedTextInputWindowController; + +- (NSTextInputContext *)inputContext; +- (BOOL)interpretKeyEvent:(NSEvent *)event string:(NSString **)string; + +- (void)keyboardInputSourceChanged; + +@end + +#endif // WKTextInputWindowController_h diff --git a/Source/WebKit2/UIProcess/API/mac/WKTextInputWindowController.mm b/Source/WebKit2/UIProcess/API/mac/WKTextInputWindowController.mm new file mode 100644 index 0000000..b7dae31 --- /dev/null +++ b/Source/WebKit2/UIProcess/API/mac/WKTextInputWindowController.mm @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "WKTextInputWindowController.h" + +#include <WebKitSystemInterface.h> + +@interface WKTextInputPanel : NSPanel { + NSTextView *_inputTextView; +} + +- (NSTextInputContext *)_inputContext; +- (BOOL)_interpretKeyEvent:(NSEvent *)event string:(NSString **)string; + +@end + +#define inputWindowHeight 20 + +@implementation WKTextInputPanel + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [_inputTextView release]; + + [super dealloc]; +} + +- (id)init +{ + self = [super initWithContentRect:NSZeroRect styleMask:WKGetInputPanelWindowStyle() backing:NSBackingStoreBuffered defer:YES]; + if (!self) + return nil; + + // Set the frame size. + NSRect visibleFrame = [[NSScreen mainScreen] visibleFrame]; + NSRect frame = NSMakeRect(visibleFrame.origin.x, visibleFrame.origin.y, visibleFrame.size.width, inputWindowHeight); + + [self setFrame:frame display:NO]; + + _inputTextView = [[NSTextView alloc] initWithFrame:[self.contentView frame]]; + _inputTextView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable | NSViewMaxXMargin | NSViewMinXMargin | NSViewMaxYMargin | NSViewMinYMargin; + + NSScrollView* scrollView = [[NSScrollView alloc] initWithFrame:[self.contentView frame]]; + scrollView.documentView = _inputTextView; + self.contentView = scrollView; + [scrollView release]; + + [self setFloatingPanel:YES]; + + return self; +} + +- (void)_keyboardInputSourceChanged +{ + [_inputTextView setString:@""]; + [self orderOut:nil]; +} + +- (BOOL)_interpretKeyEvent:(NSEvent *)event string:(NSString **)string +{ + BOOL hadMarkedText = [_inputTextView hasMarkedText]; + + *string = nil; + + if (![[_inputTextView inputContext] handleEvent:event]) + return NO; + + if ([_inputTextView hasMarkedText]) { + // Don't show the input method window for dead keys + if ([[event characters] length] > 0) + [self orderFront:nil]; + + return YES; + } + + if (hadMarkedText) { + [self orderOut:nil]; + + NSString *text = [[_inputTextView textStorage] string]; + if ([text length] > 0) + *string = [[text copy] autorelease]; + } + + [_inputTextView setString:@""]; + return hadMarkedText; +} + +- (NSTextInputContext *)_inputContext +{ + return [_inputTextView inputContext]; +} + +@end + +@implementation WKTextInputWindowController + ++ (WKTextInputWindowController *)sharedTextInputWindowController +{ + static WKTextInputWindowController *textInputWindowController; + if (!textInputWindowController) + textInputWindowController = [[WKTextInputWindowController alloc] init]; + + return textInputWindowController; +} + +- (id)init +{ + self = [super init]; + if (!self) + return nil; + + _panel = [[WKTextInputPanel alloc] init]; + + return self; +} + +- (NSTextInputContext *)inputContext +{ + return [_panel _inputContext]; +} + +- (BOOL)interpretKeyEvent:(NSEvent *)event string:(NSString **)string +{ + return [_panel _interpretKeyEvent:event string:string]; +} + +- (void)keyboardInputSourceChanged +{ + [_panel _keyboardInputSourceChanged]; +} + +@end diff --git a/Source/WebKit2/UIProcess/API/mac/WKView.h b/Source/WebKit2/UIProcess/API/mac/WKView.h new file mode 100644 index 0000000..618bbc4 --- /dev/null +++ b/Source/WebKit2/UIProcess/API/mac/WKView.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Cocoa/Cocoa.h> +#import <WebKit2/WKBase.h> + +@class WKViewData; + +WK_EXPORT +@interface WKView : NSView <NSTextInput> { + WKViewData *_data; +} + +- (id)initWithFrame:(NSRect)frame contextRef:(WKContextRef)contextRef; +- (id)initWithFrame:(NSRect)frame contextRef:(WKContextRef)contextRef pageGroupRef:(WKPageGroupRef)pageGroupRef; + +- (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo forFrame:(WKFrameRef)frameRef; +- (BOOL)canChangeFrameLayout:(WKFrameRef)frameRef; + +@property(readonly) WKPageRef pageRef; + +@property BOOL drawsBackground; +@property BOOL drawsTransparentBackground; + +@end diff --git a/Source/WebKit2/UIProcess/API/mac/WKView.mm b/Source/WebKit2/UIProcess/API/mac/WKView.mm new file mode 100644 index 0000000..da9b7c9 --- /dev/null +++ b/Source/WebKit2/UIProcess/API/mac/WKView.mm @@ -0,0 +1,1795 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "WKView.h" + +#import "ChunkedUpdateDrawingAreaProxy.h" +#import "DataReference.h" +#import "DrawingAreaProxyImpl.h" +#import "FindIndicator.h" +#import "FindIndicatorWindow.h" +#import "LayerBackedDrawingAreaProxy.h" +#import "Logging.h" +#import "NativeWebKeyboardEvent.h" +#import "PDFViewController.h" +#import "PageClientImpl.h" +#import "PrintInfo.h" +#import "RunLoop.h" +#import "TextChecker.h" +#import "TextCheckerState.h" +#import "WKAPICast.h" +#import "WKStringCF.h" +#import "WKTextInputWindowController.h" +#import "WebContext.h" +#import "WebEventFactory.h" +#import "WebPage.h" +#import "WebPageProxy.h" +#import "WebProcessManager.h" +#import "WebProcessProxy.h" +#import "WebSystemInterface.h" +#import <QuartzCore/QuartzCore.h> +#import <WebCore/ColorMac.h> +#import <WebCore/DragController.h> +#import <WebCore/DragData.h> +#import <WebCore/FloatRect.h> +#import <WebCore/IntRect.h> +#import <WebCore/KeyboardEvent.h> +#import <WebCore/PlatformMouseEvent.h> +#import <WebCore/PlatformScreen.h> +#import <WebKitSystemInterface.h> +#import <wtf/RefPtr.h> +#import <wtf/RetainPtr.h> + +// FIXME (WebKit2) <rdar://problem/8728860> WebKit2 needs to be localized +#define UI_STRING(__str, __desc) [NSString stringWithUTF8String:__str] + +@interface NSApplication (Details) +- (void)speakString:(NSString *)string; +@end + +@interface NSView (Details) +- (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView; +@end + +@interface NSWindow (Details) +- (NSRect)_growBoxRect; +- (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; + +namespace WebKit { + +typedef id <NSValidatedUserInterfaceItem> ValidationItem; +typedef Vector<RetainPtr<ValidationItem> > ValidationVector; +typedef HashMap<String, ValidationVector> ValidationMap; + +} + +@interface WKViewData : NSObject { +@public + OwnPtr<PageClientImpl> _pageClient; + RefPtr<WebPageProxy> _page; + + // For ToolTips. + NSToolTipTag _lastToolTipTag; + id _trackingRectOwner; + void* _trackingRectUserData; + +#if USE(ACCELERATED_COMPOSITING) + NSView *_layerHostingView; +#endif + + RetainPtr<id> _remoteAccessibilityChild; + + // For asynchronous validation. + ValidationMap _validationMap; + + OwnPtr<PDFViewController> _pdfViewController; + + OwnPtr<FindIndicatorWindow> _findIndicatorWindow; + // We keep here the event when resending it to + // the application to distinguish the case of a new event from one + // that has been already sent to WebCore. + NSEvent *_keyDownEventBeingResent; + Vector<KeypressCommand> _commandsList; + + // 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; + + Vector<IntRect> _printingPageRects; + double _totalScaleFactorForPrinting; + + bool _inBecomeFirstResponder; + bool _inResignFirstResponder; +} +@end + +@implementation WKViewData +@end + +@interface WebFrameWrapper : NSObject { +@public + RefPtr<WebFrameProxy> _frame; +} + +- (id)initWithFrameProxy:(WebFrameProxy*)frame; +- (WebFrameProxy*)webFrame; +@end + +@implementation WebFrameWrapper + +- (id)initWithFrameProxy:(WebFrameProxy*)frame +{ + self = [super init]; + if (!self) + return nil; + + _frame = frame; + return self; +} + +- (WebFrameProxy*)webFrame +{ + return _frame.get(); +} + +@end + +NSString * const PrintedFrameKey = @"WebKitPrintedFrameKey"; + +@interface NSObject (NSTextInputContextDetails) +- (BOOL)wantsToHandleMouseEvents; +- (BOOL)handleMouseEvent:(NSEvent *)event; +@end + +@implementation WKView + +static bool useNewDrawingArea() +{ + static bool useNewDrawingArea = getenv("USE_NEW_DRAWING_AREA"); + + return useNewDrawingArea; +} + +- (id)initWithFrame:(NSRect)frame +{ + return [self initWithFrame:frame contextRef:toAPI(WebContext::sharedProcessContext())]; +} + +- (id)initWithFrame:(NSRect)frame contextRef:(WKContextRef)contextRef +{ + return [self initWithFrame:frame contextRef:contextRef pageGroupRef:nil]; +} + +static NSString * const WebArchivePboardType = @"Apple Web Archive pasteboard type"; +static NSString * const WebURLsWithTitlesPboardType = @"WebURLsWithTitlesPboardType"; +static NSString * const WebURLPboardType = @"public.url"; +static NSString * const WebURLNamePboardType = @"public.url-name"; + +- (void)_registerDraggedTypes +{ + NSArray *editableTypes = [NSArray arrayWithObjects:WebArchivePboardType, NSHTMLPboardType, NSFilenamesPboardType, NSTIFFPboardType, NSPDFPboardType, +#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) + NSPICTPboardType, +#endif + NSURLPboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSColorPboardType, kUTTypePNG, nil]; + NSArray *URLTypes = [NSArray arrayWithObjects:WebURLsWithTitlesPboardType, NSURLPboardType, WebURLPboardType, WebURLNamePboardType, NSStringPboardType, NSFilenamesPboardType, nil]; + NSMutableSet *types = [[NSMutableSet alloc] initWithArray:editableTypes]; + [types addObjectsFromArray:URLTypes]; + [self registerForDraggedTypes:[types allObjects]]; + [types release]; +} + +- (id)initWithFrame:(NSRect)frame contextRef:(WKContextRef)contextRef pageGroupRef:(WKPageGroupRef)pageGroupRef +{ + self = [super initWithFrame:frame]; + if (!self) + return nil; + + InitWebCoreSystemInterface(); + RunLoop::initializeMainRunLoop(); + + NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:frame + options:(NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect) + owner:self + userInfo:nil]; + [self addTrackingArea:trackingArea]; + [trackingArea release]; + + _data = [[WKViewData alloc] init]; + + _data->_pageClient = PageClientImpl::create(self); + _data->_page = toImpl(contextRef)->createWebPage(_data->_pageClient.get(), toImpl(pageGroupRef)); + _data->_page->initializeWebPage(); + + [self _registerDraggedTypes]; + + WebContext::statistics().wkViewCount++; + +#if !defined(BUILDING_ON_SNOW_LEOPARD) + NSData *remoteToken = (NSData *)WKAXRemoteTokenForElement(self); + CoreIPC::DataReference dataToken = CoreIPC::DataReference(reinterpret_cast<const uint8_t*>([remoteToken bytes]), [remoteToken length]); + _data->_page->sendAccessibilityPresenterToken(dataToken); +#endif + + return self; +} + +- (void)dealloc +{ + _data->_page->close(); + + [_data release]; + + WebContext::statistics().wkViewCount--; + + [super dealloc]; +} + +- (WKPageRef)pageRef +{ + return toAPI(_data->_page.get()); +} + +- (void)setDrawsBackground:(BOOL)drawsBackground +{ + _data->_page->setDrawsBackground(drawsBackground); +} + +- (BOOL)drawsBackground +{ + return _data->_page->drawsBackground(); +} + +- (void)setDrawsTransparentBackground:(BOOL)drawsTransparentBackground +{ + _data->_page->setDrawsTransparentBackground(drawsTransparentBackground); +} + +- (BOOL)drawsTransparentBackground +{ + return _data->_page->drawsTransparentBackground(); +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (BOOL)becomeFirstResponder +{ + NSSelectionDirection direction = [[self window] keyViewSelectionDirection]; + + _data->_inBecomeFirstResponder = true; + _data->_page->viewStateDidChange(WebPageProxy::ViewIsFocused); + _data->_inBecomeFirstResponder = false; + + if (direction != NSDirectSelection) + _data->_page->setInitialFocus(direction == NSSelectingNext); + + return YES; +} + +- (BOOL)resignFirstResponder +{ + _data->_inResignFirstResponder = true; + _data->_page->viewStateDidChange(WebPageProxy::ViewIsFocused); + _data->_inResignFirstResponder = false; + + return YES; +} + +- (BOOL)isFlipped +{ + return YES; +} + +- (void)setFrameSize:(NSSize)size +{ + [super setFrameSize:size]; + + if (!_data->_page->drawingArea()) + return; + + _data->_page->drawingArea()->setSize(IntSize(size)); +} + +- (void)_updateWindowAndViewFrames +{ + NSWindow *window = [self window]; + ASSERT(window); + + NSRect windowFrameInScreenCoordinates = [window frame]; + NSRect viewFrameInWindowCoordinates = [self convertRect:[self frame] toView:nil]; + NSPoint accessibilityPosition = [[self accessibilityAttributeValue:NSAccessibilityPositionAttribute] pointValue]; + + _data->_page->windowAndViewFramesChanged(enclosingIntRect(windowFrameInScreenCoordinates), enclosingIntRect(viewFrameInWindowCoordinates), IntPoint(accessibilityPosition)); +} + +- (void)renewGState +{ + // Hide the find indicator. + _data->_findIndicatorWindow = nullptr; + + // Update the view frame. + if ([self window]) + [self _updateWindowAndViewFrames]; + + [super renewGState]; +} + +typedef HashMap<SEL, String> SelectorNameMap; + +// Map selectors into Editor command names. +// This is not needed for any selectors that have the same name as the Editor command. +static const SelectorNameMap* createSelectorExceptionMap() +{ + SelectorNameMap* map = new HashMap<SEL, String>; + + map->add(@selector(insertNewlineIgnoringFieldEditor:), "InsertNewline"); + map->add(@selector(insertParagraphSeparator:), "InsertNewline"); + map->add(@selector(insertTabIgnoringFieldEditor:), "InsertTab"); + map->add(@selector(pageDown:), "MovePageDown"); + map->add(@selector(pageDownAndModifySelection:), "MovePageDownAndModifySelection"); + map->add(@selector(pageUp:), "MovePageUp"); + map->add(@selector(pageUpAndModifySelection:), "MovePageUpAndModifySelection"); + + return map; +} + +static String commandNameForSelector(SEL selector) +{ + // Check the exception map first. + static const SelectorNameMap* exceptionMap = createSelectorExceptionMap(); + SelectorNameMap::const_iterator it = exceptionMap->find(selector); + if (it != exceptionMap->end()) + return it->second; + + // Remove the trailing colon. + // No need to capitalize the command name since Editor command names are + // not case sensitive. + const char* selectorName = sel_getName(selector); + size_t selectorNameLength = strlen(selectorName); + if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':') + return String(); + return String(selectorName, selectorNameLength - 1); +} + +// Editing commands + +#define WEBCORE_COMMAND(command) - (void)command:(id)sender { _data->_page->executeEditCommand(commandNameForSelector(_cmd)); } + +WEBCORE_COMMAND(copy) +WEBCORE_COMMAND(cut) +WEBCORE_COMMAND(paste) +WEBCORE_COMMAND(delete) +WEBCORE_COMMAND(pasteAsPlainText) +WEBCORE_COMMAND(selectAll) +WEBCORE_COMMAND(takeFindStringFromSelection) + +#undef WEBCORE_COMMAND + +// Menu items validation + +static NSMenuItem *menuItem(id <NSValidatedUserInterfaceItem> item) +{ + if (![(NSObject *)item isKindOfClass:[NSMenuItem class]]) + return nil; + return (NSMenuItem *)item; +} + +static NSToolbarItem *toolbarItem(id <NSValidatedUserInterfaceItem> item) +{ + if (![(NSObject *)item isKindOfClass:[NSToolbarItem class]]) + return nil; + return (NSToolbarItem *)item; +} + +- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item +{ + SEL action = [item action]; + + if (action == @selector(showGuessPanel:)) { + if (NSMenuItem *menuItem = ::menuItem(item)) { + BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible]; + [menuItem setTitle:panelShowing + ? UI_STRING("Hide Spelling and Grammar", "menu item title") + : UI_STRING("Show Spelling and Grammar", "menu item title")]; + } + return _data->_page->selectionState().isContentEditable; + } + + if (action == @selector(checkSpelling:) || action == @selector(changeSpelling:)) + return _data->_page->selectionState().isContentEditable; + + if (action == @selector(toggleContinuousSpellChecking:)) { + bool enabled = TextChecker::isContinuousSpellCheckingAllowed(); + bool checked = enabled && TextChecker::state().isContinuousSpellCheckingEnabled; + [menuItem(item) setState:checked ? NSOnState : NSOffState]; + return enabled; + } + + if (action == @selector(toggleGrammarChecking:)) { + bool checked = TextChecker::state().isGrammarCheckingEnabled; + [menuItem(item) setState:checked ? NSOnState : NSOffState]; + return YES; + } + + if (action == @selector(toggleAutomaticSpellingCorrection:)) { + bool checked = TextChecker::state().isAutomaticSpellingCorrectionEnabled; + [menuItem(item) setState:checked ? NSOnState : NSOffState]; + return _data->_page->selectionState().isContentEditable; + } + + if (action == @selector(orderFrontSubstitutionsPanel:)) { + if (NSMenuItem *menuItem = ::menuItem(item)) { + BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible]; + [menuItem setTitle:panelShowing + ? UI_STRING("Hide Substitutions", "menu item title") + : UI_STRING("Show Substitutions", "menu item title")]; + } + return _data->_page->selectionState().isContentEditable; + } + + if (action == @selector(toggleSmartInsertDelete:)) { + bool checked = _data->_page->isSmartInsertDeleteEnabled(); + [menuItem(item) setState:checked ? NSOnState : NSOffState]; + return _data->_page->selectionState().isContentEditable; + } + + if (action == @selector(toggleAutomaticQuoteSubstitution:)) { + bool checked = TextChecker::state().isAutomaticQuoteSubstitutionEnabled; + [menuItem(item) setState:checked ? NSOnState : NSOffState]; + return _data->_page->selectionState().isContentEditable; + } + + if (action == @selector(toggleAutomaticDashSubstitution:)) { + bool checked = TextChecker::state().isAutomaticDashSubstitutionEnabled; + [menuItem(item) setState:checked ? NSOnState : NSOffState]; + return _data->_page->selectionState().isContentEditable; + } + + if (action == @selector(toggleAutomaticLinkDetection:)) { + bool checked = TextChecker::state().isAutomaticLinkDetectionEnabled; + [menuItem(item) setState:checked ? NSOnState : NSOffState]; + return _data->_page->selectionState().isContentEditable; + } + + if (action == @selector(toggleAutomaticTextReplacement:)) { + bool checked = TextChecker::state().isAutomaticTextReplacementEnabled; + [menuItem(item) setState:checked ? NSOnState : NSOffState]; + return _data->_page->selectionState().isContentEditable; + } + + if (action == @selector(uppercaseWord:) || action == @selector(lowercaseWord:) || action == @selector(capitalizeWord:)) + return _data->_page->selectionState().selectedRangeLength && _data->_page->selectionState().isContentEditable; + + if (action == @selector(stopSpeaking:)) + return [NSApp isSpeaking]; + + // Next, handle editor commands. Start by returning YES for anything that is not an editor command. + // Returning YES is the default thing to do in an AppKit validate method for any selector that is not recognized. + String commandName = commandNameForSelector([item action]); + if (!Editor::commandIsSupportedFromMenuOrKeyBinding(commandName)) + return YES; + + // Add this item to the vector of items for a given command that are awaiting validation. + pair<ValidationMap::iterator, bool> addResult = _data->_validationMap.add(commandName, ValidationVector()); + addResult.first->second.append(item); + if (addResult.second) { + // If we are not already awaiting validation for this command, start the asynchronous validation process. + // FIXME: Theoretically, there is a race here; when we get the answer it might be old, from a previous time + // we asked for the same command; there is no guarantee the answer is still valid. + // FIXME: The function called here should be renamed validateCommand because it is not specific to menu items. + _data->_page->validateMenuItem(commandName); + } + + // Treat as enabled until we get the result back from the web process and _setUserInterfaceItemState is called. + // FIXME <rdar://problem/8803459>: This means disabled items will flash enabled at first for a moment. + // But returning NO here would be worse; that would make keyboard commands such as command-C fail. + return YES; +} + +static void speakString(WKStringRef string, WKErrorRef error, void*) +{ + if (error) + return; + if (!string) + return; + + NSString *convertedString = toImpl(string)->string(); + [NSApp speakString:convertedString]; +} + +- (IBAction)startSpeaking:(id)sender +{ + _data->_page->getSelectionOrContentsAsString(StringCallback::create(0, speakString)); +} + +- (IBAction)stopSpeaking:(id)sender +{ + [NSApp stopSpeaking:sender]; +} + +- (IBAction)showGuessPanel:(id)sender +{ + NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker]; + if (!checker) { + LOG_ERROR("No NSSpellChecker"); + return; + } + + NSPanel *spellingPanel = [checker spellingPanel]; + if ([spellingPanel isVisible]) { + [spellingPanel orderOut:sender]; + return; + } + + _data->_page->advanceToNextMisspelling(true); + [spellingPanel orderFront:sender]; +} + +- (IBAction)checkSpelling:(id)sender +{ + _data->_page->advanceToNextMisspelling(false); +} + +- (void)changeSpelling:(id)sender +{ + NSString *word = [[sender selectedCell] stringValue]; + + _data->_page->changeSpellingToWord(word); +} + +- (IBAction)toggleContinuousSpellChecking:(id)sender +{ + bool spellCheckingEnabled = !TextChecker::state().isContinuousSpellCheckingEnabled; + TextChecker::setContinuousSpellCheckingEnabled(spellCheckingEnabled); + + _data->_page->process()->updateTextCheckerState(); + + if (!spellCheckingEnabled) + _data->_page->unmarkAllMisspellings(); +} + +- (IBAction)toggleGrammarChecking:(id)sender +{ + bool grammarCheckingEnabled = !TextChecker::state().isGrammarCheckingEnabled; + TextChecker::setGrammarCheckingEnabled(grammarCheckingEnabled); + + _data->_page->process()->updateTextCheckerState(); + + if (!grammarCheckingEnabled) + _data->_page->unmarkAllBadGrammar(); +} + +- (IBAction)toggleAutomaticSpellingCorrection:(id)sender +{ + TextChecker::setAutomaticSpellingCorrectionEnabled(!TextChecker::state().isAutomaticSpellingCorrectionEnabled); + + _data->_page->process()->updateTextCheckerState(); +} + +- (void)orderFrontSubstitutionsPanel:(id)sender +{ + NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker]; + if (!checker) { + LOG_ERROR("No NSSpellChecker"); + return; + } + + NSPanel *substitutionsPanel = [checker substitutionsPanel]; + if ([substitutionsPanel isVisible]) { + [substitutionsPanel orderOut:sender]; + return; + } + [substitutionsPanel orderFront:sender]; +} + +- (IBAction)toggleSmartInsertDelete:(id)sender +{ + _data->_page->setSmartInsertDeleteEnabled(!_data->_page->isSmartInsertDeleteEnabled()); +} + +- (BOOL)isAutomaticQuoteSubstitutionEnabled +{ + return TextChecker::state().isAutomaticQuoteSubstitutionEnabled; +} + +- (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag +{ + if (static_cast<bool>(flag) == TextChecker::state().isAutomaticQuoteSubstitutionEnabled) + return; + + TextChecker::setAutomaticQuoteSubstitutionEnabled(flag); + _data->_page->process()->updateTextCheckerState(); +} + +- (void)toggleAutomaticQuoteSubstitution:(id)sender +{ + TextChecker::setAutomaticQuoteSubstitutionEnabled(!TextChecker::state().isAutomaticQuoteSubstitutionEnabled); + _data->_page->process()->updateTextCheckerState(); +} + +- (BOOL)isAutomaticDashSubstitutionEnabled +{ + return TextChecker::state().isAutomaticDashSubstitutionEnabled; +} + +- (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag +{ + if (static_cast<bool>(flag) == TextChecker::state().isAutomaticDashSubstitutionEnabled) + return; + + TextChecker::setAutomaticDashSubstitutionEnabled(flag); + _data->_page->process()->updateTextCheckerState(); +} + +- (void)toggleAutomaticDashSubstitution:(id)sender +{ + TextChecker::setAutomaticDashSubstitutionEnabled(!TextChecker::state().isAutomaticDashSubstitutionEnabled); + _data->_page->process()->updateTextCheckerState(); +} + +- (BOOL)isAutomaticLinkDetectionEnabled +{ + return TextChecker::state().isAutomaticLinkDetectionEnabled; +} + +- (void)setAutomaticLinkDetectionEnabled:(BOOL)flag +{ + if (static_cast<bool>(flag) == TextChecker::state().isAutomaticLinkDetectionEnabled) + return; + + TextChecker::setAutomaticLinkDetectionEnabled(flag); + _data->_page->process()->updateTextCheckerState(); +} + +- (void)toggleAutomaticLinkDetection:(id)sender +{ + TextChecker::setAutomaticLinkDetectionEnabled(!TextChecker::state().isAutomaticLinkDetectionEnabled); + _data->_page->process()->updateTextCheckerState(); +} + +- (BOOL)isAutomaticTextReplacementEnabled +{ + return TextChecker::state().isAutomaticTextReplacementEnabled; +} + +- (void)setAutomaticTextReplacementEnabled:(BOOL)flag +{ + if (static_cast<bool>(flag) == TextChecker::state().isAutomaticTextReplacementEnabled) + return; + + TextChecker::setAutomaticTextReplacementEnabled(flag); + _data->_page->process()->updateTextCheckerState(); +} + +- (void)toggleAutomaticTextReplacement:(id)sender +{ + TextChecker::setAutomaticTextReplacementEnabled(!TextChecker::state().isAutomaticTextReplacementEnabled); + _data->_page->process()->updateTextCheckerState(); +} + +- (void)uppercaseWord:(id)sender +{ + _data->_page->uppercaseWord(); +} + +- (void)lowercaseWord:(id)sender +{ + _data->_page->lowercaseWord(); +} + +- (void)capitalizeWord:(id)sender +{ + _data->_page->capitalizeWord(); +} + +// Events + +// Override this so that AppKit will send us arrow keys as key down events so we can +// support them via the key bindings mechanism. +- (BOOL)_wantsKeyDownForEvent:(NSEvent *)event +{ + return YES; +} + +#define EVENT_HANDLER(Selector, Type) \ + - (void)Selector:(NSEvent *)theEvent \ + { \ + Web##Type##Event webEvent = WebEventFactory::createWeb##Type##Event(theEvent, self); \ + _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 + +#define MOUSE_EVENT_HANDLER(Selector) \ + - (void)Selector:(NSEvent *)theEvent \ + { \ + NSInputManager *currentInputManager = [NSInputManager currentInputManager]; \ + if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:theEvent]) \ + return; \ + WebMouseEvent webEvent = WebEventFactory::createWebMouseEvent(theEvent, self); \ + _data->_page->handleMouseEvent(webEvent); \ + } + +MOUSE_EVENT_HANDLER(mouseDown) +MOUSE_EVENT_HANDLER(mouseDragged) +MOUSE_EVENT_HANDLER(mouseUp) + +#undef MOUSE_EVENT_HANDLER + +- (void)doCommandBySelector:(SEL)selector +{ + 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); + NSString *text; + bool isFromInputMethod = _data->_page->selectionState().hasComposition; + + if (isAttributedString) { + 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; + + String eventText = text; + + 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)); + } +} + +- (BOOL)_handleStyleKeyEquivalent:(NSEvent *)event +{ + if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) != NSCommandKeyMask) + return NO; + + // Here we special case cmd+b and cmd+i but not cmd+u, for historic reason. + // This should not be changed, since it could break some Mac applications that + // rely on this inherent behavior. + // See https://bugs.webkit.org/show_bug.cgi?id=24943 + + NSString *string = [event characters]; + if ([string caseInsensitiveCompare:@"b"] == NSOrderedSame) { + _data->_page->executeEditCommand("ToggleBold"); + return YES; + } + if ([string caseInsensitiveCompare:@"i"] == NSOrderedSame) { + _data->_page->executeEditCommand("ToggleItalic"); + return YES; + } + + return NO; +} + +- (BOOL)performKeyEquivalent:(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]; + + BOOL eventWasSentToWebCore = (_data->_keyDownEventBeingResent == event); + + // Pass key combos through WebCore if there is a key binding available for + // this event. This lets web pages have a crack at intercepting key-modified keypresses. + // But don't do it if we have already handled the event. + // Pressing Esc results in a fake event being sent - don't pass it to WebCore. + if (!eventWasSentToWebCore && event == [NSApp currentEvent] && self == [[self window] firstResponder]) { + [_data->_keyDownEventBeingResent release]; + _data->_keyDownEventBeingResent = nil; + + _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(event, self)); + return YES; + } + + return [self _handleStyleKeyEquivalent:event] || [super performKeyEquivalent:event]; +} + +- (void)_setEventBeingResent:(NSEvent *)event +{ + _data->_keyDownEventBeingResent = [event retain]; +} + +- (Vector<KeypressCommand>&)_interceptKeyEvent:(NSEvent *)theEvent +{ + _data->_commandsList.clear(); + // interpretKeyEvents will trigger one or more calls to doCommandBySelector or setText + // that will populate the commandsList vector. + [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; + return _data->_commandsList; +} + +- (void)_getTextInputState:(unsigned)start selectionEnd:(unsigned)end underlines:(Vector<WebCore::CompositionUnderline>&)lines +{ + start = _data->_selectionStart; + end = _data->_selectionEnd; + lines = _data->_underlines; +} + +- (void)keyUp:(NSEvent *)theEvent +{ + _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, self)); +} + +- (void)keyDown:(NSEvent *)theEvent +{ + if (_data->_pluginComplexTextInputIdentifier) { + // Try feeding the keyboard event directly to the plug-in. + NSString *string = nil; + if ([[WKTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:theEvent string:&string]) { + if (string) + _data->_page->sendComplexTextInputToPlugin(_data->_pluginComplexTextInputIdentifier, string); + return; + } + } + + _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). + // If this is the case we should ignore the key down. + if (_data->_keyDownEventBeingResent == theEvent) { + [_data->_keyDownEventBeingResent release]; + _data->_keyDownEventBeingResent = nil; + [super keyDown:theEvent]; + return; + } + _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, self)); +} + +- (NSTextInputContext *)inputContext { + if (_data->_pluginComplexTextInputIdentifier) + return [[WKTextInputWindowController sharedTextInputWindowController] inputContext]; + + 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); +} + +- (BOOL)hasMarkedText +{ + LOG(TextInput, "hasMarkedText -> %u", _data->_page->selectionState().hasComposition); + return _data->_page->selectionState().hasComposition; +} + +- (void)unmarkText +{ + LOG(TextInput, "unmarkText"); + + _data->_commandsList.append(KeypressCommand("unmarkText")); +} + +- (NSArray *)validAttributesForMarkedText +{ + static NSArray *validAttributes; + if (!validAttributes) { + validAttributes = [[NSArray alloc] initWithObjects: + NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName, + NSMarkedClauseSegmentAttributeName, NSTextInputReplacementRangeAttributeName, 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. + // NSFontAttributeName, NSForegroundColorAttributeName, + // NSBackgroundColorAttributeName, NSLanguageAttributeName. + CFRetain(validAttributes); + } + LOG(TextInput, "validAttributesForMarkedText -> (...)"); + return validAttributes; +} + +static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnderline>& result) +{ + int length = [[string string] length]; + + int i = 0; + while (i < length) { + NSRange range; + NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)]; + + if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) { + Color color = Color::black; + if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName]) + color = colorFromNSColor([colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]); + result.append(CompositionUnderline(range.location, NSMaxRange(range), color, [style intValue] > 1)); + } + + i = range.location + range.length; + } +} + +- (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange +{ + BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString + + LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u)", isAttributedString ? [string string] : string, newSelRange.location, newSelRange.length); + + NSString *text = string; + + if (isAttributedString) { + text = [string string]; + extractUnderlines(string, _data->_underlines); + } + + _data->_commandsList.append(KeypressCommand("setMarkedText", text)); + _data->_selectionStart = newSelRange.location; + _data->_selectionEnd = NSMaxRange(newSelRange); +} + +- (NSRange)markedRange +{ + 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 +{ + // This is not implemented for now. Need to figure out how to serialize the attributed string across processes. + LOG(TextInput, "attributedSubstringFromRange"); + return nil; +} + +- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint +{ + NSWindow *window = [self window]; + + if (window) + thePoint = [window convertScreenToBase:thePoint]; + thePoint = [self convertPoint:thePoint fromView:nil]; // the point is relative to the main frame + + uint64_t result = _data->_page->characterIndexForPoint(IntPoint(thePoint)); + LOG(TextInput, "characterIndexForPoint:(%f, %f) -> %u", thePoint.x, thePoint.y, result); + return result; +} + +- (NSRect)firstRectForCharacterRange:(NSRange)theRange +{ + // 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). + if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0)) + theRange.length = 0; + + NSRect resultRect = _data->_page->firstRectForCharacterRange(theRange.location, theRange.length); + resultRect = [self convertRect:resultRect toView:nil]; + + NSWindow *window = [self window]; + if (window) + resultRect.origin = [window convertBaseToScreen:resultRect.origin]; + + 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; +} + +- (DragApplicationFlags)applicationFlags:(id <NSDraggingInfo>)draggingInfo +{ + uint32_t flags = 0; + if ([NSApp modalWindow]) + flags = DragApplicationIsModal; + if ([[self window] attachedSheet]) + flags |= DragApplicationHasAttachedSheet; + if ([draggingInfo draggingSource] == self) + flags |= DragApplicationIsSource; + if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) + flags |= DragApplicationIsCopyKeyDown; + return static_cast<DragApplicationFlags>(flags); +} + +- (NSDragOperation)draggingEntered:(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(DragControllerActionEntered, &dragData, [[draggingInfo draggingPasteboard] name]); + return NSDragOperationCopy; +} + +- (NSDragOperation)draggingUpdated:(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(DragControllerActionUpdated, &dragData, [[draggingInfo draggingPasteboard] name]); + return _data->_page->dragOperation(); +} + +- (void)draggingExited:(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(DragControllerActionExited, &dragData, [[draggingInfo draggingPasteboard] name]); + _data->_page->resetDragOperation(); +} + +- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)draggingInfo +{ + return YES; +} + +- (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]); + return YES; +} + +- (void)_updateWindowVisibility +{ + _data->_page->updateWindowIsVisible(![[self window] isMiniaturized]); +} + +- (BOOL)_ownsWindowGrowBox +{ + NSWindow* window = [self window]; + if (!window) + return NO; + + NSView *superview = [self superview]; + if (!superview) + return NO; + + NSRect growBoxRect = [window _growBoxRect]; + if (NSIsEmptyRect(growBoxRect)) + return NO; + + NSRect visibleRect = [self visibleRect]; + if (NSIsEmptyRect(visibleRect)) + return NO; + + NSRect visibleRectInWindowCoords = [self convertRect:visibleRect toView:nil]; + if (!NSIntersectsRect(growBoxRect, visibleRectInWindowCoords)) + return NO; + + return YES; +} + +- (BOOL)_updateGrowBoxForWindowFrameChange +{ + // Temporarily enable the resize indicator to make a the _ownsWindowGrowBox calculation work. + BOOL wasShowingIndicator = [[self window] showsResizeIndicator]; + [[self window] setShowsResizeIndicator:YES]; + + BOOL ownsGrowBox = [self _ownsWindowGrowBox]; + _data->_page->setWindowResizerSize(ownsGrowBox ? enclosingIntRect([[self window] _growBoxRect]).size() : IntSize()); + + // Once WebCore can draw the window resizer, this should read: + // if (wasShowingIndicator) + // [[self window] setShowsResizeIndicator:!ownsGrowBox]; + [[self window] setShowsResizeIndicator:wasShowingIndicator]; + + return ownsGrowBox; +} + +- (void)addWindowObserversForWindow:(NSWindow *)window +{ + if (window) { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidBecomeKey:) + name:NSWindowDidBecomeKeyNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidResignKey:) + name:NSWindowDidResignKeyNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidMiniaturize:) + name:NSWindowDidMiniaturizeNotification object:window]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidDeminiaturize:) + name:NSWindowDidDeminiaturizeNotification object:window]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowFrameDidChange:) + name:NSWindowDidMoveNotification object:window]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowFrameDidChange:) + name:NSWindowDidResizeNotification object:window]; + } +} + +- (void)removeWindowObservers +{ + NSWindow *window = [self window]; + if (!window) + return; + + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResignKeyNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidMiniaturizeNotification object:window]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidMoveNotification object:window]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResizeNotification object:window]; +} + +- (void)viewWillMoveToWindow:(NSWindow *)window +{ + if (window != [self window]) { + [self removeWindowObservers]; + [self addWindowObserversForWindow:window]; + } +} + +- (void)viewDidMoveToWindow +{ + // We want to make sure to update the active state while hidden, so if the view is about to become visible, we + // update the active state first and then make it visible. If the view is about to be hidden, we hide it first and then + // update the active state. + if ([self window]) { + _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive); + _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible | WebPageProxy::ViewIsInWindow); + [self _updateWindowVisibility]; + [self _updateWindowAndViewFrames]; + } else { + _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible); + _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive | WebPageProxy::ViewIsInWindow); + } + +} + +- (void)_windowDidBecomeKey:(NSNotification *)notification +{ + NSWindow *keyWindow = [notification object]; + if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet]) + _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive); +} + +- (void)_windowDidResignKey:(NSNotification *)notification +{ + NSWindow *formerKeyWindow = [notification object]; + if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) + _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive); +} + +- (void)_windowDidMiniaturize:(NSNotification *)notification +{ + [self _updateWindowVisibility]; +} + +- (void)_windowDidDeminiaturize:(NSNotification *)notification +{ + [self _updateWindowVisibility]; +} + +- (void)_windowFrameDidChange:(NSNotification *)notification +{ + [self _updateWindowAndViewFrames]; +} + +- (void)drawRect:(NSRect)rect +{ + LOG(View, "drawRect: x:%g, y:%g, width:%g, height:%g", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); + if (useNewDrawingArea()) { + if (DrawingAreaProxyImpl* drawingArea = static_cast<DrawingAreaProxyImpl*>(_data->_page->drawingArea())) { + CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]); + drawingArea->paint(context, enclosingIntRect(rect)); + } else if (_data->_page->drawsBackground()) { + [_data->_page->drawsTransparentBackground() ? [NSColor clearColor] : [NSColor whiteColor] set]; + NSRectFill(rect); + } + + _data->_page->didDraw(); + return; + } + + if (_data->_page->isValid() && _data->_page->drawingArea()) { + CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]); + _data->_page->drawingArea()->paint(IntRect(rect), context); + _data->_page->didDraw(); + } else if (_data->_page->drawsBackground()) { + [_data->_page->drawsTransparentBackground() ? [NSColor clearColor] : [NSColor whiteColor] set]; + NSRectFill(rect); + } +} + +- (BOOL)isOpaque +{ + return _data->_page->drawsBackground(); +} + +- (void)viewDidHide +{ + _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible); +} + +- (void)viewDidUnhide +{ + _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible); +} + +- (void)_setAccessibilityChildToken:(NSData *)data +{ +#if !defined(BUILDING_ON_SNOW_LEOPARD) + _data->_remoteAccessibilityChild = WKAXRemoteElementForToken((CFDataRef)data); + WKAXInitializeRemoteElementWithWindow(_data->_remoteAccessibilityChild.get(), [self window]); +#endif +} + +- (BOOL)accessibilityIsIgnored +{ + return NO; +} + +- (id)accessibilityHitTest:(NSPoint)point +{ + return _data->_remoteAccessibilityChild.get(); +} + +- (id)accessibilityAttributeValue:(NSString*)attribute +{ + if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { + if (!_data->_remoteAccessibilityChild) + return nil; + return [NSArray arrayWithObject:_data->_remoteAccessibilityChild.get()]; + } + if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) + return NSAccessibilityGroupRole; + if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) + return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, nil); + if ([attribute isEqualToString:NSAccessibilityParentAttribute]) + return NSAccessibilityUnignoredAncestor([self superview]); + if ([attribute isEqualToString:NSAccessibilityEnabledAttribute]) + return [NSNumber numberWithBool:YES]; + + return [super accessibilityAttributeValue:attribute]; +} + +- (NSView *)hitTest:(NSPoint)point +{ + NSView *hitView = [super hitTest:point]; +#if USE(ACCELERATED_COMPOSITING) + if (hitView && _data && hitView == _data->_layerHostingView) + hitView = self; +#endif + return hitView; +} + +- (NSInteger)conversationIdentifier +{ + return (NSInteger)self; +} + +static void setFrameBeingPrinted(NSPrintOperation *printOperation, WebFrameProxy* frame) +{ + RetainPtr<WebFrameWrapper> frameWrapper(AdoptNS, [[WebFrameWrapper alloc] initWithFrameProxy:frame]); + [[[printOperation printInfo] dictionary] setObject:frameWrapper.get() forKey:PrintedFrameKey]; +} + +static WebFrameProxy* frameBeingPrinted() +{ + return [[[[[NSPrintOperation currentOperation] printInfo] dictionary] objectForKey:PrintedFrameKey] webFrame]; +} + +- (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo forFrame:(WKFrameRef)frameRef +{ + LOG(View, "Creating an NSPrintOperation for frame '%s'", toImpl(frameRef)->url().utf8().data()); + NSPrintOperation *printOperation; + + // Only the top frame can currently contain a PDF view. + if (_data->_pdfViewController) { + ASSERT(toImpl(frameRef)->isMainFrame()); + printOperation = _data->_pdfViewController->makePrintOperation(printInfo); + } else + printOperation = [NSPrintOperation printOperationWithView:self printInfo:printInfo]; + + setFrameBeingPrinted(printOperation, toImpl(frameRef)); + return printOperation; +} + +- (BOOL)canChangeFrameLayout:(WKFrameRef)frameRef +{ + // PDF documents are already paginated, so we can't change them to add headers and footers. + return !toImpl(frameRef)->isMainFrame() || !_data->_pdfViewController; +} + +// Return the number of pages available for printing +- (BOOL)knowsPageRange:(NSRangePointer)range +{ + LOG(View, "knowsPageRange:"); + WebFrameProxy* frame = frameBeingPrinted(); + ASSERT(frame); + + if (frame->isMainFrame() && _data->_pdfViewController) + return [super knowsPageRange:range]; + + _data->_page->computePagesForPrinting(frame, PrintInfo([[NSPrintOperation currentOperation] printInfo]), _data->_printingPageRects, _data->_totalScaleFactorForPrinting); + + *range = NSMakeRange(1, _data->_printingPageRects.size()); + return YES; +} + +// Take over printing. AppKit applies incorrect clipping, and doesn't print pages beyond the first one. +- (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView +{ + // FIXME: This check isn't right for some non-printing cases, such as capturing into a buffer using cacheDisplayInRect:toBitmapImageRep:. + if ([NSGraphicsContext currentContextDrawingToScreen]) { + _data->_page->endPrinting(); + [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect rectIsVisibleRectForView:visibleView topView:topView]; + return; + } + + LOG(View, "Printing rect x:%g, y:%g, width:%g, height:%g", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); + + ASSERT(self == visibleView); + ASSERT(frameBeingPrinted()); + + WebFrameProxy* frame = frameBeingPrinted(); + ASSERT(frame); + + _data->_page->beginPrinting(frame, PrintInfo([[NSPrintOperation currentOperation] printInfo])); + + // FIXME: This is optimized for print preview. Get the whole document at once when actually printing. + Vector<uint8_t> pdfData; + _data->_page->drawRectToPDF(frame, IntRect(rect), pdfData); + + RetainPtr<CGDataProviderRef> pdfDataProvider(AdoptCF, CGDataProviderCreateWithData(0, pdfData.data(), pdfData.size(), 0)); + RetainPtr<CGPDFDocumentRef> pdfDocument(AdoptCF, CGPDFDocumentCreateWithProvider(pdfDataProvider.get())); + if (!pdfDocument) { + LOG_ERROR("Couldn't create a PDF document with data passed for printing"); + return; + } + + CGPDFPageRef pdfPage = CGPDFDocumentGetPage(pdfDocument.get(), 1); + if (!pdfPage) { + LOG_ERROR("Printing data doesn't have page 1"); + return; + } + + NSGraphicsContext *nsGraphicsContext = [NSGraphicsContext currentContext]; + CGContextRef context = static_cast<CGContextRef>([nsGraphicsContext graphicsPort]); + + CGContextSaveGState(context); + // Flip the destination. + CGContextScaleCTM(context, 1, -1); + CGContextTranslateCTM(context, 0, -rect.size.height); + CGContextDrawPDFPage(context, pdfPage); + CGContextRestoreGState(context); +} + +// FIXME 3491344: This is an AppKit-internal method that we need to override in order +// to get our shrink-to-fit to work with a custom pagination scheme. We can do this better +// if AppKit makes it SPI/API. +- (CGFloat)_provideTotalScaleFactorForPrintOperation:(NSPrintOperation *)printOperation +{ + return _data->_totalScaleFactorForPrinting; +} + +// Return the drawing rectangle for a particular page number +- (NSRect)rectForPage:(NSInteger)page +{ + WebFrameProxy* frame = frameBeingPrinted(); + ASSERT(frame); + + if (frame->isMainFrame() && _data->_pdfViewController) + return [super rectForPage:page]; + + LOG(View, "rectForPage:%d -> x %d, y %d, width %d, height %d\n", (int)page, _data->_printingPageRects[page - 1].x(), _data->_printingPageRects[page - 1].y(), _data->_printingPageRects[page - 1].width(), _data->_printingPageRects[page - 1].height()); + return _data->_printingPageRects[page - 1]; +} + +@end + +@implementation WKView (Internal) + +- (PassOwnPtr<WebKit::DrawingAreaProxy>)_createDrawingAreaProxy +{ + if (useNewDrawingArea()) + return DrawingAreaProxyImpl::create(_data->_page.get()); + + return ChunkedUpdateDrawingAreaProxy::create(self, _data->_page.get()); +} + +- (BOOL)_isFocused +{ + if (_data->_inBecomeFirstResponder) + return YES; + if (_data->_inResignFirstResponder) + return NO; + return [[self window] firstResponder] == self; +} + +- (void)_processDidCrash +{ + [self setNeedsDisplay:YES]; +} + +- (void)_didRelaunchProcess +{ + [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) + return; + [cursor set]; +} + +- (void)_setUserInterfaceItemState:(NSString *)commandName enabled:(BOOL)isEnabled state:(int)newState +{ + ValidationVector items = _data->_validationMap.take(commandName); + size_t size = items.size(); + for (size_t i = 0; i < size; ++i) { + ValidationItem item = items[i].get(); + [menuItem(item) setState:newState]; + [menuItem(item) setEnabled:isEnabled]; + [toolbarItem(item) setEnabled:isEnabled]; + // FIXME <rdar://problem/8803392>: If the item is neither a menu nor toolbar item, it will be left enabled. + } +} + +- (NSRect)_convertToDeviceSpace:(NSRect)rect +{ + return toDeviceSpace(rect, [self window]); +} + +- (NSRect)_convertToUserSpace:(NSRect)rect +{ + return toUserSpace(rect, [self window]); +} + +// Any non-zero value will do, but using something recognizable might help us debug some day. +#define TRACKING_RECT_TAG 0xBADFACE + +- (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside +{ + ASSERT(_data->_trackingRectOwner == nil); + _data->_trackingRectOwner = owner; + _data->_trackingRectUserData = data; + return TRACKING_RECT_TAG; +} + +- (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag +{ + ASSERT(tag == 0 || tag == TRACKING_RECT_TAG); + ASSERT(_data->_trackingRectOwner == nil); + _data->_trackingRectOwner = owner; + _data->_trackingRectUserData = data; + return TRACKING_RECT_TAG; +} + +- (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count +{ + ASSERT(count == 1); + ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG); + ASSERT(_data->_trackingRectOwner == nil); + _data->_trackingRectOwner = owner; + _data->_trackingRectUserData = userDataList[0]; + trackingNums[0] = TRACKING_RECT_TAG; +} + +- (void)removeTrackingRect:(NSTrackingRectTag)tag +{ + if (tag == 0) + return; + + if (_data && (tag == TRACKING_RECT_TAG)) { + _data->_trackingRectOwner = nil; + return; + } + + if (_data && (tag == _data->_lastToolTipTag)) { + [super removeTrackingRect:tag]; + _data->_lastToolTipTag = 0; + return; + } + + // If any other tracking rect is being removed, we don't know how it was created + // and it's possible there's a leak involved (see 3500217) + ASSERT_NOT_REACHED(); +} + +- (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count +{ + int i; + for (i = 0; i < count; ++i) { + int tag = tags[i]; + if (tag == 0) + continue; + ASSERT(tag == TRACKING_RECT_TAG); + if (_data != nil) { + _data->_trackingRectOwner = nil; + } + } +} + +- (void)_sendToolTipMouseExited +{ + // Nothing matters except window, trackingNumber, and userData. + NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited + location:NSMakePoint(0, 0) + modifierFlags:0 + timestamp:0 + windowNumber:[[self window] windowNumber] + context:NULL + eventNumber:0 + trackingNumber:TRACKING_RECT_TAG + userData:_data->_trackingRectUserData]; + [_data->_trackingRectOwner mouseExited:fakeEvent]; +} + +- (void)_sendToolTipMouseEntered +{ + // Nothing matters except window, trackingNumber, and userData. + NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered + location:NSMakePoint(0, 0) + modifierFlags:0 + timestamp:0 + windowNumber:[[self window] windowNumber] + context:NULL + eventNumber:0 + trackingNumber:TRACKING_RECT_TAG + userData:_data->_trackingRectUserData]; + [_data->_trackingRectOwner mouseEntered:fakeEvent]; +} + +- (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data +{ + return nsStringFromWebCoreString(_data->_page->toolTip()); +} + +- (void)_toolTipChangedFrom:(NSString *)oldToolTip to:(NSString *)newToolTip +{ + if (oldToolTip) + [self _sendToolTipMouseExited]; + + if (newToolTip && [newToolTip length] > 0) { + // See radar 3500217 for why we remove all tooltips rather than just the single one we created. + [self removeAllToolTips]; + NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000); + _data->_lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL]; + [self _sendToolTipMouseEntered]; + } +} + +- (void)_setFindIndicator:(PassRefPtr<FindIndicator>)findIndicator fadeOut:(BOOL)fadeOut +{ + if (!findIndicator) { + _data->_findIndicatorWindow = 0; + return; + } + + if (!_data->_findIndicatorWindow) + _data->_findIndicatorWindow = FindIndicatorWindow::create(self); + + _data->_findIndicatorWindow->setFindIndicator(findIndicator, fadeOut); +} + +#if USE(ACCELERATED_COMPOSITING) +- (void)_startAcceleratedCompositing:(CALayer *)rootLayer +{ + if (!_data->_layerHostingView) { + NSView *hostingView = [[NSView alloc] initWithFrame:[self bounds]]; +#if !defined(BUILDING_ON_LEOPARD) + [hostingView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; +#endif + + [self addSubview:hostingView]; + [hostingView release]; + _data->_layerHostingView = hostingView; + } + + // Make a container layer, which will get sized/positioned by AppKit and CA. + CALayer *viewLayer = [CALayer layer]; + +#ifndef NDEBUG + [viewLayer setName:@"hosting layer"]; +#endif + +#if defined(BUILDING_ON_LEOPARD) + // Turn off default animations. + NSNull *nullValue = [NSNull null]; + NSDictionary *actions = [NSDictionary dictionaryWithObjectsAndKeys: + nullValue, @"anchorPoint", + nullValue, @"bounds", + nullValue, @"contents", + nullValue, @"contentsRect", + nullValue, @"opacity", + nullValue, @"position", + nullValue, @"sublayerTransform", + nullValue, @"sublayers", + nullValue, @"transform", + nil]; + [viewLayer setStyle:[NSDictionary dictionaryWithObject:actions forKey:@"actions"]]; +#endif + +#if !defined(BUILDING_ON_LEOPARD) + // If we aren't in the window yet, we'll use the screen's scale factor now, and reset the scale + // via -viewDidMoveToWindow. + CGFloat scaleFactor = [self window] ? [[self window] userSpaceScaleFactor] : [[NSScreen mainScreen] userSpaceScaleFactor]; + [viewLayer setTransform:CATransform3DMakeScale(scaleFactor, scaleFactor, 1)]; +#endif + + [_data->_layerHostingView setLayer:viewLayer]; + [_data->_layerHostingView setWantsLayer:YES]; + + // Parent our root layer in the container layer + [viewLayer addSublayer:rootLayer]; +} + +- (void)_stopAcceleratedCompositing +{ + if (_data->_layerHostingView) { + [_data->_layerHostingView setLayer:nil]; + [_data->_layerHostingView setWantsLayer:NO]; + [_data->_layerHostingView removeFromSuperview]; + _data->_layerHostingView = nil; + } +} + +- (void)_switchToDrawingAreaTypeIfNecessary:(DrawingAreaInfo::Type)type +{ + DrawingAreaInfo::Type existingDrawingAreaType = _data->_page->drawingArea() ? _data->_page->drawingArea()->info().type : DrawingAreaInfo::None; + if (existingDrawingAreaType == type) + return; + + OwnPtr<DrawingAreaProxy> newDrawingArea; + switch (type) { + case DrawingAreaInfo::Impl: + case DrawingAreaInfo::None: + break; + case DrawingAreaInfo::ChunkedUpdate: { + newDrawingArea = ChunkedUpdateDrawingAreaProxy::create(self, _data->_page.get()); + break; + } + case DrawingAreaInfo::LayerBacked: { + newDrawingArea = LayerBackedDrawingAreaProxy::create(self, _data->_page.get()); + break; + } + } + + newDrawingArea->setSize(IntSize([self frame].size)); + + _data->_page->drawingArea()->detachCompositingContext(); + _data->_page->setDrawingArea(newDrawingArea.release()); +} + +- (void)_pageDidEnterAcceleratedCompositing +{ + [self _switchToDrawingAreaTypeIfNecessary:DrawingAreaInfo::LayerBacked]; +} + +- (void)_pageDidLeaveAcceleratedCompositing +{ + // FIXME: we may want to avoid flipping back to the non-layer-backed drawing area until the next page load, to avoid thrashing. + [self _switchToDrawingAreaTypeIfNecessary:DrawingAreaInfo::ChunkedUpdate]; +} +#endif // USE(ACCELERATED_COMPOSITING) + +- (void)_setComplexTextInputEnabled:(BOOL)complexTextInputEnabled pluginComplexTextInputIdentifier:(uint64_t)pluginComplexTextInputIdentifier +{ + BOOL inputSourceChanged = _data->_pluginComplexTextInputIdentifier; + + if (complexTextInputEnabled) { + // Check if we're already allowing text input for this plug-in. + if (pluginComplexTextInputIdentifier == _data->_pluginComplexTextInputIdentifier) + return; + + _data->_pluginComplexTextInputIdentifier = pluginComplexTextInputIdentifier; + + } else { + // Check if we got a request to disable complex text input for a plug-in that is not the current plug-in. + if (pluginComplexTextInputIdentifier != _data->_pluginComplexTextInputIdentifier) + return; + + _data->_pluginComplexTextInputIdentifier = 0; + } + + if (inputSourceChanged) { + // Inform the out of line window that the input source changed. + [[WKTextInputWindowController sharedTextInputWindowController] keyboardInputSourceChanged]; + } +} + +- (void)_setPageHasCustomRepresentation:(BOOL)pageHasCustomRepresentation +{ + _data->_pdfViewController = nullptr; + + if (pageHasCustomRepresentation) + _data->_pdfViewController = PDFViewController::create(self); +} + +- (void)_didFinishLoadingDataForCustomRepresentation:(const CoreIPC::DataReference&)dataReference +{ + ASSERT(_data->_pdfViewController); + + _data->_pdfViewController->setPDFDocumentData(_data->_page->mainFrame()->mimeType(), dataReference); +} + +- (double)_customRepresentationZoomFactor +{ + if (!_data->_pdfViewController) + return 1; + + return _data->_pdfViewController->zoomFactor(); +} + +- (void)_setCustomRepresentationZoomFactor:(double)zoomFactor +{ + if (!_data->_pdfViewController) + return; + + _data->_pdfViewController->setZoomFactor(zoomFactor); +} + +@end diff --git a/Source/WebKit2/UIProcess/API/mac/WKViewInternal.h b/Source/WebKit2/UIProcess/API/mac/WKViewInternal.h new file mode 100644 index 0000000..1c70b38 --- /dev/null +++ b/Source/WebKit2/UIProcess/API/mac/WKViewInternal.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "WKView.h" +#import <WebCore/Editor.h> +#import <WebCore/KeyboardEvent.h> + +namespace WebKit { + class DrawingAreaProxy; + class FindIndicator; +} + +@interface WKView (Internal) +- (PassOwnPtr<WebKit::DrawingAreaProxy>)_createDrawingAreaProxy; +- (BOOL)_isFocused; +- (void)_processDidCrash; +- (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; +- (void)_setEventBeingResent:(NSEvent *)event; +- (NSRect)_convertToDeviceSpace:(NSRect)rect; +- (NSRect)_convertToUserSpace:(NSRect)rect; +- (void)_setFindIndicator:(PassRefPtr<WebKit::FindIndicator>)findIndicator fadeOut:(BOOL)fadeOut; + +#if USE(ACCELERATED_COMPOSITING) +- (void)_startAcceleratedCompositing:(CALayer *)rootLayer; +- (void)_stopAcceleratedCompositing; +- (void)_pageDidEnterAcceleratedCompositing; +- (void)_pageDidLeaveAcceleratedCompositing; +#endif + +- (void)_setAccessibilityChildToken:(NSData *)data; +- (void)_setComplexTextInputEnabled:(BOOL)complexTextInputEnabled pluginComplexTextInputIdentifier:(uint64_t)pluginComplexTextInputIdentifier; + +- (void)_setPageHasCustomRepresentation:(BOOL)pageHasCustomRepresentation; +- (void)_didFinishLoadingDataForCustomRepresentation:(const CoreIPC::DataReference&)dataReference; +- (double)_customRepresentationZoomFactor; +- (void)_setCustomRepresentationZoomFactor:(double)zoomFactor; + +@end |