連絡先ピッカー API

W3C作業草案

この文書の詳細
このバージョン:
https://www.w3.org/TR/2024/WD-contact-picker-20240708/
最新公開バージョン:
https://www.w3.org/TR/contact-picker/
編集者ドラフト:
https://w3c.github.io/contact-picker/
過去のバージョン:
履歴:
https://www.w3.org/standards/history/contact-picker/
フィードバック:
public-device-apis@w3.org 件名「[contact-picker] … メッセージトピック …」で (アーカイブ)
GitHub
編集者:
(Google)
前編集者:
(Google)

概要

ユーザーの連絡先情報への一度限りのアクセスを提供し、共有されるデータを完全に管理できるAPIです。

この文書の位置付け

このセクションは、公開時点でのこの文書の位置付けについて説明します。現行のW3C出版物やこの技術レポートの最新版はW3C技術レポート一覧 https://www.w3.org/TR/でご確認ください。

この文書は、デバイスとセンサー作業グループおよびWebアプリケーション作業グループによって、勧告トラックを使用して作業草案として公開されました。この文書は、W3C勧告となることを意図しています。

この文書に関するご意見は、仕様リポジトリでIssueを作成してご提出ください。

作業草案として公開されたことは、W3Cおよびそのメンバーによる承認を意味するものではありません。この文書は草案であり、随時更新、差替え、廃止される可能性があります。本書を進行中の作業以外のものとして引用するのは不適切です。

この文書は、W3C特許ポリシーのもとで作成されています。 W3Cは、特許開示の公開リスト(デバイスとセンサー)および 特許開示の公開リスト(Webアプリケーション)を管理しており、各グループの成果物に関連する特許開示の方法も記載しています。 特定の特許が必須のクレームを含むと認識した場合は、 W3C特許ポリシー第6節に従って開示してください。

一般的な作業グループの告知、事務連絡、技術以外の事項については、 public-device-apis@w3.org購読アーカイブ) または public-webapps@w3.org購読アーカイブ)へメールしてください。

この文書は、2023年11月3日W3Cプロセス文書に従って管理されています。

1. はじめに

連絡先ピッカーは、さまざまなデスクトップおよびネイティブモバイルアプリケーションで多くのユースケースに利用されています。本仕様は、連絡先ピッカーをウェブにもたらすAPIを定義し、ウェブアプリの新たなユースケースを可能にします。例えば:

連絡先ピッカーモデルは、ユーザーが共有するデータを完全に管理できるようにするために選ばれました。ユーザーは、ウェブサイトに提供する連絡先を正確に選択できます。連絡先ピッカーモデルでは、ウェブサイトはユーザーの連絡先への一度限りのアクセス権しか得られず、開発者は必要な都度アクセスを要求しなければなりません。これは一部のネイティブ連絡先APIとは異なりますが、ユーザーの連絡先が本人の認知と明示的な同意なしにアクセスされないようにするために必要です。

1.1.

ユーザークリックにより連絡先をリクエストする例。
selectRecipientsButton.addEventListener('click', async () => {
  const contacts = await navigator.contacts.select(['name', 'email'], {multiple: true});

  if (!contacts.length) {
    // ピッカーで連絡先が選択されませんでした。
    return;
  }

  // |contacts| の名前とメールアドレスをウェブサイトUIの宛先フィールドに反映する。
  populateRecipients(contacts);
});

上記の例で selectRecipientsButtonHTMLButtonElementpopulateRecipients は開発者定義の関数です。

プレゼント配送先の住所をリクエストする例。
selectRecipientButton.addEventListener('click', async () => {

  // 住所がサポートされているか、ブラウザーが提供できるかは不明です。
  if ((await navigator.contacts.getProperties()).includes('address')) {
    const contacts = await navigator.contacts.select(['address']);

    if (!contacts.length) {
      // ピッカーで連絡先が選択されませんでした。
      return;
    }

    // 複数連絡先を要求していないので length は 1。
    sendGiftToAddress(contacts[0].address);
  }

 // フォームへのフォールバック。
});

上記の例で selectRecipientButtonHTMLButtonElementsendGiftToAddress は開発者定義の関数です。

