Source/WebKit/ChangeLog

 12021-01-07 Sihui Liu <sihui_liu@appe.com>
 2
 3 Make SpeechRecognition permission error more informative
 4 https://bugs.webkit.org/show_bug.cgi?id=220436
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Make SpeechRecognitionPermissionManager complete requests with an optional SpeechRecognitionError instead of a
 9 boolean value.
 10
 11 * UIProcess/SpeechRecognitionPermissionManager.cpp:
 12 (WebKit::SpeechRecognitionPermissionManager::~SpeechRecognitionPermissionManager):
 13 (WebKit::SpeechRecognitionPermissionManager::request):
 14 (WebKit::SpeechRecognitionPermissionManager::startProcessingRequest):
 15 (WebKit::SpeechRecognitionPermissionManager::continueProcessingRequest):
 16 (WebKit::SpeechRecognitionPermissionManager::completeCurrentRequest):
 17 (WebKit::SpeechRecognitionPermissionManager::requestSpeechRecognitionServiceAccess):
 18 (WebKit::SpeechRecognitionPermissionManager::requestMicrophoneAccess):
 19 (WebKit::SpeechRecognitionPermissionManager::requestUserPermission):
 20 * UIProcess/SpeechRecognitionPermissionManager.h:
 21 * UIProcess/SpeechRecognitionPermissionRequest.h:
 22 (WebKit::SpeechRecognitionPermissionRequest::create):
 23 (WebKit::SpeechRecognitionPermissionRequest::complete):
 24 (WebKit::SpeechRecognitionPermissionRequest::SpeechRecognitionPermissionRequest):
 25 * UIProcess/SpeechRecognitionServer.cpp:
 26 (WebKit::SpeechRecognitionServer::requestPermissionForRequest):
 27 * UIProcess/SpeechRecognitionServer.h:
 28 * UIProcess/WebPageProxy.cpp:
 29 (WebKit::WebPageProxy::requestSpeechRecognitionPermission):
 30 * UIProcess/WebPageProxy.h:
 31 * UIProcess/WebProcessProxy.cpp:
 32 (WebKit::WebProcessProxy::createSpeechRecognitionServer):
 33
1342021-01-07 Sihui Liu <sihui_liu@appe.com>
235
336 ASSERTION FAILED: !m_messageReceiverMapCount under WebKit::SpeechRecognitionServer::~SpeechRecognitionServer()

Source/WebKit/UIProcess/SpeechRecognitionPermissionManager.cpp

@@SpeechRecognitionPermissionManager::SpeechRecognitionPermissionManager(WebPagePr
7272SpeechRecognitionPermissionManager::~SpeechRecognitionPermissionManager()
7373{
7474 for (auto& request : m_requests)
75  request->complete(SpeechRecognitionPermissionDecision::Deny);
 75 request->complete(SpeechRecognitionError { SpeechRecognitionErrorType::NotAllowed, "Permission manager has exited"_s });
7676}
7777
78 void SpeechRecognitionPermissionManager::request(const String& lang, const WebCore::ClientOrigin& origin, CompletionHandler<void(SpeechRecognitionPermissionDecision)>&& completiontHandler)
 78void SpeechRecognitionPermissionManager::request(const String& lang, const WebCore::ClientOrigin& origin, CompletionHandler<void(Optional<WebCore::SpeechRecognitionError>)>&& completiontHandler)
7979{
8080 m_requests.append(SpeechRecognitionPermissionRequest::create(lang, origin, WTFMove(completiontHandler)));
8181 if (m_requests.size() == 1)

@@void SpeechRecognitionPermissionManager::startProcessingRequest()
9494{
9595#if PLATFORM(COOCA)
9696 if (!checkSandboxRequirementForType(MediaPermissionType::Audio)) {
97  completeCurrentRequest(SpeechRecognitionPermissionDecision::Deny);
 97 completeCurrentRequest(SpeechRecognitionError { SpeechRecognitionErrorType::NotAllowed, "Sandbox check has failed"_s });
9898 return;
9999 }
100100#endif

@@void SpeechRecognitionPermissionManager::startProcessingRequest()
110110 m_speechRecognitionServiceCheck = CheckResult::Granted;
111111 }
112112
 113 if (m_microphoneCheck == CheckResult::Denied) {
 114 completeCurrentRequest(SpeechRecognitionError { SpeechRecognitionErrorType::NotAllowed, "Microphone permission check has failed"_s });
 115 return;
 116 }
 117
 118 if (m_speechRecognitionServiceCheck == CheckResult::Denied) {
 119 completeCurrentRequest(SpeechRecognitionError { SpeechRecognitionErrorType::ServiceNotAllowed, "Speech recognition service permission check has failed"_s });
 120 return;
 121 }
 122
113123#if HAVE(SPEECHRECOGNIZER)
114124 if (!mockCaptureDevicesEnabled && m_speechRecognitionServiceCheck != CheckResult::Denied) {
115125 // Speech recognition service can be unavailable when user does not enable dictation in system settings.
116126 // Let's avoid prompting user in that case.
117  if (!checkSpeechRecognitionServiceAvailability(m_requests.first()->lang()))
118  m_speechRecognitionServiceCheck = CheckResult::Denied;
 127 if (!checkSpeechRecognitionServiceAvailability(m_requests.first()->lang())) {
 128 completeCurrentRequest(SpeechRecognitionError { SpeechRecognitionErrorType::ServiceNotAllowed, "Speech recognition service is not available"_s });
 129 return;
 130 }
119131 }
120132#endif
121133
122  if (m_microphoneCheck == CheckResult::Denied || m_speechRecognitionServiceCheck == CheckResult::Denied) {
123  completeCurrentRequest(SpeechRecognitionPermissionDecision::Deny);
124  return;
125  }
126 
127134 // We currently don't allow third-party access.
128135 if (m_userPermissionCheck == CheckResult::Unknown) {
129136 auto clientOrigin = m_requests.first()->origin();

@@void SpeechRecognitionPermissionManager::startProcessingRequest()
134141 }
135142
136143 if (m_userPermissionCheck == CheckResult::Denied) {
137  completeCurrentRequest(SpeechRecognitionPermissionDecision::Deny);
 144 completeCurrentRequest(SpeechRecognitionError { SpeechRecognitionErrorType::NotAllowed, "User permission check has failed"_s });
138145 return;
139146 }
140147

@@void SpeechRecognitionPermissionManager::continueProcessingRequest()
162169 ASSERT(m_userPermissionCheck == CheckResult::Granted);
163170
164171 if (!m_page.isViewVisible()) {
165  completeCurrentRequest(SpeechRecognitionPermissionDecision::Deny);
 172 completeCurrentRequest(SpeechRecognitionError { SpeechRecognitionErrorType::NotAllowed, "Page is not visible to user" });
166173 return;
167174 }
168175
169  completeCurrentRequest(SpeechRecognitionPermissionDecision::Grant);
 176 completeCurrentRequest();
170177}
171178
172 void SpeechRecognitionPermissionManager::completeCurrentRequest(SpeechRecognitionPermissionDecision decision)
 179void SpeechRecognitionPermissionManager::completeCurrentRequest(Optional<SpeechRecognitionError> error)
173180{
174181 ASSERT(!m_requests.isEmpty());
175182 auto currentRequest = m_requests.takeFirst();
176  currentRequest->complete(decision);
 183 currentRequest->complete(error);
177184
178185 startNextRequest();
179186}

@@void SpeechRecognitionPermissionManager::requestSpeechRecognitionServiceAccess()
189196
190197 m_speechRecognitionServiceCheck = authorized ? CheckResult::Granted : CheckResult::Denied;
191198 if (m_speechRecognitionServiceCheck == CheckResult::Denied) {
192  completeCurrentRequest(SpeechRecognitionPermissionDecision::Deny);
 199 completeCurrentRequest(SpeechRecognitionError { SpeechRecognitionErrorType::ServiceNotAllowed, "Speech recognition service permission check has failed"_s });
193200 return;
194201 }
195202

@@void SpeechRecognitionPermissionManager::requestMicrophoneAccess()
209216
210217 m_microphoneCheck = authorized ? CheckResult::Granted : CheckResult::Denied;
211218 if (m_microphoneCheck == CheckResult::Denied) {
212  completeCurrentRequest(SpeechRecognitionPermissionDecision::Deny);
 219 completeCurrentRequest(SpeechRecognitionError { SpeechRecognitionErrorType::NotAllowed, "Microphone permission check has failed"_s });
213220 return;
214221 }
215222

@@void SpeechRecognitionPermissionManager::requestUserPermission()
231238
232239 m_userPermissionCheck = granted ? CheckResult::Granted : CheckResult::Denied;
233240 if (m_userPermissionCheck == CheckResult::Denied) {
234  completeCurrentRequest(SpeechRecognitionPermissionDecision::Deny);
 241 completeCurrentRequest(SpeechRecognitionError { SpeechRecognitionErrorType::NotAllowed, "User permission check has failed"_s });
235242 return;
236243 }
237244

@@void SpeechRecognitionPermissionManager::requestUserPermission()
240247 m_page.uiClient().decidePolicyForSpeechRecognitionPermissionRequest(m_page, API::SecurityOrigin::create(topOrigin.get()).get(), WTFMove(decisionHandler));
241248}
242249
243 
244 
245250void SpeechRecognitionPermissionManager::decideByDefaultAction(const WebCore::SecurityOrigin& origin, CompletionHandler<void(bool)>&& completionHandler)
246251{
247252#if PLATFORM(COCOA)

Source/WebKit/UIProcess/SpeechRecognitionPermissionManager.h

@@public:
3939 enum class CheckResult { Denied, Granted, Unknown };
4040 explicit SpeechRecognitionPermissionManager(WebPageProxy&);
4141 ~SpeechRecognitionPermissionManager();
42  void request(const String& lang, const WebCore::ClientOrigin&, CompletionHandler<void(SpeechRecognitionPermissionDecision)>&&);
 42 void request(const String& lang, const WebCore::ClientOrigin&, CompletionHandler<void(Optional<WebCore::SpeechRecognitionError>)>&&);
4343
4444 void decideByDefaultAction(const WebCore::SecurityOrigin&, CompletionHandler<void(bool)>&&);
4545 WebPageProxy& page() { return m_page; }

@@private:
4848 void startNextRequest();
4949 void startProcessingRequest();
5050 void continueProcessingRequest();
51  void completeCurrentRequest(SpeechRecognitionPermissionDecision);
 51 void completeCurrentRequest(Optional<WebCore::SpeechRecognitionError> = WTF::nullopt);
5252 void requestMicrophoneAccess();
5353 void requestSpeechRecognitionServiceAccess();
5454 void requestUserPermission();

Source/WebKit/UIProcess/SpeechRecognitionPermissionRequest.h

2727
2828#include "APIObject.h"
2929#include <WebCore/ClientOrigin.h>
 30#include <WebCore/SpeechRecognitionError.h>
3031#include <wtf/CompletionHandler.h>
3132
3233namespace WebKit {
3334
34 enum class SpeechRecognitionPermissionDecision : bool { Deny, Grant };
35 
3635class SpeechRecognitionPermissionRequest : public RefCounted<SpeechRecognitionPermissionRequest> {
3736public:
38  static Ref<SpeechRecognitionPermissionRequest> create(const String& lang, const WebCore::ClientOrigin& origin, CompletionHandler<void(SpeechRecognitionPermissionDecision)>&& completionHandler)
 37 static Ref<SpeechRecognitionPermissionRequest> create(const String& lang, const WebCore::ClientOrigin& origin, CompletionHandler<void(Optional<WebCore::SpeechRecognitionError>)>&& completionHandler)
3938 {
4039 return adoptRef(*new SpeechRecognitionPermissionRequest(lang, origin, WTFMove(completionHandler)));
4140 }
4241
43  void complete(SpeechRecognitionPermissionDecision decision)
 42 void complete(Optional<WebCore::SpeechRecognitionError> error)
4443 {
4544 auto completionHandler = std::exchange(m_completionHandler, { });
46  completionHandler(decision);
 45 completionHandler(error);
4746 }
4847
4948 const WebCore::ClientOrigin& origin() const { return m_origin; }
5049 const String& lang() const { return m_lang; }
5150
5251private:
53  SpeechRecognitionPermissionRequest(const String& lang, const WebCore::ClientOrigin& origin, CompletionHandler<void(SpeechRecognitionPermissionDecision)>&& completionHandler)
 52 SpeechRecognitionPermissionRequest(const String& lang, const WebCore::ClientOrigin& origin, CompletionHandler<void(Optional<WebCore::SpeechRecognitionError>)>&& completionHandler)
5453 : m_lang(lang)
5554 , m_origin(origin)
5655 , m_completionHandler(WTFMove(completionHandler))

@@private:
5857
5958 String m_lang;
6059 WebCore::ClientOrigin m_origin;
61  CompletionHandler<void(SpeechRecognitionPermissionDecision)> m_completionHandler;
 60 CompletionHandler<void(Optional<WebCore::SpeechRecognitionError>)> m_completionHandler;
6261};
6362
6463class SpeechRecognitionPermissionCallback : public API::ObjectImpl<API::Object::Type::SpeechRecognitionPermissionCallback> {

Source/WebKit/UIProcess/SpeechRecognitionServer.cpp

@@void SpeechRecognitionServer::start(WebCore::SpeechRecognitionConnectionClientId
6464
6565void SpeechRecognitionServer::requestPermissionForRequest(WebCore::SpeechRecognitionRequest& request)
6666{
67  m_permissionChecker(request.lang(), request.clientOrigin(), [this, weakThis = makeWeakPtr(this), weakRequest = makeWeakPtr(request)](auto decision) mutable {
 67 m_permissionChecker(request.lang(), request.clientOrigin(), [this, weakThis = makeWeakPtr(this), weakRequest = makeWeakPtr(request)](auto error) mutable {
6868 if (!weakThis)
6969 return;
7070

@@void SpeechRecognitionServer::requestPermissionForRequest(WebCore::SpeechRecogni
7272 return;
7373
7474 auto identifier = weakRequest->clientIdentifier();
75  if (decision == SpeechRecognitionPermissionDecision::Deny) {
 75 if (error) {
7676 m_requests.remove(identifier);
77  auto error = WebCore::SpeechRecognitionError { WebCore::SpeechRecognitionErrorType::NotAllowed, "Permission check failed"_s };
7877 sendUpdate(identifier, WebCore::SpeechRecognitionUpdateType::Error, error);
7978 return;
8079 }

Source/WebKit/UIProcess/SpeechRecognitionServer.h

@@struct ClientOrigin;
4343namespace WebKit {
4444
4545class WebProcessProxy;
46 enum class SpeechRecognitionPermissionDecision : bool;
4746
4847using SpeechRecognitionServerIdentifier = WebCore::PageIdentifier;
49 using SpeechRecognitionPermissionChecker = Function<void(const String&, const WebCore::ClientOrigin&, CompletionHandler<void(SpeechRecognitionPermissionDecision)>&&)>;
 48using SpeechRecognitionPermissionChecker = Function<void(const String&, const WebCore::ClientOrigin&, CompletionHandler<void(Optional<WebCore::SpeechRecognitionError>)>&&)>;
5049using SpeechRecognitionCheckIfMockSpeechRecognitionEnabled = Function<bool()>;
5150
5251class SpeechRecognitionServer : public CanMakeWeakPtr<SpeechRecognitionServer>, public IPC::MessageReceiver, private IPC::MessageSender {

Source/WebKit/UIProcess/WebPageProxy.cpp

@@void WebPageProxy::dispatchActivityStateUpdateForTesting()
1030210302 });
1030310303}
1030410304
10305 void WebPageProxy::requestSpeechRecognitionPermission(const String& lang, const WebCore::ClientOrigin& clientOrigin, CompletionHandler<void(SpeechRecognitionPermissionDecision)>&& completionHandler)
 10305void WebPageProxy::requestSpeechRecognitionPermission(const String& lang, const WebCore::ClientOrigin& clientOrigin, CompletionHandler<void(Optional<SpeechRecognitionError>)>&& completionHandler)
1030610306{
1030710307 if (!m_speechRecognitionPermissionManager)
1030810308 m_speechRecognitionPermissionManager = makeUnique<SpeechRecognitionPermissionManager>(*this);

Source/WebKit/UIProcess/WebPageProxy.h

@@struct MockWebAuthenticationConfiguration;
264264struct PrewarmInformation;
265265struct SecurityOriginData;
266266struct ShareData;
 267struct SpeechRecognitionError;
267268struct TextAlternativeWithRange;
268269struct TextCheckingResult;
269270struct ViewportAttributes;

@@struct UserMessage;
367368enum class CreateNewGroupForHighlight : bool;
368369enum class NegotiatedLegacyTLS : bool;
369370enum class ProcessSwapRequestedByClient : bool;
370 enum class SpeechRecognitionPermissionDecision : bool;
371371enum class UndoOrRedo : bool;
372372enum class WebContentMode : uint8_t;
373373

@@public:
18111811 void setMediaCaptureReportingDelay(Seconds captureReportingDelay) { m_mediaCaptureReportingDelay = captureReportingDelay; }
18121812 size_t suspendMediaPlaybackCounter() { return m_suspendMediaPlaybackCounter; }
18131813
1814  void requestSpeechRecognitionPermission(const String& lang, const WebCore::ClientOrigin&, CompletionHandler<void(SpeechRecognitionPermissionDecision)>&&);
 1814 void requestSpeechRecognitionPermission(const String& lang, const WebCore::ClientOrigin&, CompletionHandler<void(Optional<WebCore::SpeechRecognitionError>)>&&);
18151815 void requestSpeechRecognitionPermissionByDefaultAction(const WebCore::SecurityOrigin&, CompletionHandler<void(bool)>&&);
18161816
18171817 void syncIfMockDevicesEnabledChanged();

Source/WebKit/UIProcess/WebProcessProxy.cpp

@@void WebProcessProxy::createSpeechRecognitionServer(SpeechRecognitionServerIdent
17241724 auto& speechRecognitionServer = m_speechRecognitionServerMap.add(identifier, nullptr).iterator->value;
17251725 auto permissionChecker = [weakPage = makeWeakPtr(targetPage)](auto& lang, auto& origin, auto&& completionHandler) mutable {
17261726 if (!weakPage) {
1727  completionHandler(SpeechRecognitionPermissionDecision::Deny);
 1727 completionHandler(WebCore::SpeechRecognitionError { SpeechRecognitionErrorType::NotAllowed, "Page no longer exists"_s });
17281728 return;
17291729 }
17301730

Tools/ChangeLog

 12021-01-07 Sihui Liu <sihui_liu@appe.com>
 2
 3 Make SpeechRecognition permission error more informative
 4 https://bugs.webkit.org/show_bug.cgi?id=220436
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 * TestWebKitAPI/Tests/WebKitCocoa/SpeechRecognition.mm:
 9 (TestWebKitAPI::TEST):
 10
1112021-01-07 Sihui Liu <sihui_liu@appe.com>
212
313 ASSERTION FAILED: !m_messageReceiverMapCount under WebKit::SpeechRecognitionServer::~SpeechRecognitionServer()

Tools/TestWebKitAPI/Tests/WebKitCocoa/SpeechRecognition.mm

@@TEST(WebKit2, SpeechRecognitionUserPermissionPersistence)
119119 receivedScriptMessage = false;
120120 [webView loadTestPageNamed:@"speechrecognition-user-permission-persistence"];
121121 TestWebKitAPI::Util::run(&receivedScriptMessage);
122  EXPECT_WK_STREQ(@"Error: not-allowed - Permission check failed", [lastScriptMessage body]);
 122 EXPECT_WK_STREQ(@"Error: not-allowed - User permission check has failed", [lastScriptMessage body]);
123123 EXPECT_TRUE(permissionRequested);
124124
125125 // Permission result is remembered.

@@TEST(WebKit2, SpeechRecognitionUserPermissionPersistence)
127127 receivedScriptMessage = false;
128128 [webView stringByEvaluatingJavaScript:@"start()"];
129129 TestWebKitAPI::Util::run(&receivedScriptMessage);
130  EXPECT_WK_STREQ(@"Error: not-allowed - Permission check failed", [lastScriptMessage body]);
 130 EXPECT_WK_STREQ(@"Error: not-allowed - User permission check has failed", [lastScriptMessage body]);
131131 EXPECT_FALSE(permissionRequested);
132132
133133 // Permission result will be cleared after document changes.

@@TEST(WebKit2, SpeechRecognitionPageBecomesInvisible)
231231 receivedScriptMessage = false;
232232 [webView evaluateJavaScript:@"start()" completionHandler:nil];
233233 TestWebKitAPI::Util::run(&receivedScriptMessage);
234  EXPECT_WK_STREQ(@"Error: not-allowed - Permission check failed", [lastScriptMessage body]);
 234 EXPECT_WK_STREQ(@"Error: not-allowed - Page is not visible to user", [lastScriptMessage body]);
235235}
236236
237237TEST(WebKit2, SpeechRecognitionPageIsDestroyed)

LayoutTests/ChangeLog

 12021-01-07 Sihui Liu <sihui_liu@appe.com>
 2
 3 Make SpeechRecognition permission error more informative
 4 https://bugs.webkit.org/show_bug.cgi?id=220436
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 * fast/speechrecognition/permission-error-expected.txt:
 9 * fast/speechrecognition/permission-error.html:
 10
1112021-01-07 Lauro Moura <lmoura@igalia.com>
212
313 REGRESSION(r270425) [GTK] wheel scrolling stopped working

LayoutTests/fast/speechrecognition/permission-error-expected.txt

@@On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
66PASS recognition = new webkitSpeechRecognition() did not throw exception.
77PASS recognition.start() did not throw exception.
88PASS event.error is "not-allowed"
9 PASS event.message is "Permission check failed"
 9PASS event.message is "User permission check has failed"
1010PASS successfullyParsed is true
1111
1212TEST COMPLETE

LayoutTests/fast/speechrecognition/permission-error.html

@@shouldNotThrow("recognition = new webkitSpeechRecognition()");
1414shouldNotThrow("recognition.start()");
1515recognition.onerror = (event) => {
1616 shouldBeEqualToString("event.error", "not-allowed");
17  shouldBeEqualToString("event.message", "Permission check failed");
 17 shouldBeEqualToString("event.message", "User permission check has failed");
1818
1919 finishJSTest();
2020}