1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4 *           (C) 2000 Dirk Mueller (mueller@kde.org)
5 *           (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com)
6 *           (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
7 * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB.  If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 *
24 */
25
26#include "config.h"
27#include "RenderImage.h"
28
29#include "Frame.h"
30#include "GraphicsContext.h"
31#include "HTMLAreaElement.h"
32#include "HTMLCollection.h"
33#include "HTMLImageElement.h"
34#include "HTMLInputElement.h"
35#include "HTMLMapElement.h"
36#include "HTMLNames.h"
37#include "HitTestResult.h"
38#include "Page.h"
39#include "RenderTheme.h"
40#include "RenderView.h"
41#include "SelectionController.h"
42#include <wtf/CurrentTime.h>
43#include <wtf/UnusedParam.h>
44
45#ifdef ANDROID_LAYOUT
46#include "Settings.h"
47#endif
48
49#if ENABLE(WML)
50#include "WMLImageElement.h"
51#include "WMLNames.h"
52#endif
53
54using namespace std;
55
56namespace WebCore {
57
58static const double cInterpolationCutoff = 800. * 800.;
59static const double cLowQualityTimeThreshold = 0.050; // 50 ms
60
61class RenderImageScaleData : public Noncopyable {
62public:
63    RenderImageScaleData(RenderImage* image, const IntSize& size, double time, bool lowQualityScale)
64        : m_size(size)
65        , m_time(time)
66        , m_lowQualityScale(lowQualityScale)
67        , m_highQualityRepaintTimer(image, &RenderImage::highQualityRepaintTimerFired)
68    {
69    }
70
71    ~RenderImageScaleData()
72    {
73        m_highQualityRepaintTimer.stop();
74    }
75
76    const IntSize& size() const { return m_size; }
77    double time() const { return m_time; }
78    bool useLowQualityScale() const { return m_lowQualityScale; }
79    Timer<RenderImage>& hiqhQualityRepaintTimer() { return m_highQualityRepaintTimer; }
80
81    void setSize(const IntSize& s) { m_size = s; }
82    void setTime(double t) { m_time = t; }
83    void setUseLowQualityScale(bool b)
84    {
85        m_highQualityRepaintTimer.stop();
86        m_lowQualityScale = b;
87        if (b)
88            m_highQualityRepaintTimer.startOneShot(cLowQualityTimeThreshold);
89    }
90
91private:
92    IntSize m_size;
93    double m_time;
94    bool m_lowQualityScale;
95    Timer<RenderImage> m_highQualityRepaintTimer;
96};
97
98class RenderImageScaleObserver {
99public:
100    static bool shouldImagePaintAtLowQuality(RenderImage*, const IntSize&);
101
102    static void imageDestroyed(RenderImage* image)
103    {
104        if (gImages) {
105            RenderImageScaleData* data = gImages->take(image);
106            delete data;
107            if (gImages->size() == 0) {
108                delete gImages;
109                gImages = 0;
110            }
111        }
112    }
113
114    static void highQualityRepaintTimerFired(RenderImage* image)
115    {
116        RenderImageScaleObserver::imageDestroyed(image);
117        image->repaint();
118    }
119
120    static HashMap<RenderImage*, RenderImageScaleData*>* gImages;
121};
122
123bool RenderImageScaleObserver::shouldImagePaintAtLowQuality(RenderImage* image, const IntSize& size)
124{
125    // If the image is not a bitmap image, then none of this is relevant and we just paint at high
126    // quality.
127    if (!image->image() || !image->image()->isBitmapImage())
128        return false;
129
130    // Make sure to use the unzoomed image size, since if a full page zoom is in effect, the image
131    // is actually being scaled.
132    IntSize imageSize(image->image()->width(), image->image()->height());
133
134    // Look ourselves up in the hashtable.
135    RenderImageScaleData* data = 0;
136    if (gImages)
137        data = gImages->get(image);
138
139    if (imageSize == size) {
140        // There is no scale in effect.  If we had a scale in effect before, we can just delete this data.
141        if (data) {
142            gImages->remove(image);
143            delete data;
144        }
145        return false;
146    }
147
148    // There is no need to hash scaled images that always use low quality mode when the page demands it.  This is the iChat case.
149    if (image->document()->page()->inLowQualityImageInterpolationMode()) {
150        double totalPixels = static_cast<double>(image->image()->width()) * static_cast<double>(image->image()->height());
151        if (totalPixels > cInterpolationCutoff)
152            return true;
153    }
154
155    // If there is no data yet, we will paint the first scale at high quality and record the paint time in case a second scale happens
156    // very soon.
157    if (!data) {
158        data = new RenderImageScaleData(image, size, currentTime(), false);
159        if (!gImages)
160            gImages = new HashMap<RenderImage*, RenderImageScaleData*>;
161        gImages->set(image, data);
162        return false;
163    }
164
165    // We are scaled, but we painted already at this size, so just keep using whatever mode we last painted with.
166    if (data->size() == size)
167        return data->useLowQualityScale();
168
169    // We have data and our size just changed.  If this change happened quickly, go into low quality mode and then set a repaint
170    // timer to paint in high quality mode.  Otherwise it is ok to just paint in high quality mode.
171    double newTime = currentTime();
172    data->setUseLowQualityScale(newTime - data->time() < cLowQualityTimeThreshold);
173    data->setTime(newTime);
174    data->setSize(size);
175    return data->useLowQualityScale();
176}
177
178HashMap<RenderImage*, RenderImageScaleData*>* RenderImageScaleObserver::gImages = 0;
179
180void RenderImage::highQualityRepaintTimerFired(Timer<RenderImage>*)
181{
182    RenderImageScaleObserver::highQualityRepaintTimerFired(this);
183}
184
185using namespace HTMLNames;
186
187RenderImage::RenderImage(Node* node)
188    : RenderReplaced(node, IntSize(0, 0))
189    , m_cachedImage(0)
190{
191    updateAltText();
192
193    view()->frameView()->setIsVisuallyNonEmpty();
194}
195
196RenderImage::~RenderImage()
197{
198    if (m_cachedImage)
199        m_cachedImage->removeClient(this);
200    RenderImageScaleObserver::imageDestroyed(this);
201}
202
203void RenderImage::setCachedImage(CachedImage* newImage)
204{
205    if (m_cachedImage == newImage)
206        return;
207    if (m_cachedImage)
208        m_cachedImage->removeClient(this);
209    m_cachedImage = newImage;
210    if (m_cachedImage) {
211        m_cachedImage->addClient(this);
212        if (m_cachedImage->errorOccurred())
213            imageChanged(m_cachedImage.get());
214    }
215}
216
217// If we'll be displaying either alt text or an image, add some padding.
218static const unsigned short paddingWidth = 4;
219static const unsigned short paddingHeight = 4;
220
221// Alt text is restricted to this maximum size, in pixels.  These are
222// signed integers because they are compared with other signed values.
223static const int maxAltTextWidth = 1024;
224static const int maxAltTextHeight = 256;
225
226// Sets the image height and width to fit the alt text.  Returns true if the
227// image size changed.
228bool RenderImage::setImageSizeForAltText(CachedImage* newImage /* = 0 */)
229{
230    int imageWidth = 0;
231    int imageHeight = 0;
232
233    // If we'll be displaying either text or an image, add a little padding.
234    if (!m_altText.isEmpty() || newImage) {
235        imageWidth = paddingWidth;
236        imageHeight = paddingHeight;
237    }
238
239    if (newImage && newImage->image()) {
240        // imageSize() returns 0 for the error image.  We need the true size of the
241        // error image, so we have to get it by grabbing image() directly.
242        imageWidth += newImage->image()->width() * style()->effectiveZoom();
243        imageHeight += newImage->image()->height() * style()->effectiveZoom();
244    }
245
246    // we have an alt and the user meant it (its not a text we invented)
247    if (!m_altText.isEmpty()) {
248        const Font& font = style()->font();
249        imageWidth = max(imageWidth, min(font.width(TextRun(m_altText.characters(), m_altText.length())), maxAltTextWidth));
250        imageHeight = max(imageHeight, min(font.height(), maxAltTextHeight));
251    }
252
253    IntSize imageSize = IntSize(imageWidth, imageHeight);
254    if (imageSize == intrinsicSize())
255        return false;
256
257    setIntrinsicSize(imageSize);
258    return true;
259}
260
261void RenderImage::imageChanged(WrappedImagePtr newImage, const IntRect* rect)
262{
263    if (documentBeingDestroyed())
264        return;
265
266    if (hasBoxDecorations() || hasMask())
267        RenderReplaced::imageChanged(newImage, rect);
268
269    if (newImage != imagePtr() || !newImage)
270        return;
271
272    bool imageSizeChanged = false;
273
274    // Set image dimensions, taking into account the size of the alt text.
275    if (errorOccurred())
276        imageSizeChanged = setImageSizeForAltText(m_cachedImage.get());
277
278    bool shouldRepaint = true;
279
280    // Image dimensions have been changed, see what needs to be done
281    if (imageSize(style()->effectiveZoom()) != intrinsicSize() || imageSizeChanged) {
282        if (!errorOccurred())
283            setIntrinsicSize(imageSize(style()->effectiveZoom()));
284
285        // In the case of generated image content using :before/:after, we might not be in the
286        // render tree yet.  In that case, we don't need to worry about check for layout, since we'll get a
287        // layout when we get added in to the render tree hierarchy later.
288        if (containingBlock()) {
289            // lets see if we need to relayout at all..
290            int oldwidth = width();
291            int oldheight = height();
292            if (!prefWidthsDirty())
293                setPrefWidthsDirty(true);
294            calcWidth();
295            calcHeight();
296
297            if (imageSizeChanged || width() != oldwidth || height() != oldheight) {
298                shouldRepaint = false;
299                if (!selfNeedsLayout())
300                    setNeedsLayout(true);
301            }
302
303            setWidth(oldwidth);
304            setHeight(oldheight);
305        }
306    }
307
308    if (shouldRepaint) {
309        IntRect repaintRect;
310        if (rect) {
311            // The image changed rect is in source image coordinates (pre-zooming),
312            // so map from the bounds of the image to the contentsBox.
313            repaintRect = enclosingIntRect(mapRect(*rect, FloatRect(FloatPoint(), imageSize(1.0f)), contentBoxRect()));
314            // Guard against too-large changed rects.
315            repaintRect.intersect(contentBoxRect());
316        } else
317            repaintRect = contentBoxRect();
318
319        repaintRectangle(repaintRect);
320
321#if USE(ACCELERATED_COMPOSITING)
322        if (hasLayer()) {
323            // Tell any potential compositing layers that the image needs updating.
324            layer()->rendererContentChanged();
325        }
326#endif
327    }
328}
329
330void RenderImage::notifyFinished(CachedResource* newImage)
331{
332    if (documentBeingDestroyed())
333        return;
334
335#if USE(ACCELERATED_COMPOSITING)
336    if ((newImage == m_cachedImage) && hasLayer()) {
337        // tell any potential compositing layers
338        // that the image is done and they can reference it directly.
339        layer()->rendererContentChanged();
340    }
341#else
342    UNUSED_PARAM(newImage);
343#endif
344}
345
346void RenderImage::resetAnimation()
347{
348    if (m_cachedImage) {
349        image()->resetAnimation();
350        if (!needsLayout())
351            repaint();
352    }
353}
354
355void RenderImage::paintReplaced(PaintInfo& paintInfo, int tx, int ty)
356{
357    int cWidth = contentWidth();
358    int cHeight = contentHeight();
359    int leftBorder = borderLeft();
360    int topBorder = borderTop();
361    int leftPad = paddingLeft();
362    int topPad = paddingTop();
363
364    if (document()->printing() && !view()->printImages())
365        return;
366
367    GraphicsContext* context = paintInfo.context;
368
369    if (!hasImage() || errorOccurred()) {
370        if (paintInfo.phase == PaintPhaseSelection)
371            return;
372
373        if (cWidth > 2 && cHeight > 2) {
374            // Draw an outline rect where the image should be.
375#ifdef ANDROID_FIX // see http://b/issue?id=2052757
376            context->save();
377#endif
378            context->setStrokeStyle(SolidStroke);
379            context->setStrokeColor(Color::lightGray, style()->colorSpace());
380            context->setFillColor(Color::transparent, style()->colorSpace());
381            context->drawRect(IntRect(tx + leftBorder + leftPad, ty + topBorder + topPad, cWidth, cHeight));
382#ifdef ANDROID_FIX // see http://b/issue?id=2052757
383            context->restore();
384#endif
385
386            bool errorPictureDrawn = false;
387            int imageX = 0;
388            int imageY = 0;
389            // When calculating the usable dimensions, exclude the pixels of
390            // the ouline rect so the error image/alt text doesn't draw on it.
391            int usableWidth = cWidth - 2;
392            int usableHeight = cHeight - 2;
393
394            if (errorOccurred() && !image()->isNull() && (usableWidth >= image()->width()) && (usableHeight >= image()->height())) {
395                // Center the error image, accounting for border and padding.
396                int centerX = (usableWidth - image()->width()) / 2;
397                if (centerX < 0)
398                    centerX = 0;
399                int centerY = (usableHeight - image()->height()) / 2;
400                if (centerY < 0)
401                    centerY = 0;
402                imageX = leftBorder + leftPad + centerX + 1;
403                imageY = topBorder + topPad + centerY + 1;
404                context->drawImage(image(), style()->colorSpace(), IntPoint(tx + imageX, ty + imageY));
405                errorPictureDrawn = true;
406            }
407
408            if (!m_altText.isEmpty()) {
409                String text = document()->displayStringModifiedByEncoding(m_altText);
410                context->setFillColor(style()->color(), style()->colorSpace());
411                int ax = tx + leftBorder + leftPad;
412                int ay = ty + topBorder + topPad;
413                const Font& font = style()->font();
414                int ascent = font.ascent();
415
416                // Only draw the alt text if it'll fit within the content box,
417                // and only if it fits above the error image.
418                TextRun textRun(text.characters(), text.length());
419                int textWidth = font.width(textRun);
420                if (errorPictureDrawn) {
421                    if (usableWidth >= textWidth && font.height() <= imageY)
422                        context->drawText(style()->font(), textRun, IntPoint(ax, ay + ascent));
423                } else if (usableWidth >= textWidth && cHeight >= font.height())
424                    context->drawText(style()->font(), textRun, IntPoint(ax, ay + ascent));
425            }
426        }
427    } else if (hasImage() && cWidth > 0 && cHeight > 0) {
428        Image* img = image(cWidth, cHeight);
429        if (!img || img->isNull())
430            return;
431
432#if PLATFORM(MAC)
433        if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
434            paintCustomHighlight(tx - x(), ty - y(), style()->highlight(), true);
435#endif
436
437        IntSize contentSize(cWidth, cHeight);
438        IntRect rect(IntPoint(tx + leftBorder + leftPad, ty + topBorder + topPad), contentSize);
439        paintIntoRect(context, rect);
440    }
441}
442
443void RenderImage::paint(PaintInfo& paintInfo, int tx, int ty)
444{
445    RenderReplaced::paint(paintInfo, tx, ty);
446
447    if (paintInfo.phase == PaintPhaseOutline)
448        paintFocusRings(paintInfo, style());
449}
450
451void RenderImage::paintFocusRings(PaintInfo& paintInfo, const RenderStyle* style)
452{
453    // Don't draw focus rings if printing.
454    if (document()->printing() || !document()->frame()->selection()->isFocusedAndActive())
455        return;
456
457    if (paintInfo.context->paintingDisabled() && !paintInfo.context->updatingControlTints())
458        return;
459
460    HTMLMapElement* mapElement = imageMap();
461    if (!mapElement)
462        return;
463
464    Document* document = mapElement->document();
465    if (!document)
466        return;
467
468    Node* focusedNode = document->focusedNode();
469    if (!focusedNode)
470        return;
471
472    RefPtr<HTMLCollection> areas = mapElement->areas();
473    unsigned numAreas = areas->length();
474
475    // FIXME: Clip the paths to the image bounding box.
476    for (unsigned k = 0; k < numAreas; ++k) {
477        HTMLAreaElement* areaElement = static_cast<HTMLAreaElement*>(areas->item(k));
478        if (focusedNode != areaElement)
479            continue;
480
481        Vector<Path> focusRingPaths;
482        focusRingPaths.append(areaElement->getPath(this));
483        paintInfo.context->drawFocusRing(focusRingPaths, style->outlineWidth(), style->outlineOffset(), style->outlineColor());
484        break;
485    }
486}
487
488void RenderImage::paintIntoRect(GraphicsContext* context, const IntRect& rect)
489{
490    if (!hasImage() || errorOccurred() || rect.width() <= 0 || rect.height() <= 0)
491        return;
492
493    Image* img = image(rect.width(), rect.height());
494    if (!img || img->isNull())
495        return;
496
497    HTMLImageElement* imageElt = (node() && node()->hasTagName(imgTag)) ? static_cast<HTMLImageElement*>(node()) : 0;
498    CompositeOperator compositeOperator = imageElt ? imageElt->compositeOperator() : CompositeSourceOver;
499    bool useLowQualityScaling = RenderImageScaleObserver::shouldImagePaintAtLowQuality(this, rect.size());
500    context->drawImage(image(rect.width(), rect.height()), style()->colorSpace(), rect, compositeOperator, useLowQualityScaling);
501}
502
503int RenderImage::minimumReplacedHeight() const
504{
505    return errorOccurred() ? intrinsicSize().height() : 0;
506}
507
508HTMLMapElement* RenderImage::imageMap() const
509{
510    HTMLImageElement* i = node() && node()->hasTagName(imgTag) ? static_cast<HTMLImageElement*>(node()) : 0;
511    return i ? i->document()->getImageMap(i->getAttribute(usemapAttr)) : 0;
512}
513
514bool RenderImage::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction)
515{
516    HitTestResult tempResult(result.point());
517    bool inside = RenderReplaced::nodeAtPoint(request, tempResult, x, y, tx, ty, hitTestAction);
518
519    if (inside && node()) {
520        if (HTMLMapElement* map = imageMap()) {
521            IntRect contentBox = contentBoxRect();
522            float zoom = style()->effectiveZoom();
523            int mapX = lroundf((x - tx - this->x() - contentBox.x()) / zoom);
524            int mapY = lroundf((y - ty - this->y() - contentBox.y()) / zoom);
525            if (map->mapMouseEvent(mapX, mapY, contentBox.size(), tempResult))
526                tempResult.setInnerNonSharedNode(node());
527        }
528    }
529
530    if (inside)
531        result = tempResult;
532    return inside;
533}
534
535void RenderImage::updateAltText()
536{
537    if (!node())
538        return;
539
540    if (node()->hasTagName(inputTag))
541        m_altText = static_cast<HTMLInputElement*>(node())->altText();
542    else if (node()->hasTagName(imgTag))
543        m_altText = static_cast<HTMLImageElement*>(node())->altText();
544#if ENABLE(WML)
545    else if (node()->hasTagName(WMLNames::imgTag))
546        m_altText = static_cast<WMLImageElement*>(node())->altText();
547#endif
548}
549
550bool RenderImage::isWidthSpecified() const
551{
552    switch (style()->width().type()) {
553        case Fixed:
554        case Percent:
555            return true;
556        case Auto:
557        case Relative: // FIXME: Shouldn't this case return true?
558        case Static:
559        case Intrinsic:
560        case MinIntrinsic:
561            return false;
562    }
563    ASSERT(false);
564    return false;
565}
566
567bool RenderImage::isHeightSpecified() const
568{
569    switch (style()->height().type()) {
570        case Fixed:
571        case Percent:
572            return true;
573        case Auto:
574        case Relative: // FIXME: Shouldn't this case return true?
575        case Static:
576        case Intrinsic:
577        case MinIntrinsic:
578            return false;
579    }
580    ASSERT(false);
581    return false;
582}
583
584int RenderImage::calcReplacedWidth(bool includeMaxWidth) const
585{
586    if (imageHasRelativeWidth())
587        if (RenderObject* cb = isPositioned() ? container() : containingBlock()) {
588            if (cb->isBox())
589                setImageContainerSize(IntSize(toRenderBox(cb)->availableWidth(), toRenderBox(cb)->availableHeight()));
590        }
591
592    int width;
593    if (isWidthSpecified())
594        width = calcReplacedWidthUsing(style()->width());
595    else if (usesImageContainerSize())
596        width = imageSize(style()->effectiveZoom()).width();
597    else if (imageHasRelativeWidth())
598        width = 0; // If the image is relatively-sized, set the width to 0 until there is a set container size.
599    else
600        width = calcAspectRatioWidth();
601
602    int minW = calcReplacedWidthUsing(style()->minWidth());
603    int maxW = !includeMaxWidth || style()->maxWidth().isUndefined() ? width : calcReplacedWidthUsing(style()->maxWidth());
604
605#ifdef ANDROID_LAYOUT
606    width = max(minW, min(width, maxW));
607    // in SSR mode, we will fit the image to its container width
608    if (document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) {
609        int cw = containingBlockWidthForContent();
610        if (cw && width>cw)
611            width = cw;
612    }
613    return width;
614#else
615    return max(minW, min(width, maxW));
616#endif
617}
618
619int RenderImage::calcReplacedHeight() const
620{
621    int height;
622    if (isHeightSpecified())
623        height = calcReplacedHeightUsing(style()->height());
624    else if (usesImageContainerSize())
625        height = imageSize(style()->effectiveZoom()).height();
626    else if (imageHasRelativeHeight())
627        height = 0; // If the image is relatively-sized, set the height to 0 until there is a set container size.
628    else
629        height = calcAspectRatioHeight();
630
631    int minH = calcReplacedHeightUsing(style()->minHeight());
632    int maxH = style()->maxHeight().isUndefined() ? height : calcReplacedHeightUsing(style()->maxHeight());
633
634#ifdef ANDROID_LAYOUT
635    height = max(minH, min(height, maxH));
636    // in SSR mode, we will fit the image to its container width
637    if (height && document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) {
638        int width;
639        if (isWidthSpecified())
640            width = calcReplacedWidthUsing(style()->width());
641        else
642            width = calcAspectRatioWidth();
643        int minW = calcReplacedWidthUsing(style()->minWidth());
644        int maxW = style()->maxWidth().value() == undefinedLength ? width :
645            calcReplacedWidthUsing(style()->maxWidth());
646        width = max(minW, min(width, maxW));
647
648        int cw = containingBlockWidthForContent();
649        if (cw && width && width > cw)
650            height = cw * height / width;   // preserve aspect ratio
651    }
652    return height;
653#else
654    return max(minH, min(height, maxH));
655#endif
656}
657
658int RenderImage::calcAspectRatioWidth() const
659{
660    IntSize size = intrinsicSize();
661    if (!size.height())
662        return 0;
663    if (!hasImage() || errorOccurred())
664        return size.width(); // Don't bother scaling.
665    return RenderReplaced::calcReplacedHeight() * size.width() / size.height();
666}
667
668int RenderImage::calcAspectRatioHeight() const
669{
670    IntSize size = intrinsicSize();
671    if (!size.width())
672        return 0;
673    if (!hasImage() || errorOccurred())
674        return size.height(); // Don't bother scaling.
675    return RenderReplaced::calcReplacedWidth() * size.height() / size.width();
676}
677
678void RenderImage::calcPrefWidths()
679{
680    ASSERT(prefWidthsDirty());
681
682    int paddingAndBorders = paddingLeft() + paddingRight() + borderLeft() + borderRight();
683    m_maxPrefWidth = calcReplacedWidth(false) + paddingAndBorders;
684
685    if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength)
686        m_maxPrefWidth = min(m_maxPrefWidth, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? paddingAndBorders : 0));
687
688    if (style()->width().isPercent() || style()->height().isPercent() ||
689        style()->maxWidth().isPercent() || style()->maxHeight().isPercent() ||
690        style()->minWidth().isPercent() || style()->minHeight().isPercent())
691        m_minPrefWidth = 0;
692    else
693        m_minPrefWidth = m_maxPrefWidth;
694
695    setPrefWidthsDirty(false);
696}
697
698Image* RenderImage::nullImage()
699{
700    return Image::nullImage();
701}
702
703} // namespace WebCore
704