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(shouldShrinkToFit())
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 (shouldShrinkToFit()) {
163 // Add event listeners
164 RefPtr<EventListener> listener = new ImageEventListener(this);
165 addWindowEventListener("resize", listener, false);
166 m_imageElement->addEventListener("click", listener.release(), false);
170 float ImageDocument::scale() const
172 IntSize imageSize = m_imageElement->cachedImage()->imageSize();
173 IntSize windowSize = IntSize(frame()->view()->width(), frame()->view()->height());
175 float widthScale = (float)windowSize.width() / imageSize.width();
176 float heightScale = (float)windowSize.height() / imageSize.height();
178 return min(widthScale, heightScale);
181 void ImageDocument::resizeImageToFit()
183 IntSize imageSize = m_imageElement->cachedImage()->imageSize();
185 float scale = this->scale();
186 m_imageElement->setWidth(static_cast<int>(imageSize.width() * scale));
187 m_imageElement->setHeight(static_cast<int>(imageSize.height() * scale));
190 m_imageElement->style()->setProperty("cursor", "-webkit-zoom-in", ec);
193 void ImageDocument::imageClicked(int x, int y)
195 if (!m_imageSizeIsKnown || imageFitsInWindow())
198 m_shouldShrinkImage = !m_shouldShrinkImage;
200 if (m_shouldShrinkImage)
207 float scale = this->scale();
209 int scrollX = static_cast<int>(x / scale - (float)frame()->view()->width() / 2);
210 int scrollY = static_cast<int>(y / scale - (float)frame()->view()->height() / 2);
212 frame()->view()->setContentsPos(scrollX, scrollY);
216 void ImageDocument::imageChanged()
218 ASSERT(m_imageElement);
220 if (m_imageSizeIsKnown)
223 if (m_imageElement->cachedImage()->imageSize().isEmpty())
226 m_imageSizeIsKnown = true;
228 if (shouldShrinkToFit()) {
229 // Force resizing of the image
234 void ImageDocument::restoreImageSize()
236 if (!m_imageSizeIsKnown)
239 m_imageElement->setWidth(m_imageElement->cachedImage()->imageSize().width());
240 m_imageElement->setHeight(m_imageElement->cachedImage()->imageSize().height());
243 if (imageFitsInWindow())
244 m_imageElement->style()->removeProperty("cursor", ec);
246 m_imageElement->style()->setProperty("cursor", "-webkit-zoom-out", ec);
248 m_didShrinkImage = false;
251 bool ImageDocument::imageFitsInWindow() const
253 IntSize imageSize = m_imageElement->cachedImage()->imageSize();
254 IntSize windowSize = IntSize(frame()->view()->width(), frame()->view()->height());
256 return imageSize.width() <= windowSize.width() && imageSize.height() <= windowSize.height();
259 void ImageDocument::windowSizeChanged()
261 if (!m_imageSizeIsKnown)
264 bool fitsInWindow = imageFitsInWindow();
266 // If the image has been explicitly zoomed in, restore the cursor if the image fits
267 // and set it to a zoom out cursor if the image doesn't fit
268 if (!m_shouldShrinkImage) {
272 m_imageElement->style()->removeProperty("cursor", ec);
274 m_imageElement->style()->setProperty("cursor", "-webkit-zoom-out", ec);
278 if (m_didShrinkImage) {
279 // If the window has been resized so that the image fits, restore the image size
280 // otherwise update the restored image size.
286 // If the image isn't resized but needs to be, then resize it.
289 m_didShrinkImage = true;
294 CachedImage* ImageDocument::cachedImage()
297 createDocumentStructure();
299 return m_imageElement->cachedImage();
302 bool ImageDocument::shouldShrinkToFit() const
304 return frame()->page()->settings()->shrinksStandaloneImagesToFit() &&
305 frame()->page()->mainFrame() == frame();
308 void ImageEventListener::handleEvent(Event* event, bool isWindowEvent)
310 if (event->type() == resizeEvent)
311 m_doc->windowSizeChanged();
312 else if (event->type() == clickEvent) {
313 MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
314 m_doc->imageClicked(mouseEvent->x(), mouseEvent->y());