これは旧eviry tech blogから移行した記事です。
fujiwaraです。
safari 12よりフルスクリーンAPI(webKitFullScreen)に対応したことで、iOS12で動画のフルスクリーン再生が簡単に実装できるようになりました。
しかしながらこのフルスクリーンモードでは、画面操作中に以下のようなメッセージが表示されることがあるようです。
"It looks like you are typing while in full screen"
日本語環境では以下のメッセージになります。
"フルスクリーンでタイプ入力しているようです"
これはフィッシングサイトの可能性を示す警告メッセージのようですが、ソフトウェアキーボードを表示したり文字入力を促したりしてる訳でもないのに表示されてしまいます。
どのような理由で表示されてしまうのか、調べてみました。
まずは挙動から
自社のHTML5動画プレイヤーをフルスクリーンAPI対応させたところ、以下の操作で表示されました。
ボタンをタップ → ポップアップメニューを表示 → 選択
この操作を3回繰り返すことで警告が出ました。
(注:開発途中のバージョンで発見したもので未リリースの機能です)
文字のタップが悪いのか?
HTML5ベースの動画プレイヤーでは、一部のアイコンを除くとUIは文字ベース(div,span+CSS)で構成されています。
文字(もしくはその外側の要素)をタップすることで表示されるのか?というのが最初の仮説。
しかし以下の結果から、その仮説が怪しいことがわかります。
- 文字以外の部分をタップしても表示される。
- ボタンそのものも文字で構成されているが、ボタン連打では表示されない。
どうやら特定の要素やスタイルへの操作に対して表示されている訳ではなさそうです。
ソースを読む
"It looks like..."のメッセージでググると、stack overflowなどでは参考になる回答はなさそうですが、Webkitのソースコード(unofficial mirror site)が検索上位に引っかかりました。 これはもうソースを読むしかないか...
アラート表示箇所
WKFullScreenViewController.mm
- (void)_showPhishingAlert { NSString *alertTitle = WEB_UI_STRING("It looks like you are typing while in full screen", "Full Screen Deceptive Website Warning Sheet Title");
発見。こいつか。関数名がまんまですね。
呼び出し元は?
WKFullScreenViewController.mm
- (void)_touchDetected:(id)sender { if ([_touchGestureRecognizer state] == UIGestureRecognizerStateBegan || [_touchGestureRecognizer state] == UIGestureRecognizerStateEnded) { double score = _secheuristic.scoreOfNextTouch([_touchGestureRecognizer locationInView:self.view]); if (score > requiredScore) [self _showPhishingAlert]; }
iOS のtouchGestureRecognizerのイベントハンドラから呼ばれてますね。
ターゲットのオブジェクトは何だろう?
WKFullScreenViewController.mm
#pragma mark - UIViewController Overrides - (void)loadView { (略) _touchGestureRecognizer = adoptNS([[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(_touchDetected:)]); [_touchGestureRecognizer setCancelsTouchesInView:NO]; [_touchGestureRecognizer setMinimumPressDuration:0]; [_touchGestureRecognizer setDelegate:self]; [self.view addGestureRecognizer:_touchGestureRecognizer.get()];
WKFullScreenViewControllerはフルスクリーンのviewそのものなので、view全体でタッチイベントを受け取ってますね。
タッチされた座標の要素を調べて処理してる可能性もゼロではないですが、HTMLの構成と全く関係なく処理されていることがほぼ確定かなと思います。
表示条件
タッチ操作のターゲットが画面全体であることはわかりました。では出るか出ないかはどのように判定されているのでしょうか?
前述のソースの以下の箇所がポイントになりそうです。
double score = _secheuristic.scoreOfNextTouch([_touchGestureRecognizer locationInView:self.view]); if (score > requiredScore)
タッチ操作の内容に対して何らかの重み付け(score)をして、ある閾値を超えたら発火させているようです。この中を追いかけてみます。
FullscreenTouchSecheuristic.cpp
double FullscreenTouchSecheuristic::scoreOfNextTouch(CGPoint location) { Seconds now = MonotonicTime::now().secondsSinceEpoch(); if (!m_lastTouchTime) { m_lastTouchTime = WTFMove(now); m_lastTouchLocation = WTFMove(location); return 0; } Seconds deltaTime = now - m_lastTouchTime; double coefficient = attenuationFactor(deltaTime); m_lastScore = coefficient * distanceScore(location, m_lastTouchLocation, deltaTime) + (1 - coefficient) * m_lastScore; m_lastTouchTime = now; m_lastTouchLocation = location; return m_lastScore; }
タッチ位置(location)
、タッチ間隔(deltaTime)
、前回のタッチ位置(m_lastTouchLocation)
、前回のscore(m_lastScore)
の4つのパラメータで判断していますね。
細かい条件を知るにはさらにattenuationFactor()
やdistanceScore()
を読み進める必要がありますが、ざっくり言うと以下のような感じです。
- attenuationFactor: deltaTimeが大きいほど高スコア(0.0〜1.0)
- distanceScore: タップの速度(移動距離/タッチ間隔)
- タッチ間隔が長ければタップ速度を高評価、短ければ前回のスコアを高評価
・・・だんだん頭がこんがらがってきましたが(汗)、自社プレイヤーでの再現手順に当てはめてみると、以下のような操作手順により条件に嵌ってしまったのだと思われます。
- 画面下部のコントロールパネル上のボタンをタップ
- 画面真ん中に表示されたポップアップメニューのアイテムをタップ
- 3秒程度でまた操作1
→画面の半分くらいの移動距離、3秒程度のタップ間隔からスコア計測 - 操作2,1を同じ間隔で繰り返すと3回目に発火
→スコアが加算され3回目で閾値を超えた。
4の考察がかなり雑で、そもそもスコアが徐々に加算されるものなのか、突発的に高スコアを叩き出してしまうのかを検証していませんが、まあこの辺で納得することにしました。
表示させないためには?
以上の調査結果に私自身頭を抱えている最中ですが、極論を行ってしまうとこういうことだと思います。
フルスクリーン表示中は余計なUIを表示させない(元に戻るUIは除く)
ただそうも言ってられません。特に動画プレイヤーはフルスクリーンで動作させるケースが多いアプリケーションです。
とりあえず現時点で言えることとしては、以下のようなUIであれば、アラートが表示されにくいといえると思います。
- 画面の同じ部分や近い座標のタップで済むようなUI
例) トグルや[+][-]ボタンでの切り替え
→この辺はスマホでの操作性を下げる可能性もあります - 短時間にたくさんのタップを要求させない
- フェードイン/アウトなどにより早急な操作をさせない
あくまでも現時点でのWebKitの実装を調べた結果です。この条件は今後変わる可能性はあります。
参照
WebKit official size
Unofficial mirror of the WebKit SVN repository