summaryrefslogtreecommitdiff
path: root/Source/WebKit2/WebProcess/Plugins/PDF/SimplePDFPlugin.mm
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit2/WebProcess/Plugins/PDF/SimplePDFPlugin.mm')
-rw-r--r--Source/WebKit2/WebProcess/Plugins/PDF/SimplePDFPlugin.mm940
1 files changed, 940 insertions, 0 deletions
diff --git a/Source/WebKit2/WebProcess/Plugins/PDF/SimplePDFPlugin.mm b/Source/WebKit2/WebProcess/Plugins/PDF/SimplePDFPlugin.mm
new file mode 100644
index 000000000..98a73b2b1
--- /dev/null
+++ b/Source/WebKit2/WebProcess/Plugins/PDF/SimplePDFPlugin.mm
@@ -0,0 +1,940 @@
+/*
+ * Copyright (C) 2009, 2011, 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "SimplePDFPlugin.h"
+
+#import "PDFKitImports.h"
+#import "PluginView.h"
+#import "ShareableBitmap.h"
+#import "WebEvent.h"
+#import "WebEventConversion.h"
+#import <JavaScriptCore/JSContextRef.h>
+#import <JavaScriptCore/JSObjectRef.h>
+#import <JavaScriptCore/JSStringRef.h>
+#import <JavaScriptCore/JSStringRefCF.h>
+#import <PDFKit/PDFKit.h>
+#import <WebCore/ArchiveResource.h>
+#import <WebCore/Chrome.h>
+#import <WebCore/DocumentLoader.h>
+#import <WebCore/FocusController.h>
+#import <WebCore/Frame.h>
+#import <WebCore/FrameView.h>
+#import <WebCore/GraphicsContext.h>
+#import <WebCore/HTTPHeaderMap.h>
+#import <WebCore/LocalizedStrings.h>
+#import <WebCore/Page.h>
+#import <WebCore/PluginData.h>
+#import <WebCore/RenderBoxModelObject.h>
+#import <WebCore/ScrollAnimator.h>
+#import <WebCore/ScrollbarTheme.h>
+
+using namespace WebCore;
+using namespace std;
+
+static void appendValuesInPDFNameSubtreeToVector(CGPDFDictionaryRef subtree, Vector<CGPDFObjectRef>& values)
+{
+ CGPDFArrayRef names;
+ if (CGPDFDictionaryGetArray(subtree, "Names", &names)) {
+ size_t nameCount = CGPDFArrayGetCount(names) / 2;
+ for (size_t i = 0; i < nameCount; ++i) {
+ CGPDFObjectRef object;
+ CGPDFArrayGetObject(names, 2 * i + 1, &object);
+ values.append(object);
+ }
+ return;
+ }
+
+ CGPDFArrayRef kids;
+ if (!CGPDFDictionaryGetArray(subtree, "Kids", &kids))
+ return;
+
+ size_t kidCount = CGPDFArrayGetCount(kids);
+ for (size_t i = 0; i < kidCount; ++i) {
+ CGPDFDictionaryRef kid;
+ if (!CGPDFArrayGetDictionary(kids, i, &kid))
+ continue;
+ appendValuesInPDFNameSubtreeToVector(kid, values);
+ }
+}
+
+static void getAllValuesInPDFNameTree(CGPDFDictionaryRef tree, Vector<CGPDFObjectRef>& allValues)
+{
+ appendValuesInPDFNameSubtreeToVector(tree, allValues);
+}
+
+static void getAllScriptsInPDFDocument(CGPDFDocumentRef pdfDocument, Vector<RetainPtr<CFStringRef> >& scripts)
+{
+ if (!pdfDocument)
+ return;
+
+ CGPDFDictionaryRef pdfCatalog = CGPDFDocumentGetCatalog(pdfDocument);
+ if (!pdfCatalog)
+ return;
+
+ // Get the dictionary of all document-level name trees.
+ CGPDFDictionaryRef namesDictionary;
+ if (!CGPDFDictionaryGetDictionary(pdfCatalog, "Names", &namesDictionary))
+ return;
+
+ // Get the document-level "JavaScript" name tree.
+ CGPDFDictionaryRef javaScriptNameTree;
+ if (!CGPDFDictionaryGetDictionary(namesDictionary, "JavaScript", &javaScriptNameTree))
+ return;
+
+ // The names are arbitrary. We are only interested in the values.
+ Vector<CGPDFObjectRef> objects;
+ getAllValuesInPDFNameTree(javaScriptNameTree, objects);
+ size_t objectCount = objects.size();
+
+ for (size_t i = 0; i < objectCount; ++i) {
+ CGPDFDictionaryRef javaScriptAction;
+ if (!CGPDFObjectGetValue(reinterpret_cast<CGPDFObjectRef>(objects[i]), kCGPDFObjectTypeDictionary, &javaScriptAction))
+ continue;
+
+ // A JavaScript action must have an action type of "JavaScript".
+ const char* actionType;
+ if (!CGPDFDictionaryGetName(javaScriptAction, "S", &actionType) || strcmp(actionType, "JavaScript"))
+ continue;
+
+ const UInt8* bytes = 0;
+ CFIndex length;
+ CGPDFStreamRef stream;
+ CGPDFStringRef string;
+ RetainPtr<CFDataRef> data;
+ if (CGPDFDictionaryGetStream(javaScriptAction, "JS", &stream)) {
+ CGPDFDataFormat format;
+ data.adoptCF(CGPDFStreamCopyData(stream, &format));
+ if (!data)
+ continue;
+ bytes = CFDataGetBytePtr(data.get());
+ length = CFDataGetLength(data.get());
+ } else if (CGPDFDictionaryGetString(javaScriptAction, "JS", &string)) {
+ bytes = CGPDFStringGetBytePtr(string);
+ length = CGPDFStringGetLength(string);
+ }
+ if (!bytes)
+ continue;
+
+ CFStringEncoding encoding = (length > 1 && bytes[0] == 0xFE && bytes[1] == 0xFF) ? kCFStringEncodingUnicode : kCFStringEncodingUTF8;
+ RetainPtr<CFStringRef> script(AdoptCF, CFStringCreateWithBytes(kCFAllocatorDefault, bytes, length, encoding, true));
+ if (!script)
+ continue;
+
+ scripts.append(script);
+ }
+}
+
+namespace WebKit {
+
+const uint64_t pdfDocumentRequestID = 1; // PluginController supports loading multiple streams, but we only need one for PDF.
+
+const int gutterHeight = 10;
+const int shadowOffsetX = 0;
+const int shadowOffsetY = -2;
+const int shadowSize = 7;
+
+PassRefPtr<SimplePDFPlugin> SimplePDFPlugin::create(WebFrame* frame)
+{
+ return adoptRef(new SimplePDFPlugin(frame));
+}
+
+SimplePDFPlugin::SimplePDFPlugin(WebFrame* frame)
+ : m_frame(frame)
+{
+}
+
+SimplePDFPlugin::~SimplePDFPlugin()
+{
+}
+
+PluginInfo SimplePDFPlugin::pluginInfo()
+{
+ PluginInfo info;
+ info.name = builtInPDFPluginName();
+
+ MimeClassInfo mimeClassInfo;
+ mimeClassInfo.type ="application/pdf";
+ mimeClassInfo.desc = pdfDocumentTypeDescription();
+ mimeClassInfo.extensions.append("pdf");
+
+ info.mimes.append(mimeClassInfo);
+ return info;
+}
+
+PluginView* SimplePDFPlugin::pluginView()
+{
+ return static_cast<PluginView*>(controller());
+}
+
+const PluginView* SimplePDFPlugin::pluginView() const
+{
+ return static_cast<const PluginView*>(controller());
+}
+
+void SimplePDFPlugin::updateScrollbars()
+{
+ bool hadScrollbars = m_horizontalScrollbar || m_verticalScrollbar;
+
+ if (m_horizontalScrollbar) {
+ if (m_size.width() >= m_pdfDocumentSize.width())
+ destroyScrollbar(HorizontalScrollbar);
+ } else if (m_size.width() < m_pdfDocumentSize.width())
+ m_horizontalScrollbar = createScrollbar(HorizontalScrollbar);
+
+ if (m_verticalScrollbar) {
+ if (m_size.height() >= m_pdfDocumentSize.height())
+ destroyScrollbar(VerticalScrollbar);
+ } else if (m_size.height() < m_pdfDocumentSize.height())
+ m_verticalScrollbar = createScrollbar(VerticalScrollbar);
+
+ int horizontalScrollbarHeight = (m_horizontalScrollbar && !m_horizontalScrollbar->isOverlayScrollbar()) ? m_horizontalScrollbar->height() : 0;
+ int verticalScrollbarWidth = (m_verticalScrollbar && !m_verticalScrollbar->isOverlayScrollbar()) ? m_verticalScrollbar->width() : 0;
+
+ int pageStep = m_pageBoxes.isEmpty() ? 0 : m_pageBoxes[0].height();
+
+ if (m_horizontalScrollbar) {
+ m_horizontalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
+ m_horizontalScrollbar->setProportion(m_size.width() - verticalScrollbarWidth, m_pdfDocumentSize.width());
+ IntRect scrollbarRect(pluginView()->x(), pluginView()->y() + m_size.height() - m_horizontalScrollbar->height(), m_size.width(), m_horizontalScrollbar->height());
+ if (m_verticalScrollbar)
+ scrollbarRect.contract(m_verticalScrollbar->width(), 0);
+ m_horizontalScrollbar->setFrameRect(scrollbarRect);
+ }
+ if (m_verticalScrollbar) {
+ m_verticalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
+ m_verticalScrollbar->setProportion(m_size.height() - horizontalScrollbarHeight, m_pdfDocumentSize.height());
+ IntRect scrollbarRect(IntRect(pluginView()->x() + m_size.width() - m_verticalScrollbar->width(), pluginView()->y(), m_verticalScrollbar->width(), m_size.height()));
+ if (m_horizontalScrollbar)
+ scrollbarRect.contract(0, m_horizontalScrollbar->height());
+ m_verticalScrollbar->setFrameRect(scrollbarRect);
+ }
+
+ FrameView* frameView = m_frame->coreFrame()->view();
+ if (!frameView)
+ return;
+
+ bool hasScrollbars = m_horizontalScrollbar || m_verticalScrollbar;
+ if (hadScrollbars != hasScrollbars) {
+ if (hasScrollbars)
+ frameView->addScrollableArea(this);
+ else
+ frameView->removeScrollableArea(this);
+
+ frameView->setNeedsLayout();
+ }
+}
+
+PassRefPtr<Scrollbar> SimplePDFPlugin::createScrollbar(ScrollbarOrientation orientation)
+{
+ RefPtr<Scrollbar> widget = Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar);
+ if (orientation == HorizontalScrollbar)
+ didAddHorizontalScrollbar(widget.get());
+ else
+ didAddVerticalScrollbar(widget.get());
+ pluginView()->frame()->view()->addChild(widget.get());
+ return widget.release();
+}
+
+void SimplePDFPlugin::destroyScrollbar(ScrollbarOrientation orientation)
+{
+ RefPtr<Scrollbar>& scrollbar = orientation == HorizontalScrollbar ? m_horizontalScrollbar : m_verticalScrollbar;
+ if (!scrollbar)
+ return;
+
+ if (orientation == HorizontalScrollbar)
+ willRemoveHorizontalScrollbar(scrollbar.get());
+ else
+ willRemoveVerticalScrollbar(scrollbar.get());
+
+ scrollbar->removeFromParent();
+ scrollbar->disconnectFromScrollableArea();
+ scrollbar = 0;
+}
+
+void SimplePDFPlugin::addArchiveResource()
+{
+ // FIXME: It's a hack to force add a resource to DocumentLoader. PDF documents should just be fetched as CachedResources.
+
+ // Add just enough data for context menu handling and web archives to work.
+ ResourceResponse synthesizedResponse;
+ synthesizedResponse.setSuggestedFilename(m_suggestedFilename);
+ synthesizedResponse.setURL(m_sourceURL); // Needs to match the HitTestResult::absolutePDFURL.
+ synthesizedResponse.setMimeType("application/pdf");
+
+ RefPtr<ArchiveResource> resource = ArchiveResource::create(SharedBuffer::wrapCFData(m_data.get()), m_sourceURL, "application/pdf", String(), String(), synthesizedResponse);
+ pluginView()->frame()->document()->loader()->addArchiveResource(resource.release());
+}
+
+static void jsPDFDocInitialize(JSContextRef ctx, JSObjectRef object)
+{
+ SimplePDFPlugin* pdfView = static_cast<SimplePDFPlugin*>(JSObjectGetPrivate(object));
+ pdfView->ref();
+}
+
+static void jsPDFDocFinalize(JSObjectRef object)
+{
+ SimplePDFPlugin* pdfView = static_cast<SimplePDFPlugin*>(JSObjectGetPrivate(object));
+ pdfView->deref();
+}
+
+JSValueRef SimplePDFPlugin::jsPDFDocPrint(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ SimplePDFPlugin* pdfView = static_cast<SimplePDFPlugin*>(JSObjectGetPrivate(thisObject));
+
+ WebFrame* frame = pdfView->m_frame;
+ if (!frame)
+ return JSValueMakeUndefined(ctx);
+
+ Frame* coreFrame = frame->coreFrame();
+ if (!coreFrame)
+ return JSValueMakeUndefined(ctx);
+
+ Page* page = coreFrame->page();
+ if (!page)
+ return JSValueMakeUndefined(ctx);
+
+ page->chrome()->print(coreFrame);
+
+ return JSValueMakeUndefined(ctx);
+}
+
+JSObjectRef SimplePDFPlugin::makeJSPDFDoc(JSContextRef ctx)
+{
+ static JSStaticFunction jsPDFDocStaticFunctions[] = {
+ { "print", jsPDFDocPrint, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { 0, 0, 0 },
+ };
+
+ static JSClassDefinition jsPDFDocClassDefinition = {
+ 0,
+ kJSClassAttributeNone,
+ "Doc",
+ 0,
+ 0,
+ jsPDFDocStaticFunctions,
+ jsPDFDocInitialize, jsPDFDocFinalize, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ static JSClassRef jsPDFDocClass = JSClassCreate(&jsPDFDocClassDefinition);
+
+ return JSObjectMake(ctx, jsPDFDocClass, this);
+}
+
+void SimplePDFPlugin::pdfDocumentDidLoad()
+{
+ addArchiveResource();
+
+ m_pdfDocument.adoptNS([[pdfDocumentClass() alloc] initWithData:(NSData *)m_data.get()]);
+
+ calculateSizes();
+ updateScrollbars();
+
+ controller()->invalidate(IntRect(0, 0, m_size.width(), m_size.height()));
+
+ runScriptsInPDFDocument();
+}
+
+void SimplePDFPlugin::runScriptsInPDFDocument()
+{
+ Vector<RetainPtr<CFStringRef> > scripts;
+ getAllScriptsInPDFDocument([m_pdfDocument.get() documentRef], scripts);
+
+ size_t scriptCount = scripts.size();
+ if (!scriptCount)
+ return;
+
+ JSGlobalContextRef ctx = JSGlobalContextCreate(0);
+ JSObjectRef jsPDFDoc = makeJSPDFDoc(ctx);
+
+ for (size_t i = 0; i < scriptCount; ++i) {
+ JSStringRef script = JSStringCreateWithCFString(scripts[i].get());
+ JSEvaluateScript(ctx, script, jsPDFDoc, 0, 0, 0);
+ JSStringRelease(script);
+ }
+
+ JSGlobalContextRelease(ctx);
+}
+
+void SimplePDFPlugin::computePageBoxes()
+{
+ size_t pageCount = CGPDFDocumentGetNumberOfPages([m_pdfDocument.get() documentRef]);
+ for (size_t i = 0; i < pageCount; ++i) {
+ CGPDFPageRef pdfPage = CGPDFDocumentGetPage([m_pdfDocument.get() documentRef], i + 1);
+ ASSERT(pdfPage);
+
+ CGRect box = CGPDFPageGetBoxRect(pdfPage, kCGPDFCropBox);
+ if (CGRectIsEmpty(box))
+ box = CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox);
+ m_pageBoxes.append(IntRect(box));
+ }
+}
+
+void SimplePDFPlugin::calculateSizes()
+{
+ size_t pageCount = CGPDFDocumentGetNumberOfPages([m_pdfDocument.get() documentRef]);
+ for (size_t i = 0; i < pageCount; ++i) {
+ CGPDFPageRef pdfPage = CGPDFDocumentGetPage([m_pdfDocument.get() documentRef], i + 1);
+ ASSERT(pdfPage);
+
+ CGRect box = CGPDFPageGetBoxRect(pdfPage, kCGPDFCropBox);
+ if (CGRectIsEmpty(box))
+ box = CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox);
+ m_pageBoxes.append(IntRect(box));
+ m_pdfDocumentSize.setWidth(max(m_pdfDocumentSize.width(), static_cast<int>(box.size.width)));
+ m_pdfDocumentSize.expand(0, box.size.height);
+ }
+ m_pdfDocumentSize.expand(0, gutterHeight * (m_pageBoxes.size() - 1));
+}
+
+bool SimplePDFPlugin::initialize(const Parameters& parameters)
+{
+ // Load the src URL if needed.
+ m_sourceURL = parameters.url;
+ if (!parameters.shouldUseManualLoader && !parameters.url.isEmpty())
+ controller()->loadURL(pdfDocumentRequestID, "GET", parameters.url.string(), String(), HTTPHeaderMap(), Vector<uint8_t>(), false);
+
+ controller()->didInitializePlugin();
+ return true;
+}
+
+void SimplePDFPlugin::destroy()
+{
+ if (m_frame) {
+ if (FrameView* frameView = m_frame->coreFrame()->view())
+ frameView->removeScrollableArea(this);
+ }
+
+ destroyScrollbar(HorizontalScrollbar);
+ destroyScrollbar(VerticalScrollbar);
+}
+
+void SimplePDFPlugin::paint(GraphicsContext* graphicsContext, const IntRect& dirtyRect)
+{
+ contentAreaWillPaint();
+
+ paintBackground(graphicsContext, dirtyRect);
+
+ if (!m_pdfDocument) // FIXME: Draw loading progress.
+ return;
+
+ paintContent(graphicsContext, dirtyRect);
+ paintControls(graphicsContext, dirtyRect);
+}
+
+void SimplePDFPlugin::paintBackground(GraphicsContext* graphicsContext, const IntRect& dirtyRect)
+{
+ GraphicsContextStateSaver stateSaver(*graphicsContext);
+ graphicsContext->setFillColor(Color::gray, ColorSpaceDeviceRGB);
+ graphicsContext->fillRect(dirtyRect);
+}
+
+void SimplePDFPlugin::paintContent(GraphicsContext* graphicsContext, const IntRect& dirtyRect)
+{
+ GraphicsContextStateSaver stateSaver(*graphicsContext);
+ CGContextRef context = graphicsContext->platformContext();
+
+ graphicsContext->setImageInterpolationQuality(InterpolationHigh);
+ graphicsContext->setShouldAntialias(true);
+ graphicsContext->setShouldSmoothFonts(true);
+ graphicsContext->setFillColor(Color::white, ColorSpaceDeviceRGB);
+
+ graphicsContext->clip(dirtyRect);
+ IntRect contentRect(dirtyRect);
+ contentRect.moveBy(IntPoint(m_scrollOffset));
+ graphicsContext->translate(-m_scrollOffset.width(), -m_scrollOffset.height());
+
+ CGContextScaleCTM(context, 1, -1);
+
+ int pageTop = 0;
+ for (size_t i = 0; i < m_pageBoxes.size(); ++i) {
+ IntRect pageBox = m_pageBoxes[i];
+ float extraOffsetForCenteringX = max(roundf((m_size.width() - pageBox.width()) / 2.0f), 0.0f);
+ float extraOffsetForCenteringY = (m_pageBoxes.size() == 1) ? max(roundf((m_size.height() - pageBox.height() + shadowOffsetY) / 2.0f), 0.0f) : 0;
+
+ if (pageTop > contentRect.maxY())
+ break;
+ if (pageTop + pageBox.height() + extraOffsetForCenteringY + gutterHeight >= contentRect.y()) {
+ CGPDFPageRef pdfPage = CGPDFDocumentGetPage([m_pdfDocument.get() documentRef], i + 1);
+
+ graphicsContext->save();
+ graphicsContext->translate(extraOffsetForCenteringX - pageBox.x(), -extraOffsetForCenteringY - pageBox.y() - pageBox.height());
+
+ graphicsContext->setShadow(FloatSize(shadowOffsetX, shadowOffsetY), shadowSize, Color::black, ColorSpaceDeviceRGB);
+ graphicsContext->fillRect(pageBox);
+ graphicsContext->clearShadow();
+
+ graphicsContext->clip(pageBox);
+
+ CGContextDrawPDFPage(context, pdfPage);
+ graphicsContext->restore();
+ }
+ pageTop += pageBox.height() + gutterHeight;
+ CGContextTranslateCTM(context, 0, -pageBox.height() - gutterHeight);
+ }
+}
+
+void SimplePDFPlugin::paintControls(GraphicsContext* graphicsContext, const IntRect& dirtyRect)
+{
+ {
+ GraphicsContextStateSaver stateSaver(*graphicsContext);
+ IntRect scrollbarDirtyRect = dirtyRect;
+ scrollbarDirtyRect.moveBy(pluginView()->frameRect().location());
+ graphicsContext->translate(-pluginView()->frameRect().x(), -pluginView()->frameRect().y());
+
+ if (m_horizontalScrollbar)
+ m_horizontalScrollbar->paint(graphicsContext, scrollbarDirtyRect);
+
+ if (m_verticalScrollbar)
+ m_verticalScrollbar->paint(graphicsContext, scrollbarDirtyRect);
+ }
+
+ IntRect dirtyCornerRect = intersection(scrollCornerRect(), dirtyRect);
+ ScrollbarTheme::theme()->paintScrollCorner(0, graphicsContext, dirtyCornerRect);
+}
+
+void SimplePDFPlugin::updateControlTints(GraphicsContext* graphicsContext)
+{
+ ASSERT(graphicsContext->updatingControlTints());
+
+ if (m_horizontalScrollbar)
+ m_horizontalScrollbar->invalidate();
+ if (m_verticalScrollbar)
+ m_verticalScrollbar->invalidate();
+ invalidateScrollCorner(scrollCornerRect());
+}
+
+PassRefPtr<ShareableBitmap> SimplePDFPlugin::snapshot()
+{
+ return 0;
+}
+
+#if PLATFORM(MAC)
+PlatformLayer* SimplePDFPlugin::pluginLayer()
+{
+ return 0;
+}
+#endif
+
+
+bool SimplePDFPlugin::isTransparent()
+{
+ // This should never be called from the web process.
+ ASSERT_NOT_REACHED();
+ return false;
+}
+
+bool SimplePDFPlugin::wantsWheelEvents()
+{
+ return true;
+}
+
+void SimplePDFPlugin::geometryDidChange(const IntSize& size, const IntRect& clipRect, const AffineTransform& pluginToRootViewTransform)
+{
+ if (m_size == size) {
+ // Nothing to do.
+ return;
+ }
+
+ m_size = size;
+ updateScrollbars();
+}
+
+void SimplePDFPlugin::visibilityDidChange()
+{
+}
+
+void SimplePDFPlugin::frameDidFinishLoading(uint64_t)
+{
+ ASSERT_NOT_REACHED();
+}
+
+void SimplePDFPlugin::frameDidFail(uint64_t, bool)
+{
+ ASSERT_NOT_REACHED();
+}
+
+void SimplePDFPlugin::didEvaluateJavaScript(uint64_t, const WTF::String&)
+{
+ ASSERT_NOT_REACHED();
+}
+
+void SimplePDFPlugin::streamDidReceiveResponse(uint64_t streamID, const KURL&, uint32_t, uint32_t, const String&, const String&, const String& suggestedFilename)
+{
+ ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
+
+ m_suggestedFilename = suggestedFilename;
+}
+
+void SimplePDFPlugin::streamDidReceiveData(uint64_t streamID, const char* bytes, int length)
+{
+ ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
+
+ if (!m_data)
+ m_data.adoptCF(CFDataCreateMutable(0, 0));
+
+ CFDataAppendBytes(m_data.get(), reinterpret_cast<const UInt8*>(bytes), length);
+}
+
+void SimplePDFPlugin::streamDidFinishLoading(uint64_t streamID)
+{
+ ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
+
+ pdfDocumentDidLoad();
+}
+
+void SimplePDFPlugin::streamDidFail(uint64_t streamID, bool wasCancelled)
+{
+ ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
+
+ m_data.clear();
+}
+
+void SimplePDFPlugin::manualStreamDidReceiveResponse(const KURL& responseURL, uint32_t streamLength, uint32_t lastModifiedTime, const String& mimeType, const String& headers, const String& suggestedFilename)
+{
+ m_suggestedFilename = suggestedFilename;
+}
+
+void SimplePDFPlugin::manualStreamDidReceiveData(const char* bytes, int length)
+{
+ if (!m_data)
+ m_data.adoptCF(CFDataCreateMutable(0, 0));
+
+ CFDataAppendBytes(m_data.get(), reinterpret_cast<const UInt8*>(bytes), length);
+}
+
+void SimplePDFPlugin::manualStreamDidFinishLoading()
+{
+ pdfDocumentDidLoad();
+}
+
+void SimplePDFPlugin::manualStreamDidFail(bool)
+{
+ m_data.clear();
+}
+
+bool SimplePDFPlugin::handleMouseEvent(const WebMouseEvent& event)
+{
+ switch (event.type()) {
+ case WebEvent::MouseMove:
+ mouseMovedInContentArea();
+ // FIXME: Should also notify scrollbar to show hover effect. Should also send mouseExited to hide it.
+ break;
+ case WebEvent::MouseDown: {
+ // Returning false as will make EventHandler unfocus the plug-in, which is appropriate when clicking scrollbars.
+ // Ideally, we wouldn't change focus at all, but PluginView already did that for us.
+ // When support for PDF forms is added, we'll need to actually focus the plug-in when clicking in a form.
+ break;
+ }
+ case WebEvent::MouseUp: {
+ PlatformMouseEvent platformEvent = platform(event);
+ if (m_horizontalScrollbar)
+ m_horizontalScrollbar->mouseUp(platformEvent);
+ if (m_verticalScrollbar)
+ m_verticalScrollbar->mouseUp(platformEvent);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool SimplePDFPlugin::handleWheelEvent(const WebWheelEvent& event)
+{
+ PlatformWheelEvent platformEvent = platform(event);
+ return ScrollableArea::handleWheelEvent(platformEvent);
+}
+
+bool SimplePDFPlugin::handleMouseEnterEvent(const WebMouseEvent&)
+{
+ mouseEnteredContentArea();
+ return false;
+}
+
+bool SimplePDFPlugin::handleMouseLeaveEvent(const WebMouseEvent&)
+{
+ mouseExitedContentArea();
+ return false;
+}
+
+bool SimplePDFPlugin::handleContextMenuEvent(const WebMouseEvent&)
+{
+ // Use default WebKit context menu.
+ return false;
+}
+
+bool SimplePDFPlugin::handleKeyboardEvent(const WebKeyboardEvent&)
+{
+ return false;
+}
+
+void SimplePDFPlugin::setFocus(bool hasFocus)
+{
+}
+
+NPObject* SimplePDFPlugin::pluginScriptableNPObject()
+{
+ return 0;
+}
+
+#if PLATFORM(MAC)
+
+void SimplePDFPlugin::windowFocusChanged(bool)
+{
+}
+
+void SimplePDFPlugin::windowAndViewFramesChanged(const WebCore::IntRect& windowFrameInScreenCoordinates, const WebCore::IntRect& viewFrameInWindowCoordinates)
+{
+}
+
+void SimplePDFPlugin::windowVisibilityChanged(bool)
+{
+}
+
+void SimplePDFPlugin::contentsScaleFactorChanged(float)
+{
+}
+
+uint64_t SimplePDFPlugin::pluginComplexTextInputIdentifier() const
+{
+ return 0;
+}
+
+void SimplePDFPlugin::sendComplexTextInput(const String&)
+{
+}
+
+void SimplePDFPlugin::setLayerHostingMode(LayerHostingMode)
+{
+}
+
+#endif
+
+void SimplePDFPlugin::storageBlockingStateChanged(bool)
+{
+}
+
+void SimplePDFPlugin::privateBrowsingStateChanged(bool)
+{
+}
+
+bool SimplePDFPlugin::getFormValue(String&)
+{
+ return false;
+}
+
+bool SimplePDFPlugin::handleScroll(ScrollDirection direction, ScrollGranularity granularity)
+{
+ return scroll(direction, granularity);
+}
+
+Scrollbar* SimplePDFPlugin::horizontalScrollbar()
+{
+ return m_horizontalScrollbar.get();
+}
+
+Scrollbar* SimplePDFPlugin::verticalScrollbar()
+{
+ return m_verticalScrollbar.get();
+}
+
+IntRect SimplePDFPlugin::scrollCornerRect() const
+{
+ if (!m_horizontalScrollbar || !m_verticalScrollbar)
+ return IntRect();
+ if (m_horizontalScrollbar->isOverlayScrollbar()) {
+ ASSERT(m_verticalScrollbar->isOverlayScrollbar());
+ return IntRect();
+ }
+ return IntRect(pluginView()->width() - m_verticalScrollbar->width(), pluginView()->height() - m_horizontalScrollbar->height(), m_verticalScrollbar->width(), m_horizontalScrollbar->height());
+}
+
+ScrollableArea* SimplePDFPlugin::enclosingScrollableArea() const
+{
+ // FIXME: Walk up the frame tree and look for a scrollable parent frame or RenderLayer.
+ return 0;
+}
+
+IntRect SimplePDFPlugin::scrollableAreaBoundingBox() const
+{
+ return pluginView()->frameRect();
+}
+
+void SimplePDFPlugin::setScrollOffset(const IntPoint& offset)
+{
+ m_scrollOffset = IntSize(offset.x(), offset.y());
+ // FIXME: It would be better for performance to blit parts that remain visible.
+ controller()->invalidate(IntRect(0, 0, m_size.width(), m_size.height()));
+}
+
+int SimplePDFPlugin::scrollSize(ScrollbarOrientation orientation) const
+{
+ Scrollbar* scrollbar = ((orientation == HorizontalScrollbar) ? m_horizontalScrollbar : m_verticalScrollbar).get();
+ return scrollbar ? (scrollbar->totalSize() - scrollbar->visibleSize()) : 0;
+}
+
+bool SimplePDFPlugin::isActive() const
+{
+ if (Frame* coreFrame = m_frame->coreFrame()) {
+ if (Page* page = coreFrame->page())
+ return page->focusController()->isActive();
+ }
+
+ return false;
+}
+
+void SimplePDFPlugin::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
+{
+ IntRect dirtyRect = rect;
+ dirtyRect.moveBy(scrollbar->location());
+ dirtyRect.moveBy(-pluginView()->location());
+ controller()->invalidate(dirtyRect);
+}
+
+void SimplePDFPlugin::invalidateScrollCornerRect(const IntRect& rect)
+{
+ controller()->invalidate(rect);
+}
+
+bool SimplePDFPlugin::isScrollCornerVisible() const
+{
+ return false;
+}
+
+int SimplePDFPlugin::scrollPosition(Scrollbar* scrollbar) const
+{
+ if (scrollbar->orientation() == HorizontalScrollbar)
+ return m_scrollOffset.width();
+ if (scrollbar->orientation() == VerticalScrollbar)
+ return m_scrollOffset.height();
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+IntPoint SimplePDFPlugin::scrollPosition() const
+{
+ return IntPoint(m_scrollOffset.width(), m_scrollOffset.height());
+}
+
+IntPoint SimplePDFPlugin::minimumScrollPosition() const
+{
+ return IntPoint(0, 0);
+}
+
+IntPoint SimplePDFPlugin::maximumScrollPosition() const
+{
+ int horizontalScrollbarHeight = (m_horizontalScrollbar && !m_horizontalScrollbar->isOverlayScrollbar()) ? m_horizontalScrollbar->height() : 0;
+ int verticalScrollbarWidth = (m_verticalScrollbar && !m_verticalScrollbar->isOverlayScrollbar()) ? m_verticalScrollbar->width() : 0;
+
+ IntPoint maximumOffset(m_pdfDocumentSize.width() - m_size.width() + verticalScrollbarWidth, m_pdfDocumentSize.height() - m_size.height() + horizontalScrollbarHeight);
+ maximumOffset.clampNegativeToZero();
+ return maximumOffset;
+}
+
+int SimplePDFPlugin::visibleHeight() const
+{
+ return m_size.height();
+}
+
+int SimplePDFPlugin::visibleWidth() const
+{
+ return m_size.width();
+}
+
+IntSize SimplePDFPlugin::contentsSize() const
+{
+ return m_pdfDocumentSize;
+}
+
+bool SimplePDFPlugin::scrollbarsCanBeActive() const
+{
+ return !pluginView()->frame()->document()->inPageCache();
+}
+
+void SimplePDFPlugin::scrollbarStyleChanged(int, bool forceUpdate)
+{
+ if (!forceUpdate)
+ return;
+
+ // 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.
+ IntPoint newScrollOffset = IntPoint(m_scrollOffset).shrunkTo(maximumScrollPosition());
+ setScrollOffset(newScrollOffset);
+
+ // As size of the content area changes, scrollbars may need to appear or to disappear.
+ updateScrollbars();
+
+ ScrollableArea::contentsResized();
+}
+
+IntRect SimplePDFPlugin::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const
+{
+ IntRect rect = scrollbarRect;
+ rect.move(scrollbar->location() - pluginView()->location());
+
+ return pluginView()->frame()->view()->convertFromRenderer(pluginView()->renderer(), rect);
+}
+
+IntRect SimplePDFPlugin::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
+{
+ IntRect rect = pluginView()->frame()->view()->convertToRenderer(pluginView()->renderer(), parentRect);
+ rect.move(pluginView()->location() - scrollbar->location());
+
+ return rect;
+}
+
+IntPoint SimplePDFPlugin::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const
+{
+ IntPoint point = scrollbarPoint;
+ point.move(scrollbar->location() - pluginView()->location());
+
+ return pluginView()->frame()->view()->convertFromRenderer(pluginView()->renderer(), point);
+}
+
+IntPoint SimplePDFPlugin::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
+{
+ IntPoint point = pluginView()->frame()->view()->convertToRenderer(pluginView()->renderer(), parentPoint);
+ point.move(pluginView()->location() - scrollbar->location());
+
+ return point;
+}
+
+bool SimplePDFPlugin::isEditingCommandEnabled(const String&)
+{
+ return false;
+}
+
+bool SimplePDFPlugin::handleEditingCommand(const String&, const String&)
+{
+ return false;
+}
+
+bool SimplePDFPlugin::handlesPageScaleFactor()
+{
+ return false;
+}
+
+} // namespace WebKit