77 ArrayPrototypePush,
88 ArrayPrototypeReduce,
99 ArrayPrototypeSlice,
10+ Error,
1011 FunctionPrototypeBind,
1112 Int8Array,
1213 Number,
@@ -29,6 +30,7 @@ const {
2930 Symbol,
3031 SymbolIterator,
3132 SymbolToStringTag,
33+ TypeError,
3234 decodeURIComponent,
3335} = primordials ;
3436
@@ -118,6 +120,8 @@ const cannotHaveUsernamePasswordPort =
118120const special = Symbol ( 'special' ) ;
119121const searchParams = Symbol ( 'query' ) ;
120122const kFormat = Symbol ( 'format' ) ;
123+ const initURLSearchParamsFromRecord =
124+ Symbol ( 'init-url-search-params-from-record' ) ;
121125
122126let blob ;
123127let cryptoRandom ;
@@ -174,6 +178,20 @@ function isURLSearchParams(self) {
174178 return self && self [ searchParams ] && ! self [ searchParams ] [ searchParams ] ;
175179}
176180
181+ // WPT needs Error in URLSearchParams' constructor exactly be an instance of
182+ // TypeError.
183+ function throwTypeError ( message ) {
184+ if ( message instanceof Error ) {
185+ const err = new TypeError ( message . message ) ; // eslint-disable-line
186+ err . stack = message . stack ;
187+ if ( message . code ) err . code = message . code ;
188+ if ( message . name ) err . name = message . name ;
189+ throw err ;
190+ }
191+
192+ throw new TypeError ( message ) ; // eslint-disable-line
193+ }
194+
177195class URLSearchParams {
178196 // URL Standard says the default value is '', but as undefined and '' have
179197 // the same result, undefined is used to prevent unnecessary parsing.
@@ -191,7 +209,7 @@ class URLSearchParams {
191209 this [ searchParams ] = childParams . slice ( ) ;
192210 } else if ( method !== null && method !== undefined ) {
193211 if ( typeof method !== 'function' ) {
194- throw new ERR_ARG_NOT_ITERABLE ( 'Query pairs' ) ;
212+ throwTypeError ( new ERR_ARG_NOT_ITERABLE ( 'Query pairs' ) ) ;
195213 }
196214
197215 // Sequence<sequence<USVString>>
@@ -201,7 +219,8 @@ class URLSearchParams {
201219 if ( ( typeof pair !== 'object' && typeof pair !== 'function' ) ||
202220 pair === null ||
203221 typeof pair [ SymbolIterator ] !== 'function' ) {
204- throw new ERR_INVALID_TUPLE ( 'Each query pair' , '[name, value]' ) ;
222+ throwTypeError (
223+ new ERR_INVALID_TUPLE ( 'Each query pair' , '[name, value]' ) ) ;
205224 }
206225 const convertedPair = [ ] ;
207226 for ( const element of pair )
@@ -217,18 +236,10 @@ class URLSearchParams {
217236 ArrayPrototypePush ( this [ searchParams ] , pair [ 0 ] , pair [ 1 ] ) ;
218237 }
219238 } else {
220- // Record<USVString, USVString>
221- // Need to use reflection APIs for full spec compliance.
222- this [ searchParams ] = [ ] ;
223- const keys = ReflectOwnKeys ( init ) ;
224- for ( let i = 0 ; i < keys . length ; i ++ ) {
225- const key = keys [ i ] ;
226- const desc = ReflectGetOwnPropertyDescriptor ( init , key ) ;
227- if ( desc !== undefined && desc . enumerable ) {
228- const typedKey = toUSVString ( key ) ;
229- const typedValue = toUSVString ( init [ key ] ) ;
230- this [ searchParams ] . push ( typedKey , typedValue ) ;
231- }
239+ try {
240+ this [ initURLSearchParamsFromRecord ] ( init ) ;
241+ } catch ( e ) {
242+ throwTypeError ( e ) ;
232243 }
233244 }
234245 } else {
@@ -242,6 +253,31 @@ class URLSearchParams {
242253 this [ context ] = null ;
243254 }
244255
256+ [ initURLSearchParamsFromRecord ] ( init ) {
257+ // Record<USVString, USVString>
258+ // Need to use reflection APIs for full spec compliance.
259+ const visited = { } ;
260+ this [ searchParams ] = [ ] ;
261+ const keys = ReflectOwnKeys ( init ) ;
262+ for ( let i = 0 ; i < keys . length ; i ++ ) {
263+ const key = keys [ i ] ;
264+ const desc = ReflectGetOwnPropertyDescriptor ( init , key ) ;
265+ if ( desc !== undefined && desc . enumerable ) {
266+ const typedKey = toUSVString ( key ) ;
267+ const typedValue = toUSVString ( init [ key ] ) ;
268+
269+ // Two different key may result same after `toUSVString()`, we only
270+ // leave the later one. Refers to WPT.
271+ if ( visited [ typedKey ] !== undefined ) {
272+ this [ searchParams ] [ visited [ typedKey ] ] = typedValue ;
273+ } else {
274+ this [ searchParams ] . push ( typedKey , typedValue ) ;
275+ visited [ typedKey ] = this [ searchParams ] . length - 1 ;
276+ }
277+ }
278+ }
279+ }
280+
245281 [ inspect . custom ] ( recurseTimes , ctx ) {
246282 if ( ! isURLSearchParams ( this ) )
247283 throw new ERR_INVALID_THIS ( 'URLSearchParams' ) ;
0 commit comments