WebCore/loader/ImageDocument.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * Copyright (C) 2006, 2007, 2008, 2010 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 COMPUTER, INC. ``AS IS'' AND ANY
       
    14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
       
    15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
       
    16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
       
    17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       
    18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
       
    19  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
       
    20  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
       
    22  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
       
    23  */
       
    24 
       
    25 #include "config.h"
       
    26 #include "ImageDocument.h"
       
    27 
       
    28 #include "CachedImage.h"
       
    29 #include "DocumentLoader.h"
       
    30 #include "EventListener.h"
       
    31 #include "EventNames.h"
       
    32 #include "Frame.h"
       
    33 #include "FrameLoaderClient.h"
       
    34 #include "FrameView.h"
       
    35 #include "HTMLImageElement.h"
       
    36 #include "HTMLNames.h"
       
    37 #include "LocalizedStrings.h"
       
    38 #include "MouseEvent.h"
       
    39 #include "NotImplemented.h"
       
    40 #include "Page.h"
       
    41 #include "RawDataDocumentParser.h"
       
    42 #include "Settings.h"
       
    43 
       
    44 using std::min;
       
    45 
       
    46 namespace WebCore {
       
    47 
       
    48 using namespace HTMLNames;
       
    49 
       
    50 class ImageEventListener : public EventListener {
       
    51 public:
       
    52     static PassRefPtr<ImageEventListener> create(ImageDocument* document) { return adoptRef(new ImageEventListener(document)); }
       
    53     static const ImageEventListener* cast(const EventListener* listener)
       
    54     {
       
    55         return listener->type() == ImageEventListenerType
       
    56             ? static_cast<const ImageEventListener*>(listener)
       
    57             : 0;
       
    58     }
       
    59 
       
    60     virtual bool operator==(const EventListener& other);
       
    61 
       
    62 private:
       
    63     ImageEventListener(ImageDocument* document)
       
    64         : EventListener(ImageEventListenerType)
       
    65         , m_doc(document)
       
    66     {
       
    67     }
       
    68 
       
    69     virtual void handleEvent(ScriptExecutionContext*, Event*);
       
    70 
       
    71     ImageDocument* m_doc;
       
    72 };
       
    73     
       
    74 class ImageDocumentParser : public RawDataDocumentParser {
       
    75 public:
       
    76     ImageDocumentParser(ImageDocument* document)
       
    77         : RawDataDocumentParser(document)
       
    78     {
       
    79     }
       
    80 
       
    81     ImageDocument* document() const
       
    82     {
       
    83         return static_cast<ImageDocument*>(m_document);
       
    84     }
       
    85 
       
    86 private:
       
    87     virtual void appendBytes(DocumentWriter*, const char*, int, bool);
       
    88     virtual void finish();
       
    89 };
       
    90 
       
    91 class ImageDocumentElement : public HTMLImageElement {
       
    92 public:
       
    93     static PassRefPtr<ImageDocumentElement> create(ImageDocument*);
       
    94 
       
    95 private:
       
    96     ImageDocumentElement(ImageDocument* document)
       
    97         : HTMLImageElement(imgTag, document)
       
    98         , m_imageDocument(document)
       
    99     {
       
   100     }
       
   101 
       
   102     virtual ~ImageDocumentElement();
       
   103     virtual void willMoveToNewOwnerDocument();
       
   104 
       
   105     ImageDocument* m_imageDocument;
       
   106 };
       
   107 
       
   108 inline PassRefPtr<ImageDocumentElement> ImageDocumentElement::create(ImageDocument* document)
       
   109 {
       
   110     return adoptRef(new ImageDocumentElement(document));
       
   111 }
       
   112 
       
   113 // --------
       
   114 
       
   115 static float pageZoomFactor(Document* document)
       
   116 {
       
   117     FrameView* view = document->view();
       
   118     return view ? view->pageZoomFactor() : 1;
       
   119 }
       
   120 
       
   121 void ImageDocumentParser::appendBytes(DocumentWriter*, const char*, int, bool)
       
   122 {
       
   123     Frame* frame = document()->frame();
       
   124     Settings* settings = frame->settings();
       
   125     if (!frame->loader()->client()->allowImages(!settings || settings->areImagesEnabled()))
       
   126         return;
       
   127 
       
   128     CachedImage* cachedImage = document()->cachedImage();
       
   129     cachedImage->data(frame->loader()->documentLoader()->mainResourceData(), false);
       
   130 
       
   131     document()->imageChanged();
       
   132 }
       
   133 
       
   134 void ImageDocumentParser::finish()
       
   135 {
       
   136     if (!m_parserStopped && document()->imageElement()) {
       
   137         CachedImage* cachedImage = document()->cachedImage();
       
   138         RefPtr<SharedBuffer> data = document()->frame()->loader()->documentLoader()->mainResourceData();
       
   139 
       
   140         // If this is a multipart image, make a copy of the current part, since the resource data
       
   141         // will be overwritten by the next part.
       
   142         if (document()->frame()->loader()->documentLoader()->isLoadingMultipartContent())
       
   143             data = data->copy();
       
   144 
       
   145         cachedImage->data(data.release(), true);
       
   146         cachedImage->finish();
       
   147 
       
   148         cachedImage->setResponse(document()->frame()->loader()->documentLoader()->response());
       
   149 
       
   150         IntSize size = cachedImage->imageSize(pageZoomFactor(document()));
       
   151         if (size.width()) {
       
   152             // Compute the title, we use the decoded filename of the resource, falling
       
   153             // back on the (decoded) hostname if there is no path.
       
   154             String fileName = decodeURLEscapeSequences(document()->url().lastPathComponent());
       
   155             if (fileName.isEmpty())
       
   156                 fileName = document()->url().host();
       
   157             document()->setTitle(imageTitle(fileName, size));
       
   158         }
       
   159 
       
   160         document()->imageChanged();
       
   161     }
       
   162 
       
   163     document()->finishedParsing();
       
   164 }
       
   165     
       
   166 // --------
       
   167 
       
   168 ImageDocument::ImageDocument(Frame* frame, const KURL& url)
       
   169     : HTMLDocument(frame, url)
       
   170     , m_imageElement(0)
       
   171     , m_imageSizeIsKnown(false)
       
   172     , m_didShrinkImage(false)
       
   173     , m_shouldShrinkImage(shouldShrinkToFit())
       
   174 {
       
   175     setParseMode(Compat);
       
   176 }
       
   177     
       
   178 DocumentParser* ImageDocument::createParser()
       
   179 {
       
   180     return new ImageDocumentParser(this);
       
   181 }
       
   182 
       
   183 void ImageDocument::createDocumentStructure()
       
   184 {
       
   185     ExceptionCode ec;
       
   186     
       
   187     RefPtr<Element> rootElement = Document::createElement(htmlTag, false);
       
   188     appendChild(rootElement, ec);
       
   189 
       
   190     if (frame() && frame()->loader())
       
   191         frame()->loader()->dispatchDocumentElementAvailable();
       
   192     
       
   193     RefPtr<Element> body = Document::createElement(bodyTag, false);
       
   194     body->setAttribute(styleAttr, "margin: 0px;");
       
   195     
       
   196     rootElement->appendChild(body, ec);
       
   197     
       
   198     RefPtr<ImageDocumentElement> imageElement = ImageDocumentElement::create(this);
       
   199     
       
   200     imageElement->setAttribute(styleAttr, "-webkit-user-select: none");        
       
   201     imageElement->setLoadManually(true);
       
   202     imageElement->setSrc(url().string());
       
   203     
       
   204     body->appendChild(imageElement, ec);
       
   205     
       
   206     if (shouldShrinkToFit()) {
       
   207         // Add event listeners
       
   208         RefPtr<EventListener> listener = ImageEventListener::create(this);
       
   209         if (DOMWindow* domWindow = this->domWindow())
       
   210             domWindow->addEventListener("resize", listener, false);
       
   211         imageElement->addEventListener("click", listener.release(), false);
       
   212     }
       
   213 
       
   214     m_imageElement = imageElement.get();
       
   215 }
       
   216 
       
   217 float ImageDocument::scale() const
       
   218 {
       
   219     if (!m_imageElement)
       
   220         return 1.0f;
       
   221 
       
   222     FrameView* view = frame()->view();
       
   223     if (!view)
       
   224         return 1;
       
   225 
       
   226     IntSize imageSize = m_imageElement->cachedImage()->imageSize(view->pageZoomFactor());
       
   227     IntSize windowSize = IntSize(view->width(), view->height());
       
   228     
       
   229     float widthScale = (float)windowSize.width() / imageSize.width();
       
   230     float heightScale = (float)windowSize.height() / imageSize.height();
       
   231 
       
   232     return min(widthScale, heightScale);
       
   233 }
       
   234 
       
   235 void ImageDocument::resizeImageToFit()
       
   236 {
       
   237     if (!m_imageElement)
       
   238         return;
       
   239 
       
   240     IntSize imageSize = m_imageElement->cachedImage()->imageSize(pageZoomFactor(this));
       
   241 
       
   242     float scale = this->scale();
       
   243     m_imageElement->setWidth(static_cast<int>(imageSize.width() * scale));
       
   244     m_imageElement->setHeight(static_cast<int>(imageSize.height() * scale));
       
   245     
       
   246     ExceptionCode ec;
       
   247     m_imageElement->style()->setProperty("cursor", "-webkit-zoom-in", ec);
       
   248 }
       
   249 
       
   250 void ImageDocument::imageClicked(int x, int y)
       
   251 {
       
   252     if (!m_imageSizeIsKnown || imageFitsInWindow())
       
   253         return;
       
   254 
       
   255     m_shouldShrinkImage = !m_shouldShrinkImage;
       
   256     
       
   257     if (m_shouldShrinkImage)
       
   258         windowSizeChanged();
       
   259     else {
       
   260         restoreImageSize();
       
   261         
       
   262         updateLayout();
       
   263         
       
   264         float scale = this->scale();
       
   265         
       
   266         int scrollX = static_cast<int>(x / scale - (float)frame()->view()->width() / 2);
       
   267         int scrollY = static_cast<int>(y / scale - (float)frame()->view()->height() / 2);
       
   268         
       
   269         frame()->view()->setScrollPosition(IntPoint(scrollX, scrollY));
       
   270     }
       
   271 }
       
   272 
       
   273 void ImageDocument::imageChanged()
       
   274 {
       
   275     ASSERT(m_imageElement);
       
   276     
       
   277     if (m_imageSizeIsKnown)
       
   278         return;
       
   279 
       
   280     if (m_imageElement->cachedImage()->imageSize(pageZoomFactor(this)).isEmpty())
       
   281         return;
       
   282     
       
   283     m_imageSizeIsKnown = true;
       
   284     
       
   285     if (shouldShrinkToFit()) {
       
   286         // Force resizing of the image
       
   287         windowSizeChanged();
       
   288     }
       
   289 }
       
   290 
       
   291 void ImageDocument::restoreImageSize()
       
   292 {
       
   293     if (!m_imageElement || !m_imageSizeIsKnown)
       
   294         return;
       
   295     
       
   296     m_imageElement->setWidth(m_imageElement->cachedImage()->imageSize(pageZoomFactor(this)).width());
       
   297     m_imageElement->setHeight(m_imageElement->cachedImage()->imageSize(pageZoomFactor(this)).height());
       
   298     
       
   299     ExceptionCode ec;
       
   300     if (imageFitsInWindow())
       
   301         m_imageElement->style()->removeProperty("cursor", ec);
       
   302     else
       
   303         m_imageElement->style()->setProperty("cursor", "-webkit-zoom-out", ec);
       
   304         
       
   305     m_didShrinkImage = false;
       
   306 }
       
   307 
       
   308 bool ImageDocument::imageFitsInWindow() const
       
   309 {
       
   310     if (!m_imageElement)
       
   311         return true;
       
   312 
       
   313     FrameView* view = frame()->view();
       
   314 
       
   315     IntSize imageSize = m_imageElement->cachedImage()->imageSize(view->pageZoomFactor());
       
   316     IntSize windowSize = IntSize(view->width(), view->height());
       
   317     
       
   318     return imageSize.width() <= windowSize.width() && imageSize.height() <= windowSize.height();    
       
   319 }
       
   320 
       
   321 void ImageDocument::windowSizeChanged()
       
   322 {
       
   323     if (!m_imageElement || !m_imageSizeIsKnown)
       
   324         return;
       
   325 
       
   326     bool fitsInWindow = imageFitsInWindow();
       
   327     
       
   328     // If the image has been explicitly zoomed in, restore the cursor if the image fits
       
   329     // and set it to a zoom out cursor if the image doesn't fit
       
   330     if (!m_shouldShrinkImage) {
       
   331         ExceptionCode ec;
       
   332         
       
   333         if (fitsInWindow)
       
   334             m_imageElement->style()->removeProperty("cursor", ec);
       
   335         else
       
   336             m_imageElement->style()->setProperty("cursor", "-webkit-zoom-out", ec);
       
   337         return;
       
   338     }
       
   339     
       
   340     if (m_didShrinkImage) {
       
   341         // If the window has been resized so that the image fits, restore the image size
       
   342         // otherwise update the restored image size.
       
   343         if (fitsInWindow)
       
   344             restoreImageSize();
       
   345         else
       
   346             resizeImageToFit();
       
   347     } else {
       
   348         // If the image isn't resized but needs to be, then resize it.
       
   349         if (!fitsInWindow) {
       
   350             resizeImageToFit();
       
   351             m_didShrinkImage = true;
       
   352         }
       
   353     }
       
   354 }
       
   355 
       
   356 CachedImage* ImageDocument::cachedImage()
       
   357 { 
       
   358     if (!m_imageElement)
       
   359         createDocumentStructure();
       
   360     
       
   361     return m_imageElement->cachedImage();
       
   362 }
       
   363 
       
   364 bool ImageDocument::shouldShrinkToFit() const
       
   365 {
       
   366     return frame()->page()->settings()->shrinksStandaloneImagesToFit() &&
       
   367         frame()->page()->mainFrame() == frame();
       
   368 }
       
   369 
       
   370 // --------
       
   371 
       
   372 void ImageEventListener::handleEvent(ScriptExecutionContext*, Event* event)
       
   373 {
       
   374     if (event->type() == eventNames().resizeEvent)
       
   375         m_doc->windowSizeChanged();
       
   376     else if (event->type() == eventNames().clickEvent) {
       
   377         MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
       
   378         m_doc->imageClicked(mouseEvent->x(), mouseEvent->y());
       
   379     }
       
   380 }
       
   381 
       
   382 bool ImageEventListener::operator==(const EventListener& listener)
       
   383 {
       
   384     if (const ImageEventListener* imageEventListener = ImageEventListener::cast(&listener))
       
   385         return m_doc == imageEventListener->m_doc;
       
   386     return false;
       
   387 }
       
   388 
       
   389 // --------
       
   390 
       
   391 ImageDocumentElement::~ImageDocumentElement()
       
   392 {
       
   393     if (m_imageDocument)
       
   394         m_imageDocument->disconnectImageElement();
       
   395 }
       
   396 
       
   397 void ImageDocumentElement::willMoveToNewOwnerDocument()
       
   398 {
       
   399     if (m_imageDocument) {
       
   400         m_imageDocument->disconnectImageElement();
       
   401         m_imageDocument = 0;
       
   402     }
       
   403     HTMLImageElement::willMoveToNewOwnerDocument();
       
   404 }
       
   405 
       
   406 }