Commit 51955146 authored by Rayan Kanso's avatar Rayan Kanso Committed by Commit Bot

[Contacts] Only allow one contact picker per browsing context.

According to the spec, the contact picker can only be brought up once
per browsing context until the operation is complete.

Subsequent navigator.contacts.select calls should throw an InvalidState
DOMException.

https://wicg.github.io/contact-api/spec/#dom-contactsmanager-select

Bug: 985702
Change-Id: I838534bd84877079eec7740c773d18567acac3a3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1724514
Commit-Queue: Rayan Kanso <rayankans@chromium.org>
Reviewed-by: default avatarPeter Beverloo <peter@chromium.org>
Cr-Commit-Position: refs/heads/master@{#681892}
parent 44ed0256
...@@ -99,6 +99,13 @@ ScriptPromise ContactsManager::select(ScriptState* script_state, ...@@ -99,6 +99,13 @@ ScriptPromise ContactsManager::select(ScriptState* script_state,
"At least one property must be provided")); "At least one property must be provided"));
} }
if (contact_picker_in_use_) {
return ScriptPromise::RejectWithDOMException(
script_state, MakeGarbageCollected<DOMException>(
DOMExceptionCode::kInvalidStateError,
"Contacts Picker is already in use."));
}
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state); auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise(); ScriptPromise promise = resolver->Promise();
...@@ -115,6 +122,7 @@ ScriptPromise ContactsManager::select(ScriptState* script_state, ...@@ -115,6 +122,7 @@ ScriptPromise ContactsManager::select(ScriptState* script_state,
include_tel = true; include_tel = true;
} }
contact_picker_in_use_ = true;
GetContactsManager(script_state) GetContactsManager(script_state)
->Select(options->multiple(), include_names, include_emails, include_tel, ->Select(options->multiple(), include_names, include_emails, include_tel,
WTF::Bind(&ContactsManager::OnContactsSelected, WTF::Bind(&ContactsManager::OnContactsSelected,
...@@ -129,6 +137,8 @@ void ContactsManager::OnContactsSelected( ...@@ -129,6 +137,8 @@ void ContactsManager::OnContactsSelected(
ScriptState* script_state = resolver->GetScriptState(); ScriptState* script_state = resolver->GetScriptState();
ScriptState::Scope scope(script_state); ScriptState::Scope scope(script_state);
contact_picker_in_use_ = false;
if (!contacts.has_value()) { if (!contacts.has_value()) {
resolver->Reject(V8ThrowException::CreateTypeError( resolver->Reject(V8ThrowException::CreateTypeError(
script_state->GetIsolate(), "Unable to open a contact selector")); script_state->GetIsolate(), "Unable to open a contact selector"));
......
...@@ -40,6 +40,7 @@ class ContactsManager final : public ScriptWrappable { ...@@ -40,6 +40,7 @@ class ContactsManager final : public ScriptWrappable {
// Created lazily. // Created lazily.
mojom::blink::ContactsManagerPtr contacts_manager_; mojom::blink::ContactsManagerPtr contacts_manager_;
bool contact_picker_in_use_ = false;
}; };
} // namespace blink } // namespace blink
......
...@@ -163,4 +163,34 @@ promise_test(async () => { ...@@ -163,4 +163,34 @@ promise_test(async () => {
} }
}, 'The Contact API does not include fields that were not requested'); }, 'The Contact API does not include fields that were not requested');
promise_test(async () => {
triggerUserGesture();
// Returns partial information since no e-mail addresses are requested.
mockContactsManager.setSelectCallback(async (options) => {
return {
contacts: [{ name: ['Tim'], email: null, tel: null }]
};
});
// First request should work.
const promise1 = new Promise((resolve, reject) => {
navigator.contacts.select(['name']).then(resolve)
.catch(e => reject(e.message));
});
// Second request should fail (since the first one didn't complete yet).
const promise2 = new Promise((resolve, reject) => {
navigator.contacts.select(['name']).then(contacts => reject('This was supposed to fail'))
.catch(e => resolve(e.message));
});
const results = await Promise.all([promise1, promise2]);
const contacts = results[0];
assert_equals(contacts.length, 1);
const contact = contacts[0];
assert_equals(contact.name[0], 'Tim');
assert_equals(results[1], 'Contacts Picker is already in use.');
}, 'The Contact API cannot be used again until the first operation is complete.');
</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