~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Source/WebKit2/WebProcess/Plugins/PDF/SimplePDFPlugin.mm

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 "SimplePDFPlugin.h"
 
28
 
 
29
#import "PDFKitImports.h"
 
30
#import "PluginView.h"
 
31
#import "ShareableBitmap.h"
 
32
#import "WebEvent.h"
 
33
#import "WebEventConversion.h"
 
34
#import <JavaScriptCore/JSContextRef.h>
 
35
#import <JavaScriptCore/JSObjectRef.h>
 
36
#import <JavaScriptCore/JSStringRef.h>
 
37
#import <JavaScriptCore/JSStringRefCF.h>
 
38
#import <PDFKit/PDFKit.h>
 
39
#import <WebCore/ArchiveResource.h>
 
40
#import <WebCore/Chrome.h>
 
41
#import <WebCore/DocumentLoader.h>
 
42
#import <WebCore/FocusController.h>
 
43
#import <WebCore/Frame.h>
 
44
#import <WebCore/FrameView.h>
 
45
#import <WebCore/GraphicsContext.h>
 
46
#import <WebCore/HTTPHeaderMap.h>
 
47
#import <WebCore/LocalizedStrings.h>
 
48
#import <WebCore/Page.h>
 
49
#import <WebCore/PluginData.h>
 
50
#import <WebCore/RenderBoxModelObject.h>
 
51
#import <WebCore/ScrollAnimator.h>
 
52
#import <WebCore/ScrollbarTheme.h>
 
53
 
 
54
using namespace WebCore;
 
55
using namespace std;
 
56
 
 
57
static void appendValuesInPDFNameSubtreeToVector(CGPDFDictionaryRef subtree, Vector<CGPDFObjectRef>& values)
 
58
{
 
59
    CGPDFArrayRef names;
 
60
    if (CGPDFDictionaryGetArray(subtree, "Names", &names)) {
 
61
        size_t nameCount = CGPDFArrayGetCount(names) / 2;
 
62
        for (size_t i = 0; i < nameCount; ++i) {
 
63
            CGPDFObjectRef object;
 
64
            CGPDFArrayGetObject(names, 2 * i + 1, &object);
 
65
            values.append(object);
 
66
        }
 
67
        return;
 
68
    }
 
69
 
 
70
    CGPDFArrayRef kids;
 
71
    if (!CGPDFDictionaryGetArray(subtree, "Kids", &kids))
 
72
        return;
 
73
 
 
74
    size_t kidCount = CGPDFArrayGetCount(kids);
 
75
    for (size_t i = 0; i < kidCount; ++i) {
 
76
        CGPDFDictionaryRef kid;
 
77
        if (!CGPDFArrayGetDictionary(kids, i, &kid))
 
78
            continue;
 
79
        appendValuesInPDFNameSubtreeToVector(kid, values);
 
80
    }
 
81
}
 
82
 
 
83
static void getAllValuesInPDFNameTree(CGPDFDictionaryRef tree, Vector<CGPDFObjectRef>& allValues)
 
84
{
 
85
    appendValuesInPDFNameSubtreeToVector(tree, allValues);
 
86
}
 
87
 
 
88
static void getAllScriptsInPDFDocument(CGPDFDocumentRef pdfDocument, Vector<RetainPtr<CFStringRef> >& scripts)
 
89
{
 
90
    if (!pdfDocument)
 
91
        return;
 
92
 
 
93
    CGPDFDictionaryRef pdfCatalog = CGPDFDocumentGetCatalog(pdfDocument);
 
94
    if (!pdfCatalog)
 
95
        return;
 
96
 
 
97
    // Get the dictionary of all document-level name trees.
 
98
    CGPDFDictionaryRef namesDictionary;
 
99
    if (!CGPDFDictionaryGetDictionary(pdfCatalog, "Names", &namesDictionary))
 
100
        return;
 
101
 
 
102
    // Get the document-level "JavaScript" name tree.
 
103
    CGPDFDictionaryRef javaScriptNameTree;
 
104
    if (!CGPDFDictionaryGetDictionary(namesDictionary, "JavaScript", &javaScriptNameTree))
 
105
        return;
 
106
 
 
107
    // The names are arbitrary. We are only interested in the values.
 
108
    Vector<CGPDFObjectRef> objects;
 
109
    getAllValuesInPDFNameTree(javaScriptNameTree, objects);
 
110
    size_t objectCount = objects.size();
 
111
 
 
112
    for (size_t i = 0; i < objectCount; ++i) {
 
113
        CGPDFDictionaryRef javaScriptAction;
 
114
        if (!CGPDFObjectGetValue(reinterpret_cast<CGPDFObjectRef>(objects[i]), kCGPDFObjectTypeDictionary, &javaScriptAction))
 
115
            continue;
 
116
 
 
117
        // A JavaScript action must have an action type of "JavaScript".
 
118
        const char* actionType;
 
119
        if (!CGPDFDictionaryGetName(javaScriptAction, "S", &actionType) || strcmp(actionType, "JavaScript"))
 
120
            continue;
 
121
 
 
122
        const UInt8* bytes = 0;
 
123
        CFIndex length;
 
124
        CGPDFStreamRef stream;
 
125
        CGPDFStringRef string;
 
126
        RetainPtr<CFDataRef> data;
 
127
        if (CGPDFDictionaryGetStream(javaScriptAction, "JS", &stream)) {
 
128
            CGPDFDataFormat format;
 
129
            data.adoptCF(CGPDFStreamCopyData(stream, &format));
 
130
            if (!data)
 
131
                continue;
 
132
            bytes = CFDataGetBytePtr(data.get());
 
133
            length = CFDataGetLength(data.get());
 
134
        } else if (CGPDFDictionaryGetString(javaScriptAction, "JS", &string)) {
 
135
            bytes = CGPDFStringGetBytePtr(string);
 
136
            length = CGPDFStringGetLength(string);
 
137
        }
 
138
        if (!bytes)
 
139
            continue;
 
140
 
 
141
        CFStringEncoding encoding = (length > 1 && bytes[0] == 0xFE && bytes[1] == 0xFF) ? kCFStringEncodingUnicode : kCFStringEncodingUTF8;
 
142
        RetainPtr<CFStringRef> script(AdoptCF, CFStringCreateWithBytes(kCFAllocatorDefault, bytes, length, encoding, true));
 
143
        if (!script)
 
144
            continue;
 
145
 
 
146
        scripts.append(script);
 
147
    }
 
148
}
 
