Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright (C) 2006 Eric Seidel <eric (at) webkit.org>
      3  * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
     15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
     18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #include "config.h"
     28 #if ENABLE(SVG)
     29 #include "SVGImage.h"
     30 
     31 #include "CachedPage.h"
     32 #include "DocumentLoader.h"
     33 #include "FileChooser.h"
     34 #include "FloatRect.h"
     35 #include "Frame.h"
     36 #include "FrameLoader.h"
     37 #include "FrameView.h"
     38 #include "GraphicsContext.h"
     39 #include "HTMLFormElement.h"
     40 #include "ImageBuffer.h"
     41 #include "ImageObserver.h"
     42 #include "Page.h"
     43 #include "RenderView.h"
     44 #include "ResourceError.h"
     45 #include "SVGDocument.h"
     46 #include "SVGLength.h"
     47 #include "SVGRenderSupport.h"
     48 #include "SVGSVGElement.h"
     49 #include "Settings.h"
     50 
     51 // Moving this #include above FrameLoader.h causes the Windows build to fail due to warnings about
     52 // alignment in Timer<FrameLoader>. It seems that the definition of EmptyFrameLoaderClient is what
     53 // causes this (removing that definition fixes the warnings), but it isn't clear why.
     54 #include "EmptyClients.h" // NOLINT
     55 
     56 namespace WebCore {
     57 
     58 class SVGImageChromeClient : public EmptyChromeClient {
     59     WTF_MAKE_NONCOPYABLE(SVGImageChromeClient); WTF_MAKE_FAST_ALLOCATED;
     60 public:
     61     SVGImageChromeClient(SVGImage* image)
     62         : m_image(image)
     63     {
     64     }
     65 
     66     SVGImage* image() const { return m_image; }
     67 
     68 private:
     69     virtual void chromeDestroyed()
     70     {
     71         m_image = 0;
     72     }
     73 
     74     virtual void invalidateContentsAndWindow(const IntRect& r, bool)
     75     {
     76         if (m_image && m_image->imageObserver())
     77             m_image->imageObserver()->changedInRect(m_image, r);
     78     }
     79 
     80     SVGImage* m_image;
     81 };
     82 
     83 SVGImage::SVGImage(ImageObserver* observer)
     84     : Image(observer)
     85 {
     86 }
     87 
     88 SVGImage::~SVGImage()
     89 {
     90     if (m_page) {
     91         m_page->mainFrame()->loader()->frameDetached(); // Break both the loader and view references to the frame
     92 
     93         // Clear explicitly because we want to delete the page before the ChromeClient.
     94         // FIXME: I believe that's already guaranteed by C++ object destruction rules,
     95         // so this may matter only for the assertion below.
     96         m_page.clear();
     97     }
     98 
     99     // Verify that page teardown destroyed the Chrome
    100     ASSERT(!m_chromeClient || !m_chromeClient->image());
    101 }
    102 
    103 void SVGImage::setContainerSize(const IntSize& containerSize)
    104 {
    105     if (containerSize.isEmpty())
    106         return;
    107 
    108     if (!m_page)
    109         return;
    110     Frame* frame = m_page->mainFrame();
    111     SVGSVGElement* rootElement = static_cast<SVGDocument*>(frame->document())->rootElement();
    112     if (!rootElement)
    113         return;
    114 
    115     rootElement->setContainerSize(containerSize);
    116 }
    117 
    118 bool SVGImage::usesContainerSize() const
    119 {
    120     if (!m_page)
    121         return false;
    122     Frame* frame = m_page->mainFrame();
    123     SVGSVGElement* rootElement = static_cast<SVGDocument*>(frame->document())->rootElement();
    124     if (!rootElement)
    125         return false;
    126 
    127     return rootElement->hasSetContainerSize();
    128 }
    129 
    130 IntSize SVGImage::size() const
    131 {
    132     if (!m_page)
    133         return IntSize();
    134     Frame* frame = m_page->mainFrame();
    135     SVGSVGElement* rootElement = static_cast<SVGDocument*>(frame->document())->rootElement();
    136     if (!rootElement)
    137         return IntSize();
    138 
    139     SVGLength width = rootElement->width();
    140     SVGLength height = rootElement->height();
    141 
    142     IntSize svgSize;
    143     if (width.unitType() == LengthTypePercentage)
    144         svgSize.setWidth(rootElement->relativeWidthValue());
    145     else
    146         svgSize.setWidth(static_cast<int>(width.value(rootElement)));
    147 
    148     if (height.unitType() == LengthTypePercentage)
    149         svgSize.setHeight(rootElement->relativeHeightValue());
    150     else
    151         svgSize.setHeight(static_cast<int>(height.value(rootElement)));
    152 
    153     return svgSize;
    154 }
    155 
    156 bool SVGImage::hasRelativeWidth() const
    157 {
    158     if (!m_page)
    159         return false;
    160     SVGSVGElement* rootElement = static_cast<SVGDocument*>(m_page->mainFrame()->document())->rootElement();
    161     if (!rootElement)
    162         return false;
    163 
    164     return rootElement->width().unitType() == LengthTypePercentage;
    165 }
    166 
    167 bool SVGImage::hasRelativeHeight() const
    168 {
    169     if (!m_page)
    170         return false;
    171     SVGSVGElement* rootElement = static_cast<SVGDocument*>(m_page->mainFrame()->document())->rootElement();
    172     if (!rootElement)
    173         return false;
    174 
    175     return rootElement->height().unitType() == LengthTypePercentage;
    176 }
    177 
    178 void SVGImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace, CompositeOperator compositeOp)
    179 {
    180     if (!m_page)
    181         return;
    182 
    183     FrameView* view = m_page->mainFrame()->view();
    184 
    185     context->save();
    186     context->setCompositeOperation(compositeOp);
    187     context->clip(enclosingIntRect(dstRect));
    188     if (compositeOp != CompositeSourceOver)
    189         context->beginTransparencyLayer(1);
    190 
    191     FloatSize scale(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height());
    192 
    193     // We can only draw the entire frame, clipped to the rect we want. So compute where the top left
    194     // of the image would be if we were drawing without clipping, and translate accordingly.
    195     FloatSize topLeftOffset(srcRect.location().x() * scale.width(), srcRect.location().y() * scale.height());
    196     FloatPoint destOffset = dstRect.location() - topLeftOffset;
    197 
    198     context->translate(destOffset.x(), destOffset.y());
    199     context->scale(scale);
    200 
    201     view->resize(size());
    202 
    203     if (view->needsLayout())
    204         view->layout();
    205 
    206     view->paint(context, IntRect(0, 0, view->width(), view->height()));
    207 
    208     if (compositeOp != CompositeSourceOver)
    209         context->endTransparencyLayer();
    210 
    211     context->restore();
    212 
    213     if (imageObserver())
    214         imageObserver()->didDraw(this);
    215 }
    216 
    217 NativeImagePtr SVGImage::nativeImageForCurrentFrame()
    218 {
    219     // FIXME: In order to support dynamic SVGs we need to have a way to invalidate this
    220     // frame cache, or better yet, not use a cache for tiled drawing at all, instead
    221     // having a tiled drawing callback (hopefully non-virtual).
    222     if (!m_frameCache) {
    223         if (!m_page)
    224             return 0;
    225         OwnPtr<ImageBuffer> buffer = ImageBuffer::create(size());
    226         if (!buffer) // failed to allocate image
    227             return 0;
    228         draw(buffer->context(), rect(), rect(), ColorSpaceDeviceRGB, CompositeSourceOver);
    229         m_frameCache = buffer->copyImage();
    230     }
    231     return m_frameCache->nativeImageForCurrentFrame();
    232 }
    233 
    234 bool SVGImage::dataChanged(bool allDataReceived)
    235 {
    236     // Don't do anything if is an empty image.
    237     if (!data()->size())
    238         return true;
    239 
    240     if (allDataReceived) {
    241         static FrameLoaderClient* dummyFrameLoaderClient =  new EmptyFrameLoaderClient;
    242 
    243         Page::PageClients pageClients;
    244         m_chromeClient = adoptPtr(new SVGImageChromeClient(this));
    245         pageClients.chromeClient = m_chromeClient.get();
    246 #if ENABLE(CONTEXT_MENUS)
    247         static ContextMenuClient* dummyContextMenuClient = new EmptyContextMenuClient;
    248         pageClients.contextMenuClient = dummyContextMenuClient;
    249 #endif
    250         static EditorClient* dummyEditorClient = new EmptyEditorClient;
    251         pageClients.editorClient = dummyEditorClient;
    252 #if ENABLE(DRAG_SUPPORT)
    253         static DragClient* dummyDragClient = new EmptyDragClient;
    254         pageClients.dragClient = dummyDragClient;
    255 #endif
    256         static InspectorClient* dummyInspectorClient = new EmptyInspectorClient;
    257         pageClients.inspectorClient = dummyInspectorClient;
    258 #if ENABLE(DEVICE_ORIENTATION)
    259         static DeviceMotionClient* dummyDeviceMotionClient = new EmptyDeviceMotionClient;
    260         pageClients.deviceMotionClient = dummyDeviceMotionClient;
    261         static DeviceOrientationClient* dummyDeviceOrientationClient = new EmptyDeviceOrientationClient;
    262         pageClients.deviceOrientationClient = dummyDeviceOrientationClient;
    263 #endif
    264 
    265         // FIXME: If this SVG ends up loading itself, we might leak the world.
    266         // The Cache code does not know about CachedImages holding Frames and
    267         // won't know to break the cycle.
    268         // This will become an issue when SVGImage will be able to load other
    269         // SVGImage objects, but we're safe now, because SVGImage can only be
    270         // loaded by a top-level document.
    271         m_page.set(new Page(pageClients));
    272         m_page->settings()->setMediaEnabled(false);
    273         m_page->settings()->setJavaScriptEnabled(false);
    274         m_page->settings()->setPluginsEnabled(false);
    275 
    276         RefPtr<Frame> frame = Frame::create(m_page.get(), 0, dummyFrameLoaderClient);
    277         frame->setView(FrameView::create(frame.get()));
    278         frame->init();
    279         FrameLoader* loader = frame->loader();
    280         loader->setForcedSandboxFlags(SandboxAll);
    281         ASSERT(loader->activeDocumentLoader()); // DocumentLoader should have been created by frame->init().
    282         loader->activeDocumentLoader()->writer()->setMIMEType("image/svg+xml");
    283         loader->activeDocumentLoader()->writer()->begin(KURL()); // create the empty document
    284         loader->activeDocumentLoader()->writer()->addData(data()->data(), data()->size());
    285         loader->activeDocumentLoader()->writer()->end();
    286         frame->view()->setTransparent(true); // SVG Images are transparent.
    287     }
    288 
    289     return m_page;
    290 }
    291 
    292 String SVGImage::filenameExtension() const
    293 {
    294     return "svg";
    295 }
    296 
    297 }
    298 
    299 #endif // ENABLE(SVG)
    300