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