diff options
-rw-r--r-- | packages/bun-types/globals.d.ts | 9 | ||||
-rw-r--r-- | src/bun.js/bindings/DOMURL.cpp | 13 | ||||
-rw-r--r-- | src/bun.js/bindings/DOMURL.h | 1 | ||||
-rw-r--r-- | src/bun.js/bindings/webcore/JSDOMURL.cpp | 29 | ||||
-rw-r--r-- | test/js/web/url/url.test.ts | 47 |
5 files changed, 96 insertions, 3 deletions
diff --git a/packages/bun-types/globals.d.ts b/packages/bun-types/globals.d.ts index d43ee78bd..2931cad1d 100644 --- a/packages/bun-types/globals.d.ts +++ b/packages/bun-types/globals.d.ts @@ -1290,7 +1290,6 @@ interface RequestInit { } interface FetchRequestInit extends RequestInit { - /** * Log the raw HTTP request & response to stdout. This API may be * removed in a future version of Bun without notice. @@ -2448,6 +2447,14 @@ declare var URL: { createObjectURL(obj: Blob): string; /** Not implemented yet */ revokeObjectURL(url: string): void; + + /** + * Check if `url` is a valid URL string + * + * @param url URL string to parse + * @param base URL to resolve against + */ + canParse(url: string, base?: string): boolean; }; type TimerHandler = (...args: any[]) => void; diff --git a/src/bun.js/bindings/DOMURL.cpp b/src/bun.js/bindings/DOMURL.cpp index 31381b867..9b5893a35 100644 --- a/src/bun.js/bindings/DOMURL.cpp +++ b/src/bun.js/bindings/DOMURL.cpp @@ -42,6 +42,17 @@ inline DOMURL::DOMURL(URL&& completeURL, const URL& baseURL) { } +DOMURL::~DOMURL() = default; + +bool DOMURL::canParse(const String& url, const String& base) +{ + URL baseURL { base }; + if (!base.isNull() && !baseURL.isValid()) + return false; + URL completeURL { baseURL, url }; + return completeURL.isValid(); +} + ExceptionOr<Ref<DOMURL>> DOMURL::create(const String& url, const URL& base) { ASSERT(base.isValid() || base.isNull()); @@ -64,8 +75,6 @@ ExceptionOr<Ref<DOMURL>> DOMURL::create(const String& url, const DOMURL& base) return create(url, base.href()); } -DOMURL::~DOMURL() = default; - ExceptionOr<void> DOMURL::setHref(const String& url) { URL completeURL { URL {}, url }; diff --git a/src/bun.js/bindings/DOMURL.h b/src/bun.js/bindings/DOMURL.h index 0a6210690..0f19510b6 100644 --- a/src/bun.js/bindings/DOMURL.h +++ b/src/bun.js/bindings/DOMURL.h @@ -43,6 +43,7 @@ public: static ExceptionOr<Ref<DOMURL>> create(const String& url, const DOMURL& base); ~DOMURL(); + static bool canParse(const String& url, const String& base); const URL& href() const { return m_url; } ExceptionOr<void> setHref(const String&); void setQuery(const String&); diff --git a/src/bun.js/bindings/webcore/JSDOMURL.cpp b/src/bun.js/bindings/webcore/JSDOMURL.cpp index 747154bed..3f99fc2b7 100644 --- a/src/bun.js/bindings/webcore/JSDOMURL.cpp +++ b/src/bun.js/bindings/webcore/JSDOMURL.cpp @@ -64,6 +64,7 @@ using namespace JSC; static JSC_DECLARE_HOST_FUNCTION(jsDOMURLPrototypeFunction_toJSON); static JSC_DECLARE_HOST_FUNCTION(jsDOMURLConstructorFunction_createObjectURL); static JSC_DECLARE_HOST_FUNCTION(jsDOMURLConstructorFunction_revokeObjectURL); +static JSC_DECLARE_HOST_FUNCTION(jsDOMURLConstructorFunction_canParse); static JSC_DECLARE_HOST_FUNCTION(jsDOMURLPrototypeFunction_toString); // Attributes @@ -131,6 +132,7 @@ using JSDOMURLDOMConstructor = JSDOMConstructor<JSDOMURL>; static const HashTableValue JSDOMURLConstructorTableValues[] = { { "createObjectURL"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDOMURLConstructorFunction_createObjectURL, 1 } }, { "revokeObjectURL"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDOMURLConstructorFunction_revokeObjectURL, 1 } }, + { "canParse"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDOMURLConstructorFunction_canParse, 2 } }, }; static inline EncodedJSValue constructJSDOMURL1(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame) @@ -688,6 +690,33 @@ JSC_DEFINE_HOST_FUNCTION(jsDOMURLConstructorFunction_revokeObjectURL, (JSGlobalO return IDLOperation<JSDOMURL>::callStatic<jsDOMURLConstructorFunction_revokeObjectURLBody>(*lexicalGlobalObject, *callFrame, "revokeObjectURL"); } +static inline JSC::EncodedJSValue jsDOMURLConstructorFunction_canParseBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + if (UNLIKELY(callFrame->argumentCount() < 1)) + return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); + + EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); + auto url = convert<IDLUSVString>(*lexicalGlobalObject, argument0.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + + EnsureStillAliveScope argument1 = callFrame->argument(1); + String base; + if (!argument1.value().isUndefinedOrNull()) { + base = convert<IDLUSVString>(*lexicalGlobalObject, argument1.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + } + + return JSValue::encode(jsBoolean(DOMURL::canParse(url, base))); +} + +JSC_DEFINE_HOST_FUNCTION(jsDOMURLConstructorFunction_canParse, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return IDLOperation<JSDOMURL>::callStatic<jsDOMURLConstructorFunction_canParseBody>(*lexicalGlobalObject, *callFrame, "canParse"); +} + #if ENABLE(MEDIA_SOURCE) static inline JSC::EncodedJSValue jsDOMURLConstructorFunction_createObjectURL2Body(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) { diff --git a/test/js/web/url/url.test.ts b/test/js/web/url/url.test.ts index 6ad691c1b..1591c2b44 100644 --- a/test/js/web/url/url.test.ts +++ b/test/js/web/url/url.test.ts @@ -165,4 +165,51 @@ describe("url", () => { expect(result.username).toBe(values.username); } }); + + describe("URL.canParse", () => { + ( + [ + { + "url": undefined, + "base": undefined, + "expected": false, + }, + { + "url": "a:b", + "base": undefined, + "expected": true, + }, + { + "url": undefined, + "base": "a:b", + "expected": false, + }, + { + "url": "a:/b", + "base": undefined, + "expected": true, + }, + { + "url": undefined, + "base": "a:/b", + "expected": true, + }, + { + "url": "https://test:test", + "base": undefined, + "expected": false, + }, + { + "url": "a", + "base": "https://b/", + "expected": true, + }, + ] as const + ).forEach(({ url, base, expected }) => { + it(`URL.canParse(${url}, ${base})`, () => { + // @ts-expect-error + expect(URL.canParse(url, base)).toBe(expected); + }); + }); + }); }); |