🧊

ブラウザかWebViewか、どちらで開かれたのかを判別するには

あると思った?
残念、そんなものはない!

とはいえそれは"厳密に"やる場合の話で、今現在はなんとか・・なんとか・・・。
これを良しとするかはあなたのココロ次第です。

以下、奮闘記をお送りいたします。

20150813: 更新
‘fullscreenEnabled’ in documentってすればもしかして(iOSのみ)
https://github.com/uupaa/UserAgent.js こういうのもあるらしい

20150422: 更新
まさかのiOSのLINEアプリのWebViewのUAが変わったらしく、FacebookとLINEが判別できるように!

20150407: 更新
まさかのiOSTwitterアプリのWebViewのUAが変わったらしく、もうFacebookしか判別できないみたいです・・。

先に結論を書いておくと、現時点でiOSに限ってであればなんとかなります。
ただ、どのアプリのWebViewかを厳密に見たいとか、AndroidのWebViewも含めて判断したいとかなってくると、現時点では"不可能"です。

モチベーション

提供するアプリの挙動を、通常のブラウザかアプリ内WebViewかで変えたかった。
アプリ内WebViewの場合、体験版みたいな感じにするとか、本編はブラウザで!って出すべく。

切り分けたかった環境の例

まあこういうのはだいたいUAとか見ればいけると思ってましたよ、よ。

リクエスト情報から判断するパターン

ユーザーエージェントでみる

お決まりのやーつです。
まずは現状を把握しましょう。

