銀色うつ時間

思い出すたび何か胸につっかえてるだけ

Feature PolicyおよびFeature unsized-mediaの導入ガイド

個人的に注目している Feature Policy というWeb標準の技術と、Feature Policyの中で課せられるルールのうちの1つ、 unsized-media について。

Feature-Policy

Feature Policyは、ブラウザ内の機能やAPIの使用可否を開発者に提供する機能。AMPはHTMLのサブセットとして強制的にパフォーマンスの劣化を起こす機能にアクセスできないようにするプラットフォームだが、Feature Policyはそれを通常のウェブサイトで、細やかに制御ができるように設計されている。Cntent Security Policy (CSP)と構文は似ているが、CSPはセキュリティをコントロールするのに対して、Feature Policyは機能をコントロールする。開発者側で機能を明示的に制御することで、大規模サービスでの長期にわたる開発運用や、サードパーティSDKおよびライブラリの意図しない動作によってサイトのUXが低下するリスクを下げることができる。

github.com

blog.jxck.io

多くのポリシーが存在するが、例を挙げると以下のようなユースケースが考えられる。

  • サードパーティの動画のデフォルトautoplayを制御する
  • フルスクリーン画面の許可をする
  • サードパーティスクリプトの同期XHRやdocument.writeを禁止する
  • Webフォント使用時のswapを制御
  • 不必要に大きい画像のローディング制御
  • etc…

また、単に機能を許可/禁止するだけでなく、geolocationなどiframe内では原則として取得できないAPIも、Feature Policyでの許可によってアクセスを可能とすることを意図している。

基本的な構文

Feature Policyは、以下の2つの方法で適用される。

HTTPヘッダ

おそらく基本的にはこちらを使うことになるだろう。Featureには使用したいポリシーを、allow listにはドメインを列挙するか、後述する特殊文字を記述する。

Feature-Policy:<Feature><allow list origin(s)>; <Another feature><another allow list origin(s)>

実際に適用するとこんな感じ。

Feature-Policy: sync-xhr 'self' https://example.com; unsized-media 'none'

これは、同期XHRを配信する同一オリジンまたはexample.comのみで許可することを示す。特殊文字は以下。

  • * 全てのドメインで許可される。
  • 'none’ 全てのドメインで禁止される。
  • ‘self’ 同一オリジン上は許可し、それ以外のオリジンでは制限する。

iframe allow属性

Iframe内のコンテンツを制御するためには、iframeタグ内に直接ポリシーリストを記述することができる。

<iframe src="https://example.com/iframe/src” allow="fullscreen"></iframe>

Policy一覧

現在のポリシー制御機能のセットは、2つのカテゴリに分類される。

  • Enforcing best practices for good user experiences (UXのベストプラクティスを強制)
  • Providing granular control over sensitive or powerful features (強力なAPIの柔軟な制御)

Enforcing best practices for good user experiences

sync XHRやdocument.writeなどユーザ体験を損なう振る舞いを3rd partyスクリプトなどのサブリソース含めて(重要)制御する。

policy 意味
font-display-late-swap webフォントのローディング遅延によるフォントの切り替わり
layout-animations CSSアニメーションによるlayoutの発生
legacy-image-formats 古い画像フォーマットの配信
oversized-images 実際にレンダリングされるサイズより大きな画像
sync-script 同期的に読み込まれるスクリプト
sync-xhr 同期XMLHttp​Request
unoptimized-images 最適化されていない画像
sync-xhr 同期XMLHttp​Request
unsized-media サイズが未定義のメディアリソース

Providing granular control over sensitive or powerful features

ユーザの許諾が必要な強力なAPIやデバイスの機能にアクセスするものを制御できる。

policy 意味
accelerometer 加速度センサー
ambient-light-sensor 環境光センサー
autoplay メディアの自動再生
camera カメラ
encrypted-media 暗号化メディア拡張
fullscreen フルスクリーン
geolocation 位置情報
gyroscope ジャイロスコープ
lazyload 遅延ロード
microphone マイク
midi Web MIDI
payment Web Payment
picture-in-picture picture in picture
speaker スピーカー
usb WebUSB
vr VR / XR

上の表にはExperimentalなものも含まれており、ChromeであればflagやOrigin Trialなどが必要なものもある。詳しくは以下のリンクを参照すること。

github.com

プロポーザル段階のものや新しいアイディアなどがissueで議論されておりfeatureの粒度もそれぞれだが、これから更に新しいものが追加される可能性もある。

また、ブラウザの対応状況についてはこちらを。基本的にChromeFirefox, Operaのみ対応しており(各フィーチャーの対応状況はばらばら)、EdgeやSafariは現在対応していない。

developer.mozilla.org

Reporting

