この仕様は、ウェブサイトが自身をウェブ共有ターゲットとして宣言できるようにする API を定義する。ウェブ共有ターゲットは、[[[Web-Share]]]、またはシステムイベント (例えば、ネイティブアプリからの共有)から共有コンテンツを受け取ることができる。

これは {{NavigatorContentUtils/registerProtocolHandler()}} と似た仕組みであり、 ユーザーエージェントにウェブサイトを登録しておき、後で別のサイトまたは ネイティブアプリケーションから、ユーザーエージェントを介して(場合によっては ユーザーの裁量で)呼び出されるようにする点で同様である。違いは、 {{NavigatorContentUtils/registerProtocolHandler()}} がプログラム的な API によって ハンドラーを登録するのに対し、Web Share Target は [[[appmanifest]]] 内で宣言され、 ユーザーエージェントまたはユーザーが選択する時点で登録される点である。

これは Web Share Target 仕様の初期ドラフトである。

前提条件

この API を実装するためには、ユーザーエージェントは [[[appmanifest]]] を サポートしなければならない(MUST)。この仕様は [[[Web-Share]]] 仕様の一部の 定義も再利用する。ただし、[[[Web-Share]]] のサポートは任意である(OPTIONAL)。

使用例

サイトを共有ターゲットとして登録するには、次のように、[=manifest/share_target=] エントリーを [[[appmanifest]]] に追加する。

      {
        "name": "Includinator",
        "share_target": {
          "action": "share.html",
          "params": {
            "title": "name",
            "text": "description",
            "url": "link"
          }
        }
      }
      

[=ShareTarget/params=] のキーは [[[Web-Share]]] の {{ShareData}} における キー名に対応し、その値は、ターゲットが起動されたときにクエリパラメーターとして 使用される任意の名前である。

共有が行われ、ユーザーがこの共有ターゲットを選択した場合、ユーザーエージェントは HTML フォーム送信と同様に、共有データを含むクエリパラメーター値を付けて、 `action` URL に新しい閲覧コンテキストを開く。

この例では、マニフェストが `https://example.org/includinator/manifest.webmanifest` にあるものとする。

      <html>
      <link rel="manifest" href="manifest.webmanifest">
      <script>
        window.addEventListener('load', () => {
          const parsedUrl = new URL(window.location);
          const { searchParams } = parsedUrl;
          console.log("Title shared:", searchParams.get('name'));
          console.log("Text shared:", searchParams.get('description'));
          console.log("URL shared:", searchParams.get('link'));
        });
      </script>
      

入ってくる共有にタイトル "My News" と URL `http://example.com/news` が含まれる場合、 ユーザーエージェントは新しいウィンドウまたはタブを開き、次へ移動する。

https://example.org/includinator/share.html?name=My+News&link=http%3A%2F%2Fexample.com%2Fnews

[=`application\/x-www-form-urlencoded`=] エンコーディングを使用するため、 U+0020(SPACE)文字は、予想されるかもしれない "`%20`" ではなく "`+`" として エンコードされる。処理系は、U+002B(+)文字を U+0020(SPACE)としてデコードするよう 注意しなければならない。ECMAScript の `decodeURIComponent` 関数を含む一部の URL デコードライブラリは、これを自動的には行わない場合がある。

クエリパラメーターには、共有される {{ShareData}} からの情報が設定される。 {{ShareData}} に特定のメンバーの情報が含まれない場合、そのクエリパラメーターは 省略される。

共有ターゲットは、{{ShareData}} メンバーの一部だけに関心を持つ場合がある。 この例では、`POST` リクエストとしてデータを受け取る共有ターゲットも示している。 リクエストが即時の副作用を引き起こす場合は、この方式にすべきである。

      {
        "name": "Bookmark",
        "share_target": {
          "action": "/bookmark",
          "method": "POST",
          "enctype": "multipart/form-data",
          "params": {
            "url": "link"
          }
        }
      }
      

共有された情報は、サーバーへネットワーク越しに送信されるのではなく、 [=service worker=] によって読み取られる場合がある。

        self.addEventListener("fetch", (event) => {
          if (event.request.method !== "POST") {
            event.respondWith(fetch(event.request));
            return;
          }

          const formDataPromise = event.request.formData();
          event.respondWith(
            formDataPromise.then((formData) => {
              const link = formData.get("link") || "";
              saveBookmark(link);
              return new Response(`Bookmark saved: ${link}`);
            })
          );
        });
      

ハンドラーが共有データをどのように扱うかはハンドラーの裁量であり、 一般にはアプリの種類に依存する。いくつかの提案を以下に示す。

Web App Manifest への拡張

[=manifest=] は JSON であるため、 この仕様は [[JSON]] 仕様で定義される型、すなわち objectstring に依存する。

次の手順を [=processing extension-point of web manifest=] に追加する。

  1. |json| と |manifest| を、[=processing a manifest=] からの対応する変数とする。
  2. |json| と |manifest| を用いて [=Process the `share_target` member=] を行う。

`share_target` メンバー

マニフェストの share_target メンバーは [=object=] である。存在する場合、このアプリケーションを ウェブ共有ターゲットとして宣言し、アプリケーションが共有データを どのように受け取るかを記述する。

