├── .nojekyll ├── .pr-preview.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── PRIVACY_AND_SECURITY.md ├── README.md ├── index.html ├── isInputPending-1.jpeg ├── isInputPending-2.jpeg └── w3c.json /.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WICG/is-input-pending/fb2f76822168481cfa5e480156da826c44b7af67/.nojekyll -------------------------------------------------------------------------------- /.pr-preview.json: -------------------------------------------------------------------------------- 1 | { 2 | "src_file": "index.html", 3 | "type": "respec" 4 | } 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | All documentation, code and communication under this repository are covered by the [W3C Code of Ethics and Professional Conduct](https://www.w3.org/Consortium/cepc/). 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /PRIVACY_AND_SECURITY.md: -------------------------------------------------------------------------------- 1 | # Answers to [Security and Privacy Questionnaire](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 | 8 | ### 3.2 Does this specification deal with high-value data? 9 | 10 | No. 11 | 12 | 13 | ### 3.3 Does this specification introduce new state for an origin that persists across browsing sessions? 14 | 15 | No. 16 | 17 | 18 | ### 3.4 Does this specification expose persistent, cross-origin state to the web? 19 | 20 | No. 21 | 22 | 23 | ### 3.5 Does this specification expose any other data to an origin that it doesn’t currently have access to? 24 | 25 | No. However, implementors must take care to ensure that input detected by isInputPending does not result in the dispatch of an event on a cross-origin event target (e.g. an iframe). 26 | 27 | 28 | ### 3.6 Does this specification enable new script execution/loading mechanisms? 29 | 30 | No. 31 | 32 | 33 | ### 3.7 Does this specification allow an origin access to a user’s location? 34 | 35 | No. 36 | 37 | 38 | ### 3.8 Does this specification allow an origin access to sensors on a user’s device? 39 | 40 | No. 41 | 42 | 43 | ### 3.9 Does this specification allow an origin access to aspects of a user’s local computing environment? 44 | 45 | No. 46 | 47 | 48 | ### 3.10 Does this specification allow an origin access to other devices? 49 | 50 | No. 51 | 52 | 53 | ### 3.11 Does this specification allow an origin some measure of control over a user agent’s native UI? 54 | 55 | No. 56 | 57 | 58 | ### 3.12 Does this specification expose temporary identifiers to the web? 59 | 60 | No. 61 | 62 | 63 | ### 3.13 Does this specification distinguish between behavior in first-party and third-party contexts? 64 | 65 | No. 66 | 67 | 68 | ### 3.14 How should this specification work in the context of a user agent’s "incognito" mode? 69 | 70 | Semantics should be unchanged. 71 | 72 | 73 | ### 3.15 Does this specification persist data to a user’s local device? 74 | 75 | No. 76 | 77 | 78 | ### 3.16 Does this specification have a "Security Considerations" and "Privacy Considerations" section? 79 | 80 | Yes. 81 | 82 | 83 | ### 3.17 Does this specification allow downgrading default security characteristics? 84 | 85 | No. 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # isInputPending 2 | 3 | ## The Problem 4 | Today, developers must tradeoff how fast they can accomplish large blocks of 5 | work and how responsive they are to user input. For example, during page load, 6 | there may be a set of components and scripts to initialize. These are often 7 | ordered by priority: for example, first installing event handlers on primary 8 | buttons, then a search box, then a messaging widget, and then finally moving on 9 | to analytics scripts and ads. 10 | 11 | In this example, a developer can either optimize for: 12 | * Completing all work as fast as possible. 13 | * For example, we want the messaging widget to be initialized by the time the 14 | user interacts with it. 15 | * Responding to user input as fast as possible. 16 | * For example, when the user taps one of our primary buttons, we don't want to 17 | block until our whole page is ready to go before responding. 18 | 19 | Completing all work as fast as possible is easy, the developer can simply 20 | execute all the work in the minimal number of tasks. 21 | 22 | Responding to user input as fast as possible today requires paying some 23 | performance overhead, and minimizing that performance overhead is quite 24 | complicated. The most straight forward approach is to perform a unit of work, 25 | and then continue the work in a macrotask such as `setTimeout(..., 0)`. 26 | 27 | The performance overhead of this approach comes from a few sources: 28 | * Inherent overhead in posting a task. 29 | * Task throttling from the browser. 30 | * Execution of other browser work, such as rendering, postponing task execution. 31 | * Sometimes this is desirable, to get something visible on screen sooner. If 32 | we're executing script which is required to display our app however, this 33 | isn't desirable. 34 | 35 | ## Proposal 36 | 37 | In order to enable developers to complete their work as fast as possible if the 38 | user isn't interacting, but respond to user input as fast as possible if input 39 | occurs, we propose adding a new `navigator.scheduling.isInputPending()` API. 40 | This API takes an IsInputPendingOptions dictionary, which may be configured to 41 | include discrete input (e.g. mouse clicks, key presses), or discrete _and_ 42 | continuous input (e.g. mouse movement, touch dragging). If given no options 43 | object, the default is the former. 44 | 45 | See the draft specification [here](https://wicg.github.io/is-input-pending/). 46 | 47 | ## Example 48 | 49 | Using `navigator.scheduling.isInputPending()` requires having some way to 50 | schedule tasks. We anticipate most adoption coming from frameworks and large 51 | sites. However, if you have a list of tasks that need executing, adoption is 52 | very simple. 53 | 54 | ```javascript 55 | let taskQueue = [task1, task2, ...]; 56 | 57 | const options = { 58 | includeContinuous: false, 59 | }; 60 | 61 | function doWork() { 62 | while (let task = taskQueue.pop()) { 63 | task.execute(); 64 | if (navigator.scheduling.isInputPending(options)) { 65 | setTimeout(doWork, 0); 66 | break; 67 | } 68 | } 69 | } 70 | 71 | doWork(); 72 | ``` 73 | 74 | ## What about `requestIdleCallback`? 75 | `requestIdleCallback` has the capacity to solve similar problems, but only when the work being done is low priority. It waits for an idle period before executing the associated work. In the example of registering behavior for a variety of UI components, we can't wait for an idle period before continuing to register additional UI components. 76 | 77 | We could use an API shape similar to `requestIdleCallback`, which instead posted a normal priority task, and provided a parallel to `IdleDeadline.timeRemaining()` which returned 0 if there was pending user input. However, this forces clients to use a specific method of posting a task, which is less flexible than the more generic `scheduling.isInputPending()` proposal. 78 | 79 | ## How slow is posting a task, really? 80 | 81 | Breaking tasks up into small chunks has some overhead, but is it really enough to warrant a new API? 82 | 83 | The web is different from most platforms due to it's compositional nature. The overhead of posting a task isn't merely the scheduling overhead. Posting a task may involve being interrupted by arbitrary third party content on the page. For many pages, it isn't feasible to regularly yield to third party script in order to repond to input quickly. 84 | 85 | ## Constraints 86 | 87 | Ideally, a solution to this problem would meet the following constraints: 88 | 1. Enable stopping JS execution when input arrives. 89 | 1. Enable efficiently resuming JS execution once input has been processed. 90 | 1. Provide the browser a way to stop the JS from executing when input isn’t 91 | pending, in case the browser decides it really needs to draw something. 92 | 1. Not require paying the cost of posting a task unless the browser has 93 | pending high priority work 94 | 1. Prevent other JS from running between when the script stops and resumes JS 95 | execution 96 | * This excludes JS associated with browser work like event processing, rAF 97 | callbacks etc 98 | 99 | This proposal focuses on constraints 1, 3 & 4, and ignores 2 and 5, which will 100 | be addressed independently. 101 | 102 | The fifth constraint is interesting - in order for work which is incentivized to 103 | finish quickly (e.g., ads script) to be able to adopt this API and improve 104 | responsiveness to input, we need some way to prevent arbitrary javascript from 105 | executing between when script yields and when it is rescheduled. 106 | 107 | ## Privacy and Security 108 | 109 | The primary concerns with this API stem from the correctness of input event attribution. UAs must make sure they don't leak information about pending events dispatched to other frames by performing necessary hit-testing and focus attribution. For maximum utility, this should be done off the event loop so that running JS does not have to yield in order to detect input. 110 | 111 | Implementors must take care to avoid leaking inputs across different origins, particularly during iframe movement or focus changes. Under no circumstances should input detected by an agent of origin A be dispatched to an agent of origin B -- this would allow origin A to determine if origin B had a DOM event dispatched, which violates the same-origin policy. Implementors may work around this by being consistent with hit testing dispatch for inputs detected using isInputPending as well as for DOM event dispatch, or discarding events where inconsistencies are inevitable (e.g. cases where off-main-thread hit testing is insufficient). 112 | 113 | User agents may want consider a combination of off-main-thread based hit testing (e.g. done on the compositor thread, if present) and keyboard focus to determine the appropriate document to mark as having input pending. By performing hit testing online, calls to isInputPending can be made faster, safer from timing attacks, and thus more effective for scheduling purposes. 114 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |30 | This document describes an API for detecting if there are pending input events that script might be delaying from firing by continuing to run. 31 |
32 |35 | This document is in the draft stage and has yet to be submitted to a working group for approval. 36 |
37 |42 | When running script that's required to display something there is a trade off that developers need to make today. If a script might take a long time to run and a user provides some sort of input while this is happening, then the user agent would need to wait until the script has finished before dispatching the input event. Having a lot of lag before responding to an input event is not a great user experience, so developers will often break up long scripts into smaller chunks in order to allow the user agent to dispatch events between the chunks. Each time the script yields it will need to somehow post a message, call a combination of requestAnimation frame and requestIdleCallback, or do something else in order to make sure it both truly yields to allow for events to be dispatched and then that it can get called again after yielding. Even in the best case scenario, each time the script yields it can take many milliseconds before it gets a chance to run again. So unfortunately, this is also not a great user experience since in part now the initial script gets delayed by quite a bit since it needs to yield so much. 43 |
44 |51 | The goal of the isInputPending api is that it will now allow developers to eliminate this trade off. Instead of totally yielding to the user agent and having to incur the cost of one or multiple event loops after yielding, long running script can now run to its completion while still remaining responsive. 52 |
53 |63 | If a developer has a list of tasks that need executing, then adoption is quite simple. 64 |
65 |66 | let taskQueue = [task1, task2, ...]; 67 | const options = {includeContinuous: true}; 68 | 69 | function doWork() { 70 | while (let task = taskQueue.pop()) { 71 | task.execute(); 72 | if (navigator.scheduling.isInputPending(options)) { 73 | setTimeout(doWork, 0); 74 | break; 75 | } 76 | } 77 | } 78 | 79 | doWork(); 80 |81 |
The IsInputPendingOptions
type represents an options dictionary that is used by isInputPending.
88 | dictionary IsInputPendingOptions { 89 | boolean includeContinuous = false; 90 | }; 91 |92 |
97 | [Exposed=Window] interface Scheduling { 98 | boolean isInputPending(optional IsInputPendingOptions isInputPendingOptions = {}); 99 | }; 100 |101 |
The isInputPending
method returns a value based on the options set listed in isInputPendingOptions
. If the isInputPending
method is called then the user agent MUST run the following steps:
The overall goal for this api is to allow user agents to return true if they think an event may end up getting dispatched to a unit of same origin browsing context soon, and not only if an event is already placed into the queue. This may mean that a user agent may look into the current configuration of iframes, other events in the queue, or other information when making decisions in step 2.1. For example, there may be cases where if a mousedown event is dispatched then a click event may be fired for this frame after the mousedown event. However if the mousedown event is canceled then the user agent knows that the click event will not be fired. In this case the user agent should return 'true' for a 'click' input before the canceling mousedown event, but after it knows that a click will not be fired it should return false.
119 | 120 |The specification for the isInputPending method is intentionally written in such a way that a user agent may return false for any reason. This is so user agents may include their own security and performance measures while implementing this api and remain compliant with this specification. For example, some user agents may not know which unit of same origin browsing context they would dispatch an event to without a prohibitively expensive computation, in that case it would be okay for the user agent to return false. It's never okay for a user agent to return true when it's unsure that the unit of same origin browsing context querying isInputPending is not the same one where the event will be dispatched. Returning true for an event that may get dispatched to a different origin browsing context could allow for parent iframes to observe events in different origin child iframes, which would be a violation of security.
121 |126 | partial interface Navigator { 127 | readonly attribute Scheduling scheduling; 128 | }; 129 |130 |
The distinction between continuous events and other event types exists in order to prevent script from yielding too often. Under normal circumstances, a user moving their pointer around on a document wouldn’t expect that to have any effect on performance so by default these events are excluded from isInputPending. However, there may be times when a document wants to yield for these events.
134 |An event MUST be considered a continuous event if it is a trusted event of type mousemove, wheel, touchmove, drag, pointermove, or pointerrawupdate or a child of any of those. A user agent MAY consider other trusted events that are of a specific type and are children of UIEvent or TouchEvent to be a continuous event as long as those specific types are consistent for that user agent and that all events of those types or children of those types are considered continuous events.
137 |All of event types that are considered to be continuous events are specified in such a way where they are fired successively, and the user agent is encouraged to fire the events at a rate specific for the user agent and platform. The language about a user agent being able to include other event types is there so user agents that have nonstandard events that are defined in a similar manner may also be considered to be continuous events.
138 |A task MUST be considered a continuous event task if it will dispatch an event that is a continuous event.
142 |User agents must ensure that input dispatched to cross-origin frames is not detectable- allowing an origin to monitor events on another origin could allow for a range of attacks. The specification is written in such a way that user agents may return false for any reason from isInputPending, in order to mitigate such attacks.
147 |Implementors may want to consider a combination of off-main-thread based hit testing (e.g. done on the compositor thread, if present) and keyboard focus to determine the appropriate document to mark as having input pending. By performing hit testing asynchronously, calls to isInputPending can be made faster, safer from timing attacks, and thus more effective for scheduling purposes.
148 |The case of a child cross-origin subframe is even trickier to deal with, as the frame that gets an event may change based on what script is currently doing. In some cases a malicious cross origin frame could attempt to bring focus to itself in order to look for input events that a user could have intended to be sent to the parent frame. For example, a malicious origin could attempt to get a user to click on a child iframe by moving it around the screen. Some user agents mitigate this by discarding input events on recently moved frames. User agents should pay special attention to this case, and add appropriate countermeasures to their implementations of this api based on their architecture.
149 |