• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<!--
2  Copyright 2014 The Chromium Authors. All rights reserved.
3  Use of this source code is governed by a BSD-style license that can be
4  found in the LICENSE file.
5-->
6<html>
7<head>
8<title>Binary Size Analysis</title>
9<script src="d3/d3.js" charset="utf-8"></script>
10<script src="D3SymbolTreeMap.js" charset="utf-8"></script>
11<script src="data.js" charset="utf-8"></script>
12<style>
13body {
14    margin: 0px;
15    padding: 5px;
16}
17.swatch {
18    border: 1px solid rgb(100,100,100);
19    -webkit-user-select: none;
20    cursor: default;
21}
22</style>
23<script>
24var treemap;
25var filterChanging = false;
26var savedSettings = {};
27
28function init() {
29    if (window.metadata !== undefined && window.metadata.subtitle) {
30        document.getElementById('subtitle').innerHTML = ': ' + escape(metadata.subtitle);
31    }
32    initFilterOptions();
33    treemap = new D3SymbolTreeMap(
34        savedSettings.width,
35        savedSettings.height,
36        savedSettings.maxLevels);
37    treemap.init();
38}
39
40function getIdealSizes() {
41    var width = window.innerWidth - 20;
42    var height = window.innerHeight - 70;
43    return {'width': width, 'height': height};
44}
45
46function showReport(title, data, headers, dataFunction, styleFunction) {
47    var div =  d3.select('body').append('div')
48        .style('margin', '0')
49        .style('padding', '5px')
50        .style('position', 'absolute')
51        .style('top', '10%')
52        .style('left', '10%')
53        .style('background-color', 'rgba(255,255,255,0.9)')
54        .style('width', '80%')
55        .style('height', '80%')
56        .style('z-index', '2147483647')
57        .style('border', '3px ridge grey')
58        .style('box-shadow', '10px 10px 5px rgba(80,80,80,0.7)')
59        .style('text-align', 'center')
60        .style('border-radius', '10px');
61    var titlebar = div.append('div')
62        .style('margin', '0')
63        .style('padding', '5px')
64        .style('position', 'absolute')
65        .style('top', '0%')
66        .style('left', '0%')
67        .style('width', '100%')
68        .style('height', '10%')
69        .style('font-size', 'x-large');
70    titlebar.text(title);
71    var controls = div.append('div')
72        .style('margin', '0')
73        .style('padding', '5px')
74        .style('position', 'absolute')
75        .style('top', '90%')
76        .style('left', '0%')
77        .style('width', '100%')
78        .style('height', '10%');
79    controls.append('input').attr('type', 'button')
80        .attr('value', 'Dismiss')
81        .on('click', function(){div.remove();});
82
83    var tableDiv = div.append('div')
84        .style('overflow', 'auto')
85        .style('position', 'absolute')
86        .style('top', '10%')
87        .style('left', '0%')
88        .style('width', '100%')
89        .style('height', '80%')
90        .style('border-top', '1px solid rgb(230,230,230)')
91        .style('border-bottom', '1px solid rgb(230,230,230)');
92    var table = tableDiv.append('table')
93        .attr('border', '1')
94        .attr('cellspacing', '0')
95        .attr('cellpadding', '2')
96        .style('margin-left', 'auto')
97        .style('margin-right', 'auto');
98    var header = table.append('tr');
99    for (var i = 0; i < headers.length; i++) {
100        header.append('th').text(headers[i]);
101    }
102
103    for (var i = 0; i < data.length; i++) {
104        var row = table.append('tr');
105        for (j = 0; j < headers.length; j++) {
106            var td = row.append('td');
107            if (styleFunction) {
108                styleFunction.call(this, td, j);
109            }
110            dataFunction.call(this, data[i], j, td);
111        }
112    }
113}
114
115function bigSymbolsReport() {
116    var list = treemap.biggestSymbols(100);
117    var headers = ['Rank', 'Size (Bytes)', 'Type', 'Location'];
118    var styleFunction = function(selection, index) {
119        if (index === 3) {
120            selection.style('font-family', 'monospace');
121        }
122    };
123    var recordIndex = 1;
124    var dataFunction = function(record, index, cell) {
125        if (index === 0) {
126            cell.text(recordIndex++);
127        } else if (index === 1) {
128            cell.text(D3SymbolTreeMap._pretty(record.value));
129        } else if (index === 2) {
130            cell.text(record.t);
131        } else {
132            if (treemap.pathFor(record).indexOf('/out') == 0) {
133                cell.append('span').text(treemap.pathFor(record));
134                cell.append('br');
135                cell.append('span').text('Symbol: ');
136                cell.append('span').text(record.n);
137            } else {
138                var href = 'https://code.google.com/p/chromium/codesearch#chromium/src'
139                    + treemap.pathFor(record)
140                    + '&q='
141                    + record.n;
142                cell.append('a')
143                    .attr('href', href)
144                    .attr('target', '_blank')
145                    .text(treemap.pathFor(record));
146                cell.append('br');
147                cell.append('span').text('Symbol: ');
148                cell.append('span').text(record.n);
149            }
150        }
151    };
152    showReport('100 Largest Symbols', list, headers, dataFunction, styleFunction);
153}
154
155function bigPathsReport() {
156    var list = treemap.biggestPaths(100);
157    var headers = ['Rank', 'Size (Bytes)', 'Location'];
158    var styleFunction = function(selection, index) {
159        if (index === 2) {
160            selection.style('font-family', 'monospace');
161        }
162    };
163    var recordIndex = 1;
164    var dataFunction = function(record, index, cell) {
165        if (index === 0) {
166            cell.text(recordIndex++);
167        } else if (index === 1) {
168            cell.text(D3SymbolTreeMap._pretty(record.value));
169        } else if (index === 2) {
170            if (treemap.pathFor(record).indexOf('/out') == 0) {
171                cell.text(treemap.pathFor(record));
172            } else {
173                var href = 'https://code.google.com/p/chromium/codesearch#chromium/src' + treemap.pathFor(record);
174                cell.append('a')
175                    .attr('href', href)
176                    .attr('target', '_blank')
177                    .text(treemap.pathFor(record));
178            }
179
180        }
181    };
182    showReport('100 Largest Paths', list, headers, dataFunction, styleFunction);
183}
184
185function symbolFilterTextChanged() {
186    if (filterChanging) return true;
187    filterChanging = true;
188    var enabled = document.getElementById('symbol_types_filter').value;
189    for (var x=0; x<=25; x++) {
190        var checkBox = document.getElementById('check_' + x);
191        checkBox.checked = (enabled.indexOf(checkBox.value) != -1);
192    }
193    filterChanging = false;
194}
195
196function updateFilterText() {
197    if (filterChanging) return true;
198    filterChanging = true;
199    var text = '';
200    for (var x=0; x<=25; x++) {
201        var checkBox = document.getElementById('check_' + x);
202        if (checkBox.checked) {
203            text += checkBox.value;
204        }
205    }
206    document.getElementById('symbol_types_filter').value=text;
207    filterChanging = false;
208}
209
210function initFilterOptions() {
211    updateFilterText();
212    for (var x=0; x<=25; x++) {
213        var checkBox = document.getElementById('check_' + x);
214        checkBox.onchange=updateFilterText;
215        var swatch = document.getElementById('swatch_' + x);
216        swatch.style.backgroundColor = D3SymbolTreeMap.getColorForType(checkBox.value).toString();
217    }
218    var gteCheckbox = document.getElementById('check_gte');
219    gteCheckbox.onchange = function() {
220        document.getElementById('symbol_filter_gte').disabled = !gteCheckbox.checked;
221    }
222    var regexCheckbox = document.getElementById('check_regex');
223    regexCheckbox.onchange = function() {
224        document.getElementById('symbol_filter_regex').disabled = !regexCheckbox.checked;
225    }
226    var excludeRegexCheckbox = document.getElementById('check_exclude_regex');
227    excludeRegexCheckbox.onchange = function() {
228        document.getElementById('symbol_filter_exclude_regex').disabled = !excludeRegexCheckbox.checked;
229    }
230    var idealSizes = getIdealSizes();
231    document.getElementById('width').value = idealSizes.width;
232    document.getElementById('height').value = idealSizes.height;
233    saveFilterSettings();
234}
235
236function filterSetAll(enabled) {
237    for (var x=0; x<=25; x++) {
238        var checkBox = document.getElementById('check_' + x);
239        checkBox.checked = enabled;
240    }
241    updateFilterText();
242}
243
244function showOptions() {
245    loadFilterSettings();
246    var container = document.getElementById('options_container');
247    var w = container.offsetWidth;
248    var h = container.offsetHeight;
249    container.style.margin = '-' + (h/2) + 'px 0 0 -' + (w/2) + 'px';
250    container.style.visibility = 'visible';
251}
252
253function hideOptions() {
254    var container = document.getElementById('options_container');
255    container.style.visibility = 'hidden';
256}
257
258function applySettings() {
259    hideOptions();
260    var oldWidth = savedSettings.width;
261    var oldHeight = savedSettings.height;
262    var oldSymbols = savedSettings.symbolTypes;
263    var oldRegex = savedSettings.regex;
264    var oldExcludeRegex = savedSettings.excludeRegex;
265    var oldGte = savedSettings.gte;
266    var oldMaxLevels = savedSettings.maxLevels;
267    saveFilterSettings();
268    var resizeNeeded = oldWidth !== savedSettings.width || oldHeight !== savedSettings.height;
269    var regexChanged = oldRegex !== savedSettings.regex;
270    var excludeRegexChanged = oldExcludeRegex !== savedSettings.excludeRegex;
271    var symbolsChanged = oldSymbols !== savedSettings.symbolTypes;
272    var gteChanged = oldGte !== savedSettings.gte;
273    var filterChanged = regexChanged || excludeRegexChanged || symbolsChanged || gteChanged;
274    var maxLevelsChanged = oldMaxLevels !== savedSettings.maxLevels;
275
276    if (filterChanged) {
277        // Type filters
278        typeFilter = function(datum) {
279            if (datum.depth === 0) return true; // root node
280            if (datum.t === undefined) return true;
281            return savedSettings.symbolTypes !== undefined &&
282                savedSettings.symbolTypes.indexOf(datum.t) !== -1;
283        }
284
285        // Regex filter
286        var regexFilter = undefined;
287        if (savedSettings.regex !== undefined && savedSettings.regex.length > 0) {
288            console.log('filter: regex is "' + savedSettings.regex + '"');
289            var regex = new RegExp(savedSettings.regex);
290            regexFilter = function(datum) {
291                if (datum.depth === 0) return true; // root node
292                var fullName = this.pathFor(datum);
293                if (datum.children === undefined) { // it is a leaf node (symbol)
294                    fullName += ':' + datum.n;
295                }
296                return regex.test(fullName);
297            }
298        }
299
300        // Exclude regex filter
301        var excludeRegexFilter = undefined;
302        if (savedSettings.excludeRegex !== undefined && savedSettings.excludeRegex.length > 0) {
303            console.log('filter: exclude-regex is "' + savedSettings.excludeRegex + '"');
304            var excludeRegex = new RegExp(savedSettings.excludeRegex);
305            excludeRegexFilter = function(datum) {
306                if (datum.depth === 0) return true; // root node
307                var fullName = this.pathFor(datum);
308                if (datum.children === undefined) { // it is a leaf node (symbol)
309                    fullName += ':' + datum.n;
310                }
311                return !excludeRegex.test(fullName);
312            }
313        }
314
315        // Size filter
316        var sizeFilter = undefined;
317        if (savedSettings.gte !== undefined) {
318            console.log('filter: minimum size is ' + savedSettings.gte + ' bytes');
319            sizeFilter = function(datum) {
320                if (datum.children !== undefined) return true; // non-leaf
321                if (datum.value === undefined) console.log('whoops');
322                return datum.value >= savedSettings.gte;
323            }
324        }
325
326        // Make a filter to apply to the tree
327        var filter = function(datum) {
328            if (typeFilter && !typeFilter.call(this, datum)) return false;
329            if (regexFilter && !regexFilter.call(this, datum)) return false;
330            if (excludeRegexFilter && !excludeRegexFilter.call(this, datum)) return false;
331            if (sizeFilter && !sizeFilter.call(this, datum)) return false;
332            return true;
333        };
334        treemap.filter(filter);
335    }
336
337    // Adjust levels if needed.
338    if (maxLevelsChanged) {
339        treemap.setMaxLevels(savedSettings.maxLevels);
340    }
341
342    // Resize map if necessary.
343    if (resizeNeeded) {
344        console.log('desired treemap dimensions have changed, requesting resize');
345        treemap.resize(savedSettings.width, savedSettings.height);
346    }
347}
348
349function cancelSettings() {
350    hideOptions();
351    loadFilterSettings();
352}
353
354function saveFilterSettings() {
355    savedSettings.symbolTypes = document.getElementById('symbol_types_filter').value;
356    if (document.getElementById('check_regex').checked) {
357        savedSettings.regex = document.getElementById('symbol_filter_regex').value;
358    } else {
359        savedSettings.regex = undefined;
360    }
361    if (document.getElementById('check_exclude_regex').checked) {
362        savedSettings.excludeRegex = document.getElementById('symbol_filter_exclude_regex').value;
363    } else {
364        savedSettings.excludeRegex = undefined;
365    }
366    if (document.getElementById('check_gte').checked) {
367        savedSettings.gte = parseInt(document.getElementById('symbol_filter_gte').value);
368    } else {
369        savedSettings.gte = undefined;
370    }
371    savedSettings.width = parseInt(document.getElementById('width').value);
372    savedSettings.height = parseInt(document.getElementById('height').value);
373    savedSettings.maxLevels = parseInt(document.getElementById('max_levels').value);
374}
375
376function loadFilterSettings() {
377    document.getElementById('symbol_types_filter').value = savedSettings.symbolTypes;
378    symbolFilterTextChanged();
379    if (savedSettings.regex !== undefined) {
380        document.getElementById('check_regex').checked = true;
381        document.getElementById('symbol_filter_regex').value = savedSettings.regex;
382    } else {
383        document.getElementById('check_regex').checked = false;
384    }
385    if (savedSettings.excludeRegex !== undefined) {
386        document.getElementById('check_exclude_regex').checked = true;
387        document.getElementById('symbol_filter_exclude_regex').value = savedSettings.excludeRegex;
388    } else {
389        document.getElementById('check_exclude_regex').checked = false;
390    }
391    if (savedSettings.gte !== undefined) {
392        document.getElementById('check_gte').checked = true;
393        document.getElementById('symbol_filter_gte').value = savedSettings.gte;
394    } else {
395        document.getElementById('check_gte').checked = false;
396    }
397    document.getElementById('width').value = savedSettings.width;
398    document.getElementById('height').value = savedSettings.height;
399    document.getElementById('max_levels').value = savedSettings.maxLevels;
400}
401
402function escape(str) {
403    return str.replace(/&/g, '&amp;')
404              .replace(/"/g, '&quot;')
405              .replace(/</g, '&lt;')
406              .replace(/>/g, '&gt;');
407}
408</script>
409</head>
410<body onload='init()'>
411<div style='position: absolute; top: 5px; left: 5px;'>
412  <input type='button' onclick='showOptions()' value='Options &amp; Legend...'>
413  <span style='-webkit-user-select: none; cursor: help;' title='Click to view the symbol legend or to configure filters and options for the treemap'>[?]</span>
414</div>
415<div style='position: absolute; right: 5px; top: 5px; white-space: nowrap;'>
416    Reports:
417    <input type='button' onclick='bigSymbolsReport()' value='Large Symbols' title='Click to view a report of the largest 100 symbols that are with the bounds of the treemap that is currently displayed.'>
418    <input type='button' onclick='bigPathsReport()' value='Large Files' title='Click to view a report of the largest 100 source files that are with the bounds of the treemap that is currently displayed.'>
419</div>
420<div style='text-align: center; margin-bottom: 5px;'>
421    <span style='font-size: x-large; font-weight: bold; font-variant: small-caps'>Binary Size Analysis<span id='subtitle'></span></span>
422    <br><span style='font-size: small; font-style: italic;'>Double-click a box to zoom in, double-click outermost title to zoom out.</span>
423</div>
424<table id='options_container' style='visibility: hidden; border: 3px ridge grey; padding: 0px; top: 50%; left: 50%; position: fixed; z-index: 2147483646; overflow: auto; background-color: rgba(255,255,255,0.9); border-radius: 10px; box-shadow: 10px 10px 5px rgba(80,80,80,0.7);'><tr><td style='vertical-align: top'>
425    <table cellspacing=0 cellborder=0 style='width:100%'>
426        <tr><th colspan=3 style='padding-bottom: .25em; text-decoration: underline;'>Symbol Types To Show</th></tr>
427        <tr>
428            <td style='width: 33%; white-space: nowrap; vertical-align: top;'>
429                    <span class='swatch' id='swatch_0'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_0' value='A'>Global absolute (A)
430                <br><span class='swatch' id='swatch_1'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_1' value='B'>Global uninitialized data (B)
431                <br><span class='swatch' id='swatch_2'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_2' value='b'>Local uninitialized data (b)
432                <br><span class='swatch' id='swatch_3'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_3' value='C'>Global uninitialized common (C)
433                <br><span class='swatch' id='swatch_4'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_4' value='D'>Global initialized data (D)
434                <br><span class='swatch' id='swatch_5'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_5' value='d'>Local initialized data (d)
435                <br><span class='swatch' id='swatch_6'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_6' value='G'>Global small initialized data (G)
436                <br><span class='swatch' id='swatch_7'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_7' value='g'>Local small initialized data (g)
437                <br><span class='swatch' id='swatch_8'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_8' value='i'>Indirect function (i)
438            </td>
439            <td style='width: 33%; white-space: nowrap; vertical-align: top;'>
440                    <span class='swatch' id='swatch_9'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_9' value='N'>Debugging (N)
441                <br><span class='swatch' id='swatch_10'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_10' value='p'>Stack unwind (p)
442                <br><span class='swatch' id='swatch_11'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_11' value='R'>Global read-only data (R)
443                <br><span class='swatch' id='swatch_12'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_12' value='r'>Local read-only data (r)
444                <br><span class='swatch' id='swatch_13'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_13' value='S'>Global small uninitialized data (S)
445                <br><span class='swatch' id='swatch_14'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_14' value='s'>Local small uninitialized data (s)
446                <br><span class='swatch' id='swatch_15'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_15' value='T'>Global code (T)
447                <br><span class='swatch' id='swatch_16'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_16' value='t'>Local code (t)
448                <br><span class='swatch' id='swatch_17'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_17' value='U'>Undefined (U)
449            </td>
450            <td style='width: 33%; white-space: nowrap; vertical-align: top;'>
451                    <span class='swatch' id='swatch_18'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_18' value='u'>Unique (u)
452                <br><span class='swatch' id='swatch_19'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_19' value='V'>Global weak object (V)
453                <br><span class='swatch' id='swatch_20'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_20' value='v'>Local weak object (v)
454                <br><span class='swatch' id='swatch_21'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_21' value='W'>Global weak symbol (W)
455                <br><span class='swatch' id='swatch_22'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_22' value='w'>Local weak symbol (w)
456                <br><span class='swatch' id='swatch_23'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_23' value='@'>Vtable entry (@)
457                <br><span class='swatch' id='swatch_24'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_24' value='-'>STABS debugging (-)
458                <br><span class='swatch' id='swatch_25'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_25' value='?'>Unrecognized (?)
459            </td>
460        </tr>
461        <tr><td colspan=3 style='text-align: center; white-space: nowrap; padding-top: 1em;'>
462            Select <input type='button' onclick='filterSetAll(true)' value='All'>,
463            <input type='button' onclick='filterSetAll(false)' value='None'>,
464            or type a string: <input id='symbol_types_filter' size=30 value='' onkeyup='symbolFilterTextChanged()' onblur='updateFilterText()'>
465            <span style='-webkit-user-select: none; cursor: help;' title='Enter codes from the list above for the symbols you want to see. The checkboxes will update automatically to match the string that you enter.'>[?]</span>
466        </td></tr>
467   </table>
468</td></tr><tr><td style='vertical-align: top; padding-top: 10px; border-top: 1px solid grey;'>
469    <table cellspacing=0 cellborder=0 style='width: 100%'>
470        <tr><th colspan=2 style='padding-bottom: .25em; text-decoration: underline;'>Advanced Options</th></tr>
471        <tr>
472            <td style='white-space: nowrap; vertical-align: top;'>
473                <input type='checkbox' id='check_regex'>
474                Only include symbols matching this regex:
475            </td>
476            <td style='text-align: right; vertical-align: top;'>
477                <input disabled id='symbol_filter_regex' size=30 value='' style='text-align: right;'>
478                <span style='-webkit-user-select: none; cursor: help;' title='Enter a javascript regex. Only symbols that match this regex will be shown. This filter applies before any exclusion regex specified below. The format of each symbol is [path]:[symbol_name]'>[?]</span>
479            </td>
480        </tr>
481        <tr>
482            <td style='white-space: nowrap; vertical-align: top;'>
483                <input type='checkbox' id='check_exclude_regex'>
484                Exclude all symbols matching this regex:
485            </td>
486            <td style='text-align: right; vertical-align: top;'>
487                <input disabled id='symbol_filter_exclude_regex' size=30 value='' style='text-align: right;'>
488                <span style='-webkit-user-select: none; cursor: help;' title='Enter a javascript regex. Symbols that match this tegex will not be shown. This filter applies after any inclusion filter specified above. The format of each symbol is [path]:[symbol_name]'>[?]</span>
489            </td>
490        </tr>
491        <tr>
492            <td style='white-space: nowrap; vertical-align: top;'>
493                <input type='checkbox' id='check_gte'>
494                Only include symbols that are at least <span style='font-style: italic;'>n</span> bytes:
495            </td>
496            <td style='text-align: right; vertical-align: top;'>
497                <input disabled id='symbol_filter_gte' size=8 value='' style='text-align: right;'>
498                <span style='-webkit-user-select: none; cursor: help;' title='Symbols whose size is less than this value will be hidden.'>[?]</span>
499            </td>
500        </tr>
501        <tr>
502            <td style='white-space: nowrap vertical-align: top;;'>
503                Show at most <span style='font-style: italic;'>n</span> levels of detail at a time:
504            </td>
505            <td style='text-align: right; vertical-align: top;'>
506                <input id='max_levels' size=4 value='2' style='text-align: right;'><span style='-webkit-user-select: none; cursor: help;' title='Increasing this value shows more detail without the need to zoom, but uses more computing power.'>[?]</span>
507            </td>
508        </tr>
509        <tr>
510            <td style='white-space: nowrap vertical-align: top;;'>
511                Set the size of the treemap to <span style='font-style: italic;'>W x H</span> pixels:
512            </td>
513            <td style='text-align: right; vertical-align: top;'>
514                <input id='width' size=4 value='' style='text-align: right;'>
515                &nbsp;x&nbsp;<input id='height' size=4 value='' style='text-align: right;'>
516            </td>
517        </tr>
518    </table>
519</td></tr>
520<tr><td style='padding-top: 10px; text-align: right; border-top: 1px solid grey'>
521    <input type='button' value='Apply' onclick='applySettings()'>
522    <input type='button' value='Cancel' onclick='cancelSettings()'>
523</td></tr></table>
524</body>
525</html>
526