149
 
 
150
namespace WebKit {
 
151
 
 
152
const uint64_t pdfDocumentRequestID = 1; // PluginController supports loading multiple streams, but we only need one for PDF.
 
153
 
 
154
const int gutterHeight = 10;
 
155
const int shadowOffsetX = 0;
 
156
const int shadowOffsetY = -2;
 
157
const int shadowSize = 7;
 
158
 
 
159
PassRefPtr<SimplePDFPlugin> SimplePDFPlugin::create(WebFrame* frame)
 
160
{
 
161
    return adoptRef(new SimplePDFPlugin(frame));
 
162
}
 
163
 
 
164
SimplePDFPlugin::SimplePDFPlugin(WebFrame* frame)
 
165
    : m_frame(frame)
 
166
{
 
167
}
 
168
 
 
169
SimplePDFPlugin::~SimplePDFPlugin()
 
170
{
 
171
}
 
172
 
 
173
PluginInfo SimplePDFPlugin::pluginInfo()
 
174
{
 
175
    PluginInfo info;
 
176
    info.name = builtInPDFPluginName();
 
177
 
 
178
    MimeClassInfo mimeClassInfo;
 
179
    mimeClassInfo.type = "application/pdf";
 
180
    mimeClassInfo.desc = pdfDocumentTypeDescription();
 
181
    mimeClassInfo.extensions.append("pdf");
 
182
 
 
183
    info.mimes.append(mimeClassInfo);
 
184
    return info;
 
185
}
 
186
 
 
187
PluginView* SimplePDFPlugin::pluginView()
 
188
{
 
189
    return static_cast<PluginView*>(controller());
 
190
}
 
191
 
 
192
const PluginView* SimplePDFPlugin::pluginView() const
 
193
{
 
194
    return static_cast<const PluginView*>(controller());
 
195
}
 
196
 
 
197
void SimplePDFPlugin::updateScrollbars()
 
198
{
 
199
    bool hadScrollbars = m_horizontalScrollbar || m_verticalScrollbar;
 
200
 
 
201
    if (m_horizontalScrollbar) {
 
202
        if (m_size.width() >= m_pdfDocumentSize.width())
 
203
            destroyScrollbar(HorizontalScrollbar);
 
204
    } else if (m_size.width() < m_pdfDocumentSize.width())
 
205
        m_horizontalScrollbar = createScrollbar(HorizontalScrollbar);
 
206
 
 
207
    if (m_verticalScrollbar) {
 
208
        if (m_size.height() >= m_pdfDocumentSize.height())
 
209
            destroyScrollbar(VerticalScrollbar);
 
210
    } else if (m_size.height() < m_pdfDocumentSize.height())
 
211
        m_verticalScrollbar = createScrollbar(VerticalScrollbar);
 
212
 
 
213
    int horizontalScrollbarHeight = (m_horizontalScrollbar && !m_horizontalScrollbar->isOverlayScrollbar()) ? m_horizontalScrollbar->height() : 0;
 
214
    int verticalScrollbarWidth = (m_verticalScrollbar && !m_verticalScrollbar->isOverlayScrollbar()) ? m_verticalScrollbar->width() : 0;
 
215
 
 
216
    int pageStep = m_pageBoxes.isEmpty() ? 0 : m_pageBoxes[0].height();
 
217
 
 
218
    if (m_horizontalScrollbar) {
 
219
        m_horizontalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
 
220
        m_horizontalScrollbar->setProportion(m_size.width() - verticalScrollbarWidth, m_pdfDocumentSize.width());
 
221
        IntRect scrollbarRect(pluginView()->x(), pluginView()->y() + m_size.height() - m_horizontalScrollbar->height(), m_size.width(), m_horizontalScrollbar->height());
 
222
        if (m_verticalScrollbar)
 
223
            scrollbarRect.contract(m_verticalScrollbar->width(), 0);
 
224
        m_horizontalScrollbar->setFrameRect(scrollbarRect);
 
225
    }
 
226
    if (m_verticalScrollbar) {
 
227
        m_verticalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
 
228
        m_verticalScrollbar->setProportion(m_size.height() - horizontalScrollbarHeight, m_pdfDocumentSize.height());
 
229
        IntRect scrollbarRect(IntRect(pluginView()->x() + m_size.width() - m_verticalScrollbar->width(), pluginView()->y(), m_verticalScrollbar->width(), m_size.height()));
 
230
        if (m_horizontalScrollbar)
 
231
            scrollbarRect.contract(0, m_horizontalScrollbar->height());
 
232
        m_verticalScrollbar->setFrameRect(scrollbarRect);
 
233
    }
 
234
    
 
235
    FrameView* frameView = m_frame->coreFrame()->view();
 
236
    if (!frameView)
 
237
        return;
 
238
 
 
239
    bool hasScrollbars = m_horizontalScrollbar || m_verticalScrollbar;
 
240
    if (hadScrollbars != hasScrollbars) {
 
241
        if (hasScrollbars)
 
242
            frameView->addScrollableArea(this);
 
243
        else
 
244
            frameView->removeScrollableArea(this);
 
245
 
 
246
        frameView->setNeedsLayout();
 
247
    }
 
248
}
 
249
 
 
250
PassRefPtr<Scrollbar> SimplePDFPlugin::createScrollbar(ScrollbarOrientation orientation)
 
251
{
 
252
    RefPtr<Scrollbar> widget = Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar);
 
253
    if (orientation == HorizontalScrollbar)
 
254
        didAddHorizontalScrollbar(widget.get());
 
255
    else 
 
256
        didAddVerticalScrollbar(widget.get());
 
257
    pluginView()->frame()->view()->addChild(widget.get());
 
258
    return widget.release();
 
259
}
 
