1<!doctype html>
2<title>Image width and height attributes are used to infer aspect-ratio</title>
3<script src="/resources/testharness.js"></script>
4<script src="/resources/testharnessreport.js"></script>
5<style>
6 img:not([nowidth]) {
7 width: 100%;
8 max-width: 100px;
9 height: auto;
10 }
11</style>
12<picture>
13 <source srcset="/images/green-100x50.png"></source>
14 <img>
15</picture>
16
17<picture>
18 <source srcset="/images/green-100x50.png" width="100" height="100"></source>
19 <img>
20</picture>
21
22<picture>
23 <source srcset="/images/green-100x50.png" width="100" height="100"></source>
24 <img width="50" height="100">
25</picture>
26
27<picture>
28 <source srcset="/images/green-100x50.png" width="100" height="100" id="twosource-s1"></source>
29 <source srcset="/images/green-100x50.png" width="300" height="150"></source>
30 <img id="twosource-img">
31</picture>
32
33<div style="width: 100px;">
34 <picture>
35 <source srcset="/images/green-100x50.png" width="100%" height="50%" id="percent-src"></source>
36 <img style="contain:size;" id="percent-img" nowidth="true">
37 </picture>
38</div>
39
40<picture>
41 <source srcset="/images/green-100x50.png" width="100abc" height="50abc" id="trailing-src"></source>
42 <img style="contain:size;" id="trailing-img" nowidth="true">
43</picture>
44
45<script>
46let t = async_test("source width and height attributes are used to infer aspect-ratio in <picture>");
47function assert_ratio(img, expected, description) {
48 let epsilon = 0.001;
49 assert_approx_equals(parseFloat(getComputedStyle(img).width, 10) / parseFloat(getComputedStyle(img).height, 10),
50 expected, epsilon, description);
51}
52
53function createPicture(width, height) {
54 var picture = document.createElement("picture");
55 var source = document.createElement("source");
56 if (width !== undefined)
57 source.setAttribute("width", width);
58 if (height !== undefined)
59 source.setAttribute("height", height);
60 source.setAttribute("srcset", "/images/green.png");
61 picture.appendChild(source);
62 var img = document.createElement("img");
63 picture.appendChild(img);
64 document.body.appendChild(picture);
65 return img;
66}
67
68function assert_cs(img, val) {
69 assert_equals(getComputedStyle(img).aspectRatio, val);
70}
71
72// Create and append a new image and immediately check the ratio.
73// This is not racy because the spec requires the user agent to queue a task:
74// https://html.spec.whatwg.org/multipage/images.html#updating-the-image-data
75test(function() {
76 var img = createPicture(100, 100);
77 assert_ratio(img, 1.0);
78 assert_cs(img, "auto 100 / 100");
79 img.style.display = "none";
80 img.setAttribute("nowidth", "true");
81 assert_equals(getComputedStyle(img).width, "100px");
82 assert_equals(getComputedStyle(img).height, "100px");
83 var source = img.previousSibling;
84 assert_equals(getComputedStyle(source).width, "auto");
85 assert_equals(getComputedStyle(source).height, "auto");
86}, "Computed style for width/height/aspect-ratio");
87
88test(function() {
89 img = createPicture(200, 100);
90 img.setAttribute("width", 250);
91 img.setAttribute("height", 50);
92 assert_ratio(img, 2.0);
93 assert_cs(img, "auto 200 / 100");
94 img.style.display = "none";
95 img.setAttribute("nowidth", "true");
96 assert_equals(getComputedStyle(img).width, "200px");
97 assert_equals(getComputedStyle(img).height, "100px");
98 source = img.previousSibling;
99 assert_equals(getComputedStyle(source).width, "auto");
100 assert_equals(getComputedStyle(source).height, "auto");
101}, "Source width/height should take precedence over img attributes.");
102
103test(function() {
104 img.parentNode.removeChild(img.previousSibling);
105 assert_cs(img, "auto 250 / 50");
106 img.src = "/images/green.png";
107 assert_ratio(img, 5.0);
108 img.style.display = "none";
109 img.setAttribute("nowidth", "true");
110 assert_equals(getComputedStyle(img).width, "250px");
111 assert_equals(getComputedStyle(img).height, "50px");
112}, "Make sure style gets invalidated correctly when the source gets removed.");
113
114test(function() {
115 img = createPicture(100, undefined);
116 img.setAttribute("width", 200);
117 img.setAttribute("height", 100);
118 assert_cs(img, "auto");
119 img.style.display = "none";
120 img.setAttribute("nowidth", "true");
121 assert_equals(getComputedStyle(img).width, "100px");
122 assert_equals(getComputedStyle(img).height, "auto");
123}, "If the <source> has only one of width/height, we don't get an aspect ratio, even if the <img> has both.");
124
125test(function() {
126 img = createPicture(undefined, undefined);
127 img.setAttribute("width", 200);
128 img.setAttribute("height", 100);
129 assert_cs(img, "auto 200 / 100");
130}, "If we don't have width/height on the source, we fall back to width/height on the <img>.");
131
132test(function() {
133 img = createPicture(100, undefined);
134 img.parentNode.style.display = "none";
135 img.setAttribute("width", "200");
136 img.setAttribute("height", "300");
137 img.setAttribute("nowidth", "true");
138 assert_cs(img, "auto");
139 assert_equals(getComputedStyle(img).width, "100px");
140 assert_equals(getComputedStyle(img).height, "auto");
141
142 img = createPicture(undefined, 100);
143 img.parentNode.style.display = "none";
144 img.setAttribute("width", "200");
145 img.setAttribute("height", "300");
146 img.setAttribute("nowidth", "true");
147 assert_cs(img, "auto");
148 assert_equals(getComputedStyle(img).width, "auto");
149 assert_equals(getComputedStyle(img).height, "100px");
150}, "If we only have one width/height attribute, we should get that attribute mapped but no aspect ratio, even if <img> has attributes.");
151
152test(function() {
153 img = createPicture(100, 100);
154 assert_cs(img, "auto 100 / 100");
155 img.previousSibling.setAttribute("height", "300");
156 assert_cs(img, "auto 100 / 300");
157 img.previousSibling.setAttribute("width", "10");
158 assert_cs(img, "auto 10 / 300");
159 img.style.display = "none";
160 img.setAttribute("nowidth", "true");
161 assert_equals(getComputedStyle(img).width, "10px");
162 assert_equals(getComputedStyle(img).height, "300px");
163}, "Dynamically changing width/height should change computed style");
164
165test(function() {
166 img = document.getElementById("twosource-img");
167 assert_cs(img, "auto 100 / 100");
168 source = document.getElementById("twosource-s1");
169 source.setAttribute("type", "x-foo/x-bar");
170 // We should now match the second source
171 assert_cs(img, "auto 300 / 150");
172 img.style.display = "none";
173 img.setAttribute("nowidth", "true");
174 assert_equals(getComputedStyle(img).width, "300px");
175 assert_equals(getComputedStyle(img).height, "150px");
176}, "Changing which <source> matches should change computed style");
177
178test(function() {
179 img = document.getElementById("percent-img");
180 assert_equals(img.offsetWidth, 100);
181 assert_equals(img.offsetHeight, 0);
182 assert_cs(img, "auto");
183 source = document.getElementById("percent-src");
184 assert_equals(source.width, 100);
185 assert_equals(source.height, 50);
186 img.style.display = "none";
187 img.setAttribute("nowidth", "true");
188 assert_equals(getComputedStyle(img).width, "100%");
189 assert_equals(getComputedStyle(img).height, "50%");
190}, "Percentages on source should be ignored for aspect-ratio but used for width/height.");
191
192test(function() {
193 img = document.getElementById("trailing-img");
194 assert_equals(img.offsetWidth, 100);
195 assert_equals(img.offsetHeight, 50);
196 assert_cs(img, "auto 100 / 50");
197 source = document.getElementById("trailing-src");
198 assert_equals(source.width, 100);
199 assert_equals(source.height, 50);
200 img.style.display = "none";
201 img.setAttribute("nowidth", "true");
202 assert_equals(getComputedStyle(img).width, "100px");
203 assert_equals(getComputedStyle(img).height, "50px");
204}, "Trailing garbage should be ignored but not make the attribute invalid");
205
206onload = t.step_func_done(function() {
207 let images = document.querySelectorAll("img");
208 var img = images[0];
209 assert_ratio(img, 2.0, "2.0 is the original aspect ratio of green-100x50.png");
210 assert_cs(img, "auto");
211 img.style.display = "none";
212 img.setAttribute("nowidth", "true");
213 assert_equals(getComputedStyle(img).width, "auto");
214 assert_equals(getComputedStyle(img).height, "auto");
215 img = images[1];
216 assert_ratio(img, 2.0, "Loaded image's aspect ratio, at least by default, overrides width / height ratio.");
217 assert_cs(img, "auto 100 / 100");
218 img.style.display = "none";
219 img.setAttribute("nowidth", "true");
220 assert_equals(getComputedStyle(img).width, "100px");
221 assert_equals(getComputedStyle(img).height, "100px");
222 img = images[2];
223 assert_ratio(img, 2.0, "Loaded image's aspect ratio, at least by default, overrides width / height ratio (2).");
224 assert_cs(img, "auto 100 / 100");
225 img.style.display = "none";
226 img.setAttribute("nowidth", "true");
227 assert_equals(getComputedStyle(img).width, "100px");
228 assert_equals(getComputedStyle(img).height, "100px");
229});
230</script>