1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
|
<!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>
|