@@ -99,9 +99,9 @@ namespace tns {
9999 // `(sequence<sequence<USVString>> or record<USVString, USVString> or USVString)`
100100 // that browsers/Node apply (https://url.spec.whatwg.org/#interface-urlsearchparams):
101101 // object with @@iterator -> sequence (Array, Map, Set, URLSearchParams, generator)
102- // object without @@iterator-> record (own enumerable string keys, in order)
103- // other primitive -> USVString (number/boolean/bigint -> string; Symbol throws)
104- // null / undefined -> empty
102+ // object without @@iterator-> record (own enumerable keys, in order; a Symbol key throws )
103+ // other primitive / null -> USVString (number/boolean/bigint/null -> string; Symbol throws)
104+ // undefined / missing -> empty (the IDL default "")
105105 namespace {
106106 void ThrowTypeError (v8::Isolate *isolate, const char *message) {
107107 isolate->ThrowException (
@@ -448,12 +448,15 @@ namespace tns {
448448 }
449449
450450 // Record init form: a plain object of name -> value, iterated in own
451- // enumerable string-key order. A value that cannot be coerced to a string
452- // aborts with the JS exception left pending.
451+ // enumerable key order ([[OwnPropertyKeys]]: strings, then symbols). Per
452+ // WebIDL record conversion every enumerable key is converted to a
453+ // USVString, so an own enumerable Symbol key throws a TypeError
454+ // (ValueToString below raises it); symbols are NOT silently skipped. A
455+ // key or value that cannot be coerced to a string aborts with the JS
456+ // exception left pending.
453457 bool BuildFromRecord (v8::Local<v8::Context> context, v8::Local<v8::Object> object,
454458 ada::url_search_params ¶ms) {
455- auto filter = static_cast <v8::PropertyFilter>(
456- v8::PropertyFilter::ONLY_ENUMERABLE | v8::PropertyFilter::SKIP_SYMBOLS );
459+ auto filter = v8::PropertyFilter::ONLY_ENUMERABLE ;
457460 v8::Local<v8::Array> keys;
458461 if (!object->GetOwnPropertyNames (context, filter,
459462 v8::KeyConversionMode::kConvertToString ).ToLocal (&keys)) {
@@ -516,17 +519,21 @@ namespace tns {
516519 ThrowTypeError (isolate, " URLSearchParams init Symbol.iterator is not a function" );
517520 return ;
518521 }
519- } else if (!value->IsNullOrUndefined ()) {
520- // Other primitive (number / boolean / bigint / symbol): coerce to a
521- // USVString and run the urlencoded string parser. A Symbol cannot be
522- // converted and throws here, matching the spec.
522+ } else if (!value->IsUndefined ()) {
523+ // Other primitive (number / boolean / bigint / symbol / null): coerce
524+ // to a USVString and run the urlencoded string parser. The WebIDL
525+ // union conversion has no null special case (the union is not
526+ // nullable and a record is not a dictionary), so
527+ // new URLSearchParams(null) parses the string "null", as the
528+ // reference implementation does. A Symbol cannot be converted and
529+ // throws here, matching the spec.
523530 std::string init;
524531 if (!ValueToString (context, value, init)) {
525532 return ;
526533 }
527534 params = ada::url_search_params (init);
528535 }
529- // null / undefined -> leave params empty
536+ // undefined / missing -> the IDL default "" -> leave params empty
530537
531538 auto searchParams = new URLSearchParamsImpl (params);
532539
@@ -541,6 +548,9 @@ namespace tns {
541548 void URLSearchParamsImpl::Append (const v8::FunctionCallbackInfo<v8::Value> &args) {
542549 URLSearchParamsImpl *ptr = GetPointer (args.This ());
543550 if (ptr == nullptr ) {
551+ // WebIDL brand check: every member requires a genuine URLSearchParams
552+ // receiver (same as entries()/keys()/values()).
553+ ThrowTypeError (args.GetIsolate (), " Illegal invocation" );
544554 return ;
545555 }
546556 // Both arguments are USVStrings (url.bs:3860); a Symbol or throwing
@@ -558,6 +568,7 @@ namespace tns {
558568 void URLSearchParamsImpl::Delete (const v8::FunctionCallbackInfo<v8::Value> &args) {
559569 URLSearchParamsImpl *ptr = GetPointer (args.This ());
560570 if (ptr == nullptr ) {
571+ ThrowTypeError (args.GetIsolate (), " Illegal invocation" );
561572 return ;
562573 }
563574 // The name is a USVString (url.bs:3861): coerce it like the optional
@@ -597,6 +608,7 @@ namespace tns {
597608 auto isolate = args.GetIsolate ();
598609 auto context = isolate->GetCurrentContext ();
599610 if (ptr == nullptr ) {
611+ ThrowTypeError (isolate, " Illegal invocation" );
600612 return ;
601613 }
602614 auto callback = args[0 ].As <v8::Function>();
@@ -628,7 +640,7 @@ namespace tns {
628640 URLSearchParamsImpl *ptr = GetPointer (args.This ());
629641 auto isolate = args.GetIsolate ();
630642 if (ptr == nullptr ) {
631- args. GetReturnValue (). SetUndefined ( );
643+ ThrowTypeError (isolate, " Illegal invocation " );
632644 return ;
633645 }
634646 // The name is a USVString (url.bs:3862); a Symbol or throwing toString
@@ -653,7 +665,7 @@ namespace tns {
653665 auto isolate = args.GetIsolate ();
654666 auto context = isolate->GetCurrentContext ();
655667 if (ptr == nullptr ) {
656- args. GetReturnValue (). Set ( v8::Array::New ( isolate) );
668+ ThrowTypeError ( isolate, " Illegal invocation " );
657669 return ;
658670 }
659671 // The name is a USVString (url.bs:3863); a Symbol or throwing toString
@@ -674,7 +686,7 @@ namespace tns {
674686 void URLSearchParamsImpl::Has (const v8::FunctionCallbackInfo<v8::Value> &args) {
675687 URLSearchParamsImpl *ptr = GetPointer (args.This ());
676688 if (ptr == nullptr ) {
677- args.GetReturnValue (). Set ( false );
689+ ThrowTypeError ( args.GetIsolate (), " Illegal invocation " );
678690 return ;
679691 }
680692 // The name is a USVString (url.bs:3864): coerce it like the optional
@@ -713,6 +725,7 @@ namespace tns {
713725 void URLSearchParamsImpl::Set (const v8::FunctionCallbackInfo<v8::Value> &args) {
714726 URLSearchParamsImpl *ptr = GetPointer (args.This ());
715727 if (ptr == nullptr ) {
728+ ThrowTypeError (args.GetIsolate (), " Illegal invocation" );
716729 return ;
717730 }
718731 // Both arguments are USVStrings (url.bs:3865); a Symbol or throwing
@@ -731,7 +744,7 @@ namespace tns {
731744 const v8::PropertyCallbackInfo<v8::Value> &info) {
732745 URLSearchParamsImpl *ptr = GetPointer (info.This ());
733746 if (ptr == nullptr ) {
734- info.GetReturnValue (). Set ( 0 );
747+ ThrowTypeError ( info.GetIsolate (), " Illegal invocation " );
735748 return ;
736749 }
737750
@@ -743,6 +756,7 @@ namespace tns {
743756 void URLSearchParamsImpl::Sort (const v8::FunctionCallbackInfo<v8::Value> &args) {
744757 URLSearchParamsImpl *ptr = GetPointer (args.This ());
745758 if (ptr == nullptr ) {
759+ ThrowTypeError (args.GetIsolate (), " Illegal invocation" );
746760 return ;
747761 }
748762 ptr->GetURLSearchParams ()->sort ();
@@ -751,7 +765,7 @@ namespace tns {
751765 void URLSearchParamsImpl::ToString (const v8::FunctionCallbackInfo<v8::Value> &args) {
752766 URLSearchParamsImpl *ptr = GetPointer (args.This ());
753767 if (ptr == nullptr ) {
754- args.GetReturnValue (). SetEmptyString ( );
768+ ThrowTypeError ( args.GetIsolate (), " Illegal invocation " );
755769 return ;
756770 }
757771 auto isolate = args.GetIsolate ();
0 commit comments