#include "config.h"
#include "core/html/HTMLImageElement.h"
-#include "bindings/v8/ScriptEventListener.h"
+#include "bindings/core/v8/ScriptEventListener.h"
#include "core/CSSPropertyNames.h"
#include "core/HTMLNames.h"
#include "core/MediaTypeNames.h"
#include "core/css/MediaQueryMatcher.h"
-#include "core/css/MediaValuesCached.h"
+#include "core/css/MediaValuesDynamic.h"
#include "core/css/parser/SizesAttributeParser.h"
#include "core/dom/Attribute.h"
+#include "core/dom/NodeTraversal.h"
#include "core/fetch/ImageResource.h"
+#include "core/frame/UseCounter.h"
#include "core/html/HTMLAnchorElement.h"
#include "core/html/HTMLCanvasElement.h"
#include "core/html/HTMLFormElement.h"
#include "core/html/canvas/CanvasRenderingContext.h"
#include "core/html/parser/HTMLParserIdioms.h"
#include "core/html/parser/HTMLSrcsetParser.h"
+#include "core/inspector/ConsoleMessage.h"
#include "core/rendering/RenderImage.h"
#include "platform/MIMETypeRegistry.h"
#include "platform/RuntimeEnabledFeatures.h"
-namespace WebCore {
+namespace blink {
using namespace HTMLNames;
+class HTMLImageElement::ViewportChangeListener FINAL : public MediaQueryListListener {
+public:
+ static RefPtrWillBeRawPtr<ViewportChangeListener> create(HTMLImageElement* element)
+ {
+ return adoptRefWillBeNoop(new ViewportChangeListener(element));
+ }
+
+ virtual void call() OVERRIDE
+ {
+ if (m_element)
+ m_element->notifyViewportChanged();
+ }
+
+#if !ENABLE(OILPAN)
+ void clearElement() { m_element = nullptr; }
+#endif
+ virtual void trace(Visitor* visitor) OVERRIDE
+ {
+ visitor->trace(m_element);
+ MediaQueryListListener::trace(visitor);
+ }
+private:
+ explicit ViewportChangeListener(HTMLImageElement* element) : m_element(element) { }
+ RawPtrWillBeMember<HTMLImageElement> m_element;
+};
+
HTMLImageElement::HTMLImageElement(Document& document, HTMLFormElement* form, bool createdByParser)
: HTMLElement(imgTag, document)
, m_imageLoader(HTMLImageLoader::create(this))
, m_imageDevicePixelRatio(1.0f)
, m_formWasSetByParser(false)
, m_elementCreatedByParser(createdByParser)
+ , m_intrinsicSizingViewportDependant(false)
+ , m_effectiveSizeViewportDependant(false)
{
ScriptWrappable::init(this);
if (form && form->inDocument()) {
HTMLImageElement::~HTMLImageElement()
{
#if !ENABLE(OILPAN)
+ if (m_listener) {
+ document().mediaQueryMatcher().removeViewportListener(m_listener.get());
+ m_listener->clearElement();
+ }
if (m_form)
m_form->disassociate(*this);
#endif
void HTMLImageElement::trace(Visitor* visitor)
{
visitor->trace(m_imageLoader);
+ visitor->trace(m_listener);
visitor->trace(m_form);
HTMLElement::trace(visitor);
}
+void HTMLImageElement::notifyViewportChanged()
+{
+ // Re-selecting the source URL in order to pick a more fitting resource
+ // And update the image's intrinsic dimensions when the viewport changes.
+ // Picking of a better fitting resource is UA dependant, not spec required.
+ selectSourceURL(ImageLoader::UpdateSizeChanged);
+}
+
PassRefPtrWillBeRawPtr<HTMLImageElement> HTMLImageElement::createForJSConstructor(Document& document, int width, int height)
{
RefPtrWillBeRawPtr<HTMLImageElement> image = adoptRefWillBeNoop(new HTMLImageElement(document));
void HTMLImageElement::formRemovedFromTree(const Node& formRoot)
{
ASSERT(m_form);
- if (highestAncestorOrSelf() != formRoot)
+ if (NodeTraversal::highestAncestorOrSelf(*this) != formRoot)
resetFormOwner();
}
float candidateDensity = candidate.density();
if (candidateDensity >= 0)
m_imageDevicePixelRatio = 1.0 / candidateDensity;
+ if (candidate.resourceWidth() > 0)
+ m_intrinsicSizingViewportDependant = true;
if (renderer() && renderer()->isImage())
toRenderImage(renderer())->setImageDevicePixelRatio(m_imageDevicePixelRatio);
}
if (renderer() && renderer()->isImage())
toRenderImage(renderer())->updateAltText();
} else if (name == srcAttr || name == srcsetAttr || name == sizesAttr) {
- selectSourceURL(UpdateIgnorePreviousError);
+ selectSourceURL(ImageLoader::UpdateIgnorePreviousError);
} else if (name == usemapAttr) {
setIsLink(!value.isNull());
} else if (name == compositeAttr) {
- // FIXME: images don't support blend modes in their compositing attribute.
blink::WebBlendMode blendOp = blink::WebBlendModeNormal;
if (!parseCompositeAndBlendOperator(value, m_compositeOperator, blendOp))
m_compositeOperator = CompositeSourceOver;
+ else if (m_compositeOperator != CompositeSourceOver)
+ UseCounter::count(document(), UseCounter::HTMLImageElementComposite);
} else {
HTMLElement::parseAttribute(name, value);
}
continue;
HTMLSourceElement* source = toHTMLSourceElement(child);
+ if (!source->fastGetAttribute(srcAttr).isNull())
+ UseCounter::countDeprecation(document(), UseCounter::PictureSourceSrc);
String srcset = source->fastGetAttribute(srcsetAttr);
if (srcset.isEmpty())
continue;
if (!type.isEmpty() && !supportedImageType(type))
continue;
- String media = source->fastGetAttribute(mediaAttr);
- if (!media.isEmpty()) {
- RefPtrWillBeRawPtr<MediaQuerySet> mediaQueries = MediaQuerySet::create(media);
- if (!document().mediaQueryMatcher().evaluate(mediaQueries.get()))
- continue;
- }
+ if (!source->mediaQueryMatches())
+ continue;
- unsigned effectiveSize = SizesAttributeParser::findEffectiveSize(source->fastGetAttribute(sizesAttr), MediaValuesCached::create(document()));
+ SizesAttributeParser parser = SizesAttributeParser(MediaValuesDynamic::create(document()), source->fastGetAttribute(sizesAttr));
+ unsigned effectiveSize = parser.length();
+ m_effectiveSizeViewportDependant = parser.viewportDependant();
ImageCandidate candidate = bestFitSourceForSrcsetAttribute(document().devicePixelRatio(), effectiveSize, source->fastGetAttribute(srcsetAttr));
if (candidate.isEmpty())
continue;
Node::InsertionNotificationRequest HTMLImageElement::insertedInto(ContainerNode* insertionPoint)
{
- if (!m_formWasSetByParser || insertionPoint->highestAncestorOrSelf() != m_form->highestAncestorOrSelf())
+ if (!m_formWasSetByParser || NodeTraversal::highestAncestorOrSelf(*insertionPoint) != NodeTraversal::highestAncestorOrSelf(*m_form.get()))
resetFormOwner();
+ if (m_listener)
+ document().mediaQueryMatcher().addViewportListener(m_listener.get());
bool imageWasModified = false;
if (RuntimeEnabledFeatures::pictureEnabled()) {
// If we have been inserted from a renderer-less document,
// our loader may have not fetched the image, so do it now.
if ((insertionPoint->inDocument() && !imageLoader().image()) || imageWasModified)
- imageLoader().updateFromElement(m_elementCreatedByParser ? ImageLoader::ForceLoadImmediately : ImageLoader::LoadNormally);
+ imageLoader().updateFromElement(ImageLoader::UpdateNormal, m_elementCreatedByParser ? ImageLoader::ForceLoadImmediately : ImageLoader::LoadNormally);
return HTMLElement::insertedInto(insertionPoint);
}
void HTMLImageElement::removedFrom(ContainerNode* insertionPoint)
{
- if (!m_form || m_form->highestAncestorOrSelf() != highestAncestorOrSelf())
+ if (!m_form || NodeTraversal::highestAncestorOrSelf(*m_form.get()) != NodeTraversal::highestAncestorOrSelf(*this))
resetFormOwner();
+ if (m_listener)
+ document().mediaQueryMatcher().removeViewportListener(m_listener.get());
HTMLElement::removedFrom(insertionPoint);
}
if (!imageLoader().image())
return 0;
- return imageLoader().image()->imageSizeForRenderer(renderer(), 1.0f).width();
+ return imageLoader().image()->imageSizeForRenderer(renderer(), 1.0f, ImageResource::IntrinsicSize).width();
}
int HTMLImageElement::naturalHeight() const
if (!imageLoader().image())
return 0;
- return imageLoader().image()->imageSizeForRenderer(renderer(), 1.0f).height();
+ return imageLoader().image()->imageSizeForRenderer(renderer(), 1.0f, ImageResource::IntrinsicSize).height();
}
const String& HTMLImageElement::currentSrc() const
return srcAttr;
}
-const AtomicString& HTMLImageElement::alt() const
-{
- return fastGetAttribute(altAttr);
-}
-
bool HTMLImageElement::draggable() const
{
// Image elements are draggable by default.
int HTMLImageElement::x() const
{
+ document().updateLayoutIgnorePendingStylesheets();
RenderObject* r = renderer();
if (!r)
return 0;
int HTMLImageElement::y() const
{
+ document().updateLayoutIgnorePendingStylesheets();
RenderObject* r = renderer();
if (!r)
return 0;
sourceImage->setContainerSize(sourceImage->size());
*status = NormalSourceImageStatus;
- return sourceImage.release();
+ return sourceImage->imageForDefaultFrame();
}
bool HTMLImageElement::wouldTaintOrigin(SecurityOrigin* destinationSecurityOrigin) const
return size;
}
-void HTMLImageElement::selectSourceURL(UpdateFromElementBehavior behavior)
+void HTMLImageElement::selectSourceURL(ImageLoader::UpdateFromElementBehavior behavior)
{
bool foundURL = false;
if (RuntimeEnabledFeatures::pictureEnabled()) {
if (!foundURL) {
unsigned effectiveSize = 0;
- if (RuntimeEnabledFeatures::pictureSizesEnabled())
- effectiveSize = SizesAttributeParser::findEffectiveSize(fastGetAttribute(sizesAttr), MediaValuesCached::create(document()));
+ if (RuntimeEnabledFeatures::pictureSizesEnabled()) {
+ SizesAttributeParser parser = SizesAttributeParser(MediaValuesDynamic::create(document()), fastGetAttribute(sizesAttr));
+ effectiveSize = parser.length();
+ m_effectiveSizeViewportDependant = parser.viewportDependant();
+ }
ImageCandidate candidate = bestFitSourceForImageAttributes(document().devicePixelRatio(), effectiveSize, fastGetAttribute(srcAttr), fastGetAttribute(srcsetAttr));
setBestFitURLAndDPRFromImageCandidate(candidate);
}
- if (behavior == UpdateIgnorePreviousError)
- imageLoader().updateFromElementIgnoringPreviousError();
- else
- imageLoader().updateFromElement();
+ if (m_intrinsicSizingViewportDependant && m_effectiveSizeViewportDependant && !m_listener.get()) {
+ m_listener = ViewportChangeListener::create(this);
+ document().mediaQueryMatcher().addViewportListener(m_listener.get());
+ }
+ imageLoader().updateFromElement(behavior);
}
const KURL& HTMLImageElement::sourceURL() const