エメラルドマウンテンの麓。

「デスクトップ版を表示する」にチェックを付けると平然とUAを偽装するAndroid用標準ブラウザの判定をムキになってしてみます

モバイル版 Safari の「デスクトップ用サイトを表示」でも iOS のバージョンを取得できるようになりました。(2018/1/16 追記)

リッチな Web アプリや手厚いフォールバックの実装にあたっては、やはり「ブラウザ判定こそ基礎中の基礎、最近の表現だと一丁目一番地だ」と何年振りかに根を詰めてやってみました。

はじめに

Request desktop site

Android 標準ブラウザ(AOSP, Chrome WebView)の判定には、navigator.userAgent 文字列から "Android"と"Version"の有無を利用するものが知られています。しかしこれらの文字列の有無は必ずしも信頼できるものではありません。

例えば Android 用ブラウザの多くには「デスクトップ版を表示する」といった設定があります。このチェックが付くとユーザーエージェント文字列は大きく変化して OS とブラウザの判定が難しくなります。しかしユーザーエージェント文字列が変わったからと言って、各モバイルブラウザに固有の挙動(バグを含む)が PC 用ブラウザのソレへと変化することはありません。よってユーザーエージェント文字列に惑わされず正しく環境を判定する必要があります。

Request mobile site

さらにユーザーエージェント文字列の変化を利用してユーザーの「デスクトップ版的なものを表示して欲しい」という意図を検出することも可能です。この情報を元にページの表現を切り替えればよりユーザーにフレンド、エクスペリエンスあげあげな HTML コンテンツを提供できるのではないでしょうか。

いわゆる「おもてなし」、最近では「忖度」。それでは張り切っていきましょう。

各環境とブラウザの判定方法

OS+ブラウザPC版を表示OS判定OSバージョンエンジン判定ブラウザバージョン
Android4.3- AOSPAndroidvVersion/OSバージョンと同じ
v矛盾文字列(*1)△不正確(*2)
Android4.4+ Chrome WebViewAndroidv(*3)OSバージョンと同じ
v矛盾文字列(*1)△不正確(*2)
Android FirefoxAndroidv'MozAppearance''rv:'
v矛盾文字列(*1)不明
Android ChromeAndroidvwindow.chrome'Chrome/'
v矛盾文字列(*1)不明
Android Prsto Operaplatform=Androidv(ua)window.operagetVersion()
vplatform=Android不明
Android Blink Operaplatform=Androidv(ua)window.opr'OPR/'
v矛盾文字列(*1)不明
  1. 次の「矛盾するプラットフォーム文字列を使ったAndroidシステムの判定」を参照
  2. 「Android 標準ブラウザの Android バージョン判定」を参照
  3. UA を AOSP に偽装して出荷している端末があるため document.registerElement を確認する

矛盾するプラットフォーム文字列を使ったAndroidシステムの判定

次の Android 用ブラウザでは「デスクトップ版を表示する」場合にユーザーエージェント文字列等から Android 文字列が消えます。

  1. Android 標準ブラウザ AOSP, Chrome WebView
  2. Android 用 Chrome
  3. Android 用 Blink Opera

この際には、プラットフォーム文字列に "Linux armv7l" または "Linux i686" が入っている一方で、ユーザーエージェント文字列に "Linux x86_64" という矛盾した値が入っている点を利用して Android を判定します。

maybeAndroid = ( navigator.platform === 'Linux armv7l' || navigator.platform === 'Linux i686' ) && navigator.userAgent.indexOf( 'Linux x86_64' ) !== -1;

また Android バージョンの取得ができなくなります。Android にバンドルされている標準ブラウザに限っては、実装状況をヒントに Android バージョンを推測することはできます。

Ubuntu Mobile や Firefox OS などが似たようなことをしていないか?などは未検証です。

Android 標準ブラウザの Android バージョン判定

さて、ユーザーエージェント文字列等に "Android" がなくとも、Android と標準ブラウザの組み合わせであることが分かりました。続いて Android バージョンを判定していきます。

Android 4.0 以降ではユーザーエージェント文字列の WebKit バージョンが当てにならなくなるので、プロパティの有無で判定していきます。

バージョン判定法メモ
5.xdocument.registerElement && !document.execCommandexecCommand が居なくなる…
4.4.3+document.registerElement && document.execCommandこれ以降 Chrome Web View
4.4window.Int8Array && !navigator.connectionここまで AOSP
4.2window.Int8Array && !window.searchBoxJavaBridge_4.2 と 4.3 の判定は出来ていない
4.1window.Int8Array && Number.isNaN
4.0window.Int8Array
3.x534 <= verWebKit
2.3531 <= verWebKit && window.HTMLAudioElement
2.2531 <= verWebKit && !window.HTMLAudioElement
2.0529 <= verWebKit2.0 と 2.1 の判定は出来ていない
1.xverWebKit < 529

ところで、Web 開発では「ユーザーエージェント判定よりも機能判定」とはよく言われます。しかし、触るとクラッシュする等で機能判定の使えないケースについてはコード内に有効な機能のカタログを持つしかありません。また Android のバージョンが分かれば端末の大まかなスペックも推定できるのが素晴らしいですね。