260
 
 
261
void SimplePDFPlugin::destroyScrollbar(ScrollbarOrientation orientation)
 
262
{
 
263
    RefPtr<Scrollbar>& scrollbar = orientation == HorizontalScrollbar ? m_horizontalScrollbar : m_verticalScrollbar;
 
264
    if (!scrollbar)
 
265
        return;
 
266
 
 
267
    if (orientation == HorizontalScrollbar)
 
268
        willRemoveHorizontalScrollbar(scrollbar.get());
 
269
    else
 
270
        willRemoveVerticalScrollbar(scrollbar.get());
 
271
 
 
272
    scrollbar->removeFromParent();
 
273
    scrollbar->disconnectFromScrollableArea();
 
274
    scrollbar = 0;
 
275
}
 
276
 
 
277
void SimplePDFPlugin::addArchiveResource()
 
278
{
 
279
    // FIXME: It's a hack to force add a resource to DocumentLoader. PDF documents should just be fetched as CachedResources.
 
280
 
 
281
    // Add just enough data for context menu handling and web archives to work.
 
282
    ResourceResponse synthesizedResponse;
 
283
    synthesizedResponse.setSuggestedFilename(m_suggestedFilename);
 
284
    synthesizedResponse.setURL(m_sourceURL); // Needs to match the HitTestResult::absolutePDFURL.
 
285
    synthesizedResponse.setMimeType("application/pdf");
 
286
 
 
287
    RefPtr<ArchiveResource> resource = ArchiveResource::create(SharedBuffer::wrapCFData(m_data.get()), m_sourceURL, "application/pdf", String(), String(), synthesizedResponse);
 
288
    pluginView()->frame()->document()->loader()->addArchiveResource(resource.release());
 
289
}
 
290
 
 
291
static void jsPDFDocInitialize(JSContextRef ctx, JSObjectRef object)
 
292
{
 
293
    SimplePDFPlugin* pdfView = static_cast<SimplePDFPlugin*>(JSObjectGetPrivate(object));
 
294
    pdfView->ref();
 
295
}
 
296
 
 
297
static void jsPDFDocFinalize(JSObjectRef object)
 
298
{
 
299
    SimplePDFPlugin* pdfView = static_cast<SimplePDFPlugin*>(JSObjectGetPrivate(object));
 
300
    pdfView->deref();
 
301
}
 
