diff --git a/content/browser/speech/speech_recognition_dispatcher_host.cc b/content/browser/speech/speech_recognition_dispatcher_host.cc index 3cc9651bc822e..5321a3a331d99 100644 --- a/content/browser/speech/speech_recognition_dispatcher_host.cc +++ b/content/browser/speech/speech_recognition_dispatcher_host.cc @@ -32,6 +32,7 @@ #include "mojo/public/cpp/bindings/message.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom.h" #if !BUILDFLAG(IS_ANDROID) #include "components/soda/soda_util.h" @@ -175,6 +176,13 @@ void SpeechRecognitionDispatcherHost::StartRequestOnUI( storage_partition == browser_context->GetDefaultStoragePartition() ? true : !rfh->GetLastCommittedURL().SchemeIsHTTPOrHTTPS(); + // Browser-side enforcement of the on-device-speech-recognition + // Permissions-Policy. The renderer is expected to gate `on_device` itself, + // but a compromised renderer can lie, so re-check authoritatively here. + if (!rfh->IsFeatureEnabled(network::mojom::PermissionsPolicyFeature:: + kOnDeviceSpeechRecognition)) { + can_render_frame_use_on_device = false; + } const std::string& language = SpeechRecognitionDispatcherHost::GetAcceptedLanguages( params->language, diff --git a/third_party/blink/renderer/modules/speech/speech_recognition.cc b/third_party/blink/renderer/modules/speech/speech_recognition.cc index 386be511d2335..befcccb0170aa 100644 --- a/third_party/blink/renderer/modules/speech/speech_recognition.cc +++ b/third_party/blink/renderer/modules/speech/speech_recognition.cc @@ -605,10 +605,18 @@ void SpeechRecognition::StartController( GetExecutionContext()->GetTaskRunner(TaskType::kMiscPlatformAPI)); receiver_.set_disconnect_handler(BindOnce( &SpeechRecognition::OnConnectionError, WrapWeakPersistent(this))); + // Only request on-device recognition when the frame is permitted to use it + // (same gates as available()/install()): the on-device-speech-recognition + // Permissions-Policy must be granted and the frame must not be a cross-site + // subframe. + const bool on_device_allowed = + DomWindow() && !DomWindow()->IsCrossSiteSubframeIncludingScheme() && + DomWindow()->IsFeatureEnabled(network::mojom::PermissionsPolicyFeature:: + kOnDeviceSpeechRecognition); auto params = controller_->BuildStartSpeechRecognitionRequestParams( std::move(session_receiver), std::move(session_client), *grammars_, phrases_.Get(), lang_, continuous_, interim_results_, max_alternatives_, - /*on_device=*/true, // On-device speech recognition is always preferred. + /*on_device=*/on_device_allowed, /*allow_cloud_fallback=*/!process_locally_, SpeechRecognitionQualityToMojom(quality_), std::move(audio_forwarder_receiver), std::move(audio_parameters));