IE11とその他ブラウザの最新版ってどのくらいベンチスコアに差があるのか(手抜き版)

IE11のリリースは2013年秋。それから7年間で競合がつっこんだ投資の結果、ベンチマークスコアに差はどのくらい生まれているのかを知りたかった

注意点

  • EdgeHTMLを実行できる古いWindows環境は手元にないので無視する
  • Microsoft Edge (Blink)はどうせBlinkでしょ….ということで無視する
    • Edge限定のパスとかMSが用意してないことに掛ける
  • もはや大体のベンチマークは動かないので、ノー修正で動くものだけトライする
    • 結果, JSとDOMだけの計測に
    • たぶんpolyfill入れたりbabel通せば動くとは思うが、とりあえず今回はスキップする
      • MotionMarkは動かしたかったが…..
    • そのうち真面目に色々動くようにした上で再計測にしたやつをやるかも

環境

  • Windows 10 20H2
  • Dell XPS 13 (2015)
    • CPU: Intel Core i7 5500U 2.4GHz(物理2コア4スレッド)
    • RAM: 8GB
    • GPU: Intel HD Graphics 5500
  • ハイエンドではないけど、まだまだローエンドってわけでもない、はず

スコア

JetStream 1.1

IE11 Firefox 85.0 Chrome 88.0.4324.146
Latency 74.477±18.065 75.946±22.499 47.194±22.116
Throughput 69.304±4.1786 144.56±15.191 191.58±125.61
Geometric Mean 71.482±9.9780 109.16±20.701 103.91±58.452

Speedometer 1.0

IE11 Firefox 85.0 Chrome 88.0.4324.146
Score 23.6±1.9 89.9±2.8 125.4±0.81

BlinkとWebKitの違い(大雑把)

「〜がChromiumベースに!」なことが起こるたびに「Chromium/BlinkはWebKitを源流とするエンジンでしかじか」みたいな話が出てきて、「実質WebKitだから同じだね」という反応が出てくるのが恒例行事っぽくなってるけど、結構モニョモニョする。

先祖が同じなら子孫も同じ、ってそんな単純な話じゃない。

fork前、BlinkがChromium WebKitというかWebKit Chromium portと呼ばれていた頃でさえ、Chromium portとApple portの2つが同じエンジンと呼べる箇所って、layoutとかdomとかstyleとかブラウザエンジンのコア部分だけで、他はV8とJSCとか、SkiaとCore Graphicsとか、そもそもプロセス分けてる方法も違うし、呉越同舟というか寄り合い所帯感だった。composition周りだってApple portはCore Animationにべっとり依存するような実装じゃなかったけ。

それで、数少ない共有部なコアでさえ、fork後に色々変わっている。自分の知っている・思い出せる範囲で大きいやつだと

  • layout
    • ChromiumはlayoutをLayoutNGって書き直しをやった
    • WebKitもLFCってコードネームで書き直し中
  • style
    • Chromiumは大きな書き直しは(僕の聞いた限りだと)ない、はず
    • WebKitCSS JITを投入した。その時にJITの有無に関わらずに色々変更が入った
  • DOMのオブジェクト管理
    • ChromiumはOilpan GCとかやってるし
    • WebKitはconstraintsとかいう魔術的な仕組みが投入された模様

とかとか。細かいあれこれを挙げだすとキリがない。

GeckoやEdgeHTMLと比較した場合、WebKitとBlinkは近縁種だと思うけど相対的なものでしかなくて、それぞれが呉越同舟していた時代に追加された仕様未定義な挙動に箇所に関するテストケースや、同祖であることに由来する箇所くらいしか、同じエンジンとは言えない。ましてや最近実装されたものは以下略。

リポジトリを境界としたコードベースとしては、WebKitは変わらずライブラリ指向だけど、Chromium/Blinkはプラットフォーム志向で、リポジトリの成長の方向性としてはFirefox/Geckoの方が近くなってきている

でも、そう変わるのも当然で、当代で世界トップクラスにお金を持っている二社が別々の方針で(数十人とか数百人のオーダーで)エンジニアを7年も張り付けていて、しかもそれぞれプロジェクトが実現したいWebに対するスタンスは結構違っているのに色々変わらない訳が無い。

でも、一方でWebGLの実装とかはWebKit(のApple port)もANGLE使おうとしてて、このままだとGecko/Blink/WebKitのどれもANGLEになろうとしているんだから、面白い。

EdgeHTMLを悼む

久々に色々書きたい気持ちになった + 矢倉さんの書かれたものを見て、彼とは微妙に考えることは違うかなあと思ったので書くだけ書いてみる。意見似てるなと思ってるところは書かないようにはした(標準化方面周りとか)。あと、Webブラウザ周りの現状に明るくない同僚や友人向けのテイストは含んでいる。

そもそもの大前提

まず、Webという文書・アプリケーションプラットフォームの価値は「標準仕様に基づく相互運用性」「インストールせずとも使える」の二点に集約されると自分は思っている。

最近はずいぶん聞かなくなった「Webは簡単に作りやすい」というメリットは、「Win32のデスクトップアプリに比べると」という但し書き付きで、90年代は事実だったと思うけど.NET Frameworkの進化とかモバイルOSアプリが出たりとか業界の成熟に伴って事実ではなくなって久しいと思う。

この「標準仕様に基づく相互運用性」というのは、標準仕様を実装しているソフトウェアがあればあるほど担保されやすくなっている。単一の実装と単一のテストケースだけでは、どうしても特定の実装に依存する標準仕様になっていることがあって、まず相互運用性なんてものは実現されないと思っていい。複数の実装があって、それぞれで相互に検証して、どれが実装のバグなのかどうかを試しかめるフェーズが非常に重要になってくる。