302
 
 
303
JSValueRef SimplePDFPlugin::jsPDFDocPrint(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
 
304
{
 
305
    SimplePDFPlugin* pdfView = static_cast<SimplePDFPlugin*>(JSObjectGetPrivate(thisObject));
 
306
    
 
307
    WebFrame* frame = pdfView->m_frame;
 
308
    if (!frame)
 
309
        return JSValueMakeUndefined(ctx);
 
310
    
 
311
    Frame* coreFrame = frame->coreFrame();
 
312
    if (!coreFrame)
 
313
        return JSValueMakeUndefined(ctx);
 
314
    
 
315
    Page* page = coreFrame->page();
 
316
    if (!page)
 
317
        return JSValueMakeUndefined(ctx);
 
318
    
 
319
    page->chrome()->print(coreFrame);
 
320
    
 
321
    return JSValueMakeUndefined(ctx);
 
322
}
 
323
 
 
324
JSObjectRef SimplePDFPlugin::makeJSPDFDoc(JSContextRef ctx)
 
325
{
 
326
    static JSStaticFunction jsPDFDocStaticFunctions[] = {
 
327
        { "print", jsPDFDocPrint, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
 
328
        { 0, 0, 0 },
 
329
    };
 
330
    
 
331
    static JSClassDefinition jsPDFDocClassDefinition = {
 
332
        0,
 
333
        kJSClassAttributeNone,
 
334
        "Doc",
 
335
        0,
 
336
        0,
 
337
        jsPDFDocStaticFunctions,
 
338
        jsPDFDocInitialize, jsPDFDocFinalize, 0, 0, 0, 0, 0, 0, 0, 0, 0
 
339
    };
 
340
    
 
341
    static JSClassRef jsPDFDocClass = JSClassCreate(&jsPDFDocClassDefinition);
 
342
    
 
343
    return JSObjectMake(ctx, jsPDFDocClass, this);
 
344
}
 
345
 
 
346
void SimplePDFPlugin::pdfDocumentDidLoad()
 
347
{
 
348
    addArchiveResource();
 
349
 
 
350
    m_pdfDocument.adoptNS([[pdfDocumentClass() alloc] initWithData:(NSData *)m_data.get()]);
 
351
 
 
352
    calculateSizes();
 
353
    updateScrollbars();
 
354
 
 
355
    controller()->invalidate(IntRect(0, 0, m_size.width(), m_size.height()));
 
356
 
 
357
    runScriptsInPDFDocument();
 
358
}
 
359
    
 
360
void SimplePDFPlugin::runScriptsInPDFDocument()
 
361
{
 
362
    Vector<RetainPtr<CFStringRef> > scripts;
 
363
    getAllScriptsInPDFDocument([m_pdfDocument.get() documentRef], scripts);
 
364
 
 
365
    size_t scriptCount = scripts.size();
 
366
    if (!scriptCount)
 
367
        return;
 
368
 
 
369
    JSGlobalContextRef ctx = JSGlobalContextCreate(0);
 
370
    JSObjectRef jsPDFDoc = makeJSPDFDoc(ctx);
 
371
 
 
372
    for (size_t i = 0; i < scriptCount; ++i) {
 
373
        JSStringRef script = JSStringCreateWithCFString(scripts[i].get());
 
374
        JSEvaluateScript(ctx, script, jsPDFDoc, 0, 0, 0);
 
375
        JSStringRelease(script);
 
376
    }
 
377
 
 
378
    JSGlobalContextRelease(ctx);
 
379
}
 
380
    
 
381
void SimplePDFPlugin::computePageBoxes()
 
382
{
 
383
    size_t pageCount = CGPDFDocumentGetNumberOfPages([m_pdfDocument.get() documentRef]);
 
384
    for (size_t i = 0; i < pageCount; ++i) {
 
385
        CGPDFPageRef pdfPage = CGPDFDocumentGetPage([m_pdfDocument.get() documentRef], i + 1);
 
386
        ASSERT(pdfPage);
 
387
        
 
388
        CGRect box = CGPDFPageGetBoxRect(pdfPage, kCGPDFCropBox);
 
389
        if (CGRectIsEmpty(box))
 
390
            box = CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox);
 
391
        m_pageBoxes.append(IntRect(box));
 
392
    }
 
393
}
 
394
 
 
395
void SimplePDFPlugin::calculateSizes()
 
396
{
 
397
    size_t pageCount = CGPDFDocumentGetNumberOfPages([m_pdfDocument.get() documentRef]);
 
398
    for (size_t i = 0; i < pageCount; ++i) {
 
399
        CGPDFPageRef pdfPage = CGPDFDocumentGetPage([m_pdfDocument.get() documentRef], i + 1);
 
400
        ASSERT(pdfPage);
 
401
 
 
402
        CGRect box = CGPDFPageGetBoxRect(pdfPage, kCGPDFCropBox);
 
403
        if (CGRectIsEmpty(box))
 
404
            box = CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox);
 
405
        m_pageBoxes.append(IntRect(box));
 
406
        m_pdfDocumentSize.setWidth(max(m_pdfDocumentSize.width(), static_cast<int>(box.size.width)));
 
407
        m_pdfDocumentSize.expand(0, box.size.height);
 
408
    }
 
409
    m_pdfDocumentSize.expand(0, gutterHeight * (m_pageBoxes.size() - 1));
 
410
}
 
411
 
 
412
bool SimplePDFPlugin::initialize(const Parameters& parameters)
 
413
{
 
414
    // Load the src URL if needed.
 
415
    m_sourceURL = parameters.url;
 
416
    if (!parameters.shouldUseManualLoader && !parameters.url.isEmpty())
 
417
        controller()->loadURL(pdfDocumentRequestID, "GET", parameters.url.string(), String(), HTTPHeaderMap(), Vector<uint8_t>(), false);
 
418
 
 
419
    controller()->didInitializePlugin();
 
420
    return true;
 
421
}
 
422
 
 
423
void SimplePDFPlugin::destroy()
 
424
{
 
425
    if (m_frame) {
 
426
        if (FrameView* frameView = m_frame->coreFrame()->view())
 
427
            frameView->removeScrollableArea(this);
 
428
    }
 
429
 
 
430
    destroyScrollbar(HorizontalScrollbar);
 
431
    destroyScrollbar(VerticalScrollbar);
 
432
}
 
433
 
 
434
void SimplePDFPlugin::paint(GraphicsContext* graphicsContext, const IntRect& dirtyRect)
 
435
{
 
436
    contentAreaWillPaint();
 
437
 
 
438
    paintBackground(graphicsContext, dirtyRect);
 
439
 
 
440
    if (!m_pdfDocument) // FIXME: Draw loading progress.
 
441
        return;
 
442
 
 
443
    paintContent(graphicsContext, dirtyRect);
 
444
    paintControls(graphicsContext, dirtyRect);
 
445
}
 
