OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "core/html/HTMLImageFallbackHelper.h" | 5 #include "core/html/HTMLImageFallbackHelper.h" |
6 | 6 |
7 #include "core/HTMLNames.h" | 7 #include "core/HTMLNames.h" |
8 #include "core/InputTypeNames.h" | 8 #include "core/InputTypeNames.h" |
9 #include "core/dom/ElementRareData.h" | 9 #include "core/dom/ElementRareData.h" |
10 #include "core/dom/Text.h" | 10 #include "core/dom/Text.h" |
11 #include "core/dom/shadow/ShadowRoot.h" | 11 #include "core/dom/shadow/ShadowRoot.h" |
12 #include "core/html/HTMLDivElement.h" | |
13 #include "core/html/HTMLElement.h" | 12 #include "core/html/HTMLElement.h" |
14 #include "core/html/HTMLImageElement.h" | 13 #include "core/html/HTMLImageElement.h" |
15 #include "core/html/HTMLImageLoader.h" | 14 #include "core/html/HTMLImageLoader.h" |
16 #include "core/html/HTMLInputElement.h" | 15 #include "core/html/HTMLInputElement.h" |
| 16 #include "core/html/HTMLSpanElement.h" |
17 #include "core/html/HTMLStyleElement.h" | 17 #include "core/html/HTMLStyleElement.h" |
18 #include "platform/wtf/text/StringBuilder.h" | 18 #include "platform/wtf/text/StringBuilder.h" |
19 | 19 |
20 namespace blink { | 20 namespace blink { |
21 | 21 |
22 using namespace HTMLNames; | 22 using namespace HTMLNames; |
23 | 23 |
24 static bool NoImageSourceSpecified(const Element& element) { | 24 static bool ImageSmallerThanAltImage(int pixels_for_alt_image, |
25 bool no_src_specified = !element.hasAttribute(srcAttr) || | 25 const Length width, |
26 element.getAttribute(srcAttr).IsNull() || | 26 const Length height) { |
27 element.getAttribute(srcAttr).IsEmpty(); | 27 // We don't have a layout tree so can't compute the size of an image |
28 bool no_srcset_specified = !element.hasAttribute(srcsetAttr) || | 28 // relative dimensions - so we just assume we should display the alt image. |
29 element.getAttribute(srcsetAttr).IsNull() || | 29 if (!width.IsFixed() && !height.IsFixed()) |
30 element.getAttribute(srcsetAttr).IsEmpty(); | 30 return false; |
31 return no_src_specified && no_srcset_specified; | 31 if (height.IsFixed() && height.Value() < pixels_for_alt_image) |
| 32 return true; |
| 33 return width.IsFixed() && width.Value() < pixels_for_alt_image; |
32 } | 34 } |
33 | 35 |
34 void HTMLImageFallbackHelper::CreateAltTextShadowTree(Element& element) { | 36 void HTMLImageFallbackHelper::CreateAltTextShadowTree(Element& element) { |
35 ShadowRoot& root = element.EnsureUserAgentShadowRoot(); | 37 ShadowRoot& root = element.EnsureUserAgentShadowRoot(); |
36 | 38 |
37 HTMLDivElement* container = HTMLDivElement::Create(element.GetDocument()); | 39 HTMLSpanElement* container = HTMLSpanElement::Create(element.GetDocument()); |
38 root.AppendChild(container); | 40 root.AppendChild(container); |
39 container->setAttribute(idAttr, AtomicString("alttext-container")); | 41 container->setAttribute(idAttr, AtomicString("alttext-container")); |
40 container->SetInlineStyleProperty(CSSPropertyOverflow, CSSValueHidden); | |
41 container->SetInlineStyleProperty(CSSPropertyBorderWidth, 1, | |
42 CSSPrimitiveValue::UnitType::kPixels); | |
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, | |
48 CSSPrimitiveValue::UnitType::kPixels); | |
49 | 42 |
50 HTMLImageElement* broken_image = | 43 HTMLImageElement* broken_image = |
51 HTMLImageElement::Create(element.GetDocument()); | 44 HTMLImageElement::Create(element.GetDocument()); |
52 container->AppendChild(broken_image); | 45 container->AppendChild(broken_image); |
53 broken_image->SetIsFallbackImage(); | 46 broken_image->SetIsFallbackImage(); |
54 broken_image->setAttribute(idAttr, AtomicString("alttext-image")); | 47 broken_image->setAttribute(idAttr, AtomicString("alttext-image")); |
55 broken_image->setAttribute(widthAttr, AtomicString("16")); | 48 broken_image->setAttribute(widthAttr, AtomicString("16")); |
56 broken_image->setAttribute(heightAttr, AtomicString("16")); | 49 broken_image->setAttribute(heightAttr, AtomicString("16")); |
57 broken_image->setAttribute(alignAttr, AtomicString("left")); | 50 broken_image->setAttribute(alignAttr, AtomicString("left")); |
58 broken_image->SetInlineStyleProperty(CSSPropertyMargin, 0, | 51 broken_image->SetInlineStyleProperty(CSSPropertyMargin, 0, |
59 CSSPrimitiveValue::UnitType::kPixels); | 52 CSSPrimitiveValue::UnitType::kPixels); |
60 | 53 |
61 HTMLDivElement* alt_text = HTMLDivElement::Create(element.GetDocument()); | 54 HTMLSpanElement* alt_text = HTMLSpanElement::Create(element.GetDocument()); |
62 container->AppendChild(alt_text); | 55 container->AppendChild(alt_text); |
63 alt_text->setAttribute(idAttr, AtomicString("alttext")); | 56 alt_text->setAttribute(idAttr, AtomicString("alttext")); |
64 alt_text->SetInlineStyleProperty(CSSPropertyOverflow, CSSValueHidden); | |
65 alt_text->SetInlineStyleProperty(CSSPropertyDisplay, CSSValueBlock); | |
66 | 57 |
67 Text* text = | 58 Text* text = |
68 Text::Create(element.GetDocument(), ToHTMLElement(element).AltText()); | 59 Text::Create(element.GetDocument(), ToHTMLElement(element).AltText()); |
69 alt_text->AppendChild(text); | 60 alt_text->AppendChild(text); |
70 } | 61 } |
71 | 62 |
72 PassRefPtr<ComputedStyle> HTMLImageFallbackHelper::CustomStyleForAltText( | 63 PassRefPtr<ComputedStyle> HTMLImageFallbackHelper::CustomStyleForAltText( |
73 Element& element, | 64 Element& element, |
74 PassRefPtr<ComputedStyle> new_style) { | 65 PassRefPtr<ComputedStyle> new_style) { |
75 // If we have an author shadow root or have not created the UA shadow root | 66 // If we have an author shadow root or have not created the UA shadow root |
(...skipping 20 matching lines...) Expand all Loading... |
96 else if (new_style->Height().IsSpecifiedOrIntrinsic() && | 87 else if (new_style->Height().IsSpecifiedOrIntrinsic() && |
97 new_style->Width().IsAuto()) | 88 new_style->Width().IsAuto()) |
98 new_style->SetWidth(new_style->Height()); | 89 new_style->SetWidth(new_style->Height()); |
99 if (new_style->Width().IsSpecifiedOrIntrinsic() && | 90 if (new_style->Width().IsSpecifiedOrIntrinsic() && |
100 new_style->Height().IsSpecifiedOrIntrinsic()) { | 91 new_style->Height().IsSpecifiedOrIntrinsic()) { |
101 place_holder->SetInlineStyleProperty(CSSPropertyVerticalAlign, | 92 place_holder->SetInlineStyleProperty(CSSPropertyVerticalAlign, |
102 CSSValueBaseline); | 93 CSSValueBaseline); |
103 } | 94 } |
104 } | 95 } |
105 | 96 |
106 // If the image has specified dimensions allow the alt-text container expand | 97 bool image_has_intrinsic_dimensions = |
107 // to fill them. | 98 new_style->Width().IsSpecifiedOrIntrinsic() && |
108 if (new_style->Width().IsSpecifiedOrIntrinsic() && | 99 new_style->Height().IsSpecifiedOrIntrinsic(); |
109 new_style->Height().IsSpecifiedOrIntrinsic()) { | 100 bool image_has_no_alt_attribute = ToHTMLElement(element).AltText().IsNull(); |
110 place_holder->SetInlineStyleProperty( | 101 bool treat_as_replaced = |
111 CSSPropertyWidth, 100, CSSPrimitiveValue::UnitType::kPercentage); | 102 image_has_intrinsic_dimensions && |
112 place_holder->SetInlineStyleProperty( | 103 (element.GetDocument().InQuirksMode() || image_has_no_alt_attribute); |
113 CSSPropertyHeight, 100, CSSPrimitiveValue::UnitType::kPercentage); | 104 // 16px for the image and 2px for its top/left border/padding offset. |
| 105 int pixels_for_alt_image = 18; |
| 106 if (treat_as_replaced && |
| 107 !ImageSmallerThanAltImage(pixels_for_alt_image, new_style->Width(), |
| 108 new_style->Height())) { |
| 109 // https://html.spec.whatwg.org/multipage/rendering.html#images-3: |
| 110 // "If the element does not represent an image, but the element already has |
| 111 // intrinsic dimensions (e.g. from the dimension attributes or CSS rules), |
| 112 // and either: the user agent has reason to believe that the image will |
| 113 // become available and be rendered in due course, or the element has no alt |
| 114 // attribute, or the Document is in quirks mode The user agent is expected |
| 115 // to treat the element as a replaced element whose content is the text that |
| 116 // the element represents, if any." |
| 117 place_holder->SetInlineStyleProperty(CSSPropertyOverflow, CSSValueHidden); |
| 118 place_holder->SetInlineStyleProperty(CSSPropertyDisplay, |
| 119 CSSValueInlineBlock); |
| 120 place_holder->SetInlineStyleProperty(CSSPropertyBorderWidth, 1, |
| 121 CSSPrimitiveValue::UnitType::kPixels); |
| 122 place_holder->SetInlineStyleProperty(CSSPropertyBorderStyle, CSSValueSolid); |
| 123 place_holder->SetInlineStyleProperty(CSSPropertyBorderColor, |
| 124 CSSValueSilver); |
| 125 place_holder->SetInlineStyleProperty(CSSPropertyPadding, 1, |
| 126 CSSPrimitiveValue::UnitType::kPixels); |
| 127 place_holder->SetInlineStyleProperty(CSSPropertyBoxSizing, |
| 128 CSSValueBorderBox); |
| 129 CSSPrimitiveValue::UnitType unit = |
| 130 new_style->Height().IsPercent() |
| 131 ? CSSPrimitiveValue::UnitType::kPercentage |
| 132 : CSSPrimitiveValue::UnitType::kPixels; |
| 133 place_holder->SetInlineStyleProperty(CSSPropertyHeight, |
| 134 new_style->Height().Value(), unit); |
| 135 place_holder->SetInlineStyleProperty(CSSPropertyWidth, |
| 136 new_style->Width().Value(), unit); |
| 137 broken_image->SetInlineStyleProperty(CSSPropertyDisplay, CSSValueInline); |
| 138 // Make sure the broken image icon appears on the appropriate side of the |
| 139 // image for the element's writing direction. |
| 140 broken_image->SetInlineStyleProperty( |
| 141 CSSPropertyFloat, |
| 142 AtomicString(new_style->Direction() == TextDirection::kLtr ? "left" |
| 143 : "right")); |
| 144 } else { |
| 145 // "If the element is an img element that represents nothing and the user |
| 146 // agent does not expect this to change the user agent is expected to treat |
| 147 // the element as an empty inline element." |
| 148 // - We achieve this by hiding the broken image so that the span is empty. |
| 149 // "If the element is an img element that represents some text and the user |
| 150 // agent does not expect this to change the user agent is expected to treat |
| 151 // the element as a non-replaced phrasing element whose content is the text, |
| 152 // optionally with an icon indicating that an image is missing, so that the |
| 153 // user can request the image be displayed or investigate why it is not |
| 154 // rendering." |
| 155 // - We opt not to display an icon, like Firefox. |
| 156 if (!treat_as_replaced && new_style->Display() == EDisplay::kInline) { |
| 157 new_style->SetWidth(Length()); |
| 158 new_style->SetHeight(Length()); |
| 159 } |
| 160 broken_image->SetInlineStyleProperty(CSSPropertyDisplay, CSSValueNone); |
114 } | 161 } |
115 | 162 |
116 // Make sure the broken image icon appears on the appropriate side of the | |
117 // image for the element's writing direction. | |
118 broken_image->SetInlineStyleProperty( | |
119 CSSPropertyFloat, | |
120 AtomicString(new_style->Direction() == TextDirection::kLtr ? "left" | |
121 : "right")); | |
122 | |
123 // This is an <img> with no attributes, so don't display anything. | |
124 if (NoImageSourceSpecified(element) && | |
125 !new_style->Width().IsSpecifiedOrIntrinsic() && | |
126 !new_style->Height().IsSpecifiedOrIntrinsic() && | |
127 ToHTMLElement(element).AltText().IsEmpty()) | |
128 new_style->SetDisplay(EDisplay::kNone); | |
129 | |
130 // This preserves legacy behaviour originally defined when alt-text was | |
131 // managed by LayoutImage. | |
132 if (NoImageSourceSpecified(element)) | |
133 broken_image->SetInlineStyleProperty(CSSPropertyDisplay, CSSValueNone); | |
134 else | |
135 broken_image->SetInlineStyleProperty(CSSPropertyDisplay, CSSValueInline); | |
136 | |
137 return new_style; | 163 return new_style; |
138 } | 164 } |
139 | 165 |
140 } // namespace blink | 166 } // namespace blink |
OLD | NEW |