use content_security_policy::{CspList, PolicyDisposition};
use http::header::HeaderMap;
use hyper_serde::Serde;
use net_traits::fetch::headers::get_decode_and_split_header_name;
use rustc_hash::FxHashSet;
use servo_url::MutableOrigin;
use crate::dom::node::NodeTraits;
use crate::dom::window::Window;
pub(crate) fn check_a_navigation_response_adherence_to_x_frame_options(
window: &Window,
csp_list: Option<&CspList>,
destination_origin: &MutableOrigin,
headers: Option<&Serde<HeaderMap>>,
) -> bool {
if window.window_proxy().parent().is_none() {
return true;
}
if let Some(csp_list) = csp_list {
for policy in csp_list.0.iter() {
if policy.disposition != PolicyDisposition::Enforce {
continue;
}
if policy.contains_a_directive_whose_name_is("frame-ancestors") {
return true;
}
}
}
let Some(headers) = headers else {
return true;
};
let Some(raw_xframe_options) = get_decode_and_split_header_name("X-Frame-Options", headers)
else {
return true;
};
let x_frame_options =
FxHashSet::from_iter(raw_xframe_options.iter().map(|value| value.to_lowercase()));
if x_frame_options.len() > 1 &&
x_frame_options
.iter()
.any(|value| value == "deny" || value == "allowall" || value == "sameorigin")
{
return false;
}
if x_frame_options.len() > 1 {
return true;
}
let Some(first_item) = x_frame_options.iter().next() else {
return true;
};
if first_item == "deny" {
return false;
}
if first_item == "sameorigin" {
let mut window_proxy = window.window_proxy();
while let Some(container_element) = window_proxy.frame_element() {
let container_document = container_element.owner_document();
if !container_document.origin().same_origin(destination_origin) {
return false;
}
window_proxy = container_document.window().window_proxy()
}
if window_proxy.parent().is_some() {
return false;
}
}
true
}