ここで問題になるのが、「今日の商用利用に耐えうるWebブラウザをゼロから新規に作り直すのは現実的には不可能である」ということ。理由は

  1. Webブラウザに求められる機能が非常に多いため、開発には多大なる人間や金や時間が必要であるため
  2. 歴史的経緯に基づく挙動や標準仕様、意図的に無視する必要のある仕様もいくつか存在するため、それらを含めて相互運用可能な状態にするには本当に大変な労力が必要となる
  3. 今日の商用利用に耐えうるWebブラウザは化石ではないため、日進月歩で進化を続けていく。仮に再実装する場合は、彼らに追いつく速度で進化させつつ足りない歴史的な機能を実装し続けていく必要がある
  4. そのような大事業を新規でやることに経済合理性がある会社は世界中のどこにも存在しない。
    • 2008年にGoogle ChromeをリリースしたGoogleでさえWebKitを利用することを選んだし、そのWebKitを生み出したSafariでさえ2001年の時点でKHTMLをforkすることを選んだ。どのエンジンも90年代のWeb黎明期に作られたエンジンを20年かけて改良し続けることで今日の日常的な商用利用に耐えうるソフトウェア足り得ている

などが挙げられる。

おもちゃ程度であれば一人でも十分に再実装は可能だし、00年代の中盤に求められた程度の機能であればエース級エンジニアを10人ほど集めて2~3年やれば近しいところまで実現できるというのはMozillaのServo Projectが2013~15年ごろに証明したけれども、アクセシビリティとかコーナーケースなどの対応を考えるともっと人数がいるし時間もいる。

そこまでやってもゼロからの新規開発では15年近く前の環境しか作れないので、何も経済合理性が存在せず、むしろ既存の実装を捨てることで影響力が消えたり、イニシアチブを取れなくなったり、相互運用性を損なう業界へのリスクを考慮して継続しているのがWebプラットフォームないしWebブラウザ開発の現場だったりする。

Opera (Presto)の思い出とwebでコードを書く話

そんな状況に陥ったWebブラウザの開発が中断されるのは別に今回が初めてじゃない。2013年には組み込み向けビジネスを当時のWebKitに破壊され、収益の柱を失ったOperaが自社製エンジンの開発を断念してChromiumに移っている。

自分はPrestoの死を悼むし、今回のEdgeHTMLの死も悼むけれども、それらが別にコードを書いていて楽しい相手だったかといえば必ずしもそうではない。

ソフトウェアなのでもちろんバグってることはあるし、「このブラウザ向けに対応しても喜ぶユーザーは滅茶苦茶少ないかもなあ」と思ってそれら専用のworkaroundを書くときは複雑な心境ではあった。だけどそれはPrestoやEdgeHTMLに限った話ではない。あらゆるソフトウェアにおいてバグが存在しているので、それに出会うかどうかの運の問題でしかないし、ユーザーが多いブラウザの方がバグが見つかりやすくて直されやすいというだけでしかない。Google ChromeだってSafariだってFirefoxだって、ブラウザの実装バグに悩まされなかったブラウザなんて一つもない。むしろインターネットの声の大きな人たちから好かれて絶賛されているメジャーなブラウザがバグっていて、普段は嫌われ者のあいつがちゃんと動いてるときは本当に複雑な気持ちだった。だけど、それが相互運用性のあるプラットフォームというものなのであるし、そうしたバグを踏んでしまった場合に誰が悪いかを決めるのが標準仕様なんだ。僕たちはそのメンドくささを飲み込んで、その上にあるメリットを掴み取ることにした。だからそこに愚痴は言えども、ちゃんとissue trackerにバグを報告して次のリリースで直ってくれればいい。それで終わりだ。

そんな心持ちでいたからこそOperaがPrestroを止めたときはショックだったし、その後にBlinkに移行した結果、Presto時代のOperaの開発者の多くがレイオフないしは自主的な退職でOperaを去り、Opera Software所属の社員の姿を標準化のメーリングリストや議事録から消えるのを見るのは複雑な心境だった。もちろんAnneみたいに他のベンダに転職して継続して活動している人もいるけど、全体としては標準化の要である「目」と実装の数が減ってしまったのは事実だ。

そうした過去や、MicrosoftIEで市場を一旦破壊・独占した結果として標準仕様の価値が無に帰した時代を知っているだけに、少なくともWebでコードを書いて銭を稼いでいた人間としてはこれ以上エンジンが消えることはWebの相互運用性に悪影響を及ぼす可能性というものは心配していた。

ましてやGoogle Chromeが、Googleの多大なる投資の結果、彼らのWebサービスとの強い結合とともにWebにおいて、かつてのIEの権勢を思い出すような強いシェアをモバイルとデスクトップの両面で持ち始めている。私企業の経済活動が「うっかりやり過ぎてしまって」、結果として全体としては不幸なことにになリかねないという懸念の矢先にEgdeHTMLの開発停止が発表されてしまった。

EdgeHTML

EdgeHTMLは、TridentをforkしてIE時代の互換コードをバッサリと落としたり、内部の文書構造がWebブラウザ向けではなかったのをDOMベースにするなどのリファクタリング話をある時期までよく発表していた。Chromium互換っぽく見せることに注力する傾向があったため、悪い意味でChromiumのダメな箇所を引きずったりもしているなと思ったし(Extension周りのAPIの作り方とか。あれは今やるならMozillaみたいにPromise使わないとダメだよ)、Windows 10のややこしすぎるサポートポリシーのために厳密な市場シェアの高いバージョンを調べるにはサービスごとのユーザー動向を見る必要はあったけれども、総体としてはずいぶんサクサクと動いてくれるし、IEと比べてwebdevとしてもすごくユーザーに勧めやすいブラウザの中身になっていたと思う。

動画系のAPIのように標準化の隙間が結構見え隠れするAPIの挙動は不安定だったりするし、Microsoft Connectに登録しても本当に直してくれるのか不安なところはあった。けれども、それは各OSに特化して作ったブラウザのあるあるなところで、Safariみたいなものだと思えば先述の通り問題なかった。少なくともEdgeHTMLについては、標準仕様に基づく相互運用性が高く、かつ時代や業界の要求してくるAPIについても大きなタイムラグなく実装してくれていたので、開発者としてもユーザーとしても期待が持てるブラウザだった。実家の父親がパソコンを買った時に「新しくGoogle Chromeをインストールして....」とヘルプしなくても済むし、サポートしがいのある(ナーバスな気持ちにならずに積極的にコードを書いていいと思える)ブラウザだと思っていた。

