豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content

Commit 1f3e61e

Browse files
Connor ClarkDevtools-frontend LUCI CQ
authored andcommitted
Improve error case in Base64 encode w/ large input
`result` can be an empty string even when `onerror` does not fire if the input is too large to represent as a string. In that case, the function was returning undefined, which would certainly error any caller. Instead throw an explicit error and document that callers are expected to handle if the input is large. Bug: 436482118 Change-Id: I18330da4b17ff76310cd3141075a86d3df8718b2 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7455444 Auto-Submit: Connor Clark <cjamcl@chromium.org> Commit-Queue: Connor Clark <cjamcl@chromium.org> Commit-Queue: Paul Irish <paulirish@chromium.org> Reviewed-by: Paul Irish <paulirish@chromium.org>
1 parent d1b4db0 commit 1f3e61e

File tree

2 files changed

+20
-6
lines changed

2 files changed

+20
-6
lines changed

front_end/core/common/Base64.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,23 @@ export function decode(input: string): Uint8Array<ArrayBuffer> {
3232
return bytes;
3333
}
3434

35+
/**
36+
* Note: if input can be very large (larger than the max string size), callers should
37+
* expect this to throw an error.
38+
*/
3539
export function encode(input: BlobPart): Promise<string> {
3640
return new Promise((resolve, reject) => {
3741
const reader = new FileReader();
38-
reader.onerror = () => reject(new Error('failed to convert to base64'));
42+
reader.onerror = () => reject(new Error('failed to convert to base64: internal error'));
3943
reader.onload = () => {
44+
// The input was too large to encode as a string. The caller should anticipate
45+
// this and use a workaround. See TimelinePanel.ts innerSaveToFile for an example.
46+
// For more information, see crbug.com/436482118.
47+
if (reader.result === '') {
48+
reject(new Error('failed to convert to base64: input too large to encode as base64 string'));
49+
return;
50+
}
51+
4052
// This string can be very large, so take care to not double memory. `split`
4153
// was used here before, which always results in new strings in V8. By using
4254
// slice instead, we leverage the sliced string optimization in V8 and avoid

front_end/panels/timeline/TimelinePanel.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,21 +1564,23 @@ export class TimelinePanel extends Common.ObjectWrapper.eventMixin<EventTypes, t
15641564
const blobType = blob.type; // blob may be reassigned later.
15651565

15661566
// In some cases Base64.encode() can return undefined; see crbug.com/436482118 for details.
1567-
// TODO(crbug.com/436482118): understand this edge case and fix the Base64.encode method to not just return undefined.
15681567
let bytesAsB64: string|null = null;
15691568
try {
15701569
// The maximum string length in v8 is `2 ** 29 - 23`, aka 538 MB.
15711570
// If the gzipped&base64-encoded trace is larger than that, this'll throw a RangeError.
15721571
this.statusDialog.updateStatus(i18nString(UIStrings.encodingTraceForDownload));
15731572
this.statusDialog.updateProgressBar(i18nString(UIStrings.encodingTraceForDownload), 100);
15741573
bytesAsB64 = await Common.Base64.encode(blob);
1575-
if (bytesAsB64.length) {
1576-
blob = new Blob(); // Don't retain this large object for the remaining lifetime of this function.
1574+
blob = new Blob(); // Don't retain this large object for the remaining lifetime of this function.
1575+
} catch (err) {
1576+
if (err instanceof Error && err.message.startsWith('failed to convert to base64')) {
1577+
// Expected and handled below.
1578+
} else {
1579+
throw err;
15771580
}
1578-
} catch {
15791581
}
15801582

1581-
if (bytesAsB64?.length) {
1583+
if (bytesAsB64) {
15821584
const contentData = new TextUtils.ContentData.ContentData(bytesAsB64, /* isBase64=*/ true, blobType);
15831585
await Workspace.FileManager.FileManager.instance().save(fileName, contentData, /* forceSaveAs=*/ true);
15841586
Workspace.FileManager.FileManager.instance().close(fileName);

0 commit comments

Comments
 (0)