Feature Policyによる制限は多岐にわたりかつ強力で、プロダクションで強制的に適用するのは難しいケースもある。そのためにReporting APIを使ったポリシー違反を収集するレポーティングのみ行う機能が提供されている。

webappsec-feature-policy/reporting.md at master · w3c/webappsec-feature-policy · GitHub

現在HTTPヘッダを使ったレポーティングの機能はOrigin Trialが必要。

Report-toヘッダ

Report-To: {
  “group”: “feature-policy”,
  “max_age”: 86400,
  “endpoints”: [{“url”: “https://example.com/reports”}]
}

上記ヘッダを送信しつつ、Feature Policyヘッダにreport-toを付与する。

Feature-Policy: …; report-to feature-policy

ポリシー違反はPOSTで指定したエンドポイントに送信される。送信されるデータは以下のようになる。

{
 "type": "feature-policy-violation",
 "url": "https://a.featurepolicy.rocks/geolocation.html",
 "age": 60000,
 "user_agent": "Mozilla/1.22 (compatible; MSIE 2.0; Windows 95)",
 "body": {
    "featureId": "geolocation",
    "message": "Geolocation access has been blocked because of a Feature Policy applied to the current document. See https://goo.gl/EuHzyv for more details.",
    "source_file": "https://a.featurepolicy.rocks/geolocation.html",
    "line_number": 20,
    "column_number": 37,
    "disposition": "enforce"
  }
}

ReportingObserver

ReportingObserver APIを利用することで、JavaScriptからポリシー違反を収集することもできる。

const observer = new ReportingObserver(reportList => {
  reportList.forEach(report => {
    console.warn("Whatever you just tried to do was blocked by policy.: " + report.body.featureId);
  });
}, {"types": ["feature-policy-violation"]});

observer.observe();

unsized-media

unsized-mediaは、画像や動画などのメディアリソースの”本来”のサイズで表示するかを制御するポリシー。画像や動画に明示的にサイズが指定されていない場合、ブラウザはレイアウトを決定できずリソースをロードして初めて描画が行われるが、unsized-mediaはこれにより起きるリフローを防ぐ。サイズの指定がなくポリシー違反を起こしたとき、imgなどのタグは300x150のデフォルトサイズとして扱われるようになる。ポリシーを遵守するためには、width/height属性、CSSintrinsicSize属性によるサイズ・アスペクト比のいずれか、またはこれらの組み合わせをimgやvideoといったメディア系のタグに付与する必要がある。

intrinsic sizeって?

上述の通りメディアリソースは実際にロードしてみるまでサイズが分からない(レイアウトが決定できない)が、もしリソースそのもののサイズが予め分かっており、それをブラウザに伝える手段があればどうだろうか。 intrinsicsize 属性を指定することで、リソースの実際のサイズを上書きすることができる。

プロポーザルの詳細はこちらで確認できる。

github.com

unsized-media を適用した場合、メディアリソースのサイズは以下のように扱われる。

  • width or height の指定がなくintrinsicsizeの指定もない場合、画像は300x150でレンダリングされる
  • width or heightが指定されており、intrinsicsizeが指定されていない場合、画像リソース自体のサイズは300x150として扱われ、300x150のアスペクト比を保ちつつ指定されたwidth or heightでレンダリングされる
  • width and heightの指定がなく、intrinsicsizeのみ指定されていた場合は、画像はintrinsicsizeをの値をベースにレンダリングされる

DEMO

Layout Stability

unsized-media を単体で利用してもよいが、同じく現在Origin TrialとなっているLayout Stability APIを併用するとレイアウト崩れの大きさを定量的に収集できるかもしれない。

DEMO

利用するにあたって

2019/06/04現在Chrome Stableで確認するためにはフラグおよびOrigin Trialでトークンを取得する必要がある。

unsized-mediaを利用するにあたっては、 chrome://flags/#enable-experimental-web-platform-features を、Reporting APIとLayout Stability APIを使うためにはOrigin Trialを。

実際の利用シーン

Feature Policyはポリシーに違反した場合、代替画像で表示されたりそもそもスクリプトが実行できなかったりする。プロダクションでこのような挙動は避けるべきなので、まずはReportingから開始するのがいいだろう。レポートの送り先は集計して観測できれば何でもよい。Sentryなどを利用するか、Elasticsearchに放り込んでKibanaで可視化するといった方法が考えられる。

また、プロジェクトの早い段階で導入しておくことで強い権限を要求するAPIの使用や、意図しないパフォーマンスの劣化を防ぐことができる(または、検知できる)。外部スクリプトが好ましくない挙動をする場合も、提供者側により正確にフィードバックしやすくなる。

Feature Policyは強力かつHTTPヘッダを1つ追加するだけで適用できるので、小さいところからトライしていくとよさそう。

参考URL

ご指摘等あれば@sisidovskiまで。

日本経済新聞社を退職しました

f:id:sisidovski:20181209190053j:plain

いわゆる退職エントリ。興味のない人は閉じるボタンを。

11月末で日本経済新聞社を退職した。2年8ヶ月という短い期間だったが、素晴らしい経験をさせてもらった。

やっていたこと

日経に入社して、日経電子版のwebを新しくモダンなアーキテクチャで作り直すプロジェクトの立ち上げから参画した。これは現在r.nikkei.comというドメインから配信されている。

r.nikkei.com

結局退職までこのプロジェクトがメインの仕事になったわけだが、最後まで全く飽きることはなかった。技術的な面で飽きずに働けるということはエンジニアにとって簡単なようでいて難しいことで、それができたのは最初のアーキテクチャの設計が優れていたこと、特定のフレームワークやライブラリに過度にロックインさせないポリシー、新しい取り組みにどんどん挑戦していけるカルチャーや環境(これは単純に人手不足という話もあるかもしれない)があってこその感覚だと思う。

自分はいわばフルスタックで働いており、

  • フロントエンド
  • CDN(Fastly)
  • サーバサイド(Node.js)
  • インフラ(AWS)・DevOps

加えて、一時はPM業なども行っていた。フロントエンドでは、今でこそ当たり前になったレスポンシブデザイン、コンポーネント志向のデザインシステム、Service Workerなどを使ったPWAの実装などを実践で経験することができた。フロントエンド方面のみ取り上げられることも多いが、ハイパフォーマンスなwebアプリケーションを開発するにあたって最も大事なのはキャッシュ戦略とそれを実現する設計であり、このプロダクトを通じてそれが学べたのは大きな財産になった。単なるCDNの役割を超えたFastlyを使ったedge computingをそれなりに大規模なサービスで利用できる機会は貴重だし、利用しているプロダクトは日本を見回しても数少ないと思う。FastlyとElastic Beanstalkを使ったマイクロサービスの設計も、スケールしつつある組織とプロダクトにとってよかったように思う。フロントエンドも画面ごとにリポジトリレベルで別管理されており、分割が容易だ。前職ではCDNAWSも使った経験がなかったが、これを経験豊富な同僚のもと学ぶことができたことは自分にとって僥倖としか言えない。

@triblondon, @cssradar, @boblet, @sugimoto1981 といったとんでもなく優秀なメンバーと仕事ができたことは自分にとって糧になった。新しく入った若いメンバーたちも優秀で、下の世代から学ぶことも多かった。

リリース後には幸いにして多くの反響をもらい、PRという意味でもビジネス的にもポジティブな結果を残せた。特にサイトの速度という点に着目されたのは、エンジニア冥利に尽きるというものだ。これは自分の力というわけではなくて、優秀な同僚たちのお陰であり、更には日本経済新聞という大きな看板を背負わせていただいたからに他ならない。

サービスの大小に貴賤なしとはいえそれなりに大規模なトラフィックを有するサービスは外への影響力も大きく、自分の仕事が社会に与えるインパクトをダイレクトに感じられた。今年のGoogle I/Oやその他の海外カンファレンスで事例として紹介されたことはエンジニアとして大きな自信に繋がったし、それ以上に自分たちのサービスから配信されるニュースが日本に大きな影響を与えたり、市場を揺るがしたりすることに大きなやりがいを覚えた。

逆に言えば、システム障害で電子版が止まるということは社会に大きな影響を与えることであり、それなりの責任感も求められる。

f:id:sisidovski:20180509100434j:plain
同僚に撮ってもらったGoogle I/Oでの一コマ。日経について@addyosmaniが話している。

ニュースメディアは社会的意義のある仕事だ。

英語について

英語に関しては、日経に転職してから初めてちゃんと勉強した。海外グループ会社から来た人間や外国籍のメンバーがいたこともあり、チームでは英語でドキュメントを書いたりMTGをしたりしていた。大学時代は語学の単位を落として留年するほどには外国語が苦手だった自分もそのお陰で、多少は上達したように思う。もちろん手放しで上達したわけではなくそれなりに努力もしたが、入社して2年後には海外の現地企業でなんとか働ける程度にはなった(もちろんまだまだ課題はあるし、全然上手くはない)。日経はイギリスの経済紙フィナンシャル・タイムズをグループ会社に持っており、彼らと一緒に仕事ができたのは他では得られない経験だった。あまり知られていないが、日経では意外にも英語を使うシーンが多い。

そういったパスが整備されていて誰でも機会が得られるわけではないが、自分から主体的に働きかけていけば機会は得られるし、会社はそのためのサポートをしてくれる。海外カンファレンス参加の機会は当たり前として、何なら海外赴任だってチャンスはある。語学に関する投資もしてくれる。

やらざるを得ない環境に身を置くこと、時節訪れる機会に手を挙げることが人の人生においていかに重要かを身を持って学んだ。

日経、あるいは非web系の大企業で働くということ

日経電子版というビジネスは、おそらく大抵の人の想像を超えるほど多くの人間が関わっている。エンジニア、デザイナ、プロダクトマネージャ、マーケター、データサイエンティスト、そして記者や編集者などがステークホルダとなる。想像の通り組織は強烈な縦割り構造が存在し、年功序列、終身雇用といった古き良き日本社会の副作用がそこかしこに内在している。そんな組織の中で仕事を進めるのはかなりのハードワークだし、上を説得するための資料づくりみたいな仕事に躍起になっている人もままいたりして(現段階でこれはある種必要なことだ)、気がつけば社内のことばかりを考えてエンドユーザを蔑ろにしがちだったりする。おそらく多くの日系大企業が同じ病理に陥っているのだろう。しかし、だからこそ。こういった組織を変えていく経験はweb業界では中々できないことだし、その先のキャリアパスも明るいように思う。本当に、ここを変えて行けなければ日本は駄目になってしまう。そのくらい大企業が日本経済に占める比率は高いのだ。前職での似たようなバックグラウンドの人たちに囲まれた会社員生活に時節望郷の念を抱くこともあったが、多種多様な職種の人と仕事をするのもある種の楽しさを覚えるものだ。

その一方で、電子版の開発組織はかなり若い組織でもある。20~30代の若いエンジニアがバリバリに活躍しているし、上層部は若い世代の感覚を理解しており、使うツールなども殆どの場面で制限がない。現状の組織では人数が少ないこともあり、個々人が活躍でき、プレゼンスを発揮しやすい。対外発表にも積極的で、過半数のエンジニアが何かしら外に向けて発信した経験があるのではないか。自分もいくつかのカンファレンスで発表したり、技術書典で記事を執筆したりした。エンジニア向けの新人研修も行うようになったし、社内isuconなども今後やっていく予定だ。

電子版をはじめとする日経のデジタル事業は、いわゆる大企業的な面と非常に若い組織という面を併せ持っている。大企業と聞いて想像するような、色々な環境が用意されている状態を期待する人には向いていない。エンジニア組織もまだまだ未熟だ。自分の理想とする環境や組織を自分の力で作り出していかなければいけない。しかしながらそれができれば、これ以上ない活躍のフィールドを与えてくれる。そういった組織を変えるべく日夜働いている同僚たちもおり、彼らを心から尊敬しているし、遅かれ早かれ古い体質から脱却していくことを確信している。

f:id:sisidovski:20181209231538j:plain
千鳥ヶ淵が近いので花見をしながらランチなどもした

このような変革は度々起きているはず。創業140年を超える会社なのだ。

業界の話

ニュースメディアという産業は今危機的な状況を迎えている。戦後より続く紙の新聞配達という強固なビジネスモデルがいよいよ終わりを迎えつつあり、デジタルで生き残れるモデルを確立できないとその先にあるのは衰退だ。本旨から逸れるのであまりこれに反応してほしくはないが、新聞社や各種メディアが力をなくしたとき、政治の腐敗や企業の粉飾など社会を揺るがす不祥事を、一体誰が監視するのだろう。地方のしがない出来事を誰が世に出すのだろう。美術展やスポーツのパトロンは誰が?この業界は単に新聞を届けるだけの仕事ではない。

更に言うと、人件費を下げればいい話だとも思わない。儲からない仕事に優秀な人間は入ってこない。若者が憧れる仕事にジャーナリストという職種があってほしいし、彼らには高給取りであってほしい。内製によるエンジニアリングは、それに貢献する。

驚くべきことに、日経が新聞製作システムをコンピュータ化したのは1970年代だ。また、データベースビジネスもこの頃から始めている(こういった偉業を成し遂げた圓城寺次郎という人間については後日記事にする)。90年代になっても電子版の前身であるNIKKEI NETの開設や広いIPアドレス帯の取得など、日経には実は古くから連綿と続くエンジニアリングの歴史がある。そして現在も、間違いなく業界のトップランナーとしてデジタルビジネスを作っているが、まだまだ紙のビジネスの代替たり得ない。

そういった大きな業界のうねりに身を置くことは、非常に意義を感じられる楽しくやりがいのある仕事だった。

なぜ辞めるのか

いかに新聞業界が斜陽産業と言われ年功序列で若い世代が年長者よりも給料が低いとはいえ、業界トップレベルの企業ということで待遇も申し分なかった。自分の年収はここでは明かせないが、詳しく知りたい人は直接聞いてもらうなりググったりしてもらえば分かるかと思う。社内には見晴らしのいい社食やトレーニングルーム、何なら診療所まである。長い目で見たときにこの先どうなるかは分からないが、それはどの会社でも同じだと思う。

辞めるのにはいくつかの理由がある。

1つは、プロジェクトの志半ばでチームメンバが大量に離脱したことだ。様々な事情があるにせよ、組織の論理が働いたことは事実だ。その人事異動に透明性と納得感があれば、あるいは会社を離れずに済んだかもしれない。組織に所属する上でどうしても避けられないことがあるのは百も承知しているしそういった場面でも受け入れて仕事をしてきたつもりだが、僕らは会社の犬じゃない。納得は全てに優先する。

もう1つは、働いているうちにエンジニアリングだけで解決するのが難しい事柄が増えてきたことだ。これはどんな組織でも同じだと思うが、最終的に問題は技術そのものでなく、組織デザインや意思決定に依る部分になってくる。ここにエンジニア個人としてのキャリアの限界を感じていた。もちろんエンジニアとして働いていけるのに越したことはないが、自分が思うユーザに本当に提供したいもの、ビジネスのあり方を考えていくうちに、フォーカスすべきは目先のエンジニアリングではないかもしれないと思うに至った。

強調しておくが、これは会社の問題ではなく、僕個人の人生観の問題だ。

しがないCSの学位もない凡庸な技術者だった自分を拾ってくれた会社には感謝しているし、道半ばで離れることを申し訳なく思う気持ちが強い。一緒に働いた同僚たちのことは大好きだし、これからも何かしらの形で関わっていきたい。

このエントリはある程度フェアな視点で書いたつもりなので、これを見て少しでも日経をはじめとする日本のメディアビジネスに興味を持ってくれると嬉しく思う。日経では中途・新卒問わずエンジニア・プロダクトマネージャなどデジタルビジネスの人材を募集しているので、興味の湧いた人、進路を考えている人は選択肢のひとつに入れてほしい。僕に直接DMを送ってもらっても構わない。

HACK The Nikkei

Shunya Shishido (@sisidovski) | Twitter

次について

Mobile Technical Solutions Consultantとして、12月からGoogle Japanで働いている。エンジニアという肩書きはなくなったが、引き続きwebに関わる仕事をやっていくし、コードもたくさん書いていく所存。PWAやAMPはもちろんのこと、他にも面白いweb技術をたくさん世に出していきたい。

というわけでみなさん、これからもよろしくお願いします。

f:id:sisidovski:20181209190453j:plain

Happy Hacking Keyboard 無刻印モデル 清掃時のキー配置

Happy Hacking Keyboardを掃除しようと思ったとき、キートップを外した後無刻印モデルはキーがどこに配置されていたのか分からなくなるという問題がある。掃除そのものに関してはたくさんの記事が他に存在しているのでそちらに任せるとして、キー配置についてだけ記述しておく。

実は無刻印モデルでもキートップの裏に番号が記載されていて、それを基にどこにキーが配置されていたかが分かるのだが、全てメモを取っていくのは骨が折れる。今回骨を折ってキー配置をメモした図を作成したので、無刻印のHHKBを利用している人は参考にするとよい。

f:id:sisidovski:20171125124315p:plain

上記のマップは、HHKB US配列 無刻印、製造年は2011-06の製品を基に作成した。HHKB、無刻印キーボードでも裏に謎の番号ではなくてどのキーに対応しているか印字しておいてほしいですね。

Mockeryでモジュールをモックに差し替える

Mockeryを遣ってNode.jsのテストを書いている。requireしたモジュールをそのままモックに差し替えるとき、差し替えたいモジュールに渡されている文字列と全く同じものを渡す必要がある。差し替えるモジュールが標準ライブラリやnpm経由で取得してきたものであった場合はそのまま書けばよいが、自前のモジュールを相対パスなんかで呼び出しているときに気をつける。例えばこんな場面。

/lib/module.js

module.exports = {
  ouputMessage: () => {
    console.log('Hello from mod');
  }
};

/lib/foo.js

const module = require('./module.js');
module.outputMessage(); // Hello from mod

/lib/bar.js

const foo = require('./foo.js'); // Hello from mod

/test/index.js

const mockery = require('mockery');

describe('test', () => {

  before(() => {
    const mock = {
      outputMessage: () => {
        console.log('Hello from mock');
      }
    };
    mockery.registerMock('./module.js', mock);  // ここ
    mockery.enable({
      useCleanCache: true,
      warnOnUnregistered: false
    });
  });

  after(() => {
    mockery.deregisterMock('mod1');
    mockery.disable();
  });

  it('should say hello from mock', () => {
    const bar = require('../lib/bar.js') // Hello from mock
  });
});

テストコードからの相対パスではなく、モック化したいモジュールのパスをそのまま記述しないと動作しない。割と忘れて気づかないパターンが多い。

github.com

The arguments to registerMock are as follows:

module, the name or path of the module for which a mock is being registered. This must exactly match the argument to require; there is no “clever” matching.

はい😇

fetch APIのredirect関連メモ

HTTPS環境下で特定のURLからデータを取得するときに、リソースがHTTPで配信されていた場合、当たり前だけどMixed Content警告が発生する。

fetch('http://www.yahoo.co.jp/');
// こんなのが出力される
// Mixed Content: The page at 'https://example.com/' was loaded over HTTPS, but requested an insecure resource 'http://www.yahoo.co.jp/'. This request has been blocked; the content must be served over HTTPS.
// Fetch API cannot load http://www.yahoo.co.jp/. Failed to start loading.(anonymous function) 
// Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: TypeError: Failed to fetch
// Uncaught (in promise) TypeError: Failed to fetch(…)

基本的にはHTTPS以外のものは呼ばない、CSPなどを利用してそもそもHTTPのリソースがブロックしてしまうといった解決策が存在するが、HTTPSのURLがHTTPのURLにリダイレクトする場合はどうすればよいのだろうか。

$ curl -I https://example.com
HTTP/1.1 302 Found
Content-Type: text/html; charset=UTF-8
Location: http://other-domain.com
Content-Length: 261

HTTPSからHTTPにリダイレクトなんてないだろ、と思ったりもするが、後方互換性を重視した開発の現場では稀にそういうことがある。IDの若い特定のユーザには古いページを表示する、古いページはHTTPSに対応していないといった状況などがそれにあたる。

fetch APIはredirectオプションによってリダイレクト時の振る舞いを制御できる。

developer.mozilla.org

redirect: The redirect mode to use: follow (automatically follow redirects), error (abort with an error if a redirect occurs), or manual (handle redirects manually). In Chrome the default was follow before Chrome 47 and manual starting with Chrome 47.

Fetch Standard

A request has an associated redirect mode, which is "follow", "error", or "manual". Unless stated otherwise, it is "follow".

w3cのspecにも未設定の場合はfollowが選択されるとあるし、実際の振る舞いもリダイレクトしてリソースを取得している。オプションの振る舞いをまとめると、2016年10月現在では以下のようになる。

  • follow
    • デフォルト. リダイレクト先まで追従してリソースの取得を行う
  • error
    • リダイレクトが発生した場合にネットワークエラーを投げる
  • manual
    • 手動でリダイレクトを制御する。このときResponseオブジェクトは opaqueredirect となる。opaqueredirectは、レスポンス内容が隠蔽された状態のオブジェクトで、実際のレスポンスはinternal responseにアクセスする必要がある(この辺りは検証不十分)

つまり、今回のようにHTTPS→HTTPへのリダイレクトが行われ得るリソースに関しては、redirectオプションにmanualを渡してあげれば一応mixed content警告は回避できそう。

fetch('https://redirect-to-http-resource.com', {redirect: 'manual'})
  .then(res => {
    // do something
  });

8月の現状

今日のこと。

七尾旅人の「兵士A」が各所で評判がよいので観たいと思うも、最終回17時半スタートという厳し過ぎる現実に突きつけられる。「兵士A」という名前からも、性質からもかの作品「911FANTISIA」を想起させるし、いずれお目にかかりたい。

代わりにちょうど公開初日だった「君の名は。」を観た。

f:id:sisidovski:20160827040419j:plain

まだ会ったことのない君を、探している

新海誠について、割と苦手意識があったのであまり期待していなかったのだけれど、大きく裏切られることになった。

新海誠作品に対して抱く名状し難い感情や身構えてしまうような感覚は、割と多くの人が共有できるところだと思う。肥大化した自意識と自己完結型の展開、それらに辟易としつつもある種の愛しさを湛えるようで、例えるなら村上春樹の影響をもろに受けた高校生の痛々しさ、そういう類の嫌悪、懐かしさであるように思う。今作はそういった「全力っぽさ」を上手に使えているようで、安心して観ることができた。今までの作品と比較すると、物語の骨子が強固なものになっていて、安定した土台があることが大きいようで、その土台があるからこそある種の白々しさを楽しめた、ということになるのだろうか。途中で挟まれるRADWIMPSの歌すら心地よく、新海誠的な思春期の男子が抱く陰湿な演出ではなく、ある種の爽やかさが駆け抜けるわけである。

新海作品の根底には常に「距離」が描かれていて、そのメタファ・舞台装置として空・線が登場する。今作も同様だが過去作品と比較してみると明らかに異質なのは、長編アニメーションとして成立するだけの奥行きを持ったストーリーが存在する点だ。そこから派生していくコメディ・SFに引き込まれ、夢中になっている内に、気がつけば「距離」の袋小路に入ってしまった。今までの作品は最初から「距離」を露骨に描きすぎいてどこか観ながら冷めてしまう自分がいたが、今作はよい意味で感情移入できて、最後は胸が締め付けられる思いだった。よい意味で新海誠らしからぬ普遍性あるストーリー・演出から来る面白さと、新海誠らしい言葉選び・距離感の描写・美しい映像が混じりあって、傑作だったように思う。セルフオマージュが散見しているところもよくて、秒速5センチメートルの、言の葉の庭の。ケモナーなってしまった細田作品と比較しても遜色がない程に面白かったのではと、思う。

youtu.be

夏の気持ち

この夏、蔵前や南千住で飲んだくれたり、隅田川をぼんやり眺めたり、シン・ゴジラを何度も観たり、サマソニでCreepを聴いたりと、割と楽しく過ごしている、たくさんサウナに行ったし、瞑想や筋トレもしてるし、夏の終わりにまた素晴らしい映画に出会えたし、やっぱ夏はエモくて最高っぽい。

観終わったあと、岐阜に住む祖父母に会いたいと思ったし、帰りに観た東京のビル群は、美しかった。何を書いてもネタバレになりそうだし、早く観た人と話したいし、早くもう一度観たい。

Progressive Web Appsについて

ドワンゴ Advent Calendar 2015 4日目の記事です。最近ちらほら耳にする、Progressive Web Appsというものについて。

Progressive Web Appsとは

Progressive Web Appsは、モバイルWebを開発する上での1つの標榜である、という認識でよさそう。Alex Russell氏がブログで提唱したのが始まりで、先日行われたChrome Dev Summitのkeynoteでも取り上げられている。

medium.com

www.youtube.com

Progressive Web Apps自体は特に何か新しい技術を指すわけではなく、既存の技術の組み合わせに対して名前をつけただけのいわばマーケティングワードみたいなもので、つまるところ「◯◯と☓☓を組み合わせて作られた、よりアプリっぽい快適な動作をするWebアプリ」のことをProgressive Web Appsと呼びましょう、これからのWebはそうやって作っていきましょう、という宣言のようなもの。上記エントリでは次のように表現されている。

they’re just websites that took all the right vitamins.

こういった考え方が出てきた背景には、モバイルでの開発を取り巻くいくつかの課題が存在している。

とりあえずアプリ作ればいいと思ってませんか?

ネイティブアプリは確かに使いやすいしユーザとのエンゲージメントも高いけど、インストールされなくては意味が無い。ユーザが1ヶ月で利用する平均的なアプリの数は25個程度で、他のアプリとその枠を巡ってしのぎを削らなくちゃいけない。あなたのアプリは果たして、ストアに存在する膨大なアプリの中から検索され、インストールされ、実際に起動される、その25個の中に入れるだろうか。また、iOSAndroid、その他プラットフォームも含めてそれぞれで開発しなくてはならないし、保守コストに関しても馬鹿にならない。

ネイティブアプリの方が便利じゃないですか?

問題点として、モバイルにおけるWebアプリが根本的にイケてないということが挙げられる。ネイティブアプリよりレスポンスが遅い・タッチした後のフィードバックが作りこまれていない、オフラインで利用できない、プッシュ通知がない、ブラウザはタブが使いにくい等、考えればきりがない。

Progressive Web Appsが解決するモバイルWeb

上記問題に対して、TitaniumやXamarin、PhoneGapなど複数プラットフォームで利用できるハイブリッドアプリの開発が考えられるが、それはプラットフォームごとに開発するコストを減らすのみで、デプロイが楽になるわけでもないし、ストアからインストールされる可能性を上げてくれるわけではない。もっとも、開発コストを減らしてスピーディーに開発ができているかは、実際にハイブリッドアプリからネイティブに書きなおした事例なんかを考えてみると、有用な場面はかなり限定されると思う。

そこで提唱されているのが、HybridではなくProgressiveなWeb Apps。各プラットフォーム上で動くハイブリッドアプリを作るよりも、Web上で動作するアプリケーションを作っていく方が、よりネイティブアプリに近い動きを実現し、またユーザが利用する機会も増やせるのではないかという仮説に基づいている。WebをWebたらしめる性質なので当たり前だが、Web上にホストしてインストール不要で使えること、ハイパーリンクが利用可能であることなどを考えると、ユーザが利用する上でも、アプリケーションを提供する側としても扱いやすいし、プラットフォームごとに開発するコストやストアという仕組み上の制約(上記インストールまで到達する困難さ、課金システム、表現上の問題など)を考慮する必要がないことは大きなメリットとなり得るように思える。App Indexで最近アプリも載せられるようになってきたけど、検索インデックスを利用することもできる。

Webアプリが"appy"になるために必要なテクノロジー

下記のような機能を用いて、よりネイティブらしいアプリケーションを提供する。先に紹介したAlex氏のエントリや動画を見ると他にも列挙されているが、ここではより重要と思われるもののみ抜粋している。

  • Responsive Web Design
  • Service Worker(オフラインでの利用を想定)
  • ネイティブアプリのようなUIとインタラクション
  • Push Notification on the Open Web(Webアプリでのプッシュ通知)
  • App Install Banners(いわゆるブラウザが提供するホームに追加)

Responsive Web Design

割愛。

Service Worker

一応Service Workerについて軽く説明。Service Workerは通常のWebページのとは独立したサイクルでjsの実行コンテクストを提供する、イベント方式の機能。リクエストをフックしてキャッシュから応答したり、サーバのイベントを受けてそれをクライアントに通知するといった、クライアント用proxyとして利用できる。Service WorkerはHTTPSもしくはlocalhostでしか利用できないので、遊びで使うならgithub pagesなどを使うのが遊びではよさそう。デバッグしづらいので、作業はシークレットモードで行った方がよい。まあとにかく、Servie WorkerはProgressive Web Appsのコアとなる技術で、以下のような役割を果たす。

  • 静的ファイルをキャッシュすることで表示速度を改善する
  • サーバから取得したデータをキャッシュすることで、オフラインでの利用が可能になる
  • PUSH通知を受け取る
  • Service Workerを利用する = TLSでの通信が行われることなので、安全性を高められる

http://www.w3.org/TR/service-workers/

Web App Manifest

アプリに関連するメタデータを記述したjsonファイルをサーバに置くことで、それらの情報をブラウザやクローラが解釈してくれる。Service Workerと併用することで、プッシュ通知やホームに追加が実現できる。

https://w3c.github.io/manifest/

Offline Use

Service Workerを使ってfetchイベントをキャプチャし、キャッシュが存在すればそれを返し、なければネットワークから取ってくる、といった動きが実現できる。これによって、1度閲覧したページはキャッシュされ、オフライン状態でもキャッシュから表示ができるようになる。

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        if (response) {
          return response;
        }

        var fetchRequest = event.request.clone();
        return fetch(fetchRequest)
          .then(function(response) {
            if (!response || response.status !== 200 || response.type !== 'basic') {
              return response;
            }

            var responseToCache = response.clone();

            caches.open(CACHE_NAME)
              .then(function(cache) {
                cache.put(event.request, responseToCache);
              });

            return response;
          });
      })
  );
});

