183 lines
6.4 KiB
HTML
183 lines
6.4 KiB
HTML
<!DOCTYPE html>
|
|
<title>SpeechRecognition availableOnDevice</title>
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<script>
|
|
promise_test(async (t) => {
|
|
const lang = "en-US";
|
|
window.SpeechRecognition = window.SpeechRecognition ||
|
|
window.webkitSpeechRecognition;
|
|
|
|
// Test that it returns a promise.
|
|
const resultPromise = SpeechRecognition.availableOnDevice(lang);
|
|
assert_true(
|
|
resultPromise instanceof Promise,
|
|
"availableOnDevice should return a Promise."
|
|
);
|
|
|
|
// Verify the resolved value is a string.
|
|
const result = await resultPromise;
|
|
assert_true(
|
|
typeof result === "string",
|
|
"The resolved value of the availableOnDevice promise should be a string."
|
|
);
|
|
|
|
assert_true(
|
|
result === "unavailable" || result === "downloadable" ||
|
|
result === "downloading" || result === "available",
|
|
"The resolved value of the availableOnDevice promise should be a " +
|
|
"valid value."
|
|
);
|
|
}, "SpeechRecognition.availableOnDevice resolves with a string value.");
|
|
|
|
promise_test(async (t) => {
|
|
const iframe = document.createElement("iframe");
|
|
document.body.appendChild(iframe);
|
|
const frameWindow = iframe.contentWindow;
|
|
const frameDOMException = frameWindow.DOMException;
|
|
const frameSpeechRecognition =
|
|
frameWindow.SpeechRecognition || frameWindow.webkitSpeechRecognition;
|
|
|
|
iframe.remove();
|
|
await promise_rejects_dom(
|
|
t,
|
|
"InvalidStateError",
|
|
frameDOMException,
|
|
frameSpeechRecognition.availableOnDevice("en-US"),
|
|
);
|
|
}, "SpeechRecognition.availableOnDevice rejects in a detached context.");
|
|
|
|
promise_test(async (t) => {
|
|
const iframe = document.createElement("iframe");
|
|
// This policy should make the on-device speech recognition
|
|
// feature unavailable.
|
|
iframe.setAttribute("allow", "on-device-speech-recognition 'none'");
|
|
document.body.appendChild(iframe);
|
|
t.add_cleanup(() => iframe.remove());
|
|
|
|
await new Promise(resolve => {
|
|
if (iframe.contentWindow &&
|
|
iframe.contentWindow.document.readyState === 'complete') {
|
|
resolve();
|
|
} else {
|
|
iframe.onload = resolve;
|
|
}
|
|
});
|
|
|
|
const frameWindow = iframe.contentWindow;
|
|
const frameSpeechRecognition = frameWindow.SpeechRecognition ||
|
|
frameWindow.webkitSpeechRecognition;
|
|
|
|
assert_true(!!frameSpeechRecognition,
|
|
"SpeechRecognition should exist in iframe.");
|
|
assert_true(!!frameSpeechRecognition.availableOnDevice,
|
|
"availableOnDevice method should exist on SpeechRecognition in iframe.");
|
|
|
|
// Call availableOnDevice and expect it to resolve to "unavailable".
|
|
const availabilityStatus =
|
|
await frameSpeechRecognition.availableOnDevice("en-US");
|
|
assert_equals(availabilityStatus, "unavailable",
|
|
"availableOnDevice should resolve to 'unavailable' if " +
|
|
"'on-device-speech-recognition' Permission Policy is 'none'."
|
|
);
|
|
}, "SpeechRecognition.availableOnDevice resolves to 'unavailable' if " +
|
|
"'on-device-speech-recognition' Permission Policy is 'none'.");
|
|
|
|
promise_test(async (t) => {
|
|
const html = `
|
|
<!DOCTYPE html>
|
|
<script>
|
|
window.addEventListener('message', async (event) => {
|
|
// Ensure we only process the message intended to trigger the test.
|
|
if (event.data !== "runTestCallAvailableOnDevice") return;
|
|
|
|
try {
|
|
const SpeechRecognition = window.SpeechRecognition ||
|
|
window.webkitSpeechRecognition;
|
|
if (!SpeechRecognition || !SpeechRecognition.availableOnDevice) {
|
|
parent.postMessage({
|
|
type: "error", // Use "error" for API not found or other issues.
|
|
name: "NotSupportedError",
|
|
message: "SpeechRecognition.availableOnDevice API not " +
|
|
"available in iframe"
|
|
}, "*");
|
|
return;
|
|
}
|
|
|
|
// Call availableOnDevice and post its resolution.
|
|
const availabilityStatus =
|
|
await SpeechRecognition.availableOnDevice("en-US");
|
|
parent.postMessage(
|
|
{ type: "resolution", result: availabilityStatus },
|
|
"*"
|
|
); // Post the string status
|
|
} catch (err) {
|
|
// Catch any unexpected errors during the API call or message post.
|
|
parent.postMessage({
|
|
type: "error",
|
|
name: err.name,
|
|
message: err.message
|
|
}, "*");
|
|
}
|
|
});
|
|
<\/script>
|
|
`;
|
|
|
|
const blob = new Blob([html], { type: "text/html" });
|
|
const blobUrl = URL.createObjectURL(blob);
|
|
// Important: Revoke the blob URL after the test to free up resources.
|
|
t.add_cleanup(() => URL.revokeObjectURL(blobUrl));
|
|
|
|
const iframe = document.createElement("iframe");
|
|
iframe.src = blobUrl;
|
|
// Sandboxing with "allow-scripts" is needed for the script inside
|
|
// the iframe to run.
|
|
// The cross-origin nature is primarily due to the blob URL's origin being
|
|
// treated as distinct from the parent page's origin for security
|
|
// purposes.
|
|
iframe.setAttribute("sandbox", "allow-scripts");
|
|
document.body.appendChild(iframe);
|
|
t.add_cleanup(() => iframe.remove());
|
|
|
|
await new Promise(resolve => iframe.onload = resolve);
|
|
|
|
const testResult = await new Promise((resolve, reject) => {
|
|
const timeoutId = t.step_timeout(() => {
|
|
reject(new Error("Test timed out waiting for message from iframe. " +
|
|
"Ensure iframe script is correctly posting a message."));
|
|
}, 6000); // 6-second timeout
|
|
|
|
window.addEventListener("message", t.step_func((event) => {
|
|
// Basic check to ensure the message is from our iframe.
|
|
if (event.source !== iframe.contentWindow) return;
|
|
clearTimeout(timeoutId);
|
|
resolve(event.data);
|
|
}));
|
|
|
|
// Send a distinct message to the iframe to trigger its test logic.
|
|
iframe.contentWindow.postMessage("runTestCallAvailableOnDevice", "*");
|
|
});
|
|
|
|
// Check if the iframe's script reported an error (e.g., API not found).
|
|
if (testResult.type === "error") {
|
|
const errorMessage =
|
|
`Iframe reported an error: ${testResult.name} - ` +
|
|
testResult.message;
|
|
assert_unreached(errorMessage);
|
|
}
|
|
|
|
assert_equals(
|
|
testResult.type,
|
|
"resolution",
|
|
"The call from the iframe should resolve and post a 'resolution' " +
|
|
"message."
|
|
);
|
|
assert_equals(
|
|
testResult.result, // Expecting the string "unavailable".
|
|
"unavailable",
|
|
"availableOnDevice should resolve to 'unavailable' in a cross-origin " +
|
|
"iframe."
|
|
);
|
|
}, "SpeechRecognition.availableOnDevice should resolve to 'unavailable' " +
|
|
"in a cross-origin iframe.");
|
|
</script>
|