From 16ca3afb1d221e6c576774d17499da5f48389dde Mon Sep 17 00:00:00 2001 From: W3D Date: Sat, 4 Apr 2026 22:47:28 +0200 Subject: [PATCH] [GENERAL][FIXED] - Fix WHATWG spec violations in URL and URLSearchParams polyfills Summary: Three spec violations in the URL/URLSearchParams polyfills: 1. URLSearchParams.size returned the number of unique keys (Map.size) instead of the total number of name-value pairs per the WHATWG spec. 2. URLSearchParams string constructor split on every '=' character, dropping parts of values that contain '='. Only the first '=' should be treated as a key-value separator. 3. URL.hash getter used a regex that stopped at '/', breaking fragment identifiers like #/users/123 common in SPA routing. --- packages/react-native/Libraries/Blob/URL.js | 4 ++-- .../react-native/Libraries/Blob/URLSearchParams.js | 14 ++++++++++---- .../Libraries/Blob/__tests__/URL-test.js | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/react-native/Libraries/Blob/URL.js b/packages/react-native/Libraries/Blob/URL.js index f4f879a51214..f65b3762796b 100644 --- a/packages/react-native/Libraries/Blob/URL.js +++ b/packages/react-native/Libraries/Blob/URL.js @@ -123,8 +123,8 @@ export class URL { } get hash(): string { - const hashMatch = this._url.match(/#([^/]*)/); - return hashMatch ? `#${hashMatch[1]}` : ''; + const hashIndex = this._url.indexOf('#'); + return hashIndex !== -1 ? this._url.slice(hashIndex) : ''; } get host(): string { diff --git a/packages/react-native/Libraries/Blob/URLSearchParams.js b/packages/react-native/Libraries/Blob/URLSearchParams.js index d325f7bb25a8..c3570513b2cd 100644 --- a/packages/react-native/Libraries/Blob/URLSearchParams.js +++ b/packages/react-native/Libraries/Blob/URLSearchParams.js @@ -14,7 +14,11 @@ export class URLSearchParams { _searchParams: Map = new Map(); get size(): number { - return this._searchParams.size; + let count = 0; + for (const values of this._searchParams.values()) { + count += values.length; + } + return count; } constructor(params?: Record | string | [string, string][]) { @@ -33,9 +37,11 @@ export class URLSearchParams { if (!pair) { return; } - const [key, value] = pair - .split('=') - .map(part => decodeURIComponent(part.replace(/\+/g, ' '))); + const eqIndex = pair.indexOf('='); + const rawKey = eqIndex === -1 ? pair : pair.slice(0, eqIndex); + const rawValue = eqIndex === -1 ? '' : pair.slice(eqIndex + 1); + const key = decodeURIComponent(rawKey.replace(/\+/g, ' ')); + const value = decodeURIComponent(rawValue.replace(/\+/g, ' ')); this.append(key, value); }); } else if (Array.isArray(params)) { diff --git a/packages/react-native/Libraries/Blob/__tests__/URL-test.js b/packages/react-native/Libraries/Blob/__tests__/URL-test.js index 72467dc08ebb..86ae866482be 100644 --- a/packages/react-native/Libraries/Blob/__tests__/URL-test.js +++ b/packages/react-native/Libraries/Blob/__tests__/URL-test.js @@ -110,7 +110,7 @@ describe('URL', function () { ['key1', 'value2'], ['key2', 'value3'], ]); - expect(paramsFromArray.size).toBe(2); + expect(paramsFromArray.size).toBe(3); expect(paramsFromArray.getAll('key1')).toEqual(['value1', 'value2']); expect(paramsFromArray.get('key2')).toBe('value3');