しかしながら、MicrosoftWindows Phoneを止め、Android向けに出したEdgeはBlinkベースになり、Microsoft自体はナデラがOpenを謳う一方でconsumer clientへの会社としてやる気の薄さとAzure/Officeの稼ぎぶりが目立つようになっていったあたりから次第にEdgeHTMLやChakraCoreも新機能開発してる話も細々とアナウンスされるようになったり、かつてはやる気に満ち溢れていたリファクタリングの話も聞こえなくなり、Chromiumが押しに押しているWeb Componentもなかなか実装されないなあと感じるようになった。

正直、FirefoxOSに全賭けして大敗北した間にAndrod版の投資をサボりにサボった結果の果てにモバイルにおける影響力をほぼ全て失ったMozillaよりも先にWebブラウザを諦めても不思議じゃないなと思うようになっていた。それでも、WebViewをGUI環境のAPIとしてWindowsのいろんなところで使っていたり、WebベースのOffice製品やVisual Studio関連製品を出しており、収益も株価も決して不調とは言えないMicrosoftが今更ブラウザエンジンを止めるとも思えず、杞憂に終わるのではないかと思っていた。そんなことを1年ほどぼんやりと心の片隅に置いていたところで、MicrosoftがEdgeHTMLが止めるかもしれないという噂が報道され、そして数日後にそれは現実のものとなった。

止める理由は漠然とわかる。Web標準への準拠ならびに市場最大シェアとなっていたChromiumとの互換性を強く押し出し、Windows 10のデフォルトブラウザとしての地位を使い、あの手この手でユーザーに使わせようとしていた割にはシェアが零細のまま伸び悩んでいたし、スマートフォンタブレットの普及の進展に伴い、モバイルで大敗北したWindowsはインターネット接続デバイスの総数における影響度は落ち始めていた。それにいくらスマートフォン市場で全く勝てなかったにしてもモバイルOSは時代の華でであり、Holo LensなどのXRの橋頭堡としての利用価値だってあったはずのWindows PhoneのGUIシェルのチームすらも解散させるような前科のある、今のMicrosoftがPersonal Computingをいつ諦めても不思議ではないという雰囲気は1ユーザーからも見てわかるように漂っていた。それ故に驚きはなく、ただただ切なさだけがこみ上げてくるだけだ。ましてや実装のdiversityの懸念がPrestoの脱落以後ずっと気にされていた中で、KHTML出身なWebKitやBlinkとは由来が全く違うTrident由来の、最も実装のdiversityにとって重要なエンジンの一つと目されていたものの脱落だけに、なおさらに切ない。今後、ORTCのようなcounter proposalが出る機会も失われるのだろうと思うと、単純に実装の一角が落ちたという以上の悲しみがある。

Mozillaの公式声明について

この件についてMozillaがChris Beard名義で以下のような内容を出している.

Goodbye, EdgeHTML - The Mozilla Blog

自分はこの声明の語調の強さには少々顔をしかめたくなるのだけれど、独立系ベンダーとしてのOperaも今は無く、WebKitも(胴元のAppleの方針も相まって)プロジェクトとして何か言うことは無いだろうし、Googleは当然何もいうはずがない。今や Google ChromeのVPであるGoogleのDarin Fisherは、彼には彼のポジショントークがあるにしても、こんな感じのことを言ってしまう状況だからね...... そのような状況を踏まえると、もはやMozillaしか言えないからMozillaが語調を荒げていうことに対しては一定の理解はできる。

だけど、この状況において(ある意味ではチャンスとも言える状況において)、FirefoxOSの失敗と同時にFirefox Androidに投資を殆どしていなかった結果、ことモバイルにおいてはMozillaは影響力が皆無に等しい状態にある。また、Web Engineのコアバリューの一つである速度の面で、総合的な結果として他二つに後塵を拝することになっているGeckoがユーザーに選んでもらえるかというと、短期的には厳しいんじゃないか。一番速い必要はなくとも、競合と比較した場合に遅いというのは選び続ける理由としては厳しいものがある。

そして、MicrosoftChromium Projectに(どの程度かは知らないけれども)参加する意向を示している以上は、少なくともWindows向けのIntegrationくらいはやるだろう。そうなるとMozillaは最後にして唯一のプロプラOSベンダーからの支援が名実ともに一切無いProjectになるわけで、モバイルやWeb全盛の中では開発チームを動態保存するだけでも精一杯とまで揶揄されるようなデスクトップOS向けアプリケーション開発としては長期的には競合比で不利になるのではないかと懸念する。

なので、頑張っては欲しい。自分はcontributorとしては退いてしまった身なのであんまり偉そうなことは言えないけども......

WebKitについて

Mozillaについて書いたので、一応こちらにも触れておく。

従来、WebKitについては結構心情としては複雑。彼らのsoftware engineeringにおけるuser first主義とそのためのperformance first主義、そして「Web BrowserはOSの1コンポーネントであるが、同時に1コンポーネントに過ぎない」と言わんばかりの姿勢には大変なる尊敬を自分は抱いている。だいたい2015年ぐらいからは標準化されている範囲に関しても、きっちりと作り込んで来ている印象もある。

一方で、specに書かれていないことの合間を縫っていると思しき最適化の結果や、モバイル環境向けにメモリリソースなどを削りこんでいると思しき最適化、彼らのprivacy重視の実装の結果として発生するバグについてはwebdevとしては本当に厄介。しかし標準化されていない・解釈次第ともいうべき箇所であったりするので強く文句も言えないし、標準化されていない範囲に関してはそれぞれの実装方針は自由であるというのがWebの多様性を支えている重要な点でもある。地道に「せめてデバッグしやすくしてくれ」「せめて動かない理由がわかるようにしてくれ」とbugzillaに要望を書いたりしていくとかするしかないんだろう。面倒だけど仕方ないね。

めんどくさい奴だけど、現状においてはそのシェアの問題とスタンスの違いから、WebUSB/WebBluetoothのように、WebのOS化の夢を諦めていないGoogle Chromeに対する非常に重要なカウンターパートなので、Webのdiversityの観点からは非常に重要なキーファクターであり、当面は応援したいところ。

