~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Source/WebCore/rendering/svg/RenderSVGRoot.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
 
3
 * Copyright (C) 2004, 2005, 2007, 2008, 2009 Rob Buis <buis@kde.org>
 
4
 * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
 
5
 * Copyright (C) 2009 Google, Inc.
 
6
 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
 
7
 *
 
8
 * This library is free software; you can redistribute it and/or
 
9
 * modify it under the terms of the GNU Library General Public
 
10
 * License as published by the Free Software Foundation; either
 
11
 * version 2 of the License, or (at your option) any later version.
 
12
 *
 
13
 * This library is distributed in the hope that it will be useful,
 
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
16
 * Library General Public License for more details.
 
17
 *
 
18
 * You should have received a copy of the GNU Library General Public License
 
19
 * along with this library; see the file COPYING.LIB.  If not, write to
 
20
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 
21
 * Boston, MA 02110-1301, USA.
 
22
 */
 
23
 
 
24
#include "config.h"
 
25
 
 
26
#if ENABLE(SVG)
 
27
#include "RenderSVGRoot.h"
 
28
 
 
29
#include "Chrome.h"
 
30
#include "ChromeClient.h"
 
31
#include "Frame.h"
 
32
#include "GraphicsContext.h"
 
33
#include "HitTestResult.h"
 
34
#include "LayoutRepainter.h"
 
35
#include "Page.h"
 
36
#include "RenderPart.h"
 
37
#include "RenderSVGContainer.h"
 
38
#include "RenderSVGResource.h"
 
39
#include "RenderSVGResourceContainer.h"
 
40
#include "RenderView.h"
 
41
#include "SVGLength.h"
 
42
#include "SVGRenderingContext.h"
 
43
#include "SVGResources.h"
 
44
#include "SVGResourcesCache.h"
 
45
#include "SVGSVGElement.h"
 
46
#include "SVGStyledElement.h"
 
47
#include "SVGViewSpec.h"
 
48
#include "TransformState.h"
 
49
 
 
50
#if ENABLE(FILTERS)
 
51
#include "RenderSVGResourceFilter.h"
 
52
#endif
 
53
 
 
54
using namespace std;
 