446
 
 
447
void SimplePDFPlugin::paintBackground(GraphicsContext* graphicsContext, const IntRect& dirtyRect)
 
448
{
 
449
    GraphicsContextStateSaver stateSaver(*graphicsContext);
 
450
    graphicsContext->setFillColor(Color::gray, ColorSpaceDeviceRGB);
 
451
    graphicsContext->fillRect(dirtyRect);
 
452
}
 
453
 
 
454
void SimplePDFPlugin::paintContent(GraphicsContext* graphicsContext, const IntRect& dirtyRect)
 
455
{
 
456
    GraphicsContextStateSaver stateSaver(*graphicsContext);
 
457
    CGContextRef context = graphicsContext->platformContext();
 
458
 
 
459
    graphicsContext->setImageInterpolationQuality(InterpolationHigh);
 
460
    graphicsContext->setShouldAntialias(true);
 
461
    graphicsContext->setShouldSmoothFonts(true);
 
462
    graphicsContext->setFillColor(Color::white, ColorSpaceDeviceRGB);
 
463
 
 
464
    graphicsContext->clip(dirtyRect);
 
465
    IntRect contentRect(dirtyRect);
 
466
    contentRect.moveBy(IntPoint(m_scrollOffset));
 
467
    graphicsContext->translate(-m_scrollOffset.width(), -m_scrollOffset.height());
 
468
 
 
469
    CGContextScaleCTM(context, 1, -1);
 
470
 
 
471
    int pageTop = 0;
 
472
    for (size_t i = 0; i < m_pageBoxes.size(); ++i) {
 
473
        IntRect pageBox = m_pageBoxes[i];
 
474
        float extraOffsetForCenteringX = max(roundf((m_size.width() - pageBox.width()) / 2.0f), 0.0f);
 
475
        float extraOffsetForCenteringY = (m_pageBoxes.size() == 1) ? max(roundf((m_size.height() - pageBox.height() + shadowOffsetY) / 2.0f), 0.0f) : 0;
 
476
 
 
477
        if (pageTop > contentRect.maxY())
 
478
            break;
 
479
        if (pageTop + pageBox.height() + extraOffsetForCenteringY + gutterHeight >= contentRect.y()) {
 
480
            CGPDFPageRef pdfPage = CGPDFDocumentGetPage([m_pdfDocument.get() documentRef], i + 1);
 
481
 
 
482
            graphicsContext->save();
 
483
            graphicsContext->translate(extraOffsetForCenteringX - pageBox.x(), -extraOffsetForCenteringY - pageBox.y() - pageBox.height());
 
484
 
 
485
            graphicsContext->setShadow(FloatSize(shadowOffsetX, shadowOffsetY), shadowSize, Color::black, ColorSpaceDeviceRGB);
 
486
            graphicsContext->fillRect(pageBox);
 
487
            graphicsContext->clearShadow();
 
488
 
 
489
            graphicsContext->clip(pageBox);
 
490
 
 
491
            CGContextDrawPDFPage(context, pdfPage);
 
492
            graphicsContext->restore();
 
493
        }
 
494
        pageTop += pageBox.height() + gutterHeight;
 
495
        CGContextTranslateCTM(context, 0, -pageBox.height() - gutterHeight);
 
496
    }
 
497
}
 
498
 
 
499
void SimplePDFPlugin::paintControls(GraphicsContext* graphicsContext, const IntRect& dirtyRect)
 
500
{
 
501
    {
 
502
        GraphicsContextStateSaver stateSaver(*graphicsContext);
 
503
        IntRect scrollbarDirtyRect = dirtyRect;
 
504
        scrollbarDirtyRect.moveBy(pluginView()->frameRect().location());
 
505
        graphicsContext->translate(-pluginView()->frameRect().x(), -pluginView()->frameRect().y());
 
506
 
 
507
        if (m_horizontalScrollbar)
 
508
            m_horizontalScrollbar->paint(graphicsContext, scrollbarDirtyRect);
 
509
 
 
510
        if (m_verticalScrollbar)
 
511
            m_verticalScrollbar->paint(graphicsContext, scrollbarDirtyRect);
 
512
    }
 
513
 
 
514
    IntRect dirtyCornerRect = intersection(scrollCornerRect(), dirtyRect);
 
515
    ScrollbarTheme::theme()->paintScrollCorner(0, graphicsContext, dirtyCornerRect);
 
516
}
 
517
 
 
518
void SimplePDFPlugin::updateControlTints(GraphicsContext* graphicsContext)
 
519
{
 
520
    ASSERT(graphicsContext->updatingControlTints());
 
521
 
 
522
    if (m_horizontalScrollbar)
 
523
        m_horizontalScrollbar->invalidate();
 
524
    if (m_verticalScrollbar)
 
525
        m_verticalScrollbar->invalidate();
 
526
    invalidateScrollCorner(scrollCornerRect());
 
527
}
 
528
 
 
529
PassRefPtr<ShareableBitmap> SimplePDFPlugin::snapshot()
 
530
{
 
531
    return 0;
 
532
}
 
533
 
 
534
#if PLATFORM(MAC)
 
535
PlatformLayer* SimplePDFPlugin::pluginLayer()
 
536
{
 
537
    return 0;
 
538
}
 
539
#endif
 
540
 
 
541
 
 
542
bool SimplePDFPlugin::isTransparent()
 
