├── .github └── workflows │ └── pr-push.yml ├── .pr-preview.json ├── CONTRIBUTING.md ├── HOWTO.md ├── LICENSE.md ├── README.md ├── annex.md ├── example-page-info.png ├── example-permission-dialog.png ├── index.bs ├── logo-idle.png ├── logo-idle.svg ├── mdn-drafts └── QUICK-REFERENCE.md ├── security-privacy-self-assessment.md └── w3c.json /.github/workflows/pr-push.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: {} 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | main: 9 | name: Build, Validate and Deploy 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: w3c/spec-prod@v1 14 | with: 15 | GH_PAGES_BRANCH: gh-pages 16 | VALIDATE_MARKUP: false 17 | -------------------------------------------------------------------------------- /.pr-preview.json: -------------------------------------------------------------------------------- 1 | { 2 | "src_file": "index.bs", 3 | "type": "bikeshed", 4 | "params": { 5 | "force": 1 6 | } 7 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Web Platform Incubator Community Group 2 | 3 | This repository is being used for work in the W3C Web Platform Incubator Community Group, governed by the [W3C Community License 4 | Agreement (CLA)](http://www.w3.org/community/about/agreements/cla/). To make substantive contributions, 5 | you must join the CG. 6 | 7 | If you are not the sole contributor to a contribution (pull request), please identify all 8 | contributors in the pull request comment. 9 | 10 | To add a contributor (other than yourself, that's automatic), mark them one per line as follows: 11 | 12 | ``` 13 | +@github_username 14 | ``` 15 | 16 | If you added a contributor by mistake, you can remove them in a comment with: 17 | 18 | ``` 19 | -@github_username 20 | ``` 21 | 22 | If you are making a pull request on behalf of someone else but you had no part in designing the 23 | feature, you can remove yourself with the above syntax. 24 | -------------------------------------------------------------------------------- /HOWTO.md: -------------------------------------------------------------------------------- 1 | # How to use the IdleDetector API 2 | 3 | The IdleDetector API is currently available in Chrome 84 and later behind a flag or by registering your domain for [the Origin Trial](https://developers.chrome.com/origintrials/#/view_trial/551690954352885761) (available until November 10th, 2020). 4 | 5 | 1) Download Chrome ([Android](https://play.google.com/store/apps/details?id=com.android.chrome), [Desktop](https://www.google.com/chrome/)). 6 | 2) Navigate to `chrome://flags` and enable `Experimental Web Platform features`. 7 | 3) Navigate to a test page, e.g. https://reillyeon.github.io/idle.html 8 | 4) Go idle (e.g. stop moving your mouse, typing on your keyboard or lock your screen) 9 | 10 | Here is an example of how to use the API: 11 | 12 | ```javascript 13 | async function main() { 14 | // feature detection. 15 | if (!window.IdleDetector) { 16 | console.log("IdleDetector is not available :("); 17 | return; 18 | } 19 | 20 | log("IdleDetector is available! Go idle!"); 21 | 22 | try { 23 | await IdleDetector.requestPermission(); 24 | 25 | let idleDetector = new IdleDetector(); 26 | idleDetector.addEventListener('change', e => { 27 | console.log(`[${new Date().toLocaleString()}] idle change: ${idleDetector.userState}, ${idleDetector.screenState}`); 28 | }); 29 | // There is a minimum limit here of 60 seconds. 30 | await idleDetector.start({threshold: 60000}); 31 | } catch (e) { 32 | // deal with initialization errors. 33 | // permission denied, running outside of top-level frame, etc 34 | console.log(`Initialization error: ${e}.`); 35 | } 36 | }; 37 | ``` 38 | 39 | You should see something along the lines of: 40 | 41 | ``` 42 | [4/4/2019, 2:59:54 PM] idle change: active, unlocked 43 | [4/4/2019, 3:00:54 PM] idle change: idle, unlocked 44 | [4/4/2019, 3:01:07 PM] idle change: active, unlocked 45 | [4/4/2019, 3:02:25 PM] idle change: idle, unlocked 46 | [4/4/2019, 3:02:41 PM] idle change: active, unlocked 47 | [4/4/2019, 3:03:56 PM] idle change: idle, unlocked 48 | [4/4/2019, 3:12:56 PM] idle change: idle, locked 49 | [4/4/2019, 3:17:38 PM] idle change: active, unlocked 50 | ``` 51 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All Reports in this Repository are licensed by Contributors 2 | under the 3 | [W3C Software and Document License](http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document). 4 | 5 | Contributions to Specifications are made under the 6 | [W3C CLA](https://www.w3.org/community/about/agreements/cla/). 7 | 8 | Contributions to Test Suites are made under the 9 | [W3C 3-clause BSD License](https://www.w3.org/Consortium/Legal/2008/03-bsd-license.html) 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # User Idle Detection 4 | 5 | [Read the draft specification](https://wicg.github.io/idle-detection). 6 | 7 | This proposed API allows developers to add an event listener for when the user 8 | becomes idle (e.g. they don’t interact with the keyboard, mouse or touchscreen, 9 | when a screensaver activates or when the screen is locked). Unlike solutions 10 | based on monitoring input events this capability extends beyond the site's 11 | content area (e.g. when users move to a different window or tab). 12 | 13 | Native applications and browser extensions (e.g. [Chrome 14 | apps](https://developer.chrome.com/apps/idle), [Android 15 | apps](https://stackoverflow.com/questions/8317331/detecting-when-screen-is-locked), 16 | [Firefox 17 | extensions](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/idle), 18 | [Edge 19 | extensions](https://github.com/MicrosoftDocs/edge-developer/blob/master/microsoft-edge/extensions/api-support/supported-apis.md#idle)) 20 | use idle detection to notify other users that the user is unreachable (e.g. in 21 | chat applications), to show timely alerts (e.g. "welcome back" when a user 22 | returns to their task) or to pause media (e.g. to save bandwidth when the user 23 | is not present). 24 | 25 | The API should provide a means to _detect_ the user's idle status (active, idle, 26 | locked), and a power-efficient way to be _notified_ of changes to the status 27 | without polling from script. 28 | 29 | Feedback: [WICG Discourse 30 | Thread](https://discourse.wicg.io/t/idle-detection-api/2959) — 31 | [Issues](https://github.com/inexorabletash/idle-detection/issues) 32 | 33 | ## Use cases 34 | 35 | * Chat application: presenting a user's status to other users and delivering 36 | notifications to the device where the user is active. 37 | * Showing timely notifications - e.g. deferring displaying feedback until the 38 | user returns to an active state. 39 | * Updating an outdated service worker when there's no unsaved state by 40 | triggering reloading of the tab. 41 | 42 | ## Relationship with other APIs 43 | 44 | * As opposed to the 45 | [requestIdleCallback](https://www.w3.org/TR/requestidlecallback/), this is 46 | _not_ about asynchronously scheduling work when the **system** is idle. 47 | * As opposed to the [Page Visibility 48 | API](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API), 49 | this API enables detecting idleness even after a page is no longer visible 50 | (e.g. after the page is no longer visible, is the user still around? if i 51 | showed a notification, would it be perceived?). 52 | 53 | ## Polyfills 54 | 55 | Currently, web apps (e.g. Dropbox’s 56 | [idle.ts](https://github.com/dropbox/idle.ts)) are constrained to their own 57 | content area: 58 | 59 | 1. costly polling for input events or 60 | 1. listening to [visibility 61 | changes](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API) 62 | 63 | Script can't tell today when a user goes idle outside of its content area (e.g. 64 | whether a user is on a different tab or logged out of the computer altogether). 65 | 66 | ## Model 67 | 68 | The API assumes that there is some level of engagement between the user, user 69 | agent, and operating system of the device in use. This is represented in two 70 | dimensions: 71 | 72 | 1. The user idle state 73 | * **active**/**idle** - the user has / has not interacted with the user agent 74 | for some period of time 75 | 2. The screen idle state 76 | * **locked**/**unlocked** - the system has an active screen lock preventing 77 | interaction with the user agent 78 | 79 | Distinguishing "active" from "idle" requires heuristics that may differ across 80 | user, user agent, and operating system. It should also be a reasonably coarse 81 | threshold (See Privacy). 82 | 83 | The model intentionally does not formally distinguish between interaction with 84 | particular content (i.e. the web page in a tab using the API), the user agent as 85 | a whole, or the operating system; this definition is left to the user agent. 86 | 87 | > Example: The user is interacting with an operating system providing multiple 88 | > virtual desktops. The user may be actively interacting with one virtual 89 | > desktop, but unable to see the content of another virtual desktop. A user 90 | > agent presenting content on the second virtual desktop may report an "idle" 91 | > state rather than an "active" state. 92 | 93 | ## API Design 94 | 95 | The API design is largely inspired by the [Sensors 96 | API](https://w3c.github.io/sensors). You can find more about alternatives 97 | considered [here](annex.md). 98 | 99 | ### WebIDL 100 | 101 | ```js 102 | dictionary IdleOptions { 103 | [EnforceRange] unsigned long threshold; 104 | AbortSignal signal; 105 | }; 106 | 107 | enum UserIdleState { 108 | "active", 109 | "idle" 110 | }; 111 | 112 | enum ScreenIdleState { 113 | "locked", 114 | "unlocked" 115 | }; 116 | 117 | [ 118 | SecureContext, 119 | Exposed=(Window,DedicatedWorker) 120 | ] interface IdleDetector : EventTarget { 121 | constructor(); 122 | readonly attribute UserIdleState? userState; 123 | readonly attribute ScreenIdleState? screenState; 124 | attribute EventHandler onchange; 125 | [Exposed=Window] static Promise requestPermission(); 126 | Promise start(optional IdleOptions options = {}); 127 | }; 128 | ``` 129 | 130 | ### Example 131 | 132 | Here is an example of how to use it (more detailed instructions 133 | [here](HOWTO.md)): 134 | 135 | ```js 136 | const main = async () => { 137 | // Feature detection. 138 | if (!('IdleDetector' in window)) { 139 | return console.log('IdleDetector is not available.'); 140 | } 141 | // Request permission to use the feature. 142 | if ((await IdleDetector.requestPermission() !== 'granted') { 143 | return console.log('Idle detection permission not granted.'); 144 | } 145 | try { 146 | const controller = new AbortController(); 147 | const signal = controller.signal; 148 | 149 | const idleDetector = new IdleDetector(); 150 | idleDetector.addEventListener('change', () => { 151 | console.log(`Idle change: ${idleDetector.userState}, ${idleDetector.screenState}.`); 152 | }); 153 | await idleDetector.start({ 154 | threshold: 60000, 155 | signal, 156 | }); 157 | console.log('IdleDetector is active.'); 158 | 159 | window.setTimeout(() => { 160 | controller.abort(); 161 | console.log('IdleDetector is stopped.'); 162 | }, 120000); 163 | } catch (err) { 164 | // Deal with initialization errors like permission denied, 165 | // running outside of top-level frame, etc. 166 | console.error(err.name, err.message); 167 | } 168 | }; 169 | 170 | main(); 171 | ``` 172 | 173 | ## Platforms 174 | 175 | All platforms (Linux, Windows, macOS, Android, iOS and Chrome OS) support some 176 | form of idle detection. 177 | 178 | On desktop devices (Chrome OS, Linux, macOS and Windows), a screen saver (from a 179 | time when monitors were damaged if the same pixels were lit for an extended 180 | period of time) activates after a user-configurable period of inactivity. The 181 | operating system may optionally require the user to reauthenticate (i.e. lock 182 | the screen) after the screen saver has been activated for a period of time. Both 183 | of these events are observable by engines. 184 | 185 | On mobile devices (Android and iOS), the screen is dimmed after a few seconds of 186 | inactivity (to save battery, not pixels) but this isn't observable by engines 187 | (on Android). The screen is eventually turned off (to save further battery) if 188 | the user remains inactive for a configurable amount of time (typically 30 189 | seconds), and that is observable by engines. When the screen goes off, the 190 | screen is also typically locked (unlockable by Swipe, Pattern, PIN or Password), 191 | although it can be configured to be left off but unlocked. 192 | 193 | ## Permissions 194 | 195 | The ability to use this API will be controlled by the new [`"idle-detection"` 196 | permission]. 197 | 198 | ## Security and Privacy 199 | 200 | See answers to the W3C TAG's [security & privacy self-review 201 | questionnaire](https://www.w3.org/TR/security-privacy-questionnaire/) in 202 | [security-privacy-self-assessment.md](security-privacy-self-assessment.md). 203 | 204 | The idle state is a global system property and so care must be taken to prevent 205 | this from being used as a cross-origin communication or identification channel. 206 | This is similar to other APIs which provide access to system events such as 207 | [Generic Sensors](https://w3c.github.io/sensors/) and 208 | [Geolocation](https://w3c.github.io/geolocation-api/). 209 | 210 | A short idle threshold could be used to identify user behavior in another tab. 211 | With a short enough threshold an idle state change could be used to measure 212 | typing cadence when the user is in another application and thus leak sensitive 213 | data such as passwords. 214 | 215 | Users with physical or cognitive impairments may require more time to interact 216 | with user agents and content. The API should not allow distinguishing such 217 | users, or limiting their ability to interact with content any more than existing 218 | observation of UI events. 219 | 220 | If an implementation restricts the detection threshold it should also restrict 221 | how quickly responses to `start()` are delivered or ensure that the response is 222 | cached or otherwise provide a guarantee that rapid polling does not bypass the 223 | restriction on data granularity. 224 | 225 | To mitigate the exposure of this global state the API should be restricted to 226 | top-level frames with a new `"idle-detection"` permission. This permission can 227 | be delegated to sub-frames via [Permissions Policy] or the [`sandbox` 228 | attribute]. The top-level frame requirement significantly reduces the number of 229 | cross-origin contexts which can observe the state event and thus identify the 230 | user through backend communication channels. 231 | 232 | Requiring a permission does not completely mitigate the cross-origin 233 | identification issue but further reduces the number of sites which are able to 234 | participate in such an attack. 235 | 236 | A new permission informs the user about the permission the page is requesting. 237 | 238 | > ![Screenshot of a permission request dialog](example-permission-dialog.png) 239 | >
240 | > Example icon and text for an `"idle-detection"` permission request. 241 | 242 | Rather than expanding the definition of an existing permission, it is clear to 243 | users which sites have access to additional capabilities. 244 | 245 | > ![Screenshot of Chromium's page information dialog](example-page-info.png) 246 | >
247 | > Example page information dialog box showing a site with both 248 | > `"notifications"` and `"idle-detection"` permissions granted. 249 | 250 | This capability changes the web privacy model by allowing the site to observe a 251 | limited amount of information about how the user interacts with their device 252 | outside the border of the site's content area. Sites using this permission 253 | should present the request in a context which explains the value to the user of 254 | being granted this capability. 255 | 256 | Implementations that provide a "privacy browsing" mode should not enable this 257 | capability in such a mode. This may be satisfied incidentally by not allowing 258 | notification permission to be granted. Care should be taken to avoid allowing 259 | sites to detect this mode by, for example, randomly delaying the automatic 260 | disapproval of the notification permission so that it appears to have been 261 | denied by the user. 262 | 263 | ## Alternative Permissions Models 264 | 265 | ### Event Timing Fuzzing 266 | 267 | The current idle state and the timing of transitions between states are global 268 | state which could be used to identify a user across origin boundaries. For 269 | example, two cooperating sites could compare the timestamps at which they 270 | observed clients transitioning from `"active"` to `"idle"`. Two clients which 271 | appear to consistently make transitions at the same time have a high likelyhood 272 | of being the same user. 273 | 274 | A mitigation with the potential to directly address this attack is to add a 275 | random delay between when the specified threshold is passed and when the 276 | `"change"` event is fired. 277 | 278 | This mitigation was considered and determined to be unacceptable because: 279 | 280 | * Delaying the event by even a small amount drastically reduces the usefulness 281 | of the API in chat applications which want to ensure that messages are 282 | delivered to the correct device. 283 | * Even with a large fuzzing factor (e.g. 30 seconds) data collected over a long 284 | period of time is still sufficient to identify the user. 285 | 286 | ### Combined with Notification Permission 287 | 288 | Rather than defining a new `"idle-detection"` permission, the definition of the 289 | existing `"notifications"` permission could be expanded to control access to 290 | this capability. The most compelling use cases for this capability involve 291 | messaging applications, so the [`"notifications"` permission] seems 292 | appropriate. 293 | 294 | The advantage of not defining a new permission type is that it helps to avoid 295 | "consent fatigue", in which users are presented with an endlessly increasing 296 | number of choices by operating systems, browsers, and web sites. Adding this 297 | capability to a related permission such as `"notifications"` can maintain a 298 | user's control while reducing the number of decisions that need to be made. 299 | 300 | The disadvantage of not defining a new permission is that it changes the meaning 301 | of permission decisions the user had made in the past. While notifications are 302 | related to signals of user presence the ability to monitor this state may not be 303 | something the user considers when granting this permission. 304 | 305 | There is a middle-ground in which the permission prompt text for the 306 | [`"notifications"` permission] is updated to explain this new capability but 307 | both capabilities are still controlled by a single permission. Implementations 308 | could internally track whether the user has seen the updated prompt and require 309 | a site re-request the [`"notifications"` permission] before it has access to the 310 | new capability. 311 | 312 | ## Prior Work 313 | 314 | * Chrome's [chrome.idle](https://developer.chrome.com/apps/idle) API for 315 | apps/extensions, which is a direct inspiration for this proposal. 316 | * Also exposed to Extensions in Firefox 317 | [MDN](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/idle) 318 | * And 319 | [Edge](https://github.com/MicrosoftDocs/edge-developer/blob/master/microsoft-edge/extensions/api-support/supported-apis.md#idle) 320 | * That API has a global (per-execution-context) threshold and one source of 321 | events. This makes it difficult for two components on the same page to 322 | implement different thresholds. 323 | * Attempts to do this from JS running on the page: 324 | * [idle.ts](https://github.com/dropbox/idle.ts) from Dropbox 325 | * [Idle.js](http://shawnmclean.com/detecting-if-user-is-idle-away-or-back-by-using-idle-js/) 326 | 327 | [`"notifications"` permission]: https://w3c.github.io/permissions/#notifications 328 | [Permissions Policy]: https://w3c.github.io/webappsec-permissions-policy/ 329 | [`sandbox` attribute]: https://html.spec.whatwg.org/multipage/iframe-embed-object.html#attr-iframe-sandbox 330 | -------------------------------------------------------------------------------- /annex.md: -------------------------------------------------------------------------------- 1 | There are multiple alternatives to be considered here. Here are the ones that we ran into: 2 | 3 | * [IdleObserver](#IdleObserver) 4 | * [navigator.idle.query()](#navigatoridlequery) and variations ([chrome.idle](#chrome.idle.query), [browser.idle](#browser.idle.query)) 5 | 6 | Here are some guidance on [Events vs Observers](https://w3ctag.github.io/design-principles/#events-vs-observers) we got from the TAG review. 7 | 8 | ### Alternatives Considered 9 | 10 | #### IdleDetector 11 | 12 | This formulation is inspired by [@kenchris's feedback](https://github.com/w3ctag/design-reviews/issues/336#issuecomment-470077151), the overall guidance on [Observers vs EventTargets](https://w3ctag.github.io/design-principles/#events-vs-observers), and the [Sensor API](https://w3c.github.io/sensors/#feature-detection), specifically, the [`Accelerometer`](https://w3c.github.io/sensors/#feature-detection) class. 13 | 14 | ```js 15 | async function main() { 16 | // feature detection. 17 | if (!window.IdleDetector) { 18 | console.log("IdleDetector is not available :("); 19 | return; 20 | } 21 | 22 | console.log("IdleDetector is available! Go idle!"); 23 | 24 | try { 25 | let idleDetector = new IdleDetector({ threshold: 60 }); 26 | idleDetector.addEventListener('change', ({user, screen}) => { 27 | console.log(`idle change: ${user}, ${screen}`); 28 | }); 29 | await idleDetector.start(); 30 | } catch (e) { 31 | // deal with initialization errors. 32 | // permission denied, running outside of top-level frame, etc 33 | } 34 | }; 35 | ``` 36 | 37 | And for a one-shot reading of the state: 38 | 39 | ```js 40 | const {user, screen} = await IdleDetector.read({ threshold: 2 * 60 }); 41 | ``` 42 | 43 | #### IdleObserver 44 | 45 | This formulation is inspired by the [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver), the [IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) and the [PerformanceObserver](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver) APIs. 46 | 47 | ```js 48 | const observer = new IdleObserver({user, screen} => { 49 | // do stuff 50 | }); 51 | 52 | // Define "idle" as two minutes of inactivity. 53 | observer.observe({threshold: 2*60}); 54 | ``` 55 | 56 | Open questions: 57 | 58 | * Should we allow observer.disconnect()? 59 | * Should we allow multiple new IdleObserver() to run concurrently? 60 | 61 | 62 | #### navigator.idle.query 63 | 64 | This formulation is closer to chrome's [`chrome.idle.query()`](https://developer.chrome.com/apps/idle) API and [`browser.idle,queryState()`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/idle/queryState): 65 | 66 | ```js 67 | const monitor = navigator.idle.query({threshold: 2 * 60}); 68 | 69 | // Listen to state changes 70 | monitor.addEventListener('change', ({user, screen}) => { 71 | // do stuff 72 | }); 73 | ``` 74 | 75 | Or, if you only care about the current state: 76 | 77 | ```js 78 | navigator.idle.query({threshold: 2 * 60}) 79 | .addEventListener({once: true}, ({user, screen}) => { 80 | // do stuff 81 | }); 82 | ``` 83 | 84 | Open questions: 85 | 86 | * do we have a preference between `navigator.idle.query()` or `navigator.idle.observe()`? 87 | * should we `navigator.idle.query()` return a `Promise` such that the current state is returned easily? 88 | 89 | #### Variations 90 | 91 | Here are some variations of the Observer pattern that we could try too: 92 | 93 | ```js 94 | navigator.idle.observe({threshold: 2*60}, (e) => console.log(e)) 95 | 96 | let observer = navigator.idle.query({threshold: 2*60}, e => console.log(e)) 97 | observe.observe() 98 | 99 | // More consistent with MutationObserver, less consistent with 100 | // the other static navigator.* APIs. 101 | let observer = new IdleObserver(e => console.log(e)); 102 | observer.observe({threshold: 2*60}); 103 | ``` 104 | -------------------------------------------------------------------------------- /example-page-info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WICG/idle-detection/183730b9a7a73e015866d5296c7e957aafe19463/example-page-info.png -------------------------------------------------------------------------------- /example-permission-dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WICG/idle-detection/183730b9a7a73e015866d5296c7e957aafe19463/example-permission-dialog.png -------------------------------------------------------------------------------- /index.bs: -------------------------------------------------------------------------------- 1 | 16 | 17 |
 18 | spec: ECMAScript; urlPrefix: https://tc39.github.io/ecma262/#
 19 |     type: dfn
 20 |         text: internal slot; url: sec-object-internal-methods-and-internal-slots
 21 | spec: page-visibility-2; urlPrefix: https://www.w3.org/TR/page-visibility-2/#
 22 |     for: Document
 23 |         type: attribute
 24 |             text: hidden; url: dom-document-hidden
 25 |             text: onvisibilitychange; url:dom-document-onvisibilitychange
 26 | 
27 | 28 | 35 | 36 | 48 | 49 | # Introduction # {#introduction} 50 | 51 | *This section is non-normative.* 52 | 53 | Using existing capabilities a page is able to determine when it is currently 54 | visible to the user (using the {{Document/hidden}} property and 55 | {{Document/onvisibilitychange}} event). It is also possible to know when the 56 | user has recently interacted with the page by observing 57 | {{GlobalEventHandlers/onmousemove}}, {{GlobalEventHandlers/onkeypress}}, and 58 | other events triggered by user input. While sufficiently reflecting user 59 | engagement with a particular page these events give an incomplete picture of 60 | whether the user is still present at their device. For example, if 61 | {{Document/hidden}} is `true`, then the device screensaver may have activated, 62 | or the user could have switched to a different application. If it is `false` but 63 | there have been no recent input events, then the user could have left their 64 | computer to grab a cup of coffee, or they could be editing a document in another 65 | window side-by-side with the page. 66 | 67 | Making these distinctions is important for applications which have the option of 68 | delivering notifications across multiple devices, such as a desktop and 69 | smartphone. Users may find it frustrating when notifications are delivered to 70 | the wrong device or are disruptive. For example, if they switch from a tab 71 | containing a messaging application to one for a document they are editing, the 72 | messaging application, not being able to observe that the user is still 73 | interacting with their device, may assume that they have left to grab a coffee 74 | and start delivering notifications to their phone, causing it to buzz 75 | distractingly, instead of displaying notifications on their desktop or 76 | incrementing a badge count. 77 | 78 | ## Alternatives Considered ## {#alternatives-considered} 79 | 80 | An alternative design would protect this information by allowing a notification 81 | to be marked as "hide on active" or "hide on idle" and not allowing the page to 82 | observe whether or not the notification was actually shown. The problem with 83 | this approach is that the intelligent notification routing described previously 84 | requires observing these signals of user presence and making centralized 85 | decisions based on the state of all of the user's devices. 86 | 87 | For example, to route notifications to a user's mobile device when they get up 88 | to grab a coffee the messaging application could detect that it is no longer 89 | visible and start sending push messages to the mobile device while marking the 90 | desktop notifications as "hide on active". If the user were still at their desk 91 | but using a different application then they would start getting the distracting 92 | notifications from their mobile device this proposal is attempting to avoid 93 | whether or not the desktop is able to successfully suppress them. Successful 94 | suppression of duplicate and disruptive notification requires multi-device 95 | coordination. 96 | 97 | Allowing notifications to be hidden also breaks implementor mitigations for the 98 | [[PUSH-API]] being used to run silent background tasks. 99 | 100 | # Observing User Presence # {#api} 101 | 102 | ## Model ## {#api-model} 103 | 104 | This specification defines a model for user presence on two dimensions: idle 105 | state and screen lock. 106 | 107 | ### The {{UserIdleState}} enum ### {#api-useridlestate} 108 | 109 | 110 | enum UserIdleState { 111 | "active", 112 | "idle" 113 | }; 114 | 115 | 116 | : {{"active"}} 117 | 118 | :: Indicates that the user has interacted with the device in the last 119 | {{IdleOptions/threshold}} milliseconds. 120 | 121 | : {{"idle"}} 122 | 123 | :: Indicates that the user has not interacted with the device in at least 124 | {{IdleOptions/threshold}} milliseconds. 125 | 126 | ### The {{ScreenIdleState}} enum ### {#api-screenidlestate} 127 | 128 | 129 | enum ScreenIdleState { 130 | "locked", 131 | "unlocked" 132 | }; 133 | 134 | 135 | : {{"locked"}} 136 | 137 | :: Indicates that the device has engaged a screensaver or lock screen which 138 | prevents content from being seen or interacted with. 139 | 140 | : {{"unlocked"}} 141 | 142 | :: Indicates that the device is able to display content and be interacted with. 143 | 144 | ## Permissions ## {#api-permissions} 145 | 146 | The "idle-detection" permission is a [=default powerful feature=]. 147 | 148 | ## Permissions policy ## {#api-permissions-policy} 149 | 150 | This specification defines a [=policy-controlled feature=] identified by the 151 | string `"idle-detection"`. Its [=default allowlist=] is `'self'`. 152 | 153 |
154 | The [=default allowlist=] of `'self'` allows usage of this feature on 155 | same-origin nested frames by default but prevents access by third-party content. 156 | 157 | Third-party usage can be selectively enabled by adding the 158 | `allow="idle-detection"` attribute to an {{iframe}} element: 159 | 160 |
161 | ```html 162 | 163 | ``` 164 |
165 | 166 | Alternatively, this feature can be disabled completely in first-party contexts 167 | by specifying the permissions policy in an HTTP response header: 168 | 169 |
170 | ```http 171 | Permissions-Policy: idle-detection 'none' 172 | ``` 173 |
174 | 175 | See [[PERMISSIONS-POLICY]] for more details. 176 |
177 | 178 | ## The {{IdleDetector}} interface ## {#api-idledetector} 179 | 180 | 181 | dictionary IdleOptions { 182 | [EnforceRange] unsigned long long threshold; 183 | AbortSignal signal; 184 | }; 185 | 186 | [ 187 | SecureContext, 188 | Exposed=(Window,DedicatedWorker) 189 | ] interface IdleDetector : EventTarget { 190 | constructor(); 191 | readonly attribute UserIdleState? userState; 192 | readonly attribute ScreenIdleState? screenState; 193 | attribute EventHandler onchange; 194 | [Exposed=Window] static Promise<PermissionState> requestPermission(); 195 | Promise<undefined> start(optional IdleOptions options = {}); 196 | }; 197 | 198 | 199 | Instances of {{IdleDetector}} are created with the [=internal slots=] described 200 | in the following table: 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 211 | 212 | 213 | 214 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 |
[=Internal slot=]Initial valueDescription (non-normative)
\[[state]] 210 | `"stopped"`Tracks the active state of the {{IdleDetector}}
\[[threshold]] 215 | `undefined`The configured idle detection threshold
\[[userState]]`null`The last known user idle state
\[[screenState]]`null`The last known screen idle state
229 | 230 | 231 | idlharness-worker.https.window.js 232 | idlharness.https.window.js 233 | 234 | 235 | Methods on this interface typically complete asynchronously, queuing work on the 236 | idle detection task source. 237 | 238 | ### {{IdleDetector/userState}} attribute ### {#api-idledetector-userstate} 239 | 240 |
241 | The userState getter steps are: 242 | 243 | 1. Return [=this=].{{[[userState]]}}. 244 | 245 |
246 | 247 | ### {{IdleDetector/screenState}} attribute ### {#api-idledetector-screenstate} 248 | 249 |
250 | The screenState getter steps are: 251 | 252 | 1. Return [=this=].{{[[screenState]]}}. 253 | 254 |
255 | 256 | ### {{IdleDetector/onchange}} attribute ### {#api-idledetector-onchange} 257 | 258 | onchange is an Event handler IDL 259 | attribute for the {{change}} event type. 260 | 261 | ### {{IdleDetector/requestPermission()}} method ### {#api-idledetector-requestpermission} 262 | 263 |
264 | The requestPermission() method steps are: 265 | 266 | 1. If [=this=]'s [=relevant global object=]'s [=associated Document=] is not 267 | [=fully active=], return [=a promise rejected with=] a 268 | "{{InvalidStateError}}" {{DOMException}}. 269 | 270 | idle-detection-detached-frame.https.html 271 | 272 | 1. If the [=relevant global object=] of [=this=] does not have [=transient 273 | activation=], return [=a promise rejected with=] a "{{NotAllowedError}}" 274 | {{DOMException}}. 275 | 1. Let |result| be [=a new promise=]. 276 | 1. [=In parallel=]: 277 | 1. Let |permissionState| be the result of [=requesting permission to use=] 278 | "idle-detection". 279 | 1. [=Queue a global task=] on the [=relevant global object=] of [=this=] 280 | using the [=idle detection task source=] to [=resolve=] |result| with 281 | |permissionState|. 282 | 1. Return |result|. 283 | 284 |
285 | 286 | ### {{IdleDetector/start()}} method ### {#api-idledetector-start} 287 | 288 |
289 | 290 | The start(|options|) method steps are: 291 | 292 | 1. Let |document| be [=this=]'s [=relevant global object=]'s [=associated 293 | Document=]. 294 | 1. If |document| is not [=fully active=], return [=a promise rejected with=] a 295 | "{{InvalidStateError}}" {{DOMException}}. 296 | 297 | idle-detection-detached-frame.https.html 298 | 299 | 1. If |document| is not [=allowed to use=] "idle-detection", 300 | return [=a promise rejected with=] a "{{NotAllowedError}}" {{DOMException}}. 301 | 302 | idle-detection-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html 303 | idle-detection-allowed-by-permissions-policy-attribute.https.sub.html 304 | idle-detection-allowed-by-permissions-policy.https.sub.html 305 | idle-detection-default-permissions-policy.https.sub.html 306 | idle-detection-disabled-by-permissions-policy.https.sub.html 307 | 308 | 1. If |this|.{{IdleDetector/[[state]]}} is not `"stopped"`, 309 | return [=a promise rejected with=] an "{{InvalidStateError}}" {{DOMException}}. 310 | 311 | interceptor.https.html 312 | 313 | 1. Set |this|.{{IdleDetector/[[state]]}} to `"starting"`. 314 | 1. If |options|[{{"threshold"}}] is less than 60,000, return [=a promise rejected with=] 315 | a {{TypeError}}. 316 | 317 | basics.tentative.https.window.js 318 | 319 | 1. Let |result| be [=a new promise=]. 320 | 1. If |options|["{{signal}}"] is present, then perform the following sub-steps: 321 | 1. If |options|["{{signal}}"] is [=AbortSignal/aborted=], then 322 | [=reject=] |result| with |options|["{{signal}}"]'s [=AbortSignal/abort reason=] 323 | and return |result|. 324 | 1. [=AbortSignal/add|Add the following abort steps=] to 325 | |options|["{{signal}}"]: 326 | 1. Set |this|.{{IdleDetector/[[state]]}} to `"stopped"`. 327 | 1. [=Reject=] |result| with |options|["{{signal}}"]'s [=AbortSignal/abort reason=]. 328 | 329 | 330 | interceptor.https.html 331 | 332 | 1. [=In parallel=]: 333 | 1. Let |permissionState| be the [=permission state=] of `"idle-detection"`. 334 | idle-permission.tentative.https.window.js 335 | 1. [=Queue a global task=] on the [=relevant global object=] of [=this=] 336 | using the [=idle detection task source=] to perform the following steps: 337 | 1. If |permissionState| is `"denied"`, 338 | 1. Set |this|.{{IdleDetector/[[state]]}} to `"stopped"`. 339 | 1. [=Reject=] |result| with a "{{NotAllowedError}}" {{DOMException}}. 340 | 1. Otherwise, 341 | 1. If |this|.{{IdleDetector/[[state]]}} is `"stopped"`, abort these 342 | steps. 343 | 1. Set |this|.{{IdleDetector/[[state]]}} to `"started"`. 344 | 1. Set |this|.{{IdleDetector/[[threshold]]}} to |options|["{{threshold}}"]. 345 | 1. [=Resolve=] |result|. 346 | 1. Return |result|. 347 | 348 | Note: The steps above are performed [=in parallel=] to enable implementations 349 | to check the permission state asynchronously. 350 | 351 |
352 | 353 |
354 | 355 | The availability of this API can be detected by looking for the {{IdleDetector}} 356 | constructor in the {{Window}} object. 357 | 358 | ```js 359 | if (!('IdleDetector' in window)) { 360 | console.log('Idle detection is not available.'); 361 | return; 362 | } 363 | ``` 364 | 365 | Calling {{start()}} will fail if the "idle-detection" permission 366 | has not been [=permission/granted=]. 367 | 368 | ```js 369 | if ((await IdleDetector.requestPermission()) !== 'granted') { 370 | console.log('Idle detection permission not granted.'); 371 | return; 372 | } 373 | ``` 374 | 375 | A set of options can be configured to control the threshold the [=user agent=] 376 | uses to decide when the user has become idle. 377 | 378 | ```js 379 | const controller = new AbortController(); 380 | const signal = controller.signal; 381 | 382 | const options = { 383 | threshold: 60_000, 384 | signal, 385 | }; 386 | ``` 387 | 388 | The {{IdleDetector}} can now be created and started. An listener for the 389 | {{"change"}} event is added and will be fired if the {{userState}} or 390 | {{screenState}} attributes change. 391 | 392 | ```js 393 | try { 394 | const idleDetector = new IdleDetector(); 395 | idleDetector.addEventListener('change', () => { 396 | console.log(`Idle change: ${idleDetector.userState}, ${idleDetector.screenState}.`); 397 | }); 398 | await idleDetector.start(options); 399 | console.log('IdleDetector is active.'); 400 | } catch (err) { 401 | // Deal with initialization errors like permission denied, 402 | // running outside of top-level frame, etc. 403 | console.error(err.name, err.message); 404 | } 405 | ``` 406 | 407 | At a later time the page can cancel its interest in state change events by 408 | removing its event listeners or using the {{AbortSignal}} that was passed to 409 | {{start()}}. 410 | 411 | ```js 412 | controller.abort(); 413 | console.log('IdleDetector is stopped.'); 414 | ``` 415 |
416 | 417 | ### Reacting to state changes ### {#state-changes} 418 | 419 |
420 | 421 | For each {{IdleDetector}} instance |detector| where |detector|.{{[[state]]}} is 422 | `"started"` the [=user agent=] MUST continuously monitor the following 423 | conditions: 424 | 425 | * If |detector|.{{[[userState]]}} is {{"active"}} and the user has not 426 | interacted with the device within the last |detector|.{{[[threshold]]}} 427 | milliseconds, it MUST [=queue a global task=] on the [=relevant global 428 | object=] of |detector| using the [=idle detection task source=] to run the 429 | following steps: 430 | 431 | 1. Set |detector|.{{[[userState]]}} to {{"idle"}}. 432 | 1. [=Fire an event=] named {{"change"}} at |detector|. 433 | 434 | 435 | * If |detector|.{{[[userState]]}} is {{"idle"}} and the user interacts with 436 | the device, it MUST [=queue a global task=] on the [=relevant global 437 | object=] of |detector| using the [=idle detection task source=] to run the 438 | following steps: 439 | 440 | 1. Set |detector|.{{[[userState]]}} to {{"active"}}. 441 | 1. [=Fire an event=] named {{"change"}} at |detector|. 442 | 443 | * If |detector|.{{[[screenState]]}} is {{"unlocked"}} and the screen is 444 | locked, it MUST [=queue a global task=] on the [=relevant global object=] of 445 | |detector| using the [=idle detection task source=] to run the following 446 | steps: 447 | 448 | 1. Set |detector|.{{[[screenState]]}} to {{"locked"}}. 449 | 1. [=Fire an event=] named {{"change"}} at |detector|. 450 | 451 | * If |detector|.{{[[screenState]]}} is {{"locked"}} and the screen is 452 | unlocked, it MUST [=queue a global task=] on the [=relevant global object=] 453 | of |detector| using the [=idle detection task source=] to run the following 454 | steps: 455 | 456 | 1. Set |detector|.{{[[screenState]]}} to {{"unlocked"}}. 457 | 1. [=Fire an event=] named {{"change"}} at |detector|. 458 | 459 |
460 | 461 | 462 | interceptor.https.html 463 | page-visibility.https.html 464 | 465 | 466 | # Security and privacy considerations # {#security-and-privacy} 467 | 468 | *This section is non-normative.* 469 | 470 | ## Cross-origin information leakage ## {#privacy-cross-origin-leakage} 471 | 472 | This interface exposes the state of global system properties and so care must be 473 | taken to prevent them from being used as cross-origin communication or 474 | identification channels. Similar concerns are present in specifications such as 475 | [[DEVICE-ORIENTATION]] and [[GEOLOCATION]], which mitigate them by requiring 476 | a visible or focused context. This prevents multiple origins from observing the 477 | global state at the same time. These mitigations are unfortunately inappropriate 478 | here because the intent of this specification is precisely to allow a limited 479 | form of tracking in blurred and hidden contexts. A malicious page could notify a 480 | tracking server whenever the user is detected as idle or active. If multiple 481 | pages the user was visiting notified the same server it could use the timing of 482 | the events to guess which sessions corresponded to a single user as they would 483 | arrive roughly simultaneously. 484 | 485 | To reduce the number of independent contexts with access to this interface this 486 | specification restricts it to top-level and same-origin contexts. Access can be 487 | delegated to a cross-origin context through [[PERMISSIONS-POLICY]]. 488 | 489 | To further reduce the number of contexts this specification requires 490 | a page to obtain the "idle-detection" permission. User agents should inform 491 | the user of the capability that this permission grants and encourage them to 492 | only [=permission/grant=] it to trusted sites which have a legitimate purpose for this data. 493 | 494 | Implementations that provide a "private browsing" mode should not allow this 495 | capability in contexts where this mode is enabled. Implementations should be 496 | careful however to avoid the lack of this capability from being used as a signal 497 | that this mode is enabled. This can be accomplished by refusing to allow the 498 | "idle-detection" permission to be [=permission/granted=] but delaying the automatic 499 | dismissal of the permission request by a random interval so that it appears to 500 | have been a user action. 501 | 502 | ## Behavior tracking ## {#privacy-behavior-tracking} 503 | 504 | While this interface does not provide details of the user interaction which 505 | triggered an {{"idle"}} to {{"active"}} transition, with a sufficiently short 506 | threshold these events could be used to detect behavior such as typing. This 507 | specification therefore restricts the requested threshold to a minimum of at 508 | least 60 seconds. 509 | 510 | The permission requirement described previously also helps to mitigate the 511 | general concern that this interface can be used to build a profile of when and 512 | for how long the user typically interacts with their device. 513 | 514 | ## User coercion ## {#privacy-user-coercion} 515 | 516 | Sites may require the user to [=permission/grant=] them the "idle-detection" [=permission=] 517 | before unlocking some functionality. For example, a testing site could require 518 | this permission as part of an anti-cheating mechanism to detect the user 519 | consulting forbidden reference materials in another window. This type of 520 | "Contract of Adhesion" has been observed with other permissions such as 521 | notifications, FIDO attestation and DRM identifiers. 522 | 523 | A potential mitigation for this concern is to design that interface so that it 524 | is not possible for a site to determine whether the user has 525 | [=permission/granted=] or [=permission/denied=] the permission. 526 | An implementation could refuse to acknowledge that the user is 527 | idle, reducing a site to only the signals currently available. This mitigation 528 | could be detectable as it is unlikely that a user who has not interacted with a 529 | page for hours has nevertheless still been continuously interacting with 530 | something else. Implementations could instead insert fake idle transition events 531 | which correspond to plausible behavior given the other signals available to the 532 | page. 533 | 534 | This specification does not mandate this type of mitigation as it could create a 535 | poor user experience when sites take action based on this false data. For 536 | example, the message application mentioned previously would not deliver 537 | notifications to the user's mobile device because it believes the signals it has 538 | been given indicate that they are still at their desktop. As the site cannot 539 | detect that it is in this state it cannot directly recommend an action for the 540 | user to take to get themselves out of it. 541 | 542 | The harm done by such a site is limited as tracking is only possible while the 543 | user is visiting that page. Tracking across multiple origins requires permission 544 | to be requested on each participating site. 545 | 546 | # Accessibility considerations # {#accessibility} 547 | 548 | *This section is non-normative.* 549 | 550 | Users with physical or cognitive impairments may require more time to interact 551 | with user agents and content. Implementations should not allow distinguishing 552 | such users, or limiting their ability to interact with content any more than 553 | existing observation of UI events. For example, implementation should ensure 554 | that interactions from assistive technologies count towards considering the user 555 | active. 556 | 557 | The use of a permission also requires that user agents provide a user interface 558 | element to support requesting and managing that permission. Any such user 559 | interface elements must be designed with accessibility tools in mind. For 560 | example, a user interface describing the capability being requested should 561 | provide the same description to tools such as screen readers. 562 | 563 | # Internationalization considerations # {#internationalization} 564 | 565 | *This section is non-normative.* 566 | 567 | The interface described by this specification has limited internationalization 568 | considerations, however the use of a permission does require that user agents 569 | provide a user interface element to support requesting and managing that 570 | permission. Any content displayed by the user agent in this context should be 571 | translated into the user's native language. 572 | 573 | # Acknowledgements # {#acknowledgements} 574 | 575 | *This section is non-normative.* 576 | 577 | Many thanks to 578 | Kenneth Christiansen, 579 | Samuel Goto, 580 | Ayu Ishii and 581 | Thomas Steiner 582 | for their help in crafting this proposal. 583 | -------------------------------------------------------------------------------- /logo-idle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WICG/idle-detection/183730b9a7a73e015866d5296c7e957aafe19463/logo-idle.png -------------------------------------------------------------------------------- /logo-idle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /mdn-drafts/QUICK-REFERENCE.md: -------------------------------------------------------------------------------- 1 | --- 2 | recipe: api-interface 3 | title: 'IdleDetector' 4 | mdn_url: /en-US/docs/Web/API/IdleDetector 5 | specifications: https://wicg.github.io/idle-detection/#api-idledetector 6 | browser_compatibility: api.IdleDetector 7 | --- 8 | 9 | **This is a rough draft of content that will eventually live on MDN. When this 10 | feature ships, the content below will live on MDN under 11 | [developer.mozilla.org/en-US/docs/Web/API](https://developer.mozilla.org/en-US/docs/Web/API).** 12 | 13 | ## Description 14 | 15 | The `IdleDetector` interface of the Idle Detection API provides events 16 | indicating when the user is no longer interacting with their device or the 17 | screen has locked. 18 | 19 | This interface requires a secure context. 20 | 21 | ## Constructor 22 | 23 | Creates a new `IdleDetector` object. 24 | 25 | ## Properties 26 | 27 | **`IdleDetector.userState`** 28 | 29 | Returns either `"active"` to indicate that the user has interacted with the 30 | device within the threshold provided to `start()` or `"idle"` if they have not. 31 | This attribute returns `null` before `start()` is called. 32 | 33 | **`IdleDetector.screenState`** 34 | 35 | Returns either `"locked"` if the device's screen is locked or `"unlocked"` if it 36 | is not. This attribute returns `null` before `start()` is called. 37 | 38 | ## Events 39 | 40 | **`IdleDetector.onchange`** 41 | 42 | Called when the value of `userState` or `screenState` has changed. This method 43 | receives an `Event` object. 44 | 45 | ## Methods 46 | 47 | **`IdleDetector.requestPermission()`** 48 | 49 | Returns a `Promise` that resolves when the user has chosen whether or not to 50 | grant the origin access to their idle state. Resolves with `"granted"` on 51 | acceptance and `"denied"` on refusal. 52 | 53 | **`IdleDetector.start()`** 54 | 55 | Returns a `Promise` that resolves when the detector has started listening for 56 | changes in the user's idle state. `userState` and `screenState` are populated 57 | with their initial values. Takes an optional options object with the 58 | `threshold` in milliseconds where inactivity should be reported and `signal` 59 | for an `AbortSignal` to abort the idle detector. 60 | 61 | ## Examples 62 | 63 | The following example shows creating a detector and logging changes to the 64 | user's idle state. A button is used to get the necessary user activation before 65 | requesting permission. 66 | 67 | ```js 68 | const controller = new AbortController(); 69 | const signal = controller.signal; 70 | 71 | startButton.addEventListener('click', async () => { 72 | if (await IdleDetector.requestPermission() != "granted") { 73 | console.error("Idle detection permission denied."); 74 | return; 75 | } 76 | 77 | try { 78 | const idleDetector = new IdleDetector(); 79 | idleDetector.addEventListener('change', () => { 80 | const userState = idleDetector.userState; 81 | const screenState = idleDetector.screenState; 82 | console.log(`Idle change: ${userState}, ${screenState}.`); 83 | }); 84 | 85 | await idleDetector.start({ 86 | threshold: 60_000, 87 | signal, 88 | }); 89 | console.log('IdleDetector is active.'); 90 | } catch (err) { 91 | // Deal with initialization errors like permission denied, 92 | // running outside of top-level frame, etc. 93 | console.error(err.name, err.message); 94 | } 95 | }); 96 | 97 | stopButton.addEventListener('click', () => { 98 | controller.abort(); 99 | console.log('IdleDetector is stopped.'); 100 | }); 101 | ``` 102 | -------------------------------------------------------------------------------- /security-privacy-self-assessment.md: -------------------------------------------------------------------------------- 1 | https://www.w3.org/TR/security-privacy-questionnaire/ 2 | 3 | ### 3.1 Does this specification deal with personally-identifiable information? 4 | 5 | No. 6 | 7 | ### 3.2 Does this specification deal with high-value data? 8 | 9 | No. 10 | 11 | ### 3.3 Does this specification introduce new state for an origin that persists across browsing sessions? 12 | 13 | No. 14 | 15 | ### 3.4 Does this specification expose persistent, cross-origin state to the web? 16 | 17 | As not all device types may support a "lock" state, detecting such a state provides some additional distinguishing information about the user's device (i.e. potentially one bit of entropy for fingerprinting), but as this correlates with the native platform this is generally inferrable from information exposed by the user agent header already. 18 | 19 | ### 3.5 Does this specification expose any other data to an origin that it doesn’t currently have access to? 20 | 21 | Yes - the status of a "screen lock" or equivalent is not detectable by script today - other than e.g. [Wake Lock](https://w3c.github.io/wake-lock/) to detect the absence. 22 | 23 | Detection of "idle" vs. "active" can be done by script today by watching for UI events, but this is restricted to events within windows/frames controlled by the origin. 24 | 25 | The "idle" state can be described as "time since last user interface event" and is global state which, with this API, can be observed cross-origin. In one hypothetical attack, with sub-second detection it would be possible to infer keystrokes and thus guess passwords being entered in other windows. For this reason, idle time detection must be appropriately coarse. In another attack scenario different origins could collaborate to corrolate idle state change events on the back-end, allowing identification of a user session across origins. For this reason, this capability must be limited to a small set of trustworthy origins by limiting it to top-level frames with notification permission. 26 | 27 | ### 3.6 Does this specification enable new script execution/loading mechanisms? 28 | 29 | No. 30 | 31 | ### 3.7 Does this specification allow an origin access to a user’s location? 32 | 33 | No. 34 | 35 | ### 3.8 Does this specification allow an origin access to sensors on a user’s device? 36 | 37 | Not directly. A device may use sensors to control lock state (e.g. proximity sensor to lock, fingerprint sensor to unlock) but the sensor data itself is not exposed. 38 | 39 | ### 3.9 Does this specification allow an origin access to aspects of a user’s local computing environment? 40 | 41 | See the answer to 3.5 above. The user's idle state is an aspect of their local computing environment which extends beyond the boundaries of a single site. 42 | 43 | ### 3.10 Does this specification allow an origin access to other devices? 44 | 45 | No. 46 | 47 | ### 3.11 Does this specification allow an origin some measure of control over a user agent’s native UI? 48 | 49 | No. 50 | 51 | ### 3.12 Does this specification expose temporary identifiers to the web? 52 | 53 | No. 54 | 55 | ### 3.13 Does this specification distinguish between behavior in first-party and third-party contexts? 56 | 57 | This capability is not allowed in third-party contexts. 58 | 59 | ### 3.14 How should this specification work in the context of a user agent’s "incognito" mode? 60 | 61 | This capability should be disabled in "incognito" modes to avoid exposing the same global state to two content inside and outside of "incognito" mode simultaneously. 62 | 63 | ### 3.15 Does this specification persist data to a user’s local device? 64 | 65 | No. 66 | 67 | ### 3.16 Does this specification have a "Security Considerations" and "Privacy Considerations" section? 68 | 69 | _Not yet!_ 70 | 71 | **TODO: Make sure this is included when the spec is written.** 72 | 73 | ### 3.17 Does this specification allow downgrading default security characteristics? 74 | 75 | No. 76 | -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": [80485] 3 | , "contacts": ["cwilso"] 4 | , "repo-type": "cg-report" 5 | } 6 | --------------------------------------------------------------------------------