まさか2012年にWebKitに市場が席巻されるのを心配していた頃に、6年後の2018年の今ではApple WebKitがWebの相互運用性のための一角を担うことになるとは誰が思っていただろうか。

Microsoftについて

あんまりWeb関係ない感想なんだけど。

2014~2015年ごろのMicrosoft Buildの様子とは大きく変わって、最近のMicrosoftは次々とconsumer client deviceについては少なくともWindowsとしては放置を決め込んでいるように見える。Windows Phoneはシェルの開発チームごと消えてしまったし、捨てるに捨てられない戦略物資だったはずのWeb Engineは今回の件で捨ててしまったし、相変わらずHolo Lensは生きているのか死んでいるのかわからないし...... CortanaもAlexaとコラボするみたいな話があったし、Windows 10 on ARMもやる気あるのかないのかわからないし、Surfaceはハードウェアの話でSotfwareとしてのWindowsそこまで関係ないし。AzureがあるからNTカーネル自体は維持するんだろうけれども、GUIシェルよりも上に関してはもうやる気がないのかな....と邪推してしまう。

ちょっと脱線した。

webdevとして

webdevとしては「Edgeを冠するWindowsのデフォルトブラウザのリリース間隔が短くなる(かも)」という点に関してのみは嬉しい。非常に惜しむらくはそれがEdgeHTMLによって為されなかった点。半年に一度のWindows 10のUpgradeに紐付く上に、そのUpgrade対象ポリシーが非常にややこしいために「だいたい最新のEdgeが使われていると信じる」ような推定からは脱出できるからだ。しかし、それがEdgeHTMLによって為されていないという点が悔やまれる。

率直に言って、現在のWebクライアントサイドの開発においてEdgeHTMLの対応に苦心するというのは例外を除いてそんなにない。彼らがChromium互換を狙っていたというのもあるけれども、後述する例外を除いて苦心するようなケースは、経験上、そもそもの問題として何かしらcross browserで動くことを一切想定していないような事例が多い。

故に、EdgeHTMLが消失したことに対してシャンパン祝賀会を開こうと主張している、どこぞのカメラアイコンの人は、少なくとも直近数年においてはEdgeHTMLのサポートを含むまともなWebクライアントサイドの開発をしていなかったか、そもそもまともなコードを書いたことがないか、後述する例外ケースばかり泥臭くやっていて心を病んでいたんじゃないかな?と思った。そういえばiOS黎明期に一発当てて以後の最近はもっぱらUI/UXコンサルタントでしたっけ? 私は彼を詳しく存じあげていないのでよくわかりませんが。Web周りは専門外なんじゃないかな? 間違ってたらごめんなさいね。

一応例外はある。それはWebRTCであったり動画配信のように、OSやベンダーとブラウザの組み合わせごとに、バックエンドの実装が変わってしまう場合。自分はWebRTCについては詳しくはないけれども、少なくとも動画周りに関してはDRMデコーダバックエンド、再生しているフォーマットやコンテンツファイルの相性というものがいまだに残っている。しかし、ライトに使っている限りは顕在化しないし、ヘビーに使っている場合はEdgeHTMLとWindowsの組み合わせに限った問題ではなく、どのWebブラウザでも大なり小なり抱えている問題で、それを踏み抜くかは運の問題でしかない。何度も繰り返すが、相互運用が価値のプラットフォームにおいてその問題は日常茶飯事であり、瞬間的には怒りこそすれども、そのようなプラットフォーム向けにコードを書いている以上は仕方ないのないことで、統合という方向性よりもそのようなバグが修正させる方向に向かった方が健全ではある。非常に面倒くさいけどね。。。。

EdgeブランドがChromiumに移行した結果であっても、おそらく類似の問題は発生するだろう。Microsoftが今後ChromiumベースのEdgeをリリースする時に、もしEdge向けのバックエンドがWindowsとの強固なintegrationを志向して、Google Chromeとは別の実装である場合、もしかするとEdgeHTMLの時以上にブラウザのバグ回避が困難な可能性だってありえる。故に、相互運用性だのWebの価値だのと言った問題をすべて外した上でも、一概にChromiumに移行することがwebdevにとって幸せだとは言えないと自分は思っているし、むしろGoogle Chromeに真の意味で一本化されていない以上、実装のモノカルチャーが中途半端に進展して中途半端な差異が生まれるだけで不幸でさえあるかもしれない。

長くなってきた

もう少し色々ダラダラ書きたかったんだけど、いったん切ることにする。

EdgeHTMLを諦めるというMicrosoftの判断は残念ではならない。非常に悲しい。

CSSのcanvasとviewportとposition:fixedとpinch zoom

  • specを元にした概念の整理
  • 間違いあったら教えて欲しい

CSS 2.1におけるviewport

CSS 2.1におけるviewportを説明するにあたり、以下のterminologyが必要となる

canvas

For instance, user agents rendering to a screen generally impose a minimum width and choose an initial width based on the dimensions of the viewport.

viewport

User agents for continuous media generally offer users a viewport (a window or other viewing area on the screen) through which users consult a document.

When the viewport is smaller than the area of the canvas on which the document is rendered, the user agent should offer a scrolling mechanism.

initial containing block

The containing block in which the root element lives is a rectangle called the initial containing block. For continuous media, it has the dimensions of the viewport and is anchored at the canvas origin; it is the page area for paged media. The 'direction' property of the initial containing block is the same as for the root element.

  • ブラウザ(=screen mediaのUA)のスクロールバーを伴った描画領域 = viewport
  • レイアウトはcanvasに描画される
  • initial containing block(デフォルトのcanvas領域)のサイズはviewportの幅と高さに依存する.

viewportが1000pxの場合にhtml要素のwidthを100000pxにしたらどうなるのか?については、overflowプロパティによって定義されていて、

UAs must apply the 'overflow' property set on the root element to the viewport. When the root element is an HTML "HTML" element or an XHTML "html" element, and that element has an HTML "BODY" element or an XHTML "body" element as a child, user agents must instead apply the 'overflow' property from the first such child element to the viewport, if the value on the root element is 'visible'. The 'visible' value when used for the viewport must be interpreted as 'auto'. The element from which the value is propagated must have a used value for 'overflow' of 'visible'.