名前とアイコンをリクエストする例。
selectRecipientButton.addEventListener('click', async () => {

  // アイコンがサポートされているか、ブラウザーが提供できるかは不明です。
  if ((await navigator.contacts.getProperties()).includes('icon')) {
    const contacts = await navigator.contacts.select(['name', 'icon']);

    if (!contacts.length) {
      // ピッカーで連絡先が選択されませんでした。
      return;
    }

    if (!contacts[0].name.length || !contacts[0].icon.length) {
      // 情報が見つかりません。フォールバックを使用してください。
      return;
    }

    // 必要なのは1つの名前と1つの画像だけです。
    const name = contacts[0].name[0];
    const imgBlob = contacts[0].icon[0];

    // 画像を表示する。
    const url = URL.createObjectURL(imgBlob);
    imgContainer.onload = () => URL.revokeObjectURL(url);
    imgContainer.src = url;

    // 代わりにBitmapを使用することもできます。
    const imgBitmap = await createImageBitmap(imgBlob);

    // アイコンをアップロードする。
    const response = await fetch('/contacticon', {method: 'POST', body: imgBlob});
  }
});

上記の例で selectRecipientButtonHTMLButtonElementimgContainerHTMLImageElementです。

2. プライバシーに関する考慮事項

連絡先情報を公開することは、関係のない第三者の個人情報(PII)を露出させるという明確なプライバシーへの影響があります。ピッカーモデルを採用することで、ユーザーエージェントがウェブサイトといつどの情報が共有されるかをユーザーに明確に伝えるユーザー体験を提供できるようにしています。

さらに、以下の制約も適用されます:

3. セキュリティに関する考慮事項

4. レルム

すべてのプラットフォームオブジェクトは、特に記載がない限り、this関連レルムで作成されます。

5. インフラストラクチャ

連絡先ピッカー・タスクソースは、タスクソースです。

連絡先ピッカー・タスクをキューするには、オプションのeventLoopイベントループ、 デフォルトは呼び出し元のthis関連設定オブジェクト責任イベントループ)とsteps(手順)を指定し、タスクをキューする際にeventLoop連絡先ピッカー・タスクソースを使ってstepsを実行します。

5.1. 住所

住所(physical address)は以下から構成されます:

5.2. ユーザー連絡先

ユーザー連絡先(user contact)は以下から構成されます:

ユーザー連絡先は、単一ユーザーに関するデータを含みます。

注: リストのサイズは異なる場合があり、同じインデックスの要素同士が対応する必要はありません。

5.3. 連絡先ソース

連絡先ソース(contacts source)は、ユーザーの連絡先情報をユーザーエージェントに提供するサービスです。

連絡先ソースは以下から構成されます:

注: どの連絡先ソースを選択するかはユーザーエージェントの裁量です。

6. APIの説明

6.1. Navigatorへの拡張

[Exposed=Window]
partial interface Navigator {
  [SecureContext, SameObject] readonly attribute ContactsManager contacts;
};
Navigator には contacts managerContactsManager)があり、初期値は新しい ContactsManagerです。

contacts属性のgetterは、thiscontacts managerを返します。

ナビゲート可能オブジェクトには、contact picker is showing flag(連絡先ピッカー表示フラグ)があり、初期状態は未設定です。

6.2. ContactProperty

enum ContactProperty { "address", "email", "icon", "name", "tel" };

ContactProperty は、関連するuser contactフィールドにユーザーエージェントがアクセスできる場合、利用可能とみなされます。

"address"

user contactaddressesに関連します。

"email"

user contactemailsに関連します。

"icon"

user contacticonsに関連します。

"name"

user contactnamesに関連します。

"tel"

user contactnumbersに関連します。

6.3. ContactAddress

[Exposed=Window]
interface ContactAddress {
  [Default] object toJSON();
  readonly attribute DOMString city;
  readonly attribute DOMString country;
  readonly attribute DOMString dependentLocality;
  readonly attribute DOMString organization;
  readonly attribute DOMString phone;
  readonly attribute DOMString postalCode;
  readonly attribute DOMString recipient;
  readonly attribute DOMString region;
  readonly attribute DOMString sortingCode;
  readonly attribute FrozenArray<DOMString> addressLine;
};

ContactAddress インターフェースは住所(physical address)を表します。

ContactAddress インスタンスは以下を持ちます:

city属性のgetterは、thisaddresscityを返します。

country属性のgetterは、thisaddresscountryを返します。

dependentLocality属性のgetterは、thisaddressdependent localityを返します。

organization属性のgetterは、thisaddressorganizationを返します。

phone属性のgetterは、thisaddressphone numberを返します。

postalCode属性のgetterは、thisaddresspostal codeを返します。

