Skip to content

Commit

Permalink
Handle encoding image path before displaying in the app, especially f…
Browse files Browse the repository at this point in the history
…or local files
  • Loading branch information
ClementPasteau committed Jan 19, 2024
1 parent 7fbe1bd commit dd8ef40
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 7 deletions.
46 changes: 39 additions & 7 deletions newIDE/app/src/UI/CorsAwareImage.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,50 @@ type Props = {|
onLoad?: (e: any) => void,
|};

const addSearchParameterToUrl = (
export const encodeUrlAndAddEncodedSearchParameter = (
url: string,
urlEncodedParameterName: string,
urlEncodedValue: string
) => {
if (url.startsWith('data:') || url.startsWith('blob:')) {
// blob/data protocol does not support search parameters, which are useless anyway.
return url;
console.log(url);
if (
url.startsWith('http://') ||
url.startsWith('https://') ||
url.startsWith('ftp://')
) {
const urlObject = new URL(url);
urlObject.searchParams.set(urlEncodedParameterName, urlEncodedValue);
return urlObject.toString();
}

const separator = url.indexOf('?') === -1 ? '?' : '&';
return url + separator + urlEncodedParameterName + '=' + urlEncodedValue;
if (url.startsWith('file://')) {
// Local files and folders can contain special characters, which is handled badly when using
// new URL() constructor. So we do it manually.
const searchParams = url.indexOf('?') === -1 ? '' : url.split('?')[1];
const urlWithoutSearchParams = searchParams ? url.split('?')[0] : url;
const urlWithoutSearchParamsAndFileProtocol = urlWithoutSearchParams.slice(
'file://'.length
);
const encodedUrlComponents = urlWithoutSearchParamsAndFileProtocol
.split('/')
.map(component => encodeURIComponent(component));
const encodedUrlWithoutSearchParamsAndFileProtocol = encodedUrlComponents.join(
'/'
);
const newSearchParam = urlEncodedParameterName + '=' + urlEncodedValue;
const searchParamsWithNewParam = searchParams
? searchParams + '&' + newSearchParam
: newSearchParam;
return (
'file://' +
encodedUrlWithoutSearchParamsAndFileProtocol +
'?' +
searchParamsWithNewParam
);
}

// blob/data protocol or static images do not support search parameters, which are useless anyway.
return url;
};

/**
Expand Down Expand Up @@ -58,7 +90,7 @@ export const CorsAwareImage = (props: Props) => (
//
// Search for "cors-cache-workaround" in the codebase for the same workarounds.
props.src
? addSearchParameterToUrl(props.src, 'gdUsage', 'img')
? encodeUrlAndAddEncodedSearchParameter(props.src, 'gdUsage', 'img')
: undefined
}
/>
Expand Down
104 changes: 104 additions & 0 deletions newIDE/app/src/UI/CorsAwareImage.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// @flow
import { encodeUrlAndAddEncodedSearchParameter } from './CorsAwareImage';

describe('encodeUrlAndAddEncodedSearchParameter', () => {
it('should add search parameter to http url', () => {
const url = 'http://resources.gdevelop-app.com/file.png';
const result = encodeUrlAndAddEncodedSearchParameter(url, 'param', 'value');
expect(result).toBe(
'http://resources.gdevelop-app.com/file.png?param=value'
);
});

it('should add search parameter to http url with existing params', () => {
const url = 'http://resources.gdevelop-app.com/file.png?existing=param';
const result = encodeUrlAndAddEncodedSearchParameter(url, 'param', 'value');
expect(result).toBe(
'http://resources.gdevelop-app.com/file.png?existing=param&param=value'
);
});

it('should encode properly the http url', () => {
const url =
'http://resources.gdevelop-app.com/folder/my file.png?existing=param';
const result = encodeUrlAndAddEncodedSearchParameter(url, 'param', 'value');
expect(result).toBe(
'http://resources.gdevelop-app.com/folder/my%20file.png?existing=param&param=value'
);
});

it('should add search parameter to https url', () => {
const url = 'https://resources.gdevelop-app.com/file.png';
const result = encodeUrlAndAddEncodedSearchParameter(url, 'param', 'value');
expect(result).toBe(
'https://resources.gdevelop-app.com/file.png?param=value'
);
});

it('should add search parameter to https url with existing params', () => {
const url = 'https://resources.gdevelop-app.com/file.png?existing=param';
const result = encodeUrlAndAddEncodedSearchParameter(url, 'param', 'value');
expect(result).toBe(
'https://resources.gdevelop-app.com/file.png?existing=param&param=value'
);
});

it('should encode properly the https url', () => {
const url =
'https://resources.gdevelop-app.com/my folder/my file.png?existing=param';
const result = encodeUrlAndAddEncodedSearchParameter(url, 'param', 'value');
expect(result).toBe(
'https://resources.gdevelop-app.com/my%20folder/my%20file.png?existing=param&param=value'
);
});

it('should add search parameter to ftp url', () => {
const url = 'ftp://example.com/file.png';
const result = encodeUrlAndAddEncodedSearchParameter(url, 'param', 'value');
expect(result).toBe('ftp://example.com/file.png?param=value');
});

it('should add search parameter to ftp url with existing params', () => {
const url = 'ftp://example.com/file.png?existing=param';
const result = encodeUrlAndAddEncodedSearchParameter(url, 'param', 'value');
expect(result).toBe(
'ftp://example.com/file.png?existing=param&param=value'
);
});

it('should encode properly the ftp url', () => {
const url = 'ftp://example.com/my folder/my file.png?existing=param';
const result = encodeUrlAndAddEncodedSearchParameter(url, 'param', 'value');
expect(result).toBe(
'ftp://example.com/my%20folder/my%20file.png?existing=param&param=value'
);
});

it('should add search parameter to file url', () => {
const url = 'file://User/My folder/file.png';
const result = encodeUrlAndAddEncodedSearchParameter(url, 'param', 'value');
expect(result).toBe('file://User/My%20folder/file.png?param=value');
});

it('should add search parameter to file url with existing params', () => {
const url = 'file://User/My folder/file.png?existing=param';
const result = encodeUrlAndAddEncodedSearchParameter(url, 'param', 'value');
expect(result).toBe(
'file://User/My%20folder/file.png?existing=param&param=value'
);
});

it('should encode properly the file url', () => {
const url = 'file://User/My #folder/my #file.png?existing=param';
const result = encodeUrlAndAddEncodedSearchParameter(url, 'param', 'value');
expect(result).toBe(
'file://User/My%20%23folder/my%20%23file.png?existing=param&param=value'
);
});

it('should return original url for blob/data protocol or static images', () => {
const url = 'blob://example.com/file.png';
const result = encodeUrlAndAddEncodedSearchParameter(url, 'param', 'value');
expect(result).toBe(url);
});
});

0 comments on commit dd8ef40

Please sign in to comment.