summaryrefslogtreecommitdiff
path: root/Source/WebKit2/WebProcess/Plugins/PDF/PDFPlugin.mm
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit2/WebProcess/Plugins/PDF/PDFPlugin.mm')
-rw-r--r--Source/WebKit2/WebProcess/Plugins/PDF/PDFPlugin.mm582
1 files changed, 582 insertions, 0 deletions
diff --git a/Source/WebKit2/WebProcess/Plugins/PDF/PDFPlugin.mm b/Source/WebKit2/WebProcess/Plugins/PDF/PDFPlugin.mm
new file mode 100644
index 000000000..c5a3469cd
--- /dev/null
+++ b/Source/WebKit2/WebProcess/Plugins/PDF/PDFPlugin.mm
@@ -0,0 +1,582 @@
+/*
+ * Copyright (C) 2009, 2011, 2012 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.
+ */
+
+#if ENABLE(PDFKIT_PLUGIN)
+
+#import "config.h"
+#import "PDFPlugin.h"
+
+#import "PDFKitImports.h"
+#import "PluginView.h"
+#import "ShareableBitmap.h"
+#import "WebEvent.h"
+#import "WebEventConversion.h"
+#import <PDFKit/PDFKit.h>
+#import <QuartzCore/QuartzCore.h>
+#import <WebCore/ArchiveResource.h>
+#import <WebCore/Chrome.h>
+#import <WebCore/DocumentLoader.h>
+#import <WebCore/FocusController.h>
+#import <WebCore/Frame.h>
+#import <WebCore/FrameView.h>
+#import <WebCore/GraphicsContext.h>
+#import <WebCore/HTTPHeaderMap.h>
+#import <WebCore/LocalizedStrings.h>
+#import <WebCore/Page.h>
+#import <WebCore/Pasteboard.h>
+#import <WebCore/PluginData.h>
+#import <WebCore/RenderBoxModelObject.h>
+#import <WebCore/ScrollAnimator.h>
+#import <WebCore/ScrollbarTheme.h>
+
+@protocol PDFLayerControllerDelegate <NSObject>
+
+- (void)updateScrollPosition:(CGPoint)newPosition;
+- (void)writeItemsToPasteboard:(NSArray *)items withTypes:(NSArray *)types;
+- (void)showDefinitionForAttributedString:(NSAttributedString *)string atPoint:(CGPoint)point;
+- (void)performWebSearch:(NSString *)string;
+- (void)openWithPreview;
+- (void)saveToPDF;
+
+@end
+
+@interface PDFLayerController : NSObject
+@end
+
+@interface PDFLayerController (Details)
+
+@property (retain) CALayer *parentLayer;
+@property (retain) PDFDocument *document;
+@property (retain) id<PDFLayerControllerDelegate> delegate;
+
+- (void)setFrameSize:(CGSize)size;
+
+- (void)setDisplayMode:(int)mode;
+- (void)setDisplaysPageBreaks:(BOOL)pageBreaks;
+
+- (CGFloat)tileScaleFactor;
+- (void)setTileScaleFactor:(CGFloat)scaleFactor;
+
+- (CGSize)contentSize;
+- (CGSize)contentSizeRespectingZoom;
+
+- (void)snapshotInContext:(CGContextRef)context;
+
+- (void)magnifyWithMagnification:(CGFloat)magnification atPoint:(CGPoint)point immediately:(BOOL)immediately;
+
+- (CGPoint)scrollPosition;
+- (void)setScrollPosition:(CGPoint)newPosition;
+- (void)scrollWithDelta:(CGSize)delta;
+
+- (void)mouseDown:(NSEvent *)event;
+- (void)mouseMoved:(NSEvent *)event;
+- (void)mouseUp:(NSEvent *)event;
+- (void)mouseDragged:(NSEvent *)event;
+- (void)mouseEntered:(NSEvent *)event;
+- (void)mouseExited:(NSEvent *)event;
+
+- (NSArray *)findString:(NSString *)string caseSensitive:(BOOL)isCaseSensitive highlightMatches:(BOOL)shouldHighlightMatches;
+
+- (id)currentSelection;
+- (void)copySelection;
+- (void)selectAll;
+
+- (bool)keyDown:(NSEvent *)event;
+
+- (void)setHUDEnabled:(BOOL)enabled;
+- (BOOL)hudEnabled;
+
+@end
+
+using namespace WebCore;
+
+@interface WKPDFPluginScrollbarLayer : CALayer
+{
+ WebKit::PDFPlugin* _pdfPlugin;
+}
+
+@property (assign) WebKit::PDFPlugin* pdfPlugin;
+
+@end
+
+@implementation WKPDFPluginScrollbarLayer
+
+@synthesize pdfPlugin=_pdfPlugin;
+
+- (id)initWithPDFPlugin:(WebKit::PDFPlugin *)plugin
+{
+ if (!(self = [super init]))
+ return nil;
+
+ _pdfPlugin = plugin;
+
+ return self;
+}
+
+- (id<CAAction>)actionForKey:(NSString *)key
+{
+ return nil;
+}
+
+- (void)drawInContext:(CGContextRef)ctx
+{
+ _pdfPlugin->paintControlForLayerInContext(self, ctx);
+}
+
+@end
+
+@interface WKPDFLayerControllerDelegate : NSObject<PDFLayerControllerDelegate>
+{
+ WebKit::PDFPlugin* _pdfPlugin;
+}
+
+@property (assign) WebKit::PDFPlugin* pdfPlugin;
+
+@end
+
+@implementation WKPDFLayerControllerDelegate
+
+@synthesize pdfPlugin=_pdfPlugin;
+
+- (id)initWithPDFPlugin:(WebKit::PDFPlugin *)plugin
+{
+ if (!(self = [super init]))
+ return nil;
+
+ _pdfPlugin = plugin;
+
+ return self;
+}
+
+- (void)updateScrollPosition:(CGPoint)newPosition
+{
+ _pdfPlugin->notifyScrollPositionChanged(IntPoint(newPosition));
+}
+
+- (void)writeItemsToPasteboard:(NSArray *)items withTypes:(NSArray *)types
+{
+ // FIXME: Handle types other than plain text.
+
+ for (NSUInteger i = 0, count = items.count; i < count; ++i) {
+ NSString *type = [types objectAtIndex:i];
+ if ([type isEqualToString:NSStringPboardType] || [type isEqualToString:NSPasteboardTypeString]) {
+ RetainPtr<NSString> plainTextString(AdoptNS, [[NSString alloc] initWithData:[items objectAtIndex:i] encoding:NSUTF8StringEncoding]);
+ Pasteboard::generalPasteboard()->writePlainText(plainTextString.get(), Pasteboard::CannotSmartReplace);
+ }
+ }
+}
+
+- (void)showDefinitionForAttributedString:(NSAttributedString *)string atPoint:(CGPoint)point
+{
+ // FIXME: Implement.
+}
+
+- (void)performWebSearch:(NSString *)string
+{
+ // FIXME: Implement.
+}
+
+- (void)openWithPreview
+{
+ // FIXME: Implement.
+}
+
+- (void)saveToPDF
+{
+ // FIXME: Implement.
+}
+
+@end
+
+namespace WebKit {
+
+PassRefPtr<PDFPlugin> PDFPlugin::create(WebFrame* frame)
+{
+ return adoptRef(new PDFPlugin(frame));
+}
+
+PDFPlugin::PDFPlugin(WebFrame* frame)
+ : SimplePDFPlugin(frame)
+ , m_containerLayer(AdoptNS, [[CALayer alloc] init])
+ , m_contentLayer(AdoptNS, [[CALayer alloc] init])
+ , m_scrollCornerLayer(AdoptNS, [[WKPDFPluginScrollbarLayer alloc] initWithPDFPlugin:this])
+ , m_pdfLayerController(AdoptNS, [[pdfLayerControllerClass() alloc] init])
+ , m_pdfLayerControllerDelegate(AdoptNS, [[WKPDFLayerControllerDelegate alloc] initWithPDFPlugin:this])
+{
+ m_pdfLayerController.get().delegate = m_pdfLayerControllerDelegate.get();
+ m_pdfLayerController.get().parentLayer = m_contentLayer.get();
+
+ [m_containerLayer.get() addSublayer:m_contentLayer.get()];
+ [m_containerLayer.get() addSublayer:m_scrollCornerLayer.get()];
+}
+
+PDFPlugin::~PDFPlugin()
+{
+}
+
+void PDFPlugin::updateScrollbars()
+{
+ SimplePDFPlugin::updateScrollbars();
+
+ if (m_verticalScrollbarLayer) {
+ m_verticalScrollbarLayer.get().frame = verticalScrollbar()->frameRect();
+ [m_verticalScrollbarLayer.get() setNeedsDisplay];
+ }
+
+ if (m_horizontalScrollbarLayer) {
+ m_horizontalScrollbarLayer.get().frame = horizontalScrollbar()->frameRect();
+ [m_horizontalScrollbarLayer.get() setNeedsDisplay];
+ }
+
+ if (m_scrollCornerLayer) {
+ m_scrollCornerLayer.get().frame = scrollCornerRect();
+ [m_scrollCornerLayer.get() setNeedsDisplay];
+ }
+}
+
+PassRefPtr<Scrollbar> PDFPlugin::createScrollbar(ScrollbarOrientation orientation)
+{
+ RefPtr<Scrollbar> widget = Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar);
+ if (orientation == HorizontalScrollbar) {
+ m_horizontalScrollbarLayer.adoptNS([[WKPDFPluginScrollbarLayer alloc] initWithPDFPlugin:this]);
+ [m_containerLayer.get() addSublayer:m_horizontalScrollbarLayer.get()];
+
+ didAddHorizontalScrollbar(widget.get());
+ } else {
+ m_verticalScrollbarLayer.adoptNS([[WKPDFPluginScrollbarLayer alloc] initWithPDFPlugin:this]);
+ [m_containerLayer.get() addSublayer:m_verticalScrollbarLayer.get()];
+
+ didAddVerticalScrollbar(widget.get());
+ }
+ pluginView()->frame()->view()->addChild(widget.get());
+ return widget.release();
+}
+
+void PDFPlugin::destroyScrollbar(ScrollbarOrientation orientation)
+{
+ SimplePDFPlugin::destroyScrollbar(orientation);
+
+ if (orientation == HorizontalScrollbar) {
+ [m_horizontalScrollbarLayer.get() removeFromSuperlayer];
+ m_horizontalScrollbarLayer = 0;
+ } else {
+ [m_verticalScrollbarLayer.get() removeFromSuperlayer];
+ m_verticalScrollbarLayer = 0;
+ }
+}
+
+void PDFPlugin::pdfDocumentDidLoad()
+{
+ addArchiveResource();
+
+ RetainPtr<PDFDocument> document(AdoptNS, [[pdfDocumentClass() alloc] initWithData:(NSData *)data().get()]);
+
+ setPDFDocument(document);
+
+ [m_pdfLayerController.get() setFrameSize:size()];
+ m_pdfLayerController.get().document = document.get();
+
+ pluginView()->setPageScaleFactor([m_pdfLayerController.get() tileScaleFactor], IntPoint());
+
+ calculateSizes();
+ updateScrollbars();
+
+ controller()->invalidate(IntRect(IntPoint(), size()));
+
+ runScriptsInPDFDocument();
+}
+
+void PDFPlugin::calculateSizes()
+{
+ // FIXME: This should come straight from PDFKit.
+ computePageBoxes();
+
+ setPDFDocumentSize(IntSize([m_pdfLayerController.get() contentSizeRespectingZoom]));
+}
+
+void PDFPlugin::destroy()
+{
+ m_pdfLayerController.get().delegate = 0;
+
+ if (webFrame()) {
+ if (FrameView* frameView = webFrame()->coreFrame()->view())
+ frameView->removeScrollableArea(this);
+ }
+
+ destroyScrollbar(HorizontalScrollbar);
+ destroyScrollbar(VerticalScrollbar);
+
+ [m_scrollCornerLayer.get() removeFromSuperlayer];
+ [m_contentLayer.get() removeFromSuperlayer];
+}
+
+void PDFPlugin::paint(GraphicsContext* graphicsContext, const IntRect& dirtyRect)
+{
+}
+
+void PDFPlugin::paintControlForLayerInContext(CALayer *layer, CGContextRef context)
+{
+ GraphicsContext graphicsContext(context);
+ GraphicsContextStateSaver stateSaver(graphicsContext);
+
+ graphicsContext.setIsCALayerContext(true);
+
+ if (layer == m_scrollCornerLayer) {
+ IntRect scrollCornerRect = this->scrollCornerRect();
+ graphicsContext.translate(-scrollCornerRect.x(), -scrollCornerRect.y());
+ ScrollbarTheme::theme()->paintScrollCorner(0, &graphicsContext, scrollCornerRect);
+ return;
+ }
+
+ Scrollbar* scrollbar = 0;
+
+ if (layer == m_verticalScrollbarLayer)
+ scrollbar = verticalScrollbar();
+ else if (layer == m_horizontalScrollbarLayer)
+ scrollbar = horizontalScrollbar();
+
+ if (!scrollbar)
+ return;
+
+ graphicsContext.translate(-scrollbar->x(), -scrollbar->y());
+ scrollbar->paint(&graphicsContext, scrollbar->frameRect());
+}
+
+PassRefPtr<ShareableBitmap> PDFPlugin::snapshot()
+{
+ if (size().isEmpty())
+ return 0;
+
+ // FIXME: Support non-1 page/deviceScaleFactor.
+ IntSize backingStoreSize = size();
+
+ RefPtr<ShareableBitmap> bitmap = ShareableBitmap::createShareable(backingStoreSize, ShareableBitmap::SupportsAlpha);
+ OwnPtr<GraphicsContext> context = bitmap->createGraphicsContext();
+
+ context->scale(FloatSize(1, -1));
+ context->translate(0, -size().height());
+
+ [m_pdfLayerController.get() snapshotInContext:context->platformContext()];
+
+ return bitmap.release();
+}
+
+PlatformLayer* PDFPlugin::pluginLayer()
+{
+ return m_containerLayer.get();
+}
+
+void PDFPlugin::geometryDidChange(const IntSize& pluginSize, const IntRect&, const AffineTransform& pluginToRootViewTransform)
+{
+ if (size() == pluginSize && pluginView()->pageScaleFactor() == [m_pdfLayerController.get() tileScaleFactor])
+ return;
+
+ setSize(pluginSize);
+ m_rootViewToPluginTransform = pluginToRootViewTransform.inverse();
+ [m_pdfLayerController.get() setFrameSize:pluginSize];
+
+ [CATransaction begin];
+ [CATransaction setDisableActions:YES];
+ CATransform3D transform = CATransform3DMakeScale(1, -1, 1);
+
+ CGFloat magnification = pluginView()->pageScaleFactor() - [m_pdfLayerController.get() tileScaleFactor];
+
+ // FIXME: Instead of m_lastMousePoint, we should use the zoom origin from PluginView::setPageScaleFactor.
+ [m_pdfLayerController.get() magnifyWithMagnification:magnification atPoint:m_lastMousePoint immediately:YES];
+ [m_contentLayer.get() setSublayerTransform:CATransform3DTranslate(transform, 0, -pluginSize.height(), 0)];
+ [CATransaction commit];
+
+ calculateSizes();
+ updateScrollbars();
+}
+
+static NSUInteger modifierFlagsFromWebEvent(const WebEvent& event)
+{
+ return (event.shiftKey() ? NSShiftKeyMask : 0)
+ | (event.controlKey() ? NSControlKeyMask : 0)
+ | (event.altKey() ? NSAlternateKeyMask : 0)
+ | (event.metaKey() ? NSCommandKeyMask : 0);
+}
+
+static NSEventType eventTypeFromWebEvent(const WebEvent& event, bool mouseButtonIsDown)
+{
+ switch (event.type()) {
+ case WebEvent::KeyDown:
+ return NSKeyDown;
+ case WebEvent::KeyUp:
+ return NSKeyUp;
+
+ case WebEvent::MouseDown:
+ switch (static_cast<const WebMouseEvent&>(event).button()) {
+ case WebMouseEvent::LeftButton:
+ return NSLeftMouseDown;
+ case WebMouseEvent::RightButton:
+ return NSRightMouseDown;
+ default:
+ return 0;
+ }
+ break;
+ case WebEvent::MouseUp:
+ switch (static_cast<const WebMouseEvent&>(event).button()) {
+ case WebMouseEvent::LeftButton:
+ return NSLeftMouseUp;
+ case WebMouseEvent::RightButton:
+ return NSRightMouseUp;
+ default:
+ return 0;
+ }
+ break;
+ case WebEvent::MouseMove:
+ if (mouseButtonIsDown) {
+ switch (static_cast<const WebMouseEvent&>(event).button()) {
+ case WebMouseEvent::LeftButton:
+ return NSLeftMouseDragged;
+ case WebMouseEvent::RightButton:
+ return NSRightMouseDragged;
+ default:
+ return 0;
+ }
+ } else
+ return NSMouseMoved;
+ break;
+
+ default:
+ return 0;
+ }
+}
+
+bool PDFPlugin::handleMouseEvent(const WebMouseEvent& event)
+{
+ static bool mouseButtonIsDown;
+
+ IntPoint mousePosition = event.position();
+
+ // FIXME: Forward mouse events to the appropriate scrollbar.
+ if (IntRect(m_verticalScrollbarLayer.get().frame).contains(mousePosition)
+ || IntRect(m_horizontalScrollbarLayer.get().frame).contains(mousePosition)
+ || IntRect(m_scrollCornerLayer.get().frame).contains(mousePosition))
+ return false;
+
+ IntPoint positionInPDFView(mousePosition);
+ positionInPDFView = m_rootViewToPluginTransform.mapPoint(positionInPDFView);
+ positionInPDFView.setY(size().height() - positionInPDFView.y());
+
+ m_lastMousePoint = positionInPDFView;
+
+ NSEventType eventType = eventTypeFromWebEvent(event, mouseButtonIsDown);
+
+ if (!eventType)
+ return false;
+
+ NSUInteger modifierFlags = modifierFlagsFromWebEvent(event);
+
+ NSEvent *fakeEvent = [NSEvent mouseEventWithType:eventType location:positionInPDFView modifierFlags:modifierFlags timestamp:0 windowNumber:0 context:nil eventNumber:0 clickCount:event.clickCount() pressure:0];
+
+ switch (event.type()) {
+ case WebEvent::MouseMove:
+ if (mouseButtonIsDown)
+ [m_pdfLayerController.get() mouseDragged:fakeEvent];
+ else
+ [m_pdfLayerController.get() mouseMoved:fakeEvent];
+ mouseMovedInContentArea();
+ return true;
+ case WebEvent::MouseDown: {
+ mouseButtonIsDown = true;
+ [m_pdfLayerController.get() mouseDown:fakeEvent];
+ return true;
+ }
+ case WebEvent::MouseUp: {
+ [m_pdfLayerController.get() mouseUp:fakeEvent];
+ mouseButtonIsDown = false;
+ PlatformMouseEvent platformEvent = platform(event);
+ return true;
+ }
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool PDFPlugin::handleKeyboardEvent(const WebKeyboardEvent& event)
+{
+ NSEventType eventType = eventTypeFromWebEvent(event, false);
+ NSUInteger modifierFlags = modifierFlagsFromWebEvent(event);
+
+ NSEvent *fakeEvent = [NSEvent keyEventWithType:eventType location:NSZeroPoint modifierFlags:modifierFlags timestamp:0 windowNumber:0 context:0 characters:event.text() charactersIgnoringModifiers:event.unmodifiedText() isARepeat:event.isAutoRepeat() keyCode:event.nativeVirtualKeyCode()];
+
+ switch (event.type()) {
+ case WebEvent::KeyDown:
+ return [m_pdfLayerController.get() keyDown:fakeEvent];
+ default:
+ return false;
+ }
+
+ return false;
+}
+
+bool PDFPlugin::handleEditingCommand(const String& commandName, const String& argument)
+{
+ if (commandName == "copy")
+ [m_pdfLayerController.get() copySelection];
+ else if (commandName == "selectAll")
+ [m_pdfLayerController.get() selectAll];
+
+ return true;
+}
+
+bool PDFPlugin::isEditingCommandEnabled(const String& commandName)
+{
+ if (commandName == "copy")
+ return [m_pdfLayerController.get() currentSelection];
+
+ if (commandName == "selectAll")
+ return true;
+
+ return false;
+}
+
+void PDFPlugin::setScrollOffset(const IntPoint& offset)
+{
+ SimplePDFPlugin::setScrollOffset(offset);
+ [m_pdfLayerController.get() setScrollPosition:offset];
+}
+
+void PDFPlugin::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
+{
+ if (scrollbar == horizontalScrollbar())
+ [m_horizontalScrollbarLayer.get() setNeedsDisplay];
+ else if (scrollbar == verticalScrollbar())
+ [m_verticalScrollbarLayer.get() setNeedsDisplay];
+}
+
+void PDFPlugin::invalidateScrollCornerRect(const IntRect& rect)
+{
+ [m_scrollCornerLayer.get() setNeedsDisplay];
+}
+
+} // namespace WebKit
+
+#endif // ENABLE(PDFKIT_PLUGIN)