のように,

  • root要素のoverflowプロパティがviewportに対しても適用される
  • viewportのoverflowプロパティがvisibleである場合、autoとして解釈されるので、スクロール機能の有無はUA依存となる

position:fixedの挙動

In the case of handheld, projection, screen, tty, and tv media types, the box is fixed with respect to the viewport and does not move when scrolled.

For other elements, if the element's position is 'relative' or 'static', the containing block is formed by the content edge of the nearest ancestor box that is a block container or which establishes a formatting context.

要は、対象のボックスがviewportに対しての固定座標に配置されるということ.

CSS Device Adoptationでの拡張

CSS Device Adaptationでは、meta[name="viewport"]でのviewportサイズの指定を可能にしたiOS Safariなどの実装を追認を行う形で仕様が策定されており、ここでは、initial viewportとacutual viewportの二種類が定義されることになった。

initial viewport

This refers to the viewport before any UA or author styles have overridden the viewport given by the window or viewing area of the UA. Note that the initial viewport size will change with the size of the window or viewing area.

actual viewport

This is the viewport you get after the cascaded viewport descriptors, and the following constraining procedure have been applied.

たとえば、meta[name="viewport"][content="width=device-width"]を指定した場合は以下のようになる

  1. initial viewportがブラウザのウィンドウサイズや描画領域に基づき決定される
  2. UAスタイルおよびユーザースタイルによるmeta[name="viewport"]@viewportの計算が行われる

    1. meta要素によるwidth=device-width@viewport { width: 100vw; }として取り扱われる
    2. ここでの100vw1で計算されたものが取り扱われる
    3. width: 100vw;max-widthmin-widthマップされる
    4. 3でマップされた結果を元に、actual viewportの幅を計算する
  3. 計算の結果、deviceの幅=ウィンドウの幅を自身の幅としたactual viewportが算出される

  4. actual viewportを元にinitial containg blockを再度計算する

ここでもUAスタイルがあるために、仮にviewportの指定が行われていない場合は、従来のサイトとの互換性のために、モバイルブラウザでは自動的にviewportの横幅が1000px前後に設定されたりするようになる。

デスクトップブラウザについては@viewportUAスタイルを持たないと解釈すればいいのだろうが、dbaronによるissue表記があるので、完全に定義が確定しているわけではないのだろう。

では、メディアクエリはどこで計算されるのか? それは以下のように定義されている:

  1. Cascade all @viewport rules using the initial viewport size for values and evaluations which rely on viewport size
  2. Compute the actual viewport from the cascaded viewport descriptors
  3. Cascade all other rules using the actual viewport size

つまり、viewportの計算が全て終わらないとmedia queryをはじめとした計算は起こらない

zoom

ズームについては、まず、CSSOM Viewにて二種類のズームがあることが述べられている:

There are two kinds of zoom, page zoom which affects the size of the initial viewport, and pinch zoom which acts like a magnifying glass and does not affect the initial viewport or actual viewport.

この詳細についてはCSSOM View側では「CSS Device Adaptationを参照」とあるのだが、CSS Device Adaptationには明確なterminologyが設定されているわけではなく、断片的に文章中に記述されているだけである。

When the actual viewport cannot fit inside the window or viewing area, either because the actual viewport is larger than the initial viewport or the zoom factor causes only parts of the actual viewport to be visible, the UA should offer a scrolling or panning mechanism.

This is a magnifying glass type of zoom. Interactively changing the zoom factor from the initial zoom factor does not affect the size of the initial or the actual viewport.

position:fixedとpinch zoomを組み合わせるとどうなるのか

今までに述べた内容をまとめると、

  • viewportはブラウザ(=screen mediaのUA)のスクロールバーを伴った描画領域
  • position: fixedは、対象のボックスをviewportに対する固定座標に配置する
  • pinch zoomはactual viewportに影響を与えない

となるのだが、pinch zoomをした場合に、Android BrowserやiOS Safariでは常にUAの描画領域の枠の固定位置にposition: fixedが配置されるのは、仕様上正確なのかどうか分かり難い…… pinch zoomではviewportに影響を与えないとあるが、zoomをしている以上は、UAとして表示している領域としてのviewportの横幅は相対比で小さくなっているはず。ちなみにW3C Bugzillaでは特に何も見つからず……

pinch zoomとpostion: fixedへの各UAの対応

IE11 Mobileや最近のChromiumでは、この問題に対して、「pinch zoomを行っても、position: fixedを常にUAとして表示している領域に配置しないようにした」(私の知りうる限り、Firefox修正を検討中である)

どのような変更を行ったのかはChromiumの変更を開設したスライドがわかりやすい。

このスライドでは、viewportとされるものは正確には、

  • visual viewport: UAとして表示している領域という意味でのviewport
  • layout viewport: レイアウト計算に用いられるviewport

の二種類が存在するとしている(正確には、この二種類が存在しているとすることで、問題を解決することに成功した)。

pinch zoomの存在しなかった時代(CSS 2.1での定義)では両者は統一して扱われるものであったのだが、モバイルブラウザ(およびUIとしてのpinch zoom)の登場により、分割して扱う必要が出てきた。しかし、現行のCSS Device Adaptationではそこにまで踏み込んだ定義がされていないために話がややこしくなっている。いや、この一件に限らず、CSS Device Adaptationは出来がいいspecとは思えないですけどね……

ちなみにWindow.scrollX/Ywindow.innerWidth/Heightなどのviewportのスクロールに絡む座標は、Chrome Beta for Android 40.0.2214.69で確認したところ、visual viewportを基準に算出されるようになる。まあそうするしかないですよねcompat的にも。なので、

The innerWidth attribute must return the viewport width including the size of a rendered scroll bar (if any), or zero if there is no viewport.

The scrollX attribute attribute must return the x-coordinate, relative to the initial containing block origin, of the left of the viewport, or zero if there is no viewport.

以上の定義中にある"viewport"は, visual viewport。ややこしい。

まとめ

図にするとこんな感じ。仕様+実装を元に、viewportというものは、このような概念であると解釈が出来る。

