mirror of
https://github.com/servo/servo.git
synced 2025-06-08 08:33:26 +00:00
199 lines
6.3 KiB
Rust
199 lines
6.3 KiB
Rust
/* 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 https://mozilla.org/MPL/2.0/. */
|
|
|
|
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeBinding::NodeMethods;
|
|
use crate::dom::bindings::inheritance::Castable;
|
|
use crate::dom::bindings::num::Finite;
|
|
use crate::dom::bindings::root::{Dom, DomRoot};
|
|
use crate::dom::cssstylesheet::CSSStyleSheet;
|
|
use crate::dom::document::Document;
|
|
use crate::dom::element::Element;
|
|
use crate::dom::htmlelement::HTMLElement;
|
|
use crate::dom::node;
|
|
use crate::dom::shadowroot::ShadowRoot;
|
|
use crate::dom::window::Window;
|
|
use crate::dom::windowproxy::WindowProxy;
|
|
use euclid::Point2D;
|
|
use js::jsapi::JS_GetRuntime;
|
|
use script_layout_interface::message::{NodesFromPointQueryType, QueryMsg};
|
|
use script_traits::UntrustedNodeAddress;
|
|
|
|
macro_rules! proxy_call(
|
|
($fn_name:ident, $return_type:ty) => (
|
|
pub fn $fn_name(&self) -> $return_type {
|
|
match self {
|
|
DocumentOrShadowRoot::Document(doc) => doc.$fn_name(),
|
|
DocumentOrShadowRoot::ShadowRoot(root) => root.$fn_name(),
|
|
}
|
|
}
|
|
);
|
|
|
|
($fn_name:ident, $arg1:ident, $arg1_type:ty, $return_type:ty) => (
|
|
pub fn $fn_name(&self, $arg1: $arg1_type) -> $return_type {
|
|
match self {
|
|
DocumentOrShadowRoot::Document(doc) => doc.$fn_name($arg1),
|
|
DocumentOrShadowRoot::ShadowRoot(root) => root.$fn_name($arg1),
|
|
}
|
|
}
|
|
);
|
|
);
|
|
|
|
#[must_root]
|
|
#[derive(JSTraceable, MallocSizeOf)]
|
|
pub enum DocumentOrShadowRoot {
|
|
Document(Dom<Document>),
|
|
ShadowRoot(Dom<ShadowRoot>),
|
|
}
|
|
|
|
impl DocumentOrShadowRoot {
|
|
proxy_call!(stylesheet_count, usize);
|
|
proxy_call!(stylesheet_at, index, usize, Option<DomRoot<CSSStyleSheet>>);
|
|
}
|
|
|
|
// https://w3c.github.io/webcomponents/spec/shadow/#extensions-to-the-documentorshadowroot-mixin
|
|
#[must_root]
|
|
#[derive(JSTraceable, MallocSizeOf)]
|
|
pub struct DocumentOrShadowRootImpl {
|
|
has_browsing_context: bool,
|
|
window: Dom<Window>,
|
|
}
|
|
|
|
impl DocumentOrShadowRootImpl {
|
|
pub fn new(window: &Window, has_browsing_context: bool) -> Self {
|
|
Self {
|
|
has_browsing_context,
|
|
window: Dom::from_ref(window),
|
|
}
|
|
}
|
|
|
|
/// <https://html.spec.whatwg.org/multipage/#concept-document-bc>
|
|
#[inline]
|
|
pub fn browsing_context(&self) -> Option<DomRoot<WindowProxy>> {
|
|
if self.has_browsing_context {
|
|
self.window.undiscarded_window_proxy()
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn nodes_from_point(
|
|
&self,
|
|
client_point: &Point2D<f32>,
|
|
reflow_goal: NodesFromPointQueryType,
|
|
) -> Vec<UntrustedNodeAddress> {
|
|
if !self
|
|
.window
|
|
.layout_reflow(QueryMsg::NodesFromPointQuery(*client_point, reflow_goal))
|
|
{
|
|
return vec![];
|
|
};
|
|
|
|
self.window.layout().nodes_from_point_response()
|
|
}
|
|
|
|
#[allow(unsafe_code)]
|
|
// https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint
|
|
pub fn element_from_point(
|
|
&self,
|
|
x: Finite<f64>,
|
|
y: Finite<f64>,
|
|
document_element: Option<DomRoot<Element>>,
|
|
) -> Option<DomRoot<Element>> {
|
|
let x = *x as f32;
|
|
let y = *y as f32;
|
|
let point = &Point2D::new(x, y);
|
|
let viewport = self.window.window_size().initial_viewport;
|
|
|
|
if self.browsing_context().is_none() {
|
|
return None;
|
|
}
|
|
|
|
if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
|
|
return None;
|
|
}
|
|
|
|
match self
|
|
.nodes_from_point(point, NodesFromPointQueryType::Topmost)
|
|
.first()
|
|
{
|
|
Some(address) => {
|
|
let js_runtime = unsafe { JS_GetRuntime(self.window.get_cx()) };
|
|
let node = unsafe { node::from_untrusted_node_address(js_runtime, *address) };
|
|
let parent_node = node.GetParentNode().unwrap();
|
|
let element_ref = node
|
|
.downcast::<Element>()
|
|
.unwrap_or_else(|| parent_node.downcast::<Element>().unwrap());
|
|
|
|
Some(DomRoot::from_ref(element_ref))
|
|
},
|
|
None => document_element,
|
|
}
|
|
}
|
|
|
|
#[allow(unsafe_code)]
|
|
// https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint
|
|
pub fn elements_from_point(
|
|
&self,
|
|
x: Finite<f64>,
|
|
y: Finite<f64>,
|
|
document_element: Option<DomRoot<Element>>,
|
|
) -> Vec<DomRoot<Element>> {
|
|
let x = *x as f32;
|
|
let y = *y as f32;
|
|
let point = &Point2D::new(x, y);
|
|
let viewport = self.window.window_size().initial_viewport;
|
|
|
|
if self.browsing_context().is_none() {
|
|
return vec![];
|
|
}
|
|
|
|
// Step 2
|
|
if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
|
|
return vec![];
|
|
}
|
|
|
|
let js_runtime = unsafe { JS_GetRuntime(self.window.get_cx()) };
|
|
|
|
// Step 1 and Step 3
|
|
let nodes = self.nodes_from_point(point, NodesFromPointQueryType::All);
|
|
let mut elements: Vec<DomRoot<Element>> = nodes
|
|
.iter()
|
|
.flat_map(|&untrusted_node_address| {
|
|
let node = unsafe {
|
|
node::from_untrusted_node_address(js_runtime, untrusted_node_address)
|
|
};
|
|
DomRoot::downcast::<Element>(node)
|
|
})
|
|
.collect();
|
|
|
|
// Step 4
|
|
if let Some(root_element) = document_element {
|
|
if elements.last() != Some(&root_element) {
|
|
elements.push(root_element);
|
|
}
|
|
}
|
|
|
|
// Step 5
|
|
elements
|
|
}
|
|
|
|
// https://html.spec.whatwg.org/multipage/#dom-document-activeelement
|
|
pub fn get_active_element(
|
|
&self,
|
|
focused_element: Option<DomRoot<Element>>,
|
|
body: Option<DomRoot<HTMLElement>>,
|
|
document_element: Option<DomRoot<Element>>,
|
|
) -> Option<DomRoot<Element>> {
|
|
// TODO: Step 2.
|
|
|
|
match focused_element {
|
|
Some(element) => Some(element), // Step 3. and 4.
|
|
None => match body {
|
|
// Step 5.
|
|
Some(body) => Some(DomRoot::upcast(body)),
|
|
None => document_element,
|
|
},
|
|
}
|
|
}
|
|
}
|