Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(567)

Side by Side Diff: LayoutTests/svg/as-object/sizing/svg-in-object.js

Issue 26390004: Rework SVG sizing (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Fix mishap during rebase in svg.css Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // global async_test, assert_equals
2 //
3 // This test generates a couple of scenarios (each a TestData) for
4 // sizing SVG inside an inline <object> and has a simple JavaScript
5 // sizing implementation that handles the generated scenarios. It
6 // generates a DOM corresponding to the scenario and compares the laid
7 // out size to the calculated size.
8 //
9 // The tests loops through different combinations of:
10 //
11 // * width and height on <object>
12 //
13 // * width and height on <svg>
14 //
15 // * viewBox on <svg> (gives intrinsic ratio)
16 //
17 // * width and height on containing block of <object>
18 //
19 // All these contribute to the final size of the SVG in some way.
20 //
21 // The test focuses on the size of the CSS box generated by the SVG.
22 // The SVG is always empty by itself so no actual SVG are tested.
23 //
24 // Focus is also put on how the different attributes interact, little
25 // focus is put on variations within an attribute that doesn't affect
26 // the relationship to other attributes, i.e only px and % units are
27 // used since that covers the interactions.
28 //
29 // To debug a specific test append ?<test-id> to the URL. An <iframe>
30 // is generated with equivalent test and the source of the test is
31 // added to a <pre> element.
32 //
33 // Note: placeholder is an alternative name for the tested <object>
34 // element; 'object' becomes such an ambigious name when placed in
35 // code.
36
37 (function() {
38 function parseLength(l) {
39 var match = /^([-+]?[0-9]+|[-+]?[0-9]*\.[0-9]+)(px|%)?$/.exec(l);
40 if (!match)
41 return null;
42 return new Length(Number(match[1]), match[2] ? match[2] : "px");
43 }
44
45 function parseViewBox(input) {
46 if (!input)
47 return null;
48
49 var arr = input.split(' ');
50 return arr.map(function(a) { return parseInt(a); });
51 }
52
53 // Only px and % are used
54 function convertToPx(input, percentRef) {
55 if (input == null)
56 return null;
57 var length = parseLength(input);
58 if (length.amount == 0)
59 return 0;
60 if (!length.unit)
61 length.unit = "px";
62 if (length.unit == "%" && percentRef === undefined)
63 return null;
64 return length.amount * { px: 1,
65 "%": percentRef/100}[length.unit];
66 }
67
68 function Length(amount, unit) {
69 this.amount = amount;
70 this.unit = unit;
71 }
72
73 function describe(data) {
74 function dumpObject(obj) {
75 var r = "";
76 for (var property in obj) {
77 if (obj.hasOwnProperty(property)) {
78 var value = obj[property];
79 if (typeof value == 'string')
80 value = "'" + value + "'";
81 else if (value == null)
82 value = "null";
83 else if (typeof value == 'object')
84 {
85 if (value instanceof Array)
86 value = "[" + value + "]";
87 else
88 value = "{" + dumpObject(value) + "}";
89 }
90
91 if (value != "null")
92 r += property + ": " + value + ", ";
93 }
94 }
95 return r;
96 }
97 var result = dumpObject(data);
98 if (result == "")
99 return "(initial values)";
100 return result;
101 }
102
103 function TestData(config) {
104 this.config = config;
105 this.name = describe(config);
106 this.style = {};
107 this.mapPresentationalHintLength("width", config.placeholderWidthAttr);
108 this.mapPresentationalHintLength("height", config.placeholderHeightAttr) ;
109 }
110
111 TestData.prototype.mapPresentationalHintLength =
112 function(cssProperty, attr) {
113 if (attr) {
114 var l = parseLength(attr);
115 if (l)
116 this.style[cssProperty] = l.amount + l.unit;
117 }
118 };
119
120 TestData.prototype.computedWidthIsAuto = function() {
121 return !this.style["width"] || this.style["width"] == 'auto';
122 };
123
124 TestData.prototype.computedHeightIsAuto = function() {
125 return !this.style["height"] || this.style["height"] == 'auto' ||
126 (parseLength(this.style["height"]).unit == '%' &&
127 this.containerComputedHeightIsAuto());
128 };
129
130 TestData.prototype.containerComputedWidthIsAuto = function() {
131 return !this.config.containerWidthStyle ||
132 this.config.containerWidthStyle == 'auto';
133 };
134
135 TestData.prototype.containerComputedHeightIsAuto = function() {
136 return !this.config.containerHeightStyle ||
137 this.config.containerHeightStyle == 'auto';
138 };
139
140 TestData.prototype.intrinsicInformation = function() {
141 var w = convertToPx(this.config.svgWidthAttr) || 0;
142 var h = convertToPx(this.config.svgHeightAttr) || 0;
143 var r = 0;
144 if (w && h) {
145 r = w / h;
146 } else {
147 var vb = parseViewBox(this.config.svgViewBoxAttr);
148 if (vb) {
149 r = vb[2] / vb[3];
150 }
151 if (r) {
152 if (!w && h)
153 w = h * r;
154 else if (!h && w)
155 h = w / r;
156 }
157 }
158 return { width: w, height: h, ratio: r };
159 };
160
161
162 TestData.prototype.computeInlineReplacedSize = function() {
163 var intrinsic = this.intrinsicInformation();
164 var self = this;
165
166 // http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-height
167 function calculateUsedHeight() {
168 if (self.computedHeightIsAuto()) {
169 if (self.computedWidthIsAuto() && intrinsic.height)
170 return intrinsic.height;
171 if (intrinsic.ratio)
172 return calculateUsedWidth() / intrinsic.ratio;
173 if (intrinsic.height)
174 return intrinsic.height;
175 return 150;
176 }
177
178 return convertToPx(self.style["height"],
179 convertToPx(self.config.containerHeightStyle,
180 self.outerHeight));
181 }
182
183 // http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-width
184 function calculateUsedWidth() {
185 if (self.computedWidthIsAuto()) {
186 if (self.computedHeightIsAuto() && intrinsic.width)
187 return intrinsic.width;
188 if (!self.computedHeightIsAuto() && intrinsic.ratio)
189 return calculateUsedHeight() * intrinsic.ratio;
190 if (self.computedHeightIsAuto() && intrinsic.ratio) {
191 if (self.containerComputedWidthIsAuto()) {
192 // Note: While this is actually undefined in CSS
193 // 2.1, use the suggested value by examining the
194 // ancestor widths.
195 return self.outerWidth;
196 } else {
197 return convertToPx(self.config.containerWidthStyle,
198 self.outerWidth);
199 }
200 }
201 if (intrinsic.width)
202 return intrinsic.width;
203 return 300;
204 }
205
206 if (self.containerComputedWidthIsAuto())
207 return convertToPx(self.style["width"], self.outerWidth);
208 else
209 return convertToPx(self.style["width"],
210 convertToPx(self.config.containerWidthStyle,
211 self.outerWidth));
212 }
213 return { width: calculateUsedWidth(),
214 height: calculateUsedHeight() };
215 };
216
217 var testContainer = document.querySelector('#testContainer');
218 TestData.prototype.outerWidth = testContainer.getBoundingClientRect().width;
219 TestData.prototype.outerHeight = testContainer.getBoundingClientRect().heigh t;
220
221 window.TestData = TestData;
222 })();
223
224 function setupContainer(testData, placeholder, options) {
225 options = options || {};
226
227 var container = document.createElement("div");
228
229 container.id = "container";
230 if (testData.config.containerWidthStyle)
231 container.style.width = testData.config.containerWidthStyle;
232
233 if (testData.config.containerHeightStyle)
234 container.style.height = testData.config.containerHeightStyle;
235
236 if (options.pretty)
237 container.appendChild(document.createTextNode("\n\t\t"));
238 container.appendChild(placeholder);
239 if (options.pretty)
240 container.appendChild(document.createTextNode("\n\t"));
241
242 return container;
243 }
244
245 function setupPlaceholder(testData, options) {
246 options = options || {};
247
248 function generateSVGURI(testData, encoder) {
249 var res = '<svg xmlns="http://www.w3.org/2000/svg"';
250 function addAttr(attr, prop) {
251 if (testData.config[prop])
252 res += ' ' + attr + '="' + testData.config[prop] + '"';
253 }
254 addAttr("width", "svgWidthAttr");
255 addAttr("height", "svgHeightAttr");
256 addAttr("viewBox", "svgViewBoxAttr");
257 res += '></svg>';
258 return 'data:image/svg+xml' + encoder(res);
259 }
260
261 var placeholder = document.createElement("object");
262
263 if (options.pretty) {
264 placeholder.appendChild(document.createTextNode("\n\t\t\t"));
265 placeholder.appendChild(
266 document.createComment(
267 generateSVGURI(testData, function(x) { return "," + x; })));
268 placeholder.appendChild(document.createTextNode("\n\t\t"));
269 }
270
271 placeholder.setAttribute("id", "test");
272 if (testData.config.placeholderWidthAttr)
273 placeholder.setAttribute("width", testData.config.placeholderWidthAttr);
274 if (testData.config.placeholderHeightAttr)
275 placeholder.setAttribute("height", testData.config.placeholderHeightAttr );
276 placeholder.setAttribute("data",
277 generateSVGURI(testData, function(x) {
278 return ";base64," + btoa(x);
279 }));
280 return placeholder;
281 }
282
283 function buildDemo(testData) {
284 // Non-essential debugging tool
285
286 var options = { pretty: true };
287 var expectedRect =
288 testData.computeInlineReplacedSize();
289 var container =
290 setupContainer(testData, setupPlaceholder(testData, options), option s);
291
292 var root = document.createElement("html");
293 var style = document.createElement("style");
294
295 style.textContent = "\n" +
296 "\tbody { margin: 0; font-family: sans-serif }\n" +
297 "\t#expected {\n" +
298 "\t\twidth: " + (expectedRect.width) + "px; height: "
299 + (expectedRect.height) + "px;\n" +
300 "\t\tborder: 10px solid lime; position: absolute;\n" +
301 "\t\tbackground-color: red }\n" +
302 "\t#testContainer { position: absolute;\n" +
303 "\t\ttop: 10px; left: 10px; width: 800px; height: 600px }\n" +
304 "\tobject { background-color: green }\n" +
305 "\t.result { position: absolute; top: 0; right: 0;\n" +
306 "\t\tbackground-color: hsla(0,0%, 0%, 0.85); border-radius: 0.5em;\n" +
307 "\t\tpadding: 0.5em; border: 0.25em solid black }\n" +
308 "\t.pass { color: lime }\n" +
309 "\t.fail { color: red }\n";
310
311 root.appendChild(document.createTextNode("\n"));
312 root.appendChild(style);
313 root.appendChild(document.createTextNode("\n"));
314
315 var script = document.createElement("script");
316 script.textContent = "\n" +
317 "onload = function() {\n" +
318 "\tvar objectRect = \n" +
319 "\t\tdocument.querySelector('#test').getBoundingClientRect();\n" +
320 "\tpassed = (objectRect.width == " + expectedRect.width + " && " +
321 "objectRect.height == " + expectedRect.height + ");\n" +
322 "\tdocument.body.insertAdjacentHTML('beforeEnd',\n" +
323 "\t\t'<span class=\"result '+ (passed ? 'pass' : 'fail') " +
324 "+ '\">' + (passed ? 'Pass' : 'Fail') + '</span>');\n" +
325 "};\n";
326
327 root.appendChild(script);
328 root.appendChild(document.createTextNode("\n"));
329
330 var expectedElement = document.createElement("div");
331 expectedElement.id = "expected";
332 root.appendChild(expectedElement);
333 root.appendChild(document.createTextNode("\n"));
334
335 var testContainer = document.createElement("div");
336 testContainer.id = "testContainer";
337 testContainer.appendChild(document.createTextNode("\n\t"));
338 testContainer.appendChild(container);
339 testContainer.appendChild(document.createTextNode("\n"));
340 root.appendChild(testContainer);
341 root.appendChild(document.createTextNode("\n"));
342
343 return "<!DOCTYPE html>\n" + root.outerHTML;
344 }
345
346 function doCombinationTest(values, func)
347 {
348 // Recursively construct all possible combinations of values and
349 // send them to |func|. Example:
350 //
351 // values: [["X", ["a", "b"]],
352 // ["Y", ["c", "d"]]]
353 //
354 // generates the objects:
355 //
356 // 1: { "X": "a", "Y": "c" }
357 // 2: { "X": "a", "Y": "d" }
358 // 3: { "X": "b", "Y": "c" }
359 // 4: { "X": "b", "Y": "d" }
360 //
361 // and each will be sent to |func| with the corresponding prefixed
362 // id (modulo order).
363
364 var combinationId = 1;
365 function doCombinationTestRecursive(slicedValues, config) {
366 if (slicedValues.length > 0) {
367 var configKey = slicedValues[0][0];
368 slicedValues[0][1].forEach(function(configValue) {
369 var new_config = {};
370 for (k in config)
371 new_config[k] = config[k];
372 new_config[configKey] = configValue;
373 doCombinationTestRecursive(slicedValues.slice(1), new_config);
374 });
375 } else {
376 func(config, combinationId++);
377 }
378 }
379 doCombinationTestRecursive(values, {});
380 }
381
382 var debugHint = function(id) { return "(append ?"+id+" to debug) " };
383 var testSingleId;
384 if (window.location.search) {
385 testSingleId = window.location.search.substring(1);
386 debugHint = function(id) { return ""; };
387 }
388
389 function testSVGInObjectWithPlaceholderHeightAttr(placeholderHeightAttr) {
390 // Separated over placeholderHeightAttr so that the test count is around ~20 0
391
392 doCombinationTest(
393 [["containerWidthStyle", [null, "400px"]],
394 ["containerHeightStyle", [null, "400px"]],
395 ["placeholderWidthAttr", [null, "100", "50%"]],
396 ["placeholderHeightAttr", [placeholderHeightAttr]],
397 ["svgViewBoxAttr", [ null, "0 0 100 200" ]],
398 ["svgWidthAttr", [ null, "200", "25%" ]],
399 ["svgHeightAttr", [ null, "200", "25%" ]]],
400 function(config, id) {
401 if (!testSingleId || testSingleId == id) {
402 var testData = new TestData(config);
403 var t = async_test(testData.name);
404
405 var expectedRect =
406 testData.computeInlineReplacedSize();
407 var placeholder = setupPlaceholder(testData);
408 var container =
409 setupContainer(testData, placeholder);
410
411 var checkSize = function() {
412 var placeholderRect =
413 placeholder.getBoundingClientRect();
414
415 try {
416 assert_equals(placeholderRect.width,
417 expectedRect.width,
418 debugHint(id) + "Wrong width");
419 assert_equals(placeholderRect.height,
420 expectedRect.height,
421 debugHint(id) + "Wrong height");
422 } finally {
423 testContainer.removeChild(container);
424 if (testSingleId)
425 document.body.removeChild(testContainer);
426 }
427 t.done();
428 };
429
430 t.step(function() {
431 placeholder.addEventListener('load', function() {
432 // setTimeout is a work-around to let engines
433 // finish layout of child browsing contexts even
434 // after the load event
435 setTimeout(t.step_func(checkSize), 0);
436 });
437 testContainer.appendChild(container);
438 });
439 }
440
441 if (testSingleId == id) {
442 var pad = function(n, width, z) {
443 z = z || '0';
444 n = n + '';
445 return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
446 };
447
448 var demo = buildDemo(testData);
449 var iframe = document.createElement('iframe');
450 iframe.style.width = (Math.max(900, expectedRect.width)) + "px";
451 iframe.style.height = (Math.max(400, expectedRect.height)) + "px ";
452 iframe.src = "data:text/html;charset=utf-8," + encodeURIComponen t(demo);
453 document.body.appendChild(iframe);
454
455 document.body.insertAdjacentHTML(
456 'beforeEnd',
457 '<p><a href="data:application/octet-stream;charset=utf-8;bas e64,' +
458 btoa(demo) + '" download="svg-in-object-test-' + pad(id, 3) + '.html">Download</a></p>');
459 }
460 });
461 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698