зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
565cfe4d40
Коммит
25a528496c
|
@ -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 shouldn’t be
|
||||
### localized (https://freebsoft.org/speechd).
|
||||
|
||||
speech-dispatcher-lib-missing = You can’t use speech synthesis because the Speech Dispatcher library is missing.
|
||||
speech-dispatcher-lib-too-old = You can’t use speech synthesis because Speech Dispatcher needs to be updated.
|
||||
speech-dispatcher-missing-symbol = You can’t use speech synthesis because the Speech Dispatcher library is broken.
|
||||
speech-dispatcher-open-fail = You can’t use speech synthesis because Speech Dispatcher won’t open.
|
||||
speech-dispatcher-no-voices = You can’t use speech synthesis because voices aren’t available in Speech Dispatcher.
|
||||
speech-dispatcher-dismiss-button =
|
||||
.label = Don’t 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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче