Skip to content
Permalink
Browse files
Web Inspector: Adopt Number.prototype.toLocaleString For All Sizes an…
…d Times

https://bugs.webkit.org/show_bug.cgi?id=152033
<rdar://problem/23815589>

Reviewed by Timothy Hatcher.

Source/WebInspectorUI:

Update string formatters to localize float and percentage strings. Hook up
console message formatters to use String.standardFormatters so that console
statements (e.g. console.log("%.3f", 3.14159)) are properly formatted.

* Localizations/en.lproj/localizedStrings.js:
* UserInterface/Base/Utilities.js:
(value):
tokenizeFormatString should default to 6 digits when no precision
sub-specifier is provided.

percentageString should localize formatting, and take a fraction value
(0 to 1) instead of a percentage.

secondsToString should perform special-case formatting for zero values
("0ms") instead of the general purpose float formatter.

(value.d):
Switch to parseInt to floor floating point values and support numeric strings.
Return NaN instead of zero when passed a value that can't be converted to integer.

(value.f):
Switch to parseFloat to support numeric strings, and localize formatting.
Remove precision check, as it will never be less than zero. Return NaN
instead of zero when passed a value that can't be converted to float.

(prettyFunctionName):
Convert substitutions (an arguments object) to an array before calling join.

* UserInterface/Views/ConsoleMessageView.js:
(WebInspector.ConsoleMessageView.prototype._formatWithSubstitutionString.floatFormatter):
Use String.standardFormatters.f.

(WebInspector.ConsoleMessageView.prototype._formatWithSubstitutionString.integerFormatter):
Use String.standardFormatters.d.

* UserInterface/Views/LayoutTimelineDataGridNode.js:
(WebInspector.LayoutTimelineDataGridNode.prototype.createCellContent):
(WebInspector.LayoutTimelineDataGridNode):
Use integer formatting for pixel values.

* UserInterface/Views/ProfileDataGridNode.js:
(WebInspector.ProfileDataGridNode.prototype._recalculateData):
(WebInspector.ProfileDataGridNode.prototype._totalTimeContent):
Treat percentage as a fraction from 0 to 1.

* UserInterface/Views/ResourceDetailsSidebarPanel.js:
(WebInspector.ResourceDetailsSidebarPanel.prototype._refreshImageSizeSection):
Use integer formatting for pixel values.

LayoutTests:

Add test coverage for string formatters, and additional test cases for
Number.percentageString and Number.secondsToString.

* inspector/unit-tests/number-utilities-expected.txt:
* inspector/unit-tests/number-utilities.html:
* inspector/unit-tests/string-utilities-expected.txt: Added.
* inspector/unit-tests/string-utilities.html: Added.


Canonical link: https://commits.webkit.org/174770@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@199635 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
LuckyKobold committed Apr 17, 2016
1 parent 6bb5c11 commit a18087b7283cadb5cd10ce4553a191a0a09e2b5b
@@ -1,3 +1,19 @@
2016-04-16 Matt Baker <mattbaker@apple.com>

Web Inspector: Adopt Number.prototype.toLocaleString For All Sizes and Times
https://bugs.webkit.org/show_bug.cgi?id=152033
<rdar://problem/23815589>

Reviewed by Timothy Hatcher.

Add test coverage for string formatters, and additional test cases for
Number.percentageString and Number.secondsToString.

* inspector/unit-tests/number-utilities-expected.txt:
* inspector/unit-tests/number-utilities.html:
* inspector/unit-tests/string-utilities-expected.txt: Added.
* inspector/unit-tests/string-utilities.html: Added.

2016-04-15 Myles C. Maxfield <mmaxfield@apple.com>

[Font Loading] Test promise gets rejected when unknown format used
@@ -14,6 +14,7 @@ PASS: constrain of a value above max becomes max
FAIL: constrain of NaN becomes min

-- Running test case: Number.secondsToString
PASS: normal resolution of 0ms should be ms with no decimals
PASS: normal resolution of sub 1ms should be ms
PASS: normal resolution of sub 10ms should be ms
PASS: normal resolution of sub 100ms should be ms
@@ -25,6 +26,7 @@ PASS: normal resolution of greater than 1min but sub 1hr should be minutes
PASS: normal resolution of greater than 1hr but sub 1 day should be hrs
PASS: normal resolution of greater than 1 day should be days
PASS: normal resolution of greater than 1 day should be days
PASS: high resolution of 0ms should be ms with no decimals
PASS: high resolution of sub 1ms should be ms with decimals
PASS: high resolution of sub 10ms should be ms with decimals
PASS: high resolution of sub 100ms should be ms with decimals
@@ -46,3 +48,6 @@ PASS: high resolution of sub 10k should be kilobytes
PASS: high resolution of sub 10mb should be megabytes
PASS: high resolution of greater than 10mb should be megabytes