55
 
 
56
namespace WebCore {
 
57
 
 
58
RenderSVGRoot::RenderSVGRoot(SVGStyledElement* node)
 
59
    : RenderReplaced(node)
 
60
    , m_objectBoundingBoxValid(false)
 
61
    , m_isLayoutSizeChanged(false)
 
62
    , m_needsBoundariesOrTransformUpdate(true)
 
63
    , m_hasSVGShadow(false)
 
64
{
 
65
}
 
66
 
 
67
RenderSVGRoot::~RenderSVGRoot()
 
68
{
 
69
}
 
70
 
 
71
void RenderSVGRoot::computeIntrinsicRatioInformation(FloatSize& intrinsicSize, double& intrinsicRatio, bool& isPercentageIntrinsicSize) const
 
72
{
 
73
    // Spec: http://www.w3.org/TR/SVG/coords.html#IntrinsicSizing
 
74
    // SVG needs to specify how to calculate some intrinsic sizing properties to enable inclusion within other languages.
 
75
    // The intrinsic width and height of the viewport of SVG content must be determined from the ā€˜widthā€™ and ā€˜heightā€™ attributes.
 
76
    // If either of these are not specified, a value of '100%' must be assumed. Note: the ā€˜widthā€™ and ā€˜heightā€™ attributes are not
 
77
    // the same as the CSS width and height properties. Specifically, percentage values do not provide an intrinsic width or height,
 
78
    // and do not indicate a percentage of the containing block. Rather, once the viewport is established, they indicate the portion
 
79
    // of the viewport that is actually covered by image data.
 
80
    SVGSVGElement* svg = static_cast<SVGSVGElement*>(node());
 
81
    Length intrinsicWidthAttribute = svg->intrinsicWidth(SVGSVGElement::IgnoreCSSProperties);
 
82
    Length intrinsicHeightAttribute = svg->intrinsicHeight(SVGSVGElement::IgnoreCSSProperties);
 
83
 
 
84
    // The intrinsic aspect ratio of the viewport of SVG content is necessary for example, when including SVG from an ā€˜objectā€™
 
85
    // element in HTML styled with CSS. It is possible (indeed, common) for an SVG graphic to have an intrinsic aspect ratio but
 
86
    // not to have an intrinsic width or height. The intrinsic aspect ratio must be calculated based upon the following rules:
 
87
    // - The aspect ratio is calculated by dividing a width by a height.
 
88
    // - If the ā€˜widthā€™ and ā€˜heightā€™ of the rootmost ā€˜svgā€™ element are both specified with unit identifiers (in, mm, cm, pt, pc,
 
89
    //   px, em, ex) or in user units, then the aspect ratio is calculated from the ā€˜widthā€™ and ā€˜heightā€™ attributes after
 
90
    //   resolving both values to user units.
 
91
    if (intrinsicWidthAttribute.isFixed() || intrinsicHeightAttribute.isFixed()) {
 
92
        if (intrinsicWidthAttribute.isFixed())
 
93
            intrinsicSize.setWidth(floatValueForLength(intrinsicWidthAttribute, 0));
 
94
        if (intrinsicHeightAttribute.isFixed())
 
95
            intrinsicSize.setHeight(floatValueForLength(intrinsicHeightAttribute, 0));
 
96
        if (!intrinsicSize.isEmpty())
 
97
            intrinsicRatio = intrinsicSize.width() / static_cast<double>(intrinsicSize.height());
 
98
        return;
 
99
    }
 
100
 
 
101
    // - If either/both of the ā€˜widthā€™ and ā€˜heightā€™ of the rootmost ā€˜svgā€™ element are in percentage units (or omitted), the
 
102
    //   aspect ratio is calculated from the width and height values of the ā€˜viewBoxā€™ specified for the current SVG document
 
103
    //   fragment. If the ā€˜viewBoxā€™ is not correctly specified, or set to 'none', the intrinsic aspect ratio cannot be
 
104
    //   calculated and is considered unspecified.
 
105
    intrinsicSize = svg->viewBox().size();
 
106
    if (!intrinsicSize.isEmpty()) {
 
107
        // The viewBox can only yield an intrinsic ratio, not an intrinsic size.
 
108
        intrinsicRatio = intrinsicSize.width() / static_cast<double>(intrinsicSize.height());
 
109
        intrinsicSize = FloatSize();
 
110
        return;
 
111
    }
 
112
 
 
113
    // If our intrinsic size is in percentage units, return those to the caller through the intrinsicSize. Notify the caller
 
114
    // about the special situation, by setting isPercentageIntrinsicSize=true, so it knows how to interpret the return values.
 
115
    if (intrinsicWidthAttribute.isPercent() && intrinsicHeightAttribute.isPercent()) {
 
116
        isPercentageIntrinsicSize = true;
 
117
        intrinsicSize = FloatSize(intrinsicWidthAttribute.percent(), intrinsicHeightAttribute.percent());
 
118
    }
 
119
}
 
120
 
 
121
bool RenderSVGRoot::isEmbeddedThroughSVGImage() const
 
122
{
 
123
    if (!node())
 
124
        return false;
 
125
 
 
126
    Frame* frame = node()->document()->frame();
 
127
    if (!frame)
 
128
        return false;
 
129
 
 
130
    // Test whether we're embedded through an img.
 
131
    if (!frame->page() || !frame->page()->chrome())
 
132
        return false;
 
133
 
 
134
    ChromeClient* chromeClient = frame->page()->chrome()->client();
 
135
    if (!chromeClient || !chromeClient->isSVGImageChromeClient())
 
136
        return false;
 
137
 
 
138
    return true;
 
139
}
 
140
 
 
141
bool RenderSVGRoot::isEmbeddedThroughFrameContainingSVGDocument() const
 
142
{
 
143
    if (!node())
 
144
        return false;
 
145
 
 
146
    Frame* frame = node()->document()->frame();
 
147
    if (!frame)
 
148
        return false;
 
149
 
 
150
    // If our frame has an owner renderer, we're embedded through eg. object/embed/iframe,
 
151
    // but we only negotiate if we're in an SVG document.
 
152
    if (!frame->ownerRenderer())
 
153
        return false;
 
154
    return frame->document()->isSVGDocument();
 
155
}
 
156
 
 
157
static inline LayoutUnit resolveLengthAttributeForSVG(const Length& length, float scale, float maxSize, RenderView* renderView)
 
158
{
 
159
    return static_cast<LayoutUnit>(valueForLength(length, maxSize, renderView) * (length.isFixed() ? scale : 1));
 
160
}
 
161
 
 
162
LayoutUnit RenderSVGRoot::computeReplacedLogicalWidth(bool includeMaxWidth) const
 
163
{
 
164
    SVGSVGElement* svg = static_cast<SVGSVGElement*>(node());
 
165
    ASSERT(svg);
 
166
 
 
167
    // When we're embedded through SVGImage (border-image/background-image/<html:img>/...) we're forced to resize to a specific size.
 
168
    if (!m_containerSize.isEmpty())
 
169
        return m_containerSize.width();
 
170
 
 
171
    if (style()->logicalWidth().isSpecified() || style()->logicalMaxWidth().isSpecified())
 
172
        return RenderReplaced::computeReplacedLogicalWidth(includeMaxWidth);
 
173
 
 
174
    if (svg->widthAttributeEstablishesViewport())
 
175
        return resolveLengthAttributeForSVG(svg->intrinsicWidth(SVGSVGElement::IgnoreCSSProperties), style()->effectiveZoom(), containingBlock()->availableLogicalWidth(), view());
 
176
 
 
177
    // SVG embedded through object/embed/iframe.
 
178
    if (isEmbeddedThroughFrameContainingSVGDocument())
 
179
        return document()->frame()->ownerRenderer()->availableLogicalWidth();
 
180
 
 
181
    // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG.
 
182
    return RenderReplaced::computeReplacedLogicalWidth(includeMaxWidth);
 
183
}
 
184
 
 
185
LayoutUnit RenderSVGRoot::computeReplacedLogicalHeight() const
 
186
{
 
187
    SVGSVGElement* svg = static_cast<SVGSVGElement*>(node());
 
188
    ASSERT(svg);
 
189
 
 
190
    // When we're embedded through SVGImage (border-image/background-image/<html:img>/...) we're forced to resize to a specific size.
 
191
    if (!m_containerSize.isEmpty())
 
192
        return m_containerSize.height();
 
193
 
 
194
    if (style()->logicalHeight().isSpecified() || style()->logicalMaxHeight().isSpecified())
 
195
        return RenderReplaced::computeReplacedLogicalHeight();
 
196
 
 
197
    if (svg->heightAttributeEstablishesViewport()) {
 
198
        Length height = svg->intrinsicHeight(SVGSVGElement::IgnoreCSSProperties);
 
199
        if (height.isPercent()) {
 
200
            RenderBlock* cb = containingBlock();
 
201
            ASSERT(cb);
 
202
            while (cb->isAnonymous()) {
 
203
                cb = cb->containingBlock();
 
204
                cb->addPercentHeightDescendant(const_cast<RenderSVGRoot*>(this));
 
205
            }
 
206
        } else
 
207
            RenderBlock::removePercentHeightDescendant(const_cast<RenderSVGRoot*>(this));
 
208
 
 
209
        return resolveLengthAttributeForSVG(height, style()->effectiveZoom(), containingBlock()->availableLogicalHeight(), view());
 
210
    }
 
211
 
 
212
    // SVG embedded through object/embed/iframe.
 
213
    if (isEmbeddedThroughFrameContainingSVGDocument())
 
214
        return document()->frame()->ownerRenderer()->availableLogicalHeight();
 
215
 
 
216
    // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG.
 
217
    return RenderReplaced::computeReplacedLogicalHeight();
 
218
}
 
219
 
 
220
void RenderSVGRoot::layout()
 
221
{
 
222
    StackStats::LayoutCheckPoint layoutCheckPoint;
 
223
    ASSERT(needsLayout());
 
224
 
 
225
    m_resourcesNeedingToInvalidateClients.clear();
 
226
 
 
227
    // Arbitrary affine transforms are incompatible with LayoutState.
 
228
    LayoutStateDisabler layoutStateDisabler(view());
 
229
 
 
230
    bool needsLayout = selfNeedsLayout();
 
231
    LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && needsLayout);
 
232
 
 
233
    LayoutSize oldSize = size();
 
234
    updateLogicalWidth();
 
235
    updateLogicalHeight();
 
236
    buildLocalToBorderBoxTransform();
 
237
 
 
238
    SVGSVGElement* svg = static_cast<SVGSVGElement*>(node());
 
239
    m_isLayoutSizeChanged = needsLayout || (svg->hasRelativeLengths() && oldSize != size());
 
240
    SVGRenderSupport::layoutChildren(this, needsLayout || SVGRenderSupport::filtersForceContainerLayout(this));
 
241
 
 
242
    if (!m_resourcesNeedingToInvalidateClients.isEmpty()) {
 
243
        // Invalidate resource clients, which may mark some nodes for layout.
 
244
        HashSet<RenderSVGResourceContainer*>::iterator end = m_resourcesNeedingToInvalidateClients.end();
 
245
        for (HashSet<RenderSVGResourceContainer*>::iterator it = m_resourcesNeedingToInvalidateClients.begin(); it != end; ++it)
 
246
            (*it)->removeAllClientsFromCache();
 
247
 
 
248
        m_isLayoutSizeChanged = false;
 
249
        SVGRenderSupport::layoutChildren(this, false);
 
250
    }
 
251
 
 
252
    // At this point LayoutRepainter already grabbed the old bounds,
 
253
    // recalculate them now so repaintAfterLayout() uses the new bounds.
 
254
    if (m_needsBoundariesOrTransformUpdate) {
 
255
        updateCachedBoundaries();
 
256
        m_needsBoundariesOrTransformUpdate = false;
 
257
    }
 
258
 
 
259
    repainter.repaintAfterLayout();
 
260
 
 
261
    setNeedsLayout(false);
 
262
}
 