recipient属性のgetterは、thisaddressrecipientを返します。

region属性のgetterは、thisaddressregionを返します。

sortingCode属性のgetterは、thisaddresssorting codeを返します。

addressLine属性のgetterは、thisaddressaddress lineを返します。

6.4. ContactsManager

dictionary ContactInfo {
    sequence<ContactAddress> address;
    sequence<DOMString> email;
    sequence<Blob> icon;
    sequence<DOMString> name;
    sequence<DOMString> tel;
};

dictionary ContactsSelectOptions {
    boolean multiple = false;
};

[Exposed=Window, SecureContext]
interface ContactsManager {
    Promise<sequence<ContactProperty>> getProperties();
    Promise<sequence<ContactInfo>> select(sequence<ContactProperty> properties, optional ContactsSelectOptions options = {});
};

6.4.1. getProperties()

getProperties()メソッドが呼び出された時、以下の手順を実行します:
  1. promise新しいPromiseを設定する。

  2. 以下の手順を並列で実行:

    1. promisecontacts sourcesupported propertiesで解決する。

  3. promiseを返す。

6.4.2. select()

select(properties, options) メソッドが呼び出された際、以下の手順を実行します:
  1. globalを、this関連グローバルオブジェクトとします。

  2. navigableglobalnavigableとします。

  3. もしnavigable最上位トラバーサブルでなければ、Promiseを InvalidStateError DOMExceptionで拒否して返します。

  4. もしglobal一時的アクティベーションがなければ、Promiseを SecurityError DOMExceptionで拒否して返します。

  5. それ以外の場合は、ユーザーアクティベーションを消費します(globalの)。

  6. もしnavigablecontact picker is showing flagが設定されていたら、 Promiseを InvalidStateError DOMExceptionで拒否して返します。

  7. もしpropertiesなら、 Promiseを TypeErrorで拒否して返します。

  8. propertyについてpropertiesを:

    1. もし連絡先ソースサポートされるプロパティpropertyを含んでいなければPromiseを TypeErrorで拒否して返します。

  9. navigablecontact picker is showing flagを設定します。

  10. promise新しいPromiseとします。

  11. 以下の手順を並列で実行:

    1. selectedContactsを、contact pickerの起動の結果として、 optionsmultipleメンバーとpropertiesで取得します。失敗した場合:

      1. 連絡先ピッカー・タスクをキューして以下の手順を実行:

        1. promiseを InvalidStateError DOMExceptionで拒否します。

        2. navigablecontact picker is showing flagを解除します。

        3. これらの手順を中止します。

    2. navigablecontact picker is showing flagを解除します。

    3. 連絡先ピッカー・タスクをキューして以下の手順を実行:

      1. contactsを空のリストとします。

      2. selectedContactについてselectedContactsを:

        1. contactを新しいContactInfoとして次のように設定:

          address

          selectedContactaddressespropertiesが "address"を含む場合のみ)、それ以外はundefined。

          email

          selectedContactemailspropertiesが "email"を含む場合のみ)、それ以外はundefined。

          icon

          selectedContacticonspropertiesが "icon"を含む場合のみ)、それ以外はundefined。

          name

          selectedContactnamespropertiesが "name"を含む場合のみ)、それ以外はundefined。

          tel

          selectedContactnumberspropertiesが "tel"を含む場合のみ)、それ以外はundefined。

        2. contactを contactsに追加します。

      3. promisecontactsで解決します。

  12. promiseを返します。

7. 連絡先ピッカー

連絡先ピッカーを起動するためには、 allowMultipleboolean型)、propertiesリスト型、DOMString)を受け取り、 ユーザーエージェントは以下のルールに従ったユーザーインターフェースを表示しなければなりません:
  • ユーザーインターフェースの表示に失敗した場合、または連絡先ソース利用可能な連絡先へのアクセスが失敗した場合、失敗を返す。

  • UIは最上位トラバーサブルオリジンを目立つ形で表示しなければならない。

  • UIは連絡先のどのpropertiesが要求されているかを明確に示さなければならない。

    注: この情報はpropertiesから導出されます。

  • UIはユーザーが特定の連絡先情報の共有を拒否できる方法を提供するべきである。

    注: ユーザーが共有を拒否した場合は、 選択された連絡先を返す前に該当するユーザー連絡先フィールドを修正するべきです。 共有されなかった情報なのか、もともと存在しなかった情報なのかは、返されるユーザー連絡先から区別できないようにする必要があります。

  • UIはどの情報が共有されるかを明確に示さなければならない。

  • UIは個別の連絡先を選択できる方法を提供しなければならない。allowMultipleがfalseの場合、選択できる連絡先は1つのみとする。

  • UIは連絡先を一切共有せずにキャンセル・戻るためのオプションを提供しなければならず、その場合はUIを削除し、空のリストを返す。

  • UIはユーザーが選択を完了したことを示す方法を提供しなければならず、その場合はUIを削除し、選択された連絡先のリストユーザー連絡先として返す。