ウェブ共有ターゲットは、 [=manifest/share_target=] メンバーを含む有効な [=manifest=] を持つウェブサイトである。

ウェブ共有ターゲットは、共有ターゲットの一種である(他の種類、 例えば一部のシステムアプリケーションも利用可能な場合がある)。

[=object=] |json:JSON| と [=ordered map=] |manifest:ordered map| が与えられたとき、 `share_target` メンバーを処理するには、次を実行する。

  1. |json|["share_target"] が [=object=] でない場合、返る。
  2. |target:object| を |json|["share_target"] とする。
  3. |target|["action"] または |target|["params"] が欠けている場合、返る。
  4. [=ShareTarget/action=] を処理する。
    1. |share target|["action"] を |manifest URL| に相対で、 エンコーディング上書きなしで [=URL parser|parsing=] した結果を |action:URL| とする。結果が failure の場合、返る。
    2. |action| が |manifest|["scope"] の [=URL/within scope=] でない場合、 返る。
    3. |action| の [=url/origin=] が [=potentially trustworthy origin=] でない場合、返る。
  5. |method:string| を "GET" とする。
  6. |target|["method"] が存在する場合、[=ShareTarget/method=] を処理する。
    1. |target|["method"] が文字列 `"GET"` とも `"POST"` とも [=ASCII case-insensitive=] 一致しない場合、返る。
    2. |method| を [=ASCII uppercase=] |target|["method"] に設定する。
  7. |enctype:string| を "application/x-www-form-urlencoded" とする。
  8. |method| が `"POST"` である場合:
    1. |target|["enctype"] が文字列 `"application/x-www-form-urlencoded"` とも `"multipart/form-data"` とも [=ASCII case-insensitive=] 一致しない場合、返る。
    2. |enctype| を [=ASCII lowercase=] |target|["enctype"] に設定する。
  9. |params:ordered map| を新しい [=ordered map=] とする。
  10. [=ShareTarget/params=] を処理する。
    1. « "title", "text", "url" » の各 |member:string| について [=List/For each=]:
      1. |target|["param"] がプロパティ |member| を持たない場合、 continue する。
      2. |target|["param"][member] が [=string=] でない場合、返る。
      3. |params|[member] を |target|["param"][member] に設定する。
  11. |manifest|["share_target"] を [=ordered map=] «[
    "action" → [=URL serializer|serialize=] |action|,
    "enctype" → |enctype|,
    "method" → |method|,
    "params" → |params|,
    ]» に設定する。

`ShareTarget` とそのメンバー

ShareTarget [=object=] は、次のメンバーを持つことができる。

action メンバー
[=web share target=] 用の [=URL=] を指定する [=string=]。
method メンバー
[=web share target=] 用の HTTP [=request=] [=request/method=] を 指定する [=string=]。
enctype メンバー
`POST` リクエストの本文で共有データをどのようにエンコードするかを 指定する [=string=]。[=method=] が `"GET"` の場合は無視される。
params メンバー
ShareTargetParams [=object=]。

`ShareTargetParams` とそのメンバー

ShareTargetParams [=object=] は、次のメンバーを持つことができる。

title メンバー
共有される文書のタイトルに使用されるクエリパラメーター名を指定する [=string=]。
text メンバー
共有されるメッセージ本文を構成する任意のテキストに使用される クエリパラメーター名を指定する [=string=]。
url メンバー
共有されるリソースを参照する URL 文字列に使用されるクエリパラメーター名を 指定する [=string=]。

ウェブ共有ターゲットの登録

ウェブ共有ターゲットがいつどのように「登録」されるかは、ユーザーエージェント および/またはエンドユーザーの裁量に委ねられる。実際、 「登録」はユーザーエージェント固有の概念であり、ここでは正式には定義しない。 ユーザーエージェントはウェブ共有ターゲットを「登録」することを要求されない(NOT REQUIRED)。 ただし、エンドユーザーが選択したウェブ共有ターゲットへ共有データを伝達するための 何らかの仕組みを提供することだけは要求される。ユーザーエージェントは、ウェブ共有ターゲットが [=installed web application|installed=] されていない場合でも、それを「登録済み」と 見なしてもよい(MAY)。

ユーザーエージェントは、ユーザーがサイトを訪問した際にすべてのウェブ共有ターゲットを 自動的に登録してもよい(MAY)が、多数のターゲットの選択肢でユーザーを圧倒しないよう、 より慎重に判断することが推奨される(RECOMMENDED)。

ユーザーエージェントが採用できる登録戦略の例は次のとおりである。

エンドユーザーにウェブ共有ターゲットの一覧を提示する際、ユーザーエージェントは、 事前にマニフェストを索引付けしたオンラインサービスを使用してもよく、その結果、 ユーザーが一度も訪問したことがない、または明示的に登録したことがないターゲットを 表示してもよい。

入ってくる共有の処理

ウェブ共有ターゲットは、エンドユーザーが汎用アプリケーション向けの何らかの データを共有しており、そのデータの受信者として特定のウェブ共有ターゲットを 示したときに、呼び出される

