Skip to content

Commit

Permalink
documentScan: Add setOptions function
Browse files Browse the repository at this point in the history
setOptions takes a scanner handle returned from openScanner and sets
zero or more supplied options to new values.  The response includes the
result of setting each option and an updated set of option values in the
same format as the original response from openScanner.

The real work is done in DocumentScanAPIHandler::SetOptions.  This CL
adds the JS function entry points and the matching apitests.

Bug: b:297435721
Test: Scan from test extension with non-default options on a Chromebook
Change-Id: Ib5dab0e804c3b61823cbf7cf0c69b148203a6e8b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5096610
Reviewed-by: Devlin Cronin <rdevlin.cronin@chromium.org>
Commit-Queue: Benjamin Gordon <bmgordon@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1237823}
  • Loading branch information
yetamrra authored and Chromium LUCI CQ committed Dec 14, 2023
1 parent 4ef10eb commit 8f1e0b8
Show file tree
Hide file tree
Showing 9 changed files with 246 additions and 0 deletions.
23 changes: 23 additions & 0 deletions chrome/browser/extensions/api/document_scan/document_scan_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,29 @@ void DocumentScanCloseScannerFunction::OnResponseReceived(
api::document_scan::CloseScanner::Results::Create(response)));
}

DocumentScanSetOptionsFunction::DocumentScanSetOptionsFunction() = default;
DocumentScanSetOptionsFunction::~DocumentScanSetOptionsFunction() = default;

ExtensionFunction::ResponseAction DocumentScanSetOptionsFunction::Run() {
auto params = api::document_scan::SetOptions::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);

DocumentScanAPIHandler::Get(browser_context())
->SetOptions(
extension_, std::move(params->scanner_handle),
std::move(params->options),
base::BindOnce(&DocumentScanSetOptionsFunction::OnResponseReceived,
this));

return did_respond() ? AlreadyResponded() : RespondLater();
}

void DocumentScanSetOptionsFunction::OnResponseReceived(
api::document_scan::SetOptionsResponse response) {
Respond(
ArgumentList(api::document_scan::SetOptions::Results::Create(response)));
}

DocumentScanStartScanFunction::DocumentScanStartScanFunction() = default;
DocumentScanStartScanFunction::~DocumentScanStartScanFunction() = default;

Expand Down
19 changes: 19 additions & 0 deletions chrome/browser/extensions/api/document_scan/document_scan_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,25 @@ class DocumentScanCloseScannerFunction : public ExtensionFunction {
DOCUMENTSCAN_CLOSESCANNER)
};

class DocumentScanSetOptionsFunction : public ExtensionFunction {
public:
DocumentScanSetOptionsFunction();
DocumentScanSetOptionsFunction(const DocumentScanSetOptionsFunction&) =
delete;
DocumentScanSetOptionsFunction& operator=(
const DocumentScanSetOptionsFunction&) = delete;

protected:
~DocumentScanSetOptionsFunction() override;

// ExtensionFunction:
ResponseAction Run() override;

private:
void OnResponseReceived(api::document_scan::SetOptionsResponse response);
DECLARE_EXTENSION_FUNCTION("documentScan.setOptions", DOCUMENTSCAN_SETOPTIONS)
};