8. ユーザー入力からContactAddressを作成する

ユーザー入力から ContactAddressを作成する手順は以下のアルゴリズムによって示されます。 このアルゴリズムはオプションでリスト型のredactListを受け取ります。 redactListが渡されなかった場合、既定ではリストとなります。

注: redactListはオプションとして、 ユーザーエージェントがAPIで要求元アプリケーションに共有する受取人情報の量を制限するために利用できます。 生成されるContactAddress オブジェクトは、通信やサービス提供など必要な処理を行うのに十分な情報を提供しますが、 多くの場合、受取人を物理的に特定・一意に識別するには十分ではありません。 ただし、redactListがあっても、 郵便番号が特定の国では個人を一意に特定できるほど細分化されている場合があるため、 受取人の匿名性を保証することはできません。

  1. detailsマップ« "addressLine" → 空のリスト, "country" → "", "phone" → "", "city" → "", "dependentLocality" → "", "organization" → "", "postalCode" → "", "recipient" → "", "region" → "", "sortingCode" → "" »とする。

  2. もしredactListが"addressLine"を含まなければdetails["addressLine"]にユーザーが入力した住所行を分割してリストとしたものを設定する。

    注: 住所行の分割方法はロケール依存であり、本仕様の範囲外です。

  3. もしredactListが"country"を含まなければdetails["country"]にユーザーが入力したを大文字の[ISO3166-1] アルファ2コードとして設定する。

  4. もしredactListが"phone"を含まなければdetails["phone"]にユーザーが入力した電話番号を設定する。

    注: ユーザーのプライバシー保護のため、 連絡先住所に紐付く電話番号がエンドユーザー自身のものと同じ場合と異なる場合があるため、 実装者はエンドユーザーの同意なく電話番号を提供しないよう十分注意する必要があります。

  5. もしredactListが"city"を含まなければdetails["city"]にユーザーが入力した市区町村を設定する。

  6. もしredactListが"dependentLocality"を含まなければdetails["dependentLocality"]にユーザーが入力した従属地域を設定する。

  7. もしredactListが"organization"を含まなければdetails["organization"]にユーザーが入力した受取人の組織を設定する。

  8. もしredactListが"postalCode"を含まなければdetails["postalCode"]にユーザーが入力した郵便番号を設定する。 必要に応じてdetails["postalCode"]の一部を秘匿する。

    注: 郵便番号が一部の国では個人を一意に特定できるほど細分化されているため、 プライバシー保護の観点から、アプリケーションの用途に十分な部分のみを返す場合があります。 国・地域ごとに事情が異なるため、郵便番号の全部または一部の秘匿は、ユーザー保護の観点から実装者の判断に委ねられます。

  9. もしredactListが"recipient"を含まなければdetails["recipient"]にユーザーが入力した受取人情報を設定する。

  10. もしredactListが"region"を含まなければdetails["region"]にユーザーが入力した地域を設定する。

    注: ベルギー等一部の国では、 地域を住所に含めることが一般的でない場合があります(国の全地域が [ISO3166-2]に含まれていても)。 そのため、ユーザーエージェントが特定の国向け住所入力と認識している場合は 地域入力欄を提供しないことがあります。 こういった場合はContactAddressregion属性が空文字となりますが、 通信やサービス提供など本来の目的には問題なく利用できます。

  11. もしredactListが"sortingCode"を含まなければdetails["sortingCode"]にユーザーが入力した仕分けコードを設定する。

  12. detailsの内容に一致する属性値を持つ新しいContactAddressを返す。

9. 謝辞

