[FIX] web: truncate filename when importing custom module via studio

Issue:
When uploading a large custom module via studio for importing, Chrome
crashes with an internal SIGKILL and Firefox manages to load, but is
really sluggish.

Steps to reproduce:
- Install Studio
- In the Apps Dashboard > Customizations > Import
- Upload a large custom module (50 MiB)
- Chrome crashes, while on Firefox the page is sluggish

Cause:
In the module `base_import_module`, the `module_file` doesn't have a
corresponding `filename`, therefor the name used to be displayed in
the wizard is the content of the binary file in base64 encoding...
For a module that is of a large size, this is a *gigantic* string
that crashes Chrome and slows down the DOM on Firefox.

Fix:
Truncate the maximum filename that is displayed in the template to
the max length necessary to encode in base64 (0xFF) 255 bytes, as
255 bytes is the maximum filename size on Linux/Windows/MacOS.

Affected versions:
16.0 up to master

Reference:
opw-3491998

closes odoo/odoo#134532

X-original-commit: 218c9875c0422beb2a5b199797e531ed3625a13b
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
Signed-off-by: Piryns Victor (pivi) <pivi@odoo.com>
This commit is contained in:
Victor Piryns (pivi)
2023-09-07 09:00:30 +00:00
parent 038169ee9f
commit 0ba867a88f
3 changed files with 38 additions and 2 deletions
@@ -7,3 +7,12 @@
export function isBinarySize(value) {
return /^\d+(\.\d*)? [^0-9]+$/.test(value);
}
/**
* Get the length necessary for a base64 str to encode maxBytes
* @param {number} maxBytes number of bytes we want to encode in base64
* @returns {number} number of char
*/
export function toBase64Length(maxBytes) {
return Math.ceil(maxBytes * 4 / 3);
}
@@ -2,13 +2,16 @@
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
import { isBinarySize } from "@web/core/utils/binary";
import { isBinarySize, toBase64Length } from "@web/core/utils/binary";
import { download } from "@web/core/network/download";
import { standardFieldProps } from "../standard_field_props";
import { FileUploader } from "../file_handler";
import { _t } from "@web/core/l10n/translation";
import { Component } from "@odoo/owl";
export const MAX_FILENAME_SIZE_BYTES = 0xFF; // filenames do not exceed 255 bytes on Linux/Windows/MacOS
export class BinaryField extends Component {
static template = "web.BinaryField";
static components = {
@@ -32,7 +35,7 @@ export class BinaryField extends Component {
this.props.record.data[this.props.fileNameField] ||
this.props.record.data[this.props.name] ||
""
);
).slice(0, toBase64Length(MAX_FILENAME_SIZE_BYTES));
}
update({ data, name }) {
@@ -15,6 +15,8 @@ import { makeView, setupViewRegistries } from "@web/../tests/views/helpers";
import { browser } from "@web/core/browser/browser";
import { errorService } from "@web/core/errors/error_service";
import { registry } from "@web/core/registry";
import { MAX_FILENAME_SIZE_BYTES } from "@web/views/fields/binary/binary_field";
import { toBase64Length } from "@web/core/utils/binary";
const BINARY_FILE =
"R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7";
@@ -472,6 +474,28 @@ QUnit.module("Fields", (hooks) => {
assert.containsNone(target, "button.fa-download");
});
QUnit.test("Binary filename doesn't exceed 255 bytes", async function (assert) {
const LARGE_BINARY_FILE = BINARY_FILE.repeat(5);
assert.ok((LARGE_BINARY_FILE.length / 4 * 3) > MAX_FILENAME_SIZE_BYTES,
"The initial binary file should be larger than max bytes that can represent the filename");
serverData.models.partner.fields.document.default = LARGE_BINARY_FILE;
await makeView({
serverData,
type: "form",
resModel: "partner",
arch: `
<form>
<field name="document"/>
</form>
`,
});
assert.strictEqual(
target.querySelector(".o_field_binary input[type=text]").value.length,
toBase64Length(MAX_FILENAME_SIZE_BYTES),
"The filename shouldn't exceed the maximum size in bytes in base64"
);
});
QUnit.test("BinaryField filename is updated when using the pager", async function (assert) {
serverData.models.partner.records.push(
{