2 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 #include "ImageDocument.h"
28 #include "CachedImage.h"
29 #include "DocumentLoader.h"
31 #include "EventListener.h"
32 #include "EventNames.h"
34 #include "FrameLoader.h"
35 #include "FrameView.h"
36 #include "HTMLImageElement.h"
37 #include "HTMLNames.h"
38 #include "MouseEvent.h"
40 #include "SegmentedString.h"
43 #include "XMLTokenizer.h"
46 #include "ImageDocumentMac.h"
53 using namespace EventNames;
54 using namespace HTMLNames;
56 class ImageEventListener : public EventListener {
58 ImageEventListener(ImageDocument* doc) : m_doc(doc) { }
60 virtual void handleEvent(Event*, bool isWindowEvent);
65 class ImageTokenizer : public Tokenizer {
67 ImageTokenizer(ImageDocument* doc) : m_doc(doc) {}
69 virtual bool write(const SegmentedString&, bool appendData);
70 virtual void finish();
71 virtual bool isWaitingForScripts() const;
73 virtual bool wantsRawData() const { return true; }
74 virtual bool writeRawData(const char* data, int len);
79 bool ImageTokenizer::write(const SegmentedString& s, bool appendData)
85 bool ImageTokenizer::writeRawData(const char* data, int len)
87 CachedImage* cachedImage = m_doc->cachedImage();
88 cachedImage->data(m_doc->frame()->loader()->documentLoader()->mainResourceData(), false);
90 m_doc->imageChanged();
95 void ImageTokenizer::finish()
97 if (!m_parserStopped && m_doc->imageElement()) {
98 CachedImage* cachedImage = m_doc->cachedImage();
99 RefPtr<SharedBuffer> data = m_doc->frame()->loader()->documentLoader()->mainResourceData();
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());
106 cachedImage->data(data.release(), true);
107 cachedImage->finish();
109 cachedImage->setResponse(m_doc->frame()->loader()->documentLoader()->response());
111 // FIXME: Need code to set the title for platforms other than Mac OS X.
113 finishImageLoad(m_doc, cachedImage);
117 m_doc->finishedParsing();
120 bool ImageTokenizer::isWaitingForScripts() const
122 // An image document is never waiting for scripts
126 ImageDocument::ImageDocument(DOMImplementation* implementation, Frame* frame)
127 : HTMLDocument(implementation, frame)
129 , m_imageSizeIsKnown(false)
130 , m_didShrinkImage(false)
131 , m_shouldShrinkImage(true)
133 setParseMode(Compat);
136 Tokenizer* ImageDocument::createTokenizer()
138 return new ImageTokenizer(this);
141 void ImageDocument::createDocumentStructure()
145 RefPtr<Element> rootElement = createElementNS(xhtmlNamespaceURI, "html", ec);
146 appendChild(rootElement, ec);
148 RefPtr<Element> body = createElementNS(xhtmlNamespaceURI, "body", ec);
149 body->setAttribute(styleAttr, "margin: 0px;");
151 rootElement->appendChild(body, ec);
153 RefPtr<Element> imageElement = createElementNS(xhtmlNamespaceURI, "img", ec);
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());
160 body->appendChild(imageElement, ec);
162 if (!frame()->page()->settings()->shrinksStandaloneImagesToFit())
165 // Add event listeners
166 RefPtr<EventListener> listener = new ImageEventListener(this);
167 addWindowEventListener("resize", listener, false);
168 m_imageElement->addEventListener("click", listener.release(), false);
171 float ImageDocument::scale() const
173 IntSize imageSize = m_imageElement->cachedImage()->imageSize();
174 IntSize windowSize = IntSize(frame()->view()->width(), frame()->view()->height());
176 float widthScale = (float)windowSize.width() / imageSize.width();
177 float heightScale = (float)windowSize.height() / imageSize.height();
179 return min(widthScale, heightScale);
182 void ImageDocument::resizeImageToFit()
184 IntSize imageSize = m_imageElement->cachedImage()->imageSize();
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));
191 m_imageElement->style()->setProperty("cursor", "-webkit-zoom-in", ec);
194 void ImageDocument::imageClicked(int x, int y)
196 if (!m_imageSizeIsKnown || imageFitsInWindow())
199 m_shouldShrinkImage = !m_shouldShrinkImage;
201 if (m_shouldShrinkImage)
208 float scale = this->scale();
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);
213 frame()->view()->setContentsPos(scrollX, scrollY);
217 void ImageDocument::imageChanged()
219 ASSERT(m_imageElement);
221 if (m_imageSizeIsKnown)
224 if (m_imageElement->cachedImage()->imageSize().isEmpty())
227 m_imageSizeIsKnown = true;
229 if (!frame()->page()->settings()->shrinksStandaloneImagesToFit())
232 // Force resizing of the image
236 void ImageDocument::restoreImageSize()
238 if (!m_imageSizeIsKnown)
241 m_imageElement->setWidth(m_imageElement->cachedImage()->imageSize().width());
242 m_imageElement->setHeight(m_imageElement->cachedImage()->imageSize().height());
245 if (imageFitsInWindow())
246 m_imageElement->style()->removeProperty("cursor", ec);
248 m_imageElement->style()->setProperty("cursor", "-webkit-zoom-out", ec);
250 m_didShrinkImage = false;
253 bool ImageDocument::imageFitsInWindow() const
255 IntSize imageSize = m_imageElement->cachedImage()->imageSize();
256 IntSize windowSize = IntSize(frame()->view()->width(), frame()->view()->height());
258 return imageSize.width() <= windowSize.width() && imageSize.height() <= windowSize.height();
261 void ImageDocument::windowSizeChanged()
263 if (!m_imageSizeIsKnown)
266 bool fitsInWindow = imageFitsInWindow();
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) {
274 m_imageElement->style()->removeProperty("cursor", ec);
276 m_imageElement->style()->setProperty("cursor", "-webkit-zoom-out", ec);
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.
288 // If the image isn't resized but needs to be, then resize it.
291 m_didShrinkImage = true;
296 CachedImage* ImageDocument::cachedImage()
299 createDocumentStructure();
301 return m_imageElement->cachedImage();
304 void ImageEventListener::handleEvent(Event* event, bool isWindowEvent)
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());