543
{
 
544
    // This should never be called from the web process.
 
545
    ASSERT_NOT_REACHED();
 
546
    return false;
 
547
}
 
548
 
 
549
bool SimplePDFPlugin::wantsWheelEvents()
 
550
{
 
551
    return true;
 
552
}
 
553
 
 
554
void SimplePDFPlugin::geometryDidChange(const IntSize& size, const IntRect& clipRect, const AffineTransform& pluginToRootViewTransform)
 
555
{
 
556
    if (m_size == size) {
 
557
        // Nothing to do.
 
558
        return;
 
559
    }
 
560
 
 
561
    m_size = size;
 
562
    updateScrollbars();
 
563
}
 
564
 
 
565
void SimplePDFPlugin::visibilityDidChange()
 
566
{
 
567
}
 
568
 
 
569
void SimplePDFPlugin::frameDidFinishLoading(uint64_t)
 
570
{
 
571
    ASSERT_NOT_REACHED();
 
572
}
 
573
 
 
574
void SimplePDFPlugin::frameDidFail(uint64_t, bool)
 
575
{
 
576
    ASSERT_NOT_REACHED();
 
577
}
 
578
 
 
579
void SimplePDFPlugin::didEvaluateJavaScript(uint64_t, const WTF::String&)
 
580
{
 
581
    ASSERT_NOT_REACHED();
 
582
}
 
583
 
 
584
void SimplePDFPlugin::streamDidReceiveResponse(uint64_t streamID, const KURL&, uint32_t, uint32_t, const String&, const String&, const String& suggestedFilename)
 
585
{
 
586
    ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
 
587
 
 
588
    m_suggestedFilename = suggestedFilename;
 
589
}
 
590
                                           
 
591
void SimplePDFPlugin::streamDidReceiveData(uint64_t streamID, const char* bytes, int length)
 
592
{
 
593
    ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
 
594
 
 
595
    if (!m_data)
 
596
        m_data.adoptCF(CFDataCreateMutable(0, 0));
 
597
 
 
598
    CFDataAppendBytes(m_data.get(), reinterpret_cast<const UInt8*>(bytes), length);
 
599
}
 
600
 
 
601
void SimplePDFPlugin::streamDidFinishLoading(uint64_t streamID)
 
602
{
 
603
    ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
 
604
 
 
605
    pdfDocumentDidLoad();
 
606
}
 
607
 
 
608
void SimplePDFPlugin::streamDidFail(uint64_t streamID, bool wasCancelled)
 
609
{
 
610
    ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
 
611
 
 
612
    m_data.clear();
 
613
}
 
614
 
 
615
void SimplePDFPlugin::manualStreamDidReceiveResponse(const KURL& responseURL, uint32_t streamLength,  uint32_t lastModifiedTime, const String& mimeType, const String& headers, const String& suggestedFilename)
 
616
{
 
617
    m_suggestedFilename = suggestedFilename;
 
618
}
 
619
 
 
620
void SimplePDFPlugin::manualStreamDidReceiveData(const char* bytes, int length)
 
621
{
 
622
    if (!m_data)
 
623
        m_data.adoptCF(CFDataCreateMutable(0, 0));
 
624
 
 
625
    CFDataAppendBytes(m_data.get(), reinterpret_cast<const UInt8*>(bytes), length);
 
626
}
 
627
 
 
628
void SimplePDFPlugin::manualStreamDidFinishLoading()
 
629
{
 
630
    pdfDocumentDidLoad();
 
631
}
 
632
 
 
633
void SimplePDFPlugin::manualStreamDidFail(bool)
 
634
{
 
635
    m_data.clear();
 
636
}
 
637
 
 
638
bool SimplePDFPlugin::handleMouseEvent(const WebMouseEvent& event)
 
639
{
 
640
    switch (event.type()) {
 
641
    case WebEvent::MouseMove:
 
642
        mouseMovedInContentArea();
 
643
        // FIXME: Should also notify scrollbar to show hover effect. Should also send mouseExited to hide it.
 
644
        break;
 
645
    case WebEvent::MouseDown: {
 
646
        // Returning false as will make EventHandler unfocus the plug-in, which is appropriate when clicking scrollbars.
 
647
        // Ideally, we wouldn't change focus at all, but PluginView already did that for us.
 
648
        // When support for PDF forms is added, we'll need to actually focus the plug-in when clicking in a form.
 
649
        break;
 
650
    }
 
651
    case WebEvent::MouseUp: {
 
652
        PlatformMouseEvent platformEvent = platform(event);
 
653
        if (m_horizontalScrollbar)
 
654
            m_horizontalScrollbar->mouseUp(platformEvent);
 
655
        if (m_verticalScrollbar)
 
656
            m_verticalScrollbar->mouseUp(platformEvent);
 
657
        break;
 
658
    }
 
659
    default:
 
660
        break;
 
661
    }
 
662
        
 
663
    return false;
 
664
}
 
665
 
 
666
bool SimplePDFPlugin::handleWheelEvent(const WebWheelEvent& event)
 
667
{
 
668
    PlatformWheelEvent platformEvent = platform(event);
 
669
    return ScrollableArea::handleWheelEvent(platformEvent);
 
670
}
 
671
 
 
672
bool SimplePDFPlugin::handleMouseEnterEvent(const WebMouseEvent&)
 