263
 
 
264
void RenderSVGRoot::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
 
265
{
 
266
    // An empty viewport disables rendering.
 
267
    if (pixelSnappedBorderBoxRect().isEmpty())
 
268
        return;
 
269
 
 
270
    // Don't paint, if the context explicitely disabled it.
 
271
    if (paintInfo.context->paintingDisabled())
 
272
        return;
 
273
 
 
274
    Page* page = 0;
 
275
    if (Frame* frame = this->frame())
 
276
        page = frame->page();
 
277
 
 
278
    // Don't paint if we don't have kids, except if we have filters we should paint those.
 
279
    if (!firstChild()) {
 
280
        SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
 
281
        if (!resources || !resources->filter()) {
 
282
            if (page && paintInfo.phase == PaintPhaseForeground)
 
283
                page->addRelevantUnpaintedObject(this, visualOverflowRect());
 
284
            return;
 
285
        }
 
286
    }
 
287
 
 
288
    if (page && paintInfo.phase == PaintPhaseForeground)
 
289
        page->addRelevantRepaintedObject(this, visualOverflowRect());
 
290
 
 
291
    // Make a copy of the PaintInfo because applyTransform will modify the damage rect.
 
292
    PaintInfo childPaintInfo(paintInfo);
 
293
    childPaintInfo.context->save();
 
294
 
 
295
    // Apply initial viewport clip - not affected by overflow handling
 
296
    childPaintInfo.context->clip(pixelSnappedIntRect(overflowClipRect(paintOffset, paintInfo.renderRegion)));
 
297
 
 
298
    // Convert from container offsets (html renderers) to a relative transform (svg renderers).
 
299
    // Transform from our paint container's coordinate system to our local coords.
 
300
    IntPoint adjustedPaintOffset = roundedIntPoint(paintOffset);
 
301
    childPaintInfo.applyTransform(AffineTransform::translation(adjustedPaintOffset.x(), adjustedPaintOffset.y()) * localToBorderBoxTransform());
 
302
 
 
303
    // SVGRenderingContext must be destroyed before we restore the childPaintInfo.context, because a filter may have
 
304
    // changed the context and it is only reverted when the SVGRenderingContext destructor finishes applying the filter.
 
305
    {
 
306
        SVGRenderingContext renderingContext;
 
307
        bool continueRendering = true;
 
308
        if (childPaintInfo.phase == PaintPhaseForeground) {
 
309
            renderingContext.prepareToRenderSVGContent(this, childPaintInfo);
 
310
            continueRendering = renderingContext.isRenderingPrepared();
 
311
        }
 
312
 
 
313
        if (continueRendering)
 
314
            RenderBox::paint(childPaintInfo, LayoutPoint());
 
315
    }
 
316
 
 
317
    childPaintInfo.context->restore();
 
318
}
 
