Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update navigator interface and event targets #116

Merged
merged 3 commits into from
Feb 2, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 59 additions & 48 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,12 @@ interface VRDisplay : EventTarget {
* created without preserveDrawingBuffer set to true will be cleared.
*/
void submitFrame();

attribute EventHandler onactivate;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we just call this mount and unmount? the verbiage is confusing (see #163).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if there's an event for this, IMO there should be a boolean (isMounted) à la isConnected and isPresenting. for example, during page navigation, I wouldn't expect activate to fire, but I may want to know (and can't assume) whether a headset is (still) worn.

attribute EventHandler ondeactivate;
attribute EventHandler onblur;
attribute EventHandler onfocus;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given we have blur and focus, it's odd there's no visibilitychange (especially since we have a presentchange).

FWIW, because the original blur and focus events weren't meaningful enough, browsers introduced the Page Visibility API, which gives you document.visibilityState, document.hidden, document.onvisibilitychange.

attribute EventHandler onpresentchange;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems a bit odd that we have presentchange as opposed to presentbegin and presentend (or presententer and presentexit)? (Also, see comment above.)

Copy link
Member Author

@toji toji Jan 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was modeled after fullscreenchange and pointerlockchange, which seemed to me like similar state transitions. I also see that the Page Visibility API include visibilitychange, so I'd say this verbiage is consistent with the rest of the web platform.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see different tenses and inconsistent patterns of usage and naming (and method signatures/args) with event names, booleans vs. state enums, and property types even with the newer APIs. The Presentation API, Web MIDI, Web USB, and Web Bluetooth in particular all handle input/output a little differently.

Would like to get @annevk, et. al.'s opinions on these, so we can change the API interfaces now to be as closest to the latest best practice today.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be a little easier to discuss design in dedicated threads, rather than subthreads of a PR.

What matters here are the use cases and what the typical programming experience would be like. Having said that, exposing state transitions through promises is the latest trend and if that makes sense for your API that'd the best to align on.

};
</pre>

Expand Down Expand Up @@ -215,6 +221,24 @@ Returns an array with the {{VRLayer}} currently being presented. MUST return an
<dfn method for="VRDisplay">submitFrame()</dfn>
Captures the current state of the {{VRLayer}} currently being presented and displays it on the {{VRDisplay}}. It is assumed that the frame was rendered using the {{VRPose}} and matrices provided by the last call to {{getFrameData()}}. If {{getFrameData()}} was not called prior to calling {{submitFrame()}} the user agent MAY warn the user of potentially malformed visuals or prevent the frame from being shown at all.

### Events ### {#vrdisplay-events}

The UA MUST provide the following new events. The corresponding events must be of type {{VRDisplayEvent}} and must fire on a {{VRDisplay}} object. Registration for and firing of the events must follow the usual behavior of DOM4 Events.

<dfn event for="VRDisplay" id="vrdisplay-onactivate-event">onactivate</dfn>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these should not have the on; event names never have prefix

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since OpenVR uses the activate/deactivate terminology, you can ignore my other comment about mounted/unmounted.

A user agent MAY dispatch this event type to indicate that something has occured which suggests the {{VRDisplay}} should be presented to. For example, if the {{VRDisplay}} is capable of detecting when the user has put it on, this event SHOULD fire when they do so with the reason "mounted".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add a update or statechange event that fires when a property on the VRDisplay object changes? see this OpenVR event.

since there's no way to observe an object (anymore), this can be problematic when folks want to know when the property's values have changed but no event otherwise fired.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<dfn event for="VRDisplay" id="vrdisplay-ondeactivate-event">ondeactivate</dfn>
A user agent MAY dispatch this event type to indicate that something has occured which suggests the {{VRDisplay}} should exit presentation. For example, if the {{VRDisplay}} is capable of detecting when the user has taken it off, this event SHOULD fire when they do so with the reason "unmounted".

<dfn event for="VRDisplay" id="vrdisplay-onblur-event">onblur</dfn>
A user agent MAY dispatch this event type to indicate that presentation to the {{VRDisplay}} by the page is paused by the user agent, OS, or VR hardware. While a {{VRDisplay}} is blurred it does not lose it's presenting status ({{isPresenting}} continues to report true) but {{getFrameData()}} returns false without updating the provided {{VRFrameData}} and {{getPose()}} returns null. This is to prevent tracking while the user interacts with potentially sensitive UI. For example: A user agent SHOULD blur the presenting application when the user is typing a URL into the browser with a virtual keyboard, otherwise the presenting page may be able to guess the URL the user is entering by tracking their head motions.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will there be a reason in the event detail?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a "blur" reason and text indicating it must be used here.


<dfn event for="VRDisplay" id="vrdisplay-onfocus-event">onfocus</dfn>
A user agent MAY dispatch this event type to indicate that presentation to the {{VRDisplay}} by the page has resumed after being blurred.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will there be a reason in the event detail?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a "focus" reason and text indicating it must be used here.


<dfn event for="VRDisplay" id="vrdisplay-onpresentchange-event">onpresentchange</dfn>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If presentstart and presentend are going to be the reasons, might we want to do the same thing for blur/focus (i.e., merge to a single visibilitychange event) and activate/deactivate (e.g., a connectionchange event)?

A user agent MUST dispatch this event type to indicate that the {{VRDisplay}} has begun or ended VR presentation. This event should not fire on subsequent calls to {{requestPresent()}} after the {{VRDisplay}} has already begun VR presentation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will there be a reason in the event detail?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added "presentstart" and "presentend" reasons and text indicating they must be used here.


<div class="example">
The following code demonstrates presenting a simple rendering loop to a {{VRDisplay}}.
Expand Down Expand Up @@ -559,31 +583,56 @@ Width of the play-area bounds in meters. The bounds are defined as an axis-align
<dfn attribute for="VRStageParameters">sizeZ</dfn>
Depth of the play-area bounds in meters. The bounds are defined as an axis-aligned rectangle on the floor. The center of the rectangle is at (0,0,0) in standing-space coordinates. These bounds are defined for safety purposes. Content should not require the user to move beyond these bounds; however, it is possible for the user to ignore the bounds resulting in position values outside of this rectangle.


## Navigator Interface extension ## {#interface-navigator}
## VR Interface ## {#interface-vr}

<pre class="idl">
partial interface Navigator {
Promise&lt;sequence&lt;VRDisplay&gt;&gt; getVRDisplays();
readonly attribute FrozenArray&lt;VRDisplay&gt; activeVRDisplays;
interface VR : EventTarget {
Promise&lt;sequence&lt;VRDisplay&gt;&gt; getDisplays();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider: getDevices, but only if we have a good reason for it. Same for active* if we change the getter here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what you mean here: We did use the terminology "devices" previously, but moved away from that with WebVR 1.0. I don't see any reason to stop using the term "Displays" at this point.

readonly attribute FrozenArray&lt;VRDisplay&gt; activeDisplays;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been thinking about this for a while. I'm not sure how useful this attribute is, especially considering the liveness of the VRDisplay instances (they have isConnected and isPresenting attributes).

how about instead having navigator.vr.referringDisplays(), which would resolve the displays that are actively being presented to and set upon page navigation, and the next page could look at those to know which to present to (though in most cases, only one)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't make sense to me. I'm unclear of the use case, and it's ambiguous.

See my proposed navigator.vr.referringDisplay(s) idea: https://gist.github.com/cvan/9c0a2e6e1a6839816128aeb461eb3184#file-sample-usage-js-L21-L32


À la navigator.languages), I suggest having introducing a navigator.vr.preferredDisplays, an array of display name strings for user-preferred VR headsets.

If we think of VR devices/displays à la languages/locales, you can imagine a user flow something like this:

  1. When a user first plugs in a headset, the UA remembers that headset name in some internal persisted list of "known VR displays" in that user's UA profile.
  2. A user opens the UA's "Preferences" to add/remove/rearrange the preferred headsets to use when display WebVR content.
  3. When a requests any page, the browser now starts sending a Accept-VR-Display request header (e.g., Accept-VR-Display: HTC Vive, Oculus Rift) – à la the Accept-Language request header [e.g., Accept-Language: en-US, fr; q=0.7]).
  4. If a page's response header is Content-VR-Display: Gear VR, HTC Vive (or a <meta> tag: <meta http-equiv="content-vr-display" content="available HTC Vive, Oculus Rift"> – [or CSP-Style: <meta http-equiv="Content-VR-Policy" content="default-display 'HTC Vive'; supported-displays 'HTC Vive', 'Oculus Rift'; prefetch-navigation-to 'https://*.neocities.org'">]), then the UA knows which displays are supported by this WebVR page (or scenes within the page, if we're going to get complicated).
  5. The UA now knows that when the page loads, the user may want to enter VR by clicking on some in-content button that calls <VRDisplay>.requestPresent(…) (where <VRDisplay> would be a VR Display that exists in navigator.vr.preferredDisplays and is also supported by the content creator of the WebVR scene).

Here's what I mocked up in Firefox's Preferences, for example:

screen shot 2017-01-26 at 5 50 52 pm

screen shot 2017-01-26 at 6 32 47 pm


If while a WebVR source is presenting to a headset and a window navigation occurs, the UA can ought to *fire the navigate event on navigator.vr in addition to pushing the display to navigator.vr.referringDisplays (i.e., attribute FrozenArray<VRDisplay> referringDisplays).

A developer could then check that and immediately request presentation back to the display the user was using before/during the page navigation.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think allowing sites to say which headsets they support would be as dangerous as letting them say which browsers or OS they support and increase fragmentation

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was giving some thought to this as well, since it's going to be very difficult to get pages to always properly handle multiple headsets. (Even my WebVR.info samples don't, though there's comments in the source alluding to the fact that you should.) I think it's going to be very very very very common to have sited just use display[0].

It seems like the dialog that you mocked up above could be pretty effective if the only thing it did was control the order that headsets are exposed in through getDisplays(). Want to use your Vive instead of your Rift? Make it always show up as display[0]. Sites that do the dumb thing will almost always pick it up, sites that are better behaved will allow you to pick anyway.

(A page can still break it by actively filtering out displays unless displayName.includes('Rift'), but at that point they're just being actively user hostile and I don't think we can help them much.)


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was suggesting adding navigator.vr.getAvailability (as a replacement for the navigator.vrEnabled property, which should obviously be removed).

This would respect the Feature Policy (which supports <iframe enable="vr" src="https://example.com/scene.html"></iframe> – see issue #86).

Besides the aforementioned <iframe> case, there are cases (e.g., browser extension) when a UA may not want to (or cannot attempt to) expose VR capabilities. (A VR display could still be connected, but the page may not be privileged to use it.)

Some similar methods in other web APIs:

Alternatively, instead of a Promise<boolean> getAvailability(), we could have the following:

    readonly attribute VRAvailability availability;
    attribute EventHandler onavailabilitychange;
    enum VRAvailability {
        'available',
        'unavailable',
        'unknown'
    };

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM, thanks for the research and suggestion! I'll make the update soon.

attribute EventHandler ondisplayconnect;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also [SecureContext]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps we add a displayadd event (à la OpenVR's IServerDriverHost. TrackedDeviceAdded)?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cvan , Are you suggesting we add displayadd in addition to displayconnect or replace displayconnect with displayadd? In your mind, what is the difference between 'adding' a display and 'connecting' a display?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, replace. I find the displayadd verbiage make a bit more sense.

attribute EventHandler ondisplaydisconnect;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add onavailabilitychange (imagine this could change based on a UA-presented dialogue via the Permissions API, for example).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see Web Bluetooth's, for measure

attribute EventHandler onnavigate;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would love to improve the whole Web platform and just properly support a navigate event in the form of @jakearchibald's latest Navigation Transitions proposal. I understand the desire to not cause cross-spec dependencies and being able to nicely confine the WebVR API surface area to this navigator.vr namespace.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this event integrate with HTML? Is it properly hooked?

};
</pre>

### Attributes ### {#navigator-attributes}
### Attributes ### {#vr-attributes}

<dfn method for="Navigator" id="navigator-getvrdisplays-attribute">getVRDisplays()</dfn>
<dfn method for="VR" id="vr-getdisplays-attribute">getDisplays()</dfn>
Return a Promise which resolves to a list of available {{VRDisplay}}s.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

attribute or method?


<dfn attribute for="Navigator" id="navigator-activevrdisplays-attribute">activeVRDisplays</dfn>
{{activeVRDisplays}} includes every {{VRDisplay}} that is currently presenting.
<dfn attribute for="VR" id="vr-activedisplays-attribute">activeDisplays</dfn>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this? why can't we just use getDisplays() and look at displays[0].isPresenting?

{{activeDisplays}} includes every {{VRDisplay}} that is currently presenting.

### Events ### {#vr-events}

The UA MUST provide the following new events. The corresponding events must be of type {{VRDisplayEvent}} and must fire on the {{VR}} object. Registration for and firing of the events must follow the usual behavior of DOM4 Events.

<dfn event for="VR" id="vr-ondisplayconnect-event">ondisplayconnect</dfn>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also remove the on prefix. that's for the event handler methods.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not to complicate things, but just making sure that all the ergonomics with the events are consistent? should there be a displayconnectionchange event?

A user agent MAY dispatch this event type to indicate that a {{VRDisplay}} has been connected.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you use "UA" above. we should be consistent.


<dfn event for="VR" id="vr-ondisplaydisconnect-event">ondisplaydisconnect</dfn>
A user agent MAY dispatch this event type to indicate that a {{VRDisplay}} has been disconnected.

<dfn event for="VR" id="vr-ondisplaynavigate-event">onnavigate</dfn>
A user agent MAY dispatch this event type to indicate that the current page has been navigated to from a page that was actively presenting VR content. The current page can call {{requestPresent()}} in response to this event in order to stay in VR presentation mode.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the term page sufficient? it's technically called a browsing context (or document), right?



## Navigator Interface extension ## {#interface-navigator}

<pre class="idl">
partial interface Navigator {
readonly attribute VR vr;
};
</pre>

<div class="example">
The following code finds the first available {{VRDisplay}}.

<pre class="lang-js">
var vrDisplay;

navigator.getVRDisplays().then(function (displays) {
navigator.vr.getDisplays().then(function (displays) {
// Use the first display in the array if one is available. If multiple
// displays are present, you may want to present the user with a way to
// select which display to use.
Expand Down Expand Up @@ -642,44 +691,6 @@ The {{VRDisplay}} associated with this event.
<dfn attribute for="VRDisplayEvent">reason</dfn>
{{VRDisplayEventReason}} describing why this event has has been fired.


## Window Interface extension ## {#interface-window}

<pre class="idl">
partial interface Window {
attribute EventHandler onvrdisplayconnect;
attribute EventHandler onvrdisplaydisconnect;
attribute EventHandler onvrdisplayactivate;
attribute EventHandler onvrdisplaydeactivate;
attribute EventHandler onvrdisplayblur;
attribute EventHandler onvrdisplayfocus;
attribute EventHandler onvrdisplaypresentchange;
};
</pre>

User agents implementing this specification MUST provide the following new DOM events. The corresponding events must be of type {{VRDisplayEvent}} and must fire on the window object. Registration for and firing of the events must follow the usual behavior of DOM4 Events.

<dfn event for="Window" id="window-vrdisplayconnect-event">onvrdisplayconnect</dfn>
A user agent MAY dispatch this event type to indicate that a {{VRDisplay}} has been connected.

<dfn event for="Window" id="window-vrdisplaydisconnect-event">onvrdisplaydisconnect</dfn>
A user agent MAY dispatch this event type to indicate that a {{VRDisplay}} has been disconnected.

<dfn event for="Window" id="window-vrdisplayactivate-event">onvrdisplayactivate</dfn>
A user agent MAY dispatch this event type to indicate that something has occured which suggests the {{VRDisplay}} should be presented to. For example, if the {{VRDisplay}} is capable of detecting when the user has put it on, this event SHOULD fire when they do so with the reason "mounted".

<dfn event for="Window" id="window-vrdisplaydeactivate-event">onvrdisplaydeactivate</dfn>
A user agent MAY dispatch this event type to indicate that something has occured which suggests the {{VRDisplay}} should exit presentation. For example, if the {{VRDisplay}} is capable of detecting when the user has taken it off, this event SHOULD fire when they do so with the reason "unmounted".

<dfn event for="Window" id="window-onvrdisplayblur-event">onvrdisplayblur</dfn>
A user agent MAY dispatch this event type to indicate that presentation to the display by the page is paused by the user agent, OS, or VR hardware. While a {{VRDisplay}} is blurred it does not lose it's presenting status ({{isPresenting}} continues to report true) but {{getFrameData()}} returns false without updating the provided {{VRFrameData}} and {{getPose()}} returns null. This is to prevent tracking while the user interacts with potentially sensitive UI. For example: A user agent SHOULD blur the presenting application when the user is typing a URL into the browser with a virtual keyboard, otherwise the presenting page may be able to guess the URL the user is entering by tracking their head motions.

<dfn event for="Window" id="window-onvrdisplayfocus-event">onvrdisplayfocus</dfn>
A user agent MAY dispatch this event type to indicate that presentation to the display by the page has resumed after being blurred.

<dfn event for="Window" id="window-vrdisplaypresentchange-event">onvrdisplaypresentchange</dfn>
A user agent MUST dispatch this event type to indicate that a {{VRDisplay}} has begun or ended VR presentation. This event should not fire on subsequent calls to {{requestPresent()}} after the {{VRDisplay}} has already begun VR presentation.

## Gamepad Interface extension ## {#interface-gamepad}

<pre class="idl">
Expand Down
Loading