summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author迷渡 <justjavac@gmail.com>2018-12-27 10:12:55 +0800
committerRyan Dahl <ry@tinyclouds.org>2018-12-27 02:12:55 +0000
commit5e518b741704b738e29352c341ee6a956cce1fff (patch)
tree3eab76cf45be5cd60c5fa0e0ebd49de6c41c2f06
parent9bfe9a005b7b90f880414747cecad4f8def02a02 (diff)
check runtime `FormData` and `Headers` params (#1415)
-rw-r--r--js/form_data.ts51
-rw-r--r--js/form_data_test.ts70
-rw-r--r--js/headers.ts6
-rw-r--r--js/headers_test.ts70
-rw-r--r--js/mixins/dom_iterable.ts6
5 files changed, 197 insertions, 6 deletions
diff --git a/js/form_data.ts b/js/form_data.ts
index 6f0cadfef..163c33b74 100644
--- a/js/form_data.ts
+++ b/js/form_data.ts
@@ -3,6 +3,7 @@ import * as domTypes from "./dom_types";
import * as blob from "./blob";
import * as file from "./file";
import { DomIterableMixin } from "./mixins/dom_iterable";
+import { requiredArguments } from "./util";
const dataSymbol = Symbol("data");
@@ -18,6 +19,8 @@ class FormDataBase {
append(name: string, value: string): void;
append(name: string, value: blob.DenoBlob, filename?: string): void;
append(name: string, value: string | blob.DenoBlob, filename?: string): void {
+ requiredArguments("FormData.append", arguments.length, 2);
+ name = String(name);
if (value instanceof blob.DenoBlob) {
const dfile = new file.DenoFile([value], filename || name);
this[dataSymbol].push([name, dfile]);
@@ -31,6 +34,8 @@ class FormDataBase {
* formData.delete('name');
*/
delete(name: string): void {
+ requiredArguments("FormData.delete", arguments.length, 1);
+ name = String(name);
let i = 0;
while (i < this[dataSymbol].length) {
if (this[dataSymbol][i][0] === name) {
@@ -47,6 +52,8 @@ class FormDataBase {
* formData.getAll('name');
*/
getAll(name: string): domTypes.FormDataEntryValue[] {
+ requiredArguments("FormData.getAll", arguments.length, 1);
+ name = String(name);
const values = [];
for (const entry of this[dataSymbol]) {
if (entry[0] === name) {
@@ -63,6 +70,8 @@ class FormDataBase {
* formData.get('name');
*/
get(name: string): domTypes.FormDataEntryValue | null {
+ requiredArguments("FormData.get", arguments.length, 1);
+ name = String(name);
for (const entry of this[dataSymbol]) {
if (entry[0] === name) {
return entry[1];
@@ -78,23 +87,53 @@ class FormDataBase {
* formData.has('name');
*/
has(name: string): boolean {
+ requiredArguments("FormData.has", arguments.length, 1);
+ name = String(name);
return this[dataSymbol].some(entry => entry[0] === name);
}
/** Sets a new value for an existing key inside a `FormData` object, or
* adds the key/value if it does not already exist.
+ * ref: https://xhr.spec.whatwg.org/#dom-formdata-set
*
* formData.set('name', 'value');
*/
set(name: string, value: string): void;
set(name: string, value: blob.DenoBlob, filename?: string): void;
set(name: string, value: string | blob.DenoBlob, filename?: string): void {
- this.delete(name);
- if (value instanceof blob.DenoBlob) {
- const dfile = new file.DenoFile([value], filename || name);
- this[dataSymbol].push([name, dfile]);
- } else {
- this[dataSymbol].push([name, String(value)]);
+ requiredArguments("FormData.set", arguments.length, 2);
+ name = String(name);
+
+ // If there are any entries in the context object’s entry list whose name
+ // is name, replace the first such entry with entry and remove the others
+ let found = false;
+ let i = 0;
+ while (i < this[dataSymbol].length) {
+ if (this[dataSymbol][i][0] === name) {
+ if (!found) {
+ if (value instanceof blob.DenoBlob) {
+ const dfile = new file.DenoFile([value], filename || name);
+ this[dataSymbol][i][1] = dfile;
+ } else {
+ this[dataSymbol][i][1] = String(value);
+ }
+ found = true;
+ } else {
+ this[dataSymbol].splice(i, 1);
+ continue;
+ }
+ }
+ i++;
+ }
+
+ // Otherwise, append entry to the context object’s entry list.
+ if (!found) {
+ if (value instanceof blob.DenoBlob) {
+ const dfile = new file.DenoFile([value], filename || name);
+ this[dataSymbol].push([name, dfile]);
+ } else {
+ this[dataSymbol].push([name, String(value)]);
+ }
}
}
}
diff --git a/js/form_data_test.ts b/js/form_data_test.ts
index f3fbdd872..109220f63 100644
--- a/js/form_data_test.ts
+++ b/js/form_data_test.ts
@@ -96,3 +96,73 @@ test(function formDataParamsForEachSuccess() {
});
assertEqual(callNum, init.length);
});
+
+test(function formDataParamsArgumentsCheck() {
+ const methodRequireOneParam = ["delete", "getAll", "get", "has", "forEach"];
+
+ const methodRequireTwoParams = ["append", "set"];
+
+ methodRequireOneParam.forEach(method => {
+ const formData = new FormData();
+ let hasThrown = 0;
+ let errMsg = "";
+ try {
+ formData[method]();
+ hasThrown = 1;
+ } catch (err) {
+ errMsg = err.message;
+ if (err instanceof TypeError) {
+ hasThrown = 2;
+ } else {
+ hasThrown = 3;
+ }
+ }
+ assertEqual(hasThrown, 2);
+ assertEqual(
+ errMsg,
+ `FormData.${method} requires at least 1 argument, but only 0 present`
+ );
+ });
+
+ methodRequireTwoParams.forEach(method => {
+ const formData = new FormData();
+ let hasThrown = 0;
+ let errMsg = "";
+
+ try {
+ formData[method]();
+ hasThrown = 1;
+ } catch (err) {
+ errMsg = err.message;
+ if (err instanceof TypeError) {
+ hasThrown = 2;
+ } else {
+ hasThrown = 3;
+ }
+ }
+ assertEqual(hasThrown, 2);
+ assertEqual(
+ errMsg,
+ `FormData.${method} requires at least 2 arguments, but only 0 present`
+ );
+
+ hasThrown = 0;
+ errMsg = "";
+ try {
+ formData[method]("foo");
+ hasThrown = 1;
+ } catch (err) {
+ errMsg = err.message;
+ if (err instanceof TypeError) {
+ hasThrown = 2;
+ } else {
+ hasThrown = 3;
+ }
+ }
+ assertEqual(hasThrown, 2);
+ assertEqual(
+ errMsg,
+ `FormData.${method} requires at least 2 arguments, but only 1 present`
+ );
+ });
+});
diff --git a/js/headers.ts b/js/headers.ts
index ca99d5789..7cc38181f 100644
--- a/js/headers.ts
+++ b/js/headers.ts
@@ -1,6 +1,7 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
import * as domTypes from "./dom_types";
import { DomIterableMixin } from "./mixins/dom_iterable";
+import { requiredArguments } from "./util";
// From node-fetch
// Copyright (c) 2016 David Frank. MIT License.
@@ -75,6 +76,7 @@ class HeadersBase {
// ref: https://fetch.spec.whatwg.org/#concept-headers-append
append(name: string, value: string): void {
+ requiredArguments("Headers.append", arguments.length, 2);
const [newname, newvalue] = this._normalizeParams(name, value);
this._validateName(newname);
this._validateValue(newvalue);
@@ -84,12 +86,14 @@ class HeadersBase {
}
delete(name: string): void {
+ requiredArguments("Headers.delete", arguments.length, 1);
const [newname] = this._normalizeParams(name);
this._validateName(newname);
this[headerMap].delete(newname);
}
get(name: string): string | null {
+ requiredArguments("Headers.get", arguments.length, 1);
const [newname] = this._normalizeParams(name);
this._validateName(newname);
const value = this[headerMap].get(newname);
@@ -97,12 +101,14 @@ class HeadersBase {
}
has(name: string): boolean {
+ requiredArguments("Headers.has", arguments.length, 1);
const [newname] = this._normalizeParams(name);
this._validateName(newname);
return this[headerMap].has(newname);
}
set(name: string, value: string): void {
+ requiredArguments("Headers.set", arguments.length, 2);
const [newname, newvalue] = this._normalizeParams(name, value);
this._validateName(newname);
this._validateValue(newvalue);
diff --git a/js/headers_test.ts b/js/headers_test.ts
index 53c8ef089..9591b24f8 100644
--- a/js/headers_test.ts
+++ b/js/headers_test.ts
@@ -228,3 +228,73 @@ test(function headerIllegalReject() {
// 'o k' is valid value but invalid name
new Headers({ "He-y": "o k" });
});
+
+test(function headerParamsArgumentsCheck() {
+ const methodRequireOneParam = ["delete", "get", "has", "forEach"];
+
+ const methodRequireTwoParams = ["append", "set"];
+
+ methodRequireOneParam.forEach(method => {
+ const headers = new Headers();
+ let hasThrown = 0;
+ let errMsg = "";
+ try {
+ headers[method]();
+ hasThrown = 1;
+ } catch (err) {
+ errMsg = err.message;
+ if (err instanceof TypeError) {
+ hasThrown = 2;
+ } else {
+ hasThrown = 3;
+ }
+ }
+ assertEqual(hasThrown, 2);
+ assertEqual(
+ errMsg,
+ `Headers.${method} requires at least 1 argument, but only 0 present`
+ );
+ });
+
+ methodRequireTwoParams.forEach(method => {
+ const headers = new Headers();
+ let hasThrown = 0;
+ let errMsg = "";
+
+ try {
+ headers[method]();
+ hasThrown = 1;
+ } catch (err) {
+ errMsg = err.message;
+ if (err instanceof TypeError) {
+ hasThrown = 2;
+ } else {
+ hasThrown = 3;
+ }
+ }
+ assertEqual(hasThrown, 2);
+ assertEqual(
+ errMsg,
+ `Headers.${method} requires at least 2 arguments, but only 0 present`
+ );
+
+ hasThrown = 0;
+ errMsg = "";
+ try {
+ headers[method]("foo");
+ hasThrown = 1;
+ } catch (err) {
+ errMsg = err.message;
+ if (err instanceof TypeError) {
+ hasThrown = 2;
+ } else {
+ hasThrown = 3;
+ }
+ }
+ assertEqual(hasThrown, 2);
+ assertEqual(
+ errMsg,
+ `Headers.${method} requires at least 2 arguments, but only 1 present`
+ );
+ });
+});
diff --git a/js/mixins/dom_iterable.ts b/js/mixins/dom_iterable.ts
index 2daf80b71..6a2a9fcfe 100644
--- a/js/mixins/dom_iterable.ts
+++ b/js/mixins/dom_iterable.ts
@@ -1,5 +1,6 @@
import { DomIterable } from "../dom_types";
import { globalEval } from "../global_eval";
+import { requiredArguments } from "../util";
// if we import it directly from "globals" it will break the unit tests so we
// have to grab a reference to the global scope a different way
@@ -52,6 +53,11 @@ export function DomIterableMixin<K, V, TBase extends Constructor>(
// tslint:disable-next-line:no-any
thisArg?: any
): void {
+ requiredArguments(
+ `${this.constructor.name}.forEach`,
+ arguments.length,
+ 1
+ );
callbackfn = callbackfn.bind(thisArg == null ? window : Object(thisArg));
for (const [key, value] of (this as any)[dataSymbol]) {
callbackfn(value, key, this);