-- Running test case: Number.percentageString
PASS: precision should default to 1 if unspecified

@@ -32,6 +32,7 @@
name: "Number.secondsToString",
test: () => {
// Normal resolution.
InspectorTest.expectThat(Number.secondsToString(0, false) === "0ms", "normal resolution of 0ms should be ms with no decimals");
InspectorTest.expectThat(Number.secondsToString(0.000123456, false) === "0.12ms", "normal resolution of sub 1ms should be ms");
InspectorTest.expectThat(Number.secondsToString(0.00123456, false) === "1.23ms", "normal resolution of sub 10ms should be ms");
InspectorTest.expectThat(Number.secondsToString(0.0123456, false) === "12.3ms", "normal resolution of sub 100ms should be ms");
@@ -45,6 +46,7 @@
InspectorTest.expectThat(Number.secondsToString(1234567, false) === "14.3 days", "normal resolution of greater than 1 day should be days");

// High resolution.
InspectorTest.expectThat(Number.secondsToString(0, true) === "0ms", "high resolution of 0ms should be ms with no decimals");
InspectorTest.expectThat(Number.secondsToString(0.000123456, true) === "0.123ms", "high resolution of sub 1ms should be ms with decimals");
InspectorTest.expectThat(Number.secondsToString(0.00123456, true) === "1.235ms", "high resolution of sub 10ms should be ms with decimals");
InspectorTest.expectThat(Number.secondsToString(0.0123456, true) === "12.35ms", "high resolution of sub 100ms should be ms with decimals");
@@ -82,6 +84,15 @@
}
});

suite.addTestCase({
name: "Number.percentageString",
test: () => {
InspectorTest.expectThat(Number.percentageString(1 / 3) === "33.3%", "precision should default to 1 if unspecified");

return true;
}
});

suite.runTestCasesAndFinish();
}
</script>
@@ -0,0 +1,22 @@

== Running test suite: StringUtilities
-- Running test case: String.format
PASS: float format specifier with no sub-specifier should show 6 decimal digits
PASS: float format specifier with precision 0 should show 0 decimal digits
PASS: float format specifier with precision 1 should show 1 decimal digit
PASS: float format specifier with precision 2 should show 2 decimal digits
PASS: float format specifier with precision 3 should show 3 decimal digits
PASS: float format specifier with precision 4 should show 4 decimal digits
PASS: float format specifier with precision 5 should show 5 decimal digits
PASS: float format specifier with precision 6 should show 6 decimal digits
PASS: float format specifier with precision 7 should show 7 decimal digits
PASS: float format specifier with precision 8 should show 8 decimal digits
PASS: float format specifier with precision 9 should show 9 decimal digits
PASS: float format specifier with string argument should attempt conversion to float
PASS: float format specifier with Infinity argument should show "∞"
PASS: float format specifier with NaN argument should show "NaN"
PASS: integer format specifier with float argument should convert to integer
PASS: integer format specifier with string argument should attempt conversion to integer
PASS: integer format specifier with Infinity argument should show "NaN"
PASS: integer format specifier with NaN argument should show "NaN"

