Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
"prettier": "2.8.8",
"prettier-plugin-hermes-parser": "0.25.1",
"react": "19.0.0",
"react-test-renderer": "19.0.0",

"rimraf": "^3.0.2",
"shelljs": "^0.8.5",
"signedsource": "^1.0.0",
Expand Down
76 changes: 43 additions & 33 deletions packages/react-native/Libraries/Blob/URL.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,27 @@
*/

import type Blob from './Blob';

import NativeBlobModule from './NativeBlobModule';

let BLOB_URL_PREFIX = null;

// Initialize BLOB_URL_PREFIX if NativeBlobModule is available and properly configured
if (
NativeBlobModule &&
typeof NativeBlobModule.getConstants().BLOB_URI_SCHEME === 'string'
) {
const constants = NativeBlobModule.getConstants();
// $FlowFixMe[incompatible-type] asserted above
// $FlowFixMe[unsafe-addition]
BLOB_URL_PREFIX = constants.BLOB_URI_SCHEME + ':';
if (typeof constants.BLOB_URI_HOST === 'string') {
BLOB_URL_PREFIX += `//${constants.BLOB_URI_HOST}/`;
BLOB_URL_PREFIX += //${constants.BLOB_URI_HOST}/;
}
}

/*
* To allow Blobs be accessed via `content://` URIs,
* you need to register `BlobProvider` as a ContentProvider in your app's `AndroidManifest.xml`:
* To allow Blobs be accessed via content:// URIs,
* you need to register BlobProvider as a ContentProvider in your app's AndroidManifest.xml:
*
* ```xml
* xml
* <manifest>
* <application>
* <provider
Expand All @@ -41,44 +39,50 @@ if (
* />
* </application>
* </manifest>
* ```
* And then define the `blob_provider_authority` string in `res/values/strings.xml`.
*
* And then define the blob_provider_authority string in res/values/strings.xml.
* Use a dotted name that's entirely unique to your app:
*
* ```xml
* xml
* <resources>
* <string name="blob_provider_authority">your.app.package.blobs</string>
* </resources>
* ```
*
*/

export {URLSearchParams} from './URLSearchParams';

