diff --git a/components/script/dom/xr.rs b/components/script/dom/xr.rs index a6fb4057e2c..a916d7a8267 100644 --- a/components/script/dom/xr.rs +++ b/components/script/dom/xr.rs @@ -23,6 +23,7 @@ use crate::dom::vrdisplay::VRDisplay; use crate::dom::vrdisplayevent::VRDisplayEvent; use crate::dom::xrsession::XRSession; use crate::dom::xrtest::XRTest; +use crate::script_thread::ScriptThread; use crate::task_source::TaskSource; use dom_struct::dom_struct; use ipc_channel::ipc::IpcSender; @@ -157,6 +158,11 @@ impl XRMethods for XR { ) -> Rc { let promise = Promise::new_in_current_compartment(&self.global(), comp); + if !ScriptThread::is_user_interacting() { + promise.reject_error(Error::Security); + return promise; + } + if self.pending_or_active_session() { promise.reject_error(Error::InvalidState); return promise; diff --git a/components/script/dom/xrtest.rs b/components/script/dom/xrtest.rs index b74d8c8bd6e..05d608b46a4 100644 --- a/components/script/dom/xrtest.rs +++ b/components/script/dom/xrtest.rs @@ -18,6 +18,7 @@ use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::fakexrdevice::{get_origin, get_views, FakeXRDevice}; use crate::dom::globalscope::GlobalScope; use crate::dom::promise::Promise; +use crate::script_thread::ScriptThread; use crate::task_source::TaskSource; use dom_struct::dom_struct; use euclid::RigidTransform3D; @@ -159,8 +160,9 @@ impl XRTestMethods for XRTest { /// https://github.com/immersive-web/webxr-test-api/blob/master/explainer.md fn SimulateUserActivation(&self, f: Rc) { - // XXXManishearth actually check for activation in XRSession + ScriptThread::set_user_interacting(true); let _ = f.Call__(vec![], ExceptionHandling::Rethrow); + ScriptThread::set_user_interacting(false); } /// https://github.com/immersive-web/webxr-test-api/blob/master/explainer.md diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 4cda69cdced..440de991ceb 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -690,6 +690,9 @@ pub struct ScriptThread { /// A set of all nodes ever created in this script thread node_ids: DomRefCell>, + + /// Code is running as a consequence of a user interaction + is_user_interacting: Cell, } /// In the event of thread panic, all data on the stack runs its destructor. However, there @@ -1030,6 +1033,24 @@ impl ScriptThread { }) } + pub fn set_user_interacting(interacting: bool) { + SCRIPT_THREAD_ROOT.with(|root| { + if let Some(script_thread) = root.get() { + let script_thread = unsafe { &*script_thread }; + script_thread.is_user_interacting.set(interacting); + } + }); + } + + pub fn is_user_interacting() -> bool { + SCRIPT_THREAD_ROOT.with(|root| { + root.get().map_or(false, |script_thread| { + let script_thread = unsafe { &*script_thread }; + script_thread.is_user_interacting.get() + }) + }) + } + pub fn get_fully_active_document_ids() -> HashSet { SCRIPT_THREAD_ROOT.with(|root| { root.get().map_or(HashSet::new(), |script_thread| { @@ -1339,6 +1360,7 @@ impl ScriptThread { event_loop_waker: state.event_loop_waker, node_ids: Default::default(), + is_user_interacting: Cell::new(false), } } @@ -3356,6 +3378,9 @@ impl ScriptThread { /// /// TODO: Actually perform DOM event dispatch. fn handle_event(&self, pipeline_id: PipelineId, event: CompositorEvent) { + // Assuming all CompositionEvent are generated by user interactions. + ScriptThread::set_user_interacting(true); + match event { ResizeEvent(new_size, size_type) => { self.handle_resize_event(pipeline_id, new_size, size_type); @@ -3489,6 +3514,8 @@ impl ScriptThread { document.dispatch_composition_event(composition_event); }, } + + ScriptThread::set_user_interacting(false); } fn handle_mouse_event( diff --git a/tests/wpt/metadata/MANIFEST.json b/tests/wpt/metadata/MANIFEST.json index 3ed7c848d7a..bdbe33987f4 100644 --- a/tests/wpt/metadata/MANIFEST.json +++ b/tests/wpt/metadata/MANIFEST.json @@ -705521,7 +705521,7 @@ "testharness" ], "webxr/xrWebGLLayer_constructor.https.html": [ - "0584da79c12def757f951ec53c4c048f18c39b8c", + "7e57f4286c66ea9f8380791840dada3d82b611fe", "testharness" ], "webxr/xrWebGLLayer_framebuffer_draw.https.html": [ diff --git a/tests/wpt/metadata/webxr/xrDevice_requestSession_immersive_no_gesture.https.html.ini b/tests/wpt/metadata/webxr/xrDevice_requestSession_immersive_no_gesture.https.html.ini deleted file mode 100644 index 75547bd703e..00000000000 --- a/tests/wpt/metadata/webxr/xrDevice_requestSession_immersive_no_gesture.https.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[xrDevice_requestSession_immersive_no_gesture.https.html] - [Requesting immersive session outside of a user gesture rejects] - expected: FAIL - diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index f1a55ead047..4f7aed4b20b 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -7658,9 +7658,6 @@ ] ] }, - "stub": { - "css/blockify_inline_element.html": [] - }, "support": { ".gitignore": [ [] @@ -19747,11 +19744,11 @@ "testharness" ], "webxr/create_session.html": [ - "e68ea81893a65793094f9086cdd6d73800ee1c14", + "af76c5a812d7d05a0158194560933def3fbdb9f9", "testharness" ], "webxr/obtain_frame.html": [ - "39698805b476c7e469548d25ece59560a596604d", + "74fda5bad43e8ea95552e65380e83952680e8469", "testharness" ], "webxr/resources/webxr-util.js": [ diff --git a/tests/wpt/mozilla/tests/webxr/create_session.html b/tests/wpt/mozilla/tests/webxr/create_session.html index e68ea81893a..af76c5a812d 100644 --- a/tests/wpt/mozilla/tests/webxr/create_session.html +++ b/tests/wpt/mozilla/tests/webxr/create_session.html @@ -12,7 +12,11 @@ views: TEST_VIEWS, viewerOrigin: {position: [0.5, 0.1, 0.1], orientation: [1, 0, 0, 1] } }).then((m) => { - return navigator.xr.requestSession("immersive-vr") + let sessionPromise; + navigator.xr.test.simulateUserActivation(() => { + sessionPromise = navigator.xr.requestSession("immersive-vr") + }); + return sessionPromise; }).then(() => t.done()); }); diff --git a/tests/wpt/mozilla/tests/webxr/obtain_frame.html b/tests/wpt/mozilla/tests/webxr/obtain_frame.html index 39698805b47..74fda5bad43 100644 --- a/tests/wpt/mozilla/tests/webxr/obtain_frame.html +++ b/tests/wpt/mozilla/tests/webxr/obtain_frame.html @@ -16,7 +16,11 @@ views: TEST_VIEWS, viewerOrigin: {position: [0.5, 0.1, 0.1], orientation: [1, 0, 0, 1] } }); - let session = await navigator.xr.requestSession("immersive-vr"); + let sessionPromise; + navigator.xr.test.simulateUserActivation(() => { + sessionPromise = navigator.xr.requestSession("immersive-vr"); + }); + let session = await sessionPromise; await session.updateRenderState({"baseLayer": new XRWebGLLayer(session, gl, {})}); let resolve; diff --git a/tests/wpt/web-platform-tests/webxr/xrWebGLLayer_constructor.https.html b/tests/wpt/web-platform-tests/webxr/xrWebGLLayer_constructor.https.html index 0584da79c12..7e57f4286c6 100644 --- a/tests/wpt/web-platform-tests/webxr/xrWebGLLayer_constructor.https.html +++ b/tests/wpt/web-platform-tests/webxr/xrWebGLLayer_constructor.https.html @@ -17,8 +17,11 @@ xr_promise_test("Ensure that XRWebGLLayer's constructor throws appropriate error let gl = webglCanvas.getContext('webgl', glAttributes); return navigator.xr.test.simulateDeviceConnection(TRACKED_IMMERSIVE_DEVICE) .then(() => { - return navigator.xr.requestSession('inline') - .then((session) => { + let sessionPromise; + navigator.xr.test.simulateUserActivation(function() { + sessionPromise = navigator.xr.requestSession('inline'); + }); + return sessionPromise.then((session) => { try { let webglLayerIncompatible = new XRWebGLLayer(session, gl); } catch (err) {