@@ -0,0 +1,42 @@
<!doctype html>
<html>
<head>
<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
<script>
function test()
{
let suite = InspectorTest.createSyncSuite("StringUtilities");

suite.addTestCase({
name: "String.format",
test: () => {
InspectorTest.expectThat("%f".format(1.23456789) === "1.234568", "float format specifier with no sub-specifier should show 6 decimal digits");
InspectorTest.expectThat("%.0f".format(1.23456789) === "1", "float format specifier with precision 0 should show 0 decimal digits");
InspectorTest.expectThat("%.1f".format(1.23456789) === "1.2", "float format specifier with precision 1 should show 1 decimal digit");
InspectorTest.expectThat("%.2f".format(1.23456789) === "1.23", "float format specifier with precision 2 should show 2 decimal digits");
InspectorTest.expectThat("%.3f".format(1.23456789) === "1.235", "float format specifier with precision 3 should show 3 decimal digits");
InspectorTest.expectThat("%.4f".format(1.23456789) === "1.2346", "float format specifier with precision 4 should show 4 decimal digits");
InspectorTest.expectThat("%.5f".format(1.23456789) === "1.23457", "float format specifier with precision 5 should show 5 decimal digits");
InspectorTest.expectThat("%.6f".format(1.23456789) === "1.234568", "float format specifier with precision 6 should show 6 decimal digits");
InspectorTest.expectThat("%.7f".format(1.23456789) === "1.2345679", "float format specifier with precision 7 should show 7 decimal digits");
InspectorTest.expectThat("%.8f".format(1.23456789) === "1.23456789", "float format specifier with precision 8 should show 8 decimal digits");
InspectorTest.expectThat("%.9f".format(1.23456789) === "1.234567890", "float format specifier with precision 9 should show 9 decimal digits");
InspectorTest.expectThat("%f".format("1.23456789") === "1.234568", "float format specifier with string argument should attempt conversion to float");
InspectorTest.expectThat("%f".format(Infinity) === "\u221E", "float format specifier with Infinity argument should show \"\u221E\"");
InspectorTest.expectThat("%f".format(NaN) === "NaN", "float format specifier with NaN argument should show \"NaN\"");

InspectorTest.expectThat("%d".format(137.1) === "137", "integer format specifier with float argument should convert to integer");
InspectorTest.expectThat("%d".format("137") === "137", "integer format specifier with string argument should attempt conversion to integer");
InspectorTest.expectThat("%d".format(Infinity) === "NaN", "integer format specifier with Infinity argument should show \"NaN\"");
InspectorTest.expectThat("%d".format(NaN) === "NaN", "integer format specifier with NaN argument should show \"NaN\"");
return true;
}
});

suite.runTestCasesAndFinish();
}
</script>
</head>
<body onLoad="runTest()">
</body>
</html>
@@ -1,3 +1,60 @@
2016-04-16 Matt Baker <mattbaker@apple.com>

Web Inspector: Adopt Number.prototype.toLocaleString For All Sizes and Times
https://bugs.webkit.org/show_bug.cgi?id=152033
<rdar://problem/23815589>

Reviewed by Timothy Hatcher.

Update string formatters to localize float and percentage strings. Hook up
console message formatters to use String.standardFormatters so that console
statements (e.g. console.log("%.3f", 3.14159)) are properly formatted.

* Localizations/en.lproj/localizedStrings.js:
* UserInterface/Base/Utilities.js:
(value):
tokenizeFormatString should default to 6 digits when no precision
sub-specifier is provided.

percentageString should localize formatting, and take a fraction value
(0 to 1) instead of a percentage.

secondsToString should perform special-case formatting for zero values
("0ms") instead of the general purpose float formatter.

(value.d):
Switch to parseInt to floor floating point values and support numeric strings.
Return NaN instead of zero when passed a value that can't be converted to integer.

(value.f):
Switch to parseFloat to support numeric strings, and localize formatting.
Remove precision check, as it will never be less than zero. Return NaN
instead of zero when passed a value that can't be converted to float.

(prettyFunctionName):
Convert substitutions (an arguments object) to an array before calling join.

* UserInterface/Views/ConsoleMessageView.js:
(WebInspector.ConsoleMessageView.prototype._formatWithSubstitutionString.floatFormatter):
Use String.standardFormatters.f.

(WebInspector.ConsoleMessageView.prototype._formatWithSubstitutionString.integerFormatter):
Use String.standardFormatters.d.

* UserInterface/Views/LayoutTimelineDataGridNode.js:
(WebInspector.LayoutTimelineDataGridNode.prototype.createCellContent):
(WebInspector.LayoutTimelineDataGridNode):
Use integer formatting for pixel values.

* UserInterface/Views/ProfileDataGridNode.js:
(WebInspector.ProfileDataGridNode.prototype._recalculateData):
(WebInspector.ProfileDataGridNode.prototype._totalTimeContent):
Treat percentage as a fraction from 0 to 1.

* UserInterface/Views/ResourceDetailsSidebarPanel.js:
(WebInspector.ResourceDetailsSidebarPanel.prototype._refreshImageSizeSection):
Use integer formatting for pixel values.

2016-04-16 Matt Baker <mattbaker@apple.com>

display:inline on the tbody is causing the width of the iframe to be shrunk to the minimum size of its text.
Binary file not shown.
@@ -640,15 +640,17 @@ Object.defineProperty(String, "tokenizeFormatString",
}
}

var precision = -1;
const defaultPrecision = 6;

