1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 #include "core/html/HTMLImageFallbackHelper.h"
8 #include "core/HTMLNames.h"
9 #include "core/InputTypeNames.h"
10 #include "core/dom/ElementRareData.h"
11 #include "core/dom/Text.h"
12 #include "core/dom/shadow/ShadowRoot.h"
13 #include "core/fetch/ImageResource.h"
14 #include "core/html/HTMLDivElement.h"
15 #include "core/html/HTMLElement.h"
16 #include "core/html/HTMLImageElement.h"
17 #include "core/html/HTMLImageLoader.h"
18 #include "core/html/HTMLInputElement.h"
19 #include "core/html/HTMLStyleElement.h"
20 #include "wtf/PassOwnPtr.h"
21 #include "wtf/text/StringBuilder.h"
25 using namespace HTMLNames
;
27 static bool noImageSourceSpecified(const Element
& element
)
29 bool noSrcSpecified
= !element
.hasAttribute(srcAttr
) || element
.getAttribute(srcAttr
).isNull() || element
.getAttribute(srcAttr
).isEmpty();
30 bool noSrcsetSpecified
= !element
.hasAttribute(srcsetAttr
) || element
.getAttribute(srcsetAttr
).isNull() || element
.getAttribute(srcsetAttr
).isEmpty();
31 return noSrcSpecified
&& noSrcsetSpecified
;
34 void HTMLImageFallbackHelper::createAltTextShadowTree(Element
& element
)
36 ShadowRoot
& root
= element
.ensureUserAgentShadowRoot();
38 RefPtrWillBeRawPtr
<HTMLDivElement
> container
= HTMLDivElement::create(element
.document());
39 root
.appendChild(container
);
40 container
->setAttribute(idAttr
, AtomicString("alttext-container", AtomicString::ConstructFromLiteral
));
41 container
->setInlineStyleProperty(CSSPropertyOverflow
, CSSValueHidden
);
42 container
->setInlineStyleProperty(CSSPropertyBorderWidth
, 1, CSSPrimitiveValue::UnitType::Pixels
);
43 container
->setInlineStyleProperty(CSSPropertyBorderStyle
, CSSValueSolid
);
44 container
->setInlineStyleProperty(CSSPropertyBorderColor
, CSSValueSilver
);
45 container
->setInlineStyleProperty(CSSPropertyDisplay
, CSSValueInlineBlock
);
46 container
->setInlineStyleProperty(CSSPropertyBoxSizing
, CSSValueBorderBox
);
47 container
->setInlineStyleProperty(CSSPropertyPadding
, 1, CSSPrimitiveValue::UnitType::Pixels
);
49 RefPtrWillBeRawPtr
<HTMLImageElement
> brokenImage
= HTMLImageElement::create(element
.document());
50 container
->appendChild(brokenImage
);
51 brokenImage
->setIsFallbackImage();
52 brokenImage
->setAttribute(idAttr
, AtomicString("alttext-image", AtomicString::ConstructFromLiteral
));
53 brokenImage
->setAttribute(widthAttr
, AtomicString("16", AtomicString::ConstructFromLiteral
));
54 brokenImage
->setAttribute(heightAttr
, AtomicString("16", AtomicString::ConstructFromLiteral
));
55 brokenImage
->setAttribute(alignAttr
, AtomicString("left", AtomicString::ConstructFromLiteral
));
56 brokenImage
->setInlineStyleProperty(CSSPropertyMargin
, 0, CSSPrimitiveValue::UnitType::Pixels
);
58 RefPtrWillBeRawPtr
<HTMLDivElement
> altText
= HTMLDivElement::create(element
.document());
59 container
->appendChild(altText
);
60 altText
->setAttribute(idAttr
, AtomicString("alttext", AtomicString::ConstructFromLiteral
));
61 altText
->setInlineStyleProperty(CSSPropertyOverflow
, CSSValueHidden
);
62 altText
->setInlineStyleProperty(CSSPropertyDisplay
, CSSValueBlock
);
64 RefPtrWillBeRawPtr
<Text
> text
= Text::create(element
.document(), toHTMLElement(element
).altText());
65 altText
->appendChild(text
);
68 PassRefPtr
<ComputedStyle
> HTMLImageFallbackHelper::customStyleForAltText(Element
& element
, PassRefPtr
<ComputedStyle
> newStyle
)
70 // If we have an author shadow root or have not created the UA shadow root yet, bail early. We can't
71 // use ensureUserAgentShadowRoot() here because that would alter the DOM tree during style recalc.
72 if (element
.authorShadowRoot() || !element
.userAgentShadowRoot())
75 Element
* placeHolder
= element
.userAgentShadowRoot()->getElementById("alttext-container");
76 Element
* brokenImage
= element
.userAgentShadowRoot()->getElementById("alttext-image");
77 // Input elements have a UA shadow root of their own. We may not have replaced it with fallback content yet.
78 if (!placeHolder
|| !brokenImage
)
82 if (element
.document().inQuirksMode()) {
83 // Mimic the behaviour of the image host by setting symmetric dimensions if only one dimension is specified.
84 if (newStyle
->width().isSpecifiedOrIntrinsic() && newStyle
->height().isAuto())
85 newStyle
->setHeight(newStyle
->width());
86 else if (newStyle
->height().isSpecifiedOrIntrinsic() && newStyle
->width().isAuto())
87 newStyle
->setWidth(newStyle
->height());
88 if (newStyle
->width().isSpecifiedOrIntrinsic() && newStyle
->height().isSpecifiedOrIntrinsic()) {
89 placeHolder
->setInlineStyleProperty(CSSPropertyVerticalAlign
, CSSValueBaseline
);
93 // If the image has specified dimensions allow the alt-text container expand to fill them.
94 if (newStyle
->width().isSpecifiedOrIntrinsic() && newStyle
->height().isSpecifiedOrIntrinsic()) {
95 placeHolder
->setInlineStyleProperty(CSSPropertyWidth
, 100, CSSPrimitiveValue::UnitType::Percentage
);
96 placeHolder
->setInlineStyleProperty(CSSPropertyHeight
, 100, CSSPrimitiveValue::UnitType::Percentage
);
99 // Make sure the broken image icon appears on the appropriate side of the image for the element's writing direction.
100 brokenImage
->setInlineStyleProperty(CSSPropertyFloat
, AtomicString(newStyle
->direction() == LTR
? "left" : "right"));
102 // This is an <img> with no attributes, so don't display anything.
103 if (noImageSourceSpecified(element
) && !newStyle
->width().isSpecifiedOrIntrinsic() && !newStyle
->height().isSpecifiedOrIntrinsic() && toHTMLElement(element
).altText().isEmpty())
104 newStyle
->setDisplay(NONE
);
106 // This preserves legacy behaviour originally defined when alt-text was managed by LayoutImage.
107 if (noImageSourceSpecified(element
))
108 brokenImage
->setInlineStyleProperty(CSSPropertyDisplay
, CSSValueNone
);
110 brokenImage
->setInlineStyleProperty(CSSPropertyDisplay
, CSSValueInline
);