Bug 1432719 - Notify user on speechd errors r=eeejay,fluent-reviewers,flod

Differential Revision: https://phabricator.services.mozilla.com/D176532
This commit is contained in:
Alexandre Lissy 2023-06-06 06:28:14 +00:00
Родитель 565cfe4d40
Коммит 25a528496c
14 изменённых файлов: 218 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,10 @@
/* vim: set ts=2 sw=2 sts=2 et tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
export class SpeechDispatcherChild extends JSWindowActorChild {
observe(aSubject, aTopic, aData) {
this.sendAsyncMessage("SpeechDispatcher:Error", aData);
}
}

Просмотреть файл

@ -0,0 +1,90 @@
/* vim: set ts=2 sw=2 sts=2 et tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
export class SpeechDispatcherParent extends JSWindowActorParent {
prefName() {
return "media.webspeech.synth.dont_notify_on_error";
}
disableNotification() {
Services.prefs.setBoolPref(this.prefName(), true);
}
async receiveMessage(aMessage) {
// The top level browsing context's embedding element should be a xul browser element.
let browser = this.browsingContext.top.embedderElement;
if (!browser) {
// We don't have a browser so bail!
return;
}
let notificationId;
if (Services.prefs.getBoolPref(this.prefName(), false)) {
console.info("Opted out from speech-dispatcher error notification");
return;
}
let messageId;
switch (aMessage.data) {
case "lib-missing":
messageId = "speech-dispatcher-lib-missing";
break;
case "lib-too-old":
messageId = "speech-dispatcher-lib-too-old";
break;
case "missing-symbol":
messageId = "speech-dispatcher-missing-symbol";
break;
case "open-fail":
messageId = "speech-dispatcher-open-fail";
break;
case "no-voices":
messageId = "speech-dispatcher-no-voices";
break;
default:
break;
}
let MozXULElement = browser.ownerGlobal.MozXULElement;
MozXULElement.insertFTLIfNeeded("browser/speechDispatcher.ftl");
// Now actually create the notification
let notificationBox = browser.getTabBrowser().getNotificationBox(browser);
if (notificationBox.getNotificationWithValue(notificationId)) {
return;
}
let buttons = [
{
supportPage: "speechd-setup",
},
{
"l10n-id": "speech-dispatcher-dismiss-button",
callback: () => {
this.disableNotification();
},
},
];
let iconURL = "chrome://browser/skin/drm-icon.svg";
notificationBox.appendNotification(
notificationId,
{
label: { "l10n-id": messageId },
image: iconURL,
priority: notificationBox.PRIORITY_INFO_HIGH,
type: "warning",
},
buttons
);
}
}

Просмотреть файл

@ -82,6 +82,8 @@ FINAL_TARGET_FILES.actors += [
"ScreenshotsComponentChild.sys.mjs",
"SearchSERPTelemetryChild.sys.mjs",
"SearchSERPTelemetryParent.sys.mjs",
"SpeechDispatcherChild.sys.mjs",
"SpeechDispatcherParent.sys.mjs",
"SwitchDocumentDirectionChild.sys.mjs",
"WebRTCChild.sys.mjs",
"WebRTCParent.sys.mjs",

Просмотреть файл

@ -752,6 +752,20 @@ let JSWINDOWACTORS = {
matches: ["about:studies*"],
},
SpeechDispatcher: {
parent: {
esModuleURI: "resource:///actors/SpeechDispatcherParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/SpeechDispatcherChild.sys.mjs",
observers: ["chrome-synth-voices-error"],
},
messageManagerGroups: ["browsers"],
allFrames: true,
},
ASRouter: {
parent: {
esModuleURI: "resource:///actors/ASRouterParent.sys.mjs",

Просмотреть файл

@ -0,0 +1,15 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
### Speech Dispatches is the name of a speech synthesis tool and shouldnt be
### localized (https://freebsoft.org/speechd).
speech-dispatcher-lib-missing = You cant use speech synthesis because the Speech Dispatcher library is missing.
speech-dispatcher-lib-too-old = You cant use speech synthesis because Speech Dispatcher needs to be updated.
speech-dispatcher-missing-symbol = You cant use speech synthesis because the Speech Dispatcher library is broken.
speech-dispatcher-open-fail = You cant use speech synthesis because Speech Dispatcher wont open.
speech-dispatcher-no-voices = You cant use speech synthesis because voices arent available in Speech Dispatcher.
speech-dispatcher-dismiss-button =
.label = Dont show again
.accesskey = D

Просмотреть файл

@ -65,6 +65,7 @@ SpeechSynthesis::SpeechSynthesis(nsPIDOMWindowInner* aParent)
if (obs) {
obs->AddObserver(this, "inner-window-destroyed", true);
obs->AddObserver(this, "synth-voices-changed", true);
obs->AddObserver(this, "synth-voices-error", true);
}
}
@ -307,6 +308,23 @@ SpeechSynthesis::Observe(nsISupports* aSubject, const char* aTopic,
AdvanceQueue();
}
}
} else if (strcmp(aTopic, "synth-voices-error") == 0) {
NS_WARNING("SpeechSynthesis::Observe: synth-voices-error");
LOG(LogLevel::Debug, ("SpeechSynthesis::onvoiceserror"));
nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (obs) {
obs->NotifyObservers(window, "chrome-synth-voices-error", aData);
}
if (!mSpeechQueue.IsEmpty()) {
for (RefPtr<SpeechSynthesisUtterance>& utterance : mSpeechQueue) {
utterance->DispatchSpeechSynthesisEvent(u"error"_ns, 0, nullptr, 0,
u""_ns);
}
mSpeechQueue.Clear();
}
}
return NS_OK;

Просмотреть файл

@ -36,6 +36,8 @@ child:
async NotifyVoicesChanged();
async NotifyVoicesError(nsString aError);
async InitialVoicesAndState(RemoteVoice[] aVoices, nsString[] aDefaults,
bool aIsSpeaking);

Просмотреть файл

@ -52,6 +52,12 @@ mozilla::ipc::IPCResult SpeechSynthesisChild::RecvNotifyVoicesChanged() {
return IPC_OK();
}
mozilla::ipc::IPCResult SpeechSynthesisChild::RecvNotifyVoicesError(
const nsAString& aError) {
nsSynthVoiceRegistry::RecvNotifyVoicesError(aError);
return IPC_OK();
}
PSpeechSynthesisRequestChild*
SpeechSynthesisChild::AllocPSpeechSynthesisRequestChild(
const nsAString& aText, const nsAString& aLang, const nsAString& aUri,

Просмотреть файл

@ -36,6 +36,8 @@ class SpeechSynthesisChild : public PSpeechSynthesisChild {
mozilla::ipc::IPCResult RecvNotifyVoicesChanged();
mozilla::ipc::IPCResult RecvNotifyVoicesError(const nsAString& aError);
protected:
SpeechSynthesisChild();
virtual ~SpeechSynthesisChild();

Просмотреть файл

@ -38,6 +38,11 @@ interface nsISynthVoiceRegistry : nsISupports
*/
void notifyVoicesChanged();
/**
* Notify chrome code of an error when starting speech synthesis service
*/
void notifyVoicesError(in AString aError);
/**
* Set a voice as default.
*

Просмотреть файл

@ -282,6 +282,15 @@ void nsSynthVoiceRegistry::RecvNotifyVoicesChanged() {
gSynthVoiceRegistry->NotifyVoicesChanged();
}
void nsSynthVoiceRegistry::RecvNotifyVoicesError(const nsAString& aError) {
// If we dont have a local instance of the registry yet, we don't care.
if (!gSynthVoiceRegistry) {
return;
}
gSynthVoiceRegistry->NotifyVoicesError(aError);
}
NS_IMETHODIMP
nsSynthVoiceRegistry::AddVoice(nsISpeechService* aService,
const nsAString& aUri, const nsAString& aName,
@ -369,6 +378,27 @@ nsSynthVoiceRegistry::NotifyVoicesChanged() {
return NS_OK;
}
NS_IMETHODIMP
nsSynthVoiceRegistry::NotifyVoicesError(const nsAString& aError) {
if (XRE_IsParentProcess()) {
nsTArray<SpeechSynthesisParent*> ssplist;
GetAllSpeechSynthActors(ssplist);
for (uint32_t i = 0; i < ssplist.Length(); ++i) {
Unused << ssplist[i]->SendNotifyVoicesError(aError);
}
}
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (NS_WARN_IF(!(obs))) {
return NS_ERROR_NOT_AVAILABLE;
}
obs->NotifyObservers(nullptr, "synth-voices-error", aError.BeginReading());
return NS_OK;
}
NS_IMETHODIMP
nsSynthVoiceRegistry::SetDefaultVoice(const nsAString& aUri, bool aIsDefault) {
bool found = false;

Просмотреть файл

@ -65,6 +65,8 @@ class nsSynthVoiceRegistry final : public nsISynthVoiceRegistry {
static void RecvNotifyVoicesChanged();
static void RecvNotifyVoicesError(const nsAString& aError);
private:
virtual ~nsSynthVoiceRegistry();

Просмотреть файл

@ -322,6 +322,7 @@ void SpeechDispatcherService::Setup() {
if (!speechdLib) {
NS_WARNING("Failed to load speechd library");
NotifyError(u"lib-missing"_ns);
return;
}
@ -329,6 +330,7 @@ void SpeechDispatcherService::Setup() {
// There is no version getter function, so we rely on a symbol that was
// introduced in release 0.8.2 in order to check for ABI compatibility.
NS_WARNING("Unsupported version of speechd detected");
NotifyError(u"lib-too-old"_ns);
return;
}
@ -340,6 +342,7 @@ void SpeechDispatcherService::Setup() {
NS_WARNING(nsPrintfCString("Failed to find speechd symbol for'%s'",
kSpeechDispatcherSymbols[i].functionName)
.get());
NotifyError(u"missing-symbol"_ns);
return;
}
}
@ -348,6 +351,7 @@ void SpeechDispatcherService::Setup() {
spd_open("firefox", "web speech api", "who", SPD_MODE_THREADED);
if (!mSpeechdClient) {
NS_WARNING("Failed to call spd_open");
NotifyError(u"open-fail"_ns);
return;
}
@ -386,6 +390,10 @@ void SpeechDispatcherService::Setup() {
}
}
if (mVoices.Count() == 0) {
NotifyError(u"no-voices"_ns);
}
NS_DispatchToMainThread(
NewRunnableMethod("dom::SpeechDispatcherService::RegisterVoices", this,
&SpeechDispatcherService::RegisterVoices));
@ -395,6 +403,18 @@ void SpeechDispatcherService::Setup() {
// private methods
void SpeechDispatcherService::NotifyError(const nsString& aError) {
if (!NS_IsMainThread()) {
NS_DispatchToMainThread(NewRunnableMethod<const nsString>(
"dom::SpeechDispatcherService::NotifyError", this,
&SpeechDispatcherService::NotifyError, aError));
return;
}
RefPtr<nsSynthVoiceRegistry> registry = nsSynthVoiceRegistry::GetInstance();
DebugOnly<nsresult> rv = registry->NotifyVoicesError(aError);
}
void SpeechDispatcherService::RegisterVoices() {
RefPtr<nsSynthVoiceRegistry> registry = nsSynthVoiceRegistry::GetInstance();
for (const auto& entry : mVoices) {

Просмотреть файл

@ -47,6 +47,8 @@ class SpeechDispatcherService final : public nsIObserver,
private:
virtual ~SpeechDispatcherService();
void NotifyError(const nsString& aError);
void RegisterVoices();
bool mInitialized;