Push Notification

ブラウザからのPUSH通知。facebookなんかはもう実装されていた気がする。今回時間の問題で実装できなかったが、概ね以下のような手続きのはず。

  • GCMを使うためにGoogle Developer Consoleの利用登録してプロジェクトIDを取得する
  • manifest.jsonに書く
  • Service Worker登録
  • pushManagerを利用してsubscribe
  • Service Worker側でpushイベントをlistenする 

App Install Banners

バナーの追加UIを表示させる

  • manifest.jsonの配置
  • ServiceWorkerの登録

manifest.jsonに以下の値を設定する。

{
  "name": "a progressive web app for cats",
  "short_name": "PWA for cats",
  "icons": [{
        "src": "images/touch/ms-touch-icon-144x144-precomposed.png",
        "sizes": "144x144",
        "type": "image/png"
      }],
  "start_url": "/?homescreen=1",
  "display": "standalone",
}
  • iconは144*144サイズのものが必要
  • start_urlはホームから起動したときのURL
  • displayは起動時の表示方式。ネイティブアプリのようにブラウザとは別のタスクとして扱うことが可能
  • Service WorkerはregisterしていればOK

その他

他にも、ページ内のコンテンツとUIの部分を分けてUIのみ先に描画する工夫であったり、ブロッキングを行う読み込みをasyncなどを用いて改善するといった対応が必要。以下のページにこのあたりの改善方法についても書かれている。

