Implement 'URLSearchParams'
This patch implements the 'URLSearchParams' interface (https://url.spec.whatwg.org/#interface-urlsearchparams), extracted
from Sigbjorn's excellent https://codereview.chromium.org/143313002/.
The bits of that patch which integrate 'URLSearchParams' with 'URL' and
'URLUtils' are dependent on Node moving to Oilpan, and will be added in
a subsequent patch.
Intent to Implement and Ship: https://groups.google.com/a/chromium.org/d/msg/blink-dev/grHZDbldP04/JdsoQ169AQAJ
BUG=303152
Review URL: https://codereview.chromium.org/1442643008
Cr-Commit-Position: refs/heads/master@{#360086}
diff --git a/third_party/WebKit/LayoutTests/fast/domurl/resources/urlsearchparams-worker.js b/third_party/WebKit/LayoutTests/fast/domurl/resources/urlsearchparams-worker.js
new file mode 100644
index 0000000..999ddfc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/domurl/resources/urlsearchparams-worker.js
@@ -0,0 +1,14 @@
+importScripts("../../../resources/testharness.js")
+
+test(function() {
+ assert_true('URLSearchParams' in self);
+ assert_true('toString' in URLSearchParams.prototype);
+ assert_true('append' in URLSearchParams.prototype);
+ assert_true('delete' in URLSearchParams.prototype);
+ assert_true('get' in URLSearchParams.prototype);
+ assert_true('getAll' in URLSearchParams.prototype);
+ assert_true('has' in URLSearchParams.prototype);
+ assert_true('set' in URLSearchParams.prototype);
+}, 'URLSearchParams interface');
+
+done();
diff --git a/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-append.html b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-append.html
new file mode 100644
index 0000000..74071f1a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-append.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf8">
+<link rel="help" href="https://url.spec.whatwg.org/#dom-urlsearchparams-append">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/testharness-extras.js"></script>
+<script>
+test(function() {
+ var params = new URLSearchParams();
+ params.append('a', 'b');
+ assert_equals(params + '', 'a=b');
+ params.append('a', 'b');
+ assert_equals(params + '', 'a=b&a=b');
+ params.append('a', 'c');
+ assert_equals(params + '', 'a=b&a=b&a=c');
+}, 'Append same name');
+
+test(function() {
+ var params = new URLSearchParams();
+ params.append('', '');
+ assert_equals(params + '', '=');
+ params.append('', '');
+ assert_equals(params + '', '=&=');
+}, 'Append empty strings');
+
+test(function() {
+ var params = new URLSearchParams();
+ params.append(null, null);
+ assert_equals(params + '', 'null=null');
+ params.append(null, null);
+ assert_equals(params + '', 'null=null&null=null');
+}, 'Append null');
+
+test(function() {
+ var params = new URLSearchParams();
+ params.append('first', 1);
+ params.append('second', 2);
+ params.append('third', '');
+ params.append('first', 10);
+ assert_true(params.has('first'), 'Search params object has name "first"');
+ assert_equals(params.get('first'), '1', 'Search params object has name "first" with value "1"');
+ assert_equals(params.get('second'), '2', 'Search params object has name "second" with value "2"');
+ assert_equals(params.get('third'), '', 'Search params object has name "third" with value ""');
+ params.append('first', 10);
+ assert_equals(params.get('first'), '1', 'Search params object has name "first" with value "1"');
+}, 'Append multiple');
+</script>
+</head>
+</html>
diff --git a/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-constructor.html b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-constructor.html
new file mode 100644
index 0000000..33a446e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-constructor.html
@@ -0,0 +1,163 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf8">
+<link rel="help" href="https://url.spec.whatwg.org/#urlsearchparams">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/testharness-extras.js"></script>
+<script>
+function assert_type_error(f, msg) {
+ assert_throws(TypeError(), f, msg);
+}
+
+// Consider upstreaming something kinda like this.
+function assert_iteration_equals(iteration, expectation) {
+ var actual = [...iteration];
+ assert_equals(actual.length, expectation.length);
+ for (var i = 0; i < actual.length; i++) {
+ assert_equals(typeof actual[i], typeof expectation[i]);
+ if (actual[i] instanceof Array)
+ assert_array_equals(actual[i], expectation[i]);
+ else
+ assert_equals(actual[i], expectation[i]);
+ }
+}
+
+test(function() {
+ var params = new URLSearchParams();
+ assert_equals(params + '', '');
+ params = new URLSearchParams('');
+ assert_equals(params + '', '');
+ params = new URLSearchParams('a=b');
+ assert_equals(params + '', 'a=b');
+ params = new URLSearchParams(params);
+ assert_equals(params + '', 'a=b');
+}, 'Basic URLSearchParams construction');
+
+test(function() {
+ assert_type_error(function () { URLSearchParams(); }, 'Failed to construct \'URLSearchParams\': Please use the \'new\' operator, this DOM object constructor cannot be called as a function.');
+
+ var params = new URLSearchParams('');
+ assert_not_equals(params, null, 'constructor returned non-null value.');
+ assert_equals(params.__proto__, URLSearchParams.prototype, 'expected URLSearchParams.prototype as prototype.');
+ params = new URLSearchParams({});
+ assert_equals(params + '', '%5Bobject+Object%5D=');
+}, 'URLSearchParams constructor, empty.');
+
+test(function() {
+ var params = new URLSearchParams('a=b');
+ assert_not_equals(params, null, 'constructor returned non-null value.');
+ assert_true(params.has('a'), 'Search params object has name "a"');
+ assert_false(params.has('b'), 'Search params object has not got name "b"');
+
+ var params = new URLSearchParams('a=b&c');
+ assert_not_equals(params, null, 'constructor returned non-null value.');
+ assert_true(params.has('a'), 'Search params object has name "a"');
+ assert_true(params.has('c'), 'Search params object has name "c"');
+
+ var params = new URLSearchParams('&a&&& &&&&&a+b=& c&m%c3%b8%c3%b8');
+ assert_not_equals(params, null, 'constructor returned non-null value.');
+ assert_true(params.has('a'), 'Search params object has name "a"');
+ assert_true(params.has('a b'), 'Search params object has name "a b"');
+ assert_true(params.has(' '), 'Search params object has name " "');
+ assert_false(params.has('c'), 'Search params object did not have the name "c"');
+ assert_true(params.has(' c'), 'Search params object has name " c"');
+ assert_true(params.has('møø'), 'Search params object has name "møø"');
+}, 'URLSearchParams constructor, string.');
+
+test(function() {
+ var seed = new URLSearchParams('a=b&c=d');
+ var params = new URLSearchParams(seed);
+ assert_not_equals(params, null, 'constructor returned non-null value.');
+ assert_iteration_equals(params, [ ['a', 'b' ], [ 'c', 'd' ] ]);
+ // The name-value pairs are copied when created; later updates
+ // should not be observable.
+ seed.append('e', 'f');
+ assert_false(params.has('e'));
+ params.append('g', 'h');
+ assert_false(seed.has('g'));
+}, 'URLSearchParams constructor, object.');
+
+test(function() {
+ var params = new URLSearchParams('a=b+c');
+ assert_equals(params.get('a'), 'b c');
+ params = new URLSearchParams('a+b=c');
+ assert_equals(params.get('a b'), 'c');
+}, 'Parse +');
+
+test(function() {
+ var params = new URLSearchParams('a=b%2Bc');
+ assert_equals(params.get('a'), 'b+c');
+ params = new URLSearchParams('a%2Bb=c');
+ assert_equals(params.get('a+b'), 'c');
+}, 'Parse %2B');
+
+test(function() {
+ var params = new URLSearchParams('a=b c');
+ assert_equals(params.get('a'), 'b c');
+ params = new URLSearchParams('a b=c');
+ assert_equals(params.get('a b'), 'c');
+}, 'Parse space');
+
+test(function() {
+ var params = new URLSearchParams('a=b%20c');
+ assert_equals(params.get('a'), 'b c');
+ params = new URLSearchParams('a%20b=c');
+ assert_equals(params.get('a b'), 'c');
+}, 'Parse %20');
+
+test(function() {
+ var params = new URLSearchParams('a=b\0c');
+ assert_equals(params.get('a'), 'b\0c');
+ params = new URLSearchParams('a\0b=c');
+ assert_equals(params.get('a\0b'), 'c');
+}, 'Parse \\0');
+
+test(function() {
+ var params = new URLSearchParams('a=b%00c');
+ assert_equals(params.get('a'), 'b\0c');
+ params = new URLSearchParams('a%00b=c');
+ assert_equals(params.get('a\0b'), 'c');
+}, 'Parse %00');
+
+test(function() {
+ var params = new URLSearchParams('a=b\u2384');
+ assert_equals(params.get('a'), 'b\u2384');
+ params = new URLSearchParams('a\u2384b=c');
+ assert_equals(params.get('a\u2384b'), 'c');
+}, 'Parse \u2384'); // Unicode Character 'COMPOSITION SYMBOL' (U+2384)
+
+test(function() {
+ var params = new URLSearchParams('a=b%e2%8e%84');
+ assert_equals(params.get('a'), 'b\u2384');
+ params = new URLSearchParams('a%e2%8e%84b=c');
+ assert_equals(params.get('a\u2384b'), 'c');
+}, 'Parse %e2%8e%84'); // Unicode Character 'COMPOSITION SYMBOL' (U+2384)
+
+test(function() {
+ var params = new URLSearchParams('a=b\uD83D\uDCA9c');
+ assert_equals(params.get('a'), 'b\uD83D\uDCA9c');
+ params = new URLSearchParams('a\uD83D\uDCA9b=c');
+ assert_equals(params.get('a\uD83D\uDCA9b'), 'c');
+}, 'Parse \uD83D\uDCA9'); // Unicode Character 'PILE OF POO' (U+1F4A9)
+
+test(function() {
+ var params = new URLSearchParams('a=b%f0%9f%92%a9c');
+ assert_equals(params.get('a'), 'b\uD83D\uDCA9c');
+ params = new URLSearchParams('a%f0%9f%92%a9b=c');
+ assert_equals(params.get('a\uD83D\uDCA9b'), 'c');
+}, 'Parse %f0%9f%92%a9'); // Unicode Character 'PILE OF POO' (U+1F4A9)
+
+test(function() {
+ var params = new URLSearchParams('=');
+ assert_equals(params.toString(), '=');
+}, 'Parse =');
+
+test(function() {
+ var params = new URLSearchParams('foobar=a\nb');
+ assert_equals(params.toString(), 'foobar=a%0Ab');
+}, 'Parse \\n');
+</script>
+</head>
+</html>
diff --git a/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-delete.html b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-delete.html
new file mode 100644
index 0000000..0ffae60
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-delete.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf8">
+<link rel="help" href="https://url.spec.whatwg.org/#dom-urlsearchparams-delete">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/testharness-extras.js"></script>
+<script>
+test(function() {
+ var params = new URLSearchParams('a=b&c=d');
+ params.delete('a');
+ assert_equals(params + '', 'c=d');
+ params = new URLSearchParams('a=a&b=b&a=a&c=c');
+ params.delete('a');
+ assert_equals(params + '', 'b=b&c=c');
+ params = new URLSearchParams('a=a&=&b=b&c=c');
+ params.delete('');
+ assert_equals(params + '', 'a=a&b=b&c=c');
+ params = new URLSearchParams('a=a&null=null&b=b');
+ params.delete(null);
+ assert_equals(params + '', 'a=a&b=b');
+ params = new URLSearchParams('a=a&undefined=undefined&b=b');
+ params.delete(undefined);
+ assert_equals(params + '', 'a=a&b=b');
+}, 'Delete basics');
+
+test(function() {
+ var params = new URLSearchParams();
+ params.append('first', 1);
+ assert_true(params.has('first'), 'Search params object has name "first"');
+ assert_equals(params.get('first'), '1', 'Search params object has name "first" with value "1"');
+ params.delete('first');
+ assert_false(params.has('first'), 'Search params object has no "first" name');
+ params.append('first', 1);
+ params.append('first', 10);
+ params.delete('first');
+ assert_false(params.has('first'), 'Search params object has no "first" name');
+}, 'Deleting appended multiple');
+</script>
+</head>
+</html>
diff --git a/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-get.html b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-get.html
new file mode 100644
index 0000000..91ef209a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-get.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf8">
+<link rel="help" href="https://url.spec.whatwg.org/#dom-urlsearchparams-get">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/testharness-extras.js"></script>
+<script>
+test(function() {
+ var params = new URLSearchParams('a=b&c=d');
+ assert_equals(params.get('a'), 'b');
+ assert_equals(params.get('c'), 'd');
+ assert_equals(params.get('e'), null);
+ params = new URLSearchParams('a=b&c=d&a=e');
+ assert_equals(params.get('a'), 'b');
+ params = new URLSearchParams('=b&c=d');
+ assert_equals(params.get(''), 'b');
+ params = new URLSearchParams('a=&c=d&a=e');
+ assert_equals(params.get('a'), '');
+}, 'Get basics');
+
+test(function() {
+ var params = new URLSearchParams('first=second&third&&');
+ assert_true(params != null, 'constructor returned non-null value.');
+ assert_true(params.has('first'), 'Search params object has name "first"');
+ assert_equals(params.get('first'), 'second', 'Search params object has name "first" with value "second"');
+ assert_equals(params.get('third'), '', 'Search params object has name "third" with the empty value.');
+ assert_equals(params.get('fourth'), null, 'Search params object has no "fourth" name and value.');
+}, 'More get() basics');
+</script>
+</head>
+</html>
diff --git a/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-getall.html b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-getall.html
new file mode 100644
index 0000000..4454e2b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-getall.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf8">
+<link rel="help" href="https://url.spec.whatwg.org/#dom-urlsearchparams-getAll">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/testharness-extras.js"></script>
+<script>
+test(function() {
+ var params = new URLSearchParams('a=b&c=d');
+ assert_array_equals(params.getAll('a'), ['b']);
+ assert_array_equals(params.getAll('c'), ['d']);
+ assert_array_equals(params.getAll('e'), []);
+ params = new URLSearchParams('a=b&c=d&a=e');
+ assert_array_equals(params.getAll('a'), ['b', 'e']);
+ params = new URLSearchParams('=b&c=d');
+ assert_array_equals(params.getAll(''), ['b']);
+ params = new URLSearchParams('a=&c=d&a=e');
+ assert_array_equals(params.getAll('a'), ['', 'e']);
+}, 'getAll() basics');
+
+test(function() {
+ var params = new URLSearchParams('a=1&a=2&a=3&a');
+ assert_true(params.has('a'), 'Search params object has name "a"');
+ var matches = params.getAll('a');
+ assert_true(matches && matches.length == 4, 'Search params object has values for name "a"');
+ assert_array_equals(matches, ['1', '2', '3', ''], 'Search params object has expected name "a" values');
+ params.set('a', 'one');
+ assert_equals(params.get('a'), 'one', 'Search params object has name "a" with value "one"');
+ var matches = params.getAll('a');
+ assert_true(matches && matches.length == 1, 'Search params object has values for name "a"');
+ assert_array_equals(matches, ['one'], 'Search params object has expected name "a" values');
+}, 'getAll() multiples');
+</script>
+</head>
+</html>
diff --git a/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-has.html b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-has.html
new file mode 100644
index 0000000..e020253
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-has.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf8">
+<link rel="help" href="https://url.spec.whatwg.org/#dom-urlsearchparams-has">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/testharness-extras.js"></script>
+<script>
+test(function() {
+ var params = new URLSearchParams('a=b&c=d');
+ assert_true(params.has('a'));
+ assert_true(params.has('c'));
+ assert_false(params.has('e'));
+ params = new URLSearchParams('a=b&c=d&a=e');
+ assert_true(params.has('a'));
+ params = new URLSearchParams('=b&c=d');
+ assert_true(params.has(''));
+ params = new URLSearchParams('null=a');
+ assert_true(params.has(null));
+}, 'has() basics');
+
+test(function() {
+ var params = new URLSearchParams('a=b&c=d&&');
+ params.append('first', 1);
+ params.append('first', 2);
+ assert_true(params.has('a'), 'Search params object has name "a"');
+ assert_true(params.has('c'), 'Search params object has name "c"');
+ assert_true(params.has('first'), 'Search params object has name "first"');
+ assert_false(params.has('d'), 'Search params object has no name "d"');
+ params.delete('first');
+ assert_false(params.has('first'), 'Search params object has no name "first"');
+}, 'has() following delete()');
+</script>
+</head>
+</html>
diff --git a/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-iterable.html b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-iterable.html
new file mode 100644
index 0000000..3e7b0f0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-iterable.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf8">
+<link rel="help" href="https://url.spec.whatwg.org/#dom-urlsearchparams">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+var expectedValues = {
+ 'a': '1',
+ 'b': '2',
+ 'c': '3'
+};
+
+var params = new URLSearchParams();
+params.append('a', '1');
+params.append('b', '2');
+params.append('c', '3');
+
+test(function() {
+ for (var param of params) {
+ var key = param[0];
+ var value = param[1];
+ assert_true(key in expectedValues);
+ assert_equals(params.get(key), expectedValues[key]);
+ assert_equals(value, expectedValues[key]);
+ }
+}, 'for...of Iteration');
+
+test(function() {
+ for (var key of params.keys()) {
+ assert_true(key in expectedValues);
+ assert_equals(params.get(key), expectedValues[key]);
+ }
+}, 'keys');
+
+test(function() {
+ var expectedKeys = {};
+ for (var key in expectedValues)
+ expectedKeys[expectedValues[key]] = key;
+
+ for (var value of params.values())
+ assert_true(value in expectedKeys);
+}, 'values');
+
+test(function () {
+ for (var param of params.entries()) {
+ var key = param[0];
+ var value = param[1];
+ assert_true(key in expectedValues);
+ assert_equals(params.get(key), expectedValues[key]);
+ assert_equals(value, expectedValues[key]);
+ }
+}, 'entries');
+
+test(function () {
+ params.forEach(function (value, key, paramsObject) {
+ assert_true(key in expectedValues);
+ assert_equals(params.get(key), expectedValues[key]);
+ assert_equals(value, expectedValues[key]);
+ assert_equals(paramsObject, params);
+ });
+}, 'forEach');
+</script>
+</head>
+</html>
diff --git a/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-set.html b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-set.html
new file mode 100644
index 0000000..bb9b8a1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-set.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf8">
+<link rel="help" href="https://url.spec.whatwg.org/#dom-urlsearchparams-set">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/testharness-extras.js"></script>
+<script>
+test(function() {
+ var params = new URLSearchParams('a=b&c=d');
+ params.set('a', 'B');
+ assert_equals(params + '', 'a=B&c=d');
+ params = new URLSearchParams('a=b&c=d&a=e');
+ params.set('a', 'B');
+ assert_equals(params + '', 'a=B&c=d')
+ params.set('e', 'f');
+ assert_equals(params + '', 'a=B&c=d&e=f')
+}, 'set() basics');
+
+test(function() {
+ var params = new URLSearchParams('a=1&a=2&a=3');
+ assert_true(params.has('a'), 'Search params object has name "a"');
+ assert_equals(params.get('a'), '1', 'Search params object has name "a" with value "1"');
+ params.set('first', 4);
+ assert_true(params.has('a'), 'Search params object has name "a"');
+ assert_equals(params.get('a'), '1', 'Search params object has name "a" with value "1"');
+ assert_equals(params + '', 'a=1&a=2&a=3&first=4');
+ params.set('a', 4);
+ assert_true(params.has('a'), 'Search params object has name "a"');
+ assert_equals(params.get('a'), '4', 'Search params object has name "a" with value "4"');
+ assert_equals(params + '', 'a=4&first=4');
+}, 'set() replaces multiple values');
+</script>
+</head>
+</html>
+
diff --git a/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-stringifier-expected.txt b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-stringifier-expected.txt
new file mode 100644
index 0000000..397c97b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-stringifier-expected.txt
@@ -0,0 +1,270 @@
+This is a testharness.js-based test.
+PASS Serialize space
+PASS Serialize empty value
+PASS Serialize empty name
+PASS Serialize empty name and value
+PASS Serialize +
+PASS Serialize =
+PASS Serialize &
+PASS Serialize U+0000 -> '\0'
+PASS Serialize U+0001 -> ''
+PASS Serialize U+0002 -> ''
+PASS Serialize U+0003 -> ''
+PASS Serialize U+0004 -> ''
+PASS Serialize U+0005 -> ''
+PASS Serialize U+0006 -> ''
+PASS Serialize U+0007 -> ''
+PASS Serialize U+0008 -> ''
+PASS Serialize U+0009 -> ' '
+PASS Serialize U+000A -> '
+'
+PASS Serialize U+000B -> ''
+PASS Serialize U+000C -> ''
+PASS Serialize U+000D -> '\r'
+PASS Serialize U+000E -> ''
+PASS Serialize U+000F -> ''
+PASS Serialize U+0010 -> ''
+PASS Serialize U+0011 -> ''
+PASS Serialize U+0012 -> ''
+PASS Serialize U+0013 -> ''
+PASS Serialize U+0014 -> ''
+PASS Serialize U+0015 -> ''
+PASS Serialize U+0016 -> ''
+PASS Serialize U+0017 -> ''
+PASS Serialize U+0018 -> ''
+PASS Serialize U+0019 -> ''
+PASS Serialize U+001A -> ''
+PASS Serialize U+001B -> ''
+PASS Serialize U+001C -> ''
+PASS Serialize U+001D -> ''
+PASS Serialize U+001E -> ''
+PASS Serialize U+001F -> ''
+PASS Serialize U+0020 -> ' '
+FAIL Serialize U+0021 -> '!' assert_equals: expected "33=%21" but got "33=!"
+PASS Serialize U+0022 -> '"'
+PASS Serialize U+0023 -> '#'
+PASS Serialize U+0024 -> '$'
+PASS Serialize U+0025 -> '%'
+PASS Serialize U+0026 -> '&'
+PASS Serialize U+0027 -> '''
+FAIL Serialize U+0028 -> '(' assert_equals: expected "40=%28" but got "40=("
+FAIL Serialize U+0029 -> ')' assert_equals: expected "41=%29" but got "41=)"
+PASS Serialize U+002A -> '*'
+PASS Serialize U+002B -> '+'
+PASS Serialize U+002C -> ','
+PASS Serialize U+002D -> '-'
+PASS Serialize U+002E -> '.'
+FAIL Serialize U+002F -> '/' assert_equals: expected "47=%2F" but got "47=/"
+PASS Serialize U+0030 -> '0'
+PASS Serialize U+0031 -> '1'
+PASS Serialize U+0032 -> '2'
+PASS Serialize U+0033 -> '3'
+PASS Serialize U+0034 -> '4'
+PASS Serialize U+0035 -> '5'
+PASS Serialize U+0036 -> '6'
+PASS Serialize U+0037 -> '7'
+PASS Serialize U+0038 -> '8'
+PASS Serialize U+0039 -> '9'
+PASS Serialize U+003A -> ':'
+PASS Serialize U+003B -> ';'
+PASS Serialize U+003C -> '<'
+PASS Serialize U+003D -> '='
+PASS Serialize U+003E -> '>'
+PASS Serialize U+003F -> '?'
+PASS Serialize U+0040 -> '@'
+PASS Serialize U+0041 -> 'A'
+PASS Serialize U+0042 -> 'B'
+PASS Serialize U+0043 -> 'C'
+PASS Serialize U+0044 -> 'D'
+PASS Serialize U+0045 -> 'E'
+PASS Serialize U+0046 -> 'F'
+PASS Serialize U+0047 -> 'G'
+PASS Serialize U+0048 -> 'H'
+PASS Serialize U+0049 -> 'I'
+PASS Serialize U+004A -> 'J'
+PASS Serialize U+004B -> 'K'
+PASS Serialize U+004C -> 'L'
+PASS Serialize U+004D -> 'M'
+PASS Serialize U+004E -> 'N'
+PASS Serialize U+004F -> 'O'
+PASS Serialize U+0050 -> 'P'
+PASS Serialize U+0051 -> 'Q'
+PASS Serialize U+0052 -> 'R'
+PASS Serialize U+0053 -> 'S'
+PASS Serialize U+0054 -> 'T'
+PASS Serialize U+0055 -> 'U'
+PASS Serialize U+0056 -> 'V'
+PASS Serialize U+0057 -> 'W'
+PASS Serialize U+0058 -> 'X'
+PASS Serialize U+0059 -> 'Y'
+PASS Serialize U+005A -> 'Z'
+PASS Serialize U+005B -> '['
+PASS Serialize U+005C -> '\'
+PASS Serialize U+005D -> ']'
+PASS Serialize U+005E -> '^'
+PASS Serialize U+005F -> '_'
+PASS Serialize U+0060 -> '`'
+PASS Serialize U+0061 -> 'a'
+PASS Serialize U+0062 -> 'b'
+PASS Serialize U+0063 -> 'c'
+PASS Serialize U+0064 -> 'd'
+PASS Serialize U+0065 -> 'e'
+PASS Serialize U+0066 -> 'f'
+PASS Serialize U+0067 -> 'g'
+PASS Serialize U+0068 -> 'h'
+PASS Serialize U+0069 -> 'i'
+PASS Serialize U+006A -> 'j'
+PASS Serialize U+006B -> 'k'
+PASS Serialize U+006C -> 'l'
+PASS Serialize U+006D -> 'm'
+PASS Serialize U+006E -> 'n'
+PASS Serialize U+006F -> 'o'
+PASS Serialize U+0070 -> 'p'
+PASS Serialize U+0071 -> 'q'
+PASS Serialize U+0072 -> 'r'
+PASS Serialize U+0073 -> 's'
+PASS Serialize U+0074 -> 't'
+PASS Serialize U+0075 -> 'u'
+PASS Serialize U+0076 -> 'v'
+PASS Serialize U+0077 -> 'w'
+PASS Serialize U+0078 -> 'x'
+PASS Serialize U+0079 -> 'y'
+PASS Serialize U+007A -> 'z'
+PASS Serialize U+007B -> '{'
+PASS Serialize U+007C -> '|'
+PASS Serialize U+007D -> '}'
+FAIL Serialize U+007E -> '~' assert_equals: expected "126=%7E" but got "126=~"
+PASS Serialize U+007F -> ''
+PASS Serialize U+0080 -> ''
+PASS Serialize U+0081 -> ''
+PASS Serialize U+0082 -> ''
+PASS Serialize U+0083 -> ''
+PASS Serialize U+0084 -> ''
+PASS Serialize U+0085 -> '
'
+PASS Serialize U+0086 -> ''
+PASS Serialize U+0087 -> ''
+PASS Serialize U+0088 -> ''
+PASS Serialize U+0089 -> ''
+PASS Serialize U+008A -> ''
+PASS Serialize U+008B -> ''
+PASS Serialize U+008C -> ''
+PASS Serialize U+008D -> ''
+PASS Serialize U+008E -> ''
+PASS Serialize U+008F -> ''
+PASS Serialize U+0090 -> ''
+PASS Serialize U+0091 -> ''
+PASS Serialize U+0092 -> ''
+PASS Serialize U+0093 -> ''
+PASS Serialize U+0094 -> ''
+PASS Serialize U+0095 -> ''
+PASS Serialize U+0096 -> ''
+PASS Serialize U+0097 -> ''
+PASS Serialize U+0098 -> ''
+PASS Serialize U+0099 -> ''
+PASS Serialize U+009A -> ''
+PASS Serialize U+009B -> ''
+PASS Serialize U+009C -> ''
+PASS Serialize U+009D -> ''
+PASS Serialize U+009E -> ''
+PASS Serialize U+009F -> ''
+PASS Serialize U+00A0 -> ' '
+PASS Serialize U+00A1 -> '¡'
+PASS Serialize U+00A2 -> '¢'
+PASS Serialize U+00A3 -> '£'
+PASS Serialize U+00A4 -> '¤'
+PASS Serialize U+00A5 -> '¥'
+PASS Serialize U+00A6 -> '¦'
+PASS Serialize U+00A7 -> '§'
+PASS Serialize U+00A8 -> '¨'
+PASS Serialize U+00A9 -> '©'
+PASS Serialize U+00AA -> 'ª'
+PASS Serialize U+00AB -> '«'
+PASS Serialize U+00AC -> '¬'
+PASS Serialize U+00AD -> ''
+PASS Serialize U+00AE -> '®'
+PASS Serialize U+00AF -> '¯'
+PASS Serialize U+00B0 -> '°'
+PASS Serialize U+00B1 -> '±'
+PASS Serialize U+00B2 -> '²'
+PASS Serialize U+00B3 -> '³'
+PASS Serialize U+00B4 -> '´'
+PASS Serialize U+00B5 -> 'µ'
+PASS Serialize U+00B6 -> '¶'
+PASS Serialize U+00B7 -> '·'
+PASS Serialize U+00B8 -> '¸'
+PASS Serialize U+00B9 -> '¹'
+PASS Serialize U+00BA -> 'º'
+PASS Serialize U+00BB -> '»'
+PASS Serialize U+00BC -> '¼'
+PASS Serialize U+00BD -> '½'
+PASS Serialize U+00BE -> '¾'
+PASS Serialize U+00BF -> '¿'
+PASS Serialize U+00C0 -> 'À'
+PASS Serialize U+00C1 -> 'Á'
+PASS Serialize U+00C2 -> 'Â'
+PASS Serialize U+00C3 -> 'Ã'
+PASS Serialize U+00C4 -> 'Ä'
+PASS Serialize U+00C5 -> 'Å'
+PASS Serialize U+00C6 -> 'Æ'
+PASS Serialize U+00C7 -> 'Ç'
+PASS Serialize U+00C8 -> 'È'
+PASS Serialize U+00C9 -> 'É'
+PASS Serialize U+00CA -> 'Ê'
+PASS Serialize U+00CB -> 'Ë'
+PASS Serialize U+00CC -> 'Ì'
+PASS Serialize U+00CD -> 'Í'
+PASS Serialize U+00CE -> 'Î'
+PASS Serialize U+00CF -> 'Ï'
+PASS Serialize U+00D0 -> 'Ð'
+PASS Serialize U+00D1 -> 'Ñ'
+PASS Serialize U+00D2 -> 'Ò'
+PASS Serialize U+00D3 -> 'Ó'
+PASS Serialize U+00D4 -> 'Ô'
+PASS Serialize U+00D5 -> 'Õ'
+PASS Serialize U+00D6 -> 'Ö'
+PASS Serialize U+00D7 -> '×'
+PASS Serialize U+00D8 -> 'Ø'
+PASS Serialize U+00D9 -> 'Ù'
+PASS Serialize U+00DA -> 'Ú'
+PASS Serialize U+00DB -> 'Û'
+PASS Serialize U+00DC -> 'Ü'
+PASS Serialize U+00DD -> 'Ý'
+PASS Serialize U+00DE -> 'Þ'
+PASS Serialize U+00DF -> 'ß'
+PASS Serialize U+00E0 -> 'à'
+PASS Serialize U+00E1 -> 'á'
+PASS Serialize U+00E2 -> 'â'
+PASS Serialize U+00E3 -> 'ã'
+PASS Serialize U+00E4 -> 'ä'
+PASS Serialize U+00E5 -> 'å'
+PASS Serialize U+00E6 -> 'æ'
+PASS Serialize U+00E7 -> 'ç'
+PASS Serialize U+00E8 -> 'è'
+PASS Serialize U+00E9 -> 'é'
+PASS Serialize U+00EA -> 'ê'
+PASS Serialize U+00EB -> 'ë'
+PASS Serialize U+00EC -> 'ì'
+PASS Serialize U+00ED -> 'í'
+PASS Serialize U+00EE -> 'î'
+PASS Serialize U+00EF -> 'ï'
+PASS Serialize U+00F0 -> 'ð'
+PASS Serialize U+00F1 -> 'ñ'
+PASS Serialize U+00F2 -> 'ò'
+PASS Serialize U+00F3 -> 'ó'
+PASS Serialize U+00F4 -> 'ô'
+PASS Serialize U+00F5 -> 'õ'
+PASS Serialize U+00F6 -> 'ö'
+PASS Serialize U+00F7 -> '÷'
+PASS Serialize U+00F8 -> 'ø'
+PASS Serialize U+00F9 -> 'ù'
+PASS Serialize U+00FA -> 'ú'
+PASS Serialize U+00FB -> 'û'
+PASS Serialize U+00FC -> 'ü'
+PASS Serialize U+00FD -> 'ý'
+PASS Serialize U+00FE -> 'þ'
+PASS Serialize %
+PASS Serialize \0
+PASS Serialize 💩
+PASS URLSearchParams.toString
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-stringifier.html b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-stringifier.html
new file mode 100644
index 0000000..495a1ac3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-stringifier.html
@@ -0,0 +1,146 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf8">
+<link rel="help" href="https://url.spec.whatwg.org/#dom-urlsearchparams-set">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/testharness-extras.js"></script>
+<script>
+test(function() {
+ var params = new URLSearchParams();
+ params.append('a', 'b c');
+ assert_equals(params + '', 'a=b+c');
+ params.delete('a');
+ params.append('a b', 'c');
+ assert_equals(params + '', 'a+b=c');
+}, 'Serialize space');
+
+test(function() {
+ var params = new URLSearchParams();
+ params.append('a', '');
+ assert_equals(params + '', 'a=');
+ params.append('a', '');
+ assert_equals(params + '', 'a=&a=');
+ params.append('', 'b');
+ assert_equals(params + '', 'a=&a=&=b');
+ params.append('', '');
+ assert_equals(params + '', 'a=&a=&=b&=');
+ params.append('', '');
+ assert_equals(params + '', 'a=&a=&=b&=&=');
+}, 'Serialize empty value');
+
+test(function() {
+ var params = new URLSearchParams();
+ params.append('', 'b');
+ assert_equals(params + '', '=b');
+ params.append('', 'b');
+ assert_equals(params + '', '=b&=b');
+}, 'Serialize empty name');
+
+test(function() {
+ var params = new URLSearchParams();
+ params.append('', '');
+ assert_equals(params + '', '=');
+ params.append('', '');
+ assert_equals(params + '', '=&=');
+}, 'Serialize empty name and value');
+
+test(function() {
+ var params = new URLSearchParams();
+ params.append('a', 'b+c');
+ assert_equals(params + '', 'a=b%2Bc');
+ params.delete('a');
+ params.append('a+b', 'c');
+ assert_equals(params + '', 'a%2Bb=c');
+}, 'Serialize +');
+
+test(function() {
+ var params = new URLSearchParams();
+ params.append('=', 'a');
+ assert_equals(params + '', '%3D=a');
+ params.append('b', '=');
+ assert_equals(params + '', '%3D=a&b=%3D');
+}, 'Serialize =');
+
+test(function() {
+ var params = new URLSearchParams();
+ params.append('&', 'a');
+ assert_equals(params + '', '%26=a');
+ params.append('b', '&');
+ assert_equals(params + '', '%26=a&b=%26');
+}, 'Serialize &');
+
+function intToHex(i) {
+ return (i<0x10 ? '0':'') + i.toString(16).toUpperCase();
+}
+
+function urlEncodedSerialize(n) {
+ if (n === 0x20) {
+ return "\x2B";
+ }
+
+ if (n === 0x2A || n === 0x2D || n === 0x2E ||
+ (0x30 <= n && n <= 0x39) || (0x41 <= n && n <= 0x5A) ||
+ n === 0x5F || (0x61 <= n && n <= 0x7A)) {
+ return String.fromCharCode(n);
+ }
+ var bytes = (new TextEncoder()).encode(String.fromCharCode(n));
+ var ret = "";
+ for (var i = 0; i < bytes.length; i++) {
+ ret += "%" + intToHex(bytes[i]);
+ }
+ return ret;
+}
+
+for (var i = 0x00; i < 0xFF; i++) {
+ // Not all bytes can appear in valid UTF-8, so some bytes aren't covered.
+ // TODO(mkwst): We fail to properly encode a few bytes: https://crbug.com/557063
+ test(function() {
+ var params = new URLSearchParams();
+ params.append('' + i, String.fromCodePoint(i));
+ assert_equals(params + '', '' + i + '=' + urlEncodedSerialize(i));
+ }, "Serialize U+00" + intToHex(i) + " -> '" + String.fromCodePoint(i) + "'");
+}
+
+test(function() {
+ var params = new URLSearchParams();
+ params.append('a', 'b%c');
+ assert_equals(params + '', 'a=b%25c');
+ params.delete('a');
+ params.append('a%b', 'c');
+ assert_equals(params + '', 'a%25b=c');
+}, 'Serialize %');
+
+test(function() {
+ var params = new URLSearchParams();
+ params.append('a', 'b\0c');
+ assert_equals(params + '', 'a=b%00c');
+ params.delete('a');
+ params.append('a\0b', 'c');
+ assert_equals(params + '', 'a%00b=c');
+}, 'Serialize \\0');
+
+test(function() {
+ var params = new URLSearchParams();
+ params.append('a', 'b\uD83D\uDCA9c');
+ assert_equals(params + '', 'a=b%F0%9F%92%A9c');
+ params.delete('a');
+ params.append('a\uD83D\uDCA9b', 'c');
+ assert_equals(params + '', 'a%F0%9F%92%A9b=c');
+}, 'Serialize \uD83D\uDCA9'); // Unicode Character 'PILE OF POO' (U+1F4A9)
+
+test(function() {
+ var params;
+ params = new URLSearchParams('a=b&c=d&&e&&');
+ assert_equals(params.toString(), 'a=b&c=d&e=');
+ params = new URLSearchParams('a = b &a=b&c=d%20');
+ assert_equals(params.toString(), 'a+=+b+&a=b&c=d+');
+ // The lone '=' _does_ survive the roundtrip.
+ params = new URLSearchParams('a=&a=b');
+ assert_equals(params.toString(), 'a=&a=b');
+}, 'URLSearchParams.toString');
+</script>
+</head>
+</html>
+
diff --git a/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-worker.html b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-worker.html
new file mode 100644
index 0000000..5fb8474
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams-worker.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf8">
+<link rel="help" href="https://url.spec.whatwg.org/#interface-urlsearchparams">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+fetch_tests_from_worker(new Worker("./resources/urlsearchparams-worker.js"));
+</script>
+</head>
+</html>
diff --git a/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams.html b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams.html
new file mode 100644
index 0000000..80861f51
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/domurl/urlsearchparams.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf8">
+<link rel="help" href="https://url.spec.whatwg.org/#interface-urlsearchparams">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+test(function() {
+ assert_true('URLSearchParams' in window);
+ assert_true('toString' in URLSearchParams.prototype);
+ assert_true('append' in URLSearchParams.prototype);
+ assert_true('delete' in URLSearchParams.prototype);
+ assert_true('get' in URLSearchParams.prototype);
+ assert_true('getAll' in URLSearchParams.prototype);
+ assert_true('has' in URLSearchParams.prototype);
+ assert_true('set' in URLSearchParams.prototype);
+}, 'URLSearchParams interface');
+</script>
+</head>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/WebKit/LayoutTests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index f6ff90d..54605ce 100644
--- a/third_party/WebKit/LayoutTests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -629,6 +629,19 @@
setter protocol
setter search
setter username
+interface URLSearchParams
+ method append
+ method constructor
+ method delete
+ method entries
+ method forEach
+ method get
+ method getAll
+ method has
+ method keys
+ method set
+ method toString
+ method values
interface WebSocket : EventTarget
attribute CLOSED
attribute CLOSING
diff --git a/third_party/WebKit/LayoutTests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/WebKit/LayoutTests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index 48d165e..36dedba4 100644
--- a/third_party/WebKit/LayoutTests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -528,6 +528,19 @@
setter protocol
setter search
setter username
+interface URLSearchParams
+ method append
+ method constructor
+ method delete
+ method entries
+ method forEach
+ method get
+ method getAll
+ method has
+ method keys
+ method set
+ method toString
+ method values
interface WebSocket : EventTarget
attribute CLOSED
attribute CLOSING
diff --git a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 886ce38..bb0e7c2 100644
--- a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -514,6 +514,19 @@
[Worker] setter protocol
[Worker] setter search
[Worker] setter username
+[Worker] interface URLSearchParams
+[Worker] method append
+[Worker] method constructor
+[Worker] method delete
+[Worker] method entries
+[Worker] method forEach
+[Worker] method get
+[Worker] method getAll
+[Worker] method has
+[Worker] method keys
+[Worker] method set
+[Worker] method toString
+[Worker] method values
[Worker] interface WebSocket : EventTarget
[Worker] attribute CLOSED
[Worker] attribute CLOSING
diff --git a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
index ba13e8c..4311a8b 100644
--- a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -4750,6 +4750,19 @@
setter protocol
setter search
setter username
+interface URLSearchParams
+ method append
+ method constructor
+ method delete
+ method entries
+ method forEach
+ method get
+ method getAll
+ method has
+ method keys
+ method set
+ method toString
+ method values
interface VTTCue : TextTrackCue
getter align
getter line
diff --git a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
index 9af80ab..d53d411 100644
--- a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -506,6 +506,19 @@
[Worker] setter protocol
[Worker] setter search
[Worker] setter username
+[Worker] interface URLSearchParams
+[Worker] method append
+[Worker] method constructor
+[Worker] method delete
+[Worker] method entries
+[Worker] method forEach
+[Worker] method get
+[Worker] method getAll
+[Worker] method has
+[Worker] method keys
+[Worker] method set
+[Worker] method toString
+[Worker] method values
[Worker] interface WebSocket : EventTarget
[Worker] attribute CLOSED
[Worker] attribute CLOSING
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index b71358c..843ac62 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -579,6 +579,19 @@
[Worker] setter protocol
[Worker] setter search
[Worker] setter username
+[Worker] interface URLSearchParams
+[Worker] method append
+[Worker] method constructor
+[Worker] method delete
+[Worker] method entries
+[Worker] method forEach
+[Worker] method get
+[Worker] method getAll
+[Worker] method has
+[Worker] method keys
+[Worker] method set
+[Worker] method toString
+[Worker] method values
[Worker] interface WebSocket : EventTarget
[Worker] attribute CLOSED
[Worker] attribute CLOSING
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index 1f0a02b..33088f5 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -5340,6 +5340,19 @@
setter protocol
setter search
setter username
+interface URLSearchParams
+ method append
+ method constructor
+ method delete
+ method entries
+ method forEach
+ method get
+ method getAll
+ method has
+ method keys
+ method set
+ method toString
+ method values
interface USBAlternateInterface
getter alternateSetting
getter endpoints
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-shared-worker-expected.txt
index 01ec856..c0d05bc 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -571,6 +571,19 @@
[Worker] setter protocol
[Worker] setter search
[Worker] setter username
+[Worker] interface URLSearchParams
+[Worker] method append
+[Worker] method constructor
+[Worker] method delete
+[Worker] method entries
+[Worker] method forEach
+[Worker] method get
+[Worker] method getAll
+[Worker] method has
+[Worker] method keys
+[Worker] method set
+[Worker] method toString
+[Worker] method values
[Worker] interface WebSocket : EventTarget
[Worker] attribute CLOSED
[Worker] attribute CLOSING
diff --git a/third_party/WebKit/Source/core/core.gypi b/third_party/WebKit/Source/core/core.gypi
index 6578811..c1551df 100644
--- a/third_party/WebKit/Source/core/core.gypi
+++ b/third_party/WebKit/Source/core/core.gypi
@@ -96,6 +96,7 @@
'dom/Uint32Array.idl',
'dom/Uint8Array.idl',
'dom/Uint8ClampedArray.idl',
+ 'dom/URLSearchParams.idl',
'dom/XMLDocument.idl',
'dom/shadow/ShadowRoot.idl',
'editing/Selection.idl',
@@ -2501,6 +2502,8 @@
'dom/TreeShared.h',
'dom/TreeWalker.cpp',
'dom/TreeWalker.h',
+ 'dom/URLSearchParams.cpp',
+ 'dom/URLSearchParams.h',
'dom/UserActionElementSet.cpp',
'dom/UserActionElementSet.h',
'dom/ViewportDescription.cpp',
diff --git a/third_party/WebKit/Source/core/dom/URLSearchParams.cpp b/third_party/WebKit/Source/core/dom/URLSearchParams.cpp
new file mode 100644
index 0000000..141aab1
--- /dev/null
+++ b/third_party/WebKit/Source/core/dom/URLSearchParams.cpp
@@ -0,0 +1,188 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+#include "core/dom/URLSearchParams.h"
+
+#include "platform/weborigin/KURL.h"
+#include "wtf/text/StringBuilder.h"
+#include "wtf/text/TextEncoding.h"
+
+namespace blink {
+
+namespace {
+
+class URLSearchParamsIterationSource final : public PairIterable<String, String>::IterationSource {
+public:
+ URLSearchParamsIterationSource(Vector<std::pair<String, String>> params) : m_params(params), m_current(0) { }
+
+ bool next(ScriptState*, String& key, String& value, ExceptionState&) override
+ {
+ if (m_current >= m_params.size())
+ return false;
+
+ key = m_params[m_current].first;
+ value = m_params[m_current].second;
+ m_current++;
+ return true;
+ }
+
+private:
+ Vector<std::pair<String, String>> m_params;
+ size_t m_current;
+};
+
+} // namespace
+
+URLSearchParams* URLSearchParams::create(const URLSearchParamsInit& init)
+{
+ if (init.isUSVString())
+ return new URLSearchParams(init.getAsUSVString());
+ if (init.isURLSearchParams())
+ return new URLSearchParams(init.getAsURLSearchParams());
+
+ ASSERT(init.isNull());
+ return new URLSearchParams(String());
+}
+
+URLSearchParams::URLSearchParams(const String& queryString)
+{
+ if (!queryString.isEmpty())
+ setInput(queryString);
+}
+
+URLSearchParams::URLSearchParams(URLSearchParams* searchParams)
+{
+ ASSERT(searchParams);
+ m_params = searchParams->m_params;
+}
+
+URLSearchParams::~URLSearchParams()
+{
+}
+
+static String decodeString(String input)
+{
+ return decodeURLEscapeSequences(input.replace('+', ' '));
+}
+
+void URLSearchParams::setInput(const String& queryString)
+{
+ ASSERT(m_params.isEmpty());
+ size_t start = 0;
+ size_t queryStringLength = queryString.length();
+ while (start < queryStringLength) {
+ size_t nameStart = start;
+ size_t nameValueEnd = queryString.find('&', start);
+ if (nameValueEnd == kNotFound)
+ nameValueEnd = queryStringLength;
+ if (nameValueEnd > start) {
+ size_t endOfName = queryString.find('=', start);
+ if (endOfName == kNotFound || endOfName > nameValueEnd)
+ endOfName = nameValueEnd;
+ String name = decodeString(queryString.substring(nameStart, endOfName - nameStart));
+ String value;
+ if (endOfName != nameValueEnd)
+ value = decodeString(queryString.substring(endOfName + 1, nameValueEnd - endOfName - 1));
+ if (value.isNull())
+ value = "";
+ m_params.append(std::make_pair(name, value));
+ }
+ start = nameValueEnd + 1;
+ }
+}
+static String encodeString(const String& input)
+{
+ return encodeWithURLEscapeSequences(input).replace("%20", "+");
+}
+
+String URLSearchParams::toString() const
+{
+ StringBuilder result;
+ for (size_t i = 0; i < m_params.size(); ++i) {
+ if (i)
+ result.append('&');
+ result.append(encodeString(m_params[i].first));
+ result.append('=');
+ result.append(encodeString(m_params[i].second));
+ }
+ return result.toString();
+}
+
+void URLSearchParams::append(const String& name, const String& value)
+{
+ m_params.append(std::make_pair(name, value));
+}
+
+void URLSearchParams::deleteAllWithName(const String& name)
+{
+ for (size_t i = 0; i < m_params.size();) {
+ if (m_params[i].first == name)
+ m_params.remove(i);
+ else
+ i++;
+ }
+}
+
+String URLSearchParams::get(const String& name) const
+{
+ for (const auto& param : m_params) {
+ if (param.first == name)
+ return param.second;
+ }
+ return String();
+}
+
+Vector<String> URLSearchParams::getAll(const String& name) const
+{
+ Vector<String> result;
+ for (const auto& param : m_params) {
+ if (param.first == name)
+ result.append(param.second);
+ }
+ return result;
+}
+
+bool URLSearchParams::has(const String& name) const
+{
+ for (const auto& param : m_params) {
+ if (param.first == name)
+ return true;
+ }
+ return false;
+}
+
+void URLSearchParams::set(const String& name, const String& value)
+{
+ bool foundMatch = false;
+ for (size_t i = 0; i < m_params.size();) {
+ // If there are any name-value whose name is 'name', set
+ // the value of the first such name-value pair to 'value'
+ // and remove the others.
+ if (m_params[i].first == name) {
+ if (!foundMatch) {
+ m_params[i++].second = value;
+ foundMatch = true;
+ } else {
+ m_params.remove(i);
+ }
+ } else {
+ i++;
+ }
+ }
+ // Otherwise, append a new name-value pair to the list.
+ if (!foundMatch)
+ append(name, value);
+}
+
+DEFINE_TRACE(URLSearchParams)
+{
+}
+
+PairIterable<String, String>::IterationSource* URLSearchParams::startIteration(ScriptState*, ExceptionState&)
+{
+ return new URLSearchParamsIterationSource(m_params);
+}
+
+} // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/URLSearchParams.h b/third_party/WebKit/Source/core/dom/URLSearchParams.h
new file mode 100644
index 0000000..7eb3dfb
--- /dev/null
+++ b/third_party/WebKit/Source/core/dom/URLSearchParams.h
@@ -0,0 +1,58 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef URLSearchParams_h
+#define URLSearchParams_h
+
+#include "bindings/core/v8/Iterable.h"
+#include "bindings/core/v8/ScriptWrappable.h"
+#include "bindings/core/v8/UnionTypesCore.h"
+#include "platform/heap/Handle.h"
+#include "wtf/Forward.h"
+#include "wtf/text/WTFString.h"
+#include <utility>
+
+namespace blink {
+
+class ExceptionState;
+
+typedef USVStringOrURLSearchParams URLSearchParamsInit;
+
+class CORE_EXPORT URLSearchParams final : public GarbageCollectedFinalized<URLSearchParams>, public ScriptWrappable, public PairIterable<String, String> {
+ DEFINE_WRAPPERTYPEINFO();
+
+public:
+ // TODO(mkwst): We should support integration with URLUtils, as explored in
+ // https://codereview.chromium.org/143313002/. That approach is totally
+ // reasonable, but relies on Node switching to Oilpan. Sigbjorn assures me
+ // that this will happen Real Soon Now(tm).
+ static URLSearchParams* create(const URLSearchParamsInit&);
+
+ // TODO(mkwst): ScriptWrappable doesn't have a destructor with Oilpan, so this
+ // won't need to be virtual once that's the default.
+ virtual ~URLSearchParams();
+
+ // URLSearchParams interface methods
+ String toString() const;
+ void append(const String& name, const String& value);
+ void deleteAllWithName(const String&);
+ String get(const String&) const;
+ Vector<String> getAll(const String&) const;
+ bool has(const String&) const;
+ void set(const String& name, const String& value);
+ void setInput(const String&);
+
+ DECLARE_TRACE();
+
+private:
+ explicit URLSearchParams(const String&);
+ explicit URLSearchParams(URLSearchParams*);
+ Vector<std::pair<String, String>> m_params;
+
+ IterationSource* startIteration(ScriptState*, ExceptionState&) override;
+};
+
+} // namespace blink
+
+#endif // URLSearchParams_h
diff --git a/third_party/WebKit/Source/core/dom/URLSearchParams.idl b/third_party/WebKit/Source/core/dom/URLSearchParams.idl
new file mode 100644
index 0000000..409542c
--- /dev/null
+++ b/third_party/WebKit/Source/core/dom/URLSearchParams.idl
@@ -0,0 +1,21 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://url.spec.whatwg.org/#interface-urlsearchparams
+
+[
+ Constructor(optional (USVString or URLSearchParams) init = ""),
+ GarbageCollected,
+ Exposed=(Window,Worker)
+] interface URLSearchParams {
+ void append(USVString name, USVString value);
+ [ImplementedAs=deleteAllWithName] void delete(USVString name);
+ USVString? get(USVString name);
+ sequence<USVString> getAll(USVString name);
+ boolean has(USVString name);
+ void set(USVString name, USVString value);
+
+ iterable<USVString, USVString>;
+ stringifier;
+};