let precision = defaultPrecision;
if (format[index] === ".") {
// This is a precision specifier. If no digit follows the ".",
// then the precision should be zero.
// then use the default precision of six digits (ISO C99 specification).
++index;

precision = parseInt(format.substring(index), 10);
if (isNaN(precision))
precision = 0;
precision = defaultPrecision;

while (!isNaN(format[index]))
++index;
@@ -715,14 +717,21 @@ Object.defineProperty(String, "standardFormatters",
value: {
d: function(substitution)
{
return !isNaN(substitution) ? substitution : 0;
return parseInt(substitution);
},

f: function(substitution, token)
{
if (substitution && token.precision > -1)
substitution = substitution.toFixed(token.precision);
return !isNaN(substitution) ? substitution : (token.precision > -1 ? Number(0).toFixed(token.precision) : 0);
let value = parseFloat(substitution);
if (isNaN(value))
return NaN;

let options = {
minimumFractionDigits: token.precision,
maximumFractionDigits: token.precision,
useGrouping: false
};
return value.toLocaleString(undefined, options);
},

s: function(substitution)
@@ -741,7 +750,7 @@ Object.defineProperty(String, "format",

function prettyFunctionName()
{
return "String.format(\"" + format + "\", \"" + substitutions.join("\", \"") + "\")";
return "String.format(\"" + format + "\", \"" + Array.from(substitutions).join("\", \"") + "\")";
}

function warn(msg)
@@ -912,10 +921,10 @@ Object.defineProperty(Number, "constrain",

Object.defineProperty(Number, "percentageString",
{
value: function(percent, precision = 1)
value: function(fraction, precision = 1)
{
console.assert(percent >= 0 && percent <= 100);
return percent.toFixed(precision) + "%";
console.assert(fraction >= 0 && fraction <= 1);
return fraction.toLocaleString(undefined, {minimumFractionDigits: precision, style: "percent"});
}
});

@@ -936,6 +945,8 @@ Object.defineProperty(Number, "secondsToString",
value: function(seconds, higherResolution)
{
let ms = seconds * 1000;
if (!ms)
return WebInspector.UIString("%.0fms").format(0);

if (Math.abs(ms) < 10) {
if (higherResolution)
@@ -599,18 +599,16 @@ WebInspector.ConsoleMessageView = class ConsoleMessageView extends WebInspector.
return obj.description;
}

function floatFormatter(obj)
function floatFormatter(obj, token)
{
if (typeof obj.value !== "number")
return parseFloat(obj.description);
return obj.value;
let value = typeof obj.value === "number" ? obj.value : obj.description;
return String.standardFormatters.f(value, token);
}

function integerFormatter(obj)
{
if (typeof obj.value !== "number")
return parseInt(obj.description);
return Math.floor(obj.value);
let value = typeof obj.value === "number" ? obj.value : obj.description;
return String.standardFormatters.d(value);
}

var currentStyle = null;
@@ -68,10 +68,10 @@ WebInspector.LayoutTimelineDataGridNode = class LayoutTimelineDataGridNode exten

case "width":
case "height":
return isNaN(value) ? emDash : WebInspector.UIString("%fpx").format(value);
return isNaN(value) ? emDash : WebInspector.UIString("%dpx").format(value);

case "area":
return isNaN(value) ? emDash : WebInspector.UIString("%fpx²").format(value);
return isNaN(value) ? emDash : WebInspector.UIString("%dpx²").format(value);

case "startTime":
return isNaN(value) ? emDash : Number.secondsToString(value - this._baseStartTime, true);
@@ -188,22 +188,22 @@ WebInspector.ProfileDataGridNode = class ProfileDataGridNode extends WebInspecto

let totalTime = duration;
let selfTime = leafDuration + this._extraSelfTimeFromChargedChildren;
let percent = (totalTime / this._tree.totalSampleTime) * 100;
let fraction = totalTime / this._tree.totalSampleTime;

this._data = {totalTime, selfTime, percent};
this._data = {totalTime, selfTime, fraction};
}

_totalTimeContent()
{
let {totalTime, percent} = this._data;
let {totalTime, fraction} = this._data;

let fragment = document.createDocumentFragment();
let timeElement = fragment.appendChild(document.createElement("span"));
timeElement.classList.add("time");
timeElement.textContent = Number.secondsToMillisecondsString(totalTime);
let percentElement = fragment.appendChild(document.createElement("span"));
percentElement.classList.add("percentage");
percentElement.textContent = Number.percentageString(percent);
percentElement.textContent = Number.percentageString(fraction);
return fragment;
}

@@ -416,8 +416,8 @@ WebInspector.ResourceDetailsSidebarPanel = class ResourceDetailsSidebarPanel ext

// Get the metrics for this resource and fill in the metrics rows with that information.
resource.getImageSize(function(size) {
this._imageWidthRow.value = WebInspector.UIString("%fpx").format(size.width);
this._imageHeightRow.value = WebInspector.UIString("%fpx").format(size.height);
this._imageWidthRow.value = WebInspector.UIString("%dpx").format(size.width);
this._imageHeightRow.value = WebInspector.UIString("%dpx").format(size.height);
}.bind(this));
}

0 comments on commit a18087b

Please sign in to comment.