今からでも間に合う！　JavaScriptのWake Lock APIの使い方

**2019/05/28　追記： Wake Lock APIの仕様が大幅に変更されたので、この記事は役に立たなくなりました。改稿をお待ちください。**

近頃、PWAに代表されるような、ブラウザアプリのモバイル端末への進出が進んでいることは皆さんご存知かと思います。とはいえ、ネイティブアプリに比べると、ブラウザ上で動くJavaScriptにできることは限られています。現在、それを補うために、JavaScriptから使用することができる様々なAPIが次々と策定されています。この記事で紹介する**Wake Lock API**もその1つです。

Wake Lockという言葉は、ネイティブアプリ開発をしている方なら知っている言葉かもしれません。これは、要するに**ほっといても画面が勝手に消えない機能**です。

スマートフォンのような端末は、一定時間触らないでいると画面が消えてアプリ等の動作も止まるのが普通です。しかし例えば動画を再生するアプリなどは、触らないからといって勝手に消えてもらっては困ります。ですから、この自動画面オフを制御する機能が必要となるのです。そして、このような制御をJavaScriptから行えるのが**Wake Lock API**なのです。

# Wake Lock APIの使用法
では、さっそくWake Lock APIの使用法を解説していきます。まず、**WakeLockオブジェクト**というものを取得する必要があります。そのために使うのが`navigator.getWakeLock`メソッドです。このメソッドの返り値はPromiseであり、成功すればこのPromiseはWakeLockオブジェクトに解決されます。

```js:WakeLockの取得
// スクリーンのWakeLockを取得
navigator.getWakeLock('screen').then(wakelock => {
  // WakeLockの取得に成功した
})
.catch(err => {
 // 失敗
  console.error(err);
});
```

WakeLockが設定で許可されていないなどの場合はWakeLockの取得に失敗することになります。

そして、実際に画面のWakeLockを有効にするにはWakeLockオブジェクトの`createRequest`メソッドを呼び出します。

```js:リクエストの取得
// スクリーンのWakeLockを取得
navigator.getWakeLock('screen').then(wakelock => {
  // リクエストを取得
  const request = wakelock.createRequest();
});
```

`createRequest`によりリクエストを取得した時点でスクリーンのWakeLockが有効化され、勝手に画面が消えなくなります。

これを解除するためには、取得したリクエストの`cancel`メソッドを呼び出します。例えば、1分後に解除する単純なコードは次のようになります。

```js:リクエストの解除
// スクリーンのWakeLockを取得
navigator.getWakeLock('screen').then(wakelock => {
  // リクエストを取得
  const request = wakelock.createRequest();

  setTimeout(()=> {
    // リクエストを解除
    request.cancel();
  }, 60000);
});
```

なお、`createReqesut`を複数回呼び出すことで複数個のリクエストを得ることも可能です。この場合、全てのリクエストが解除されるまでWakeLockは有効になります。この仕様は、複数のライブラリが独立にWakeLockを取得しても大丈夫なのでありがたいですね。

# 2種類のWakeLock
上のサンプルでは、`getWakeLock`に`'screen'`という文字列を渡していることに気づいたかと思います。これはWakeLockの種類を表す引数であり、`'screen'`と`'system'`の2種類があります。

`'screen'`というのが今まで説明したような、自動的な画面オフを防ぐWakeLockです。

もうひとつ`'system'`というのがあり、これはCPUが省電力モードに移行してプログラムが止まるのを防ぐWakeLockです。実装にもよりますが、このWakeLockを取得しておくことで、場合によってはモバイル端末がロックされている状態でもプログラムを実行し続けることが可能です[^system_wake_lock_off]。

[^system_wake_lock_off]: [仕様書](https://www.w3.org/TR/2017/CR-wake-lock-20171214/#dfn-the-wake-lock-is-applicable)の“Manually switching off the screen MUST NOT affect the applicability of the system wake lock.”という文を根拠としていますが、実際問題として本当にそんなことが許可されるのかはよく分かりません。 

# WakeLockオブジェクトのその他の機能
WakeLockオブジェクトには、現在WakeLockが有効かどうかを調べたり、有効/無効の状態が変化したときにイベントで知らせてもらえる機能もあります。

```js
navigator.getWakeLock('screen').then(wakelock => {
  // activeプロパティ（真偽値）で現在の状態を取得
  if (wakelock.active) {
    console.log('なんかWakeLockが有効です');
  }
  // onactivechange イベントハンドラで状態の変化を検知
  wakelock.onactivechange = ()=> {
    if (wakelock.active) {
      console.log('有効になりました'); 
    } else {
      console.log('無効になりました');
    }
  };
  // WakeLockはEventTargetなのでaddEventListenerも使用可
  wakelock.addEventListener('activechange', ()=> { /* 省略 */ });
});
```

この`active`プロパティの情報はグローバルな状態であることに注意してください。すなわち、自分ではなく他の文書がWakeLockを取得した場合でも`true`となります。実際に自分の文書がリクエストを取得しているかどうかは別問題なので、別途管理する必要があります[^global_security]。

[^global_security]: 実際、[仕様書](https://w3c.github.io/wake-lock/#security-and-privacy-considerations)では無関係な（オリジン境界をまたいだ）文書間で通信が行える可能性が指摘されています。Wake Lock APIでシリアル通信するライブラリとかが発生したら面白いですね。OSからしたらいい迷惑ですが。

# WakeLockの有効範囲
`'screen'` WakeLockリクエストは、それを取得したページが表示されている間のみ有効です。つまり、`'screen'` WakeLockリクエストを取得しているページがあっても、そのページを閲覧していない場合（別のタブが開かれている場合など）は普通に画面が消えます。

また、WakeLockリクエストを取得している場合でも、例えばバッテリーが少ないなどの事情により、WakeLockが無効化されることがあります。JavaScript程度ではOSには逆らえないということですね。

なお、このような外的要因でWakeLockが無効化された場合も、ちゃんと前述の`active`プロパティは`false`になります。

また、Wake Lock APIは[secure context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts)でしか使えません。大雑把に言えば、HTTPSで配信されているページでないと使えないということです。

Wake Lock APIの仕様はこれだけです。シンプルで結構分かりやすいですね。

※ Wake Lock APIはまだW3C勧告ではないため、将来的に内容の変更があるかもしれません。ご注意ください。

# 対応ブラウザ

**対応ブラウザはまだありません。**（[Chrome Platform Status](https://www.chromestatus.com/feature/4636879949398016)）

よかったですね、間に合いました。

※ 記事執筆時点（2018年10月12日）の情報です。

---

- Chrome（Chromium）にはフラグ付きの実装が入っていますが、まだ完成には程遠い試験実装程度の状態のようです。

- また、Firefox OSでは古いバージョンのAPIがサポートされているようです（[MDN](https://developer.mozilla.org/en-US/docs/Archive/B2G_OS/API/Wake_Lock_API)）。

- よく分かりませんが、EdgeではWinRT APIというものにアクセスすることで同じことができるそうです。それを用いた[Edge用のPolyfillがあるようです](https://github.com/patrickkettner/WinRT-wake-lock-polyfill)。

- Wake Lock APIを使わずに似たようなことをする[NoSleep.js](https://github.com/richtr/NoSleep.js)というライブラリがあります。これは、ページ上で見えない動画を再生し続けることで画面が消えるのを防いでいるようです。