<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:series="http://organizeseries.com/"
	>

<channel>
	<title>保呂毅 &#8211; HTML5Experts.jp</title>
	<atom:link href="/horo/feed/" rel="self" type="application/rss+xml" />
	<link>https://html5experts.jp</link>
	<description>日本に、もっとエキスパートを。</description>
	<lastBuildDate>Sat, 07 Jul 2018 03:14:05 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.7.19</generator>
	<item>
		<title>超詳解！Service Worker Deep Dive ── HTML5 Conference 2016セッションレポート</title>
		<link>/horo/21360/</link>
		<pubDate>Mon, 24 Oct 2016 01:05:13 +0000</pubDate>
		<dc:creator><![CDATA[保呂毅]]></dc:creator>
				<category><![CDATA[最新動向]]></category>
		<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[Service Worker]]></category>
		<category><![CDATA[Web Push]]></category>

		<guid isPermaLink="false">/?p=21360</guid>
		<description><![CDATA[連載： HTML5 Conference 2016 特集 (5)はじめまして。GoogleでChromeの開発をしている保呂毅です。 Chromeの中では特にService Worker周りを担当してまして、最近はNav...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/html5-conf2016/" class="series-403" title="HTML5 Conference 2016 特集" data-wpel-link="internal">HTML5 Conference 2016 特集</a> (5)</div><p>はじめまして。GoogleでChromeの開発をしている保呂毅です。<br>
Chromeの中では特にService Worker周りを担当してまして、最近は<a href="https://crbug.com/649558" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Navigation Preload</a>という新機能をがんばって実装しています。
先日開催された<a href="http://events.html5j.org/conference/2016/9/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">HTML5 Conference 2016</a>でService Worker周辺の最近（ここ1年くらい）の動向に関する発表をさせていただいきました。<br>今回は、この発表の内容を振り返りたいと思います。</p>

<p><img src="/wp-content/uploads/2016/10/DSC06505.jpg" alt="" width="640" height="414" class="alignnone size-full wp-image-21547" srcset="/wp-content/uploads/2016/10/DSC06505.jpg 640w, /wp-content/uploads/2016/10/DSC06505-300x194.jpg 300w, /wp-content/uploads/2016/10/DSC06505-207x134.jpg 207w" sizes="(max-width: 640px) 100vw, 640px" /></p>

<h2>Service Workerとは</h2>

<p>まず本題に入る前に簡単にService Workerの説明します。
Service Workerとはどういうものかと言いますと、
下のコードのようにnavigator.serviceWorker.registerというAPIで登録する、バックグラウンドで動作するJavaScript実行環境です。</p>

<p></p><pre class="crayon-plain-tag">navigator.serviceWorker.register('./sw.js', {scope: './'});</pre><p></p>

<p>登録されたServiceWorkerでは、ページからのネットワークリクエストを横取りすることができます。
下のコードでは、ページからのネットワークリクエストを横取りしてHello Worldという文字列をページに返しています。</p>

<p></p><pre class="crayon-plain-tag">self.addEventListener('fetch', event =&gt; {
  event.respondWith(new Response('Hello World!'));
});</pre><p></p>

<p>また、ページからのネットワークリクエストの横取りをする以外にも、ページを開いていなくても Push通知を受け取って Notification （通知）を表示する、Push Notificationsという機能も備えています。</p>

<p>Service Workerは新しいAPIでして、まだ利用できるブラウザが限られています。
<a href="https://jakearchibald.github.io/isserviceworkerready/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Is ServiceWorker ready?</a>というページに各ブラウザの対応状況が書かれています。ChromeとFirefox、Opera、Samsungのブラウザは対応していて、Edgeは開発中、Safariは検討中となっています。</p>

<h2>Push Notifications</h2>

<h3>Push Notificationsの仕組み</h3>

<p><img src="/wp-content/uploads/2016/10/push_flow0-640x284.png" alt="Push Notificationsの仕組み" width="640" height="284" class="aligncenter size-large wp-image-21408" srcset="/wp-content/uploads/2016/10/push_flow0.png 640w, /wp-content/uploads/2016/10/push_flow0-300x133.png 300w, /wp-content/uploads/2016/10/push_flow0-207x92.png 207w" sizes="(max-width: 640px) 100vw, 640px" /></p>

<p>Push Notificationはどういう仕組みかといいますと、WebサービスのサーバーからFCM (Firebase Cloud Messaging)のサーバーにメッセージをポストすると、それがブラウザに届いて通知を表示できるというものです。FCMは、最近GCM (Google Cloud Messaging)から名前が変わりました。</p>

<h3>Push Event</h3>

<p></p><pre class="crayon-plain-tag">self.addEventListener('push', event =&gt; {  
  event.waitUntil(  
    self.registration.showNotification(
      'Hello',
      {
        body: 'We have received a push message.',
        icon: 'message.png',
        tag: 'tag'
      }));  
});</pre><p></p>

<p>このコードはService Worker上の<code>Push</code>イベントハンドラです。WebサービスのサーバーからFCMのサーバーにPOSTリクエストすると、このイベントハンドラが呼びだされます。このコードでは、<code>ServiceWorkerRegistration</code> の <code>showNotification</code> というAPIで下のような通知を表示しています。
タイトルが&#8217;Hello&#8217;で中身が&#8217;We have received a push message.&#8217;で、アイコンにmessage.pngを指定しています。</p>

<p><img src="/wp-content/uploads/2016/10/push00-300x71.png" alt="push00" width="300" height="71" class="aligncenter size-medium wp-image-21410" srcset="/wp-content/uploads/2016/10/push00-300x71.png 300w, /wp-content/uploads/2016/10/push00.png 640w, /wp-content/uploads/2016/10/push00-207x49.png 207w" sizes="(max-width: 300px) 100vw, 300px" /></p>

<h3>Notificationclick Event</h3>

<p></p><pre class="crayon-plain-tag">self.addEventListener('notificationclick', event =&gt; {
  event.notification.close();
  clients.openWindow('/messages');
});</pre><p> 
このコードは<code>NotificationClick</code>イベントハンドラで、先ほどの通知をクリックした際の処理を書いています。このコードでは、通知を閉じて、messagesというページをopenWindowで開いています。</p>

<h3>Payload Data</h3>

<p>ここまでの機能は2015年4月にリリースされたChrome 42から使えます。
ところが、Chrome 49までは、Pushメッセージにデータを含めることができないという制限がありました。例えばチャットアプリなどでチャットの内容を通知に表示する場合は、Fetch APIを使ってWebサーバーに問い合わせる必要がありました。
それが、今年の4月にリリースされたChrome 50からは、Pushメッセージ自体に暗号化したデータを含めることができるようになりました。</p>

<p></p><pre class="crayon-plain-tag">self.addEventListener('push', event =&gt; {
  if (event.data) {
    console.log(event.data.json());
  }
});</pre><p></p>

<p>Service Worker側ではこちらのコードのようにPushEventにdataというプロパティがついて、データを読めるようになります。
サーバーからどう送信するかの詳細はこちらの<a href="https://developers.google.com/web/updates/2016/03/web-push-encryption" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">URL</a>で説明されているので、興味のある方はぜひ読んでみてください。Node.js用のライブラリ <a href="https://github.com/web-push-libs/web-push" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Web Push library for Node.js</a> もありますので、Node.jsをサーバーで使っている方はこちらを使うといいかと思います。</p>

<h3>Notification Actions</h3>

<p><img src="/wp-content/uploads/2016/10/push_actions-300x109.png" alt="Notification actions" width="300" height="109" class="aligncenter size-medium wp-image-21412" srcset="/wp-content/uploads/2016/10/push_actions-300x109.png 300w, /wp-content/uploads/2016/10/push_actions.png 640w, /wp-content/uploads/2016/10/push_actions-207x75.png 207w" sizes="(max-width: 300px) 100vw, 300px" /></p>

<p>続きまして、Chrome 48から使える機能、Notification Actionsです。この機能を使うと上の絵のように、ボタンを追加できるようになります。
</p><pre class="crayon-plain-tag">self.registration.showNotification(
  'Hello',
  {
    body: 'We have received a push message.',
    icon: 'message.png',
    tag: 'tag',
    data: 1234,
    actions: [
      {action: 'like', title: 'Like', icon: 'like.png'},
      {action: 'reply', title: 'Reply', icon: 'reply.png'}]
  });</pre><p>
コードを見ると、Actionsという項目で指定しているのが分かるかと思います。各ボタンに対応する、actionの文字列と、titleの文字列と、iconの画像を指定しています。</p>

<h3>Notificationclick Event</h3>

<p>このボタンをクリックすると、<code>NotificationClick</code>イベントハンドラが実行され、NotificationClickEventのactionを見ることで、どのボタンがクリックされたのかがわかります。
</p><pre class="crayon-plain-tag">self.addEventListener('notificationclick', event =&gt; { 
  var messageId = event.notification.data;
  event.notification.close();
  if (event.action == 'like') {
    silentlyLikeItem();
  } else if (event.action == 'reply') {
    clients.openWindow('/messages?reply=' + messageId);
  } else {
    clients.openWindow('/messages?reply=' + messageId);
  }
});</pre><p>
このコードの場合、likeがクリックされたら<code>silentlyLikeItem()</code>というメソッドを呼び出しています。Replyがクリックされた場合は、該当するメッセージのページを開いています。ボタン以外の部分がクリックされた場合も同様です。
ちなみに、メッセージを表示するURLのメッセージIDに<code>notification.data</code>の値を使っていますが、こちらはさきほどの通知を表示する際に指定したdataが渡ってきています。
この値は、<code>Push</code>イベントのdataから取得するといいかと思います。</p>

<h3>Standard Web Push Protocol</h3>

<p>Chrome 51までは、Web Pushを実現するためには、以下のような手順が必要でした。この認証方法はFCM独自のプロトコルです。</p>

<ol>
<li>事前にFCMに登録をして<code>gcm_sender_id</code>というIDを取得</li>
<li><code>manifest.json</code>にそのIDを記述</li>
<li>FCMサーバーへのPOSTリクエストの際に<code>Authorization</code>ヘッダにAPIキーを指定する</li>
</ol>

<p>ところが、Chrome 52からVoluntary Application Server Identification (VAPID) という標準化されたプロトコルでサーバー認証することでFCMへの事前登録が必要なくなりました。
詳しくはこちらの<a href="https://developers.google.com/web/updates/2016/07/web-push-interop-wins" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">URL</a>を御覧ください。</p>

<h2>Stream</h2>

<p>サーバーにFetchリクエストをして、帰ってきたレスポンスに手を加えてページに返すService Workerを考えます。</p>


<!-- iframe plugin v.4.3 wordpress.org/plugins/iframe/ -->
<iframe src="https://docs.google.com/presentation/d/1iYCCnASg-xCpwVUB2I--gGsm-eElVMexoRXY1YZTbA4/embed?start=false&amp;loop=true&amp;delayms=3000" frameborder="0" width="640" height="389" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true" class="aligncenter" scrolling="yes"></iframe>


<p>Chrome 51までは、このような処理をした場合、上のアニメにあるようにレスポンスを最後まで読んでからでないと、ページに返すことができませんでした。
しかし、今年の７月にリリースされたChrome 52から、Streamの新しい機能を使うことで、サーバーからのレスポンスを逐次処理しながらページに返すことができるようになりました。</p>

<p>具体的な例で解説します。</p>

<p>ページのヘッダとフッタを事前にキャッシュに入れておいて、ページを読み込む際はコンテンツだけをサーバに問い合わせて、Service Worker内で文字列連結してページに返す。ということを考えます。</p>

<p></p><pre class="crayon-plain-tag">self.addEventListener('install', event =&gt; {
    event.waitUntil(
      caches.open('cache_name')
        .then(cache =&gt; cache.addAll(['./header.txt',
                                     './footer.txt'])));
});</pre><p></p>

<p>まず、ヘッダとフッタをキャッシュに入れるには、<code>Install</code>イベントハンドラで上のコードのようにCacheStorage APIを使います。この例では<code>header.txt</code>と<code>footer.txt</code>をキャッシュに保存しています。
<code>caches.open()</code>でキャッシュを開き、取得した<code>cache</code>に対して、<code>cache.addAll()</code>を呼んでヘッダとフッタをサーバーから取ってきて保存しています。</p>

<p></p><pre class="crayon-plain-tag">self.addEventListener('fetch', event =&gt; {
  var url = event.request.url;
  if (!url.endsWith('.html'))
    return;

  event.respondWith(
    Promise.all(
        [caches.match('./header.txt'),
         fetch(url +  '.txt'),
         caches.match('./footer.txt')])
      .then(responseList =&gt; Promise.all(responseList.map(res =&gt; res.text())))
      .then(textList =&gt;
            new Response(textList.join(''),
                         {headers:[['content-type', 'text/html']]})));
});</pre><p></p>

<p>次に、<code>Fetch</code>イベントハンドラで、先ほど保存したヘッダとフッタをキャッシュから取り出して、サーバーから取得したコンテンツと文字列連結します。
先ほど保存した<code>header.txt</code>と<code>footer.txt</code>をキャッシュから読み込みつつ、Fetch APIで元のURLに”.txt”をつなげたURLにネットワークリクエストを投げています。
そして、それらすべてのレスポンスから、text()でコンテンツ本文を取得し、その後、joinで連結しています。
これで目的通り、ヘッダとフッタを連結してページに返すことができるのですが、問題があります。
先ほど書いたとおり、サーバーからのコンテンツ全体を取得完了するまでページに返せないのです。
そこで、解決方法がStreamです。Streamを使えば、サーバーから取得したコンテンツを徐々にページに返していくことができます。</p>

<p></p><pre class="crayon-plain-tag">self.addEventListener('fetch', event =&gt; {
    var url = event.request.url;
    if (!url.endsWith('.html'))
      return;

    var stream = new ReadableStream({
        【次コード参照】
    });

    event.respondWith(new Response(
        stream,
        {headers:[['content-type', 'text/html']]}));
  });</pre><p></p>

<p>具体的にコードを見ていきましょう。
<code>Install</code>イベントハンドラは先ほどと同じです。
<code>Fetch</code> イベントハンドラは上のコードのようになります。
<code>ReadableStream</code>という新しいクラスが登場します。中身は下で説明しますが、ここで<code>ReadableStream</code>をつくって、それを<code>Response</code>オブジェクトを作る際に渡し、<code>respondWith()</code>でレスポンスをページに返しています。</p>

<p></p><pre class="crayon-plain-tag">var stream = new ReadableStream({
start(controller) {
  var promises = [caches.match('./header.txt'),
                  fetch(url + '.txt'),
                  caches.match('./footer.txt')];
  function pushStream(body) {
    var reader = body.getReader();
    return reader.read().then(function proc(result) {
      if (result.done)
        return;
      controller.enqueue(result.value);
      return reader.read().then(proc);
    });
  }
  promises[0]
    .then(response =&gt; pushStream(response.body))
    .then(() =&gt; promises[1]).then(response =&gt; pushStream(response.body))
    .then(() =&gt; promises[2]).then(response =&gt; pushStream(response.body))
    .then(() =&gt; controller.close());
}});</pre><p>
<code>ReadableStream</code>の中身は、このようになります。
<code>start()</code>というメソッドの中でまず、<code>header.txt</code>と<code>footer.txt</code>のキャッシュからの読み込みと、サーバへのFetchリクエストのPromiseを作っています。
下の方で、まず、ひとつ目のPromise、つまりHeader読み込みのPromiseからレスポンスを取得し、そのレスポンスボディを<code>pushStream()</code>という関数に渡しています。
<code>pushStream()</code>では<code>body</code>から<code>reader</code>を取得して、順番に読んでいって、<code>controller.enqueue()</code>で詰め込んでいきます。
それが終わったら２つ目のPromise、つまりFetchのPromiseからレスポンスを取得して、あとは同様にbodyの中身を徐々にenqueueしていきます。その後、３つ目のPromise、つまりfooter.txtのPromiseも同様です。最後に、controller.close()を呼んでStreamを閉じています。</p>

<p>このようにすると、サーバーからのレスポンスを最後まで待たずに、徐々にページにデータを返していくことができます。</p>

<h2>Unified Media Pipeline</h2>

<p>実は、Android Chromeは51まで、audioエレメントやvideoエレメントではAndroidのメディアスタックを利用していました。それが、7月にリリースされたChrome 52からデスクトップ版Chromeと共通化されました。
そのおかげで、</p>

<ul>
<li>Service Workerを使ったキャッシュ</li>
<li>Blob URLからの再生</li>
<li><code>playbackRate</code>の指定</li>
<li><code>MediaRecorder</code>からの<code>MediaStreams</code>をWeb Audioに送信する</li>
</ul>

<p>といったことができるようになり、クロスデバイスの開発が容易になりました。
詳細はこちらの<a href="https://developers.google.com/web/updates/2016/06/ump" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">URL</a>を参照してください。</p>

<h2>Background Sync</h2>

<p>Background Syncはどういうものかと言いますと、その名の通り、バックグラウンドでデータを送受信できる機能です。
例えば、「オフライン時にメッセージを書いて、オンラインになったときに自動で送信」といったことができるようになります。AndroidだとChromeアプリを閉じていても動作します。
この機能はChrome 49 から利用可能です。</p>

<p>ちょうどいい動画がYouTubeにあったので<a href="https://www.youtube.com/watch?v=2ugAVXRkr9U" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">こちら</a>をご覧ください。
この動画は、Service Workerを使ってオフラインでもWikipediaの記事を読むことができるというWebアプリのデモです。このアプリで、キャッシュに入っていない記事を読もうとしたら、エラーになるのですが、Background Syncを使ってオンラインになった時に自動で記事をダウンロードし、ダウンロードが完了したら先ほど紹介したNotificationのAPIで通知を表示しています。</p>

<h2>Foreign Fetch</h2>

<h3>普通の Fetchイベント</h3>

<p><img src="/wp-content/uploads/2016/10/normal_fetch_1-640x320.png" alt="normal_fetch_1" width="640" height="320" class="aligncenter size-large wp-image-21460" srcset="/wp-content/uploads/2016/10/normal_fetch_1.png 640w, /wp-content/uploads/2016/10/normal_fetch_1-300x150.png 300w, /wp-content/uploads/2016/10/normal_fetch_1-207x104.png 207w" sizes="(max-width: 640px) 100vw, 640px" /></p>

<p>Foreign Fetchの説明をする前に通常の<code>Fetch</code>イベントを復習します。
<code>a.com</code>のサイトにService Workerが登録されているとします。
この場合、</p>

<ul>
<li><code>a.com</code>のページ自体のHTMLファイル（メインリソースと言います）へのリクエスト</li>
<li>このページで読み込まれる、<code>a.com</code>上にある画像等のサブリソースへのリクエスト</li>
<li>このページで読み込まれる、<code>b.com</code>上にあるサブリソースへのリクエスト</li>
</ul>

<p>これらはすべて、Service Workerの<code>Fetch</code>イベントハンドラで横取りすることができます。</p>

<p><img src="/wp-content/uploads/2016/10/normal_fetch_2-640x320.png" alt="normal_fetch_2" width="640" height="320" class="aligncenter size-large wp-image-21459" srcset="/wp-content/uploads/2016/10/normal_fetch_2.png 640w, /wp-content/uploads/2016/10/normal_fetch_2-300x150.png 300w, /wp-content/uploads/2016/10/normal_fetch_2-207x104.png 207w" sizes="(max-width: 640px) 100vw, 640px" /></p>

<p>しかし、例えば、<code>b.com</code>のサイトのページから<code>a.com</code>上にある画像などのサブリソースへのリクエストは<code>Fetch</code>イベントハンドラで横取りすることができません。</p>

<h3>Foreign Fetchを使うと</h3>

<p><img src="/wp-content/uploads/2016/10/foreign_fetch-640x320.png" alt="foreign_fetch" width="640" height="320" class="aligncenter size-large wp-image-21458" srcset="/wp-content/uploads/2016/10/foreign_fetch.png 640w, /wp-content/uploads/2016/10/foreign_fetch-300x150.png 300w, /wp-content/uploads/2016/10/foreign_fetch-207x104.png 207w" sizes="(max-width: 640px) 100vw, 640px" /></p>

<p>これを可能にするのがForeign Fetchです。
Foreign Fetchを使うと、他のサイト(この図の場合は<code>b.com</code>)からの自分のサイト(この図の場合は<code>a.com</code>)へのサブリソースのリクエストを<code>a.com</code>のService Workerで横取りすることができるようになります。</p>

<h3>普通のFetchイベント vs Foreign Fetchイベント</h3>

<p>まとめますとこうなります。
「ネットワークリクエストを横取りする」という点は共通です。
相違点としては、
普通の<code>Fetch</code>イベントは自分のサイトのページからのネットワークリクエストを横取りします。自分のサイトのHTMLページと、そこからの画像などのサブリソースリクエストも横取りできます。</p>

<p>それに対して<code>ForeignFetch</code>イベントでは、他のサイトから自分のサイトへのサブリソースのリクエストを横取りすることができるようになります。Web FontサーバやCDN等で活用できるのではと考えられます。</p>

<h3>Foreign Fetch の登録方法</h3>

<p>具体的にコードを見ていきましょう
</p><pre class="crayon-plain-tag">self.addEventListener('install', event =&gt; {
    event.registerForeignFetch({
        scopes: ['/myscope/'],
        origins: ['https://b.com/']
      });
  });</pre><p>
Foreign Fetchの登録方法ですが、先程の例のように、<code>a.com</code>上のService Workerが、 <code>b.com</code> からの <code>a.com/myscope/</code> 以下へのリクエストを横取りしたい場合は、<code>Install</code>イベントハンドラで上のコードのように<code>registerForeignFetch()</code>というAPIを叩きます。
<code>scopes</code>に横取りしたいリクエストの範囲、<code>origins</code>にリクエスト元のオリジンを指定します。すべてのオリジンからのリクエストを横取りしたい場合は、<code>origins</code>にアスタリスクを指定します。</p>

<p>上のコードのようにForeign Fetchを登録すると、 <code>a.com/myscope/</code> 以下へのネットワークリクエストの度に<code>ForeignFetch</code>イベントハンドラが実行されるようになります。</p>

<h3>Foreign Fetch Event</h3>

<p><code>ForeignFetch</code>イベントハンドラでは普通の<code>Fetch</code>イベントと同じように<code>respondWith()</code>でページにレスポンスを返すことができるのですが、普通の<code>Fetch</code>イベントと違って、辞書に包む（以下のコードの<code>{response: res}</code>）必要があります。
</p><pre class="crayon-plain-tag">self.addEventListener('foreignfetch', event =&gt; {
    event.respondWith(
        fetch(event.request).then(res =&gt; ({response: res}));
  });</pre><p>
注意点として、このようにしてページに返したレスポンスはページではOpaqueになります。つまり、&lt;img&gt;タグで画面に表示はできるのですが、JavaScriptから中身を読むことができません。
これを読めるようにするにはCORSの設定が必要になります。</p>

<h3>CORS (Cross-Origin Resource Sharing)</h3>

<p></p><pre class="crayon-plain-tag">self.addEventListener('foreignfetch', event =&gt; {
  event.respondWith(
     fetch(event.request)
       .then(response =&gt;({response: response,
                          origin: event.origin,
                          headers: ['...']})));
});</pre><p>
具体的にはこのように、<code>respondWith()</code>に渡す辞書でページの<code>origin</code>を指定します。<code>headers</code>を指定すると、指定したHTTPヘッダもページ側で読めるようになります。</p>

<p>このForeign Fetchですが、まだ実験中の機能でして、試す場合は、Chrome 54以降でchrome://flagsのenable-experimental-web-platform-featuresを有効にしてください。</p>

<h2>Header-based Installation</h2>

<p>今まで、Service Workerを登録する場合、JavaScriptで<code>navigator.serviceWorker.register()</code>を呼ぶ必要がありました。
</p><pre class="crayon-plain-tag">navigator.serviceWorker.register('/sw.js', {scope: '/'});</pre><p></p>

<p>Header-based Installationを使うと，これが、下のようなHTTPのLink Headerで登録したり、
</p><pre class="crayon-plain-tag">Link: &lt;/sw.js&gt;; rel=serviceworker; scope=/</pre><p> 
下のような、HTMLのLink Elementで登録できるようになります。
</p><pre class="crayon-plain-tag">&lt;link rel="serviceworker" scope="/" href="/sw.js"&gt;</pre><p></p>

<p>ここでポイントとなるのが、サブリソースのリクエストに対するHTTPレスポンスのLink HeaderでもService Workerをインストールできるということです。この機能を使うと、Iframe等でHTMLを読み込ませる必要なく、Foreign FetchのService Workerをインストールできます。CDNなどでサブリソースをサーブしている場合でもCDNのドメインに対してForeign FetchのService Workerを登録できるようになるのです。</p>

<p>この機能もForeign Fetchと同様に実験中の機能でして、試す場合は、Chrome 54以降でchrome://flagsのenable-experimental-web-platform-featuresを有効にしてください。</p>

<h2>Origin Trials</h2>

<p>Origin TrialとはChromeの実験的な新しいAPIを、申請のあった特定のオリジン(ドメイン)で一定期間だけ使ってもらってフィードバックをもらう仕組みです。
いま(2016年10月現在)はPersistent Storage, Web Bluetooth, Web USB, Foreign Fetch (Header-based Installation含む)が対象になっています。</p>

<p>申請の方法など詳しくはこちらの<a href="https://github.com/jpchase/OriginTrials/blob/gh-pages/developer-guide.md" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">URL</a>を参照してください</p>

<h2>AppCache</h2>

<p>Application Cache (通称AppCache)という、Webサイトのオフライン対応のためにかつて提案された機能があります。</p>

<p>しかし、FirefoxはApplication Cacheの非サポート化を<a href="https://www.fxsitecompat.com/en-CA/docs/2016/application-cache-support-will-be-removed/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">明言</a>してまして、Chromeも非セキュアなコンテクストではAppCacheを<a href="https://www.chromestatus.com/feature/5714236168732672" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">サポートしなくする</a>としています。
ですので、Webサイトのオフライン対応はAppCacheではなく、Service Workerを使おうという流れにあります。</p>

<p>Service Workerに移行する際の参考として紹介したいのが、sw-appcache-behaviorというAppCacheの動作を Service Workerを使ってシミュレーションするライブラリです。
もし興味がありましたら、この<a href="https://github.com/GoogleChrome/sw-helpers/tree/master/projects/sw-appcache-behavior" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">URL</a>を参照してください。</p>

<h2>DevTools</h2>

<p>DevToolsは進化が早くてよくUIが変わるのですが、今は、”Application”というタブの中にService Workersというのがありまして、こちらでService Workerの状態を確認したり、デバッグしたりすることができます。</p>

<p><img src="/wp-content/uploads/2016/10/devtools-640x360.png" alt="devtools" width="640" height="360" class="aligncenter size-large wp-image-21465" srcset="/wp-content/uploads/2016/10/devtools.png 640w, /wp-content/uploads/2016/10/devtools-300x169.png 300w, /wp-content/uploads/2016/10/devtools-207x116.png 207w" sizes="(max-width: 640px) 100vw, 640px" /></p>

<p>この画面で注目していただきたいのが、このメッセージです。
<code>Service Worker termination by a timeout timer was canceled because DevTools is attached.</code>
このメッセージは最近表示するようにしたのですが、「DevToolsがアタッチされてるので、タイムアウトによるService Workerの停止がキャンセルされました」と書かれてます。</p>

<p>Service Workerは一定期間イベントが実行されないと停止し、次回イベントで再起動するようになっています。ただし、DevToolsを開いている間は停止しません。
つまり、通常の状態だと、しばらくほっておくと勝手に停止し、FetchEventなどのイベントハンドラを呼ぶ必要が出ると再起動します。
ですので、Globalスコープに変数を置いて保存していると、再起動時に消えるので、Service Workerのコードを書く際は気をつけてください。</p>

<h2>まとめ</h2>

<p>駆け足になりましたが、Service Worker周辺の最近（ここ１年くらい）の動向について説明いたしました。下に関連リンクを貼り付けていますので、もっと詳細を確認したい方はご参照ください。</p>

<h2>関連リンク集</h2>

<h3>Introduction to Service Worker</h3>

<ul>
<li><a href="https://developers.google.com/web/fundamentals/primers/service-worker/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://developers.google.com/web/fundamentals/primers/service-worker/</a></li>
</ul>

<h3>Push</h3>

<ul>
<li><a href="https://developers.google.com/web/updates/2015/03/push-notifications-on-the-open-web" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://developers.google.com/web/updates/2015/03/push-notifications-on-the-open-web</a></li>
<li><a href="https://developers.google.com/web/updates/2016/01/notification-actions" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://developers.google.com/web/updates/2016/01/notification-actions</a></li>
<li><a href="https://developers.google.com/web/updates/2016/03/web-push-encryption" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://developers.google.com/web/updates/2016/03/web-push-encryption</a></li>
<li><a href="https://developers.google.com/web/updates/2016/03/notifications" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://developers.google.com/web/updates/2016/03/notifications</a></li>
<li><a href="https://developers.google.com/web/updates/2016/07/web-push-interop-wins" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://developers.google.com/web/updates/2016/07/web-push-interop-wins</a></li>
<li><a href="https://github.com/web-push-libs/web-push/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://github.com/web-push-libs/web-push/</a></li>
<li><a href="https://developers.google.com/cloud-messaging/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://developers.google.com/cloud-messaging/</a></li>
</ul>

<h3>Stream</h3>

<ul>
<li><a href="https://developers.google.com/web/updates/2016/06/sw-readablestreams" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://developers.google.com/web/updates/2016/06/sw-readablestreams</a></li>
<li><a href="https://www.youtube.com/watch?v=Pii-LaWOyuo" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://www.youtube.com/watch?v=Pii-LaWOyuo</a></li>
<li><a href="https://jakearchibald.com/2016/streams-ftw/#creating-your-own-readable-stream" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://jakearchibald.com/2016/streams-ftw/#creating-your-own-readable-stream</a></li>
</ul>

<h3>Unified Media Pipeline</h3>

<ul>
<li><a href="https://developers.google.com/web/updates/2016/06/ump" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://developers.google.com/web/updates/2016/06/ump</a></li>
</ul>

<h3>Background Sync</h3>

<ul>
<li><a href="https://developers.google.com/web/updates/2015/12/background-sync" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://developers.google.com/web/updates/2015/12/background-sync</a></li>
<li><a href="https://github.com/WICG/BackgroundSync/blob/master/explainer.md" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://github.com/WICG/BackgroundSync/blob/master/explainer.md</a></li>
<li><a href="https://ponyfoo.com/articles/backgroundsync" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://ponyfoo.com/articles/backgroundsync</a></li>
</ul>

<h3>Foreign Fetch</h3>

<ul>
<li><a href="https://github.com/slightlyoff/ServiceWorker/blob/master/foreign_fetch_explainer.md" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://github.com/slightlyoff/ServiceWorker/blob/master/foreign_fetch_explainer.md</a></li>
<li><a href="https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#on-foreign-fetch-request-algorithm" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#on-foreign-fetch-request-algorithm</a></li>
<li><a href="https://crbug.com/540509" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://crbug.com/540509</a></li>
<li><a href="https://github.com/slightlyoff/ServiceWorker/pull/751" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://github.com/slightlyoff/ServiceWorker/pull/751</a></li>
</ul>

<h3>Header-based installation</h3>

<ul>
<li><a href="https://crbug.com/582310" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://crbug.com/582310</a></li>
<li><a href="https://github.com/slightlyoff/ServiceWorker/issues/685#issuecomment-176473764" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://github.com/slightlyoff/ServiceWorker/issues/685#issuecomment-176473764</a></li>
</ul>

<h3>Origin Trials</h3>

<ul>
<li><a href="https://github.com/jpchase/OriginTrials/blob/gh-pages/developer-guide.md" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://github.com/jpchase/OriginTrials/blob/gh-pages/developer-guide.md</a></li>
</ul>

<h3>Web Bluetooth</h3>

<ul>
<li><a href="https://developers.google.com/web/updates/2015/07/interact-with-ble-devices-on-the-web?hl=en" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://developers.google.com/web/updates/2015/07/interact-with-ble-devices-on-the-web?hl=en</a></li>
</ul>

<h3>WebUSB</h3>

<ul>
<li><a href="https://developers.google.com/web/updates/2016/03/access-usb-devices-on-the-web" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://developers.google.com/web/updates/2016/03/access-usb-devices-on-the-web</a></li>
</ul>

<p>当日の講演資料と動画は下記で公開されていますので、こちらも参照してください。</p>

<ul>
<li><a href="https://docs.google.com/presentation/d/19x3yi7Jn-6In5igGYfEiK0tBfNI290BAclT0AiqDj4Q/pub?slide=id.p" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">講演資料</a></li>
</ul>


<!-- iframe plugin v.4.3 wordpress.org/plugins/iframe/ -->
<iframe width="560" height="315" src="https://www.youtube.com/embed/MjA3XIT-IH4" frameborder="0" 0="allowfullscreen" scrolling="yes" class="iframe-class"></iframe>

]]></content:encoded>
		
		<series:name><![CDATA[HTML5 Conference 2016 特集]]></series:name>
	</item>
	</channel>
</rss>
