use std::collections::hash_map;
use rustc_hash::{FxBuildHasher, FxHashMap};
use servo_base::id::{BrowsingContextId, PipelineId};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::trace::HashMapTracedValues;
use crate::dom::document::Document;
use crate::dom::globalscope::GlobalScope;
use crate::dom::html::htmliframeelement::HTMLIFrameElement;
use crate::dom::window::Window;
#[derive(JSTraceable)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
pub(crate) struct DocumentCollection {
map: HashMapTracedValues<PipelineId, Dom<Document>, FxBuildHasher>,
}
impl DocumentCollection {
pub(crate) fn insert(&mut self, pipeline_id: PipelineId, doc: &Document) {
self.map.insert(pipeline_id, Dom::from_ref(doc));
}
pub(crate) fn remove(&mut self, pipeline_id: PipelineId) -> Option<DomRoot<Document>> {
self.map
.remove(&pipeline_id)
.map(|ref doc| DomRoot::from_ref(&**doc))
}
pub(crate) fn find_document(&self, pipeline_id: PipelineId) -> Option<DomRoot<Document>> {
self.map
.get(&pipeline_id)
.map(|doc| DomRoot::from_ref(&**doc))
}
pub(crate) fn find_window(&self, pipeline_id: PipelineId) -> Option<DomRoot<Window>> {
self.find_document(pipeline_id)
.map(|doc| DomRoot::from_ref(doc.window()))
}
pub(crate) fn find_global(&self, pipeline_id: PipelineId) -> Option<DomRoot<GlobalScope>> {
self.find_window(pipeline_id)
.map(|window| DomRoot::from_ref(window.upcast()))
}
pub(crate) fn find_iframe(
&self,
pipeline_id: PipelineId,
browsing_context_id: BrowsingContextId,
) -> Option<DomRoot<HTMLIFrameElement>> {
self.find_document(pipeline_id).and_then(|document| {
document
.iframes()
.get(browsing_context_id)
.map(|iframe| iframe.element.as_rooted())
})
}
pub(crate) fn iter(&self) -> DocumentsIter<'_> {
DocumentsIter {
iter: self.map.iter(),
}
}
pub(crate) fn documents_in_order(&self) -> Vec<PipelineId> {
DocumentTree::new(self).documents_in_order()
}
}
impl Default for DocumentCollection {
fn default() -> Self {
Self {
map: HashMapTracedValues::new_fx(),
}
}
}
pub(crate) struct DocumentsIter<'a> {
iter: hash_map::Iter<'a, PipelineId, Dom<Document>>,
}
impl Iterator for DocumentsIter<'_> {
type Item = (PipelineId, DomRoot<Document>);
fn next(&mut self) -> Option<(PipelineId, DomRoot<Document>)> {
self.iter
.next()
.map(|(id, doc)| (*id, DomRoot::from_ref(&**doc)))
}
}
#[derive(Default)]
struct DocumentTreeNode {
parent: Option<PipelineId>,
children: Vec<PipelineId>,
}
#[derive(Default)]
struct DocumentTree {
tree: FxHashMap<PipelineId, DocumentTreeNode>,
}
impl DocumentTree {
fn new(documents: &DocumentCollection) -> Self {
let mut tree = DocumentTree::default();
for (id, document) in documents.iter() {
let children: Vec<PipelineId> = document
.iframes()
.iter()
.filter_map(|iframe| iframe.pipeline_id())
.filter(|iframe_pipeline_id| documents.find_document(*iframe_pipeline_id).is_some())
.collect();
for child in &children {
tree.tree.entry(*child).or_default().parent = Some(id);
}
tree.tree.entry(id).or_default().children = children;
}
tree
}
fn documents_in_order(&self) -> Vec<PipelineId> {
let mut list = Vec::new();
for (id, node) in self.tree.iter() {
if node.parent.is_none() {
self.process_node_for_documents_in_order(*id, &mut list);
}
}
list
}
fn process_node_for_documents_in_order(&self, id: PipelineId, list: &mut Vec<PipelineId>) {
list.push(id);
for child in self
.tree
.get(&id)
.expect("Should have found child node")
.children
.iter()
{
self.process_node_for_documents_in_order(*child, list);
}
}
}