aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/bun-types/globals.d.ts9
-rw-r--r--src/bun.js/bindings/DOMURL.cpp13
-rw-r--r--src/bun.js/bindings/DOMURL.h1
-rw-r--r--src/bun.js/bindings/webcore/JSDOMURL.cpp29
-rw-r--r--test/js/web/url/url.test.ts47
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);
+ });
+ });
+ });
});