class DocumentScanStartScanFunction : public ExtensionFunction {
public:
DocumentScanStartScanFunction();
Expand Down
3 changes: 3 additions & 0 deletions chrome/common/extensions/api/_api_features.json
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,9 @@
"documentScan.closeScanner": {
"channel": "dev"
},
"documentScan.setOptions": {
"channel": "dev"
},
"documentScan.startScan": {
"channel": "dev"
},
Expand Down
14 changes: 14 additions & 0 deletions chrome/common/extensions/api/document_scan.idl
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,20 @@ namespace documentScan {
[nodoc, supportsPromises] static void closeScanner(
DOMString scannerHandle, CloseScannerCallback callback);

// Sends the list of new option values in <code>options</code> as a bundle
// to be set on <code>scannerHandle</code>. Each option will be set by the
// backend the order specified. Returns a backend response indicating the
// result of each option setting and a new set of final option values after
// all options have been updated.
// |scannerHandle| : Open scanner handle previously returned from
// <code>openScanner</code>.
// |options| : A list of <code>OptionSetting</code>s that will be applied to
// <code>scannerHandle</code>.
// |callback| : Called with the result.
[nodoc, supportsPromises] static void setOptions(
DOMString scannerHandle, OptionSetting[] options,
SetOptionsCallback callback);

// Starts a scan using a previously opened scanner handle. A response
// indicating the outcome will be sent to the callback. If successful, the
// response will include a job handle that can be used in subsequent calls
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ chrome.test.runTests([
chrome.test.assertTrue(!!chrome.documentScan.getScannerList);
chrome.test.assertTrue(!!chrome.documentScan.openScanner);
chrome.test.assertTrue(!!chrome.documentScan.closeScanner);
chrome.test.assertTrue(!!chrome.documentScan.setOptions);
chrome.test.assertTrue(!!chrome.documentScan.startScan);
chrome.test.assertTrue(!!chrome.documentScan.cancelScan);
chrome.test.assertTrue(!!chrome.documentScan.readScanData);
Expand Down
177 changes: 177 additions & 0 deletions chrome/test/data/extensions/api_test/document_scan/perform_scan.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,183 @@ chrome.test.runTests([
chrome.test.assertEq(null, readResponse2.data);
chrome.test.assertEq(null, readResponse2.estimatedCompletion);

chrome.test.succeed();
},

async function setOptionsBeforeOpenFails() {
const response = await setOptions('invalid-handle', [
{name: 'option', type: OptionType.INT}]);
chrome.test.assertEq('invalid-handle', response.scannerHandle);
chrome.test.assertEq(1, response.results.length);
chrome.test.assertEq(OperationResult.INVALID, response.results[0].result);
chrome.test.assertEq('option', response.results[0].name);
chrome.test.assertEq(null, response.options);
chrome.test.succeed();
},

async function setOptionsRequiresMatchingTypes_Fixed() {
// Fixed options can be set from int or double because JS doesn't have a
// clear distinction between these.
const options = [
{name: 'fixed1', type: OptionType.FIXED, value: 42}, // OK, mapped.
{name: 'fixed2', type: OptionType.FIXED, value: 42.0}, // OK, mapped.
{name: 'fixed3', type: OptionType.FIXED, value: 42.5}, // OK.
{name: 'fixed4', type: OptionType.FIXED, value: '1.0'}, // Wrong type.
{name: 'fixed5', type: OptionType.FIXED, value: [42, 43]}, // OK, mapped.
{name: 'fixed6', type: OptionType.FIXED,
value: [42.0, 43.0]}, // OK, mapped.
{name: 'fixed7', type: OptionType.FIXED, value: [42.5, 43.5]} // OK.
];

const scannerHandle = await getScannerHandle();
chrome.test.assertNe(null, scannerHandle);

const response = await setOptions(scannerHandle, options);
chrome.test.assertEq(scannerHandle, response.scannerHandle);
chrome.test.assertEq(options.length, response.results.length);
// Match each result individually instead of one big array to make it easier
// to tell where any failures occur.
chrome.test.assertEq(
{name: 'fixed1', result: OperationResult.SUCCESS}, response.results[0]);
chrome.test.assertEq(
{name: 'fixed2', result: OperationResult.SUCCESS}, response.results[1]);
chrome.test.assertEq(
{name: 'fixed3', result: OperationResult.SUCCESS}, response.results[2]);
chrome.test.assertEq(
{name: 'fixed4', result: OperationResult.WRONG_TYPE},
response.results[3]);
chrome.test.assertEq(
{name: 'fixed5', result: OperationResult.SUCCESS}, response.results[4]);
chrome.test.assertEq(
{name: 'fixed6', result: OperationResult.SUCCESS}, response.results[5]);
chrome.test.assertEq(
{name: 'fixed7', result: OperationResult.SUCCESS}, response.results[6]);

chrome.test.assertNe(null, response.options);
chrome.test.succeed();
},

async function setOptionsRequiresMatchingTypes_Int() {
// Int options can be set from int values or from double values with a zero
// fractional part because JS doesn't have a clear distinction between
// these.
const options = [
{name: 'int1', type: OptionType.INT, value: 42}, // OK.
{name: 'int2', type: OptionType.INT, value: 42.0}, // OK, mapped.
{name: 'int3', type: OptionType.INT, value: 42.5}, // Wrong type.
{name: 'int4', type: OptionType.INT, value: '1.0'}, // Wrong type.
{name: 'int5', type: OptionType.INT, value: [42, 42]}, // OK.
{name: 'int6', type: OptionType.INT, value: [42.0, 42.0]}, // OK, mapped.
{name: 'int7', type: OptionType.INT, value: [42.5]}, // Wrong type.
{name: 'int8', type: OptionType.INT, value: 1e300}, // Wrong type.
{name: 'int9', type: OptionType.INT, value: -1e300}, // Wrong type.
{name: 'int10', type: OptionType.INT, value: [1e300]}, // Wrong type.
{name: 'int11', type: OptionType.INT, value: [-1e300]} // Wrong type.
];

const scannerHandle = await getScannerHandle();
chrome.test.assertNe(null, scannerHandle);

const response = await setOptions(scannerHandle, options);
chrome.test.assertEq(scannerHandle, response.scannerHandle);
chrome.test.assertEq(options.length, response.results.length);
// Match each result individually instead of one big array to make it easier
// to tell where any failures occur.
chrome.test.assertEq(
{name: 'int1', result: OperationResult.SUCCESS}, response.results[0]);
chrome.test.assertEq(
{name: 'int2', result: OperationResult.SUCCESS}, response.results[1]);
chrome.test.assertEq(
{name: 'int3', result: OperationResult.WRONG_TYPE}, response.results[2]);
chrome.test.assertEq(
{name: 'int4', result: OperationResult.WRONG_TYPE}, response.results[3]);
chrome.test.assertEq(
{name: 'int5', result: OperationResult.SUCCESS}, response.results[4]);
chrome.test.assertEq(
{name: 'int6', result: OperationResult.SUCCESS}, response.results[5]);
chrome.test.assertEq(
{name: 'int7', result: OperationResult.WRONG_TYPE}, response.results[6]);
chrome.test.assertEq(
{name: 'int8', result: OperationResult.WRONG_TYPE}, response.results[7]);
chrome.test.assertEq(
{name: 'int9', result: OperationResult.WRONG_TYPE}, response.results[8]);
chrome.test.assertEq(
{name: 'int10', result: OperationResult.WRONG_TYPE},
response.results[9]);
chrome.test.assertEq(
{name: 'int11', result: OperationResult.WRONG_TYPE},
response.results[10]);

chrome.test.assertNe(null, response.options);
chrome.test.succeed();
},

async function setOptionsRequiresMatchingTypes_Bool() {
// Bool options can only be set from a bool.
const options = [
{name: 'bool1', type: OptionType.BOOL, value: true}, // OK.
{name: 'bool2', type: OptionType.BOOL, value: 1}, // Wrong type.
{name: 'bool3', type: OptionType.BOOL, value: 'true'}, // Wrong type.
{name: 'bool4', type: OptionType.BOOL, value: [1]} // Wrong type.
];

const scannerHandle = await getScannerHandle();
chrome.test.assertNe(null, scannerHandle);

const response = await setOptions(scannerHandle, options);
chrome.test.assertEq(scannerHandle, response.scannerHandle);
chrome.test.assertEq(options.length, response.results.length);
// Match each result individually instead of one big array to make it easier
// to tell where any failures occur.
chrome.test.assertEq(
{name: 'bool1', result: OperationResult.SUCCESS}, response.results[0]);
chrome.test.assertEq(
{name: 'bool2', result: OperationResult.WRONG_TYPE},
response.results[1]);
chrome.test.assertEq(
{name: 'bool3', result: OperationResult.WRONG_TYPE},
response.results[2]);
chrome.test.assertEq(
{name: 'bool4', result: OperationResult.WRONG_TYPE},
response.results[3]);

chrome.test.assertNe(null, response.options);
chrome.test.succeed();
},

async function setOptionsRequiresMatchingTypes_String() {
// String options can only be set from a string.
const options = [
{name: 'string1', type: OptionType.STRING, value: 's'}, // OK.
{name: 'string2', type: OptionType.STRING, value: ''}, // OK.
{name: 'string3', type: OptionType.STRING, value: 1}, // Wrong type.
{name: 'string4', type: OptionType.STRING, value: [1]}, // Wrong type.
{name: 'string5', type: OptionType.STRING, value: true}, // Wrong type.
];

const scannerHandle = await getScannerHandle();
chrome.test.assertNe(null, scannerHandle);

const response = await setOptions(scannerHandle, options);
chrome.test.assertEq(scannerHandle, response.scannerHandle);
chrome.test.assertEq(options.length, response.results.length);
// Match each result individually instead of one big array to make it easier
// to tell where any failures occur.
chrome.test.assertEq(
{name: 'string1', result: OperationResult.SUCCESS}, response.results[0]);
chrome.test.assertEq(
{name: 'string2', result: OperationResult.SUCCESS}, response.results[1]);
chrome.test.assertEq(
{name: 'string3', result: OperationResult.WRONG_TYPE},
response.results[2]);
chrome.test.assertEq(
{name: 'string4', result: OperationResult.WRONG_TYPE},
response.results[3]);
chrome.test.assertEq(
{name: 'string5', result: OperationResult.WRONG_TYPE},
response.results[4]);

chrome.test.assertNe(null, response.options);
chrome.test.succeed();
}
]);
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// found in the LICENSE file.

OperationResult = chrome.documentScan.OperationResult;
OptionType = chrome.documentScan.OptionType;

async function getScannerList(filter) {
return new Promise(resolve => {
Expand Down Expand Up @@ -67,3 +68,9 @@ async function readScanData(jobHandle) {
chrome.documentScan.readScanData(jobHandle, resolve);
});
}

async function setOptions(scannerHandle, options) {
return new Promise(resolve => {
chrome.documentScan.setOptions(scannerHandle, options, resolve);
});
}
1 change: 1 addition & 0 deletions extensions/browser/extension_function_histogram_value.h
Original file line number Diff line number Diff line change
Expand Up @@ -1919,6 +1919,7 @@ enum HistogramValue {
DOCUMENTSCAN_STARTSCAN = 1857,
DOCUMENTSCAN_CANCELSCAN = 1858,
DOCUMENTSCAN_READSCANDATA = 1859,
DOCUMENTSCAN_SETOPTIONS = 1860,
// Last entry: Add new entries above, then run:
// tools/metrics/histograms/update_extension_histograms.py
ENUM_BOUNDARY
Expand Down
1 change: 1 addition & 0 deletions tools/metrics/histograms/enums.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16517,6 +16517,7 @@ Called by update_extension_histograms.py.-->
<int value="1857" label="DOCUMENTSCAN_STARTSCAN"/>
<int value="1858" label="DOCUMENTSCAN_CANCELSCAN"/>
<int value="1859" label="DOCUMENTSCAN_READSCANDATA"/>
<int value="1860" label="DOCUMENTSCAN_SETOPTIONS"/>
</enum>

<enum name="ExtensionInProgressRequestState">
Expand Down

0 comments on commit 8f1e0b8

Please sign in to comment.