Commit 29af52f5 authored by Rayan Kanso's avatar Rayan Kanso Committed by Commit Bot

[Contacts] Handle addresses in blink <-> mojo and back.

Pipes whether addresses are required to mojo, and handles the expected
address return values.

TBR=mkwst@chromium.org

Bug: 1016870
Change-Id: I4d77de31736195c01754369e71f78d6825a1f811
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1874206
Commit-Queue: Rayan Kanso <rayankans@chromium.org>
Reviewed-by: default avatarFinnur Thorarinsson <finnur@chromium.org>
Reviewed-by: default avatarKen Buchanan <kenrb@chromium.org>
Reviewed-by: default avatarRouslan Solomakhin <rouslan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#708710}
parent 8b8a35cd
...@@ -79,10 +79,11 @@ void ContactsManagerImpl::Select(bool multiple, ...@@ -79,10 +79,11 @@ void ContactsManagerImpl::Select(bool multiple,
bool include_names, bool include_names,
bool include_emails, bool include_emails,
bool include_tel, bool include_tel,
bool include_addresses,
SelectCallback mojom_callback) { SelectCallback mojom_callback) {
if (contacts_provider_) { if (contacts_provider_) {
contacts_provider_->Select( contacts_provider_->Select(
multiple, include_names, include_emails, include_tel, multiple, include_names, include_emails, include_tel, include_addresses,
base::BindOnce(&OnContactsSelected, std::move(mojom_callback), base::BindOnce(&OnContactsSelected, std::move(mojom_callback),
source_id_)); source_id_));
} else { } else {
......
...@@ -28,6 +28,7 @@ class CONTENT_EXPORT ContactsManagerImpl ...@@ -28,6 +28,7 @@ class CONTENT_EXPORT ContactsManagerImpl
bool include_names, bool include_names,
bool include_emails, bool include_emails,
bool include_tel, bool include_tel,
bool include_addresses,
SelectCallback mojom_callback) override; SelectCallback mojom_callback) override;
private: private:
......
...@@ -27,6 +27,7 @@ class ContactsProvider { ...@@ -27,6 +27,7 @@ class ContactsProvider {
bool include_names, bool include_names,
bool include_emails, bool include_emails,
bool include_tel, bool include_tel,
bool include_addresses,
ContactsSelectedCallback callback) = 0; ContactsSelectedCallback callback) = 0;
}; };
......
...@@ -50,6 +50,7 @@ void ContactsProviderAndroid::Select(bool multiple, ...@@ -50,6 +50,7 @@ void ContactsProviderAndroid::Select(bool multiple,
bool include_names, bool include_names,
bool include_emails, bool include_emails,
bool include_tel, bool include_tel,
bool include_addresses,
ContactsSelectedCallback callback) { ContactsSelectedCallback callback) {
if (!dialog_) { if (!dialog_) {
std::move(callback).Run(base::nullopt, /*percentage_shared=*/-1, std::move(callback).Run(base::nullopt, /*percentage_shared=*/-1,
...@@ -96,8 +97,11 @@ void ContactsProviderAndroid::AddContact( ...@@ -96,8 +97,11 @@ void ContactsProviderAndroid::AddContact(
tel = tel_vector; tel = tel_vector;
} }
base::Optional<std::vector<payments::mojom::PaymentAddressPtr>> addresses;
blink::mojom::ContactInfoPtr contact = blink::mojom::ContactInfoPtr contact =
blink::mojom::ContactInfo::New(names, emails, tel); blink::mojom::ContactInfo::New(std::move(names), std::move(emails),
std::move(tel), std::move(addresses));
contacts_.push_back(std::move(contact)); contacts_.push_back(std::move(contact));
} }
......
...@@ -26,6 +26,7 @@ class ContactsProviderAndroid : public ContactsProvider { ...@@ -26,6 +26,7 @@ class ContactsProviderAndroid : public ContactsProvider {
bool include_names, bool include_names,
bool include_emails, bool include_emails,
bool include_tel, bool include_tel,
bool include_addresses,
ContactsSelectedCallback callback) override; ContactsSelectedCallback callback) override;
// Adds one contact to the list of contacts selected. Note, EndContactsList // Adds one contact to the list of contacts selected. Note, EndContactsList
......
...@@ -4,11 +4,14 @@ ...@@ -4,11 +4,14 @@
module blink.mojom; module blink.mojom;
import "components/payments/mojom/payment_request_data.mojom";
// As per https://wicg.github.io/contact-api/spec/. // As per https://wicg.github.io/contact-api/spec/.
struct ContactInfo { struct ContactInfo {
array<string>? name; array<string>? name;
array<string>? email; array<string>? email;
array<string>? tel; array<string>? tel;
array<payments.mojom.PaymentAddress>? address;
}; };
// The Contacts Manager lives in the browser process and can be initiated by the // The Contacts Manager lives in the browser process and can be initiated by the
...@@ -18,6 +21,6 @@ interface ContactsManager { ...@@ -18,6 +21,6 @@ interface ContactsManager {
// website. The array of contacts returned can be null, in case of an error, // website. The array of contacts returned can be null, in case of an error,
// for example if the dialog cannot be shown. The array is empty if the user // for example if the dialog cannot be shown. The array is empty if the user
// does not select any contacts (e.g. cancels selection). // does not select any contacts (e.g. cancels selection).
Select(bool multiple, bool include_names, bool include_emails, bool include_tel) Select(bool multiple, bool include_names, bool include_emails, bool include_tel, bool include_addresses)
=> (array<ContactInfo>? contacts); => (array<ContactInfo>? contacts);
}; };
...@@ -6,6 +6,8 @@ import("//third_party/blink/renderer/modules/modules.gni") ...@@ -6,6 +6,8 @@ import("//third_party/blink/renderer/modules/modules.gni")
blink_modules_sources("contacts_picker") { blink_modules_sources("contacts_picker") {
sources = [ sources = [
"contact_address.cc",
"contact_address.h",
"contacts_manager.cc", "contacts_manager.cc",
"contacts_manager.h", "contacts_manager.h",
"navigator_contacts.cc", "navigator_contacts.cc",
......
include_rules = [ include_rules = [
"-third_party/blink/renderer/modules", "-third_party/blink/renderer/modules",
"+third_party/blink/renderer/modules/contacts_picker", "+third_party/blink/renderer/modules/contacts_picker",
"+third_party/blink/renderer/modules/payments",
] ]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/contacts_picker/contact_address.h"
namespace blink {
ContactAddress::ContactAddress(
payments::mojom::blink::PaymentAddressPtr payment_address)
: PaymentAddress(std::move(payment_address)) {}
ContactAddress::~ContactAddress() = default;
} // namespace blink
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_CONTACTS_PICKER_CONTACT_ADDRESS_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_CONTACTS_PICKER_CONTACT_ADDRESS_H_
#include "third_party/blink/public/mojom/payments/payment_request.mojom-blink.h"
#include "third_party/blink/renderer/modules/payments/payment_address.h"
namespace blink {
class ContactAddress : public PaymentAddress {
DEFINE_WRAPPERTYPEINFO();
public:
explicit ContactAddress(
payments::mojom::blink::PaymentAddressPtr payment_address);
~ContactAddress() override;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_CONTACTS_PICKER_CONTACT_ADDRESS_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// https://wicg.github.io/contact-api/spec/#contactaddress
[
SecureContext,
Exposed=Window,
RuntimeEnabled=ContactsManagerAddresses
] interface ContactAddress : PaymentAddress {};
...@@ -8,4 +8,5 @@ dictionary ContactInfo { ...@@ -8,4 +8,5 @@ dictionary ContactInfo {
sequence<USVString> name; sequence<USVString> name;
sequence<USVString> email; sequence<USVString> email;
sequence<USVString> tel; sequence<USVString> tel;
sequence<ContactAddress> address;
}; };
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#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/core/frame/local_frame.h"
#include "third_party/blink/renderer/modules/contacts_picker/contact_address.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"
...@@ -29,7 +30,7 @@ TypeConverter<blink::ContactInfo*, blink::mojom::blink::ContactInfoPtr>:: ...@@ -29,7 +30,7 @@ TypeConverter<blink::ContactInfo*, blink::mojom::blink::ContactInfoPtr>::
Convert(const blink::mojom::blink::ContactInfoPtr& contact) { Convert(const blink::mojom::blink::ContactInfoPtr& contact) {
blink::ContactInfo* contact_info = blink::ContactInfo::Create(); blink::ContactInfo* contact_info = blink::ContactInfo::Create();
if (contact->name.has_value()) { if (contact->name) {
Vector<String> names; Vector<String> names;
names.ReserveInitialCapacity(contact->name->size()); names.ReserveInitialCapacity(contact->name->size());
...@@ -39,7 +40,7 @@ TypeConverter<blink::ContactInfo*, blink::mojom::blink::ContactInfoPtr>:: ...@@ -39,7 +40,7 @@ TypeConverter<blink::ContactInfo*, blink::mojom::blink::ContactInfoPtr>::
contact_info->setName(names); contact_info->setName(names);
} }
if (contact->email.has_value()) { if (contact->email) {
Vector<String> emails; Vector<String> emails;
emails.ReserveInitialCapacity(contact->email->size()); emails.ReserveInitialCapacity(contact->email->size());
...@@ -49,7 +50,7 @@ TypeConverter<blink::ContactInfo*, blink::mojom::blink::ContactInfoPtr>:: ...@@ -49,7 +50,7 @@ TypeConverter<blink::ContactInfo*, blink::mojom::blink::ContactInfoPtr>::
contact_info->setEmail(emails); contact_info->setEmail(emails);
} }
if (contact->tel.has_value()) { if (contact->tel) {
Vector<String> numbers; Vector<String> numbers;
numbers.ReserveInitialCapacity(contact->tel->size()); numbers.ReserveInitialCapacity(contact->tel->size());
...@@ -59,6 +60,17 @@ TypeConverter<blink::ContactInfo*, blink::mojom::blink::ContactInfoPtr>:: ...@@ -59,6 +60,17 @@ TypeConverter<blink::ContactInfo*, blink::mojom::blink::ContactInfoPtr>::
contact_info->setTel(numbers); contact_info->setTel(numbers);
} }
if (contact->address) {
blink::HeapVector<blink::Member<blink::ContactAddress>> addresses;
for (auto& address : *contact->address) {
auto* blink_address = blink::MakeGarbageCollected<blink::ContactAddress>(
std::move(address));
addresses.push_back(blink_address);
}
contact_info->setAddress(addresses);
}
return contact_info; return contact_info;
} }
...@@ -132,6 +144,7 @@ ScriptPromise ContactsManager::select(ScriptState* script_state, ...@@ -132,6 +144,7 @@ ScriptPromise ContactsManager::select(ScriptState* script_state,
bool include_names = false; bool include_names = false;
bool include_emails = false; bool include_emails = false;
bool include_tel = false; bool include_tel = false;
bool include_addresses = false;
for (const String& property : properties) { for (const String& property : properties) {
if (!base::Contains(properties_, property)) { if (!base::Contains(properties_, property)) {
...@@ -149,6 +162,8 @@ ScriptPromise ContactsManager::select(ScriptState* script_state, ...@@ -149,6 +162,8 @@ ScriptPromise ContactsManager::select(ScriptState* script_state,
include_emails = true; include_emails = true;
else if (property == kTel) else if (property == kTel)
include_tel = true; include_tel = true;
else if (property == kAddress)
include_addresses = true;
} }
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state); auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
...@@ -157,6 +172,7 @@ ScriptPromise ContactsManager::select(ScriptState* script_state, ...@@ -157,6 +172,7 @@ ScriptPromise ContactsManager::select(ScriptState* script_state,
contact_picker_in_use_ = 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,
include_addresses,
WTF::Bind(&ContactsManager::OnContactsSelected, WTF::Bind(&ContactsManager::OnContactsSelected,
WrapPersistent(this), WrapPersistent(resolver))); WrapPersistent(this), WrapPersistent(resolver)));
......
...@@ -100,6 +100,7 @@ modules_idl_files = ...@@ -100,6 +100,7 @@ modules_idl_files =
"clipboard/clipboard_item.idl", "clipboard/clipboard_item.idl",
"compression/compression_stream.idl", "compression/compression_stream.idl",
"compression/decompression_stream.idl", "compression/decompression_stream.idl",
"contacts_picker/contact_address.idl",
"contacts_picker/contacts_manager.idl", "contacts_picker/contacts_manager.idl",
"content_index/content_index.idl", "content_index/content_index.idl",
"content_index/content_index_event.idl", "content_index/content_index_event.idl",
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
namespace blink { namespace blink {
class MODULES_EXPORT PaymentAddress final : public ScriptWrappable { class MODULES_EXPORT PaymentAddress : public ScriptWrappable {
DEFINE_WRAPPERTYPEINFO(); DEFINE_WRAPPERTYPEINFO();
public: public:
......
...@@ -59,38 +59,53 @@ contactsTestWithUserActivation(async (test, setSelectedContacts) => { ...@@ -59,38 +59,53 @@ contactsTestWithUserActivation(async (test, setSelectedContacts) => {
}, 'Supported contact properties are exposed.'); }, 'Supported contact properties are exposed.');
contactsTestWithUserActivation(async (test, setSelectedContacts) => { contactsTestWithUserActivation(async (test, setSelectedContacts) => {
const dwightAddress = {
country: 'US',
city: 'Scranton',
addressLine: ['Schrute Farms'],
};
// Returns two contacts with all information available. // Returns two contacts with all information available.
setSelectedContacts([ setSelectedContacts([
{ name: ['Dwight Schrute'], email: ['dwight@schrutefarmsbnb.com'], tel: ['000-0000'] }, { name: ['Dwight Schrute'], email: ['dwight@schrutefarmsbnb.com'], tel: ['000-0000'], address: [dwightAddress] },
{ name: ['Michael Scott', 'Prison Mike'], email: ['michael@dundermifflin.com'], tel: [] }, { name: ['Michael Scott', 'Prison Mike'], email: ['michael@dundermifflin.com'] },
]); ]);
let results = await navigator.contacts.select(['name', 'email', 'tel'], { multiple: true }); let results = await navigator.contacts.select(['name', 'email', 'tel', 'address'], { multiple: true });
assert_equals(results.length, 2); assert_equals(results.length, 2);
results = results.sort((c1, c2) => JSON.stringify(c1) < JSON.stringify(c2) ? -1 : 1); results = results.sort((c1, c2) => JSON.stringify(c1) < JSON.stringify(c2) ? -1 : 1);
{ {
const dwight = results[0]; const michael = results[0];
assert_own_property(michael, 'name');
assert_own_property(michael, 'email');
assert_own_property(michael, 'tel');
assert_own_property(michael, 'address');
assert_array_equals(michael.name, ['Michael Scott', 'Prison Mike']);
assert_array_equals(michael.email, ['michael@dundermifflin.com']);
assert_array_equals(michael.tel, []);
assert_array_equals(michael.address, []);
}
{
const dwight = results[1];
assert_own_property(dwight, 'name'); assert_own_property(dwight, 'name');
assert_own_property(dwight, 'email'); assert_own_property(dwight, 'email');
assert_own_property(dwight, 'tel'); assert_own_property(dwight, 'tel');
assert_own_property(dwight, 'address');
assert_array_equals(dwight.name, ['Dwight Schrute']); assert_array_equals(dwight.name, ['Dwight Schrute']);
assert_array_equals(dwight.email, ['dwight@schrutefarmsbnb.com']); assert_array_equals(dwight.email, ['dwight@schrutefarmsbnb.com']);
assert_array_equals(dwight.tel, ['000-0000']); assert_array_equals(dwight.tel, ['000-0000']);
}
{
const michael = results[1];
assert_own_property(michael, 'name');
assert_own_property(michael, 'email');
assert_own_property(michael, 'tel');
assert_array_equals(michael.name, ['Michael Scott', 'Prison Mike']); assert_equals(dwight.address.length, 1);
assert_array_equals(michael.email, ['michael@dundermifflin.com']); const selectedAddress = dwight.address[0];
assert_array_equals(michael.tel, []); assert_object_equals({
country: selectedAddress.country,
city: selectedAddress.city,
addressLine: selectedAddress.addressLine,
}, dwightAddress);
} }
}, 'The Contact API correctly returns ContactInfo entries'); }, 'The Contact API correctly returns ContactInfo entries');
...@@ -98,7 +113,7 @@ contactsTestWithUserActivation(async (test, setSelectedContacts) => { ...@@ -98,7 +113,7 @@ contactsTestWithUserActivation(async (test, setSelectedContacts) => {
// Returns two contacts with all information available. // Returns two contacts with all information available.
setSelectedContacts([ setSelectedContacts([
{ name: ['Dwight Schrute'], email: ['dwight@schrutefarmsbnb.com'], tel: ['000-0000'] }, { name: ['Dwight Schrute'], email: ['dwight@schrutefarmsbnb.com'], tel: ['000-0000'] },
{ name: ['Michael Scott', 'Prison Mike'], email: ['michael@dundermifflin.com'], tel: [] }, { name: ['Michael Scott', 'Prison Mike'], email: ['michael@dundermifflin.com'] },
]); ]);
const results = await navigator.contacts.select(['name', 'email', 'tel']); const results = await navigator.contacts.select(['name', 'email', 'tel']);
...@@ -108,7 +123,7 @@ contactsTestWithUserActivation(async (test, setSelectedContacts) => { ...@@ -108,7 +123,7 @@ contactsTestWithUserActivation(async (test, setSelectedContacts) => {
contactsTestWithUserActivation(async (test, setSelectedContacts) => { contactsTestWithUserActivation(async (test, setSelectedContacts) => {
// Returns partial information since no e-mail addresses are requested. // Returns partial information since no e-mail addresses are requested.
setSelectedContacts([{ name: ['Creed'], email: ['creedthoughts@www.creedthoughts.gov.www'], tel: [] }]); setSelectedContacts([{ name: ['Creed'], email: ['creedthoughts@www.creedthoughts.gov.www'] }]);
const results = await navigator.contacts.select(['name']); const results = await navigator.contacts.select(['name']);
...@@ -125,7 +140,7 @@ contactsTestWithUserActivation(async (test, setSelectedContacts) => { ...@@ -125,7 +140,7 @@ contactsTestWithUserActivation(async (test, setSelectedContacts) => {
contactsTestWithUserActivation(async (test, setSelectedContacts) => { contactsTestWithUserActivation(async (test, setSelectedContacts) => {
// Returns partial information since no e-mail addresses are requested. // Returns partial information since no e-mail addresses are requested.
setSelectedContacts([{ name: ['Kelly'], email: [], tel: [] }]); setSelectedContacts([{ name: ['Kelly'] }]);
// First request should work. // First request should work.
const promise1 = new Promise((resolve, reject) => { const promise1 = new Promise((resolve, reject) => {
......
...@@ -18,18 +18,38 @@ const WebContactsTest = (() => { ...@@ -18,18 +18,38 @@ const WebContactsTest = (() => {
this.selectedContacts_ = []; this.selectedContacts_ = [];
} }
async select(multiple, includeNames, includeEmails, includeTel) { formatAddress_(address) {
// These are all required fields in the mojo definition.
return {
country: address.country || '',
addressLine: address.addressLine || [],
region: address.region || '',
city: address.city || '',
dependentLocality: address.dependentLocality || '',
postalCode: address.postCode || '',
sortingCode: address.sortingCode || '',
organization: address.organization || '',
recipient: address.recipient || '',
phone: address.phone || '',
};
}
async select(multiple, includeNames, includeEmails, includeTel, includeAddresses) {
if (this.selectedContacts_ === null) if (this.selectedContacts_ === null)
return {contacts: null}; return {contacts: null};
const contactInfos = this.selectedContacts_.map(contact => { const contactInfos = this.selectedContacts_.map(contact => {
const contactInfo = new blink.mojom.ContactInfo(); const contactInfo = new blink.mojom.ContactInfo();
if (includeNames) if (includeNames)
contactInfo.name = contact.name; contactInfo.name = contact.name || [];
if (includeEmails) if (includeEmails)
contactInfo.email = contact.email; contactInfo.email = contact.email || [];
if (includeTel) if (includeTel)
contactInfo.tel = contact.tel; contactInfo.tel = contact.tel || [];
if (includeAddresses) {
contactInfo.address = contact.address || [];
contactInfo.address = contactInfo.address.map(address => this.formatAddress_(address));
}
return contactInfo; return contactInfo;
}); });
......
...@@ -1157,6 +1157,9 @@ interface ConstantSourceNode : AudioScheduledSourceNode ...@@ -1157,6 +1157,9 @@ interface ConstantSourceNode : AudioScheduledSourceNode
attribute @@toStringTag attribute @@toStringTag
getter offset getter offset
method constructor method constructor
interface ContactAddress : PaymentAddress
attribute @@toStringTag
method constructor
interface ContactsManager interface ContactsManager
attribute @@toStringTag attribute @@toStringTag
method constructor method constructor
......
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