319
 
 
320
void RenderSVGRoot::willBeDestroyed()
 
321
{
 
322
    RenderBlock::removePercentHeightDescendant(const_cast<RenderSVGRoot*>(this));
 
323
 
 
324
    SVGResourcesCache::clientDestroyed(this);
 
325
    RenderReplaced::willBeDestroyed();
 
326
}
 
327
 
 
328
void RenderSVGRoot::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
 
329
{
 
330
    if (diff == StyleDifferenceLayout)
 
331
        setNeedsBoundariesUpdate();
 
332
    RenderReplaced::styleWillChange(diff, newStyle);
 
333
}
 
334
 
 
335
void RenderSVGRoot::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
 
336
{
 
337
    RenderReplaced::styleDidChange(diff, oldStyle);
 
338
    SVGResourcesCache::clientStyleChanged(this, diff, style());
 
339
}
 
340
 
 
341
void RenderSVGRoot::addChild(RenderObject* child, RenderObject* beforeChild)
 
342
{
 
343
    RenderReplaced::addChild(child, beforeChild);
 
344
    SVGResourcesCache::clientWasAddedToTree(child, child->style());
 
345
}
 
346
 
 
347
void RenderSVGRoot::removeChild(RenderObject* child)
 
348
{
 
349
    SVGResourcesCache::clientWillBeRemovedFromTree(child);
 
350
    RenderReplaced::removeChild(child);
 
351
}
 
