/
githubmirror
/
servo
Обзор
Документация
Войти
/
githubmirror
/
servo
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
components/script/dom/reporting/reportingobserver.rs
344 строки
14 KB
Josh Matthews
script: Ensure event handlers on adopted elements check the owner document. (#46596)
18 июл 2026, 07:29
Не верифицирован
18 июл 2026, 07:29
1c68dd1
Код
Авторство
О чём код?
/* 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 std::cell::Cell; use std::rc::Rc; use std::time::{SystemTime, UNIX_EPOCH}; use dom_struct::dom_struct; use js::context::JSContext; use js::rust::HandleObject; use script_bindings::callback::OwnerWindow; use script_bindings::cell::DomRefCell; use script_bindings::match_domstring_ascii; use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto}; use script_bindings::str::DOMString; use servo_url::ServoUrl; use crate::dom::bindings::callback::ExceptionHandling; use crate::dom::bindings::codegen::Bindings::CSPViolationReportBodyBinding::CSPViolationReportBody; use crate::dom::bindings::codegen::Bindings::ReportingObserverBinding::{ Report, ReportList, ReportingObserverCallback, ReportingObserverMethods, ReportingObserverOptions, }; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::num::Finite; use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::DomGlobal; use crate::dom::bindings::root::DomRoot; use crate::dom::globalscope::GlobalScope; use crate::dom::window::Window; use crate::dom::workerglobalscope::WorkerGlobalScope; #[dom_struct] pub(crate) struct ReportingObserver { reflector_: Reflector, #[conditional_malloc_size_of] callback: Rc<ReportingObserverCallback>, buffered: Cell<bool>, types: DomRefCell<Vec<DOMString>>, report_queue: DomRefCell<Vec<Report>>, } impl ReportingObserver { fn new_inherited( callback: Rc<ReportingObserverCallback>, options: &ReportingObserverOptions, ) -> Self { Self { reflector_: Reflector::new(), callback, buffered: Cell::new(options.buffered), types: DomRefCell::new(options.types.clone().unwrap_or_default()), report_queue: Default::default(), } } fn new_with_proto( cx: &mut JSContext, callback: Rc<ReportingObserverCallback>, options: &ReportingObserverOptions, global: &GlobalScope, proto: Option<HandleObject>, ) -> DomRoot<Self> { reflect_dom_object_with_proto( cx, Box::new(Self::new_inherited(callback, options)), global, proto, ) } fn report_is_visible_to_reporting_observers(report: &Report) -> bool { match_domstring_ascii!(report.type_, // https://w3c.github.io/webappsec-csp/#reporting "csp-violation" => true, _ => false, ) } /// <https://w3c.github.io/reporting/#add-report> fn add_report_to_observer(&self, report: &Report) { // Step 1. If report’s type is not visible to ReportingObservers, return. if !Self::report_is_visible_to_reporting_observers(report) { return; } // Step 2. If observer’s options has a non-empty types member which does not contain report’s type, return. let types = self.types.borrow(); if !types.is_empty() && !types.contains(&report.type_) { return; } // Step 3. Create a new Report r with type initialized to report’s type, // url initialized to report’s url, and body initialized to report’s body. let report = Report { type_: report.type_.clone(), url: report.url.clone(), body: report.body.clone(), destination: report.destination.clone(), attempts: report.attempts, timestamp: report.timestamp, }; // Step 4. Append r to observer’s report queue. self.report_queue.borrow_mut().push(report); // Step 5. If the size of observer’s report queue is 1: if self.report_queue.borrow().len() == 1 { // Step 5.1. Let global be observer’s relevant global object. let global = self.global(); // Step 5.2. Queue a task to § 4.4 Invoke reporting observers with notify list // with a copy of global’s registered reporting observer list. let observers_global = Trusted::new(&*global); global.task_manager().dom_manipulation_task_source().queue( task!(notify_reporting_observers: move |cx| { Self::invoke_reporting_observers_with_notify_list( cx, observers_global.root().registered_reporting_observers() ); }), ); } } /// <https://w3c.github.io/reporting/#notify-observers> pub(crate) fn notify_reporting_observers_on_scope(global: &GlobalScope, report: &Report) { // Step 1. For each ReportingObserver observer registered with scope, // execute § 4.3 Add report to observer on report and observer. for observer in global.registered_reporting_observers().iter() { observer.add_report_to_observer(report); } // Step 2. Append report to scope’s report buffer. global.append_report(report.clone()); // Step 3. Let type be report’s type. // TODO(37328) // Step 4. If scope’s report buffer now contains more than 100 reports with // type equal to type, remove the earliest item with type equal to type in the report buffer. // TODO(37328) } /// <https://w3c.github.io/reporting/#invoke-observers> fn invoke_reporting_observers_with_notify_list( cx: &mut JSContext, notify_list: Vec<DomRoot<ReportingObserver>>, ) { // Step 1. For each ReportingObserver observer in notify list: for observer in notify_list.iter() { // Step 1.1. If observer’s report queue is empty, then continue. if observer.report_queue.borrow().is_empty() { continue; } // Step 1.2. Let reports be a copy of observer’s report queue // Step 1.3. Empty observer’s report queue let reports = std::mem::take(&mut *observer.report_queue.borrow_mut()); // Step 1.4. Invoke observer’s callback with « reports, observer » and "report", // and with observer as the callback this value. let _ = observer.callback.Call_( cx, &**observer, reports, observer, ExceptionHandling::Report, ); } } /// <https://w3c.github.io/reporting/#generate-a-report> fn generate_a_report( global: &GlobalScope, type_: DOMString, url: Option<ServoUrl>, body: Option<CSPViolationReportBody>, destination: DOMString, ) -> Report { // Step 2. If url was not provided by the caller, let url be settings’s creation URL. let url = url.unwrap_or(global.creation_url()); // Step 3. Set url’s username to the empty string, and its password to null. // Step 4. Set report’s url to the result of executing the URL serializer // on url with the exclude fragment flag set. let url = Self::strip_url_for_reports(url).into(); // Step 1. Let report be a new report object with its values initialized as follows: // Step 5. Return report. Report { type_, url, body, destination, timestamp: Finite::wrap( SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or_default() .as_millis() as f64, ), attempts: 0, } } /// <https://w3c.github.io/reporting/#generate-and-queue-a-report> pub(crate) fn generate_and_queue_a_report( global: &GlobalScope, type_: DOMString, body: Option<CSPViolationReportBody>, destination: DOMString, ) { // Step 1. Let settings be context’s relevant settings object. // Step 2. Let report be the result of running generate a report with data, type, destination and settings. let report = Self::generate_a_report(global, type_, None, body, destination); // Step 3. If settings is given, then // Step 3.1. Let scope be settings’s global object. // Step 3.2. If scope is an object implementing WindowOrWorkerGlobalScope, then // execute § 4.2 Notify reporting observers on scope with report with scope and report. Self::notify_reporting_observers_on_scope(global, &report); // Step 4. Append report to context’s reports. global.append_report(report); } /// <https://w3c.github.io/webappsec-csp/#strip-url-for-use-in-reports> pub(crate) fn strip_url_for_reports(mut url: ServoUrl) -> String { let scheme = url.scheme(); // Step 1: If url’s scheme is not an HTTP(S) scheme, then return url’s scheme. // // Note we also treat ws(s) schemes as HTTP(S) per // https://github.com/w3c/webappsec-csp/issues/735 if scheme != "https" && scheme != "http" && scheme != "ws" && scheme != "wss" { return scheme.to_owned(); } // Step 2: Set url’s fragment to the empty string. url.set_fragment(None); // Step 3: Set url’s username to the empty string. let _ = url.set_username(""); // Step 4: Set url’s password to the empty string. let _ = url.set_password(None); // Step 5: Return the result of executing the URL serializer on url. url.into_string() } } impl ReportingObserverMethods<crate::DomTypeHolder> for ReportingObserver { /// <https://w3c.github.io/reporting/#dom-reportingobserver-reportingobserver> fn Constructor( cx: &mut JSContext, global: &GlobalScope, proto: Option<HandleObject>, callback: Rc<ReportingObserverCallback>, options: &ReportingObserverOptions, ) -> DomRoot<ReportingObserver> { // Step 1. Create a new ReportingObserver object observer. // Step 2. Set observer’s callback to callback. // Step 3. Set observer’s options to options. // Step 4. Return observer. ReportingObserver::new_with_proto(cx, callback, options, global, proto) } /// <https://w3c.github.io/reporting/#dom-reportingobserver-observe> fn Observe(&self) { // Step 1. Let global be the be the relevant global object of this. let global = &self.global(); // Step 2. Append this to the global’s registered reporting observer list. global.append_reporting_observer(self); // Step 3. If this’s buffered option is false, return. if !self.buffered.get() { return; } // Step 4. Set this’s buffered option to false. self.buffered.set(false); // Step 5.For each report in global’s report buffer, queue a task to // execute § 4.3 Add report to observer with report and this. for report in global.buffered_reports() { // TODO(37328): Figure out how to put this in a task self.add_report_to_observer(&report); } } /// <https://w3c.github.io/reporting/#dom-reportingobserver-disconnect> fn Disconnect(&self) { // Step 1. If this is not registered, return. // Skipped, as this is handled in `remove_reporting_observer` // Step 2. Let global be the relevant global object of this. let global = &self.global(); // Step 3. Remove this from global’s registered reporting observer list. global.remove_reporting_observer(self); } /// <https://w3c.github.io/reporting/#dom-reportingobserver-takerecords> fn TakeRecords(&self) -> ReportList { // Step 1. Let reports be a copy of this’s report queue. // Step 2. Empty this’s report queue. // Step 3. Return reports. std::mem::take(&mut *self.report_queue.borrow_mut()) } } impl GlobalScope { fn append_reporting_observer(&self, reporting_observer: &ReportingObserver) { if let Some(window) = self.downcast::<Window>() { return window.append_reporting_observer(reporting_observer); } if let Some(worker) = self.downcast::<WorkerGlobalScope>() { return worker.append_reporting_observer(reporting_observer); } unreachable!(); } fn remove_reporting_observer(&self, reporting_observer: &ReportingObserver) { if let Some(window) = self.downcast::<Window>() { return window.remove_reporting_observer(reporting_observer); } if let Some(worker) = self.downcast::<WorkerGlobalScope>() { return worker.remove_reporting_observer(reporting_observer); } unreachable!(); } fn registered_reporting_observers(&self) -> Vec<DomRoot<ReportingObserver>> { if let Some(window) = self.downcast::<Window>() { return window.registered_reporting_observers(); } if let Some(worker) = self.downcast::<WorkerGlobalScope>() { return worker.registered_reporting_observers(); } unreachable!(); } fn append_report(&self, report: Report) { if let Some(window) = self.downcast::<Window>() { return window.append_report(report); } if let Some(worker) = self.downcast::<WorkerGlobalScope>() { return worker.append_report(report); } unreachable!(); } fn buffered_reports(&self) -> Vec<Report> { if let Some(window) = self.downcast::<Window>() { return window.buffered_reports(); } if let Some(worker) = self.downcast::<WorkerGlobalScope>() { return worker.buffered_reports(); } unreachable!(); } } impl OwnerWindow<crate::DomTypeHolder> for ReportingObserver {}