1/*
2 * Copyright (C) 2009, 2011, 2012 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#import "config.h"
27#import "PDFPlugin.h"
28
29#if ENABLE(PDFKIT_PLUGIN)
30
31#import "ArgumentCoders.h"
32#import "AttributedString.h"
33#import "DataReference.h"
34#import "DictionaryPopupInfo.h"
35#import "PDFAnnotationTextWidgetDetails.h"
36#import "PDFKitImports.h"
37#import "PDFLayerControllerDetails.h"
38#import "PDFPluginAnnotation.h"
39#import "PDFPluginPasswordField.h"
40#import "PluginView.h"
41#import "WKAccessibilityWebPageObjectMac.h"
42#import "WKPageFindMatchesClient.h"
43#import "WebContextMessages.h"
44#import "WebCoreArgumentCoders.h"
45#import "WebEvent.h"
46#import "WebEventConversion.h"
47#import "WebPage.h"
48#import "WebPageProxyMessages.h"
49#import "WebProcess.h"
50#import <JavaScriptCore/JSContextRef.h>
51#import <JavaScriptCore/JSObjectRef.h>
52#import <JavaScriptCore/JSStringRef.h>
53#import <JavaScriptCore/JSStringRefCF.h>
54#import <PDFKit/PDFKit.h>
55#import <QuartzCore/QuartzCore.h>
56#import <WebCore/ArchiveResource.h>
57#import <WebCore/Chrome.h>
58#import <WebCore/Cursor.h>
59#import <WebCore/DocumentLoader.h>
60#import <WebCore/FocusController.h>
61#import <WebCore/FormState.h>
62#import <WebCore/Frame.h>
63#import <WebCore/FrameLoader.h>
64#import <WebCore/FrameView.h>
65#import <WebCore/GraphicsContext.h>
66#import <WebCore/HTMLElement.h>
67#import <WebCore/HTMLFormElement.h>
68#import <WebCore/LocalizedStrings.h>
69#import <WebCore/MouseEvent.h>
70#import <WebCore/Page.h>
71#import <WebCore/Pasteboard.h>
72#import <WebCore/PluginData.h>
73#import <WebCore/PluginDocument.h>
74#import <WebCore/RenderBoxModelObject.h>
75#import <WebCore/ScrollbarTheme.h>
76#import <WebCore/Settings.h>
77#import <WebCore/UUID.h>
78#import <WebKitSystemInterface.h>
79#import <wtf/CurrentTime.h>
80
81using namespace WebCore;
82
83// Set overflow: hidden on the annotation container so <input> elements scrolled out of view don't show
84// scrollbars on the body. We can't add annotations directly to the body, because overflow: hidden on the body
85// will break rubber-banding.
86static const char* annotationStyle =
87"#annotationContainer {"
88"    overflow: hidden; "
89"    position: absolute; "
90"    pointer-events: none; "
91"    top: 0; "
92"    left: 0; "
93"    right: 0; "
94"    bottom: 0; "
95"    display: -webkit-box; "
96"    -webkit-box-align: center; "
97"    -webkit-box-pack: center; "
98"} "
99".annotation { "
100"    position: absolute; "
101"    pointer-events: auto; "
102"} "
103"textarea.annotation { "
104"    resize: none; "
105"} "
106"input.annotation[type='password'] { "
107"    position: static; "
108"    width: 200px; "
109"    margin-top: 100px; "
110"} ";
111
112// In non-continuous modes, a single scroll event with a magnitude of >= 20px
113// will jump to the next or previous page, to match PDFKit behavior.
114static const int defaultScrollMagnitudeThresholdForPageFlip = 20;
115
116@interface WKPDFPluginAccessibilityObject : NSObject {
117    PDFLayerController *_pdfLayerController;
118    NSObject *_parent;
119    WebKit::PDFPlugin* _pdfPlugin;
120}
121
122@property (assign) PDFLayerController *pdfLayerController;
123@property (assign) NSObject *parent;
124@property (assign) WebKit::PDFPlugin* pdfPlugin;
125
126- (id)initWithPDFPlugin:(WebKit::PDFPlugin *)plugin;
127
128@end
129
130@implementation WKPDFPluginAccessibilityObject
131
132@synthesize pdfLayerController=_pdfLayerController;
133@synthesize parent=_parent;
134@synthesize pdfPlugin=_pdfPlugin;
135
136- (id)initWithPDFPlugin:(WebKit::PDFPlugin *)plugin
137{
138    if (!(self = [super init]))
139        return nil;
140
141    _pdfPlugin = plugin;
142
143    return self;
144}
145
146- (BOOL)accessibilityIsIgnored
147{
148    return NO;
149}
150
151- (id)accessibilityAttributeValue:(NSString *)attribute
152{
153    if ([attribute isEqualToString:NSAccessibilityParentAttribute])
154        return _parent;
155    if ([attribute isEqualToString:NSAccessibilityValueAttribute])
156        return [_pdfLayerController accessibilityValueAttribute];
157    if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute])
158        return [_pdfLayerController accessibilitySelectedTextAttribute];
159    if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute])
160        return [_pdfLayerController accessibilitySelectedTextRangeAttribute];
161    if ([attribute isEqualToString:NSAccessibilityNumberOfCharactersAttribute])
162        return [_pdfLayerController accessibilityNumberOfCharactersAttribute];
163    if ([attribute isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute])
164        return [_pdfLayerController accessibilityVisibleCharacterRangeAttribute];
165    if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute])
166        return [_parent accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute];
167    if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
168        return [_pdfLayerController accessibilityRoleAttribute];
169    if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
170        return [_pdfLayerController accessibilityRoleDescriptionAttribute];
171    if ([attribute isEqualToString:NSAccessibilityWindowAttribute])
172        return [_parent accessibilityAttributeValue:NSAccessibilityWindowAttribute];
173    if ([attribute isEqualToString:NSAccessibilitySizeAttribute])
174        return [NSValue valueWithSize:_pdfPlugin->boundsOnScreen().size()];
175    if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
176        return [_parent accessibilityAttributeValue:NSAccessibilityFocusedAttribute];
177    if ([attribute isEqualToString:NSAccessibilityEnabledAttribute])
178        return [_parent accessibilityAttributeValue:NSAccessibilityEnabledAttribute];
179    if ([attribute isEqualToString:NSAccessibilityPositionAttribute])
180        return [NSValue valueWithPoint:_pdfPlugin->boundsOnScreen().location()];
181
182    return 0;
183}
184
185- (id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter
186{
187    if ([attribute isEqualToString:NSAccessibilityBoundsForRangeParameterizedAttribute]) {
188        NSRect boundsInPDFViewCoordinates = [[_pdfLayerController accessibilityBoundsForRangeAttributeForParameter:parameter] rectValue];
189        NSRect boundsInScreenCoordinates = _pdfPlugin->convertFromPDFViewToScreen(boundsInPDFViewCoordinates);
190        return [NSValue valueWithRect:boundsInScreenCoordinates];
191    }
192
193    if ([attribute isEqualToString:NSAccessibilityLineForIndexParameterizedAttribute])
194        return [_pdfLayerController accessibilityLineForIndexAttributeForParameter:parameter];
195    if ([attribute isEqualToString:NSAccessibilityRangeForLineParameterizedAttribute])
196        return [_pdfLayerController accessibilityRangeForLineAttributeForParameter:parameter];
197    if ([attribute isEqualToString:NSAccessibilityStringForRangeParameterizedAttribute])
198        return [_pdfLayerController accessibilityStringForRangeAttributeForParameter:parameter];
199
200    return 0;
201}
202
203- (CPReadingModel *)readingModel
204{
205    return [_pdfLayerController readingModel];
206}
207
208- (NSArray *)accessibilityAttributeNames
209{
210    static NSArray *attributeNames = 0;
211
212    if (!attributeNames) {
213        attributeNames = @[NSAccessibilityValueAttribute,
214            NSAccessibilitySelectedTextAttribute,
215            NSAccessibilitySelectedTextRangeAttribute,
216            NSAccessibilityNumberOfCharactersAttribute,
217            NSAccessibilityVisibleCharacterRangeAttribute,
218            NSAccessibilityParentAttribute,
219            NSAccessibilityRoleAttribute,
220            NSAccessibilityWindowAttribute,
221            NSAccessibilityTopLevelUIElementAttribute,
222            NSAccessibilityRoleDescriptionAttribute,
223            NSAccessibilitySizeAttribute,
224            NSAccessibilityFocusedAttribute,
225            NSAccessibilityEnabledAttribute,
226            NSAccessibilityPositionAttribute];
227        [attributeNames retain];
228    }
229
230    return attributeNames;
231}
232
233- (NSArray *)accessibilityActionNames
234{
235    static NSArray *actionNames = 0;
236
237    if (!actionNames)
238        actionNames = [[NSArray arrayWithObject:NSAccessibilityShowMenuAction] retain];
239
240    return actionNames;
241}
242
243- (void)accessibilityPerformAction:(NSString *)action
244{
245    if ([action isEqualToString:NSAccessibilityShowMenuAction])
246        _pdfPlugin->showContextMenuAtPoint(IntRect(IntPoint(), _pdfPlugin->size()).center());
247}
248
249- (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute
250{
251    return [_pdfLayerController accessibilityIsAttributeSettable:attribute];
252}
253
254- (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute
255{
256    return [_pdfLayerController accessibilitySetValue:value forAttribute:attribute];
257}
258
259- (NSArray *)accessibilityParameterizedAttributeNames
260{
261    return [_pdfLayerController accessibilityParameterizedAttributeNames];
262}
263
264- (id)accessibilityFocusedUIElement
265{
266    return self;
267}
268
269- (id)accessibilityHitTest:(NSPoint)point
270{
271    return self;
272}
273
274@end
275
276
277@interface WKPDFPluginScrollbarLayer : CALayer {
278    WebKit::PDFPlugin* _pdfPlugin;
279}
280
281@property (assign) WebKit::PDFPlugin* pdfPlugin;
282
283@end
284
285@implementation WKPDFPluginScrollbarLayer
286
287@synthesize pdfPlugin=_pdfPlugin;
288
289- (id)initWithPDFPlugin:(WebKit::PDFPlugin *)plugin
290{
291    if (!(self = [super init]))
292        return nil;
293
294    _pdfPlugin = plugin;
295
296    return self;
297}
298
299- (id<CAAction>)actionForKey:(NSString *)key
300{
301    return nil;
302}
303
304- (void)drawInContext:(CGContextRef)ctx
305{
306    _pdfPlugin->paintControlForLayerInContext(self, ctx);
307}
308
309@end
310
311@interface WKPDFLayerControllerDelegate : NSObject<PDFLayerControllerDelegate> {
312    WebKit::PDFPlugin* _pdfPlugin;
313}
314
315@property (assign) WebKit::PDFPlugin* pdfPlugin;
316
317@end
318
319@implementation WKPDFLayerControllerDelegate
320
321@synthesize pdfPlugin=_pdfPlugin;
322
323- (id)initWithPDFPlugin:(WebKit::PDFPlugin *)plugin
324{
325    if (!(self = [super init]))
326        return nil;
327
328    _pdfPlugin = plugin;
329
330    return self;
331}
332
333- (void)updateScrollPosition:(CGPoint)newPosition
334{
335    _pdfPlugin->notifyScrollPositionChanged(IntPoint(newPosition));
336}
337
338- (void)writeItemsToPasteboard:(NSArray *)items withTypes:(NSArray *)types
339{
340    _pdfPlugin->writeItemsToPasteboard(NSGeneralPboard, items, types);
341}
342
343- (void)showDefinitionForAttributedString:(NSAttributedString *)string atPoint:(CGPoint)point
344{
345    _pdfPlugin->showDefinitionForAttributedString(string, point);
346}
347
348- (void)performWebSearch:(NSString *)string
349{
350    _pdfPlugin->performWebSearch(string);
351}
352
353- (void)performSpotlightSearch:(NSString *)string
354{
355    _pdfPlugin->performSpotlightSearch(string);
356}
357
358- (void)openWithNativeApplication
359{
360    _pdfPlugin->openWithNativeApplication();
361}
362
363- (void)saveToPDF
364{
365    _pdfPlugin->saveToPDF();
366}
367
368- (void)pdfLayerController:(PDFLayerController *)pdfLayerController clickedLinkWithURL:(NSURL *)url
369{
370    _pdfPlugin->clickedLink(url);
371}
372
373- (void)pdfLayerController:(PDFLayerController *)pdfLayerController didChangeActiveAnnotation:(PDFAnnotation *)annotation
374{
375    _pdfPlugin->setActiveAnnotation(annotation);
376}
377
378- (void)pdfLayerController:(PDFLayerController *)pdfLayerController didChangeContentScaleFactor:(CGFloat)scaleFactor
379{
380    _pdfPlugin->notifyContentScaleFactorChanged(scaleFactor);
381}
382
383- (void)pdfLayerController:(PDFLayerController *)pdfLayerController didChangeDisplayMode:(int)mode
384{
385    _pdfPlugin->notifyDisplayModeChanged(mode);
386}
387
388- (void)pdfLayerController:(PDFLayerController *)pdfLayerController didChangeSelection:(PDFSelection *)selection
389{
390    _pdfPlugin->notifySelectionChanged(selection);
391}
392
393@end
394
395static const char* postScriptMIMEType = "application/postscript";
396const uint64_t pdfDocumentRequestID = 1; // PluginController supports loading multiple streams, but we only need one for PDF.
397
398static void appendValuesInPDFNameSubtreeToVector(CGPDFDictionaryRef subtree, Vector<CGPDFObjectRef>& values)
399{
400    CGPDFArrayRef names;
401    if (CGPDFDictionaryGetArray(subtree, "Names", &names)) {
402        size_t nameCount = CGPDFArrayGetCount(names) / 2;
403        for (size_t i = 0; i < nameCount; ++i) {
404            CGPDFObjectRef object;
405            CGPDFArrayGetObject(names, 2 * i + 1, &object);
406            values.append(object);
407        }
408        return;
409    }
410
411    CGPDFArrayRef kids;
412    if (!CGPDFDictionaryGetArray(subtree, "Kids", &kids))
413        return;
414
415    size_t kidCount = CGPDFArrayGetCount(kids);
416    for (size_t i = 0; i < kidCount; ++i) {
417        CGPDFDictionaryRef kid;
418        if (!CGPDFArrayGetDictionary(kids, i, &kid))
419            continue;
420        appendValuesInPDFNameSubtreeToVector(kid, values);
421    }
422}
423
424static void getAllValuesInPDFNameTree(CGPDFDictionaryRef tree, Vector<CGPDFObjectRef>& allValues)
425{
426    appendValuesInPDFNameSubtreeToVector(tree, allValues);
427}
428
429static void getAllScriptsInPDFDocument(CGPDFDocumentRef pdfDocument, Vector<RetainPtr<CFStringRef>>& scripts)
430{
431    if (!pdfDocument)
432        return;
433
434    CGPDFDictionaryRef pdfCatalog = CGPDFDocumentGetCatalog(pdfDocument);
435    if (!pdfCatalog)
436        return;
437
438    // Get the dictionary of all document-level name trees.
439    CGPDFDictionaryRef namesDictionary;
440    if (!CGPDFDictionaryGetDictionary(pdfCatalog, "Names", &namesDictionary))
441        return;
442
443    // Get the document-level "JavaScript" name tree.
444    CGPDFDictionaryRef javaScriptNameTree;
445    if (!CGPDFDictionaryGetDictionary(namesDictionary, "JavaScript", &javaScriptNameTree))
446        return;
447
448    // The names are arbitrary. We are only interested in the values.
449    Vector<CGPDFObjectRef> objects;
450    getAllValuesInPDFNameTree(javaScriptNameTree, objects);
451    size_t objectCount = objects.size();
452
453    for (size_t i = 0; i < objectCount; ++i) {
454        CGPDFDictionaryRef javaScriptAction;
455        if (!CGPDFObjectGetValue(reinterpret_cast<CGPDFObjectRef>(objects[i]), kCGPDFObjectTypeDictionary, &javaScriptAction))
456            continue;
457
458        // A JavaScript action must have an action type of "JavaScript".
459        const char* actionType;
460        if (!CGPDFDictionaryGetName(javaScriptAction, "S", &actionType) || strcmp(actionType, "JavaScript"))
461            continue;
462
463        const UInt8* bytes = 0;
464        CFIndex length;
465        CGPDFStreamRef stream;
466        CGPDFStringRef string;
467        RetainPtr<CFDataRef> data;
468        if (CGPDFDictionaryGetStream(javaScriptAction, "JS", &stream)) {
469            CGPDFDataFormat format;
470            data = adoptCF(CGPDFStreamCopyData(stream, &format));
471            if (!data)
472                continue;
473            bytes = CFDataGetBytePtr(data.get());
474            length = CFDataGetLength(data.get());
475        } else if (CGPDFDictionaryGetString(javaScriptAction, "JS", &string)) {
476            bytes = CGPDFStringGetBytePtr(string);
477            length = CGPDFStringGetLength(string);
478        }
479        if (!bytes)
480            continue;
481
482        CFStringEncoding encoding = (length > 1 && bytes[0] == 0xFE && bytes[1] == 0xFF) ? kCFStringEncodingUnicode : kCFStringEncodingUTF8;
483        RetainPtr<CFStringRef> script = adoptCF(CFStringCreateWithBytes(kCFAllocatorDefault, bytes, length, encoding, true));
484        if (!script)
485            continue;
486
487        scripts.append(script);
488    }
489}
490
491namespace WebKit {
492
493using namespace HTMLNames;
494
495PassRefPtr<PDFPlugin> PDFPlugin::create(WebFrame* frame)
496{
497    return adoptRef(new PDFPlugin(frame));
498}
499
500PDFPlugin::PDFPlugin(WebFrame* frame)
501    : m_frame(frame)
502    , m_isPostScript(false)
503    , m_pdfDocumentWasMutated(false)
504    , m_containerLayer(adoptNS([[CALayer alloc] init]))
505    , m_contentLayer(adoptNS([[CALayer alloc] init]))
506    , m_scrollCornerLayer(adoptNS([[WKPDFPluginScrollbarLayer alloc] initWithPDFPlugin:this]))
507    , m_pdfLayerController(adoptNS([[pdfLayerControllerClass() alloc] init]))
508    , m_pdfLayerControllerDelegate(adoptNS([[WKPDFLayerControllerDelegate alloc] initWithPDFPlugin:this]))
509{
510    m_pdfLayerController.get().delegate = m_pdfLayerControllerDelegate.get();
511    m_pdfLayerController.get().parentLayer = m_contentLayer.get();
512
513    if (supportsForms()) {
514        Document* document = webFrame()->coreFrame()->document();
515        m_annotationContainer = document->createElement(divTag, false);
516        m_annotationContainer->setAttribute(idAttr, "annotationContainer");
517
518        RefPtr<Element> m_annotationStyle = document->createElement(styleTag, false);
519        m_annotationStyle->setTextContent(annotationStyle, ASSERT_NO_EXCEPTION);
520
521        m_annotationContainer->appendChild(m_annotationStyle.get());
522        document->body()->appendChild(m_annotationContainer.get());
523    }
524
525    m_accessibilityObject = adoptNS([[WKPDFPluginAccessibilityObject alloc] initWithPDFPlugin:this]);
526    m_accessibilityObject.get().pdfLayerController = m_pdfLayerController.get();
527    m_accessibilityObject.get().parent = webFrame()->page()->accessibilityRemoteObject();
528
529    [m_containerLayer addSublayer:m_contentLayer.get()];
530    [m_containerLayer addSublayer:m_scrollCornerLayer.get()];
531}
532
533PDFPlugin::~PDFPlugin()
534{
535}
536
537PluginInfo PDFPlugin::pluginInfo()
538{
539    PluginInfo info;
540    info.name = builtInPDFPluginName();
541    info.isApplicationPlugin = true;
542
543    MimeClassInfo pdfMimeClassInfo;
544    pdfMimeClassInfo.type = "application/pdf";
545    pdfMimeClassInfo.desc = pdfDocumentTypeDescription();
546    pdfMimeClassInfo.extensions.append("pdf");
547    info.mimes.append(pdfMimeClassInfo);
548
549    MimeClassInfo textPDFMimeClassInfo;
550    textPDFMimeClassInfo.type = "text/pdf";
551    textPDFMimeClassInfo.desc = pdfDocumentTypeDescription();
552    textPDFMimeClassInfo.extensions.append("pdf");
553    info.mimes.append(textPDFMimeClassInfo);
554
555    MimeClassInfo postScriptMimeClassInfo;
556    postScriptMimeClassInfo.type = postScriptMIMEType;
557    postScriptMimeClassInfo.desc = postScriptDocumentTypeDescription();
558    postScriptMimeClassInfo.extensions.append("ps");
559    info.mimes.append(postScriptMimeClassInfo);
560
561    return info;
562}
563
564void PDFPlugin::updateScrollbars()
565{
566    bool hadScrollbars = m_horizontalScrollbar || m_verticalScrollbar;
567
568    if (m_horizontalScrollbar) {
569        if (m_size.width() >= m_pdfDocumentSize.width())
570            destroyScrollbar(HorizontalScrollbar);
571    } else if (m_size.width() < m_pdfDocumentSize.width())
572        m_horizontalScrollbar = createScrollbar(HorizontalScrollbar);
573
574    if (m_verticalScrollbar) {
575        if (m_size.height() >= m_pdfDocumentSize.height())
576            destroyScrollbar(VerticalScrollbar);
577    } else if (m_size.height() < m_pdfDocumentSize.height())
578        m_verticalScrollbar = createScrollbar(VerticalScrollbar);
579
580    int horizontalScrollbarHeight = (m_horizontalScrollbar && !m_horizontalScrollbar->isOverlayScrollbar()) ? m_horizontalScrollbar->height() : 0;
581    int verticalScrollbarWidth = (m_verticalScrollbar && !m_verticalScrollbar->isOverlayScrollbar()) ? m_verticalScrollbar->width() : 0;
582
583    int pageStep = m_pageBoxes.isEmpty() ? 0 : m_pageBoxes[0].height();
584
585    if (m_horizontalScrollbar) {
586        m_horizontalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
587        m_horizontalScrollbar->setProportion(m_size.width() - verticalScrollbarWidth, m_pdfDocumentSize.width());
588        IntRect scrollbarRect(pluginView()->x(), pluginView()->y() + m_size.height() - m_horizontalScrollbar->height(), m_size.width(), m_horizontalScrollbar->height());
589        if (m_verticalScrollbar)
590            scrollbarRect.contract(m_verticalScrollbar->width(), 0);
591        m_horizontalScrollbar->setFrameRect(scrollbarRect);
592    }
593    if (m_verticalScrollbar) {
594        m_verticalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
595        m_verticalScrollbar->setProportion(m_size.height() - horizontalScrollbarHeight, m_pdfDocumentSize.height());
596        IntRect scrollbarRect(IntRect(pluginView()->x() + m_size.width() - m_verticalScrollbar->width(), pluginView()->y(), m_verticalScrollbar->width(), m_size.height()));
597        if (m_horizontalScrollbar)
598            scrollbarRect.contract(0, m_horizontalScrollbar->height());
599        m_verticalScrollbar->setFrameRect(scrollbarRect);
600    }
601
602    FrameView* frameView = m_frame->coreFrame()->view();
603    if (!frameView)
604        return;
605
606    bool hasScrollbars = m_horizontalScrollbar || m_verticalScrollbar;
607    if (hadScrollbars != hasScrollbars) {
608        if (hasScrollbars)
609            frameView->addScrollableArea(this);
610        else
611            frameView->removeScrollableArea(this);
612
613        frameView->setNeedsLayout();
614    }
615
616    if (m_verticalScrollbarLayer) {
617        m_verticalScrollbarLayer.get().frame = verticalScrollbar()->frameRect();
618        [m_verticalScrollbarLayer setNeedsDisplay];
619    }
620
621    if (m_horizontalScrollbarLayer) {
622        m_horizontalScrollbarLayer.get().frame = horizontalScrollbar()->frameRect();
623        [m_horizontalScrollbarLayer setNeedsDisplay];
624    }
625
626    if (m_scrollCornerLayer) {
627        m_scrollCornerLayer.get().frame = scrollCornerRect();
628        [m_scrollCornerLayer setNeedsDisplay];
629    }
630}
631
632PluginView* PDFPlugin::pluginView()
633{
634    return static_cast<PluginView*>(controller());
635}
636
637const PluginView* PDFPlugin::pluginView() const
638{
639    return static_cast<const PluginView*>(controller());
640}
641
642PassRefPtr<Scrollbar> PDFPlugin::createScrollbar(ScrollbarOrientation orientation)
643{
644    RefPtr<Scrollbar> widget = Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar);
645    if (orientation == HorizontalScrollbar) {
646        m_horizontalScrollbarLayer = adoptNS([[WKPDFPluginScrollbarLayer alloc] initWithPDFPlugin:this]);
647        [m_containerLayer addSublayer:m_horizontalScrollbarLayer.get()];
648    } else {
649        m_verticalScrollbarLayer = adoptNS([[WKPDFPluginScrollbarLayer alloc] initWithPDFPlugin:this]);
650        [m_containerLayer addSublayer:m_verticalScrollbarLayer.get()];
651    }
652    didAddScrollbar(widget.get(), orientation);
653    pluginView()->frame()->view()->addChild(widget.get());
654    return widget.release();
655}
656
657void PDFPlugin::destroyScrollbar(ScrollbarOrientation orientation)
658{
659    RefPtr<Scrollbar>& scrollbar = orientation == HorizontalScrollbar ? m_horizontalScrollbar : m_verticalScrollbar;
660    if (!scrollbar)
661        return;
662
663    willRemoveScrollbar(scrollbar.get(), orientation);
664    scrollbar->removeFromParent();
665    scrollbar->disconnectFromScrollableArea();
666    scrollbar = 0;
667
668    if (orientation == HorizontalScrollbar) {
669        [m_horizontalScrollbarLayer removeFromSuperlayer];
670        m_horizontalScrollbarLayer = 0;
671    } else {
672        [m_verticalScrollbarLayer removeFromSuperlayer];
673        m_verticalScrollbarLayer = 0;
674    }
675}
676
677IntRect PDFPlugin::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const
678{
679    IntRect rect = scrollbarRect;
680    rect.move(scrollbar->location() - pluginView()->location());
681
682    return pluginView()->frame()->view()->convertFromRendererToContainingView(pluginView()->renderer(), rect);
683}
684
685IntRect PDFPlugin::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
686{
687    IntRect rect = pluginView()->frame()->view()->convertFromContainingViewToRenderer(pluginView()->renderer(), parentRect);
688    rect.move(pluginView()->location() - scrollbar->location());
689
690    return rect;
691}
692
693IntPoint PDFPlugin::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const
694{
695    IntPoint point = scrollbarPoint;
696    point.move(scrollbar->location() - pluginView()->location());
697
698    return pluginView()->frame()->view()->convertFromRendererToContainingView(pluginView()->renderer(), point);
699}
700
701IntPoint PDFPlugin::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
702{
703    IntPoint point = pluginView()->frame()->view()->convertFromContainingViewToRenderer(pluginView()->renderer(), parentPoint);
704    point.move(pluginView()->location() - scrollbar->location());
705
706    return point;
707}
708
709bool PDFPlugin::handleScroll(ScrollDirection direction, ScrollGranularity granularity)
710{
711    return scroll(direction, granularity);
712}
713
714IntRect PDFPlugin::scrollCornerRect() const
715{
716    if (!m_horizontalScrollbar || !m_verticalScrollbar)
717        return IntRect();
718    if (m_horizontalScrollbar->isOverlayScrollbar()) {
719        ASSERT(m_verticalScrollbar->isOverlayScrollbar());
720        return IntRect();
721    }
722    return IntRect(pluginView()->width() - m_verticalScrollbar->width(), pluginView()->height() - m_horizontalScrollbar->height(), m_verticalScrollbar->width(), m_horizontalScrollbar->height());
723}
724
725ScrollableArea* PDFPlugin::enclosingScrollableArea() const
726{
727    // FIXME: Walk up the frame tree and look for a scrollable parent frame or RenderLayer.
728    return 0;
729}
730
731IntRect PDFPlugin::scrollableAreaBoundingBox() const
732{
733    return pluginView()->frameRect();
734}
735
736int PDFPlugin::scrollSize(ScrollbarOrientation orientation) const
737{
738    Scrollbar* scrollbar = ((orientation == HorizontalScrollbar) ? m_horizontalScrollbar : m_verticalScrollbar).get();
739    return scrollbar ? (scrollbar->totalSize() - scrollbar->visibleSize()) : 0;
740}
741
742bool PDFPlugin::isActive() const
743{
744    if (Frame* coreFrame = m_frame->coreFrame()) {
745        if (Page* page = coreFrame->page())
746            return page->focusController().isActive();
747    }
748
749    return false;
750}
751
752bool PDFPlugin::forceUpdateScrollbarsOnMainThreadForPerformanceTesting() const
753{
754    if (Frame* coreFrame = m_frame->coreFrame()) {
755        if (Page* page = coreFrame->page())
756            return page->settings().forceUpdateScrollbarsOnMainThreadForPerformanceTesting();
757    }
758
759    return false;
760}
761
762int PDFPlugin::scrollPosition(Scrollbar* scrollbar) const
763{
764    if (scrollbar->orientation() == HorizontalScrollbar)
765        return m_scrollOffset.width();
766    if (scrollbar->orientation() == VerticalScrollbar)
767        return m_scrollOffset.height();
768    ASSERT_NOT_REACHED();
769    return 0;
770}
771
772IntPoint PDFPlugin::scrollPosition() const
773{
774    return IntPoint(m_scrollOffset.width(), m_scrollOffset.height());
775}
776
777IntPoint PDFPlugin::minimumScrollPosition() const
778{
779    return IntPoint();
780}
781
782IntPoint PDFPlugin::maximumScrollPosition() const
783{
784    int horizontalScrollbarHeight = (m_horizontalScrollbar && !m_horizontalScrollbar->isOverlayScrollbar()) ? m_horizontalScrollbar->height() : 0;
785    int verticalScrollbarWidth = (m_verticalScrollbar && !m_verticalScrollbar->isOverlayScrollbar()) ? m_verticalScrollbar->width() : 0;
786
787    IntPoint maximumOffset(m_pdfDocumentSize.width() - m_size.width() + verticalScrollbarWidth, m_pdfDocumentSize.height() - m_size.height() + horizontalScrollbarHeight);
788    maximumOffset.clampNegativeToZero();
789    return maximumOffset;
790}
791
792void PDFPlugin::scrollbarStyleChanged(int, bool forceUpdate)
793{
794    if (!forceUpdate)
795        return;
796
797    // If the PDF was scrolled all the way to bottom right and scrollbars change to overlay style, we don't want to display white rectangles where scrollbars were.
798    IntPoint newScrollOffset = IntPoint(m_scrollOffset).shrunkTo(maximumScrollPosition());
799    setScrollOffset(newScrollOffset);
800
801    // As size of the content area changes, scrollbars may need to appear or to disappear.
802    updateScrollbars();
803
804    ScrollableArea::contentsResized();
805}
806
807void PDFPlugin::addArchiveResource()
808{
809    // FIXME: It's a hack to force add a resource to DocumentLoader. PDF documents should just be fetched as CachedResources.
810
811    // Add just enough data for context menu handling and web archives to work.
812    ResourceResponse synthesizedResponse;
813    synthesizedResponse.setSuggestedFilename(m_suggestedFilename);
814    synthesizedResponse.setURL(m_sourceURL); // Needs to match the HitTestResult::absolutePDFURL.
815    synthesizedResponse.setMimeType("application/pdf");
816
817    RefPtr<ArchiveResource> resource = ArchiveResource::create(SharedBuffer::wrapCFData(m_data.get()), m_sourceURL, "application/pdf", String(), String(), synthesizedResponse);
818    pluginView()->frame()->document()->loader()->addArchiveResource(resource.release());
819}
820
821static void jsPDFDocInitialize(JSContextRef ctx, JSObjectRef object)
822{
823    PDFPlugin* pdfView = static_cast<PDFPlugin*>(JSObjectGetPrivate(object));
824    pdfView->ref();
825}
826
827static void jsPDFDocFinalize(JSObjectRef object)
828{
829    PDFPlugin* pdfView = static_cast<PDFPlugin*>(JSObjectGetPrivate(object));
830    pdfView->deref();
831}
832
833JSValueRef PDFPlugin::jsPDFDocPrint(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
834{
835    PDFPlugin* pdfView = static_cast<PDFPlugin*>(JSObjectGetPrivate(thisObject));
836
837    WebFrame* frame = pdfView->m_frame;
838    if (!frame)
839        return JSValueMakeUndefined(ctx);
840
841    Frame* coreFrame = frame->coreFrame();
842    if (!coreFrame)
843        return JSValueMakeUndefined(ctx);
844
845    Page* page = coreFrame->page();
846    if (!page)
847        return JSValueMakeUndefined(ctx);
848
849    page->chrome().print(coreFrame);
850
851    return JSValueMakeUndefined(ctx);
852}
853
854JSObjectRef PDFPlugin::makeJSPDFDoc(JSContextRef ctx)
855{
856    static JSStaticFunction jsPDFDocStaticFunctions[] = {
857        { "print", jsPDFDocPrint, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
858        { 0, 0, 0 },
859    };
860
861    static JSClassDefinition jsPDFDocClassDefinition = {
862        0,
863        kJSClassAttributeNone,
864        "Doc",
865        0,
866        0,
867        jsPDFDocStaticFunctions,
868        jsPDFDocInitialize, jsPDFDocFinalize, 0, 0, 0, 0, 0, 0, 0, 0, 0
869    };
870
871    static JSClassRef jsPDFDocClass = JSClassCreate(&jsPDFDocClassDefinition);
872
873    return JSObjectMake(ctx, jsPDFDocClass, this);
874}
875
876static RetainPtr<CFMutableDataRef> convertPostScriptDataToPDF(RetainPtr<CFDataRef> postScriptData)
877{
878    // Convert PostScript to PDF using the Quartz 2D API.
879    // http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_ps_convert/chapter_16_section_1.html
880
881    CGPSConverterCallbacks callbacks = { 0, 0, 0, 0, 0, 0, 0, 0 };
882    RetainPtr<CGPSConverterRef> converter = adoptCF(CGPSConverterCreate(0, &callbacks, 0));
883    RetainPtr<CGDataProviderRef> provider = adoptCF(CGDataProviderCreateWithCFData(postScriptData.get()));
884    RetainPtr<CFMutableDataRef> pdfData = adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0));
885    RetainPtr<CGDataConsumerRef> consumer = adoptCF(CGDataConsumerCreateWithCFData(pdfData.get()));
886
887    CGPSConverterConvert(converter.get(), provider.get(), consumer.get(), 0);
888
889    return pdfData;
890}
891
892void PDFPlugin::convertPostScriptDataIfNeeded()
893{
894    if (!m_isPostScript)
895        return;
896
897    m_suggestedFilename = String(m_suggestedFilename + ".pdf");
898    m_data = convertPostScriptDataToPDF(m_data);
899}
900
901void PDFPlugin::pdfDocumentDidLoad()
902{
903    addArchiveResource();
904
905    RetainPtr<PDFDocument> document = adoptNS([[pdfDocumentClass() alloc] initWithData:rawData()]);
906
907    setPDFDocument(document);
908
909    updatePageAndDeviceScaleFactors();
910
911    [m_pdfLayerController setFrameSize:size()];
912    m_pdfLayerController.get().document = document.get();
913
914    if (handlesPageScaleFactor())
915        pluginView()->setPageScaleFactor([m_pdfLayerController contentScaleFactor], IntPoint());
916
917    notifyScrollPositionChanged(IntPoint([m_pdfLayerController scrollPosition]));
918
919    calculateSizes();
920    updateScrollbars();
921
922    runScriptsInPDFDocument();
923
924    if ([document isLocked])
925        createPasswordEntryForm();
926}
927
928void PDFPlugin::streamDidReceiveResponse(uint64_t streamID, const URL&, uint32_t, uint32_t, const String& mimeType, const String&, const String& suggestedFilename)
929{
930    ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
931
932    m_suggestedFilename = suggestedFilename;
933
934    if (equalIgnoringCase(mimeType, postScriptMIMEType))
935        m_isPostScript = true;
936}
937
938void PDFPlugin::streamDidReceiveData(uint64_t streamID, const char* bytes, int length)
939{
940    ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
941
942    if (!m_data)
943        m_data = adoptCF(CFDataCreateMutable(0, 0));
944
945    CFDataAppendBytes(m_data.get(), reinterpret_cast<const UInt8*>(bytes), length);
946}
947
948void PDFPlugin::streamDidFinishLoading(uint64_t streamID)
949{
950    ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
951
952    convertPostScriptDataIfNeeded();
953    pdfDocumentDidLoad();
954}
955
956void PDFPlugin::streamDidFail(uint64_t streamID, bool wasCancelled)
957{
958    ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
959
960    m_data.clear();
961}
962
963void PDFPlugin::manualStreamDidReceiveResponse(const URL& responseURL, uint32_t streamLength,  uint32_t lastModifiedTime, const String& mimeType, const String& headers, const String& suggestedFilename)
964{
965    m_suggestedFilename = suggestedFilename;
966
967    if (equalIgnoringCase(mimeType, postScriptMIMEType))
968        m_isPostScript = true;
969}
970
971void PDFPlugin::manualStreamDidReceiveData(const char* bytes, int length)
972{
973    if (!m_data)
974        m_data = adoptCF(CFDataCreateMutable(0, 0));
975
976    CFDataAppendBytes(m_data.get(), reinterpret_cast<const UInt8*>(bytes), length);
977}
978
979void PDFPlugin::manualStreamDidFinishLoading()
980{
981    convertPostScriptDataIfNeeded();
982    pdfDocumentDidLoad();
983}
984
985void PDFPlugin::manualStreamDidFail(bool)
986{
987    m_data.clear();
988}
989
990void PDFPlugin::runScriptsInPDFDocument()
991{
992    Vector<RetainPtr<CFStringRef>> scripts;
993    getAllScriptsInPDFDocument([m_pdfDocument documentRef], scripts);
994
995    size_t scriptCount = scripts.size();
996    if (!scriptCount)
997        return;
998
999    JSGlobalContextRef ctx = JSGlobalContextCreate(0);
1000    JSObjectRef jsPDFDoc = makeJSPDFDoc(ctx);
1001
1002    for (size_t i = 0; i < scriptCount; ++i) {
1003        JSStringRef script = JSStringCreateWithCFString(scripts[i].get());
1004        JSEvaluateScript(ctx, script, jsPDFDoc, 0, 0, 0);
1005        JSStringRelease(script);
1006    }
1007
1008    JSGlobalContextRelease(ctx);
1009}
1010
1011void PDFPlugin::createPasswordEntryForm()
1012{
1013    m_passwordField = PDFPluginPasswordField::create(m_pdfLayerController.get(), this);
1014    m_passwordField->attach(m_annotationContainer.get());
1015}
1016
1017void PDFPlugin::attemptToUnlockPDF(const String& password)
1018{
1019    [m_pdfLayerController attemptToUnlockWithPassword:password];
1020
1021    if (![pdfDocument() isLocked]) {
1022        m_passwordField = nullptr;
1023
1024        calculateSizes();
1025        updateScrollbars();
1026    }
1027}
1028
1029void PDFPlugin::updatePageAndDeviceScaleFactors()
1030{
1031    double newScaleFactor = controller()->contentsScaleFactor();
1032    if (!handlesPageScaleFactor())
1033        newScaleFactor *= webFrame()->page()->pageScaleFactor();
1034
1035    [m_pdfLayerController setDeviceScaleFactor:newScaleFactor];
1036}
1037
1038void PDFPlugin::contentsScaleFactorChanged(float)
1039{
1040    updatePageAndDeviceScaleFactors();
1041}
1042
1043void PDFPlugin::computePageBoxes()
1044{
1045    size_t pageCount = CGPDFDocumentGetNumberOfPages([m_pdfDocument documentRef]);
1046    for (size_t i = 0; i < pageCount; ++i) {
1047        CGPDFPageRef pdfPage = CGPDFDocumentGetPage([m_pdfDocument documentRef], i + 1);
1048        ASSERT(pdfPage);
1049
1050        CGRect box = CGPDFPageGetBoxRect(pdfPage, kCGPDFCropBox);
1051        if (CGRectIsEmpty(box))
1052            box = CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox);
1053        m_pageBoxes.append(IntRect(box));
1054    }
1055}
1056
1057void PDFPlugin::calculateSizes()
1058{
1059    if ([pdfDocument() isLocked]) {
1060        setPDFDocumentSize(IntSize(0, 0));
1061        return;
1062    }
1063
1064    // FIXME: This should come straight from PDFKit.
1065    computePageBoxes();
1066
1067    setPDFDocumentSize(IntSize([m_pdfLayerController contentSizeRespectingZoom]));
1068}
1069
1070bool PDFPlugin::initialize(const Parameters& parameters)
1071{
1072    m_sourceURL = parameters.url;
1073    if (!parameters.shouldUseManualLoader && !parameters.url.isEmpty())
1074        controller()->loadURL(pdfDocumentRequestID, "GET", parameters.url.string(), String(), HTTPHeaderMap(), Vector<uint8_t>(), false);
1075
1076    controller()->didInitializePlugin();
1077    return true;
1078}
1079
1080void PDFPlugin::destroy()
1081{
1082    m_pdfLayerController.get().delegate = 0;
1083
1084    if (webFrame()) {
1085        if (FrameView* frameView = webFrame()->coreFrame()->view())
1086            frameView->removeScrollableArea(this);
1087    }
1088
1089    m_activeAnnotation = 0;
1090    m_annotationContainer = 0;
1091
1092    destroyScrollbar(HorizontalScrollbar);
1093    destroyScrollbar(VerticalScrollbar);
1094
1095    [m_scrollCornerLayer removeFromSuperlayer];
1096    [m_contentLayer removeFromSuperlayer];
1097}
1098
1099void PDFPlugin::updateControlTints(GraphicsContext* graphicsContext)
1100{
1101    ASSERT(graphicsContext->updatingControlTints());
1102
1103    if (m_horizontalScrollbar)
1104        m_horizontalScrollbar->invalidate();
1105    if (m_verticalScrollbar)
1106        m_verticalScrollbar->invalidate();
1107    invalidateScrollCorner(scrollCornerRect());
1108}
1109
1110void PDFPlugin::paintControlForLayerInContext(CALayer *layer, CGContextRef context)
1111{
1112    GraphicsContext graphicsContext(context);
1113    GraphicsContextStateSaver stateSaver(graphicsContext);
1114
1115    graphicsContext.setIsCALayerContext(true);
1116
1117    if (layer == m_scrollCornerLayer) {
1118        IntRect scrollCornerRect = this->scrollCornerRect();
1119        graphicsContext.translate(-scrollCornerRect.x(), -scrollCornerRect.y());
1120        ScrollbarTheme::theme()->paintScrollCorner(0, &graphicsContext, scrollCornerRect);
1121        return;
1122    }
1123
1124    Scrollbar* scrollbar = nullptr;
1125
1126    if (layer == m_verticalScrollbarLayer)
1127        scrollbar = verticalScrollbar();
1128    else if (layer == m_horizontalScrollbarLayer)
1129        scrollbar = horizontalScrollbar();
1130
1131    if (!scrollbar)
1132        return;
1133
1134    graphicsContext.translate(-scrollbar->x(), -scrollbar->y());
1135    scrollbar->paint(&graphicsContext, scrollbar->frameRect());
1136}
1137
1138PassRefPtr<ShareableBitmap> PDFPlugin::snapshot()
1139{
1140    if (size().isEmpty())
1141        return nullptr;
1142
1143    float contentsScaleFactor = controller()->contentsScaleFactor();
1144    IntSize backingStoreSize = size();
1145    backingStoreSize.scale(contentsScaleFactor);
1146
1147    RefPtr<ShareableBitmap> bitmap = ShareableBitmap::createShareable(backingStoreSize, ShareableBitmap::SupportsAlpha);
1148    auto context = bitmap->createGraphicsContext();
1149
1150    context->scale(FloatSize(contentsScaleFactor, -contentsScaleFactor));
1151    context->translate(-m_scrollOffset.width(), -m_pdfDocumentSize.height() + m_scrollOffset.height());
1152
1153    [m_pdfLayerController snapshotInContext:context->platformContext()];
1154
1155    return bitmap.release();
1156}
1157
1158PlatformLayer* PDFPlugin::pluginLayer()
1159{
1160    return m_containerLayer.get();
1161}
1162
1163IntPoint PDFPlugin::convertFromPluginToPDFView(const IntPoint& point) const
1164{
1165    return IntPoint(point.x(), size().height() - point.y());
1166}
1167
1168IntPoint PDFPlugin::convertFromRootViewToPlugin(const IntPoint& point) const
1169{
1170    return m_rootViewToPluginTransform.mapPoint(point);
1171}
1172
1173IntPoint PDFPlugin::convertFromPDFViewToRootView(const IntPoint& point) const
1174{
1175    IntPoint pointInPluginCoordinates(point.x(), size().height() - point.y());
1176    return m_rootViewToPluginTransform.inverse().mapPoint(pointInPluginCoordinates);
1177}
1178
1179FloatRect PDFPlugin::convertFromPDFViewToScreen(const FloatRect& rect) const
1180{
1181    FrameView* frameView = webFrame()->coreFrame()->view();
1182
1183    if (!frameView)
1184        return FloatRect();
1185
1186    FloatPoint originInPluginCoordinates(rect.x(), size().height() - rect.y() - rect.height());
1187    FloatRect rectInRootViewCoordinates = m_rootViewToPluginTransform.inverse().mapRect(FloatRect(originInPluginCoordinates, rect.size()));
1188
1189    return frameView->contentsToScreen(enclosingIntRect(rectInRootViewCoordinates));
1190}
1191
1192IntRect PDFPlugin::boundsOnScreen() const
1193{
1194    FrameView* frameView = webFrame()->coreFrame()->view();
1195
1196    if (!frameView)
1197        return IntRect();
1198
1199    FloatRect bounds = FloatRect(FloatPoint(), size());
1200    FloatRect rectInRootViewCoordinates = m_rootViewToPluginTransform.inverse().mapRect(bounds);
1201    return frameView->contentsToScreen(enclosingIntRect(rectInRootViewCoordinates));
1202}
1203
1204void PDFPlugin::geometryDidChange(const IntSize& pluginSize, const IntRect&, const AffineTransform& pluginToRootViewTransform)
1205{
1206    if (size() == pluginSize && pluginView()->pageScaleFactor() == [m_pdfLayerController contentScaleFactor])
1207        return;
1208
1209    m_size = pluginSize;
1210    m_rootViewToPluginTransform = pluginToRootViewTransform.inverse();
1211    [m_pdfLayerController setFrameSize:pluginSize];
1212
1213    [CATransaction begin];
1214    [CATransaction setDisableActions:YES];
1215    CATransform3D transform = CATransform3DMakeScale(1, -1, 1);
1216    transform = CATransform3DTranslate(transform, 0, -pluginSize.height(), 0);
1217
1218    if (handlesPageScaleFactor()) {
1219        CGFloat magnification = pluginView()->pageScaleFactor() - [m_pdfLayerController contentScaleFactor];
1220
1221        // FIXME: Instead of m_lastMousePositionInPluginCoordinates, we should use the zoom origin from PluginView::setPageScaleFactor.
1222        if (magnification)
1223            [m_pdfLayerController magnifyWithMagnification:magnification atPoint:convertFromPluginToPDFView(m_lastMousePositionInPluginCoordinates) immediately:NO];
1224    } else {
1225        // If we don't handle page scale ourselves, we need to respect our parent page's
1226        // scale, which may have changed.
1227        updatePageAndDeviceScaleFactors();
1228    }
1229
1230    calculateSizes();
1231    updateScrollbars();
1232
1233    if (m_activeAnnotation)
1234        m_activeAnnotation->updateGeometry();
1235
1236    [m_contentLayer setSublayerTransform:transform];
1237    [CATransaction commit];
1238}
1239
1240void PDFPlugin::frameDidFinishLoading(uint64_t)
1241{
1242    ASSERT_NOT_REACHED();
1243}
1244
1245void PDFPlugin::frameDidFail(uint64_t, bool)
1246{
1247    ASSERT_NOT_REACHED();
1248}
1249
1250void PDFPlugin::didEvaluateJavaScript(uint64_t, const WTF::String&)
1251{
1252    ASSERT_NOT_REACHED();
1253}
1254
1255
1256static NSUInteger modifierFlagsFromWebEvent(const WebEvent& event)
1257{
1258    return (event.shiftKey() ? NSShiftKeyMask : 0)
1259        | (event.controlKey() ? NSControlKeyMask : 0)
1260        | (event.altKey() ? NSAlternateKeyMask : 0)
1261        | (event.metaKey() ? NSCommandKeyMask : 0);
1262}
1263
1264static bool getEventTypeFromWebEvent(const WebEvent& event, NSEventType& eventType)
1265{
1266    switch (event.type()) {
1267    case WebEvent::KeyDown:
1268        eventType = NSKeyDown;
1269        return true;
1270    case WebEvent::KeyUp:
1271        eventType = NSKeyUp;
1272        return true;
1273    case WebEvent::MouseDown:
1274        switch (static_cast<const WebMouseEvent&>(event).button()) {
1275        case WebMouseEvent::LeftButton:
1276            eventType = NSLeftMouseDown;
1277            return true;
1278        case WebMouseEvent::RightButton:
1279            eventType = NSRightMouseDown;
1280            return true;
1281        default:
1282            return false;
1283        }
1284    case WebEvent::MouseUp:
1285        switch (static_cast<const WebMouseEvent&>(event).button()) {
1286        case WebMouseEvent::LeftButton:
1287            eventType = NSLeftMouseUp;
1288            return true;
1289        case WebMouseEvent::RightButton:
1290            eventType = NSRightMouseUp;
1291            return true;
1292        default:
1293            return false;
1294        }
1295    case WebEvent::MouseMove:
1296        switch (static_cast<const WebMouseEvent&>(event).button()) {
1297        case WebMouseEvent::LeftButton:
1298            eventType = NSLeftMouseDragged;
1299            return true;
1300        case WebMouseEvent::RightButton:
1301            eventType = NSRightMouseDragged;
1302            return true;
1303        case WebMouseEvent::NoButton:
1304            eventType = NSMouseMoved;
1305            return true;
1306        default:
1307            return false;
1308        }
1309    default:
1310        return false;
1311    }
1312}
1313
1314NSEvent *PDFPlugin::nsEventForWebMouseEvent(const WebMouseEvent& event)
1315{
1316    m_lastMousePositionInPluginCoordinates = convertFromRootViewToPlugin(event.position());
1317
1318    IntPoint positionInPDFViewCoordinates(convertFromPluginToPDFView(m_lastMousePositionInPluginCoordinates));
1319
1320    NSEventType eventType;
1321
1322    if (!getEventTypeFromWebEvent(event, eventType))
1323        return 0;
1324
1325    NSUInteger modifierFlags = modifierFlagsFromWebEvent(event);
1326
1327    return [NSEvent mouseEventWithType:eventType location:positionInPDFViewCoordinates modifierFlags:modifierFlags timestamp:0 windowNumber:0 context:nil eventNumber:0 clickCount:event.clickCount() pressure:0];
1328}
1329
1330void PDFPlugin::updateCursor(const WebMouseEvent& event, UpdateCursorMode mode)
1331{
1332    HitTestResult hitTestResult = None;
1333
1334    PDFSelection *selectionUnderMouse = [m_pdfLayerController getSelectionForWordAtPoint:convertFromPluginToPDFView(event.position())];
1335    if (selectionUnderMouse && [[selectionUnderMouse string] length])
1336        hitTestResult = Text;
1337
1338    if (hitTestResult == m_lastHitTestResult && mode == UpdateIfNeeded)
1339        return;
1340
1341    webFrame()->page()->send(Messages::WebPageProxy::SetCursor(hitTestResult == Text ? iBeamCursor() : pointerCursor()));
1342    m_lastHitTestResult = hitTestResult;
1343}
1344
1345bool PDFPlugin::handleMouseEvent(const WebMouseEvent& event)
1346{
1347    PlatformMouseEvent platformEvent = platform(event);
1348    IntPoint mousePosition = convertFromRootViewToPlugin(event.position());
1349
1350    m_lastMouseEvent = event;
1351
1352    RefPtr<Scrollbar> targetScrollbar;
1353    RefPtr<Scrollbar> targetScrollbarForLastMousePosition;
1354
1355    if (m_verticalScrollbarLayer) {
1356        IntRect verticalScrollbarFrame(m_verticalScrollbarLayer.get().frame);
1357        if (verticalScrollbarFrame.contains(mousePosition))
1358            targetScrollbar = verticalScrollbar();
1359        if (verticalScrollbarFrame.contains(m_lastMousePositionInPluginCoordinates))
1360            targetScrollbarForLastMousePosition = verticalScrollbar();
1361    }
1362
1363    if (m_horizontalScrollbarLayer) {
1364        IntRect horizontalScrollbarFrame(m_horizontalScrollbarLayer.get().frame);
1365        if (horizontalScrollbarFrame.contains(mousePosition))
1366            targetScrollbar = horizontalScrollbar();
1367        if (horizontalScrollbarFrame.contains(m_lastMousePositionInPluginCoordinates))
1368            targetScrollbarForLastMousePosition = horizontalScrollbar();
1369    }
1370
1371    if (m_scrollCornerLayer && IntRect(m_scrollCornerLayer.get().frame).contains(mousePosition))
1372        return false;
1373
1374    if ([pdfDocument() isLocked])
1375        return false;
1376
1377    // Right-clicks and Control-clicks always call handleContextMenuEvent as well.
1378    if (event.button() == WebMouseEvent::RightButton || (event.button() == WebMouseEvent::LeftButton && event.controlKey()))
1379        return true;
1380
1381    NSEvent *nsEvent = nsEventForWebMouseEvent(event);
1382
1383    switch (event.type()) {
1384    case WebEvent::MouseMove:
1385        mouseMovedInContentArea();
1386        updateCursor(event);
1387
1388        if (targetScrollbar) {
1389            if (!targetScrollbarForLastMousePosition) {
1390                targetScrollbar->mouseEntered();
1391                return true;
1392            }
1393            return targetScrollbar->mouseMoved(platformEvent);
1394        }
1395
1396        if (!targetScrollbar && targetScrollbarForLastMousePosition)
1397            targetScrollbarForLastMousePosition->mouseExited();
1398
1399        switch (event.button()) {
1400        case WebMouseEvent::LeftButton:
1401            [m_pdfLayerController mouseDragged:nsEvent];
1402            return true;
1403        case WebMouseEvent::RightButton:
1404        case WebMouseEvent::MiddleButton:
1405            return false;
1406        case WebMouseEvent::NoButton:
1407            [m_pdfLayerController mouseMoved:nsEvent];
1408            return true;
1409        }
1410    case WebEvent::MouseDown:
1411        switch (event.button()) {
1412        case WebMouseEvent::LeftButton:
1413            if (targetScrollbar)
1414                return targetScrollbar->mouseDown(platformEvent);
1415
1416            [m_pdfLayerController mouseDown:nsEvent];
1417            return true;
1418        case WebMouseEvent::RightButton:
1419            [m_pdfLayerController rightMouseDown:nsEvent];
1420            return true;
1421        case WebMouseEvent::MiddleButton:
1422        case WebMouseEvent::NoButton:
1423            return false;
1424        }
1425    case WebEvent::MouseUp:
1426        switch (event.button()) {
1427        case WebMouseEvent::LeftButton:
1428            if (targetScrollbar)
1429                return targetScrollbar->mouseUp(platformEvent);
1430
1431            [m_pdfLayerController mouseUp:nsEvent];
1432            return true;
1433        case WebMouseEvent::RightButton:
1434        case WebMouseEvent::MiddleButton:
1435        case WebMouseEvent::NoButton:
1436            return false;
1437        }
1438    default:
1439        break;
1440    }
1441
1442    return false;
1443}
1444
1445bool PDFPlugin::handleMouseEnterEvent(const WebMouseEvent& event)
1446{
1447    mouseEnteredContentArea();
1448    updateCursor(event, ForceUpdate);
1449    return false;
1450}
1451
1452bool PDFPlugin::handleMouseLeaveEvent(const WebMouseEvent&)
1453{
1454    mouseExitedContentArea();
1455    return false;
1456}
1457
1458bool PDFPlugin::showContextMenuAtPoint(const IntPoint& point)
1459{
1460    FrameView* frameView = webFrame()->coreFrame()->view();
1461    IntPoint contentsPoint = frameView->contentsToRootView(point);
1462    WebMouseEvent event(WebEvent::MouseDown, WebMouseEvent::RightButton, contentsPoint, contentsPoint, 0, 0, 0, 1, static_cast<WebEvent::Modifiers>(0), monotonicallyIncreasingTime());
1463    return handleContextMenuEvent(event);
1464}
1465
1466bool PDFPlugin::handleContextMenuEvent(const WebMouseEvent& event)
1467{
1468    FrameView* frameView = webFrame()->coreFrame()->view();
1469    IntPoint point = frameView->contentsToScreen(IntRect(frameView->windowToContents(event.position()), IntSize())).location();
1470
1471    if (NSMenu *nsMenu = [m_pdfLayerController menuForEvent:nsEventForWebMouseEvent(event)]) {
1472        WKPopupContextMenu(nsMenu, point);
1473        return true;
1474    }
1475
1476    return false;
1477}
1478
1479bool PDFPlugin::handleKeyboardEvent(const WebKeyboardEvent& event)
1480{
1481    NSEventType eventType;
1482
1483    if (!getEventTypeFromWebEvent(event, eventType))
1484        return false;
1485
1486    NSUInteger modifierFlags = modifierFlagsFromWebEvent(event);
1487
1488    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()];
1489
1490    switch (event.type()) {
1491    case WebEvent::KeyDown:
1492        return [m_pdfLayerController keyDown:fakeEvent];
1493    default:
1494        return false;
1495    }
1496
1497    return false;
1498}
1499
1500bool PDFPlugin::handleEditingCommand(const String& commandName, const String& argument)
1501{
1502    if (commandName == "copy")
1503        [m_pdfLayerController copySelection];
1504    else if (commandName == "selectAll")
1505        [m_pdfLayerController selectAll];
1506    else if (commandName == "takeFindStringFromSelection") {
1507        NSString *string = [m_pdfLayerController currentSelection].string;
1508        if (string.length)
1509            writeItemsToPasteboard(NSFindPboard, @[ [string dataUsingEncoding:NSUTF8StringEncoding] ], @[ NSPasteboardTypeString ]);
1510    }
1511
1512    return true;
1513}
1514
1515bool PDFPlugin::isEditingCommandEnabled(const String& commandName)
1516{
1517    if (commandName == "copy" || commandName == "takeFindStringFromSelection")
1518        return [m_pdfLayerController currentSelection];
1519
1520    if (commandName == "selectAll")
1521        return true;
1522
1523    return false;
1524}
1525
1526void PDFPlugin::setScrollOffset(const IntPoint& offset)
1527{
1528    m_scrollOffset = IntSize(offset.x(), offset.y());
1529
1530    [CATransaction begin];
1531    [m_pdfLayerController setScrollPosition:offset];
1532
1533    if (m_activeAnnotation)
1534        m_activeAnnotation->updateGeometry();
1535
1536    [CATransaction commit];
1537}
1538
1539void PDFPlugin::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
1540{
1541    if (scrollbar == horizontalScrollbar())
1542        [m_horizontalScrollbarLayer setNeedsDisplay];
1543    else if (scrollbar == verticalScrollbar())
1544        [m_verticalScrollbarLayer setNeedsDisplay];
1545}
1546
1547void PDFPlugin::invalidateScrollCornerRect(const IntRect& rect)
1548{
1549    [m_scrollCornerLayer setNeedsDisplay];
1550}
1551
1552bool PDFPlugin::isFullFramePlugin()
1553{
1554    // <object> or <embed> plugins will appear to be in their parent frame, so we have to
1555    // check whether our frame's widget is exactly our PluginView.
1556    Document* document = webFrame()->coreFrame()->document();
1557    return document->isPluginDocument() && static_cast<PluginDocument*>(document)->pluginWidget() == pluginView();
1558}
1559
1560bool PDFPlugin::handlesPageScaleFactor()
1561{
1562    return webFrame()->isMainFrame() && isFullFramePlugin();
1563}
1564
1565void PDFPlugin::clickedLink(NSURL *url)
1566{
1567    Frame* frame = webFrame()->coreFrame();
1568
1569    RefPtr<Event> coreEvent;
1570    if (m_lastMouseEvent.type() != WebEvent::NoType)
1571        coreEvent = MouseEvent::create(eventNames().clickEvent, frame->document()->defaultView(), platform(m_lastMouseEvent), 0, 0);
1572
1573    frame->loader().urlSelected(url, emptyString(), coreEvent.get(), LockHistory::No, LockBackForwardList::No, MaybeSendReferrer);
1574}
1575
1576void PDFPlugin::setActiveAnnotation(PDFAnnotation *annotation)
1577{
1578    if (!supportsForms())
1579        return;
1580
1581    if (m_activeAnnotation)
1582        m_activeAnnotation->commit();
1583
1584    if (annotation) {
1585        if ([annotation isKindOfClass:pdfAnnotationTextWidgetClass()] && static_cast<PDFAnnotationTextWidget *>(annotation).isReadOnly) {
1586            m_activeAnnotation = 0;
1587            return;
1588        }
1589
1590        m_activeAnnotation = PDFPluginAnnotation::create(annotation, m_pdfLayerController.get(), this);
1591        m_activeAnnotation->attach(m_annotationContainer.get());
1592    } else
1593        m_activeAnnotation = 0;
1594}
1595
1596bool PDFPlugin::supportsForms()
1597{
1598    // FIXME: We support forms for full-main-frame and <iframe> PDFs, but not <embed> or <object>, because those cases do not have their own Document into which to inject form elements.
1599    return isFullFramePlugin();
1600}
1601
1602void PDFPlugin::notifyContentScaleFactorChanged(CGFloat scaleFactor)
1603{
1604    if (handlesPageScaleFactor())
1605        pluginView()->setPageScaleFactor(scaleFactor, IntPoint());
1606
1607    calculateSizes();
1608    updateScrollbars();
1609}
1610
1611void PDFPlugin::notifyDisplayModeChanged(int)
1612{
1613    calculateSizes();
1614    updateScrollbars();
1615}
1616
1617PassRefPtr<SharedBuffer> PDFPlugin::liveResourceData() const
1618{
1619    NSData *pdfData = liveData();
1620
1621    if (!pdfData)
1622        return 0;
1623
1624    return SharedBuffer::wrapNSData(pdfData);
1625}
1626
1627void PDFPlugin::saveToPDF()
1628{
1629    // FIXME: We should probably notify the user that they can't save before the document is finished loading.
1630    // PDFViewController does an NSBeep(), but that seems insufficient.
1631    if (!pdfDocument())
1632        return;
1633
1634    NSData *data = liveData();
1635    webFrame()->page()->savePDFToFileInDownloadsFolder(m_suggestedFilename, webFrame()->url(), static_cast<const unsigned char *>([data bytes]), [data length]);
1636}
1637
1638void PDFPlugin::openWithNativeApplication()
1639{
1640    if (!m_temporaryPDFUUID) {
1641        // FIXME: We should probably notify the user that they can't save before the document is finished loading.
1642        // PDFViewController does an NSBeep(), but that seems insufficient.
1643        if (!pdfDocument())
1644            return;
1645
1646        NSData *data = liveData();
1647
1648        m_temporaryPDFUUID = WebCore::createCanonicalUUIDString();
1649        ASSERT(m_temporaryPDFUUID);
1650
1651        webFrame()->page()->savePDFToTemporaryFolderAndOpenWithNativeApplication(m_suggestedFilename, webFrame()->url(), static_cast<const unsigned char *>([data bytes]), [data length], m_temporaryPDFUUID);
1652        return;
1653    }
1654
1655    webFrame()->page()->send(Messages::WebPageProxy::OpenPDFFromTemporaryFolderWithNativeApplication(m_temporaryPDFUUID));
1656}
1657
1658void PDFPlugin::writeItemsToPasteboard(NSString *pasteboardName, NSArray *items, NSArray *types)
1659{
1660    Vector<String> pasteboardTypes;
1661
1662    for (NSString *type in types)
1663        pasteboardTypes.append(type);
1664
1665    uint64_t newChangeCount;
1666    WebProcess::shared().parentProcessConnection()->sendSync(Messages::WebContext::SetPasteboardTypes(pasteboardName, pasteboardTypes),
1667        Messages::WebContext::SetPasteboardTypes::Reply(newChangeCount), 0);
1668
1669    for (NSUInteger i = 0, count = items.count; i < count; ++i) {
1670        NSString *type = [types objectAtIndex:i];
1671        NSData *data = [items objectAtIndex:i];
1672
1673        // We don't expect the data for any items to be empty, but aren't completely sure.
1674        // Avoid crashing in the SharedMemory constructor in release builds if we're wrong.
1675        ASSERT(data.length);
1676        if (!data.length)
1677            continue;
1678
1679        if ([type isEqualToString:NSStringPboardType] || [type isEqualToString:NSPasteboardTypeString]) {
1680            RetainPtr<NSString> plainTextString = adoptNS([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
1681            WebProcess::shared().parentProcessConnection()->sendSync(Messages::WebContext::SetPasteboardStringForType(pasteboardName, type, plainTextString.get()),
1682                Messages::WebContext::SetPasteboardStringForType::Reply(newChangeCount), 0);
1683        } else {
1684            RefPtr<SharedBuffer> buffer = SharedBuffer::wrapNSData(data);
1685
1686            if (!buffer)
1687                continue;
1688
1689            SharedMemory::Handle handle;
1690            RefPtr<SharedMemory> sharedMemory = SharedMemory::create(buffer->size());
1691            memcpy(sharedMemory->data(), buffer->data(), buffer->size());
1692            sharedMemory->createHandle(handle, SharedMemory::ReadOnly);
1693            WebProcess::shared().parentProcessConnection()->sendSync(Messages::WebContext::SetPasteboardBufferForType(pasteboardName, type, handle, buffer->size()),
1694                Messages::WebContext::SetPasteboardBufferForType::Reply(newChangeCount), 0);
1695        }
1696    }
1697}
1698
1699void PDFPlugin::showDefinitionForAttributedString(NSAttributedString *string, CGPoint point)
1700{
1701    DictionaryPopupInfo dictionaryPopupInfo;
1702    dictionaryPopupInfo.origin = convertFromPDFViewToRootView(IntPoint(point));
1703
1704    AttributedString attributedString;
1705    attributedString.string = string;
1706
1707    webFrame()->page()->send(Messages::WebPageProxy::DidPerformDictionaryLookup(attributedString, dictionaryPopupInfo));
1708}
1709
1710unsigned PDFPlugin::countFindMatches(const String& target, WebCore::FindOptions options, unsigned maxMatchCount)
1711{
1712    if (!target.length())
1713        return 0;
1714
1715    int nsOptions = (options & FindOptionsCaseInsensitive) ? NSCaseInsensitiveSearch : 0;
1716
1717    return [[pdfDocument() findString:target withOptions:nsOptions] count];
1718}
1719
1720PDFSelection *PDFPlugin::nextMatchForString(const String& target, BOOL searchForward, BOOL caseSensitive, BOOL wrapSearch, PDFSelection *initialSelection, BOOL startInSelection)
1721{
1722    if (!target.length())
1723        return nil;
1724
1725    NSStringCompareOptions options = 0;
1726    if (!searchForward)
1727        options |= NSBackwardsSearch;
1728
1729    if (!caseSensitive)
1730        options |= NSCaseInsensitiveSearch;
1731
1732    PDFDocument *document = pdfDocument().get();
1733
1734    PDFSelection *selectionForInitialSearch = [initialSelection copy];
1735    if (startInSelection) {
1736        // Initially we want to include the selected text in the search. So we must modify the starting search
1737        // selection to fit PDFDocument's search requirements: selection must have a length >= 1, begin before
1738        // the current selection (if searching forwards) or after (if searching backwards).
1739        int initialSelectionLength = [[initialSelection string] length];
1740        if (searchForward) {
1741            [selectionForInitialSearch extendSelectionAtStart:1];
1742            [selectionForInitialSearch extendSelectionAtEnd:-initialSelectionLength];
1743        } else {
1744            [selectionForInitialSearch extendSelectionAtEnd:1];
1745            [selectionForInitialSearch extendSelectionAtStart:-initialSelectionLength];
1746        }
1747    }
1748
1749    PDFSelection *foundSelection = [document findString:target fromSelection:selectionForInitialSearch withOptions:options];
1750    [selectionForInitialSearch release];
1751
1752    // If we first searched in the selection, and we found the selection, search again from just past the selection.
1753    if (startInSelection && [foundSelection isEqual:initialSelection])
1754        foundSelection = [document findString:target fromSelection:initialSelection withOptions:options];
1755
1756    if (!foundSelection && wrapSearch)
1757        foundSelection = [document findString:target fromSelection:nil withOptions:options];
1758
1759    return foundSelection;
1760}
1761
1762bool PDFPlugin::findString(const String& target, WebCore::FindOptions options, unsigned maxMatchCount)
1763{
1764    BOOL searchForward = !(options & FindOptionsBackwards);
1765    BOOL caseSensitive = !(options & FindOptionsCaseInsensitive);
1766    BOOL wrapSearch = options & FindOptionsWrapAround;
1767
1768    unsigned matchCount;
1769    if (!maxMatchCount) {
1770        // If the max was zero, any result means we exceeded the max. We can skip computing the actual count.
1771        matchCount = static_cast<unsigned>(kWKMoreThanMaximumMatchCount);
1772    } else {
1773        matchCount = countFindMatches(target, options, maxMatchCount);
1774        if (matchCount > maxMatchCount)
1775            matchCount = static_cast<unsigned>(kWKMoreThanMaximumMatchCount);
1776    }
1777
1778    if (target.isEmpty()) {
1779        PDFSelection* searchSelection = [m_pdfLayerController searchSelection];
1780        [m_pdfLayerController findString:target caseSensitive:caseSensitive highlightMatches:YES];
1781        [m_pdfLayerController setSearchSelection:searchSelection];
1782        m_lastFoundString = emptyString();
1783        return false;
1784    }
1785
1786    if (m_lastFoundString == target) {
1787        PDFSelection *selection = nextMatchForString(target, searchForward, caseSensitive, wrapSearch, [m_pdfLayerController searchSelection], NO);
1788        if (!selection)
1789            return false;
1790
1791        [m_pdfLayerController setSearchSelection:selection];
1792        [m_pdfLayerController gotoSelection:selection];
1793    } else {
1794        [m_pdfLayerController findString:target caseSensitive:caseSensitive highlightMatches:YES];
1795        m_lastFoundString = target;
1796    }
1797
1798    return matchCount > 0;
1799}
1800
1801bool PDFPlugin::performDictionaryLookupAtLocation(const WebCore::FloatPoint& point)
1802{
1803    IntPoint localPoint = convertFromRootViewToPlugin(roundedIntPoint(point));
1804    PDFSelection* lookupSelection = [m_pdfLayerController getSelectionForWordAtPoint:convertFromPluginToPDFView(localPoint)];
1805
1806    if ([[lookupSelection string] length])
1807        [m_pdfLayerController searchInDictionaryWithSelection:lookupSelection];
1808
1809    return true;
1810}
1811
1812void PDFPlugin::focusNextAnnotation()
1813{
1814    [m_pdfLayerController activateNextAnnotation:false];
1815}
1816
1817void PDFPlugin::focusPreviousAnnotation()
1818{
1819    [m_pdfLayerController activateNextAnnotation:true];
1820}
1821
1822void PDFPlugin::notifySelectionChanged(PDFSelection *)
1823{
1824    webFrame()->page()->didChangeSelection();
1825}
1826
1827String PDFPlugin::getSelectionString() const
1828{
1829    return [[m_pdfLayerController currentSelection] string];
1830}
1831
1832void PDFPlugin::performWebSearch(NSString *string)
1833{
1834    webFrame()->page()->send(Messages::WebPageProxy::SearchTheWeb(string));
1835}
1836
1837void PDFPlugin::performSpotlightSearch(NSString *string)
1838{
1839    webFrame()->page()->send(Messages::WebPageProxy::SearchWithSpotlight(string));
1840}
1841
1842bool PDFPlugin::handleWheelEvent(const WebWheelEvent& event)
1843{
1844    PDFDisplayMode displayMode = [m_pdfLayerController displayMode];
1845
1846    if (displayMode == kPDFDisplaySinglePageContinuous || displayMode == kPDFDisplayTwoUpContinuous)
1847        return ScrollableArea::handleWheelEvent(platform(event));
1848
1849    NSUInteger currentPageIndex = [m_pdfLayerController currentPageIndex];
1850    bool inFirstPage = !currentPageIndex;
1851    bool inLastPage = [m_pdfLayerController lastPageIndex] == currentPageIndex;
1852
1853    bool atScrollTop = !scrollPosition().y();
1854    bool atScrollBottom = scrollPosition().y() == maximumScrollPosition().y();
1855
1856    bool inMomentumScroll = event.momentumPhase() != WebWheelEvent::PhaseNone;
1857
1858    int scrollMagnitudeThresholdForPageFlip = defaultScrollMagnitudeThresholdForPageFlip;
1859
1860    // Imprecise input devices should have a lower threshold so that "clicky" scroll wheels can flip pages.
1861    if (!event.hasPreciseScrollingDeltas())
1862        scrollMagnitudeThresholdForPageFlip = 0;
1863
1864    if (atScrollBottom && !inLastPage && event.delta().height() < 0) {
1865        if (event.delta().height() <= -scrollMagnitudeThresholdForPageFlip && !inMomentumScroll)
1866            [m_pdfLayerController gotoNextPage];
1867        return true;
1868    }
1869
1870    if (atScrollTop && !inFirstPage && event.delta().height() > 0) {
1871        if (event.delta().height() >= scrollMagnitudeThresholdForPageFlip && !inMomentumScroll) {
1872            [CATransaction begin];
1873            [m_pdfLayerController gotoPreviousPage];
1874            scrollToOffsetWithoutAnimation(maximumScrollPosition());
1875            [CATransaction commit];
1876        }
1877        return true;
1878    }
1879
1880    return ScrollableArea::handleWheelEvent(platform(event));
1881}
1882
1883NSData *PDFPlugin::liveData() const
1884{
1885    if (m_activeAnnotation)
1886        m_activeAnnotation->commit();
1887
1888    // Save data straight from the resource instead of PDFKit if the document is
1889    // untouched by the user, so that PDFs which PDFKit can't display will still be downloadable.
1890    if (m_pdfDocumentWasMutated)
1891        return [m_pdfDocument dataRepresentation];
1892
1893    return rawData();
1894}
1895
1896NSObject *PDFPlugin::accessibilityObject() const
1897{
1898    return m_accessibilityObject.get();
1899}
1900
1901} // namespace WebKit
1902
1903#endif // ENABLE(PDFKIT_PLUGIN)
1904