mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Auto merge of #24133 - paulrouget:userAction, r=jdm
Do not allow XR session on non user-activated events This sets the thread in "user interaction mode" when the dispatched event is trusted. I also tried an approach where we would not rely on the dispatched event but just set "user interaction mode" when we get a compositor event (which, we can assume, are only user generated). That worked as well. Fixes #23787. <!-- 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/24133) <!-- Reviewable:end -->
This commit is contained in:
commit
293ccd07e8
9 changed files with 54 additions and 15 deletions
|
@ -23,6 +23,7 @@ use crate::dom::vrdisplay::VRDisplay;
|
||||||
use crate::dom::vrdisplayevent::VRDisplayEvent;
|
use crate::dom::vrdisplayevent::VRDisplayEvent;
|
||||||
use crate::dom::xrsession::XRSession;
|
use crate::dom::xrsession::XRSession;
|
||||||
use crate::dom::xrtest::XRTest;
|
use crate::dom::xrtest::XRTest;
|
||||||
|
use crate::script_thread::ScriptThread;
|
||||||
use crate::task_source::TaskSource;
|
use crate::task_source::TaskSource;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use ipc_channel::ipc::IpcSender;
|
use ipc_channel::ipc::IpcSender;
|
||||||
|
@ -157,6 +158,11 @@ impl XRMethods for XR {
|
||||||
) -> Rc<Promise> {
|
) -> Rc<Promise> {
|
||||||
let promise = Promise::new_in_current_compartment(&self.global(), comp);
|
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() {
|
if self.pending_or_active_session() {
|
||||||
promise.reject_error(Error::InvalidState);
|
promise.reject_error(Error::InvalidState);
|
||||||
return promise;
|
return promise;
|
||||||
|
|
|
@ -18,6 +18,7 @@ use crate::dom::bindings::root::{Dom, DomRoot};
|
||||||
use crate::dom::fakexrdevice::{get_origin, get_views, FakeXRDevice};
|
use crate::dom::fakexrdevice::{get_origin, get_views, FakeXRDevice};
|
||||||
use crate::dom::globalscope::GlobalScope;
|
use crate::dom::globalscope::GlobalScope;
|
||||||
use crate::dom::promise::Promise;
|
use crate::dom::promise::Promise;
|
||||||
|
use crate::script_thread::ScriptThread;
|
||||||
use crate::task_source::TaskSource;
|
use crate::task_source::TaskSource;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use euclid::RigidTransform3D;
|
use euclid::RigidTransform3D;
|
||||||
|
@ -159,8 +160,9 @@ impl XRTestMethods for XRTest {
|
||||||
|
|
||||||
/// https://github.com/immersive-web/webxr-test-api/blob/master/explainer.md
|
/// https://github.com/immersive-web/webxr-test-api/blob/master/explainer.md
|
||||||
fn SimulateUserActivation(&self, f: Rc<Function>) {
|
fn SimulateUserActivation(&self, f: Rc<Function>) {
|
||||||
// XXXManishearth actually check for activation in XRSession
|
ScriptThread::set_user_interacting(true);
|
||||||
let _ = f.Call__(vec![], ExceptionHandling::Rethrow);
|
let _ = f.Call__(vec![], ExceptionHandling::Rethrow);
|
||||||
|
ScriptThread::set_user_interacting(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// https://github.com/immersive-web/webxr-test-api/blob/master/explainer.md
|
/// https://github.com/immersive-web/webxr-test-api/blob/master/explainer.md
|
||||||
|
|
|
@ -690,6 +690,9 @@ pub struct ScriptThread {
|
||||||
|
|
||||||
/// A set of all nodes ever created in this script thread
|
/// A set of all nodes ever created in this script thread
|
||||||
node_ids: DomRefCell<HashSet<String>>,
|
node_ids: DomRefCell<HashSet<String>>,
|
||||||
|
|
||||||
|
/// Code is running as a consequence of a user interaction
|
||||||
|
is_user_interacting: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// In the event of thread panic, all data on the stack runs its destructor. However, there
|
/// 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<PipelineId> {
|
pub fn get_fully_active_document_ids() -> HashSet<PipelineId> {
|
||||||
SCRIPT_THREAD_ROOT.with(|root| {
|
SCRIPT_THREAD_ROOT.with(|root| {
|
||||||
root.get().map_or(HashSet::new(), |script_thread| {
|
root.get().map_or(HashSet::new(), |script_thread| {
|
||||||
|
@ -1339,6 +1360,7 @@ impl ScriptThread {
|
||||||
event_loop_waker: state.event_loop_waker,
|
event_loop_waker: state.event_loop_waker,
|
||||||
|
|
||||||
node_ids: Default::default(),
|
node_ids: Default::default(),
|
||||||
|
is_user_interacting: Cell::new(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3356,6 +3378,9 @@ impl ScriptThread {
|
||||||
///
|
///
|
||||||
/// TODO: Actually perform DOM event dispatch.
|
/// TODO: Actually perform DOM event dispatch.
|
||||||
fn handle_event(&self, pipeline_id: PipelineId, event: CompositorEvent) {
|
fn handle_event(&self, pipeline_id: PipelineId, event: CompositorEvent) {
|
||||||
|
// Assuming all CompositionEvent are generated by user interactions.
|
||||||
|
ScriptThread::set_user_interacting(true);
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
ResizeEvent(new_size, size_type) => {
|
ResizeEvent(new_size, size_type) => {
|
||||||
self.handle_resize_event(pipeline_id, 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);
|
document.dispatch_composition_event(composition_event);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScriptThread::set_user_interacting(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_mouse_event(
|
fn handle_mouse_event(
|
||||||
|
|
|
@ -702192,7 +702192,7 @@
|
||||||
"testharness"
|
"testharness"
|
||||||
],
|
],
|
||||||
"webxr/xrWebGLLayer_constructor.https.html": [
|
"webxr/xrWebGLLayer_constructor.https.html": [
|
||||||
"0584da79c12def757f951ec53c4c048f18c39b8c",
|
"7e57f4286c66ea9f8380791840dada3d82b611fe",
|
||||||
"testharness"
|
"testharness"
|
||||||
],
|
],
|
||||||
"webxr/xrWebGLLayer_framebuffer_draw.https.html": [
|
"webxr/xrWebGLLayer_framebuffer_draw.https.html": [
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[xrDevice_requestSession_immersive_no_gesture.https.html]
|
|
||||||
[Requesting immersive session outside of a user gesture rejects]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -7658,9 +7658,6 @@
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"stub": {
|
|
||||||
"css/blockify_inline_element.html": []
|
|
||||||
},
|
|
||||||
"support": {
|
"support": {
|
||||||
".gitignore": [
|
".gitignore": [
|
||||||
[]
|
[]
|
||||||
|
@ -19747,11 +19744,11 @@
|
||||||
"testharness"
|
"testharness"
|
||||||
],
|
],
|
||||||
"webxr/create_session.html": [
|
"webxr/create_session.html": [
|
||||||
"e68ea81893a65793094f9086cdd6d73800ee1c14",
|
"af76c5a812d7d05a0158194560933def3fbdb9f9",
|
||||||
"testharness"
|
"testharness"
|
||||||
],
|
],
|
||||||
"webxr/obtain_frame.html": [
|
"webxr/obtain_frame.html": [
|
||||||
"39698805b476c7e469548d25ece59560a596604d",
|
"74fda5bad43e8ea95552e65380e83952680e8469",
|
||||||
"testharness"
|
"testharness"
|
||||||
],
|
],
|
||||||
"webxr/resources/webxr-util.js": [
|
"webxr/resources/webxr-util.js": [
|
||||||
|
|
|
@ -12,7 +12,11 @@
|
||||||
views: TEST_VIEWS,
|
views: TEST_VIEWS,
|
||||||
viewerOrigin: {position: [0.5, 0.1, 0.1], orientation: [1, 0, 0, 1] }
|
viewerOrigin: {position: [0.5, 0.1, 0.1], orientation: [1, 0, 0, 1] }
|
||||||
}).then((m) => {
|
}).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());
|
}).then(() => t.done());
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -16,7 +16,11 @@
|
||||||
views: TEST_VIEWS,
|
views: TEST_VIEWS,
|
||||||
viewerOrigin: {position: [0.5, 0.1, 0.1], orientation: [1, 0, 0, 1] }
|
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, {})});
|
await session.updateRenderState({"baseLayer": new XRWebGLLayer(session, gl, {})});
|
||||||
let resolve;
|
let resolve;
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,11 @@ xr_promise_test("Ensure that XRWebGLLayer's constructor throws appropriate error
|
||||||
let gl = webglCanvas.getContext('webgl', glAttributes);
|
let gl = webglCanvas.getContext('webgl', glAttributes);
|
||||||
return navigator.xr.test.simulateDeviceConnection(TRACKED_IMMERSIVE_DEVICE)
|
return navigator.xr.test.simulateDeviceConnection(TRACKED_IMMERSIVE_DEVICE)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return navigator.xr.requestSession('inline')
|
let sessionPromise;
|
||||||
.then((session) => {
|
navigator.xr.test.simulateUserActivation(function() {
|
||||||
|
sessionPromise = navigator.xr.requestSession('inline');
|
||||||
|
});
|
||||||
|
return sessionPromise.then((session) => {
|
||||||
try {
|
try {
|
||||||
let webglLayerIncompatible = new XRWebGLLayer(session, gl);
|
let webglLayerIncompatible = new XRWebGLLayer(session, gl);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue