Commit 5d7baf4f authored by Peter Beverloo's avatar Peter Beverloo Committed by Commit Bot

Require a user gesture for the Contact API

Also provide a series of new tests to cover the implementation,
particularly this requirement.

Bug: 860467
Change-Id: I274e433fadba5228ac5dc0853b5a30354904d43e
Reviewed-on: https://chromium-review.googlesource.com/c/1430014
Commit-Queue: Peter Beverloo <peter@chromium.org>
Reviewed-by: default avatarFinnur Thorarinsson <finnur@chromium.org>
Cr-Commit-Position: refs/heads/master@{#625333}
parent 6957c9a7
...@@ -8,8 +8,10 @@ ...@@ -8,8 +8,10 @@
#include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/public/cpp/bindings/interface_request.h"
#include "services/service_manager/public/cpp/interface_provider.h" #include "services/service_manager/public/cpp/interface_provider.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h" #include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/modules/contacts_picker/contact_info.h" #include "third_party/blink/renderer/modules/contacts_picker/contact_info.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/heap/visitor.h" #include "third_party/blink/renderer/platform/heap/visitor.h"
...@@ -80,6 +82,15 @@ mojom::blink::ContactsManagerPtr& ContactsManager::GetContactsManager( ...@@ -80,6 +82,15 @@ mojom::blink::ContactsManagerPtr& ContactsManager::GetContactsManager(
ScriptPromise ContactsManager::select(ScriptState* script_state, ScriptPromise ContactsManager::select(ScriptState* script_state,
ContactsSelectOptions* options) { ContactsSelectOptions* options) {
Document* document = To<Document>(ExecutionContext::From(script_state));
if (!LocalFrame::HasTransientUserActivation(document ? document->GetFrame()
: nullptr)) {
return ScriptPromise::Reject(
script_state, V8ThrowException::CreateTypeError(
script_state->GetIsolate(),
"A user gesture is required to call this method"));
}
if (!options->hasProperties() || !options->properties().size()) { if (!options->hasProperties() || !options->properties().size()) {
return ScriptPromise::Reject(script_state, return ScriptPromise::Reject(script_state,
V8ThrowException::CreateTypeError( V8ThrowException::CreateTypeError(
...@@ -114,14 +125,15 @@ ScriptPromise ContactsManager::select(ScriptState* script_state, ...@@ -114,14 +125,15 @@ ScriptPromise ContactsManager::select(ScriptState* script_state,
void ContactsManager::OnContactsSelected( void ContactsManager::OnContactsSelected(
ScriptPromiseResolver* resolver, ScriptPromiseResolver* resolver,
base::Optional<Vector<mojom::blink::ContactInfoPtr>> contacts) { base::Optional<Vector<mojom::blink::ContactInfoPtr>> contacts) {
ScriptState* script_state = resolver->GetScriptState();
ScriptState::Scope scope(script_state);
if (!contacts.has_value()) { if (!contacts.has_value()) {
resolver->Reject(DOMException::Create(DOMExceptionCode::kAbortError, resolver->Reject(V8ThrowException::CreateTypeError(
"Unable to open a contact selector")); script_state->GetIsolate(), "Unable to open a contact selector"));
return; return;
} }
ScriptState::Scope scope(resolver->GetScriptState());
HeapVector<Member<ContactInfo>> contacts_list; HeapVector<Member<ContactInfo>> contacts_list;
for (const auto& contact : *contacts) for (const auto& contact : *contacts)
contacts_list.push_back(contact.To<blink::ContactInfo*>()); contacts_list.push_back(contact.To<blink::ContactInfo*>());
......
<!DOCTYPE html>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<script>
test(function() {
assert_true('contacts' in navigator,
'navigator.contacts exists in navigator.');
}, 'navigator.contacts IDL test');
</script>
<!DOCTYPE html> <!doctype html>
<script src="../resources/testharness.js"></script> <meta charset="utf-8">
<script src="../resources/testharnessreport.js"></script> <title>Contact API: Behaviour of the select() function</title>
<script src="resources.js"></script> <script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script> <script>
promise_test(function() { 'use strict';
return assert_promise_rejects_with_message(
navigator.contacts.select({multiple: true, properties: ['name', 'email', 'tel']}), // Creates a "user gesture" using Blink's test-only eventSender.
new DOMException('Unable to open a contact selector', 'AbortError'), function triggerUserGesture() {
'navigator.contacts.select'); if (!window.eventSender)
}, 'Select function throws exception.'); throw new Error('The `eventSender` must be available for this test');
eventSender.mouseDown();
eventSender.mouseUp();
}
// Verifies that |func|, when invoked, throws a TypeError exception.
async function expectTypeError(func) {
try {
await func();
} catch (e) {
assert_equals(e.name, 'TypeError');
return;
}
assert_unreached('expected a TypeError, but none was thrown');
}
test(() => {
// Exposure of the interface and method.
assert_own_property(window, 'ContactsManager');
assert_own_property(ContactsManager.prototype, 'select');
// Exposure of the instance.
assert_idl_attribute(navigator, 'contacts');
assert_idl_attribute(navigator.contacts, 'select');
}, 'The Contact API is exposed on the Window context');
promise_test(async () => {
await expectTypeError(() =>
navigator.contacts.select({ properties: ['name'] }));
}, 'The Contact API requires a user gesture')
promise_test(async () => {
triggerUserGesture();
// At least one property must be provided.
await expectTypeError(() => navigator.contacts.select());
await expectTypeError(() => navigator.contacts.select({ properties: [] }));
// Per WebIDL parsing, no invalid values may be provided.
await expectTypeError(() =>
navigator.contacts.select({ properties: [''] }));
await expectTypeError(() =>
navigator.contacts.select({ properties: ['foo'] }));
await expectTypeError(() =>
navigator.contacts.select({ properties: ['name', 'photo'] }));
}, 'The Contact API requires at least one valid property to be provided');
promise_test(async () => {
triggerUserGesture();
// TODO(peter): Fake the Mojo interface so that we can extend this test with
// valid behaviour, and actually verify the API's functionality.
await expectTypeError(() =>
navigator.contacts.select({ properties: ['name'] }));
}, 'The Contact API can fail when the selector cannot be opened');
</script> </script>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment