LayoutTests:
[WebKit-https.git] / WebCore / loader / ImageDocument.cpp
1 /*
2  * Copyright (C) 2006 Apple Computer, 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 "Element.h"
31 #include "EventListener.h"
32 #include "EventNames.h"
33 #include "Frame.h"
34 #include "FrameLoader.h"
35 #include "FrameView.h"
36 #include "HTMLImageElement.h"
37 #include "HTMLNames.h"
38 #include "MouseEvent.h"
39 #include "Page.h"
40 #include "SegmentedString.h"
41 #include "Settings.h"
42 #include "Text.h"
43 #include "XMLTokenizer.h"
44
45 #if PLATFORM(MAC)
46 #include "ImageDocumentMac.h"
47 #endif 
48
49 using std::min;
50
51 namespace WebCore {
52
53 using namespace EventNames;
54 using namespace HTMLNames;
55
56 class ImageEventListener : public EventListener {
57 public:
58     ImageEventListener(ImageDocument* doc) : m_doc(doc) { }
59     
60     virtual void handleEvent(Event*, bool isWindowEvent);
61 private:
62     ImageDocument* m_doc;
63 };
64     
65 class ImageTokenizer : public Tokenizer {
66 public:
67     ImageTokenizer(ImageDocument* doc) : m_doc(doc) {}
68         
69     virtual bool write(const SegmentedString&, bool appendData);
70     virtual void finish();
71     virtual bool isWaitingForScripts() const;
72     
73     virtual bool wantsRawData() const { return true; }
74     virtual bool writeRawData(const char* data, int len);
75 private:
76     ImageDocument* m_doc;
77 };
78
79 bool ImageTokenizer::write(const SegmentedString& s, bool appendData)
80 {
81     ASSERT_NOT_REACHED();
82     return false;
83 }
84
85 bool ImageTokenizer::writeRawData(const char* data, int len)
86 {
87     CachedImage* cachedImage = m_doc->cachedImage();
88     cachedImage->data(m_doc->frame()->loader()->documentLoader()->mainResourceData(), false);
89
90     m_doc->imageChanged();
91     
92     return false;
93 }
94
95 void ImageTokenizer::finish()
96 {
97     if (!m_parserStopped && m_doc->imageElement()) {
98         CachedImage* cachedImage = m_doc->cachedImage();
99         RefPtr<SharedBuffer> data = m_doc->frame()->loader()->documentLoader()->mainResourceData();
100
101         // If this is a multipart image, make a copy of the current part, since the resource data
102         // will be overwritten by the next part.
103         if (m_doc->frame()->loader()->documentLoader()->isLoadingMultipartContent())
104             data = new SharedBuffer(data->data(), data->size());
105
106         cachedImage->data(data.release(), true);
107         cachedImage->finish();
108
109         cachedImage->setResponse(m_doc->frame()->loader()->documentLoader()->response());
110         
111         // FIXME: Need code to set the title for platforms other than Mac OS X.
112 #if PLATFORM(MAC)
113         finishImageLoad(m_doc, cachedImage);
114 #endif
115     }
116
117     m_doc->finishedParsing();
118 }
119     
120 bool ImageTokenizer::isWaitingForScripts() const
121 {
122     // An image document is never waiting for scripts
123     return false;
124 }
125     
126 ImageDocument::ImageDocument(DOMImplementation* implementation, Frame* frame)
127     : HTMLDocument(implementation, frame)
128     , m_imageElement(0)
129     , m_imageSizeIsKnown(false)
130     , m_didShrinkImage(false)
131     , m_shouldShrinkImage(true)
132 {
133     setParseMode(Compat);
134 }
135     
136 Tokenizer* ImageDocument::createTokenizer()
137 {
138     return new ImageTokenizer(this);
139 }
140
141 void ImageDocument::createDocumentStructure()
142 {
143     ExceptionCode ec;
144     
145     RefPtr<Element> rootElement = createElementNS(xhtmlNamespaceURI, "html", ec);
146     appendChild(rootElement, ec);
147     
148     RefPtr<Element> body = createElementNS(xhtmlNamespaceURI, "body", ec);
149     body->setAttribute(styleAttr, "margin: 0px;");
150     
151     rootElement->appendChild(body, ec);
152     
153     RefPtr<Element> imageElement = createElementNS(xhtmlNamespaceURI, "img", ec);
154     
155     m_imageElement = static_cast<HTMLImageElement *>(imageElement.get());
156     m_imageElement->setAttribute(styleAttr, "-webkit-user-select: none");        
157     m_imageElement->setLoadManually(true);
158     m_imageElement->setSrc(URL());
159     
160     body->appendChild(imageElement, ec);
161     
162     if (!frame()->page()->settings()->shrinksStandaloneImagesToFit())
163         return;
164     
165     // Add event listeners
166     RefPtr<EventListener> listener = new ImageEventListener(this);
167     addWindowEventListener("resize", listener, false);
168     m_imageElement->addEventListener("click", listener.release(), false);
169 }
170
171 float ImageDocument::scale() const
172 {
173     IntSize imageSize = m_imageElement->cachedImage()->imageSize();
174     IntSize windowSize = IntSize(frame()->view()->width(), frame()->view()->height());
175     
176     float widthScale = (float)windowSize.width() / imageSize.width();
177     float heightScale = (float)windowSize.height() / imageSize.height();
178
179     return min(widthScale, heightScale);
180 }
181
182 void ImageDocument::resizeImageToFit()
183 {
184     IntSize imageSize = m_imageElement->cachedImage()->imageSize();
185
186     float scale = this->scale();
187     m_imageElement->setWidth(static_cast<int>(imageSize.width() * scale));
188     m_imageElement->setHeight(static_cast<int>(imageSize.height() * scale));
189     
190     ExceptionCode ec;
191     m_imageElement->style()->setProperty("cursor", "-webkit-zoom-in", ec);
192 }
193
194 void ImageDocument::imageClicked(int x, int y)
195 {
196     if (!m_imageSizeIsKnown || imageFitsInWindow())
197         return;
198
199     m_shouldShrinkImage = !m_shouldShrinkImage;
200     
201     if (m_shouldShrinkImage)
202         windowSizeChanged();
203     else {
204         restoreImageSize();
205         
206         updateLayout();
207         
208         float scale = this->scale();
209         
210         int scrollX = static_cast<int>(x / scale - (float)frame()->view()->width() / 2);
211         int scrollY = static_cast<int>(y / scale - (float)frame()->view()->height() / 2);
212         
213         frame()->view()->setContentsPos(scrollX, scrollY);
214     }
215 }
216
217 void ImageDocument::imageChanged()
218 {
219     ASSERT(m_imageElement);
220     
221     if (m_imageSizeIsKnown)
222         return;
223
224     if (m_imageElement->cachedImage()->imageSize().isEmpty())
225         return;
226     
227     m_imageSizeIsKnown = true;
228     
229     if (!frame()->page()->settings()->shrinksStandaloneImagesToFit())
230         return;
231     
232     // Force resizing of the image
233     windowSizeChanged();
234 }
235
236 void ImageDocument::restoreImageSize()
237 {
238     if (!m_imageSizeIsKnown)
239         return;
240     
241     m_imageElement->setWidth(m_imageElement->cachedImage()->imageSize().width());
242     m_imageElement->setHeight(m_imageElement->cachedImage()->imageSize().height());
243     
244     ExceptionCode ec;
245     if (imageFitsInWindow())
246         m_imageElement->style()->removeProperty("cursor", ec);
247     else
248         m_imageElement->style()->setProperty("cursor", "-webkit-zoom-out", ec);
249         
250     m_didShrinkImage = false;
251 }
252
253 bool ImageDocument::imageFitsInWindow() const
254 {
255     IntSize imageSize = m_imageElement->cachedImage()->imageSize();
256     IntSize windowSize = IntSize(frame()->view()->width(), frame()->view()->height());
257     
258     return imageSize.width() <= windowSize.width() && imageSize.height() <= windowSize.height();    
259 }
260
261 void ImageDocument::windowSizeChanged()
262 {
263     if (!m_imageSizeIsKnown)
264         return;
265
266     bool fitsInWindow = imageFitsInWindow();
267     
268     // If the image has been explicitly zoomed in, restore the cursor if the image fits
269     // and set it to a zoom out cursor if the image doesn't fit
270     if (!m_shouldShrinkImage) {
271         ExceptionCode ec;
272         
273         if (fitsInWindow)
274             m_imageElement->style()->removeProperty("cursor", ec);
275         else
276             m_imageElement->style()->setProperty("cursor", "-webkit-zoom-out", ec);
277         return;
278     }
279     
280     if (m_didShrinkImage) {
281         // If the window has been resized so that the image fits, restore the image size
282         // otherwise update the restored image size.
283         if (fitsInWindow)
284             restoreImageSize();
285         else
286             resizeImageToFit();
287     } else {
288         // If the image isn't resized but needs to be, then resize it.
289         if (!fitsInWindow) {
290             resizeImageToFit();
291             m_didShrinkImage = true;
292         }
293     }
294 }
295
296 CachedImage* ImageDocument::cachedImage()
297
298     if (!m_imageElement)
299         createDocumentStructure();
300     
301     return m_imageElement->cachedImage();
302 }
303
304 void ImageEventListener::handleEvent(Event* event, bool isWindowEvent)
305 {
306     if (event->type() == resizeEvent)
307         m_doc->windowSizeChanged();
308     else if (event->type() == clickEvent) {
309         MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
310         m_doc->imageClicked(mouseEvent->x(), mouseEvent->y());
311     }
312 }
313
314 }