352
 
 
353
// RenderBox methods will expect coordinates w/o any transforms in coordinates
 
354
// relative to our borderBox origin.  This method gives us exactly that.
 
355
void RenderSVGRoot::buildLocalToBorderBoxTransform()
 
356
{
 
357
    SVGSVGElement* svg = static_cast<SVGSVGElement*>(node());
 
358
    float scale = style()->effectiveZoom();
 
359
    FloatPoint translate = svg->currentTranslate();
 
360
    LayoutSize borderAndPadding(borderLeft() + paddingLeft(), borderTop() + paddingTop());
 
361
    m_localToBorderBoxTransform = svg->viewBoxToViewTransform(contentWidth() / scale, contentHeight() / scale);
 
362
    if (borderAndPadding.isEmpty() && scale == 1 && translate == FloatPoint::zero())
 
363
        return;
 
364
    m_localToBorderBoxTransform = AffineTransform(scale, 0, 0, scale, borderAndPadding.width() + translate.x(), borderAndPadding.height() + translate.y()) * m_localToBorderBoxTransform;
 
365
}
 
366
 
 
367
const AffineTransform& RenderSVGRoot::localToParentTransform() const
 
368
{
 
369
    // Slightly optimized version of m_localToParentTransform = AffineTransform::translation(x(), y()) * m_localToBorderBoxTransform;
 
370
    m_localToParentTransform = m_localToBorderBoxTransform;
 
371
    if (x())
 
372
        m_localToParentTransform.setE(m_localToParentTransform.e() + roundToInt(x()));
 
373
    if (y())
 
374
        m_localToParentTransform.setF(m_localToParentTransform.f() + roundToInt(y()));
 
375
    return m_localToParentTransform;
 
376
}
 
377
 
 
378
LayoutRect RenderSVGRoot::clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const
 
379
{
 
380
    return SVGRenderSupport::clippedOverflowRectForRepaint(this, repaintContainer);
 
381
}
 
382
 
 
383
void RenderSVGRoot::computeFloatRectForRepaint(const RenderLayerModelObject* repaintContainer, FloatRect& repaintRect, bool fixed) const
 
384
{
 
385
    // Apply our local transforms (except for x/y translation), then our shadow, 
 
386
    // and then call RenderBox's method to handle all the normal CSS Box model bits
 
387
    repaintRect = m_localToBorderBoxTransform.mapRect(repaintRect);
 
388
 
 
389
    const SVGRenderStyle* svgStyle = style()->svgStyle();
 
390
    if (const ShadowData* shadow = svgStyle->shadow())
 
391
        shadow->adjustRectForShadow(repaintRect);
 
392
 
 
393
    // Apply initial viewport clip - not affected by overflow settings
 
394
    repaintRect.intersect(pixelSnappedBorderBoxRect());
 
395
 
 
396
    LayoutRect rect = enclosingIntRect(repaintRect);
 
397
    RenderReplaced::computeRectForRepaint(repaintContainer, rect, fixed);
 
398
    repaintRect = rect;
 
399
}
 
400
 
 
401
// This method expects local CSS box coordinates.
 
402
// Callers with local SVG viewport coordinates should first apply the localToBorderBoxTransform
 
403
// to convert from SVG viewport coordinates to local CSS box coordinates.
 
404
void RenderSVGRoot::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed) const
 
405
{
 
406
    ASSERT(mode & ~IsFixed); // We should have no fixed content in the SVG rendering tree.
 
407
    ASSERT(mode & UseTransforms); // mapping a point through SVG w/o respecting trasnforms is useless.
 
408
 
 
409
    RenderReplaced::mapLocalToContainer(repaintContainer, transformState, mode | ApplyContainerFlip, wasFixed);
 
410
}
 
411
 
 
412
const RenderObject* RenderSVGRoot::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const
 
413
{
 
414
    return RenderReplaced::pushMappingToContainer(ancestorToStopAt, geometryMap);
 
415
}
 