データがどこから来るか、またエンドユーザーがどのようにウェブ共有ターゲットを 受信者として示すかは規定しない。ただし、考えられる一つの送信元は、 同じユーザーエージェント内の {{Navigator}} の {{Navigator/share()}} メソッドへの 呼び出しである。

ウェブ共有ターゲットの呼び出しにおける、その他の考えられる送信元の例は 次のとおりである。

`ShareData` の取得

ウェブ共有ターゲット呼び出されたとき、そのデータは未規定の形式である 場合がある。ユーザーエージェントは、データがまだ {{ShareData}} オブジェクトでない場合、 まず、ホストシステムにおける同等の概念から `ShareData` のメンバーへマッピングすることにより、 そのデータを {{ShareData}} オブジェクトへ変換しなければならない(MUST)。 送信元が {{Navigator/share()}} への呼び出しであった場合、ユーザーエージェントは {{ShareData}} 引数を変更せずに使用すべきである(SHOULD)(ただし、別の形式を 経由して損失を伴う形で往復しなければならない場合があるため、常に可能とは限らない)。 ユーザーエージェントは、データを `ShareData` フィールドへできる限り適切に マッピングするためにヒューリスティックを用いてもよい(MAY)。

例えば、ホスト共有システムには専用の URL フィールドがなく、プレーンテキストと URL の両方が "text" フィールドで送信されることがある、という慣例を持つ場合がある。 Android ではこれが該当する。ユーザーエージェントは、"text" フィールドの全部または一部が [=valid URL string=] であるかを確認し、該当する場合、その部分を "text" フィールドから {{ShareData}} の {{ShareData/url}} メンバーへ移すことができる。

ウェブ共有ターゲットの起動

[=ordered map=] |manifest| を持つウェブ共有ターゲットが {{ShareData}} |data| で 呼び出されたとき、次の手順を実行する。

  1. |manifest|["share_target"]["action"] を [=URL parser|parsing=] した結果を |url:URL| とする。
  2. |entries:list| を新しい空の [=list=] とする。
  3. « "title", "text", "url" » の各 |member:string| について [=List/For each=]:
    1. |name:string| を |manifest|["share_target"]["params"][|member|] の値とする。
    2. |name| が `undefined` または空文字列である場合、continue する。
    3. |data|[|member|] が `undefined` である場合、continue する。
    4. |value:string| を ToString(|data|[|member|]) とする。
    5. [=tuple=] (|name|, |value|) を |entry list| に [=List/Append=] する。
  4. |header list| を新しく作成された [=Headers/header list=] とする。
  5. |method:string| を |manifest|["share_target"]["method"] とする。
  6. |enctype:string| を |manifest|["share_target"]["enctype"] とする。
  7. |method| が `"GET"` の場合:
    1. |query| を、|entries| とエンコーディング上書きなしで [=urlencoded serializer=] を実行した結果とする。
    2. |url| の [=URL/query=] コンポーネントを |query| に設定する。
    3. |body| を null とする。
  8. そうではなく、|method| が `"POST"` で |enctype| が `"application/x-www-form-urlencoded"` の場合:
    1. |body:string| を、|entries| とエンコーディング上書きなしで [=urlencoded serializer=] を実行した結果とする。
    2. |body| を [=UTF-8 encode=] |body| の結果に設定する。
    3. [=header list/Append=] `"Content-Type"`/`"application/x-www-form-urlencoded"` を |header list| に追加する。
  9. そうではなく、|method| が `"POST"` で | enctype| が `"multipart/form-data"` の場合:
    1. |body| を、|entries| と [=UTF-8=] エンコーディングで multipart/form-data encoding algorithm を実行した結果とする。
    2. |MIME type:string| を、文字列 `"multipart/form-data;"`、U+0020 SPACE 文字、 文字列 `"boundary="`、および [=`multipart\/form-data` encoding algorithm=] によって生成された [=`multipart\/form-data` boundary string=] を連結したものとする。
    3. [=header list/Append=] `"Content-Type"`/|MIME type| を |header list| に追加する。
  10. new [=top-level browsing context=] を作成した結果を |browsing context| とする。
  11. |request:Request| を、method が |method|、url が |url|、header list が |header list|、body が |body| である新しい [=Request=] とする。
  12. |browsing context| を |request =| へ [=Navigate=] する。

このアルゴリズムは、|manifest| に対して [=process the `share_target` member=] アルゴリズムが実行済みであり、その後も [=manifest/share_target=] を持っていることを前提としている。

アクセシビリティ

この仕様には既知のアクセシビリティ上の考慮事項はない。

セキュリティおよびプライバシーの考慮事項

謝辞

ウェブアプリの相互運用性ユースケースの基礎を築いた [[[WEBINTENTS]]] チームに感謝する。 特に、Web Share と Web Share Target の初期の普及活動に多大な尽力をした Paul Kinlan に感謝する。

この仕様の初期ドラフトを執筆し、API の設計とプロトタイプ作成に協力した Connie Pyromallis に感謝する。

この仕様の初期ドラフトにフィードバックを提供した Alex Russell と David Baron に 感謝する。