673
{
 
674
    mouseEnteredContentArea();
 
675
    return false;
 
676
}
 
677
 
 
678
bool SimplePDFPlugin::handleMouseLeaveEvent(const WebMouseEvent&)
 
679
{
 
680
    mouseExitedContentArea();
 
681
    return false;
 
682
}
 
683
 
 
684
bool SimplePDFPlugin::handleContextMenuEvent(const WebMouseEvent&)
 
685
{
 
686
    // Use default WebKit context menu.
 
687
    return false;
 
688
}
 
689
 
 
690
bool SimplePDFPlugin::handleKeyboardEvent(const WebKeyboardEvent&)
 
691
{
 
692
    return false;
 
693
}
 
694
 
 
695
void SimplePDFPlugin::setFocus(bool hasFocus)
 
696
{
 
697
}
 
698
 
 
699
NPObject* SimplePDFPlugin::pluginScriptableNPObject()
 
700
{
 
701
    return 0;
 
702
}
 
703
 
 
704
#if PLATFORM(MAC)
 
705
 
 
706
void SimplePDFPlugin::windowFocusChanged(bool)
 
707
{
 
708
}
 
709
 
 
710
void SimplePDFPlugin::windowAndViewFramesChanged(const WebCore::IntRect& windowFrameInScreenCoordinates, const WebCore::IntRect& viewFrameInWindowCoordinates)
 
711
{
 
712
}
 
713
 
 
714
void SimplePDFPlugin::windowVisibilityChanged(bool)
 
715
{
 
716
}
 
717
 
 
718
void SimplePDFPlugin::contentsScaleFactorChanged(float)
 
719
{
 
720
}
 
721
 
 
722
uint64_t SimplePDFPlugin::pluginComplexTextInputIdentifier() const
 
723
{
 
724
    return 0;
 
725
}
 
726
 
 
727
void SimplePDFPlugin::sendComplexTextInput(const String&)
 
728
{
 
729
}
 
730
 
 
731
void SimplePDFPlugin::setLayerHostingMode(LayerHostingMode)
 
732
{
 
733
}
 
734
 
 
735
#endif
 
736
 
 
737
void SimplePDFPlugin::storageBlockingStateChanged(bool)
 
738
{
 
739
}
 
740
 
 
741
void SimplePDFPlugin::privateBrowsingStateChanged(bool)
 
742
{
 
743
}
 
744
 
 
745
bool SimplePDFPlugin::getFormValue(String&)
 
746
{
 
747
    return false;
 
748
}
 
749
 
 
750
bool SimplePDFPlugin::handleScroll(ScrollDirection direction, ScrollGranularity granularity)
 
751
{
 
752
    return scroll(direction, granularity);
 
753
}
 
754
 
 
755
Scrollbar* SimplePDFPlugin::horizontalScrollbar()
 
756
{
 
757
    return m_horizontalScrollbar.get();
 
758
}
 
759
 
 
760
Scrollbar* SimplePDFPlugin::verticalScrollbar()
 
761
{
 
762
    return m_verticalScrollbar.get();
 
763
}
 
764
 
 
765
IntRect SimplePDFPlugin::scrollCornerRect() const
 
766
{
 
767
    if (!m_horizontalScrollbar || !m_verticalScrollbar)
 
768
        return IntRect();
 
769
    if (m_horizontalScrollbar->isOverlayScrollbar()) {
 
770
        ASSERT(m_verticalScrollbar->isOverlayScrollbar());
 
771
        return IntRect();
 
772
    }
 
773
    return IntRect(pluginView()->width() - m_verticalScrollbar->width(), pluginView()->height() - m_horizontalScrollbar->height(), m_verticalScrollbar->width(), m_horizontalScrollbar->height());
 
774
}
 
775
 
 
776
ScrollableArea* SimplePDFPlugin::enclosingScrollableArea() const
 
777
{
 
778
    // FIXME: Walk up the frame tree and look for a scrollable parent frame or RenderLayer.
 
779
    return 0;
 
780
}
 
781
 
 
782
IntRect SimplePDFPlugin::scrollableAreaBoundingBox() const
 
783
{
 
784
    return pluginView()->frameRect();
 
785
}
 
786
 
 
787
void SimplePDFPlugin::setScrollOffset(const IntPoint& offset)
 
788
{
 
789
    m_scrollOffset = IntSize(offset.x(), offset.y());
 
790
    // FIXME: It would be better for performance to blit parts that remain visible.
 
791
    controller()->invalidate(IntRect(0, 0, m_size.width(), m_size.height()));
 
792
}
 
793
 
 
794
int SimplePDFPlugin::scrollSize(ScrollbarOrientation orientation) const
 
795
{
 
796
    Scrollbar* scrollbar = ((orientation == HorizontalScrollbar) ? m_horizontalScrollbar : m_verticalScrollbar).get();
 
797
    return scrollbar ? (scrollbar->totalSize() - scrollbar->visibleSize()) : 0;
 
798
}
 
799
 
 
800
bool SimplePDFPlugin::isActive() const
 
801
{
 
802
    if (Frame* coreFrame = m_frame->coreFrame()) {
 
803
        if (Page* page = coreFrame->page())
 
804
            return page->focusController()->isActive();
 
805
    }
 
806
 
 
807
    return false;
 
808
}
 
809
 
 
810
void SimplePDFPlugin::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
 