尚、visual viewportとlayout viewportという単語は、Chromiumのスライドのやつが都合がいいから使ってるだけね。

   +---------------------+----------+
   |                     |          |
   |  *--------*         |          |
   |  |        |         |          |
   |  |  visual viewport |          |
   |  |        |         |          |
   |  |        |         |          |
   |  |        |         |          |
   |  *--------*         |          |
   |                     |          |
   |                     |          |
   |   layout viewport   |          |
   |                     |          |
   |                     |          |
   +---------------------+          |
   |                                |
   |       canvas                   |
   |                                |
   +--------------------------------+

  • directionがltrの場合, 左上からactual viewportが始まる
  • layout viewport = actual viewport
  • layout viewportはinitial contaning blockの大きさを決める
  • visual viewportはpinch zoomなどにより虫眼鏡のようにサイズが 可変したり、スクロールしたりする
    • overflow:hiddenがviewportに適用される場合はinitial containg blockを超えて動けない
    • Window.scrollX/Y, Window.innerWidth/Heightなどのスクロール関係値は、visual viewportを元に算出されていると解釈できる
  • IE11 Mobileや最近のChromiumを除く従来の実装では、position:fixedはvisual viewportを元にレイアウトされていると考えられる

ServoのDOMバインディングの話

Servoのリポジトリ内にDOMバインディングのデザイン覚え書きを投入したので、現時点での設計について、そろそろ日本語で説明しておく(英語版は結構前からServoのWikiに書いています)。

この記事の中で使うSpiderMonkey API用語は、結構古いものだったりするので、そこに注意(ServoはFirefox 17とかその辺りのSpiderMonkeyを使ってる)。

ブラウザにおけるDOMバインディングとは

この分野については、エデンの園でおきたこと - steps to phantasienという名記事があるので参照されたし。WebKit Chromium port時代のBlinkの話だけど、だいたいどのブラウザでも似たような問題を抱えていて、それぞれ微妙に異なるアプローチで解決している。

改めて私の言葉で要約してみると、「現代の実用的なブラウザエンジンというものは、自身の保有するDOM構造をJavaScriptなどの言語から操作可能にしている。使う側からすれば便利極まりない仕組みだけれど、ブラウザエンジン側にしてみると結構悩みの種。なぜなら、ブラウザエンジンは一般的にC/C++のような自前で参照関係を解決する種類のメモリ管理を必要とする世界であるのに対して、その構造を操作することになるスクリプト言語はだいたいエンジンが専用GC機構を持ち、その機構によってメモリの生存期間が決まるような言語仕様になっているから、これを巧くつなぐ仕組みが必要になる」ということ。

(関係ないけど、モダンなブラウザエンジンのDOMバインディングについて日本語で情報を求めると、だいたいomoさんが、その昔に書いた物にぶち当たる。本当にお世話になっています)

さて、こうした面倒くさい問題に対して、各ブラウザエンジンは幾つかのアプローチで対処している。OSSな実装で言うと:

といった感じ。

IEは、Exploring IE9's Enhanced DOM Capabilities - IEBlog - Site Home - MSDN Blogsによると、IE8まではCOMベースのバインディングを使用していたものの、IE9Chakraと密にバインディングするようにしたらしい。とにかくアーキテクチャ的にもIE9以降は違う感じはわかる。

Servo のアプローチ

上に挙げたアプローチはどれも巧いこと回っているものの、どうせならJSエンジン側のGCで全部を管理するようにしてしまいたい。Cycle Collectorを再実装したくはないし、余計な謎マクロとか色々実装したくはない。

というわけで、本題。「ServoのDOMバインディングは、どのようにオブジェクトの生存に関わる問題を解決しているのか」。答えは単純で「SpiderMonkey GCに全てを任せている」。もちろん世代別GCにも対応済。やったね!

Servoのアプローチその昔: dom.js

先述のエデンの園でおきたことでは、ServoはSpiderMonkey GCにDOMの生存管理を任せる為に、dom.jsを使うらしいと2012年末当時の観測結果として書いてある。が、現在はこの方法を使っている訳ではありません。

とりあえずdom.js路線について自分の知っている範囲の説明をする。実は、この時代は自分も知らない。今よりも、もっと実験実験してたプロジェクトの頃だし、自分もコミットチャンスを見つけてやり始める前。具体的に実装が動いていたのかもわからない。が、どちらにせよ、2012年末頃の時点では、dom.jsでDOMのデータ構造そのものをJavaScriptでself-hostしようとしていた、らしい。

原理的には一番オーソドックスにSpiderMonkey GCの恩恵を受けられる訳だし、self-hostでおなじみの効果として、DOMデータ構造そのものに対してJITが立ち入れるので、理論上はJITコンパイラによる更なる最適化のチャンスが増える。現代のフロントエンドWebプログラミングの最大の速度障壁はDOMの遅さであるので、そこの速度向上がワンチャンなので、一見すると、筋のよいアプローチに見える。

が、実際には、この試みは断念される。過去のmeeting noteを色々参照したところ、理由は単純にパフォーマンス + 複雑性の問題の様子。JS側と(文脈的にレイアウト用の)DOMの2つのレイヤーツリーを持って、常に同期し続けなければならない。ましてや並列性を狙ったエンジンなので、コードが本当に複雑になるのは想像に難くない。

具体的に何のパフォーマンスが悪かったのかは追跡できなかったのだけど、私の推論としては、おそらくは2つのDOMツリーを作る過程で発生するメモリコピーが要因ではないかと感じる。Webページを1つパースすると、だいたい数百〜数千の単位でDOMの領域のデータ構造がつくられる。これがJSオブジェクトで作成され、しかも同様のツリー構造をもう一回作るとなると、かかるコストは結構なものになるのではないかと。

何はともあれ、この路線は挫折することになる。

SpiderMonkey APIを通してDOMを管理する

というわけで、現在の路線はSpiderMonkeyAPIに従って、ラッパーオブジェクトの生死に合わせて、RustなDOMオブジェクトの生存を管理する方法を取っている。

