CCA: Fallback to inline buffer when BigBuffer fails
Add a fallback mechanism to use inline buffer when BigBuffer fails. Once
BigBuffer fails in a session, all subsequent operations will use inline
buffer.
Test: Manually
Bug: b:349015781
Change-Id: Ia36411abae145471e955a49f0458ac83a02c21da
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5678392
Commit-Queue: Chu-Hsuan Yang <chuhsuan@chromium.org>
Reviewed-by: Sean Li <seannli@google.com>
Reviewed-by: Takashi Toyoshima <toyoshim@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1323603}
diff --git a/ash/webui/camera_app_ui/camera_app_helper.mojom b/ash/webui/camera_app_ui/camera_app_helper.mojom
index 0e5bc83..37fdb98 100644
--- a/ash/webui/camera_app_ui/camera_app_helper.mojom
+++ b/ash/webui/camera_app_ui/camera_app_helper.mojom
@@ -325,6 +325,9 @@
// Performs OCR on the image and returns the OCR result.
PerformOcr(mojo_base.mojom.BigBuffer jpeg_data) => (OcrResult ocr_result);
+ // Same as `PerformOcr`, but using `array<uint8>`.
+ PerformOcrInline(array<uint8> jpeg_data) => (OcrResult ocr_result);
+
// Creates a PDF builder.
CreatePdfBuilder(pending_receiver<PdfBuilder> builder);
};
diff --git a/ash/webui/camera_app_ui/camera_app_helper_impl.cc b/ash/webui/camera_app_ui/camera_app_helper_impl.cc
index 3be84ca..e982c5ae 100644
--- a/ash/webui/camera_app_ui/camera_app_helper_impl.cc
+++ b/ash/webui/camera_app_ui/camera_app_helper_impl.cc
@@ -623,6 +623,12 @@
camera_app_ui_->delegate()->PerformOcr(jpeg_data, std::move(callback));
}
+void CameraAppHelperImpl::PerformOcrInline(
+ const std::vector<uint8_t>& jpeg_data,
+ PerformOcrCallback callback) {
+ camera_app_ui_->delegate()->PerformOcr(jpeg_data, std::move(callback));
+}
+
void CameraAppHelperImpl::CreatePdfBuilder(
mojo::PendingReceiver<camera_app::mojom::PdfBuilder> receiver) {
return camera_app_ui_->delegate()->CreatePdfBuilder(std::move(receiver));
diff --git a/ash/webui/camera_app_ui/camera_app_helper_impl.h b/ash/webui/camera_app_ui/camera_app_helper_impl.h
index 9cf347a2..fdea751 100644
--- a/ash/webui/camera_app_ui/camera_app_helper_impl.h
+++ b/ash/webui/camera_app_ui/camera_app_helper_impl.h
@@ -120,6 +120,8 @@
RenderPdfAsJpegCallback callback) override;
void PerformOcr(mojo_base::BigBuffer jpeg_data,
PerformOcrCallback callback) override;
+ void PerformOcrInline(const std::vector<uint8_t>& jpeg_data,
+ PerformOcrCallback callback) override;
void CreatePdfBuilder(
mojo::PendingReceiver<camera_app::mojom::PdfBuilder> receiver) override;
diff --git a/ash/webui/camera_app_ui/pdf_builder.mojom b/ash/webui/camera_app_ui/pdf_builder.mojom
index 00e32fb..5dfe13f 100644
--- a/ash/webui/camera_app_ui/pdf_builder.mojom
+++ b/ash/webui/camera_app_ui/pdf_builder.mojom
@@ -14,9 +14,13 @@
// `page_index` is larger than PDF's current last index(L), the created page
// index is the next available index(L+1).
AddPage(mojo_base.mojom.BigBuffer jpeg, uint32 page_index);
+ // Same as `AddPage`, but using `array<uint8>` instead.
+ AddPageInline(array<uint8> jpeg, uint32 page_index);
// Deletes the page of the PDF at `page_index` and shift the following pages
// forward. Does nothing if the page at `page_index` doesn't exist.
DeletePage(uint32 page_index);
// Returns the PDF. It can be called multiple times at any time.
Save() => (mojo_base.mojom.BigBuffer pdf);
+ // Same as `Save`, but using `array<uint8>` instead.
+ SaveInline() => (array<uint8> pdf);
};
diff --git a/ash/webui/camera_app_ui/resources/js/mojo/chrome_helper.ts b/ash/webui/camera_app_ui/resources/js/mojo/chrome_helper.ts
index 372d429..d0ad2a1 100644
--- a/ash/webui/camera_app_ui/resources/js/mojo/chrome_helper.ts
+++ b/ash/webui/camera_app_ui/resources/js/mojo/chrome_helper.ts
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import {assert, assertNotReached} from '../assert.js';
+import {assert, assertInstanceof, assertNotReached} from '../assert.js';
import {reportError} from '../error.js';
import {Point} from '../geometry.js';
import * as localDev from '../local_dev.js';
@@ -80,10 +80,12 @@
const size = bytes.byteLength;
const sharedBuffer = Mojo.createSharedBuffer(size);
- assert(sharedBuffer.result === Mojo.RESULT_OK);
+ assert(
+ sharedBuffer.result === Mojo.RESULT_OK,
+ 'Failed to create shared buffer.');
const mapBuffer = sharedBuffer.handle.mapBuffer(0, size);
- assert(mapBuffer.result === Mojo.RESULT_OK);
+ assert(mapBuffer.result === Mojo.RESULT_OK, 'Failed to map buffer.');
const uint8View = new Uint8Array(mapBuffer.buffer);
uint8View.set(bytes);
@@ -103,8 +105,24 @@
return bigBuffer;
}
+/**
+ * Creates a number array from `blob` for Mojo's `array<uint8>`.
+ */
+export async function createNumArrayFromBlob(blob: Blob): Promise<number[]> {
+ const buffer = await blob.arrayBuffer();
+ return castToNumberArray(new Uint8Array(buffer));
+}
+
export abstract class ChromeHelper {
/**
+ * TODO(b/349015781): A flag to determine if we should use BigBuffer. It
+ * will be turned off when something went wrong when using BigBuffer. In the
+ * future, we want to monitor the error metrics to see if this flag is still
+ * needed.
+ */
+ static useBigBuffer = true;
+
+ /**
* Starts monitoring tablet mode state of device.
*
* @param onChange Callback called each time when tablet mode state of device
@@ -283,6 +301,15 @@
}
return instance;
}
+
+ static handleBigBufferError(e: unknown): void {
+ ChromeHelper.useBigBuffer = false;
+ reportError(
+ ErrorType.BIG_BUFFER_FAILURE,
+ ErrorLevel.WARNING,
+ assertInstanceof(e, Error),
+ );
+ }
}
export const getInstanceImpl =
@@ -527,8 +554,17 @@
}
override async performOcr(jpeg: Blob): Promise<OcrResult> {
- const bigBuffer = await createBigBufferFromBlob(jpeg);
- const {ocrResult} = await this.remote.performOcr(bigBuffer);
+ try {
+ if (ChromeHelper.useBigBuffer) {
+ const bigBuffer = await createBigBufferFromBlob(jpeg);
+ const {ocrResult} = await this.remote.performOcr(bigBuffer);
+ return ocrResult;
+ }
+ } catch (e) {
+ ChromeHelper.handleBigBufferError(e);
+ }
+ const numArray = await createNumArrayFromBlob(jpeg);
+ const {ocrResult} = await this.remote.performOcrInline(numArray);
return ocrResult;
}
diff --git a/ash/webui/camera_app_ui/resources/js/type.ts b/ash/webui/camera_app_ui/resources/js/type.ts
index 8a4f5d86..b69da19 100644
--- a/ash/webui/camera_app_ui/resources/js/type.ts
+++ b/ash/webui/camera_app_ui/resources/js/type.ts
@@ -373,6 +373,7 @@
* Types of error used in ERROR metrics.
*/
export enum ErrorType {
+ BIG_BUFFER_FAILURE = 'big-buffer-failure',
BROKEN_THUMBNAIL = 'broken-thumbnail',
CHECK_COVER_FAILURE = 'check-cover-failed',
DEVICE_INFO_UPDATE_FAILURE = 'device-info-update-failure',
diff --git a/ash/webui/camera_app_ui/resources/js/views/document_review.ts b/ash/webui/camera_app_ui/resources/js/views/document_review.ts
index ff2f848..226e93d 100644
--- a/ash/webui/camera_app_ui/resources/js/views/document_review.ts
+++ b/ash/webui/camera_app_ui/resources/js/views/document_review.ts
@@ -21,7 +21,11 @@
import {Filenamer} from '../models/file_namer.js';
import {getI18nMessage} from '../models/load_time_data.js';
import {ResultSaver} from '../models/result_saver.js';
-import {ChromeHelper, createBigBufferFromBlob} from '../mojo/chrome_helper.js';
+import {
+ ChromeHelper,
+ createBigBufferFromBlob,
+ createNumArrayFromBlob,
+} from '../mojo/chrome_helper.js';
import {
BigBuffer,
PdfBuilderRemote,
@@ -643,8 +647,17 @@
*/
async addPage(jpg: Blob, index: number): Promise<void> {
assert(this.builder !== null);
- const bigBuffer = await createBigBufferFromBlob(jpg);
- this.builder.addPage(bigBuffer, index);
+ try {
+ if (ChromeHelper.useBigBuffer) {
+ const bigBuffer = await createBigBufferFromBlob(jpg);
+ this.builder.addPage(bigBuffer, index);
+ return;
+ }
+ } catch (e) {
+ ChromeHelper.handleBigBufferError(e);
+ }
+ const numArray = await createNumArrayFromBlob(jpg);
+ this.builder.addPageInline(numArray, index);
}
/**
@@ -660,8 +673,16 @@
*/
async save(): Promise<Blob> {
assert(this.builder !== null);
- const {pdf} = await this.builder.save();
- return this.createPdfBlob(pdf);
+ try {
+ if (ChromeHelper.useBigBuffer) {
+ const {pdf} = await this.builder.save();
+ return this.createPdfBlob(pdf);
+ }
+ } catch (e) {
+ ChromeHelper.handleBigBufferError(e);
+ }
+ const {pdf} = await this.builder.saveInline();
+ return new Blob([new Uint8Array(pdf)], {type: MimeType.PDF});
}
/**
diff --git a/chrome/browser/ash/system_web_apps/apps/camera_app/chrome_camera_app_ui_delegate.cc b/chrome/browser/ash/system_web_apps/apps/camera_app/chrome_camera_app_ui_delegate.cc
index 4ffbc7d..abc6033 100644
--- a/chrome/browser/ash/system_web_apps/apps/camera_app/chrome_camera_app_ui_delegate.cc
+++ b/chrome/browser/ash/system_web_apps/apps/camera_app/chrome_camera_app_ui_delegate.cc
@@ -386,6 +386,16 @@
void ChromeCameraAppUIDelegate::PdfServiceManager::ProgressivePdf::AddPage(
mojo_base::BigBuffer jpg,
uint32_t index) {
+ AddPageInternal(jpg, index);
+}
+
+void ChromeCameraAppUIDelegate::PdfServiceManager::ProgressivePdf::
+ AddPageInline(const std::vector<uint8_t>& jpg, uint32_t index) {
+ AddPageInternal(jpg, index);
+}
+
+void ChromeCameraAppUIDelegate::PdfServiceManager::ProgressivePdf::
+ AddPageInternal(base::span<const uint8_t> jpg, uint32_t index) {
if (!pdf_searchifier_) {
LOG(ERROR) << "Failed to add new page to PDF";
return;
@@ -406,6 +416,15 @@
void ChromeCameraAppUIDelegate::PdfServiceManager::ProgressivePdf::Save(
SaveCallback callback) {
+ SaveInline(base::BindOnce(
+ [](SaveCallback callback, const std::vector<uint8_t>& pdf) {
+ std::move(callback).Run(pdf);
+ },
+ std::move(callback)));
+}
+
+void ChromeCameraAppUIDelegate::PdfServiceManager::ProgressivePdf::SaveInline(
+ SaveInlineCallback callback) {
if (!pdf_searchifier_) {
LOG(ERROR) << "Failed to save PDF";
std::move(callback).Run({});
diff --git a/chrome/browser/ash/system_web_apps/apps/camera_app/chrome_camera_app_ui_delegate.h b/chrome/browser/ash/system_web_apps/apps/camera_app/chrome_camera_app_ui_delegate.h
index 9c56336..d91a760 100644
--- a/chrome/browser/ash/system_web_apps/apps/camera_app/chrome_camera_app_ui_delegate.h
+++ b/chrome/browser/ash/system_web_apps/apps/camera_app/chrome_camera_app_ui_delegate.h
@@ -146,13 +146,17 @@
// ash::camera_app::mojom::PdfBuilder
void AddPage(mojo_base::BigBuffer jpg, uint32_t index) override;
+ void AddPageInline(const std::vector<uint8_t>& jpg,
+ uint32_t index) override;
void DeletePage(uint32_t index) override;
void Save(SaveCallback callback) override;
+ void SaveInline(SaveInlineCallback callback) override;
private:
+ void AddPageInternal(base::span<const uint8_t> jpg, uint32_t index);
void ConsumeSaveCallback(const std::vector<uint8_t>& searchified_pdf);
- SaveCallback save_callback_;
+ SaveInlineCallback save_callback_;
mojo::Remote<pdf::mojom::PdfService> pdf_service_;
mojo::Remote<pdf::mojom::PdfProgressiveSearchifier> pdf_searchifier_;
base::WeakPtrFactory<ProgressivePdf> weak_factory_{this};