function validateBaseUrl(url: string) {
// Validate the base URL with a regular expression
function validateBaseUrl(url: string): boolean {
// from this MIT-licensed gist: https://gist.github.com/dperini/729294
return /^(?:(?:(?:https?|ftp):)?\/\/)(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)*(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$/.test(
return /^(?:(?:(?:https?|ftp):)?\/\/)(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S)?$/.test(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did you removed the *?

url,
);
}

export class URL {
_url: string;
_searchParamsInstance: ?URLSearchParams = null;
_urlObject: URL;

static createObjectURL(blob: Blob): string {
if (BLOB_URL_PREFIX === null) {
throw new Error('Cannot create URL for blob!');
throw new Error('Cannot create URL for blob! Ensure NativeBlobModule is properly configured.');
}
if (!blob || !blob.data || !blob.data.blobId) {
throw new Error('Invalid blob data: Missing blobId or data.');
}
return `${BLOB_URL_PREFIX}${blob.data.blobId}?offset=${blob.data.offset}&size=${blob.size}`;
return ${BLOB_URL_PREFIX}${blob.data.blobId}?offset=${blob.data.offset}&size=${blob.size};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing `. This is not valid JS code

}

static revokeObjectURL(url: string) {
// Do nothing.
// Do nothing, no implementation needed for revoking Blob URLs in this case.
}

// $FlowFixMe[missing-local-annot]
constructor(url: string, base: string | URL) {
let baseUrl = null;

// Validate URL format and handle base URL correctly
if (!base || validateBaseUrl(url)) {
this._url = url;
if (!this._url.endsWith('/')) {
Expand All @@ -88,67 +92,73 @@ export class URL {
if (typeof base === 'string') {
baseUrl = base;
if (!validateBaseUrl(baseUrl)) {
throw new TypeError(`Invalid base URL: ${baseUrl}`);
throw new TypeError(Invalid base URL: ${baseUrl});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Backticks ` are needed here as well

}
} else {
baseUrl = base.toString();
}

// Ensure correct URL formatting
if (baseUrl.endsWith('/')) {
baseUrl = baseUrl.slice(0, baseUrl.length - 1);
}
if (!url.startsWith('/')) {
url = `/${url}`;
url = /${url};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Backticks `

}
if (baseUrl.endsWith(url)) {
url = '';
}
this._url = `${baseUrl}${url}`;
this._url = ${baseUrl}${url};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Backticks `

}

// Create a URL object for easier parsing (browser-like behavior)
this._urlObject = new globalThis.URL(this._url);
}

get hash(): string {
throw new Error('URL.hash is not implemented');
return this._urlObject.hash || ''; // Extract the fragment part
}

get host(): string {
throw new Error('URL.host is not implemented');
return this._urlObject.host || ''; // Extracts the host and port (if present)
}

get hostname(): string {
throw new Error('URL.hostname is not implemented');
return this._urlObject.hostname || ''; // Extracts just the hostname (without port)
}

get href(): string {
return this.toString();
}

get origin(): string {
throw new Error('URL.origin is not implemented');
return this._urlObject.origin || ''; // Extracts the origin (protocol + hostname + port)
}

get password(): string {
throw new Error('URL.password is not implemented');
const match = this._urlObject.href.match(/^.:\/\/(.):(.*)@/);
return match && match[2] ? match[2] : ''; // Extract password from "username:password" part
}

get pathname(): string {
throw new Error('URL.pathname not implemented');
return this._urlObject.pathname || ''; // Extracts the pathname (e.g., "/path/to/resource")
}

get port(): string {
throw new Error('URL.port is not implemented');
return this._urlObject.port || ''; // Extracts the port part
}

get protocol(): string {
throw new Error('URL.protocol is not implemented');
return this._urlObject.protocol || ''; // Extracts the protocol (e.g., "http:" or "https:")
}

get search(): string {
throw new Error('URL.search is not implemented');
return this._urlObject.search || ''; // Extracts the query string (e.g., "?id=123")
}

get searchParams(): URLSearchParams {
if (this._searchParamsInstance == null) {
this._searchParamsInstance = new URLSearchParams();
this._searchParamsInstance = new URLSearchParams(this._urlObject.search);
}
return this._searchParamsInstance;
}
Expand All @@ -161,13 +171,13 @@ export class URL {
if (this._searchParamsInstance === null) {
return this._url;
}
// $FlowFixMe[incompatible-use]
const instanceString = this._searchParamsInstance.toString();
const separator = this._url.indexOf('?') > -1 ? '&' : '?';
return this._url + separator + instanceString;
}

get username(): string {
throw new Error('URL.username is not implemented');
const match = this._urlObject.href.match(/^.:\/\/(.?)(?::(.*))?@/);
return match && match[1] ? match[1] : ''; // Extract username from "username:password" part
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @oncall react_native
*/

import {

Check warning on line 11 in packages/react-native/Libraries/LogBox/__tests__/LogBox-integration-test.js

View workflow job for this annotation

GitHub Actions / test_js (20)

Requires should be sorted alphabetically

Check warning on line 11 in packages/react-native/Libraries/LogBox/__tests__/LogBox-integration-test.js

View workflow job for this annotation

GitHub Actions / test_js (18)

Requires should be sorted alphabetically
DoesNotUseKey,
FragmentWithProp,
ManualConsoleError,
Expand All @@ -18,7 +18,7 @@

const ExceptionsManager = require('../../Core/ExceptionsManager.js');
const LogBoxData = require('../Data/LogBoxData');
const TestRenderer = require('react-test-renderer');
import { create } from 'react-native-fantom';

const installLogBox = () => {
const LogBox = require('../LogBox').default;
Expand All @@ -41,10 +41,8 @@
jest.resetModules();
jest.restoreAllMocks();
jest.spyOn(console, 'error').mockImplementation(() => {});

mockError.mockClear();
mockWarn.mockClear();
// Reset ExceptionManager patching.
if (console._errorOriginal) {
console._errorOriginal = null;
}
Expand All @@ -63,18 +61,16 @@
// and testing the react-test-renderer instead of the real one.
// The react test renderer is now deprecated, so there is no value in keeping this test
// as it is.
it.skip('integrates with React and handles a key error in LogBox', () => {

Check warning on line 64 in packages/react-native/Libraries/LogBox/__tests__/LogBox-integration-test.js

View workflow job for this annotation

GitHub Actions / test_js (20)

Disabled test

Check warning on line 64 in packages/react-native/Libraries/LogBox/__tests__/LogBox-integration-test.js

View workflow job for this annotation

GitHub Actions / test_js (18)

Disabled test
const spy = jest.spyOn(LogBoxData, 'addLog');
installLogBox();

Check warning on line 66 in packages/react-native/Libraries/LogBox/__tests__/LogBox-integration-test.js

View workflow job for this annotation

GitHub Actions / test_js (20)

Trailing spaces not allowed

Check warning on line 66 in packages/react-native/Libraries/LogBox/__tests__/LogBox-integration-test.js

View workflow job for this annotation

GitHub Actions / test_js (18)

Trailing spaces not allowed

// Spy console.error after LogBox is installed
// so we can assert on what React logs.
jest.spyOn(console, 'error');

installLogBox();
let output;
TestRenderer.act(() => {
output = TestRenderer.create(<DoesNotUseKey />);
});
create(<DoesNotUseKey />);

// The key error should always be the highest severity.
// In LogBox, we expect these errors to:
Expand Down Expand Up @@ -124,18 +120,16 @@
// and testing the react-test-renderer instead of the real one.
// The react test renderer is now deprecated, so there is no value in keeping this test
// as it is.
it.skip('integrates with React and handles a fragment warning in LogBox', () => {

Check warning on line 123 in packages/react-native/Libraries/LogBox/__tests__/LogBox-integration-test.js

View workflow job for this annotation

GitHub Actions / test_js (20)

Disabled test

Check warning on line 123 in packages/react-native/Libraries/LogBox/__tests__/LogBox-integration-test.js

View workflow job for this annotation

GitHub Actions / test_js (18)

Disabled test
const spy = jest.spyOn(LogBoxData, 'addLog');
installLogBox();

Check warning on line 125 in packages/react-native/Libraries/LogBox/__tests__/LogBox-integration-test.js

View workflow job for this annotation

GitHub Actions / test_js (20)

Trailing spaces not allowed

Check warning on line 125 in packages/react-native/Libraries/LogBox/__tests__/LogBox-integration-test.js

View workflow job for this annotation

GitHub Actions / test_js (18)

Trailing spaces not allowed

// Spy console.error after LogBox is installed
// so we can assert on what React logs.
jest.spyOn(console, 'error');

installLogBox();
let output;
TestRenderer.act(() => {
output = TestRenderer.create(<FragmentWithProp />);
});
create(<FragmentWithProp />);

// The fragment warning is not as severe. For this warning we don't want to
// pop open a dialog, so we show a collapsed error UI.
Expand Down Expand Up @@ -188,9 +182,7 @@
jest.spyOn(console, 'error');

let output;
TestRenderer.act(() => {
output = TestRenderer.create(<ManualConsoleError />);
});
create(<ManualConsoleError />);

// Manual console errors should show a collapsed error dialog.
// When there is no component stack, we expect these errors to:
Expand Down Expand Up @@ -230,9 +222,8 @@
jest.spyOn(console, 'error');

let output;
TestRenderer.act(() => {
output = TestRenderer.create(<ManualConsoleErrorWithStack />);
});
create(<ManualConsoleErrorWithStack />);


// Manual console errors should show a collapsed error dialog.
// When there is a component stack, we expect these errors to:
Expand Down
Loading