全体のデザインはjdmによるもので、ここに書くものはコードから読み解いて、本人に直接確認した内容の覚え書きみたいなもんです。

基本的なライフサイクルについて日本語で書くと以下の通り(英語で書いたのを殆どそのままです):

前提: RustのDOMの構造

Rustには、別の構造体のデータ構造を継承した構造体をビルトインで定義する方法が無いので、Cでクラスなデータ構造をやるような手法が用いられている。また、後述するGCからのトレースの為、GC管理対象のDOMオブジェクトをJS<T>という型のメンバとして保持している:

// BarがFooを継承している場合の構造体の図
struct Bar {
    foo: Foo, // 先頭に基底クラスを格納する
    field1: JS<Hoge>, // SpiderMonkey GCの管理対象
    field2: int, // SpidrMonkey GCの管理対象でないもの
}

こうした構造体全てに対して、WebIDLから継承関係に応じたTraitを実装することで、どの型からどの型に対してのキャストが可能なのかを属性として付与している。詳しくは dom::bindings::codegen::InheritTypes.rsを見てください。

DOMの生成時

ServoがRustのオブジェクトを生成するとき、bindingコード(CodegenRust.pyによってWebIDLから生成されたglueコード)は、ラッパーオブジェクトとなるJSObjectを生成し、これに対するポインタを自身のReflectorフィールドに格納する。このラッパーオブジェクトはもちろんDOMと完全に一対一になるように生成され(でないとJSの比較演算子が変なことになる)、生成と同時に、対応するDOMオブジェクトへのポインタを自分の中に格納する。これで対応関係の出来上がり。

SpiderMonkey GCの管理下に入るDOMオブジェクトは全てが最基底な位置にReflectorというフィールドを保持しており、ここがラッパーオブジェクトとの対応関係を作っているわけですね。

ちなみに、このようなライフサイクルを作る都合上、Servoでは全てのDOMオブジェクトの生成時に、対応するラッパーオブジェクトの全てを同時に生成している。GeckoやBlinkの場合は、JS側からDOMにアクセスしたタイミングでラッパーオブジェクトを作っているが、それらのアプローチと比べるとServoの方が初期段階ではメモリを使っているはず。と、同時に、Servoのアプローチの方が世代別GCの影響が色濃く出るだろう。ページロードの瞬間からすべてのDOMに対応するJSオブジェクトが生成されているのだから。

DOMのオブジェクトグラフのトレース

このように生成したDOMのオブジェクトグラフ上をSpiderMonkey GCが歩けるようにするために、Rustの言語機能をかなりトリッキーに使ってる。

  1. SpiderMonky GCはマーキングフェーズ時に、それぞれのbindingコード内に定義された各ラッパーオブジェクトの定義元のJSClass.trace()を呼び出す。
  2. JSClass.trace()は、InhertTypes.rsの定義に従って、Foo::trace()を呼び出す。
  3. Foo::trace()は今度はFoo::encode()を呼び出す。このFoo::encode()#[deriving(Encodable)]によって、コンパイラ自動生成されたもの。現在の設計ではDOMをEncodableとして扱う目的が無い + 下手なマクロよりも確実に展開されるという前提に立っているのでこのアプローチを採用している。
  4. Foo::encode()は、メンバとして格納しているJS<T>JS<T>::encode()を呼び出す。
  5. JS<T>::encode()からdom::bindings::trace::trace_reflector()を呼び出す。
  6. trace_reflector()は、対応するラッパーオブジェクトをJSTracerに伝える = GCに伝える。
  7. 1~6までの操作を、オブジェクトグラフの終点まで延々と繰り返す
  8. 最後に、Rustのオブジェクトグラフを全部通り抜けた結果、どのオブジェクトが死ぬべきかがわかるというわけ

DOMの破棄

こうしてトレースした結果から、ライフサイクルを終えたと判別されたDOMは破棄される必要が有る。

この破棄ロジックは非常に簡単でラッパーオブジェクトの破棄タイミングに、JSClass.finalize()が呼び出され、そこからbindingコード内のFooBinding::_finalize()が呼びだされる。このメソッド内では、ラッパーオブジェクト側に格納しているRustオブジェクトのポインタが、Rustのowning pointerにキャストされ、空の変数に代入される。Rustの流儀に則り、ここでライフタイムが終わる = ポインタが指し示すオブジェクトも安全にデストラクトされるというわけ。

SpiderMonkey GCのExact GC Rootingへの対応

ここまでは基本的な動きについて話してきた。この他に、SpiderMonkeyは世代別GCを実装するにあたり、Exact (precise) GC方式に移行しているので、それへの対応が必要になっている。これに辺り、Servoでは、4種類のJS Managedな型を導入している

  • JS<T>は、前段でも述べた通り、RustなDOMオブジェクト内に保持されるDOM型のメンバを表す型。内部的には、格納対象のRustオブジェクトへの生ポインタを保持しているだけだったりする。GCによって再帰的にトレースされる対象。
  • Temporary<T>は、DOM型を返す必要があるメソッドの返り値として用いられる(典型的なのはDocument::getElementById()とか)。これは内部に対応するラッパーオブジェクトへのポインタを含んでいるので、SpiderMonkey GCのConservative Stack Scannerによってスキャンされる対象になり、結果、この型の値が生きている間、GCのrootとなる。ただし、SpiderMonkey GCはRootの選定にあたりLIFOの順番を保証する必要が有るのだけれど、メソッド間で値が移動するため、その順序が崩れてしまう場合がある。そのため、Root<T>という後述の型を導入することで、うまくそこを回避(保証)することにしている
  • Root<T>は、Temporary<T>同様にラッパーオブジェクトへのポインタを保持している。これも同様にConservative Stack ScannerによってスキャンされることでGC Rootとなるのだけれども、先述のLIFOの順番を守るようになっている。トリックとしては、RootCollectionというリストを用意して、Root<T>の生成時にこれに登録していく。スコープを抜ける際に、生成されたRoot<T>LIFOで破棄されるはずなので、破棄タイミングでアサーションを仕込んでRootCollectionからも取り出すようにして、LIFOな順番を保証するようにしている(ただ、Rustの言語挙動にかなり依存している面は否めない……)
  • JSRef<T>は、Root<T>に対する単純な参照。なんども無駄にRoot<T>を生成したくはないしね!