Instant Loading Web Apps With An Application Shell Architecture | Web Updates - Google Developers

今回のサンプルは以下

sisidovski/progressive-web-app-sample · GitHub

事例

Flipkart.lite

以前ちょっと話題になっていた、インドのECサイトFlipkartのWebアプリケーション。 通信インフラが整っていない途上国はこういった軽いページに対する需要が高そう。

stories.flipkart.com

Pokedex.org

Progressive Web Appsとして作ったポケモン情報まとめ。パフォーマンス向上のためのテクニックがすごい。60fpsで動作させるための工夫が細かく説明されている。

www.pocketjavascript.com

まとめ

業界全体が「お陰さまで合計◯◯ダウンロード!」とか煽ったりしているしネイティブから脱却するとかは全く思わないけど、ネイティブアプリの市場が成熟しつつある中で、ネイティブ風というだけでなくWebらしさを活かしていくことがモバイルWebの流れとしては出てきたのかなと感じた。Progressive Web Appsに限らずiOS9のSearch API関連(Universal LinksやSpotlightによる検索)も含めると、アプリとWebの境界が曖昧になるというトレンドが浮かび上がってくる。この辺りの話は、先日のmosaic.fmがとても面白かったので未聴の方は是非聴くとよいと思う。

#20 Browser | mozaic.fm