aboutsummaryrefslogtreecommitdiffstats
path: root/components/script
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2017-01-09 06:39:45 -0800
committerGitHub <noreply@github.com>2017-01-09 06:39:45 -0800
commit518ef39cfd429082dd8dc0d5b13e2db637d08a53 (patch)
tree79370f5a2c0a7a9677dbe04078a399218bb39ef8 /components/script
parent23265321988113b290aff190f859be2290a81545 (diff)
parentc5705bff5003675d6d266c698653834027a78227 (diff)
downloadservo-518ef39cfd429082dd8dc0d5b13e2db637d08a53.tar.gz
servo-518ef39cfd429082dd8dc0d5b13e2db637d08a53.zip
Auto merge of #14618 - MortimerGoro:webvr_api, r=larsbergstrom,emilio,jdm,nox,asajeffrey,cvan
WebVR API Implementation <!-- Please describe your changes on the following line: --> WebVR API Implementation with HTC Vive support on Windows. The current implementations only enables the WebVR support on Windows. In other platforms the API is available on JavaScript but navigator.vr.getDisplays() returns an empty array. This will change when we add support for more VR providers and platforms ;) Info about the architecture: https://blog.mozvr.com/webvr-servo-architecture-and-latency-optimizations/ --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [X] There are tests for these changes OR - [ ] These changes do not require tests because _____ Proprietary openvr.dll must be copied next to servo.exe in order to test on HTC Vive (https://github.com/ValveSoftware/openvr/tree/master/bin/win64) I have added some of the official WebVR samples for testing. Switch on your headset and run: mach run tests/html/webvr/room-scale.html <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/14618) <!-- Reviewable:end -->
Diffstat (limited to 'components/script')
-rw-r--r--components/script/Cargo.toml2
-rw-r--r--components/script/dom/bindings/conversions.rs58
-rw-r--r--components/script/dom/mod.rs9
-rw-r--r--components/script/dom/navigator.rs16
-rw-r--r--components/script/dom/vr.rs160
-rw-r--r--components/script/dom/vrdisplay.rs607
-rw-r--r--components/script/dom/vrdisplaycapabilities.rs62
-rw-r--r--components/script/dom/vrdisplayevent.rs116
-rw-r--r--components/script/dom/vreyeparameters.rs75
-rw-r--r--components/script/dom/vrfieldofview.rs58
-rw-r--r--components/script/dom/vrframedata.rs122
-rw-r--r--components/script/dom/vrpose.rs133
-rw-r--r--components/script/dom/vrstageparameters.rs75
-rw-r--r--components/script/dom/webidls/Navigator.webidl5
-rw-r--r--components/script/dom/webidls/VR.webidl10
-rw-r--r--components/script/dom/webidls/VRDisplay.webidl131
-rw-r--r--components/script/dom/webidls/VRDisplayCapabilities.webidl13
-rw-r--r--components/script/dom/webidls/VRDisplayEvent.webidl23
-rw-r--r--components/script/dom/webidls/VREyeParameters.webidl13
-rw-r--r--components/script/dom/webidls/VRFieldOfView.webidl13
-rw-r--r--components/script/dom/webidls/VRFrameData.webidl15
-rw-r--r--components/script/dom/webidls/VRLayer.webidl13
-rw-r--r--components/script/dom/webidls/VRPose.webidl14
-rw-r--r--components/script/dom/webidls/VRStageParameters.webidl11
-rw-r--r--components/script/dom/window.rs13
-rw-r--r--components/script/lib.rs1
-rw-r--r--components/script/script_runtime.rs1
-rw-r--r--components/script/script_thread.rs21
28 files changed, 1788 insertions, 2 deletions
diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml
index 10ea03bc548..f980c2f9226 100644
--- a/components/script/Cargo.toml
+++ b/components/script/Cargo.toml
@@ -82,6 +82,8 @@ url = {version = "1.2", features = ["heap_size", "query_encoding"]}
uuid = {version = "0.3.1", features = ["v4"]}
websocket = "0.17"
xml5ever = {version = "0.3.1", features = ["unstable"]}
+webvr = {path = "../webvr"}
+webvr_traits = {path = "../webvr_traits"}
[dependencies.webrender_traits]
git = "https://github.com/servo/webrender"
diff --git a/components/script/dom/bindings/conversions.rs b/components/script/dom/bindings/conversions.rs
index 18a2deb8f0f..577cecb2fd4 100644
--- a/components/script/dom/bindings/conversions.rs
+++ b/components/script/dom/bindings/conversions.rs
@@ -51,6 +51,9 @@ use js::jsapi::{JSObject, JSString, JS_GetArrayBufferViewType};
use js::jsapi::{JS_GetLatin1StringCharsAndLength, JS_GetObjectAsArrayBuffer, JS_GetObjectAsArrayBufferView};
use js::jsapi::{JS_GetReservedSlot, JS_GetTwoByteStringCharsAndLength};
use js::jsapi::{JS_IsArrayObject, JS_NewStringCopyN, JS_StringHasLatin1Chars};
+use js::jsapi::{JS_NewFloat32Array, JS_NewFloat64Array};
+use js::jsapi::{JS_NewInt8Array, JS_NewInt16Array, JS_NewInt32Array};
+use js::jsapi::{JS_NewUint8Array, JS_NewUint16Array, JS_NewUint32Array};
use js::jsapi::{MutableHandleValue, Type};
use js::jsval::{ObjectValue, StringValue};
use js::rust::{ToString, get_object_class, is_dom_class, is_dom_object, maybe_wrap_value};
@@ -463,6 +466,9 @@ pub unsafe trait ArrayBufferViewContents: Clone {
/// Check if the JS ArrayBufferView type is compatible with the implementor of the
/// trait
fn is_type_compatible(ty: Type) -> bool;
+
+ /// Creates a typed array
+ unsafe fn new(cx: *mut JSContext, num: u32) -> *mut JSObject;
}
unsafe impl ArrayBufferViewContents for u8 {
@@ -473,47 +479,79 @@ unsafe impl ArrayBufferViewContents for u8 {
_ => false,
}
}
+
+ unsafe fn new(cx: *mut JSContext, num: u32) -> *mut JSObject {
+ JS_NewUint8Array(cx, num)
+ }
}
unsafe impl ArrayBufferViewContents for i8 {
fn is_type_compatible(ty: Type) -> bool {
ty as i32 == Type::Int8 as i32
}
+
+ unsafe fn new(cx: *mut JSContext, num: u32) -> *mut JSObject {
+ JS_NewInt8Array(cx, num)
+ }
}
unsafe impl ArrayBufferViewContents for u16 {
fn is_type_compatible(ty: Type) -> bool {
ty as i32 == Type::Uint16 as i32
}
+
+ unsafe fn new(cx: *mut JSContext, num: u32) -> *mut JSObject {
+ JS_NewUint16Array(cx, num)
+ }
}
unsafe impl ArrayBufferViewContents for i16 {
fn is_type_compatible(ty: Type) -> bool {
ty as i32 == Type::Int16 as i32
}
+
+ unsafe fn new(cx: *mut JSContext, num: u32) -> *mut JSObject {
+ JS_NewInt16Array(cx, num)
+ }
}
unsafe impl ArrayBufferViewContents for u32 {
fn is_type_compatible(ty: Type) -> bool {
ty as i32 == Type::Uint32 as i32
}
+
+ unsafe fn new(cx: *mut JSContext, num: u32) -> *mut JSObject {
+ JS_NewUint32Array(cx, num)
+ }
}
unsafe impl ArrayBufferViewContents for i32 {
fn is_type_compatible(ty: Type) -> bool {
ty as i32 == Type::Int32 as i32
}
+
+ unsafe fn new(cx: *mut JSContext, num: u32) -> *mut JSObject {
+ JS_NewInt32Array(cx, num)
+ }
}
unsafe impl ArrayBufferViewContents for f32 {
fn is_type_compatible(ty: Type) -> bool {
ty as i32 == Type::Float32 as i32
}
+
+ unsafe fn new(cx: *mut JSContext, num: u32) -> *mut JSObject {
+ JS_NewFloat32Array(cx, num)
+ }
}
unsafe impl ArrayBufferViewContents for f64 {
fn is_type_compatible(ty: Type) -> bool {
ty as i32 == Type::Float64 as i32
}
+
+ unsafe fn new(cx: *mut JSContext, num: u32) -> *mut JSObject {
+ JS_NewFloat64Array(cx, num)
+ }
}
/// Returns a mutable slice of the Array Buffer View data, viewed as T, without
@@ -595,3 +633,23 @@ pub unsafe fn is_array_like(cx: *mut JSContext, value: HandleValue) -> bool {
assert!(JS_IsArrayObject(cx, value, &mut result));
result
}
+
+/// Creates a typed JS array from a Rust slice
+pub unsafe fn slice_to_array_buffer_view<T>(cx: *mut JSContext, data: &[T]) -> *mut JSObject
+ where T: ArrayBufferViewContents
+{
+ let js_object = T::new(cx, data.len() as u32);
+ assert!(!js_object.is_null());
+ update_array_buffer_view(js_object, data);
+ js_object
+}
+
+/// Updates a typed JS array from a Rust slice
+pub unsafe fn update_array_buffer_view<T>(obj: *mut JSObject, data: &[T])
+ where T: ArrayBufferViewContents
+{
+ let mut buffer = array_buffer_view_data(obj);
+ if let Some(ref mut buffer) = buffer {
+ ptr::copy_nonoverlapping(&data[0], &mut buffer[0], data.len())
+ }
+}
diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs
index 4f94ac8309f..55b56977cd9 100644
--- a/components/script/dom/mod.rs
+++ b/components/script/dom/mod.rs
@@ -429,6 +429,15 @@ pub mod validation;
pub mod validitystate;
pub mod values;
pub mod virtualmethods;
+pub mod vr;
+pub mod vrdisplay;
+pub mod vrdisplaycapabilities;
+pub mod vrdisplayevent;
+pub mod vreyeparameters;
+pub mod vrfieldofview;
+pub mod vrframedata;
+pub mod vrpose;
+pub mod vrstageparameters;
pub mod webgl_validations;
pub mod webglactiveinfo;
pub mod webglbuffer;
diff --git a/components/script/dom/navigator.rs b/components/script/dom/navigator.rs
index b7d781f5eb0..dd9a1a789c6 100644
--- a/components/script/dom/navigator.rs
+++ b/components/script/dom/navigator.rs
@@ -12,7 +12,9 @@ use dom::mimetypearray::MimeTypeArray;
use dom::navigatorinfo;
use dom::pluginarray::PluginArray;
use dom::serviceworkercontainer::ServiceWorkerContainer;
+use dom::vr::VR;
use dom::window::Window;
+use script_traits::WebVREventMsg;
#[dom_struct]
pub struct Navigator {
@@ -21,6 +23,7 @@ pub struct Navigator {
plugins: MutNullableJS<PluginArray>,
mime_types: MutNullableJS<MimeTypeArray>,
service_worker: MutNullableJS<ServiceWorkerContainer>,
+ vr: MutNullableJS<VR>
}
impl Navigator {
@@ -31,6 +34,7 @@ impl Navigator {
plugins: Default::default(),
mime_types: Default::default(),
service_worker: Default::default(),
+ vr: Default::default(),
}
}
@@ -114,4 +118,16 @@ impl NavigatorMethods for Navigator {
true
}
+ #[allow(unrooted_must_root)]
+ // https://w3c.github.io/webvr/#interface-navigator
+ fn Vr(&self) -> Root<VR> {
+ self.vr.or_init(|| VR::new(&self.global()))
+ }
+}
+
+impl Navigator {
+ pub fn handle_webvr_event(&self, event: WebVREventMsg) {
+ self.vr.get().expect("Shouldn't arrive here with an empty VR instance")
+ .handle_webvr_event(event);
+ }
}
diff --git a/components/script/dom/vr.rs b/components/script/dom/vr.rs
new file mode 100644
index 00000000000..6e077c415ac
--- /dev/null
+++ b/components/script/dom/vr.rs
@@ -0,0 +1,160 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::cell::DOMRefCell;
+use dom::bindings::codegen::Bindings::VRBinding;
+use dom::bindings::codegen::Bindings::VRBinding::VRMethods;
+use dom::bindings::error::Error;
+use dom::bindings::inheritance::Castable;
+use dom::bindings::js::{JS, Root};
+use dom::bindings::reflector::{DomObject, reflect_dom_object};
+use dom::event::Event;
+use dom::eventtarget::EventTarget;
+use dom::globalscope::GlobalScope;
+use dom::promise::Promise;
+use dom::vrdisplay::VRDisplay;
+use dom::vrdisplayevent::VRDisplayEvent;
+use ipc_channel::ipc;
+use ipc_channel::ipc::IpcSender;
+use script_traits::WebVREventMsg;
+use std::rc::Rc;
+use webvr_traits::WebVRMsg;
+use webvr_traits::webvr;
+
+#[dom_struct]
+pub struct VR {
+ eventtarget: EventTarget,
+ displays: DOMRefCell<Vec<JS<VRDisplay>>>
+}
+
+impl VR {
+ fn new_inherited() -> VR {
+ VR {
+ eventtarget: EventTarget::new_inherited(),
+ displays: DOMRefCell::new(Vec::new())
+ }
+ }
+
+ pub fn new(global: &GlobalScope) -> Root<VR> {
+ let root = reflect_dom_object(box VR::new_inherited(),
+ global,
+ VRBinding::Wrap);
+ root.register();
+ root
+ }
+}
+
+impl Drop for VR {
+ fn drop(&mut self) {
+ self.unregister();
+ }
+}
+
+impl VRMethods for VR {
+ #[allow(unrooted_must_root)]
+ // https://w3c.github.io/webvr/#interface-navigator
+ fn GetDisplays(&self) -> Rc<Promise> {
+ let promise = Promise::new(&self.global());
+
+ if let Some(webvr_thread) = self.webvr_thread() {
+ let (sender, receiver) = ipc::channel().unwrap();
+ webvr_thread.send(WebVRMsg::GetDisplays(sender)).unwrap();
+ match receiver.recv().unwrap() {
+ Ok(displays) => {
+ // Sync displays
+ for display in displays {
+ self.sync_display(&display);
+ }
+ },
+ Err(e) => {
+ promise.reject_native(promise.global().get_cx(), &e);
+ return promise;
+ }
+ }
+ } else {
+ // WebVR spec: The Promise MUST be rejected if WebVR is not enabled/supported.
+ promise.reject_error(promise.global().get_cx(), Error::Security);
+ return promise;
+ }
+
+ // convert from JS to Root
+ let displays: Vec<Root<VRDisplay>> = self.displays.borrow().iter()
+ .map(|d| Root::from_ref(&**d))
+ .collect();
+ promise.resolve_native(promise.global().get_cx(), &displays);
+
+ promise
+ }
+}
+
+
+impl VR {
+ fn webvr_thread(&self) -> Option<IpcSender<WebVRMsg>> {
+ self.global().as_window().webvr_thread()
+ }
+
+ fn find_display(&self, display_id: u64) -> Option<Root<VRDisplay>> {
+ self.displays.borrow()
+ .iter()
+ .find(|d| d.get_display_id() == display_id)
+ .map(|d| Root::from_ref(&**d))
+ }
+
+ fn register(&self) {
+ if let Some(webvr_thread) = self.webvr_thread() {
+ let msg = WebVRMsg::RegisterContext(self.global().pipeline_id());
+ webvr_thread.send(msg).unwrap();
+ }
+ }
+
+ fn unregister(&self) {
+ if let Some(webvr_thread) = self.webvr_thread() {
+ let msg = WebVRMsg::UnregisterContext(self.global().pipeline_id());
+ webvr_thread.send(msg).unwrap();
+ }
+ }
+
+ fn sync_display(&self, display: &webvr::VRDisplayData) -> Root<VRDisplay> {
+ if let Some(existing) = self.find_display(display.display_id) {
+ existing.update_display(&display);
+ existing
+ } else {
+ let root = VRDisplay::new(&self.global(), display.clone());
+ self.displays.borrow_mut().push(JS::from_ref(&*root));
+ root
+ }
+ }
+
+ pub fn handle_webvr_event(&self, event: WebVREventMsg) {
+ let WebVREventMsg::DisplayEvent(event) = event;
+ match &event {
+ &webvr::VRDisplayEvent::Connect(ref display) => {
+ let display = self.sync_display(&display);
+ display.handle_webvr_event(&event);
+ self.notify_event(&display, &event);
+ },
+ &webvr::VRDisplayEvent::Disconnect(id) => {
+ if let Some(display) = self.find_display(id) {
+ display.handle_webvr_event(&event);
+ self.notify_event(&display, &event);
+ }
+ },
+ &webvr::VRDisplayEvent::Activate(ref display, _) |
+ &webvr::VRDisplayEvent::Deactivate(ref display, _) |
+ &webvr::VRDisplayEvent::Blur(ref display) |
+ &webvr::VRDisplayEvent::Focus(ref display) |
+ &webvr::VRDisplayEvent::PresentChange(ref display, _) |
+ &webvr::VRDisplayEvent::Change(ref display) => {
+ let display = self.sync_display(&display);
+ display.handle_webvr_event(&event);
+ }
+ };
+ }
+
+ fn notify_event(&self, display: &VRDisplay, event: &webvr::VRDisplayEvent) {
+ let event = VRDisplayEvent::new_from_webvr(&self.global(), &display, &event);
+ event.upcast::<Event>().fire(self.upcast());
+ }
+}
+
diff --git a/components/script/dom/vrdisplay.rs b/components/script/dom/vrdisplay.rs
new file mode 100644
index 00000000000..bb7d1811464
--- /dev/null
+++ b/components/script/dom/vrdisplay.rs
@@ -0,0 +1,607 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use canvas_traits::CanvasMsg;
+use core::ops::Deref;
+use dom::bindings::callback::ExceptionHandling;
+use dom::bindings::cell::DOMRefCell;
+use dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceBinding::PerformanceMethods;
+use dom::bindings::codegen::Bindings::VRDisplayBinding;
+use dom::bindings::codegen::Bindings::VRDisplayBinding::VRDisplayMethods;
+use dom::bindings::codegen::Bindings::VRDisplayBinding::VREye;
+use dom::bindings::codegen::Bindings::VRLayerBinding::VRLayer;
+use dom::bindings::codegen::Bindings::WindowBinding::FrameRequestCallback;
+use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
+use dom::bindings::inheritance::Castable;
+use dom::bindings::js::{MutNullableJS, MutJS, Root};
+use dom::bindings::num::Finite;
+use dom::bindings::refcounted::Trusted;
+use dom::bindings::reflector::{DomObject, reflect_dom_object};
+use dom::bindings::str::DOMString;
+use dom::event::Event;
+use dom::eventtarget::EventTarget;
+use dom::globalscope::GlobalScope;
+use dom::promise::Promise;
+use dom::vrdisplaycapabilities::VRDisplayCapabilities;
+use dom::vrdisplayevent::VRDisplayEvent;
+use dom::vreyeparameters::VREyeParameters;
+use dom::vrframedata::VRFrameData;
+use dom::vrpose::VRPose;
+use dom::vrstageparameters::VRStageParameters;
+use dom::webglrenderingcontext::WebGLRenderingContext;
+use ipc_channel::ipc;
+use ipc_channel::ipc::{IpcSender, IpcReceiver};
+use js::jsapi::JSContext;
+use script_runtime::CommonScriptMsg;
+use script_runtime::ScriptThreadEventCategory::WebVREvent;
+use script_thread::Runnable;
+use std::cell::Cell;
+use std::mem;
+use std::rc::Rc;
+use std::sync::mpsc;
+use std::thread;
+use webrender_traits::VRCompositorCommand;
+use webvr_traits::{WebVRDisplayData, WebVRDisplayEvent, WebVRFrameData, WebVRLayer, WebVRMsg};
+
+#[dom_struct]
+pub struct VRDisplay {
+ eventtarget: EventTarget,
+ #[ignore_heap_size_of = "Defined in rust-webvr"]
+ display: DOMRefCell<WebVRDisplayData>,
+ depth_near: Cell<f64>,
+ depth_far: Cell<f64>,
+ presenting: Cell<bool>,
+ left_eye_params: MutJS<VREyeParameters>,
+ right_eye_params: MutJS<VREyeParameters>,
+ capabilities: MutJS<VRDisplayCapabilities>,
+ stage_params: MutNullableJS<VRStageParameters>,
+ #[ignore_heap_size_of = "Defined in rust-webvr"]
+ frame_data: DOMRefCell<WebVRFrameData>,
+ #[ignore_heap_size_of = "Defined in rust-webvr"]
+ layer: DOMRefCell<WebVRLayer>,
+ layer_ctx: MutNullableJS<WebGLRenderingContext>,
+ #[ignore_heap_size_of = "Defined in rust-webvr"]
+ next_raf_id: Cell<u32>,
+ /// List of request animation frame callbacks
+ #[ignore_heap_size_of = "closures are hard"]
+ raf_callback_list: DOMRefCell<Vec<(u32, Option<Rc<FrameRequestCallback>>)>>,
+ // Compositor VRFrameData synchonization
+ frame_data_status: Cell<VRFrameDataStatus>,
+ #[ignore_heap_size_of = "channels are hard"]
+ frame_data_receiver: DOMRefCell<Option<IpcReceiver<Result<Vec<u8>, ()>>>>,
+}
+
+unsafe_no_jsmanaged_fields!(WebVRDisplayData);
+unsafe_no_jsmanaged_fields!(WebVRFrameData);
+unsafe_no_jsmanaged_fields!(WebVRLayer);
+
+#[derive(Clone, Copy, PartialEq, Eq, HeapSizeOf)]
+enum VRFrameDataStatus {
+ Waiting,
+ Synced,
+ Exit
+}
+
+unsafe_no_jsmanaged_fields!(VRFrameDataStatus);
+
+impl VRDisplay {
+ fn new_inherited(global: &GlobalScope, display: WebVRDisplayData) -> VRDisplay {
+ let stage = match display.stage_parameters {
+ Some(ref params) => Some(VRStageParameters::new(params.clone(), &global)),
+ None => None
+ };
+
+ VRDisplay {
+ eventtarget: EventTarget::new_inherited(),
+ display: DOMRefCell::new(display.clone()),
+ depth_near: Cell::new(0.01),
+ depth_far: Cell::new(10000.0),
+ presenting: Cell::new(false),
+ left_eye_params: MutJS::new(&*VREyeParameters::new(display.left_eye_parameters.clone(), &global)),
+ right_eye_params: MutJS::new(&*VREyeParameters::new(display.right_eye_parameters.clone(), &global)),
+ capabilities: MutJS::new(&*VRDisplayCapabilities::new(display.capabilities.clone(), &global)),
+ stage_params: MutNullableJS::new(stage.as_ref().map(|v| v.deref())),
+ frame_data: DOMRefCell::new(Default::default()),
+ layer: DOMRefCell::new(Default::default()),
+ layer_ctx: MutNullableJS::default(),
+ next_raf_id: Cell::new(1),
+ raf_callback_list: DOMRefCell::new(vec![]),
+ frame_data_status: Cell::new(VRFrameDataStatus::Waiting),
+ frame_data_receiver: DOMRefCell::new(None),
+ }
+ }
+
+ pub fn new(global: &GlobalScope, display: WebVRDisplayData) -> Root<VRDisplay> {
+ reflect_dom_object(box VRDisplay::new_inherited(&global, display),
+ global,
+ VRDisplayBinding::Wrap)
+ }
+}
+
+impl Drop for VRDisplay {
+ fn drop(&mut self) {
+ if self.presenting.get() {
+ self.force_stop_present();
+ }
+ }
+}
+
+impl VRDisplayMethods for VRDisplay {
+ // https://w3c.github.io/webvr/#dom-vrdisplay-isconnected
+ fn IsConnected(&self) -> bool {
+ self.display.borrow().connected
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrdisplay-ispresenting
+ fn IsPresenting(&self) -> bool {
+ self.presenting.get()
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrdisplay-capabilities
+ fn Capabilities(&self) -> Root<VRDisplayCapabilities> {
+ Root::from_ref(&*self.capabilities.get())
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrdisplay-stageparameters
+ fn GetStageParameters(&self) -> Option<Root<VRStageParameters>> {
+ self.stage_params.get().map(|s| Root::from_ref(&*s))
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrdisplay-geteyeparameters
+ fn GetEyeParameters(&self, eye: VREye) -> Root<VREyeParameters> {
+ match eye {
+ VREye::Left => Root::from_ref(&*self.left_eye_params.get()),
+ VREye::Right => Root::from_ref(&*self.right_eye_params.get())
+ }
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrdisplay-displayid
+ fn DisplayId(&self) -> u32 {
+ self.display.borrow().display_id as u32
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrdisplay-displayname
+ fn DisplayName(&self) -> DOMString {
+ DOMString::from(self.display.borrow().display_name.clone())
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrdisplay-getframedata-framedata-framedata
+ fn GetFrameData(&self, frameData: &VRFrameData) -> bool {
+ // If presenting we use a synced data with compositor for the whole frame
+ if self.presenting.get() {
+ if self.frame_data_status.get() == VRFrameDataStatus::Waiting {
+ self.sync_frame_data();
+ }
+ frameData.update(& self.frame_data.borrow());
+ return true;
+ }
+
+ // If not presenting we fetch inmediante VRFrameData
+ let (sender, receiver) = ipc::channel().unwrap();
+ self.webvr_thread().send(WebVRMsg::GetFrameData(self.global().pipeline_id(),
+ self.get_display_id(),
+ self.depth_near.get(),
+ self.depth_far.get(),
+ sender)).unwrap();
+ return match receiver.recv().unwrap() {
+ Ok(data) => {
+ frameData.update(&data);
+ true
+ },
+ Err(e) => {
+ error!("WebVR::GetFrameData: {:?}", e);
+ false
+ }
+ };
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrdisplay-getpose
+ fn GetPose(&self) -> Root<VRPose> {
+ VRPose::new(&self.global(), &self.frame_data.borrow().pose)
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrdisplay-resetpose
+ fn ResetPose(&self) -> () {
+ let (sender, receiver) = ipc::channel().unwrap();
+ self.webvr_thread().send(WebVRMsg::ResetPose(self.global().pipeline_id(),
+ self.get_display_id(),
+ sender)).unwrap();
+ if let Ok(data) = receiver.recv().unwrap() {
+ // Some VRDisplay data might change after calling ResetPose()
+ *self.display.borrow_mut() = data;
+ }
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrdisplay-depthnear
+ fn DepthNear(&self) -> Finite<f64> {
+ Finite::wrap(self.depth_near.get())
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrdisplay-depthnear
+ fn SetDepthNear(&self, value: Finite<f64>) -> () {
+ self.depth_near.set(*value.deref());
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrdisplay-depthfar
+ fn DepthFar(&self) -> Finite<f64> {
+ Finite::wrap(self.depth_far.get())
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrdisplay-depthfar
+ fn SetDepthFar(&self, value: Finite<f64>) -> () {
+ self.depth_far.set(*value.deref());
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrdisplay-requestanimationframe
+ fn RequestAnimationFrame(&self, callback: Rc<FrameRequestCallback>) -> u32 {
+ if self.presenting.get() {
+ let raf_id = self.next_raf_id.get();
+ self.next_raf_id.set(raf_id + 1);
+ self.raf_callback_list.borrow_mut().push((raf_id, Some(callback)));
+ raf_id
+ } else {
+ // WebVR spec: When a VRDisplay is not presenting it should
+ // fallback to window.requestAnimationFrame.
+ self.global().as_window().RequestAnimationFrame(callback)
+ }
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrdisplay-cancelanimationframe
+ fn CancelAnimationFrame(&self, handle: u32) -> () {
+ if self.presenting.get() {
+ let mut list = self.raf_callback_list.borrow_mut();
+ if let Some(mut pair) = list.iter_mut().find(|pair| pair.0 == handle) {
+ pair.1 = None;
+ }
+ } else {
+ // WebVR spec: When a VRDisplay is not presenting it should
+ // fallback to window.cancelAnimationFrame.
+ self.global().as_window().CancelAnimationFrame(handle);
+ }
+ }
+
+ #[allow(unrooted_must_root)]
+ // https://w3c.github.io/webvr/#dom-vrdisplay-requestpresent
+ fn RequestPresent(&self, layers: Vec<VRLayer>) -> Rc<Promise> {
+ let promise = Promise::new(&self.global());
+ // TODO: WebVR spec: this method must be called in response to a user gesture
+
+ // WebVR spec: If canPresent is false the promise MUST be rejected
+ if !self.display.borrow().capabilities.can_present {
+ let msg = "VRDisplay canPresent is false".to_string();
+ promise.reject_native(promise.global().get_cx(), &msg);
+ return promise;
+ }
+
+ // Current WebVRSpec only allows 1 VRLayer if the VRDevice can present.
+ // Future revisions of this spec may allow multiple layers to enable more complex rendering effects
+ // such as compositing WebGL and DOM elements together.
+ // That functionality is not allowed by this revision of the spec.
+ if layers.len() != 1 {
+ let msg = "The number of layers must be 1".to_string();
+ promise.reject_native(promise.global().get_cx(), &msg);
+ return promise;
+ }
+
+ // Parse and validate received VRLayer
+ let layer = validate_layer(self.global().get_cx(), &layers[0]);
+
+ let layer_bounds;
+ let layer_ctx;
+
+ match layer {
+ Ok((bounds, ctx)) => {
+ layer_bounds = bounds;
+ layer_ctx = ctx;
+ },
+ Err(msg) => {
+ let msg = msg.to_string();
+ promise.reject_native(promise.global().get_cx(), &msg);
+ return promise;
+ }
+ };
+
+ // WebVR spec: Repeat calls while already presenting will update the VRLayers being displayed.
+ if self.presenting.get() {
+ *self.layer.borrow_mut() = layer_bounds;
+ self.layer_ctx.set(Some(&layer_ctx));
+ promise.resolve_native(promise.global().get_cx(), &());
+ return promise;
+ }
+
+ // Request Present
+ let (sender, receiver) = ipc::channel().unwrap();
+ self.webvr_thread().send(WebVRMsg::RequestPresent(self.global().pipeline_id(),
+ self.display.borrow().display_id,
+ sender))
+ .unwrap();
+ match receiver.recv().unwrap() {
+ Ok(()) => {
+ *self.layer.borrow_mut() = layer_bounds;
+ self.layer_ctx.set(Some(&layer_ctx));
+ self.init_present();
+ promise.resolve_native(promise.global().get_cx(), &());
+ },
+ Err(e) => {
+ promise.reject_native(promise.global().get_cx(), &e);
+ }
+ }
+
+ promise
+ }
+
+ #[allow(unrooted_must_root)]
+ // https://w3c.github.io/webvr/#dom-vrdisplay-exitpresent
+ fn ExitPresent(&self) -> Rc<Promise> {
+ let promise = Promise::new(&self.global());
+
+ // WebVR spec: If the VRDisplay is not presenting the promise MUST be rejected.
+ if !self.presenting.get() {
+ let msg = "VRDisplay is not presenting".to_string();
+ promise.reject_native(promise.global().get_cx(), &msg);
+ return promise;
+ }
+
+ // Exit present
+ let (sender, receiver) = ipc::channel().unwrap();
+ self.webvr_thread().send(WebVRMsg::ExitPresent(self.global().pipeline_id(),
+ self.display.borrow().display_id,
+ Some(sender)))
+ .unwrap();
+ match receiver.recv().unwrap() {
+ Ok(()) => {
+ self.stop_present();
+ promise.resolve_native(promise.global().get_cx(), &());
+ },
+ Err(e) => {
+ promise.reject_native(promise.global().get_cx(), &e);
+ }
+ }
+
+ promise
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrdisplay-submitframe
+ fn SubmitFrame(&self) -> () {
+ if !self.presenting.get() {
+ warn!("VRDisplay not presenting");
+ return;
+ }
+
+ let api_sender = self.layer_ctx.get().unwrap().ipc_renderer();
+ let display_id = self.display.borrow().display_id;
+ let layer = self.layer.borrow();
+ let msg = VRCompositorCommand::SubmitFrame(display_id, layer.left_bounds, layer.right_bounds);
+ api_sender.send(CanvasMsg::WebVR(msg)).unwrap();
+ }
+}
+
+impl VRDisplay {
+ fn webvr_thread(&self) -> IpcSender<WebVRMsg> {
+ self.global().as_window().webvr_thread().expect("Shouldn't arrive here with WebVR disabled")
+ }
+
+ pub fn get_display_id(&self) -> u64 {
+ self.display.borrow().display_id
+ }
+
+ pub fn update_display(&self, display: &WebVRDisplayData) {
+ *self.display.borrow_mut() = display.clone();
+ if let Some(ref stage) = display.stage_parameters {
+ if self.stage_params.get().is_none() {
+ let params = Some(VRStageParameters::new(stage.clone(), &self.global()));
+ self.stage_params.set(params.as_ref().map(|v| v.deref()));
+ } else {
+ self.stage_params.get().unwrap().update(&stage);
+ }
+ } else {
+ self.stage_params.set(None);
+ }
+ }
+
+ pub fn handle_webvr_event(&self, event: &WebVRDisplayEvent) {
+ match *event {
+ WebVRDisplayEvent::Connect(ref display) => {
+ self.update_display(&display);
+ },
+ WebVRDisplayEvent::Disconnect(_id) => {
+ self.display.borrow_mut().connected = false;
+ },
+ WebVRDisplayEvent::Activate(ref display, _) |
+ WebVRDisplayEvent::Deactivate(ref display, _) |
+ WebVRDisplayEvent::Blur(ref display) |
+ WebVRDisplayEvent::Focus(ref display) => {
+ self.update_display(&display);
+ self.notify_event(&event);
+ },
+ WebVRDisplayEvent::PresentChange(ref display, presenting) => {
+ self.update_display(&display);
+ self.presenting.set(presenting);
+ self.notify_event(&event);
+ },
+ WebVRDisplayEvent::Change(ref display) => {
+ // Change event doesn't exist in WebVR spec.
+ // So we update display data but don't notify JS.
+ self.update_display(&display);
+ }
+ };
+ }
+
+ fn notify_event(&self, event: &WebVRDisplayEvent) {
+ let root = Root::from_ref(&*self);
+ let event = VRDisplayEvent::new_from_webvr(&self.global(), &root, &event);
+ event.upcast::<Event>().fire(self.upcast());
+ }
+
+ fn init_present(&self) {
+ self.presenting.set(true);
+ let (sync_sender, sync_receiver) = ipc::channel().unwrap();
+ *self.frame_data_receiver.borrow_mut() = Some(sync_receiver);
+
+ let display_id = self.display.borrow().display_id;
+ let api_sender = self.layer_ctx.get().unwrap().ipc_renderer();
+ let js_sender = self.global().script_chan();
+ let address = Trusted::new(&*self);
+ let near_init = self.depth_near.get();
+ let far_init = self.depth_far.get();
+
+ // The render loop at native headset frame rate is implemented using a dedicated thread.
+ // Every loop iteration syncs pose data with the HMD, submits the pixels to the display and waits for Vsync.
+ // Both the requestAnimationFrame call of a VRDisplay in the JavaScript thread and the VRSyncPoses call
+ // in the Webrender thread are executed in parallel. This allows to get some JavaScript code executed ahead.
+ // while the render thread is syncing the VRFrameData to be used for the current frame.
+ // This thread runs until the user calls ExitPresent, the tab is closed or some unexpected error happened.
+ thread::Builder::new().name("WebVR_RAF".into()).spawn(move || {
+ let (raf_sender, raf_receiver) = mpsc::channel();
+ let mut near = near_init;
+ let mut far = far_init;
+
+ // Initialize compositor
+ api_sender.send(CanvasMsg::WebVR(VRCompositorCommand::Create(display_id))).unwrap();
+ loop {
+ // Run RAF callbacks on JavaScript thread
+ let msg = box NotifyDisplayRAF {
+ address: address.clone(),
+ sender: raf_sender.clone()
+ };
+ js_sender.send(CommonScriptMsg::RunnableMsg(WebVREvent, msg)).unwrap();
+
+ // Run Sync Poses in parallell on Render thread
+ let msg = VRCompositorCommand::SyncPoses(display_id, near, far, sync_sender.clone());
+ api_sender.send(CanvasMsg::WebVR(msg)).unwrap();
+
+ // Wait until both SyncPoses & RAF ends
+ if let Ok(depth) = raf_receiver.recv().unwrap() {
+ near = depth.0;
+ far = depth.1;
+ } else {
+ // Stop thread
+ // ExitPresent called or some error happened
+ return;
+ }
+ }
+ }).expect("Thread spawning failed");
+ }
+
+ fn stop_present(&self) {
+ self.presenting.set(false);
+ *self.frame_data_receiver.borrow_mut() = None;
+
+ let api_sender = self.layer_ctx.get().unwrap().ipc_renderer();
+ let display_id = self.display.borrow().display_id;
+ let msg = VRCompositorCommand::Release(display_id);
+ api_sender.send(CanvasMsg::WebVR(msg)).unwrap();
+ }
+
+ // Only called when the JSContext is destroyed while presenting.
+ // In this case we don't want to wait for WebVR Thread response.
+ fn force_stop_present(&self) {
+ self.webvr_thread().send(WebVRMsg::ExitPresent(self.global().pipeline_id(),
+ self.display.borrow().display_id,
+ None))
+ .unwrap();
+ self.stop_present();
+ }
+
+ fn sync_frame_data(&self) {
+ let status = if let Some(receiver) = self.frame_data_receiver.borrow().as_ref() {
+ match receiver.recv().unwrap() {
+ Ok(bytes) => {
+ *self.frame_data.borrow_mut() = WebVRFrameData::from_bytes(&bytes[..]);
+ VRFrameDataStatus::Synced
+ },
+ Err(()) => {
+ VRFrameDataStatus::Exit
+ }
+ }
+ } else {
+ VRFrameDataStatus::Exit
+ };
+
+ self.frame_data_status.set(status);
+ }
+
+ fn handle_raf(&self, end_sender: &mpsc::Sender<Result<(f64, f64), ()>>) {
+ self.frame_data_status.set(VRFrameDataStatus::Waiting);
+
+ let mut callbacks = mem::replace(&mut *self.raf_callback_list.borrow_mut(), vec![]);
+ let now = self.global().as_window().Performance().Now();
+
+ // Call registered VRDisplay.requestAnimationFrame callbacks.
+ for (_, callback) in callbacks.drain(..) {
+ if let Some(callback) = callback {
+ let _ = callback.Call__(Finite::wrap(*now), ExceptionHandling::Report);
+ }
+ }
+
+ if self.frame_data_status.get() == VRFrameDataStatus::Waiting {
+ // User didn't call getFrameData while presenting.
+ // We automatically reads the pending VRFrameData to avoid overflowing the IPC-Channel buffers.
+ // Show a warning as the WebVR Spec recommends.
+ warn!("WebVR: You should call GetFrameData while presenting");
+ self.sync_frame_data();
+ }
+
+ match self.frame_data_status.get() {
+ VRFrameDataStatus::Synced => {
+ // Sync succeeded. Notify RAF thread.
+ end_sender.send(Ok((self.depth_near.get(), self.depth_far.get()))).unwrap();
+ },
+ VRFrameDataStatus::Exit | VRFrameDataStatus::Waiting => {
+ // ExitPresent called or some error ocurred.
+ // Notify VRDisplay RAF thread to stop.
+ end_sender.send(Err(())).unwrap();
+ }
+ }
+ }
+}
+
+struct NotifyDisplayRAF {
+ address: Trusted<VRDisplay>,
+ sender: mpsc::Sender<Result<(f64, f64), ()>>
+}
+
+impl Runnable for NotifyDisplayRAF {
+ fn name(&self) -> &'static str { "NotifyDisplayRAF" }
+
+ fn handler(self: Box<Self>) {
+ let display = self.address.root();
+ display.handle_raf(&self.sender);
+ }
+}
+
+
+// WebVR Spect: If the number of values in the leftBounds/rightBounds arrays
+// is not 0 or 4 for any of the passed layers the promise is rejected
+fn parse_bounds(src: &Option<Vec<Finite<f32>>>, dst: &mut [f32; 4]) -> Result<(), &'static str> {
+ match *src {
+ Some(ref values) => {
+ if values.len() == 0 {
+ return Ok(())
+ }
+ if values.len() != 4 {
+ return Err("The number of values in the leftBounds/rightBounds arrays must be 0 or 4")
+ }
+ for i in 0..4 {
+ dst[i] = *values[i].deref();
+ }
+ Ok(())
+ },
+ None => Ok(())
+ }
+}
+
+fn validate_layer(cx: *mut JSContext,
+ layer: &VRLayer)
+ -> Result<(WebVRLayer, Root<WebGLRenderingContext>), &'static str> {
+ let ctx = layer.source.as_ref().map(|ref s| s.get_or_init_webgl_context(cx, None)).unwrap_or(None);
+ if let Some(ctx) = ctx {
+ let mut data = WebVRLayer::default();
+ try!(parse_bounds(&layer.leftBounds, &mut data.left_bounds));
+ try!(parse_bounds(&layer.rightBounds, &mut data.right_bounds));
+ Ok((data, ctx))
+ } else {
+ Err("VRLayer source must be a WebGL Context")
+ }
+}
diff --git a/components/script/dom/vrdisplaycapabilities.rs b/components/script/dom/vrdisplaycapabilities.rs
new file mode 100644
index 00000000000..0b131f8ffd3
--- /dev/null
+++ b/components/script/dom/vrdisplaycapabilities.rs
@@ -0,0 +1,62 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::cell::DOMRefCell;
+use dom::bindings::codegen::Bindings::VRDisplayCapabilitiesBinding;
+use dom::bindings::codegen::Bindings::VRDisplayCapabilitiesBinding::VRDisplayCapabilitiesMethods;
+use dom::bindings::js::Root;
+use dom::bindings::reflector::{Reflector, reflect_dom_object};
+use dom::globalscope::GlobalScope;
+use webvr_traits::WebVRDisplayCapabilities;
+
+#[dom_struct]
+pub struct VRDisplayCapabilities {
+ reflector_: Reflector,
+ #[ignore_heap_size_of = "Defined in rust-webvr"]
+ capabilities: DOMRefCell<WebVRDisplayCapabilities>
+}
+
+unsafe_no_jsmanaged_fields!(WebVRDisplayCapabilities);
+
+impl VRDisplayCapabilities {
+ fn new_inherited(capabilities: WebVRDisplayCapabilities) -> VRDisplayCapabilities {
+ VRDisplayCapabilities {
+ reflector_: Reflector::new(),
+ capabilities: DOMRefCell::new(capabilities)
+ }
+ }
+
+ pub fn new(capabilities: WebVRDisplayCapabilities, global: &GlobalScope) -> Root<VRDisplayCapabilities> {
+ reflect_dom_object(box VRDisplayCapabilities::new_inherited(capabilities),
+ global,
+ VRDisplayCapabilitiesBinding::Wrap)
+ }
+}
+
+impl VRDisplayCapabilitiesMethods for VRDisplayCapabilities {
+ // https://w3c.github.io/webvr/#dom-vrdisplaycapabilities-hasposition
+ fn HasPosition(&self) -> bool {
+ self.capabilities.borrow().has_position
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrdisplaycapabilities-hasorientation
+ fn HasOrientation(&self) -> bool {
+ self.capabilities.borrow().has_orientation
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrdisplaycapabilities-hasexternaldisplay
+ fn HasExternalDisplay(&self) -> bool {
+ self.capabilities.borrow().has_external_display
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrdisplaycapabilities-canpresent
+ fn CanPresent(&self) -> bool {
+ self.capabilities.borrow().can_present
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrdisplaycapabilities-maxlayers
+ fn MaxLayers(&self) -> u32 {
+ if self.CanPresent() { 1 } else { 0 }
+ }
+}
diff --git a/components/script/dom/vrdisplayevent.rs b/components/script/dom/vrdisplayevent.rs
new file mode 100644
index 00000000000..b6257464e53
--- /dev/null
+++ b/components/script/dom/vrdisplayevent.rs
@@ -0,0 +1,116 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::EventBinding::EventBinding::EventMethods;
+use dom::bindings::codegen::Bindings::VRDisplayEventBinding;
+use dom::bindings::codegen::Bindings::VRDisplayEventBinding::VRDisplayEventMethods;
+use dom::bindings::codegen::Bindings::VRDisplayEventBinding::VRDisplayEventReason;
+use dom::bindings::error::Fallible;
+use dom::bindings::inheritance::Castable;
+use dom::bindings::js::{JS, Root};
+use dom::bindings::reflector::{DomObject, reflect_dom_object};
+use dom::bindings::str::DOMString;
+use dom::event::Event;
+use dom::globalscope::GlobalScope;
+use dom::vrdisplay::VRDisplay;
+use dom::window::Window;
+use servo_atoms::Atom;
+use webvr_traits::{WebVRDisplayEvent, WebVRDisplayEventReason};
+
+#[dom_struct]
+pub struct VRDisplayEvent {
+ event: Event,
+ display: JS<VRDisplay>,
+ reason: Option<VRDisplayEventReason>
+}
+
+impl VRDisplayEvent {
+ fn new_inherited(display: &VRDisplay,
+ reason: Option<VRDisplayEventReason>)
+ -> VRDisplayEvent {
+ VRDisplayEvent {
+ event: Event::new_inherited(),
+ display: JS::from_ref(display),
+ reason: reason.clone()
+ }
+ }
+
+ pub fn new(global: &GlobalScope,
+ type_: Atom,
+ bubbles: bool,
+ cancelable: bool,
+ display: &VRDisplay,
+ reason: Option<VRDisplayEventReason>)
+ -> Root<VRDisplayEvent> {
+ let ev = reflect_dom_object(box VRDisplayEvent::new_inherited(&display, reason),
+ global,
+ VRDisplayEventBinding::Wrap);
+ {
+ let event = ev.upcast::<Event>();
+ event.init_event(type_, bubbles, cancelable);
+ }
+ ev
+ }
+
+ pub fn new_from_webvr(global: &GlobalScope,
+ display: &VRDisplay,
+ event: &WebVRDisplayEvent)
+ -> Root<VRDisplayEvent> {
+ let (name, reason) = match *event {
+ WebVRDisplayEvent::Connect(_) => ("displayconnect", None),
+ WebVRDisplayEvent::Disconnect(_) => ("displaydisconnect", None),
+ WebVRDisplayEvent::Activate(_, reason) => ("activate", Some(reason)),
+ WebVRDisplayEvent::Deactivate(_, reason) => ("deactivate", Some(reason)),
+ WebVRDisplayEvent::Blur(_) => ("blur", None),
+ WebVRDisplayEvent::Focus(_) => ("focus", None),
+ WebVRDisplayEvent::PresentChange(_, _) => ("presentchange", None),
+ WebVRDisplayEvent::Change(_) => panic!("VRDisplayEvent:Change event not available in WebVR")
+ };
+
+ // map to JS enum values
+ let reason = reason.map(|r| {
+ match r {
+ WebVRDisplayEventReason::Navigation => VRDisplayEventReason::Navigation,
+ WebVRDisplayEventReason::Mounted => VRDisplayEventReason::Mounted,
+ WebVRDisplayEventReason::Unmounted => VRDisplayEventReason::Unmounted,
+ }
+ });
+
+ VRDisplayEvent::new(&global,
+ Atom::from(DOMString::from(name)),
+ false,
+ false,
+ &display,
+ reason)
+ }
+
+ pub fn Constructor(window: &Window,
+ type_: DOMString,
+ init: &VRDisplayEventBinding::VRDisplayEventInit)
+ -> Fallible<Root<VRDisplayEvent>> {
+ Ok(VRDisplayEvent::new(&window.global(),
+ Atom::from(type_),
+ init.parent.bubbles,
+ init.parent.cancelable,
+ &init.display,
+ init.reason))
+ }
+}
+
+impl VRDisplayEventMethods for VRDisplayEvent {
+ // https://w3c.github.io/webvr/#dom-vrdisplayevent-display
+ fn Display(&self) -> Root<VRDisplay> {
+ Root::from_ref(&*self.display)
+ }
+
+ // https://w3c.github.io/webvr/#enumdef-vrdisplayeventreason
+ fn GetReason(&self) -> Option<VRDisplayEventReason> {
+ self.reason
+ }
+
+ // https://dom.spec.whatwg.org/#dom-event-istrusted
+ fn IsTrusted(&self) -> bool {
+ self.event.IsTrusted()
+ }
+}
diff --git a/components/script/dom/vreyeparameters.rs b/components/script/dom/vreyeparameters.rs
new file mode 100644
index 00000000000..9a5e2066e2c
--- /dev/null
+++ b/components/script/dom/vreyeparameters.rs
@@ -0,0 +1,75 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use core::nonzero::NonZero;
+use dom::bindings::cell::DOMRefCell;
+use dom::bindings::codegen::Bindings::VREyeParametersBinding;
+use dom::bindings::codegen::Bindings::VREyeParametersBinding::VREyeParametersMethods;
+use dom::bindings::conversions::slice_to_array_buffer_view;
+use dom::bindings::js::{JS, Root};
+use dom::bindings::reflector::{Reflector, reflect_dom_object};
+use dom::globalscope::GlobalScope;
+use dom::vrfieldofview::VRFieldOfView;
+use js::jsapi::{Heap, JSContext, JSObject};
+use std::default::Default;
+use webvr_traits::WebVREyeParameters;
+
+#[dom_struct]
+pub struct VREyeParameters {
+ reflector_: Reflector,
+ #[ignore_heap_size_of = "Defined in rust-webvr"]
+ parameters: DOMRefCell<WebVREyeParameters>,
+ offset: Heap<*mut JSObject>,
+ fov: JS<VRFieldOfView>,
+}
+
+unsafe_no_jsmanaged_fields!(WebVREyeParameters);
+
+impl VREyeParameters {
+ #[allow(unsafe_code)]
+ #[allow(unrooted_must_root)]
+ fn new_inherited(parameters: WebVREyeParameters, global: &GlobalScope) -> VREyeParameters {
+ let fov = VRFieldOfView::new(&global, parameters.field_of_view.clone());
+ let mut result = VREyeParameters {
+ reflector_: Reflector::new(),
+ parameters: DOMRefCell::new(parameters),
+ offset: Heap::default(),
+ fov: JS::from_ref(&*fov)
+ };
+
+ unsafe {
+ result.offset.set(slice_to_array_buffer_view(global.get_cx(), &result.parameters.borrow().offset));
+ }
+ result
+ }
+
+ pub fn new(parameters: WebVREyeParameters, global: &GlobalScope) -> Root<VREyeParameters> {
+ reflect_dom_object(box VREyeParameters::new_inherited(parameters, global),
+ global,
+ VREyeParametersBinding::Wrap)
+ }
+}
+
+impl VREyeParametersMethods for VREyeParameters {
+ #[allow(unsafe_code)]
+ // https://w3c.github.io/webvr/#dom-vreyeparameters-offset
+ unsafe fn Offset(&self, _cx: *mut JSContext) -> NonZero<*mut JSObject> {
+ NonZero::new(self.offset.get())
+ }
+
+ // https://w3c.github.io/webvr/#dom-vreyeparameters-fieldofview
+ fn FieldOfView(&self) -> Root<VRFieldOfView> {
+ Root::from_ref(&*self.fov)
+ }
+
+ // https://w3c.github.io/webvr/#dom-vreyeparameters-renderwidth
+ fn RenderWidth(&self) -> u32 {
+ self.parameters.borrow().render_width
+ }
+
+ // https://w3c.github.io/webvr/#dom-vreyeparameters-renderheight
+ fn RenderHeight(&self) -> u32 {
+ self.parameters.borrow().render_height
+ }
+}
diff --git a/components/script/dom/vrfieldofview.rs b/components/script/dom/vrfieldofview.rs
new file mode 100644
index 00000000000..5103e1ec781
--- /dev/null
+++ b/components/script/dom/vrfieldofview.rs
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::cell::DOMRefCell;
+use dom::bindings::codegen::Bindings::VRFieldOfViewBinding;
+use dom::bindings::codegen::Bindings::VRFieldOfViewBinding::VRFieldOfViewMethods;
+use dom::bindings::js::Root;
+use dom::bindings::num::Finite;
+use dom::bindings::reflector::{Reflector, reflect_dom_object};
+use dom::globalscope::GlobalScope;
+use webvr_traits::WebVRFieldOfView;
+
+#[dom_struct]
+pub struct VRFieldOfView {
+ reflector_: Reflector,
+ #[ignore_heap_size_of = "Defined in rust-webvr"]
+ fov: DOMRefCell<WebVRFieldOfView>
+}
+
+unsafe_no_jsmanaged_fields!(WebVRFieldOfView);
+
+impl VRFieldOfView {
+ fn new_inherited(fov: WebVRFieldOfView) -> VRFieldOfView {
+ VRFieldOfView {
+ reflector_: Reflector::new(),
+ fov: DOMRefCell::new(fov)
+ }
+ }
+
+ pub fn new(global: &GlobalScope, fov: WebVRFieldOfView) -> Root<VRFieldOfView> {
+ reflect_dom_object(box VRFieldOfView::new_inherited(fov),
+ global,
+ VRFieldOfViewBinding::Wrap)
+ }
+}
+
+impl VRFieldOfViewMethods for VRFieldOfView {
+ // https://w3c.github.io/webvr/#interface-interface-vrfieldofview
+ fn UpDegrees(&self) -> Finite<f64> {
+ Finite::wrap(self.fov.borrow().up_degrees)
+ }
+
+ // https://w3c.github.io/webvr/#interface-interface-vrfieldofview
+ fn RightDegrees(&self) -> Finite<f64> {
+ Finite::wrap(self.fov.borrow().right_degrees)
+ }
+
+ // https://w3c.github.io/webvr/#interface-interface-vrfieldofview
+ fn DownDegrees(&self) -> Finite<f64> {
+ Finite::wrap(self.fov.borrow().down_degrees)
+ }
+
+ // https://w3c.github.io/webvr/#interface-interface-vrfieldofview
+ fn LeftDegrees(&self) -> Finite<f64> {
+ Finite::wrap(self.fov.borrow().left_degrees)
+ }
+}
diff --git a/components/script/dom/vrframedata.rs b/components/script/dom/vrframedata.rs
new file mode 100644
index 00000000000..4c1d14d66a6
--- /dev/null
+++ b/components/script/dom/vrframedata.rs
@@ -0,0 +1,122 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use core::nonzero::NonZero;
+use dom::bindings::codegen::Bindings::VRFrameDataBinding;
+use dom::bindings::codegen::Bindings::VRFrameDataBinding::VRFrameDataMethods;
+use dom::bindings::conversions::{slice_to_array_buffer_view, update_array_buffer_view};
+use dom::bindings::error::Fallible;
+use dom::bindings::js::{JS, Root};
+use dom::bindings::num::Finite;
+use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
+use dom::globalscope::GlobalScope;
+use dom::vrpose::VRPose;
+use dom::window::Window;
+use js::jsapi::{Heap, JSContext, JSObject};
+use std::cell::Cell;
+use webvr_traits::WebVRFrameData;
+
+#[dom_struct]
+pub struct VRFrameData {
+ reflector_: Reflector,
+ left_proj: Heap<*mut JSObject>,
+ left_view: Heap<*mut JSObject>,
+ right_proj: Heap<*mut JSObject>,
+ right_view: Heap<*mut JSObject>,
+ pose: JS<VRPose>,
+ timestamp: Cell<f64>,
+ first_timestamp: Cell<f64>
+}
+
+impl VRFrameData {
+ #[allow(unsafe_code)]
+ #[allow(unrooted_must_root)]
+ fn new(global: &GlobalScope) -> Root<VRFrameData> {
+ let matrix = [1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0f32];
+ let pose = VRPose::new(&global, &Default::default());
+
+ let mut framedata = VRFrameData {
+ reflector_: Reflector::new(),
+ left_proj: Heap::default(),
+ left_view: Heap::default(),
+ right_proj: Heap::default(),
+ right_view: Heap::default(),
+ pose: JS::from_ref(&*pose),
+ timestamp: Cell::new(0.0),
+ first_timestamp: Cell::new(0.0)
+ };
+
+ unsafe {
+ framedata.left_proj.set(slice_to_array_buffer_view(global.get_cx(), &matrix));
+ framedata.left_view.set(slice_to_array_buffer_view(global.get_cx(), &matrix));
+ framedata.right_proj.set(slice_to_array_buffer_view(global.get_cx(), &matrix));
+ framedata.right_view.set(slice_to_array_buffer_view(global.get_cx(), &matrix));
+ }
+
+ reflect_dom_object(box framedata,
+ global,
+ VRFrameDataBinding::Wrap)
+ }
+
+ pub fn Constructor(window: &Window) -> Fallible<Root<VRFrameData>> {
+ Ok(VRFrameData::new(&window.global()))
+ }
+}
+
+
+impl VRFrameData {
+ #[allow(unsafe_code)]
+ pub fn update(&self, data: &WebVRFrameData) {
+ unsafe {
+ update_array_buffer_view(self.left_proj.get(), &data.left_projection_matrix);
+ update_array_buffer_view(self.left_view.get(), &data.left_view_matrix);
+ update_array_buffer_view(self.right_proj.get(), &data.right_projection_matrix);
+ update_array_buffer_view(self.right_view.get(), &data.right_view_matrix);
+ }
+ self.pose.update(&data.pose);
+ self.timestamp.set(data.timestamp);
+ if self.first_timestamp.get() == 0.0 {
+ self.first_timestamp.set(data.timestamp);
+ }
+ }
+}
+
+impl VRFrameDataMethods for VRFrameData {
+ // https://w3c.github.io/webvr/#dom-vrframedata-timestamp
+ fn Timestamp(&self) -> Finite<f64> {
+ Finite::wrap(self.timestamp.get() - self.first_timestamp.get())
+ }
+
+ #[allow(unsafe_code)]
+ // https://w3c.github.io/webvr/#dom-vrframedata-leftprojectionmatrix
+ unsafe fn LeftProjectionMatrix(&self, _cx: *mut JSContext) -> NonZero<*mut JSObject> {
+ NonZero::new(self.left_proj.get())
+ }
+
+ #[allow(unsafe_code)]
+ // https://w3c.github.io/webvr/#dom-vrframedata-leftviewmatrix
+ unsafe fn LeftViewMatrix(&self, _cx: *mut JSContext) -> NonZero<*mut JSObject> {
+ NonZero::new(self.left_view.get())
+ }
+
+ #[allow(unsafe_code)]
+ // https://w3c.github.io/webvr/#dom-vrframedata-rightprojectionmatrix
+ unsafe fn RightProjectionMatrix(&self, _cx: *mut JSContext) -> NonZero<*mut JSObject> {
+ NonZero::new(self.right_proj.get())
+ }
+
+ #[allow(unsafe_code)]
+ // https://w3c.github.io/webvr/#dom-vrframedata-rightviewmatrix
+ unsafe fn RightViewMatrix(&self, _cx: *mut JSContext) -> NonZero<*mut JSObject> {
+ NonZero::new(self.right_view.get())
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrframedata-pose
+ fn Pose(&self) -> Root<VRPose> {
+ Root::from_ref(&*self.pose)
+ }
+}
diff --git a/components/script/dom/vrpose.rs b/components/script/dom/vrpose.rs
new file mode 100644
index 00000000000..a6cb09d96bf
--- /dev/null
+++ b/components/script/dom/vrpose.rs
@@ -0,0 +1,133 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use core::nonzero::NonZero;
+use dom::bindings::cell::DOMRefCell;
+use dom::bindings::codegen::Bindings::VRPoseBinding;
+use dom::bindings::codegen::Bindings::VRPoseBinding::VRPoseMethods;
+use dom::bindings::conversions::{slice_to_array_buffer_view, update_array_buffer_view};
+use dom::bindings::js::Root;
+use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
+use dom::globalscope::GlobalScope;
+use js::jsapi::{Heap, JSContext, JSObject};
+use std::ptr;
+use webvr_traits::webvr;
+
+#[dom_struct]
+pub struct VRPose {
+ reflector_: Reflector,
+ position: DOMRefCell<Heap<*mut JSObject>>,
+ orientation: DOMRefCell<Heap<*mut JSObject>>,
+ linear_vel: DOMRefCell<Heap<*mut JSObject>>,
+ angular_vel: DOMRefCell<Heap<*mut JSObject>>,
+ linear_acc: DOMRefCell<Heap<*mut JSObject>>,
+ angular_acc: DOMRefCell<Heap<*mut JSObject>>
+}
+
+#[allow(unsafe_code)]
+unsafe fn update_or_create_typed_array(cx: *mut JSContext,
+ src: Option<&[f32]>,
+ dst: &DOMRefCell<Heap<*mut JSObject>>) {
+ let mut dst = dst.borrow_mut();
+ match src {
+ Some(ref data) => {
+ if dst.get().is_null() {
+ dst.set(slice_to_array_buffer_view(cx, &data));
+ } else {
+ update_array_buffer_view(dst.get(), &data);
+ }
+ },
+ None => {
+ if !dst.get().is_null() {
+ dst.set(ptr::null_mut());
+ }
+ }
+ }
+}
+
+#[inline]
+#[allow(unsafe_code)]
+fn heap_to_option(heap: &DOMRefCell<Heap<*mut JSObject>>) -> Option<NonZero<*mut JSObject>> {
+ let js_object = heap.borrow_mut().get();
+ if js_object.is_null() {
+ None
+ } else {
+ unsafe {
+ Some(NonZero::new(js_object))
+ }
+ }
+}
+
+impl VRPose {
+ fn new_inherited() -> VRPose {
+ VRPose {
+ reflector_: Reflector::new(),
+ position: DOMRefCell::new(Heap::default()),
+ orientation: DOMRefCell::new(Heap::default()),
+ linear_vel: DOMRefCell::new(Heap::default()),
+ angular_vel: DOMRefCell::new(Heap::default()),
+ linear_acc: DOMRefCell::new(Heap::default()),
+ angular_acc: DOMRefCell::new(Heap::default())
+ }
+ }
+
+ pub fn new(global: &GlobalScope, pose: &webvr::VRPose) -> Root<VRPose> {
+ let root = reflect_dom_object(box VRPose::new_inherited(),
+ global,
+ VRPoseBinding::Wrap);
+ root.update(&pose);
+ root
+ }
+
+ #[allow(unsafe_code)]
+ pub fn update(&self, pose: &webvr::VRPose) {
+ let cx = self.global().get_cx();
+ unsafe {
+ update_or_create_typed_array(cx, pose.position.as_ref().map(|v| &v[..]), &self.position);
+ update_or_create_typed_array(cx, pose.orientation.as_ref().map(|v| &v[..]), &self.orientation);
+ update_or_create_typed_array(cx, pose.linear_velocity.as_ref().map(|v| &v[..]), &self.linear_vel);
+ update_or_create_typed_array(cx, pose.angular_velocity.as_ref().map(|v| &v[..]), &self.angular_vel);
+ update_or_create_typed_array(cx, pose.linear_acceleration.as_ref().map(|v| &v[..]), &self.linear_acc);
+ update_or_create_typed_array(cx, pose.angular_acceleration.as_ref().map(|v| &v[..]), &self.angular_acc);
+ }
+ }
+}
+
+impl VRPoseMethods for VRPose {
+ #[allow(unsafe_code)]
+ // https://w3c.github.io/webvr/#dom-vrpose-position
+ unsafe fn GetPosition(&self, _cx: *mut JSContext) -> Option<NonZero<*mut JSObject>> {
+ heap_to_option(&self.position)
+ }
+
+ #[allow(unsafe_code)]
+ // https://w3c.github.io/webvr/#dom-vrpose-linearvelocity
+ unsafe fn GetLinearVelocity(&self, _cx: *mut JSContext) -> Option<NonZero<*mut JSObject>> {
+ heap_to_option(&self.linear_vel)
+ }
+
+ #[allow(unsafe_code)]
+ // https://w3c.github.io/webvr/#dom-vrpose-linearacceleration
+ unsafe fn GetLinearAcceleration(&self, _cx: *mut JSContext) -> Option<NonZero<*mut JSObject>> {
+ heap_to_option(&self.linear_acc)
+ }
+
+ #[allow(unsafe_code)]
+ // https://w3c.github.io/webvr/#dom-vrpose-orientation
+ unsafe fn GetOrientation(&self, _cx: *mut JSContext) -> Option<NonZero<*mut JSObject>> {
+ heap_to_option(&self.orientation)
+ }
+
+ #[allow(unsafe_code)]
+ // https://w3c.github.io/webvr/#dom-vrpose-angularvelocity
+ unsafe fn GetAngularVelocity(&self, _cx: *mut JSContext) -> Option<NonZero<*mut JSObject>> {
+ heap_to_option(&self.angular_vel)
+ }
+
+ #[allow(unsafe_code)]
+ // https://w3c.github.io/webvr/#dom-vrpose-angularacceleration
+ unsafe fn GetAngularAcceleration(&self, _cx: *mut JSContext) -> Option<NonZero<*mut JSObject>> {
+ heap_to_option(&self.angular_acc)
+ }
+}
diff --git a/components/script/dom/vrstageparameters.rs b/components/script/dom/vrstageparameters.rs
new file mode 100644
index 00000000000..ac3636aac91
--- /dev/null
+++ b/components/script/dom/vrstageparameters.rs
@@ -0,0 +1,75 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use core::nonzero::NonZero;
+use dom::bindings::cell::DOMRefCell;
+use dom::bindings::codegen::Bindings::VRStageParametersBinding;
+use dom::bindings::codegen::Bindings::VRStageParametersBinding::VRStageParametersMethods;
+use dom::bindings::conversions::{slice_to_array_buffer_view, update_array_buffer_view};
+use dom::bindings::js::Root;
+use dom::bindings::num::Finite;
+use dom::bindings::reflector::{Reflector, reflect_dom_object};
+use dom::globalscope::GlobalScope;
+use js::jsapi::{Heap, JSContext, JSObject};
+use webvr_traits::WebVRStageParameters;
+
+#[dom_struct]
+pub struct VRStageParameters {
+ reflector_: Reflector,
+ #[ignore_heap_size_of = "Defined in rust-webvr"]
+ parameters: DOMRefCell<WebVRStageParameters>,
+ transform: Heap<*mut JSObject>,
+}
+
+unsafe_no_jsmanaged_fields!(WebVRStageParameters);
+
+impl VRStageParameters {
+ #[allow(unsafe_code)]
+ #[allow(unrooted_must_root)]
+ fn new_inherited(parameters: WebVRStageParameters, global: &GlobalScope) -> VRStageParameters {
+ let mut stage = VRStageParameters {
+ reflector_: Reflector::new(),
+ parameters: DOMRefCell::new(parameters),
+ transform: Heap::default()
+ };
+ unsafe {
+ stage.transform.set(slice_to_array_buffer_view(global.get_cx(),
+ &stage.parameters.borrow().sitting_to_standing_transform));
+ }
+
+ stage
+ }
+
+ pub fn new(parameters: WebVRStageParameters, global: &GlobalScope) -> Root<VRStageParameters> {
+ reflect_dom_object(box VRStageParameters::new_inherited(parameters, global),
+ global,
+ VRStageParametersBinding::Wrap)
+ }
+
+ #[allow(unsafe_code)]
+ pub fn update(&self, parameters: &WebVRStageParameters) {
+ unsafe {
+ update_array_buffer_view(self.transform.get(), &parameters.sitting_to_standing_transform);
+ }
+ *self.parameters.borrow_mut() = parameters.clone();
+ }
+}
+
+impl VRStageParametersMethods for VRStageParameters {
+ #[allow(unsafe_code)]
+ // https://w3c.github.io/webvr/#dom-vrstageparameters-sittingtostandingtransform
+ unsafe fn SittingToStandingTransform(&self, _cx: *mut JSContext) -> NonZero<*mut JSObject> {
+ NonZero::new(self.transform.get())
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrstageparameters-sizex
+ fn SizeX(&self) -> Finite<f32> {
+ Finite::wrap(self.parameters.borrow().size_x)
+ }
+
+ // https://w3c.github.io/webvr/#dom-vrstageparameters-sizez
+ fn SizeZ(&self) -> Finite<f32> {
+ Finite::wrap(self.parameters.borrow().size_z)
+ }
+}
diff --git a/components/script/dom/webidls/Navigator.webidl b/components/script/dom/webidls/Navigator.webidl
index 84d5a7c3ccb..495222cecd2 100644
--- a/components/script/dom/webidls/Navigator.webidl
+++ b/components/script/dom/webidls/Navigator.webidl
@@ -57,3 +57,8 @@ interface NavigatorPlugins {
interface NavigatorCookies {
readonly attribute boolean cookieEnabled;
};
+
+// https://w3c.github.io/webvr/#interface-navigator
+partial interface Navigator {
+ [SameObject, Pref="dom.webvr.enabled"] readonly attribute VR vr;
+};
diff --git a/components/script/dom/webidls/VR.webidl b/components/script/dom/webidls/VR.webidl
new file mode 100644
index 00000000000..0fded365be0
--- /dev/null
+++ b/components/script/dom/webidls/VR.webidl
@@ -0,0 +1,10 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// https://w3c.github.io/webvr/#interface-navigator
+[Pref="dom.webvr.enabled"]
+interface VR: EventTarget {
+ Promise<sequence<VRDisplay>> getDisplays();
+ //readonly attribute FrozenArray<VRDisplay> activeVRDisplays;
+};
diff --git a/components/script/dom/webidls/VRDisplay.webidl b/components/script/dom/webidls/VRDisplay.webidl
new file mode 100644
index 00000000000..6822a994a9b
--- /dev/null
+++ b/components/script/dom/webidls/VRDisplay.webidl
@@ -0,0 +1,131 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+enum VREye {
+ "left",
+ "right"
+};
+
+
+// https://w3c.github.io/webvr/#interface-vrdisplay
+[Pref="dom.webvr.enabled"]
+interface VRDisplay : EventTarget {
+ readonly attribute boolean isConnected;
+ readonly attribute boolean isPresenting;
+
+ /**
+ * Dictionary of capabilities describing the VRDisplay.
+ */
+ [SameObject] readonly attribute VRDisplayCapabilities capabilities;
+
+ /**
+ * If this VRDisplay supports room-scale experiences, the optional
+ * stage attribute contains details on the room-scale parameters.
+ * The stageParameters attribute can not change between null
+ * and non-null once the VRDisplay is enumerated; however,
+ * the values within VRStageParameters may change after
+ * any call to VRDisplay.submitFrame as the user may re-configure
+ * their environment at any time.
+ */
+ readonly attribute VRStageParameters? stageParameters;
+
+ /**
+ * Return the current VREyeParameters for the given eye.
+ */
+ VREyeParameters getEyeParameters(VREye whichEye);
+
+ /**
+ * An identifier for this distinct VRDisplay. Used as an
+ * association point in the Gamepad API.
+ */
+ readonly attribute unsigned long displayId;
+
+ /**
+ * A display name, a user-readable name identifying it.
+ */
+ readonly attribute DOMString displayName;
+
+ /**
+ * Populates the passed VRFrameData with the information required to render
+ * the current frame.
+ */
+ boolean getFrameData(VRFrameData frameData);
+
+ /**
+ * Return a VRPose containing the future predicted pose of the VRDisplay
+ * when the current frame will be presented. The value returned will not
+ * change until JavaScript has returned control to the browser.
+ *
+ * The VRPose will contain the position, orientation, velocity,
+ * and acceleration of each of these properties.
+ */
+ [NewObject] VRPose getPose();
+
+ /**
+ * Reset the pose for this display, treating its current position and
+ * orientation as the "origin/zero" values. VRPose.position,
+ * VRPose.orientation, and VRStageParameters.sittingToStandingTransform may be
+ * updated when calling resetPose(). This should be called in only
+ * sitting-space experiences.
+ */
+ void resetPose();
+
+ /**
+ * z-depth defining the near plane of the eye view frustum
+ * enables mapping of values in the render target depth
+ * attachment to scene coordinates. Initially set to 0.01.
+ */
+ attribute double depthNear;
+
+ /**
+ * z-depth defining the far plane of the eye view frustum
+ * enables mapping of values in the render target depth
+ * attachment to scene coordinates. Initially set to 10000.0.
+ */
+ attribute double depthFar;
+
+ /**
+ * The callback passed to `requestAnimationFrame` will be called
+ * any time a new frame should be rendered. When the VRDisplay is
+ * presenting the callback will be called at the native refresh
+ * rate of the HMD. When not presenting this function acts
+ * identically to how window.requestAnimationFrame acts. Content should
+ * make no assumptions of frame rate or vsync behavior as the HMD runs
+ * asynchronously from other displays and at differing refresh rates.
+ */
+ unsigned long requestAnimationFrame(FrameRequestCallback callback);
+
+ /**
+ * Passing the value returned by `requestAnimationFrame` to
+ * `cancelAnimationFrame` will unregister the callback.
+ */
+ void cancelAnimationFrame(unsigned long handle);
+
+ /**
+ * Begin presenting to the VRDisplay. Must be called in response to a user gesture.
+ * Repeat calls while already presenting will update the VRLayers being displayed.
+ * If the number of values in the leftBounds/rightBounds arrays is not 0 or 4 for
+ * any of the passed layers the promise is rejected.
+ * If the source of any of the layers is not present (null), the promise is rejected.
+ */
+ Promise<void> requestPresent(sequence<VRLayer> layers);
+
+ /**
+ * Stops presenting to the VRDisplay.
+ */
+ Promise<void> exitPresent();
+
+ /**
+ * Get the layers currently being presented.
+ */
+ //sequence<VRLayer> getLayers();
+
+ /**
+ * The VRLayer provided to the VRDisplay will be captured and presented
+ * in the HMD. Calling this function has the same effect on the source
+ * canvas as any other operation that uses its source image, and canvases
+ * created without preserveDrawingBuffer set to true will be cleared.
+ */
+ void submitFrame();
+};
diff --git a/components/script/dom/webidls/VRDisplayCapabilities.webidl b/components/script/dom/webidls/VRDisplayCapabilities.webidl
new file mode 100644
index 00000000000..2d9cccd6a97
--- /dev/null
+++ b/components/script/dom/webidls/VRDisplayCapabilities.webidl
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// https://w3c.github.io/webvr/#interface-vrdisplaycapabilities
+[Pref="dom.webvr.enabled"]
+interface VRDisplayCapabilities {
+ readonly attribute boolean hasPosition;
+ readonly attribute boolean hasOrientation;
+ readonly attribute boolean hasExternalDisplay;
+ readonly attribute boolean canPresent;
+ readonly attribute unsigned long maxLayers;
+};
diff --git a/components/script/dom/webidls/VRDisplayEvent.webidl b/components/script/dom/webidls/VRDisplayEvent.webidl
new file mode 100644
index 00000000000..df0990d32a8
--- /dev/null
+++ b/components/script/dom/webidls/VRDisplayEvent.webidl
@@ -0,0 +1,23 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// https://w3c.github.io/webvr/#interface-vrdisplayevent
+
+enum VRDisplayEventReason {
+ "navigation",
+ "mounted",
+ "unmounted",
+ "requested"
+};
+
+[Pref="dom.webvr.enabled", Constructor(DOMString type, VRDisplayEventInit eventInitDict)]
+interface VRDisplayEvent : Event {
+ readonly attribute VRDisplay display;
+ readonly attribute VRDisplayEventReason? reason;
+};
+
+dictionary VRDisplayEventInit : EventInit {
+ required VRDisplay display;
+ VRDisplayEventReason reason;
+};
diff --git a/components/script/dom/webidls/VREyeParameters.webidl b/components/script/dom/webidls/VREyeParameters.webidl
new file mode 100644
index 00000000000..5d127f20784
--- /dev/null
+++ b/components/script/dom/webidls/VREyeParameters.webidl
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// https://w3c.github.io/webvr/#interface-vreyeparameters
+
+[Pref="dom.webvr.enabled"]
+interface VREyeParameters {
+ readonly attribute Float32Array offset;
+ [SameObject] readonly attribute VRFieldOfView fieldOfView;
+ readonly attribute unsigned long renderWidth;
+ readonly attribute unsigned long renderHeight;
+};
diff --git a/components/script/dom/webidls/VRFieldOfView.webidl b/components/script/dom/webidls/VRFieldOfView.webidl
new file mode 100644
index 00000000000..b562c5b0e7f
--- /dev/null
+++ b/components/script/dom/webidls/VRFieldOfView.webidl
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// https://w3c.github.io/webvr/#interface-vrfieldofview
+
+[Pref="dom.webvr.enabled"]
+interface VRFieldOfView {
+ readonly attribute double upDegrees;
+ readonly attribute double rightDegrees;
+ readonly attribute double downDegrees;
+ readonly attribute double leftDegrees;
+};
diff --git a/components/script/dom/webidls/VRFrameData.webidl b/components/script/dom/webidls/VRFrameData.webidl
new file mode 100644
index 00000000000..baa37ea6f68
--- /dev/null
+++ b/components/script/dom/webidls/VRFrameData.webidl
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// https://w3c.github.io/webvr/#interface-vrframedata
+
+[Pref="dom.webvr.enabled", Constructor]
+interface VRFrameData {
+ readonly attribute DOMHighResTimeStamp timestamp;
+ readonly attribute Float32Array leftProjectionMatrix;
+ readonly attribute Float32Array leftViewMatrix;
+ readonly attribute Float32Array rightProjectionMatrix;
+ readonly attribute Float32Array rightViewMatrix;
+ readonly attribute VRPose pose;
+};
diff --git a/components/script/dom/webidls/VRLayer.webidl b/components/script/dom/webidls/VRLayer.webidl
new file mode 100644
index 00000000000..47b30b324f7
--- /dev/null
+++ b/components/script/dom/webidls/VRLayer.webidl
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// https://w3c.github.io/webvr/#interface-vrlayer
+
+//typedef (HTMLCanvasElement or OffscreenCanvas) VRSource;
+
+dictionary VRLayer {
+ HTMLCanvasElement source;
+ sequence<float> leftBounds;
+ sequence<float> rightBounds;
+};
diff --git a/components/script/dom/webidls/VRPose.webidl b/components/script/dom/webidls/VRPose.webidl
new file mode 100644
index 00000000000..ffbd931cfbc
--- /dev/null
+++ b/components/script/dom/webidls/VRPose.webidl
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// https://w3c.github.io/webvr/#interface-vrpose
+[Pref="dom.webvr.enabled"]
+interface VRPose {
+ readonly attribute Float32Array? position;
+ readonly attribute Float32Array? linearVelocity;
+ readonly attribute Float32Array? linearAcceleration;
+ readonly attribute Float32Array? orientation;
+ readonly attribute Float32Array? angularVelocity;
+ readonly attribute Float32Array? angularAcceleration;
+};
diff --git a/components/script/dom/webidls/VRStageParameters.webidl b/components/script/dom/webidls/VRStageParameters.webidl
new file mode 100644
index 00000000000..255a8f07e76
--- /dev/null
+++ b/components/script/dom/webidls/VRStageParameters.webidl
@@ -0,0 +1,11 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// https://w3c.github.io/webvr/#interface-vrstageparameters
+[Pref="dom.webvr.enabled"]
+interface VRStageParameters {
+ readonly attribute Float32Array sittingToStandingTransform;
+ readonly attribute float sizeX;
+ readonly attribute float sizeZ;
+};
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index 87d15ab1616..4eab03eaeee 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -110,6 +110,7 @@ use timers::{IsInterval, TimerCallback};
use tinyfiledialogs::{self, MessageBoxIcon};
use url::Position;
use webdriver_handlers::jsval_to_webdriver;
+use webvr_traits::WebVRMsg;
/// Current state of the window object
#[derive(JSTraceable, Copy, Clone, Debug, PartialEq, HeapSizeOf)]
@@ -241,6 +242,10 @@ pub struct Window {
media_query_lists: WeakMediaQueryListVec,
test_runner: MutNullableJS<TestRunner>,
+
+ /// A handle for communicating messages to the webvr thread, if available.
+ #[ignore_heap_size_of = "channels are hard"]
+ webvr_thread: Option<IpcSender<WebVRMsg>>
}
impl Window {
@@ -321,6 +326,10 @@ impl Window {
pub fn current_viewport(&self) -> Rect<Au> {
self.current_viewport.clone().get()
}
+
+ pub fn webvr_thread(&self) -> Option<IpcSender<WebVRMsg>> {
+ self.webvr_thread.clone()
+ }
}
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
@@ -1590,7 +1599,8 @@ impl Window {
layout_chan: Sender<Msg>,
id: PipelineId,
parent_info: Option<(PipelineId, FrameType)>,
- window_size: Option<WindowSizeData>)
+ window_size: Option<WindowSizeData>,
+ webvr_thread: Option<IpcSender<WebVRMsg>>)
-> Root<Window> {
let layout_rpc: Box<LayoutRPC + Send> = {
let (rpc_send, rpc_recv) = channel();
@@ -1654,6 +1664,7 @@ impl Window {
scroll_offsets: DOMRefCell::new(HashMap::new()),
media_query_lists: WeakMediaQueryListVec::new(),
test_runner: Default::default(),
+ webvr_thread: webvr_thread
};
unsafe {
diff --git a/components/script/lib.rs b/components/script/lib.rs
index f477e562bbf..127194ebf65 100644
--- a/components/script/lib.rs
+++ b/components/script/lib.rs
@@ -98,6 +98,7 @@ extern crate url;
extern crate uuid;
extern crate webrender_traits;
extern crate websocket;
+extern crate webvr_traits;
extern crate xml5ever;
mod body;
diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs
index bd8874dc475..75ce94084f0 100644
--- a/components/script/script_runtime.rs
+++ b/components/script/script_runtime.rs
@@ -80,6 +80,7 @@ pub enum ScriptThreadEventCategory {
ServiceWorkerEvent,
EnterFullscreen,
ExitFullscreen,
+ WebVREvent
}
/// An interface for receiving ScriptMsg values in an event loop. Used for synchronous DOM
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 407a32cb1de..cb6be4e9be0 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -90,6 +90,7 @@ use script_traits::{ScriptThreadFactory, TimerEvent, TimerEventRequest, TimerSou
use script_traits::{TouchEventType, TouchId, UntrustedNodeAddress, WindowSizeData, WindowSizeType};
use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent};
use script_traits::CompositorEvent::{TouchEvent, TouchpadPressureEvent};
+use script_traits::WebVREventMsg;
use script_traits::webdriver_msg::WebDriverScriptCommand;
use serviceworkerjob::{Job, JobQueue, AsyncJobHandler, FinishJobHandler, InvokeType, SettleType};
use servo_config::opts;
@@ -116,6 +117,7 @@ use task_source::user_interaction::{UserInteractionTask, UserInteractionTaskSour
use time::Tm;
use url::Position;
use webdriver_handlers;
+use webvr_traits::WebVRMsg;
thread_local!(pub static STACK_ROOTS: Cell<Option<RootCollectionPtr>> = Cell::new(None));
thread_local!(static SCRIPT_THREAD_ROOT: Cell<Option<*const ScriptThread>> = Cell::new(None));
@@ -477,6 +479,9 @@ pub struct ScriptThread {
content_process_shutdown_chan: IpcSender<()>,
promise_job_queue: PromiseJobQueue,
+
+ /// A handle to the webvr thread, if available
+ webvr_thread: Option<IpcSender<WebVRMsg>>,
}
/// In the event of thread panic, all data on the stack runs its destructor. However, there
@@ -699,6 +704,8 @@ impl ScriptThread {
promise_job_queue: PromiseJobQueue::new(),
layout_to_constellation_chan: state.layout_to_constellation_chan,
+
+ webvr_thread: state.webvr_thread
}
}
@@ -945,6 +952,7 @@ impl ScriptThread {
ScriptThreadEventCategory::SetViewport => ProfilerCategory::ScriptSetViewport,
ScriptThreadEventCategory::TimerEvent => ProfilerCategory::ScriptTimerEvent,
ScriptThreadEventCategory::WebSocketEvent => ProfilerCategory::ScriptWebSocketEvent,
+ ScriptThreadEventCategory::WebVREvent => ProfilerCategory::ScriptWebVREvent,
ScriptThreadEventCategory::WorkerEvent => ProfilerCategory::ScriptWorkerEvent,
ScriptThreadEventCategory::ServiceWorkerEvent => ProfilerCategory::ScriptServiceWorkerEvent,
ScriptThreadEventCategory::EnterFullscreen => ProfilerCategory::ScriptEnterFullscreen,
@@ -1009,6 +1017,8 @@ impl ScriptThread {
self.handle_reload(pipeline_id),
ConstellationControlMsg::ExitPipeline(pipeline_id, discard_browsing_context) =>
self.handle_exit_pipeline_msg(pipeline_id, discard_browsing_context),
+ ConstellationControlMsg::WebVREvent(pipeline_id, event) =>
+ self.handle_webvr_event(pipeline_id, event),
msg @ ConstellationControlMsg::AttachLayout(..) |
msg @ ConstellationControlMsg::Viewport(..) |
msg @ ConstellationControlMsg::SetScrollState(..) |
@@ -1751,7 +1761,8 @@ impl ScriptThread {
incomplete.layout_chan,
incomplete.pipeline_id,
incomplete.parent_info,
- incomplete.window_size);
+ incomplete.window_size,
+ self.webvr_thread.clone());
let frame_element = frame_element.r().map(Castable::upcast);
let browsing_context = BrowsingContext::new(&window, frame_element);
@@ -2212,6 +2223,14 @@ impl ScriptThread {
}
}
+ fn handle_webvr_event(&self, pipeline_id: PipelineId, event: WebVREventMsg) {
+ let window = self.documents.borrow().find_window(pipeline_id);
+ if let Some(window) = window {
+ let navigator = window.Navigator();
+ navigator.handle_webvr_event(event);
+ }
+ }
+
pub fn enqueue_promise_job(job: EnqueuedPromiseCallback, global: &GlobalScope) {
SCRIPT_THREAD_ROOT.with(|root| {
let script_thread = unsafe { &*root.get().unwrap() };