とまあ、ざっとこんな感じになる。 こうやって、ServoはSpiderMonkey GCにDOMの生存管理を丸っと任せているというわけ。

Servo DOM bindingの今後

第1に、SpiderMonkeyを現在使用しているGecko 17相当から、Gecko 3x相当までアップグレードする必要が有る。性能試験という意味でも、先述した世代別GCの効果測定の意味でも、流石に古いままではテスト対象として微妙だからだ。

第2に、SpiderMonkeyに対してGC Rootを伝える方法の改善。今のようにStack Scannerが辿るよりも、明確にどれがRootであるのかを示すようにした方が良いのは言うまでもない。

第3に、JSObjectの中にRustオブジェクトを格納する。たしか、一発でアロケーションできるようにしたいとかそんな感じだったはず。しばらく全然進捗ないし、自分も昔一回見たっきりなので、効用までは詳しく追ってません……

第4に、DOM APIの実装。そもそも実装してるAPIがまだまだ足りないので、全然ベンチマークも取れないという問題が有るので、ゴリゴリ実装して行かなければ。

だいたいこんな感じ。こうやって構築したDOMツリーに対して、レイアウト側からどのようにさわっているかについては、そのうち書ければ書きたい。

プライベートブラウジング時に「最近閉じたタブを復元する」機能が無いのはダメ

昨日のNightly 29でプライベートウィンドウで"Undo close tab"が使えなくなってるなーと思ったら、bug 956826 が原因っぽい。というわけでコメントしたんだけど、備忘録代わりに日本語でも書こうかなと。Twitterだとかなり文章長くなるし。

この挙動の変更について、「そうは言ってもChromiumはやってないしなあ」とあるけど、ここについてはChromiumが明らかに間違っている(というよりも、オプション画面やタブの挙動など、1.0未満のベータ時代に骨子が決まったと思われるChromiumの挙動は全体的にコンセプトワークであって、ほかのソフトウェアが安易に従うには筋が悪い、参考にするには警戒すべき)。

人間が間違えてタブを閉じてしまった場合に復元する手段が無いのは、undoの無いテキストエディタのようなもので、基本的にはコンピュータのソフトウェアとしてゴミだ。ましてFirefoxは今までは、このヒューマンエラーに対する網をプライベートモード問わず張っていた訳で、これは明瞭なregressionとなりうる。

「TorBrowserユーザーなどはこの変更を望まないだろう」とあるけれども、個人的にはTorBrowserのような徹底した匿名化を望む人間は全体に対して少数であると認識している。そのようなユーザに対しての挙動を万人に提供すべきではなく、そうした匿名化を望む人に対してはオプションで機能を提供すべきだ。

というわけで、反対・賛成問わず、意見がある方がいらっしゃいましたら、Bugzillaの該当バグまでどうぞ。英語といっても、Google翻訳駆使すれば割となんとかなるよ。

Servoの夢

Rustについて、自分としては流行ってくれなくてもいいというか、別に流行ること自体は本当にどうでもよくて、言語自体の完成とServoが形になってくれさえすればそれでいいと思っている。Rust Samuraiなんてイベントやってるけれども、個人的に普及させる気がないと常々言っているのはこれが理由。

もちろん流行ることによるメリットはあって、処理系自体の改良に携わる人数の増加や、有為なツール群が出てきやすくなる土台ができるのでコミュニティにとっては重要なのだけど、個人的には二の次で、自分の第一目標はServoなんだな。

Mozillaという名前を聞いただけで抵抗感を感じる人がいるのは知っているし(それこそ泥舟のように避ける人も知ってる)、今も昔もMozillaの動きに「胡散臭さ」が伴うのは周知の通りだし、本当にハンドリングが下手だなと思うときもあるけれども、それでも、出してくる技術はイカれてて面白いし、やっぱりOSSだから進捗がしょっちゅう公開されるというのは楽しい。

Rust langが普及するか?と聞かれたら、普及の定義次第なところもあるけれども、個人的には難しいと思う。ずっとC++の首を狙うDもいるし(当然固定客がいる)、そもそもC++書ける人はそれでいいと思ってることもあるし(ここらへんaltJSとESerの関係に似てる)、最近だとUNIX+C+Googleという化物のようなブランド力を誇るGolang様もいるし(おまけにGoは地味に周辺が整ってる)。C++/Rust/Dとは用途が微妙に違うとはいっても、LLに比べればGoの速度で十分だろうし、たいていの用途はGolangでいいんじゃないのかな。自分もgo覚えたいし。

じゃあ、Rustのどこに価値があるのかというと、それはServoなんじゃないのかな。というよりも、実プロダクトとしてのプログラミング言語はビジョンを具現化する道具にすぎなくて、その道具が実用に足ることを証明できなければ意味がない=その言語でビジョンを実現できなければ意味がない。研究発表と実プロダクトの境界線はここだと思う。学術価値のある新規性なんてRustが体得する必要性はない。

かつて「Servoはブラウザエンジン界のPlan9」と揶揄したことがあったけれども、もしかしたら本当にそうなるかもしれない。過去20年に及ぶ歴史の積み重ねはどうにも重たく、また、Servoが成そうとする並列化の夢も、既存の漸進的進化で解決がなされてしまうかもしれない。そもそもHTMLに裏打ちされたWebがどこまで有効であるのかもわからない。

けれども、そこには未だに語られなかったアーキテクチャロマンがある。もはや語られなくなったインターネットとソフトウェアのアーキテクチャへのロマンがある。仮に夢が半ばで潰えたとしても、かならずそこから実を結ぶ何かがあるはずだ。そう信じなければやっていられない。

これがGoogleAppleであれば、大切なことは社内で成果になってから表に出てくるのかもしれないけれども、幸いにしてMozillaは早くから表に出してくれた。坂を駆け上る途上のコードをお披露目してくれた。だからこそServoは楽しい。だからこそブラウザは楽しい。

たとえ見果てぬ夢だとしても、そこに自分の時間を賭けるだけの価値がある。そう信じている。