iOS Safari
Mozilla/5.0 (iPhone; CPU iPhone OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B554a Safari/9537.53

iPhoneってついてて、Safariってなってる。

iOS Facebook公式アプリ
Mozilla/5.0 (iPhone; CPU iPhone OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Mobile/11B554a [FBAN/FBIOS;FBAV/15.0.0.16.28;FBBV/4463064;FBDV/iPhone6,1;FBMD/iPhone;FBSN/iPhone OS;FBSV/7.0.4;FBSS/2; FBCR/ソフトバンクモバイル;FBID/phone;FBLC/ja_JP;FBOP/5]

FBAN/FBIOS;FBAVみたいなわかりやすーい文字がある。

iOS Twitter公式アプリ

それが20141009時点ではこうだった。

Mozilla/5.0 (iPhone; CPU iPhone OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Mobile/11B554a Twitter for iPhone

しかし、20150407時点ではこう。

Mozilla/5.0 (iPhone; CPU iPhone OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Mobile/11B554a

見分けつかん・・。

iOS LINE公式アプリ
Mozilla/5.0 (iPhone; CPU iPhone OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Mobile/11B554a

通常のSafariとほぼ同じだが、Safariの文字がない。
おそらく特にUAの設定をしていないWebViewは、全部コレになるのではと予想される。

というわけで、ターゲットを絞ればやれんことは・・ないみたい。

というのは過去の話で、最近これに変わってました。
20150422時点。

Mozilla/5.0 (iPhone; CPU iPhone OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Mobile/11B554a Safari Line/5.1.1
ちなみに、最近うわさのWKWebViewのUA
Mozilla/5.0 (iPod touch; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12A365

Safariってついてないので通常のWebViewと同じとみた。

そもそも
  • 書式が各アプリ間で統一されてるわけでもない
  • WebViewのUAはアプリ側で設定できるもの

そういう意味では、ユーザーエージェントでもって"厳密な"切り分けはできない。
いったん次へ。

リクエストヘッダでみる

こういうの見つけた。

参考:Android, webview user agent vs browser user agent - Stack Overflow

でもコレ信憑性はUA以下ですねー。
いやはや。

レスポンスしてから判断するパターン

リクエスト受けた段階でわからんなら、こうするしかない。
アクセス端末をチェックする仮ページでも踏ませて、そこで判断するなら?のパターン。

漏れてるグローバル変数を探す

ダメ。

Safariで存在した window.** を全部チェックしたんですが、どのWebViewでも全部あった。
chromeならwindow.chromeとかあるのにねぇ。

local/sessionStorageの容量が違うのでは

すげー黒魔術やなーとは思いますが・・。
ストレージに限界まで突っ込んでみて、その最大容量が実は違っててわかるのでは説を聞き、試してみました。

参考:Browser Storage Abuser

まあダメですよねー。
どの環境も等しく5MB以上は通らなかったです。

てかこのサイト、WebViewで見るとレイアウトが崩れてて辛いです。ポップアップの裏にイベント突き抜けたりします。

WebSQLの容量が違うのでは

これはワンチャンある感じに見え・・ダメですね。

Safariだと、一定容量以上の書き込みをしようとすると、その度にダイアログで許可を求められます。
ただWebViewだと、コレがないです。

コレはいける・・!と思いきや、Chromeも、コレ出なかったです。

しかもSafariは許可しようが拒否しようがエラーのコールバックに入るっていうなにこれバグなのなんなのそしてそもそもopenDatabaseのタイミングで5MB超えたら確認されるって見た気がしたけど気のせいやったのかしら

参考:モダンブラウザのストレージ容量と調査方法まとめ - HTML5 Rocks

結論

いちばんいいのはやはり「アプリ側でフラグたてる」ですね。

まあそれができたらこんな悩み持ってないし・・、
とはいえFacebookやらTwitterやら各社にお願いなんてできるわけないので、
現段階でできるのは、やはりユーザーエージェントで頑張る・・かなーと思います。

ちょっと考えてみる

  • 主要なスマホのブラウザのみをサポートしたい
  • いわゆるWebViewやPCには違う対応がしたい

で、これをUAつかって判断できれば良いので・・。

AccessControllByUserAgent

いまのところFacebookTwitterも、Android版ではWebViewって存在しないみたいなので、
厳密にはiOSでだけ対応すれば良さそうです。

ゆえに、

もはやそれはWebViewであると。

var ACCESS = {
  ALLOWED: 1,
  DENIED: 0
};

var accessible = (function() {

  var ua = navigator.userAgent.toLowerCase();

  var isIOS = /ip(hone|od|ad)/.test(ua);
  var isAndroid = /android/.test(ua);
  var isMobile = isIOS || isAndroid;

  var isSafari = /safari/.test(ua);
  var isIOSChrome = /crios/.test(ua);
  var isOpera = /opera/.test(ua);

  // モバイルではないのでお帰りいただく
  if (!isMobile) { return ACCESS.DENIED; }

  // iOSSafari or iOSChrome or iOSOperaはようこそ
  if (isIOS && (isSafari || isIOSChrome || isOpera)) {
    return ACCESS.ALLOWED;
  }
  else { // ここがおそらくWebView族 }

  // Android標準 or AndroidChromeもFireFoxもOperaもなんならSBrowserもようこそ
  if (isAndroid) { return ACCESS.ALLOWED; }

  // 残ったやつにはお帰りいただく
  return ACCESS.DENIED;
}());

accessible || (location.href = '出なおしてくださいコンテンツ')

ほんとにコレでいいんかと思いながら書きました。

  • UAとかいうただの文字列を全面的に信用してる怖さ
  • AndroidにはWebViewなんて無いとするトンデモな前提
  • UA偽装機能を備えた3rdなブラウザとかいる

それを許容するなら使えると思います。

FacebookTwitterとLINEのWebViewでだけで実行したいナニカがあるなら

var mayBeWebView = (function() {

  var ua = navigator.userAgent.toLowerCase();

  var isIOS = /ip(hone|od|ad)/.test(ua);

  // WebViewはiOSしかない前提なので、
  // ここを通れないものはWebView足り得ない
  if (!isIOS) { return 0; }

  // この2アプリはUAわかりやすい
  // var isTwitterIOS = /twitter/.test(ua); もう判定できない
  var isFacebookIOS = /fbav/.test(ua);
  if (isFacebookIOS) { return 1; }

  var isSafari = /safari/.test(ua);
  var isIOSChrome = /crios/.test(ua);
  var isOpera = /opera/.test(ua);

  // iOSかつ、通常ブラウザが持ってるであろう文字がないならWebView
  var isLineIOSOrOtherWebView = !(isSafari || isIOSChrome || isOpera);
  if (isLineIOSOrOtherWebView) { return 1; }

  // 残ったやつにはお帰りいただく
  return 0;
}());

mayBeWebView && (location.href = 'WebViewさんは出なおしてくださいコンテンツ')


以上からお察しいただく通り、なんていうかアレなので、
もはやどうアクセスされても良い導線設計みたいなんを考えることに注力したほうがいいですね。

(ヽ´ω`) < 現場からは以上です。

絶望のAndroid@20141010追記

そもそもTwitterFacebookも、Android版ではWebViewが搭載されていない。
それだけを頼りにここまで生きていきましたが、どうやらLINEではWebViewあるみたいで。

AndroidLINEのユーザーエージェント

Mozilla/5.0 (Linux; U; Android 4.2.2; ja-jp; SBM206SH Build/S0014) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30

あれ、これ標準ブラウザと一緒じゃね・・あれ・・・。

追い打ち

http://www.au.kddi.com/developer/android/kishu/ua/

> WebView 「ブラウザ」と同一のUser Agent


・・・\(^o^)/

iOS TwitterUA変更@20150407追記

iOSFacebookくらいしかろくに見分けられなくなった今、果たして判別することに意義はあるのでしょうか。

私にはわかりません・・。

(ヽ´ω`) < 現場・・からは・・・以上です・・・。

iOS LINEのUA変更@20150422追記

(ヽ´ω`) < もうやだこの対応