811
{
 
812
    IntRect dirtyRect = rect;
 
813
    dirtyRect.moveBy(scrollbar->location());
 
814
    dirtyRect.moveBy(-pluginView()->location());
 
815
    controller()->invalidate(dirtyRect);
 
816
}
 
817
 
 
818
void SimplePDFPlugin::invalidateScrollCornerRect(const IntRect& rect)
 
819
{
 
820
    controller()->invalidate(rect);
 
821
}
 
822
 
 
823
bool SimplePDFPlugin::isScrollCornerVisible() const
 
824
{
 
825
    return false;
 
826
}
 
827
 
 
828
int SimplePDFPlugin::scrollPosition(Scrollbar* scrollbar) const
 
829
{
 
830
    if (scrollbar->orientation() == HorizontalScrollbar)
 
831
        return m_scrollOffset.width();
 
832
    if (scrollbar->orientation() == VerticalScrollbar)
 
833
        return m_scrollOffset.height();
 
834
    ASSERT_NOT_REACHED();
 
835
    return 0;
 
836
}
 
837
 
 
838
IntPoint SimplePDFPlugin::scrollPosition() const
 
839
{
 
840
    return IntPoint(m_scrollOffset.width(), m_scrollOffset.height());
 
841
}
 
842
 
 
843
IntPoint SimplePDFPlugin::minimumScrollPosition() const
 
844
{
 
845
    return IntPoint(0, 0);
 
846
}
 
847
 
 
848
IntPoint SimplePDFPlugin::maximumScrollPosition() const
 
849
{
 
850
    int horizontalScrollbarHeight = (m_horizontalScrollbar && !m_horizontalScrollbar->isOverlayScrollbar()) ? m_horizontalScrollbar->height() : 0;
 
851
    int verticalScrollbarWidth = (m_verticalScrollbar && !m_verticalScrollbar->isOverlayScrollbar()) ? m_verticalScrollbar->width() : 0;
 
852
 
 
853
    IntPoint maximumOffset(m_pdfDocumentSize.width() - m_size.width() + verticalScrollbarWidth, m_pdfDocumentSize.height() - m_size.height() + horizontalScrollbarHeight);
 
854
    maximumOffset.clampNegativeToZero();
 
855
    return maximumOffset;
 
856
}
 
857
 
 
858
int SimplePDFPlugin::visibleHeight() const
 
859
{
 
860
    return m_size.height();
 
861
}
 
862
 
 
863
int SimplePDFPlugin::visibleWidth() const
 
864
{
 
865
    return m_size.width();
 
866
}
 
867
 
 
868
IntSize SimplePDFPlugin::contentsSize() const
 
869
{
 
870
    return m_pdfDocumentSize;
 
871
}
 
872
 
 
873
bool SimplePDFPlugin::scrollbarsCanBeActive() const
 
874
{
 
875
    return !pluginView()->frame()->document()->inPageCache();
 
876
}
 
877
 
 
878
void SimplePDFPlugin::scrollbarStyleChanged(int, bool forceUpdate)
 
879
{
 
880
    if (!forceUpdate)
 
881
        return;
 
882
 
 
883
    // 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.
 
884
    IntPoint newScrollOffset = IntPoint(m_scrollOffset).shrunkTo(maximumScrollPosition());
 
885
    setScrollOffset(newScrollOffset);
 
886
 
 
887
    // As size of the content area changes, scrollbars may need to appear or to disappear.
 
888
    updateScrollbars();
 
889
 
 
890
    ScrollableArea::contentsResized();
 
891
}
 
892
 
 
893
IntRect SimplePDFPlugin::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const
 
894
{
 
895
    IntRect rect = scrollbarRect;
 
896
    rect.move(scrollbar->location() - pluginView()->location());
 
897
    
 
898
    return pluginView()->frame()->view()->convertFromRenderer(pluginView()->renderer(), rect);
 
899
}
 
900
 
 
901
IntRect SimplePDFPlugin::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
 
902
{
 
903
    IntRect rect = pluginView()->frame()->view()->convertToRenderer(pluginView()->renderer(), parentRect);
 
904
    rect.move(pluginView()->location() - scrollbar->location());
 
905
    
 
906
    return rect;
 
907
}
 
908
 
 
909
IntPoint SimplePDFPlugin::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const
 
910
{
 
911
    IntPoint point = scrollbarPoint;
 
912
    point.move(scrollbar->location() - pluginView()->location());
 
913
    
 
914
    return pluginView()->frame()->view()->convertFromRenderer(pluginView()->renderer(), point);
 
915
}
 
916
 
 
917
IntPoint SimplePDFPlugin::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
 
918
{
 
919
    IntPoint point = pluginView()->frame()->view()->convertToRenderer(pluginView()->renderer(), parentPoint);
 
920
    point.move(pluginView()->location() - scrollbar->location());
 
921
    
 
922
    return point;
 
923
}
 
924
 
 
925
bool SimplePDFPlugin::isEditingCommandEnabled(const String&)
 
926
{
 
927
    return false;
 
928
}
 
929
 
 
930
bool SimplePDFPlugin::handleEditingCommand(const String&, const String&)
 
931
{
 
932
    return false;
 
933
}
 
934
    
 
935
bool SimplePDFPlugin::handlesPageScaleFactor()
 
936
{
 
937
    return false;
 
938
}
 
939
 
 
940
} // namespace WebKit