/* * 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 #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import using namespace WebCore; static const char* postScriptMIMEType = "application/postscript"; static void appendValuesInPDFNameSubtreeToVector(CGPDFDictionaryRef subtree, Vector& 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& allValues) { appendValuesInPDFNameSubtreeToVector(tree, allValues); } static void getAllScriptsInPDFDocument(CGPDFDocumentRef pdfDocument, Vector>& 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 objects; getAllValuesInPDFNameTree(javaScriptNameTree, objects); size_t objectCount = objects.size(); for (size_t i = 0; i < objectCount; ++i) { CGPDFDictionaryRef javaScriptAction; if (!CGPDFObjectGetValue(reinterpret_cast(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 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 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::create(WebFrame* frame) { return adoptRef(new SimplePDFPlugin(frame)); } SimplePDFPlugin::SimplePDFPlugin(WebFrame* frame) : m_frame(frame) , m_isPostScript(false) , m_pdfDocumentWasMutated(false) { } SimplePDFPlugin::~SimplePDFPlugin() { } PluginInfo SimplePDFPlugin::pluginInfo() { PluginInfo info; info.name = builtInPDFPluginName(); info.isApplicationPlugin = true; MimeClassInfo pdfMimeClassInfo; pdfMimeClassInfo.type = "application/pdf"; pdfMimeClassInfo.desc = pdfDocumentTypeDescription(); pdfMimeClassInfo.extensions.append("pdf"); info.mimes.append(pdfMimeClassInfo); MimeClassInfo textPDFMimeClassInfo; textPDFMimeClassInfo.type = "text/pdf"; textPDFMimeClassInfo.desc = pdfDocumentTypeDescription(); textPDFMimeClassInfo.extensions.append("pdf"); info.mimes.append(textPDFMimeClassInfo); MimeClassInfo postScriptMimeClassInfo; postScriptMimeClassInfo.type = postScriptMIMEType; postScriptMimeClassInfo.desc = postScriptDocumentTypeDescription(); postScriptMimeClassInfo.extensions.append("ps"); info.mimes.append(postScriptMimeClassInfo); return info; } PluginView* SimplePDFPlugin::pluginView() { return static_cast(controller()); } const PluginView* SimplePDFPlugin::pluginView() const { return static_cast(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 SimplePDFPlugin::createScrollbar(ScrollbarOrientation orientation) { RefPtr widget = Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar); didAddScrollbar(widget.get(), orientation); pluginView()->frame()->view()->addChild(widget.get()); return widget.release(); } void SimplePDFPlugin::destroyScrollbar(ScrollbarOrientation orientation) { RefPtr& scrollbar = orientation == HorizontalScrollbar ? m_horizontalScrollbar : m_verticalScrollbar; if (!scrollbar) return; willRemoveScrollbar(scrollbar.get(), orientation); 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 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(JSObjectGetPrivate(object)); pdfView->ref(); } static void jsPDFDocFinalize(JSObjectRef object) { SimplePDFPlugin* pdfView = static_cast(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(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); } static RetainPtr convertPostScriptDataToPDF(RetainPtr postScriptData) { // Convert PostScript to PDF using the Quartz 2D API. // http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_ps_convert/chapter_16_section_1.html CGPSConverterCallbacks callbacks = { 0, 0, 0, 0, 0, 0, 0, 0 }; RetainPtr converter = adoptCF(CGPSConverterCreate(0, &callbacks, 0)); RetainPtr provider = adoptCF(CGDataProviderCreateWithCFData(postScriptData.get())); RetainPtr pdfData = adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0)); RetainPtr consumer = adoptCF(CGDataConsumerCreateWithCFData(pdfData.get())); CGPSConverterConvert(converter.get(), provider.get(), consumer.get(), 0); return pdfData; } 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> 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(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(), 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 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::convertPostScriptDataIfNeeded() { if (!m_isPostScript) return; m_suggestedFilename = String(m_suggestedFilename + ".pdf"); m_data = convertPostScriptDataToPDF(m_data); } void SimplePDFPlugin::streamDidReceiveResponse(uint64_t streamID, const KURL&, uint32_t, uint32_t, const String& mimeType, const String&, const String& suggestedFilename) { ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID); m_suggestedFilename = suggestedFilename; if (equalIgnoringCase(mimeType, postScriptMIMEType)) m_isPostScript = true; } 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(bytes), length); } void SimplePDFPlugin::streamDidFinishLoading(uint64_t streamID) { ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID); convertPostScriptDataIfNeeded(); 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; if (equalIgnoringCase(mimeType, postScriptMIMEType)) m_isPostScript = true; } void SimplePDFPlugin::manualStreamDidReceiveData(const char* bytes, int length) { if (!m_data) m_data = adoptCF(CFDataCreateMutable(0, 0)); CFDataAppendBytes(m_data.get(), reinterpret_cast(bytes), length); } void SimplePDFPlugin::manualStreamDidFinishLoading() { convertPostScriptDataIfNeeded(); pdfDocumentDidLoad(); } NSData *SimplePDFPlugin::liveData() const { // Save data straight from the resource instead of PDFKit if the document is // untouched by the user, so that PDFs which PDFKit can't display will still be downloadable. if (pdfDocumentWasMutated()) return [m_pdfDocument.get() dataRepresentation]; else return rawData(); } PassRefPtr SimplePDFPlugin::liveResourceData() const { NSData *pdfData = liveData(); if (!pdfData) return 0; return SharedBuffer::wrapNSData(pdfData); } 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