diff options
Diffstat (limited to 'Source/WebCore/html/ImageDocument.cpp')
-rw-r--r-- | Source/WebCore/html/ImageDocument.cpp | 399 |
1 files changed, 194 insertions, 205 deletions
diff --git a/Source/WebCore/html/ImageDocument.cpp b/Source/WebCore/html/ImageDocument.cpp index fd4de6490..f1a26d2e4 100644 --- a/Source/WebCore/html/ImageDocument.cpp +++ b/Source/WebCore/html/ImageDocument.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2010, 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * 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 COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY @@ -26,79 +26,73 @@ #include "ImageDocument.h" #include "CachedImage.h" +#include "Chrome.h" +#include "ChromeClient.h" #include "DocumentLoader.h" #include "EventListener.h" #include "EventNames.h" -#include "ExceptionCodePlaceholder.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" #include "FrameView.h" +#include "HTMLBodyElement.h" +#include "HTMLHeadElement.h" #include "HTMLHtmlElement.h" #include "HTMLImageElement.h" #include "HTMLNames.h" #include "LocalizedStrings.h" +#include "MIMETypeRegistry.h" #include "MainFrame.h" #include "MouseEvent.h" -#include "NotImplemented.h" #include "Page.h" #include "RawDataDocumentParser.h" #include "RenderElement.h" -#include "ResourceBuffer.h" #include "Settings.h" namespace WebCore { using namespace HTMLNames; -class ImageEventListener : public EventListener { +#if !PLATFORM(IOS) +class ImageEventListener final : public EventListener { public: - static PassRefPtr<ImageEventListener> create(ImageDocument* document) { return adoptRef(new ImageEventListener(document)); } - static const ImageEventListener* cast(const EventListener* listener) - { - return listener->type() == ImageEventListenerType - ? static_cast<const ImageEventListener*>(listener) - : 0; - } - - virtual bool operator==(const EventListener& other) override; + static Ref<ImageEventListener> create(ImageDocument& document) { return adoptRef(*new ImageEventListener(document)); } private: - ImageEventListener(ImageDocument* document) + ImageEventListener(ImageDocument& document) : EventListener(ImageEventListenerType) - , m_doc(document) + , m_document(document) { } - virtual void handleEvent(ScriptExecutionContext*, Event*) override; + bool operator==(const EventListener&) const override; + void handleEvent(ScriptExecutionContext*, Event*) override; - ImageDocument* m_doc; + ImageDocument& m_document; }; - +#endif + class ImageDocumentParser final : public RawDataDocumentParser { public: - static PassRefPtr<ImageDocumentParser> create(ImageDocument& document) + static Ref<ImageDocumentParser> create(ImageDocument& document) { - return adoptRef(new ImageDocumentParser(document)); + return adoptRef(*new ImageDocumentParser(document)); } - ImageDocument* document() const - { - return toImageDocument(RawDataDocumentParser::document()); - } - private: ImageDocumentParser(ImageDocument& document) : RawDataDocumentParser(document) { } - virtual void appendBytes(DocumentWriter&, const char*, size_t) override; - virtual void finish() override; + ImageDocument& document() const; + + void appendBytes(DocumentWriter&, const char*, size_t) override; + void finish() override; }; class ImageDocumentElement final : public HTMLImageElement { public: - static PassRefPtr<ImageDocumentElement> create(ImageDocument&); + static Ref<ImageDocumentElement> create(ImageDocument&); private: ImageDocumentElement(ImageDocument& document) @@ -108,249 +102,247 @@ private: } virtual ~ImageDocumentElement(); - virtual void didMoveToNewDocument(Document* oldDocument) override; + void didMoveToNewDocument(Document& oldDocument) override; ImageDocument* m_imageDocument; }; -inline PassRefPtr<ImageDocumentElement> ImageDocumentElement::create(ImageDocument& document) +inline Ref<ImageDocumentElement> ImageDocumentElement::create(ImageDocument& document) { - return adoptRef(new ImageDocumentElement(document)); + return adoptRef(*new ImageDocumentElement(document)); } // -------- -static float pageZoomFactor(const Document* document) +HTMLImageElement* ImageDocument::imageElement() const { - Frame* frame = document->frame(); - return frame ? frame->pageZoomFactor() : 1; + return m_imageElement; } -void ImageDocumentParser::appendBytes(DocumentWriter&, const char*, size_t) +LayoutSize ImageDocument::imageSize() +{ + ASSERT(m_imageElement); + updateStyleIfNeeded(); + return m_imageElement->cachedImage()->imageSizeForRenderer(m_imageElement->renderer(), frame() ? frame()->pageZoomFactor() : 1); +} + +void ImageDocument::updateDuringParsing() { - Frame* frame = document()->frame(); - if (!frame->loader().client().allowImage(frame->settings().areImagesEnabled(), document()->url())) + if (!settings().areImagesEnabled()) return; - CachedImage* cachedImage = document()->cachedImage(); - RefPtr<ResourceBuffer> resourceData = frame->loader().documentLoader()->mainResourceData(); - cachedImage->addDataBuffer(resourceData.get()); + if (!m_imageElement) + createDocumentStructure(); + + if (RefPtr<SharedBuffer> buffer = loader()->mainResourceData()) + m_imageElement->cachedImage()->addDataBuffer(*buffer); - document()->imageUpdated(); + imageUpdated(); } -void ImageDocumentParser::finish() +void ImageDocument::finishedParsing() { - if (!isStopped() && document()->imageElement()) { - CachedImage* cachedImage = document()->cachedImage(); - RefPtr<ResourceBuffer> data = document()->frame()->loader().documentLoader()->mainResourceData(); + if (!parser()->isStopped() && m_imageElement) { + CachedImage& cachedImage = *m_imageElement->cachedImage(); + RefPtr<SharedBuffer> data = loader()->mainResourceData(); // If this is a multipart image, make a copy of the current part, since the resource data // will be overwritten by the next part. - if (document()->frame()->loader().documentLoader()->isLoadingMultipartContent()) + if (data && loader()->isLoadingMultipartContent()) data = data->copy(); - cachedImage->finishLoading(data.get()); - cachedImage->finish(); - - cachedImage->setResponse(document()->frame()->loader().documentLoader()->response()); + cachedImage.finishLoading(data.get()); + cachedImage.finish(); // Report the natural image size in the page title, regardless of zoom level. // At a zoom level of 1 the image is guaranteed to have an integer size. - IntSize size = flooredIntSize(cachedImage->imageSizeForRenderer(document()->imageElement()->renderer(), 1.0f)); + updateStyleIfNeeded(); + IntSize size = flooredIntSize(cachedImage.imageSizeForRenderer(m_imageElement->renderer(), 1)); if (size.width()) { - // Compute the title, we use the decoded filename of the resource, falling - // back on the (decoded) hostname if there is no path. - String fileName = decodeURLEscapeSequences(document()->url().lastPathComponent()); - if (fileName.isEmpty()) - fileName = document()->url().host(); - document()->setTitle(imageTitle(fileName, size)); + // Compute the title. We use the decoded filename of the resource, falling + // back on the hostname if there is no path. + String name = decodeURLEscapeSequences(url().lastPathComponent()); + if (name.isEmpty()) + name = url().host(); + setTitle(imageTitle(name, size)); } - document()->imageUpdated(); + imageUpdated(); } - document()->finishedParsing(); + HTMLDocument::finishedParsing(); } -// -------- +inline ImageDocument& ImageDocumentParser::document() const +{ + // Only used during parsing, so document is guaranteed to be non-null. + ASSERT(RawDataDocumentParser::document()); + return downcast<ImageDocument>(*RawDataDocumentParser::document()); +} -ImageDocument::ImageDocument(Frame* frame, const URL& url) - : HTMLDocument(frame, url, ImageDocumentClass) - , m_imageElement(0) +void ImageDocumentParser::appendBytes(DocumentWriter&, const char*, size_t) +{ + document().updateDuringParsing(); +} + +void ImageDocumentParser::finish() +{ + document().finishedParsing(); +} + +ImageDocument::ImageDocument(Frame& frame, const URL& url) + : HTMLDocument(&frame, url, ImageDocumentClass) + , m_imageElement(nullptr) , m_imageSizeIsKnown(false) +#if !PLATFORM(IOS) , m_didShrinkImage(false) - , m_shouldShrinkImage(shouldShrinkToFit()) +#endif + , m_shouldShrinkImage(frame.settings().shrinksStandaloneImagesToFit() && frame.isMainFrame()) { - setCompatibilityMode(QuirksMode); + setCompatibilityMode(DocumentCompatibilityMode::QuirksMode); lockCompatibilityMode(); } -PassRefPtr<DocumentParser> ImageDocument::createParser() +Ref<DocumentParser> ImageDocument::createParser() { return ImageDocumentParser::create(*this); } void ImageDocument::createDocumentStructure() { - RefPtr<Element> rootElement = Document::createElement(htmlTag, false); - appendChild(rootElement, IGNORE_EXCEPTION); - toHTMLHtmlElement(rootElement.get())->insertedByParser(); + auto rootElement = HTMLHtmlElement::create(*this); + appendChild(rootElement); + rootElement->insertedByParser(); - if (frame()) - frame()->injectUserScripts(InjectAtDocumentStart); + frame()->injectUserScripts(InjectAtDocumentStart); - RefPtr<Element> body = Document::createElement(bodyTag, false); - body->setAttribute(styleAttr, "margin: 0px;"); - - rootElement->appendChild(body, IGNORE_EXCEPTION); - - RefPtr<ImageDocumentElement> imageElement = ImageDocumentElement::create(*this); + // We need a <head> so that the call to setTitle() later on actually has an <head> to append to <title> to. + auto head = HTMLHeadElement::create(*this); + rootElement->appendChild(head); + + auto body = HTMLBodyElement::create(*this); + body->setAttribute(styleAttr, "margin: 0px"); + if (MIMETypeRegistry::isPDFMIMEType(document().loader()->responseMIMEType())) + body->setInlineStyleProperty(CSSPropertyBackgroundColor, "white", CSSPrimitiveValue::CSS_IDENT); + rootElement->appendChild(body); - imageElement->setAttribute(styleAttr, "-webkit-user-select: none"); + auto imageElement = ImageDocumentElement::create(*this); + if (m_shouldShrinkImage) + imageElement->setAttribute(styleAttr, "-webkit-user-select:none; display:block; margin:auto;"); + else + imageElement->setAttribute(styleAttr, "-webkit-user-select:none;"); imageElement->setLoadManually(true); imageElement->setSrc(url().string()); + imageElement->cachedImage()->setResponse(loader()->response()); + body->appendChild(imageElement); - body->appendChild(imageElement, IGNORE_EXCEPTION); - - if (shouldShrinkToFit()) { - // Add event listeners - RefPtr<EventListener> listener = ImageEventListener::create(this); - if (DOMWindow* domWindow = this->domWindow()) - domWindow->addEventListener("resize", listener, false); - imageElement->addEventListener("click", listener.release(), false); + if (m_shouldShrinkImage) { #if PLATFORM(IOS) // Set the viewport to be in device pixels (rather than the default of 980). processViewport(ASCIILiteral("width=device-width"), ViewportArguments::ImageDocument); +#else + auto listener = ImageEventListener::create(*this); + if (DOMWindow* window = this->domWindow()) + window->addEventListener("resize", listener.copyRef(), false); + imageElement->addEventListener("click", WTFMove(listener), false); #endif } - m_imageElement = imageElement.get(); + m_imageElement = imageElement.ptr(); } -float ImageDocument::scale() const +void ImageDocument::imageUpdated() { + ASSERT(m_imageElement); + + if (m_imageSizeIsKnown) + return; + + LayoutSize imageSize = this->imageSize(); + if (imageSize.isEmpty()) + return; + + m_imageSizeIsKnown = true; + + if (m_shouldShrinkImage) { #if PLATFORM(IOS) - // On iOS big images are subsampled to make them smaller. So, don't resize them. - return 1; + FloatSize screenSize = page()->chrome().screenSize(); + if (imageSize.width() > screenSize.width()) + processViewport(String::format("width=%u", static_cast<unsigned>(imageSize.width().toInt())), ViewportArguments::ImageDocument); + if (page()) + page()->chrome().client().imageOrMediaDocumentSizeChanged(IntSize(imageSize.width(), imageSize.height())); #else + // Call windowSizeChanged for its side effect of sizing the image. + windowSizeChanged(); +#endif + } +} + +#if !PLATFORM(IOS) +float ImageDocument::scale() +{ if (!m_imageElement) return 1; - FrameView* view = frame()->view(); + FrameView* view = this->view(); if (!view) return 1; - LayoutSize imageSize = m_imageElement->cachedImage()->imageSizeForRenderer(m_imageElement->renderer(), pageZoomFactor(this)); - LayoutSize windowSize = LayoutSize(view->width(), view->height()); + LayoutSize imageSize = this->imageSize(); - float widthScale = static_cast<float>(windowSize.width()) / imageSize.width(); - float heightScale = static_cast<float>(windowSize.height()) / imageSize.height(); + IntSize viewportSize = view->visibleSize(); + float widthScale = viewportSize.width() / imageSize.width().toFloat(); + float heightScale = viewportSize.height() / imageSize.height().toFloat(); return std::min(widthScale, heightScale); -#endif } void ImageDocument::resizeImageToFit() { -#if PLATFORM(IOS) - // On iOS big images are subsampled to make them smaller. So, don't resize them. -#else if (!m_imageElement) return; - LayoutSize imageSize = m_imageElement->cachedImage()->imageSizeForRenderer(m_imageElement->renderer(), pageZoomFactor(this)); + LayoutSize imageSize = this->imageSize(); float scale = this->scale(); m_imageElement->setWidth(static_cast<int>(imageSize.width() * scale)); m_imageElement->setHeight(static_cast<int>(imageSize.height() * scale)); - m_imageElement->setInlineStyleProperty(CSSPropertyCursor, CSSValueWebkitZoomIn); -#endif -} - -void ImageDocument::imageClicked(int x, int y) -{ -#if PLATFORM(IOS) - // On iOS big images are subsampled to make them smaller. So, don't resize them. - UNUSED_PARAM(x); - UNUSED_PARAM(y); -#else - if (!m_imageSizeIsKnown || imageFitsInWindow()) - return; - - m_shouldShrinkImage = !m_shouldShrinkImage; - - if (m_shouldShrinkImage) - windowSizeChanged(); - else { - restoreImageSize(); - - updateLayout(); - - float scale = this->scale(); - - int scrollX = static_cast<int>(x / scale - (float)frame()->view()->width() / 2); - int scrollY = static_cast<int>(y / scale - (float)frame()->view()->height() / 2); - - frame()->view()->setScrollPosition(IntPoint(scrollX, scrollY)); - } -#endif -} - -void ImageDocument::imageUpdated() -{ - ASSERT(m_imageElement); - - if (m_imageSizeIsKnown) - return; - - if (m_imageElement->cachedImage()->imageSizeForRenderer(m_imageElement->renderer(), pageZoomFactor(this)).isEmpty()) - return; - - m_imageSizeIsKnown = true; - - if (shouldShrinkToFit()) { - // Force resizing of the image - windowSizeChanged(); - } + m_imageElement->setInlineStyleProperty(CSSPropertyCursor, CSSValueZoomIn); } void ImageDocument::restoreImageSize() { if (!m_imageElement || !m_imageSizeIsKnown) return; - - LayoutSize imageSize = m_imageElement->cachedImage()->imageSizeForRenderer(m_imageElement->renderer(), pageZoomFactor(this)); - m_imageElement->setWidth(imageSize.width()); - m_imageElement->setHeight(imageSize.height()); - + + LayoutSize imageSize = this->imageSize(); + m_imageElement->setWidth(imageSize.width().toUnsigned()); + m_imageElement->setHeight(imageSize.height().toUnsigned()); + if (imageFitsInWindow()) m_imageElement->removeInlineStyleProperty(CSSPropertyCursor); else - m_imageElement->setInlineStyleProperty(CSSPropertyCursor, CSSValueWebkitZoomOut); - + m_imageElement->setInlineStyleProperty(CSSPropertyCursor, CSSValueZoomOut); + m_didShrinkImage = false; } -bool ImageDocument::imageFitsInWindow() const +bool ImageDocument::imageFitsInWindow() { if (!m_imageElement) return true; - FrameView* view = frame()->view(); + FrameView* view = this->view(); if (!view) return true; - LayoutSize imageSize = m_imageElement->cachedImage()->imageSizeForRenderer(m_imageElement->renderer(), pageZoomFactor(this)); -#if PLATFORM(IOS) - LayoutSize windowSize = view->contentsToScreen(view->visibleContentRect()).size(); -#else - LayoutSize windowSize = LayoutSize(view->width(), view->height()); -#endif - return imageSize.width() <= windowSize.width() && imageSize.height() <= windowSize.height(); + LayoutSize imageSize = this->imageSize(); + IntSize viewportSize = view->visibleSize(); + return imageSize.width() <= viewportSize.width() && imageSize.height() <= viewportSize.height(); } + void ImageDocument::windowSizeChanged() { if (!m_imageElement || !m_imageSizeIsKnown) @@ -358,32 +350,18 @@ void ImageDocument::windowSizeChanged() bool fitsInWindow = imageFitsInWindow(); -#if PLATFORM(IOS) - if (fitsInWindow) - return; - - LayoutSize imageSize = m_imageElement->cachedImage()->imageSizeForRenderer(m_imageElement->renderer(), pageZoomFactor(this)); - LayoutRect visibleScreenSize = frame()->view()->contentsToScreen(frame()->view()->visibleContentRect()); - - float widthScale = static_cast<float>(visibleScreenSize.width()) / imageSize.width(); - float heightScale = static_cast<float>(visibleScreenSize.height()) / imageSize.height(); - if (widthScale < heightScale) - processViewport(String::format("width=%d", imageSize.width().toInt()), ViewportArguments::ImageDocument); - else - processViewport(String::format("width=%d", static_cast<int>(1.0f + (1.0f - heightScale)) * imageSize.width().toInt()), ViewportArguments::ImageDocument); -#else // If the image has been explicitly zoomed in, restore the cursor if the image fits // and set it to a zoom out cursor if the image doesn't fit if (!m_shouldShrinkImage) { if (fitsInWindow) m_imageElement->removeInlineStyleProperty(CSSPropertyCursor); else - m_imageElement->setInlineStyleProperty(CSSPropertyCursor, CSSValueWebkitZoomOut); + m_imageElement->setInlineStyleProperty(CSSPropertyCursor, CSSValueZoomOut); return; } if (m_didShrinkImage) { - // If the window has been resized so that the image fits, restore the image size + // If the window has been resized so that the image fits, restore the image size, // otherwise update the restored image size. if (fitsInWindow) restoreImageSize(); @@ -396,38 +374,49 @@ void ImageDocument::windowSizeChanged() m_didShrinkImage = true; } } -#endif -} - -CachedImage* ImageDocument::cachedImage() -{ - if (!m_imageElement) - createDocumentStructure(); - - return m_imageElement->cachedImage(); } -bool ImageDocument::shouldShrinkToFit() const +void ImageDocument::imageClicked(int x, int y) { - return frame()->settings().shrinksStandaloneImagesToFit() && frame()->isMainFrame(); + if (!m_imageSizeIsKnown || imageFitsInWindow()) + return; + + m_shouldShrinkImage = !m_shouldShrinkImage; + + if (m_shouldShrinkImage) { + // Call windowSizeChanged for its side effect of sizing the image. + windowSizeChanged(); + } else { + restoreImageSize(); + + updateLayout(); + + float scale = this->scale(); + + IntSize viewportSize = view()->visibleSize(); + int scrollX = static_cast<int>(x / scale - viewportSize.width() / 2.0f); + int scrollY = static_cast<int>(y / scale - viewportSize.height() / 2.0f); + + view()->setScrollPosition(IntPoint(scrollX, scrollY)); + } } void ImageEventListener::handleEvent(ScriptExecutionContext*, Event* event) { if (event->type() == eventNames().resizeEvent) - m_doc->windowSizeChanged(); - else if (event->type() == eventNames().clickEvent && event->isMouseEvent()) { - MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); - m_doc->imageClicked(mouseEvent->x(), mouseEvent->y()); + m_document.windowSizeChanged(); + else if (event->type() == eventNames().clickEvent && is<MouseEvent>(*event)) { + MouseEvent& mouseEvent = downcast<MouseEvent>(*event); + m_document.imageClicked(mouseEvent.offsetX(), mouseEvent.offsetY()); } } -bool ImageEventListener::operator==(const EventListener& listener) +bool ImageEventListener::operator==(const EventListener& other) const { - if (const ImageEventListener* imageEventListener = ImageEventListener::cast(&listener)) - return m_doc == imageEventListener->m_doc; - return false; + // All ImageEventListener objects compare as equal; OK since there is only one per document. + return other.type() == ImageEventListenerType; } +#endif // -------- @@ -437,11 +426,11 @@ ImageDocumentElement::~ImageDocumentElement() m_imageDocument->disconnectImageElement(); } -void ImageDocumentElement::didMoveToNewDocument(Document* oldDocument) +void ImageDocumentElement::didMoveToNewDocument(Document& oldDocument) { if (m_imageDocument) { m_imageDocument->disconnectImageElement(); - m_imageDocument = 0; + m_imageDocument = nullptr; } HTMLImageElement::didMoveToNewDocument(oldDocument); } |