416
 
 
417
void RenderSVGRoot::updateCachedBoundaries()
 
418
{
 
419
    SVGRenderSupport::computeContainerBoundingBoxes(this, m_objectBoundingBox, m_objectBoundingBoxValid, m_strokeBoundingBox, m_repaintBoundingBoxExcludingShadow);
 
420
    SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBoxExcludingShadow);
 
421
    m_repaintBoundingBoxExcludingShadow.inflate(borderAndPaddingWidth());
 
422
 
 
423
    m_repaintBoundingBox = m_repaintBoundingBoxExcludingShadow;
 
424
    SVGRenderSupport::intersectRepaintRectWithShadows(this, m_repaintBoundingBox);
 
425
}
 
426
 
 
427
bool RenderSVGRoot::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
 
428
{
 
429
    LayoutPoint pointInParent = locationInContainer.point() - toLayoutSize(accumulatedOffset);
 
430
    LayoutPoint pointInBorderBox = pointInParent - toLayoutSize(location());
 
431
 
 
432
    // Only test SVG content if the point is in our content box.
 
433
    // FIXME: This should be an intersection when rect-based hit tests are supported by nodeAtFloatPoint.
 
434
    if (contentBoxRect().contains(pointInBorderBox)) {
 
435
        FloatPoint localPoint = localToParentTransform().inverse().mapPoint(FloatPoint(pointInParent));
 
436
 
 
437
        for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
 
438
            // FIXME: nodeAtFloatPoint() doesn't handle rect-based hit tests yet.
 
439
            if (child->nodeAtFloatPoint(request, result, localPoint, hitTestAction)) {
 
440
                updateHitTestResult(result, pointInBorderBox);
 
441
                if (!result.addNodeToRectBasedTestResult(child->node(), request, locationInContainer))
 
442
                    return true;
 
443
            }
 
444
        }
 
445
    }
 
446
 
 
447
    // If we didn't early exit above, we've just hit the container <svg> element. Unlike SVG 1.1, 2nd Edition allows container elements to be hit.
 
448
    if (hitTestAction == HitTestBlockBackground && visibleToHitTesting()) {
 
449
        // Only return true here, if the last hit testing phase 'BlockBackground' is executed. If we'd return true in the 'Foreground' phase,
 
450
        // hit testing would stop immediately. For SVG only trees this doesn't matter. Though when we have a <foreignObject> subtree we need
 
451
        // to be able to detect hits on the background of a <div> element. If we'd return true here in the 'Foreground' phase, we are not able 
 
452
        // to detect these hits anymore.
 
453
        LayoutRect boundsRect(accumulatedOffset + location(), size());
 
454
        if (locationInContainer.intersects(boundsRect)) {
 
455
            updateHitTestResult(result, pointInBorderBox);
 
456
            if (!result.addNodeToRectBasedTestResult(node(), request, locationInContainer, boundsRect))
 
457
                return true;
 
458
        }
 
459
    }
 
460
 
 
461
    return false;
 
462
}
 
463
 
 
464
bool RenderSVGRoot::hasRelativeDimensions() const
 
465
{
 
466
    SVGSVGElement* svg = static_cast<SVGSVGElement*>(node());
 
467
    ASSERT(svg);
 
468
 
 
469
    return svg->intrinsicHeight(SVGSVGElement::IgnoreCSSProperties).isPercent() || svg->intrinsicWidth(SVGSVGElement::IgnoreCSSProperties).isPercent();
 
470
}
 
471
 
 
472
bool RenderSVGRoot::hasRelativeLogicalHeight() const
 
473
{
 
474
    SVGSVGElement* svg = static_cast<SVGSVGElement*>(node());
 
475
    ASSERT(svg);
 
476
 
 
477
    return svg->intrinsicHeight(SVGSVGElement::IgnoreCSSProperties).isPercent();
 
478
}
 
479
 
 
480
void RenderSVGRoot::addResourceForClientInvalidation(RenderSVGResourceContainer* resource)
 
481
{
 
482
    RenderObject* svgRoot = resource->parent();
 
483
    while (svgRoot && !svgRoot->isSVGRoot())
 
484
        svgRoot = svgRoot->parent();
 
485
    if (!svgRoot)
 
486
        return;
 
487
    static_cast<RenderSVGRoot*>(svgRoot)->m_resourcesNeedingToInvalidateClients.add(resource);
 
488
}
 
489
 
 
490
}
 
491
 
 
492
#endif // ENABLE(SVG)