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