ブラウザ拡張でのidle.onStateChangedの実行タイミングがWindows10でのみ異なる件

技術記事なのでクロス投稿をしてみる。

idle.onStateChangedとは

ブラウザの拡張機能で使用できる idle というAPIがあります。

chrome.idle - Google Chrome

私はChromeでしか試していないですが、一応、Firefoxなどでも実装されているみたいです。

idle - Mozilla | MDN

OSがスクリーンロック(Macだと⌘+Control+Q、WindowsだとWin+L)になったり、
一定時間操作が無かったかなどを取得できるAPIです。

サンプルコード

chrome.idle.onStateChanged.addListener((newState) => {  
    // newState "active", "idle", or "locked"  
});  

onStateChanged というイベントにCallbackを指定することで、
状態変化時にコードを実行することができます。

用途

用途は様々に考えられますが、例えば、チャットのような拡張機能を作ったのであれば、
一定期間何も操作されていなかったり、スクリーンロックされたら離席状態にする、というような機能に使えます。

本題:Windows10における問題

Mac、Windows7、Windows10で動作するChrome Extensionを作った際に発覚した問題として、
Mac、Windows7においては、スクリーンロックの操作(ショートカットキーを押したり、メニューからロックを選んだり)した段階で、chrome.idle.onStateChangedのイベントは実行されます。

問題点

しかし、Windows10においては、スクリーンロックの操作を行った時点では chrome.idle.onStateChanged のイベントが実行されません。

Windows10からは、スクリーンロック操作を行った時点でロックスクリーン(綺麗な壁紙に日時が表示されているアレ)が表示されますが、
この時点ではonStateChangedイベントは発生せず、
任意のキーを押すなどしてログイン画面を表示した時点でonStateChangedイベントが発生します。

バグ報告

一応、この現象はChromiumのForumにもバグとして報告されているみたいですが…
https://bugs.chromium.org/p/chromium/issues/detail?id=524663

個人的には、

  • Chrome側は「OSの通知してくるスクリーンロックのイベントを拾ってるだけだから仕様です」
  • Windows側は「即イベントを発生しないのは仕様です」

…って言いそうな気がするので、OS・ブラウザ側のバージョンアップでは対応されないのではないかと予測しています。

対応方法

上記の通り、恐らくこの動作は「仕様」であるため完全に対応することはできません。
しかし、Windows10側の設定を変更し、ロックスクリーンを無効にすることでWindows7と同じ挙動にすることが可能です。

ロックスクリーンを無効にする方法

以下のサイトに手順がまとめられています。

Windows10でロック画面を無効または有効にする方法 – iBitzEdge

いくつかの方法がありますが、今回、私の場合はWindows10 Homeだったため、
レジストリエディタで値を追加することを選択しました。

また、対象のPCが複数台あったため、バッチファイルを作成し対応しました。

ロックスクリーンを無効にするコマンド

reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Personalization" /v NoLockScreen /t REG_DWORD /d 1 /f  

上記を記述したバッチファイルを作成し、
PC上でバッチファイルを「管理者として実行」することで、レジストリに書き込まれます。

なお、管理者として実行しなかった場合は、「エラー: アクセスが拒否されました。」と表示されます。

おまけ:他に考えたけど採用しなかった対応方法

一定時間でactiveからidleになったらlockedと同じ状態として扱う

chrome.idle.setDetectionInterval()idleになるまでの秒数を設定することができるので、
ある程度はこの方法でもまかなえるかと思います。

ただ、今回の要件では、どうしてもロック時で正確に状態を取りたかったので採用しませんでした。

WindowsAPIを直接使ってどうにかする

昔はExtensionでDLLを使ったりできたので可能だったかもしれませんが…今は不可能なので諦めました。
Electronアプリで構築していた場合などは、もしかすると可能かもしれません。

詳しくどのAPIを使えば良いのかなどは調べていないので、本当に可能かどうかは不明です。