aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/window.rs
diff options
context:
space:
mode:
authorDelan Azabani <dazabani@igalia.com>2023-03-23 18:01:26 +0800
committerDelan Azabani <dazabani@igalia.com>2023-03-23 18:01:26 +0800
commitfd1de05592f9ba2485af81505fa244c79395f2ed (patch)
tree46831834fbaf0d01837af34f33c75d19399d3100 /components/script/dom/window.rs
parentbe6e25a1b223325f47db85050c47982c641c8f0f (diff)
downloadservo-fd1de05592f9ba2485af81505fa244c79395f2ed.tar.gz
servo-fd1de05592f9ba2485af81505fa244c79395f2ed.zip
apply pylbrecht/servo/named.window.getter (closes #27952)
Diffstat (limited to 'components/script/dom/window.rs')
-rw-r--r--components/script/dom/window.rs204
1 files changed, 202 insertions, 2 deletions
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index 7e36c8086fb..76dcc79424f 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -6,6 +6,7 @@ use crate::dom::bindings::cell::{DomRefCell, Ref};
use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
DocumentMethods, DocumentReadyState,
};
+use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
use crate::dom::bindings::codegen::Bindings::HistoryBinding::HistoryBinding::HistoryMethods;
use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{
ImageBitmapOptions, ImageBitmapSource,
@@ -19,7 +20,7 @@ use crate::dom::bindings::codegen::Bindings::WindowBinding::{
use crate::dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions};
use crate::dom::bindings::codegen::UnionTypes::{RequestOrUSVString, StringOrFunction};
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
-use crate::dom::bindings::inheritance::Castable;
+use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomObject;
@@ -40,6 +41,8 @@ use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::hashchangeevent::HashChangeEvent;
use crate::dom::history::History;
+use crate::dom::htmlcollection::{CollectionFilter, HTMLCollection};
+use crate::dom::htmliframeelement::HTMLIFrameElement;
use crate::dom::identityhub::Identities;
use crate::dom::location::Location;
use crate::dom::mediaquerylist::{MediaQueryList, MediaQueryListMatchState};
@@ -71,6 +74,7 @@ use crate::task_manager::TaskManager;
use crate::task_source::{TaskSource, TaskSourceName};
use crate::timers::{IsInterval, TimerCallback};
use crate::webdriver_handlers::jsval_to_webdriver;
+use crate::window_named_properties;
use app_units::Au;
use backtrace::Backtrace;
use base64;
@@ -94,7 +98,9 @@ use js::jsapi::{GCReason, StackFormat, JS_GC};
use js::jsval::UndefinedValue;
use js::jsval::{JSVal, NullValue};
use js::rust::wrappers::JS_DefineProperty;
-use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
+use js::rust::{
+ CustomAutoRooter, CustomAutoRooterGuard, HandleObject, HandleValue, MutableHandleObject,
+};
use media::WindowGLContext;
use msg::constellation_msg::{BrowsingContextId, PipelineId};
use net_traits::image_cache::{ImageCache, ImageResponder, ImageResponse};
@@ -120,17 +126,20 @@ use script_traits::{
use script_traits::{TimerSchedulerMsg, WebrenderIpcSender, WindowSizeData, WindowSizeType};
use selectors::attr::CaseSensitivity;
use servo_arc::Arc as ServoArc;
+use servo_atoms::Atom;
use servo_geometry::{f32_rect_to_au_rect, MaxRect};
use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
use std::borrow::Cow;
use std::borrow::ToOwned;
use std::cell::Cell;
+use std::cmp;
use std::collections::hash_map::Entry;
use std::collections::{HashMap, HashSet};
use std::default::Default;
use std::env;
use std::io::{stderr, stdout, Write};
use std::mem;
+use std::ptr::NonNull;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
@@ -1386,9 +1395,182 @@ impl WindowMethods for Window {
fn IsSecureContext(&self) -> bool {
self.upcast::<GlobalScope>().is_secure_context()
}
+
+ // https://html.spec.whatwg.org/multipage/#named-access-on-the-window-object
+ #[allow(unsafe_code)]
+ fn NamedGetter(&self, _cx: JSContext, name: DOMString) -> Option<NonNull<JSObject>> {
+ if name.is_empty() {
+ return None;
+ }
+ let document = self.Document();
+
+ // https://html.spec.whatwg.org/multipage/#document-tree-child-browsing-context-name-property-set
+ let iframes: Vec<_> = document
+ .iter_iframes()
+ .filter(|iframe| {
+ if let Some(window) = iframe.GetContentWindow() {
+ return window.get_name() == name;
+ }
+ false
+ })
+ .collect();
+
+ let iframe_iter = iframes.iter().map(|iframe| iframe.upcast::<Element>());
+
+ let name = Atom::from(&*name);
+
+ // Step 1.
+ let elements_with_name = document.get_elements_with_name(&name);
+ let name_iter = elements_with_name
+ .iter()
+ .map(|element| &**element)
+ .filter(|elem| is_named_element_with_name_attribute(elem));
+ let elements_with_id = document.get_elements_with_id(&name);
+ let id_iter = elements_with_id
+ .iter()
+ .map(|element| &**element)
+ .filter(|elem| is_named_element_with_id_attribute(elem));
+
+ // Step 2.
+ // TODO(pylbrecht): it would be great to just iterate over
+ // elements_with_id, but it seems document.get_elements_with_id()
+ // does not return HTMLIFrameElements. Why is that?
+ for elem in iframe_iter.clone() {
+ if let Some(nested_window_proxy) = elem
+ .downcast::<HTMLIFrameElement>()
+ .and_then(|iframe| iframe.GetContentWindow())
+ {
+ unsafe {
+ return Some(NonNull::new_unchecked(
+ nested_window_proxy.reflector().get_jsobject().get(),
+ ));
+ }
+ }
+ }
+
+ let mut elements = iframe_iter.chain(name_iter).chain(id_iter);
+
+ let first = elements.next()?;
+
+ if elements.next().is_none() {
+ // Step 3.
+ unsafe {
+ return Some(NonNull::new_unchecked(
+ first.reflector().get_jsobject().get(),
+ ));
+ }
+ }
+
+ // Step 4.
+ #[derive(JSTraceable, MallocSizeOf)]
+ struct WindowNamedGetter {
+ name: Atom,
+ }
+ impl CollectionFilter for WindowNamedGetter {
+ fn filter(&self, elem: &Element, _root: &Node) -> bool {
+ let type_ = match elem.upcast::<Node>().type_id() {
+ NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
+ _ => return false,
+ };
+ if elem.get_id().as_ref() == Some(&self.name) {
+ return true;
+ }
+ match type_ {
+ HTMLElementTypeId::HTMLEmbedElement |
+ HTMLElementTypeId::HTMLFormElement |
+ HTMLElementTypeId::HTMLImageElement |
+ HTMLElementTypeId::HTMLObjectElement => {
+ elem.get_name().as_ref() == Some(&self.name)
+ },
+ _ => false,
+ }
+ }
+ }
+ let collection = HTMLCollection::create(
+ self,
+ document.upcast(),
+ Box::new(WindowNamedGetter { name }),
+ );
+ unsafe {
+ Some(NonNull::new_unchecked(
+ collection.reflector().get_jsobject().get(),
+ ))
+ }
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-tree-accessors:supported-property-names
+ fn SupportedPropertyNames(&self) -> Vec<DOMString> {
+ let mut names_with_first_named_element_map: HashMap<&Atom, &Element> = HashMap::new();
+
+ let document = self.Document();
+ let name_map = document.name_map();
+ for (name, elements) in &*name_map {
+ if name.is_empty() {
+ continue;
+ }
+ let mut name_iter = elements
+ .iter()
+ .filter(|elem| is_named_element_with_name_attribute(elem));
+ if let Some(first) = name_iter.next() {
+ names_with_first_named_element_map.insert(name, first);
+ }
+ }
+ let id_map = document.id_map();
+ for (id, elements) in &*id_map {
+ if id.is_empty() {
+ continue;
+ }
+ let mut id_iter = elements
+ .iter()
+ .filter(|elem| is_named_element_with_id_attribute(elem));
+ if let Some(first) = id_iter.next() {
+ match names_with_first_named_element_map.entry(id) {
+ Entry::Vacant(entry) => drop(entry.insert(first)),
+ Entry::Occupied(mut entry) => {
+ if first.upcast::<Node>().is_before(entry.get().upcast()) {
+ *entry.get_mut() = first;
+ }
+ },
+ }
+ }
+ }
+
+ let mut names_with_first_named_element_vec: Vec<(&Atom, &Element)> =
+ names_with_first_named_element_map
+ .iter()
+ .map(|(k, v)| (*k, *v))
+ .collect();
+ names_with_first_named_element_vec.sort_unstable_by(|a, b| {
+ if a.1 == b.1 {
+ // This can happen if an img has an id different from its name,
+ // spec does not say which string to put first.
+ a.0.cmp(&b.0)
+ } else if a.1.upcast::<Node>().is_before(b.1.upcast::<Node>()) {
+ cmp::Ordering::Less
+ } else {
+ cmp::Ordering::Greater
+ }
+ });
+
+ names_with_first_named_element_vec
+ .iter()
+ .map(|(k, _v)| DOMString::from(&***k))
+ .collect()
+ }
}
impl Window {
+ // https://heycam.github.io/webidl/#named-properties-object
+ // https://html.spec.whatwg.org/multipage/#named-access-on-the-window-object
+ #[allow(unsafe_code)]
+ pub fn create_named_properties_object(
+ cx: JSContext,
+ proto: HandleObject,
+ object: MutableHandleObject,
+ ) {
+ window_named_properties::create(cx, proto, object)
+ }
+
pub(crate) fn set_current_event(&self, event: Option<&Event>) -> Option<DomRoot<Event>> {
let current = self
.current_event
@@ -2659,3 +2841,21 @@ impl ParseErrorReporter for CSSErrorReporter {
));
}
}
+
+fn is_named_element_with_name_attribute(elem: &Element) -> bool {
+ let type_ = match elem.upcast::<Node>().type_id() {
+ NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
+ _ => return false,
+ };
+ match type_ {
+ HTMLElementTypeId::HTMLEmbedElement |
+ HTMLElementTypeId::HTMLFormElement |
+ HTMLElementTypeId::HTMLImageElement |
+ HTMLElementTypeId::HTMLObjectElement => true,
+ _ => false,
+ }
+}
+
+fn is_named_element_with_id_attribute(elem: &Element) -> bool {
+ elem.is_html_element()
+}