Web向けContacts APIの標準化にはこれまでにも複数の試みがあり、本APIはその豊富な歴史から学ぶことを目指しています。過去の試みにはMozillaのContacts APIContacts API W3C会員提出、そしてW3C作業グループによる標準化の取組み:Contacts APIPick Contacts IntentContacts Manager APIがあります。 Contact Picker APIは、設計時にプライバシー保護を主眼としており、そのアプローチが従来と異なります。従来のAPIは一度許可すると継続的アクセスが可能だったり、プライバシーモデルが曖昧だったりしましたが、本仕様ではUI制約を強制し、ユーザーが共有データを完全に管理でき、濫用を防ぐように設計されています。例えば、ピッカーモデルを採用し、ユーザーが共有する連絡先情報の仲介者として必ず介在し、毎回完全な管理下で情報が提供されます。より詳細な歴史的背景については、従来の試みの文書ステータスのセクションもご参照ください。

適合性

文書上の慣例

適合性要件は記述的な断言とRFC 2119の用語で表現されます。 この文書の規範的箇所における「MUST」「MUST NOT」「REQUIRED」「SHALL」「SHALL NOT」「SHOULD」「SHOULD NOT」「RECOMMENDED」「MAY」「OPTIONAL」などのキーワードはRFC 2119で定義された意味に従って解釈してください。 ただし、可読性のため、本仕様ではこれらの単語はすべて大文字では記載されていません。

この仕様の文章は、明示的に非規範的とされたセクション、例、および注記を除き、すべて規範的です。[RFC2119]

本仕様の例は「for example」などの語句で導入されるか、class="example"として規範的テキストから区別されています。 例:

これは参考例です。

参考注記は「Note」で始まり、class="note"で規範的テキストと区別されています。 例:

注:これは参考注記です。

適合アルゴリズム

アルゴリズム内の命令形(例:"strip any leading space characters" や "return false and abort these steps")で表現された要件は、 アルゴリズム導入部のキーワード("must", "should", "may"など)の意味で解釈してください。

アルゴリズムや具体的な手順として記述された適合性要件は、最終的な結果が等価であれば、どのような方法でも実装可能です。 特に、この仕様のアルゴリズムは分かりやすさを重視しており、性能最適化は意図していません。 実装者は最適化を推奨します。

索引

本仕様で定義されている用語

参照で定義されている用語

参考文献

規範参考文献

[FileAPI]
Marijn Kruisselbrink. File API. 2024年5月24日. 作業草案. URL: https://www.w3.org/TR/FileAPI/
[HTML]
Anne van Kesteren 他. HTML標準. 現行標準. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 標準. 現行標準. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. RFCにおける要件レベルを示すためのキーワード. 1997年3月. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL標準. 現行標準. URL: https://webidl.spec.whatwg.org/

参考情報

[E.164]
国際公衆電気通信番号計画. 2010年11月. 勧告. URL: https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-E.164-201011-I!!PDF-E&type=items
[ISO3166-1]
国およびその細分の名称表記コード — 第1部: 国コード. 2020年8月. 公開済み. URL: https://www.iso.org/standard/72482.html
[ISO3166-2]
ISO 3166: 国およびその細分の名称表記コード – 第2部: 国細分コード. 2020年8月. 公開済み. URL: https://www.iso.org/standard/72483.html
[MIMESNIFF]
Gordon P. Hemsley. MIME Sniffing標準. 現行標準. URL: https://mimesniff.spec.whatwg.org/

IDL索引

[Exposed=Window]
partial interface Navigator {
  [SecureContext, SameObject] readonly attribute ContactsManager contacts;
};

enum ContactProperty { "address", "email", "icon", "name", "tel" };

[Exposed=Window]
interface ContactAddress {
  [Default] object toJSON();
  readonly attribute DOMString city;
  readonly attribute DOMString country;
  readonly attribute DOMString dependentLocality;
  readonly attribute DOMString organization;
  readonly attribute DOMString phone;
  readonly attribute DOMString postalCode;
  readonly attribute DOMString recipient;
  readonly attribute DOMString region;
  readonly attribute DOMString sortingCode;
  readonly attribute FrozenArray<DOMString> addressLine;
};

dictionary ContactInfo {
    sequence<ContactAddress> address;
    sequence<DOMString> email;
    sequence<Blob> icon;
    sequence<DOMString> name;
    sequence<DOMString> tel;
};

dictionary ContactsSelectOptions {
    boolean multiple = false;
};

[Exposed=Window, SecureContext]
interface ContactsManager {
    Promise<sequence<ContactProperty>> getProperties();
    Promise<sequence<ContactInfo>> select(sequence<ContactProperty> properties, optional ContactsSelectOptions options = {});
};