mirror of
https://github.com/servo/servo.git
synced 2025-07-15 11:23:39 +01:00
Implement initial version of ReportingObserver (#37905)
The specification moved around lately with how it defines its reports and report bodies. They became dictionaries, but are currently missing some fields [1]. Most tests won't be passing yet, since the `Reporting-Endpoints` header isn't used yet. In fact, the specification leaves it up to the browser to figure out when to run this task [2]. I am not sure if there some background scheduling we can do here. Confirmed with content-security-policy/reporting-api/ report-to-directive-allowed-in-meta.https.sub.html that the callback is invoked. The test doesn't pass, since the `describe_scripted_caller` is empty for HTML elements. Thus the `source_file` is empty, whereas it should be equivalent to the current document URL. Part of #37328 Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com> [1]: https://github.com/w3c/reporting/issues/286 [2]: https://w3c.github.io/reporting/#report-delivery
This commit is contained in:
parent
3d4868592a
commit
fcb2a4cd95
20 changed files with 551 additions and 101 deletions
|
@ -7,11 +7,14 @@ use serde::Serialize;
|
|||
use servo_url::ServoUrl;
|
||||
|
||||
use crate::conversions::Convert;
|
||||
use crate::dom::bindings::codegen::Bindings::CSPViolationReportBodyBinding::CSPViolationReportBody;
|
||||
use crate::dom::bindings::codegen::Bindings::EventBinding::EventInit;
|
||||
use crate::dom::bindings::codegen::Bindings::ReportingObserverBinding::ReportBody;
|
||||
use crate::dom::bindings::codegen::Bindings::SecurityPolicyViolationEventBinding::{
|
||||
SecurityPolicyViolationEventDisposition, SecurityPolicyViolationEventInit,
|
||||
};
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::reportingobserver::ReportingObserver;
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
@ -77,6 +80,28 @@ impl Convert<SecurityPolicyViolationEventInit> for SecurityPolicyViolationReport
|
|||
}
|
||||
}
|
||||
|
||||
impl Convert<CSPViolationReportBody> for SecurityPolicyViolationReport {
|
||||
fn convert(self) -> CSPViolationReportBody {
|
||||
CSPViolationReportBody {
|
||||
sample: self.sample.map(|s| s.into()),
|
||||
blockedURL: Some(self.blocked_url.into()),
|
||||
// TODO(37328): Why does /content-security-policy/reporting-api/
|
||||
// report-to-directive-allowed-in-meta.https.sub.html expect this to be
|
||||
// empty, yet the spec expects us to copy referrer from SecurityPolicyViolationReport
|
||||
referrer: Some("".to_owned().into()),
|
||||
statusCode: self.status_code,
|
||||
documentURL: self.document_url.into(),
|
||||
sourceFile: Some(self.source_file.into()),
|
||||
effectiveDirective: self.effective_directive.into(),
|
||||
lineNumber: Some(self.line_number),
|
||||
columnNumber: Some(self.column_number),
|
||||
originalPolicy: self.original_policy.into(),
|
||||
disposition: self.disposition,
|
||||
parent: ReportBody::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://www.w3.org/TR/CSP/#deprecated-serialize-violation>
|
||||
impl From<SecurityPolicyViolationReport> for CSPReportUriViolationReportBody {
|
||||
fn from(value: SecurityPolicyViolationReport) -> Self {
|
||||
|
@ -101,7 +126,7 @@ impl From<SecurityPolicyViolationReport> for CSPReportUriViolationReportBody {
|
|||
// Step 2.1. Set body["source-file'] to the result of
|
||||
// executing § 5.4 Strip URL for use in reports on violation’s source file.
|
||||
converted.source_file = ServoUrl::parse(&value.source_file)
|
||||
.map(strip_url_for_reports)
|
||||
.map(ReportingObserver::strip_url_for_reports)
|
||||
.ok();
|
||||
// Step 2.2. Set body["line-number"] to violation’s line number.
|
||||
converted.line_number = Some(value.line_number);
|
||||
|
@ -187,15 +212,15 @@ impl CSPViolationReportBuilder {
|
|||
SecurityPolicyViolationReport {
|
||||
violated_directive: self.effective_directive.clone(),
|
||||
effective_directive: self.effective_directive.clone(),
|
||||
document_url: strip_url_for_reports(global.get_url()),
|
||||
document_url: ReportingObserver::strip_url_for_reports(global.get_url()),
|
||||
disposition: match self.report_only {
|
||||
true => SecurityPolicyViolationEventDisposition::Report,
|
||||
false => SecurityPolicyViolationEventDisposition::Enforce,
|
||||
},
|
||||
// https://w3c.github.io/webappsec-csp/#violation-referrer
|
||||
referrer: match global.get_referrer() {
|
||||
Referrer::Client(url) => strip_url_for_reports(url),
|
||||
Referrer::ReferrerUrl(url) => strip_url_for_reports(url),
|
||||
Referrer::Client(url) => ReportingObserver::strip_url_for_reports(url),
|
||||
Referrer::ReferrerUrl(url) => ReportingObserver::strip_url_for_reports(url),
|
||||
_ => "".to_owned(),
|
||||
},
|
||||
sample: self.sample,
|
||||
|
@ -218,20 +243,3 @@ fn serialize_disposition<S: serde::Serializer>(
|
|||
SecurityPolicyViolationEventDisposition::Enforce => serializer.serialize_str("enforce"),
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://w3c.github.io/webappsec-csp/#strip-url-for-use-in-reports>
|
||||
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.
|
||||
if scheme != "https" && scheme != "http" {
|
||||
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()
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@ use crate::dom::bindings::codegen::Bindings::NotificationBinding::NotificationPe
|
|||
use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::{
|
||||
PermissionName, PermissionState,
|
||||
};
|
||||
use crate::dom::bindings::codegen::Bindings::ReportingObserverBinding::Report;
|
||||
use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
|
||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
|
||||
|
@ -109,6 +110,7 @@ use crate::dom::performance::Performance;
|
|||
use crate::dom::performanceobserver::VALID_ENTRY_TYPES;
|
||||
use crate::dom::promise::Promise;
|
||||
use crate::dom::readablestream::{CrossRealmTransformReadable, ReadableStream};
|
||||
use crate::dom::reportingobserver::ReportingObserver;
|
||||
use crate::dom::serviceworker::ServiceWorker;
|
||||
use crate::dom::serviceworkerregistration::ServiceWorkerRegistration;
|
||||
use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory;
|
||||
|
@ -3332,6 +3334,56 @@ impl GlobalScope {
|
|||
unreachable!();
|
||||
}
|
||||
|
||||
pub(crate) fn append_reporting_observer(&self, reporting_observer: &ReportingObserver) {
|
||||
if let Some(window) = self.downcast::<Window>() {
|
||||
return window.append_reporting_observer(DomRoot::from_ref(reporting_observer));
|
||||
}
|
||||
if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
|
||||
return worker.append_reporting_observer(DomRoot::from_ref(reporting_observer));
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
pub(crate) 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!();
|
||||
}
|
||||
|
||||
pub(crate) 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!();
|
||||
}
|
||||
|
||||
pub(crate) 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!();
|
||||
}
|
||||
|
||||
pub(crate) 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!();
|
||||
}
|
||||
|
||||
pub(crate) fn import_map(&self) -> Ref<'_, ImportMap> {
|
||||
self.import_map.borrow()
|
||||
}
|
||||
|
|
|
@ -517,6 +517,7 @@ pub(crate) mod readablestreambyobrequest;
|
|||
pub(crate) mod readablestreamdefaultcontroller;
|
||||
pub(crate) mod readablestreamdefaultreader;
|
||||
pub(crate) mod readablestreamgenericreader;
|
||||
pub(crate) mod reportingobserver;
|
||||
pub(crate) mod request;
|
||||
pub(crate) mod resizeobserver;
|
||||
pub(crate) mod resizeobserverentry;
|
||||
|
|
278
components/script/dom/reportingobserver.rs
Normal file
278
components/script/dom/reportingobserver.rs
Normal file
|
@ -0,0 +1,278 @@
|
|||
/* 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::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use dom_struct::dom_struct;
|
||||
use js::rust::HandleObject;
|
||||
use script_bindings::str::DOMString;
|
||||
use servo_url::ServoUrl;
|
||||
|
||||
use crate::dom::bindings::callback::ExceptionHandling;
|
||||
use crate::dom::bindings::cell::DomRefCell;
|
||||
use crate::dom::bindings::codegen::Bindings::CSPViolationReportBodyBinding::CSPViolationReportBody;
|
||||
use crate::dom::bindings::codegen::Bindings::ReportingObserverBinding::{
|
||||
Report, ReportList, ReportingObserverCallback, ReportingObserverMethods,
|
||||
ReportingObserverOptions,
|
||||
};
|
||||
use crate::dom::bindings::num::Finite;
|
||||
use crate::dom::bindings::refcounted::Trusted;
|
||||
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::script_runtime::CanGc;
|
||||
|
||||
#[dom_struct]
|
||||
pub(crate) struct ReportingObserver {
|
||||
reflector_: Reflector,
|
||||
|
||||
#[ignore_malloc_size_of = "Rc has unclear ownership"]
|
||||
callback: Rc<ReportingObserverCallback>,
|
||||
buffered: RefCell<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: RefCell::new(options.buffered),
|
||||
types: DomRefCell::new(options.types.clone().unwrap_or_default()),
|
||||
report_queue: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
|
||||
pub(crate) fn new_with_proto(
|
||||
callback: Rc<ReportingObserverCallback>,
|
||||
options: &ReportingObserverOptions,
|
||||
global: &GlobalScope,
|
||||
proto: Option<HandleObject>,
|
||||
can_gc: CanGc,
|
||||
) -> DomRoot<Self> {
|
||||
reflect_dom_object_with_proto(
|
||||
Box::new(Self::new_inherited(callback, options)),
|
||||
global,
|
||||
proto,
|
||||
can_gc,
|
||||
)
|
||||
}
|
||||
|
||||
fn report_is_visible_to_reporting_observers(report: &Report) -> bool {
|
||||
match report.type_.str() {
|
||||
// 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 || {
|
||||
Self::invoke_reporting_observers_with_notify_list(
|
||||
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(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_(
|
||||
&**observer,
|
||||
reports,
|
||||
observer,
|
||||
ExceptionHandling::Report,
|
||||
CanGc::note(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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().clone());
|
||||
// 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.
|
||||
if scheme != "https" && scheme != "http" {
|
||||
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(
|
||||
global: &GlobalScope,
|
||||
proto: Option<HandleObject>,
|
||||
can_gc: CanGc,
|
||||
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(callback, options, global, proto, can_gc)
|
||||
}
|
||||
|
||||
/// <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.borrow() {
|
||||
return;
|
||||
}
|
||||
// Step 4. Set this’s buffered option to false.
|
||||
*self.buffered.borrow_mut() = 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())
|
||||
}
|
||||
}
|
|
@ -102,6 +102,7 @@ use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{
|
|||
ImageBitmapOptions, ImageBitmapSource,
|
||||
};
|
||||
use crate::dom::bindings::codegen::Bindings::MediaQueryListBinding::MediaQueryList_Binding::MediaQueryListMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::ReportingObserverBinding::Report;
|
||||
use crate::dom::bindings::codegen::Bindings::RequestBinding::RequestInit;
|
||||
use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
|
||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::{
|
||||
|
@ -146,6 +147,7 @@ use crate::dom::navigator::Navigator;
|
|||
use crate::dom::node::{Node, NodeDamage, NodeTraits, from_untrusted_node_address};
|
||||
use crate::dom::performance::Performance;
|
||||
use crate::dom::promise::Promise;
|
||||
use crate::dom::reportingobserver::ReportingObserver;
|
||||
use crate::dom::screen::Screen;
|
||||
use crate::dom::selection::Selection;
|
||||
use crate::dom::shadowroot::ShadowRoot;
|
||||
|
@ -398,6 +400,12 @@ pub(crate) struct Window {
|
|||
|
||||
/// <https://dom.spec.whatwg.org/#window-current-event>
|
||||
current_event: DomRefCell<Option<Dom<Event>>>,
|
||||
|
||||
/// <https://w3c.github.io/reporting/#windoworworkerglobalscope-registered-reporting-observer-list>
|
||||
reporting_observer_list: DomRefCell<Vec<DomRoot<ReportingObserver>>>,
|
||||
|
||||
/// <https://w3c.github.io/reporting/#windoworworkerglobalscope-reports>
|
||||
report_list: DomRefCell<Vec<Report>>,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
|
@ -502,6 +510,35 @@ impl Window {
|
|||
self.window_proxy.get().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn append_reporting_observer(&self, reporting_observer: DomRoot<ReportingObserver>) {
|
||||
self.reporting_observer_list
|
||||
.borrow_mut()
|
||||
.push(reporting_observer);
|
||||
}
|
||||
|
||||
pub(crate) fn remove_reporting_observer(&self, reporting_observer: &ReportingObserver) {
|
||||
if let Some(index) = self
|
||||
.reporting_observer_list
|
||||
.borrow()
|
||||
.iter()
|
||||
.position(|observer| &**observer == reporting_observer)
|
||||
{
|
||||
self.reporting_observer_list.borrow_mut().remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn registered_reporting_observers(&self) -> Vec<DomRoot<ReportingObserver>> {
|
||||
self.reporting_observer_list.borrow().clone()
|
||||
}
|
||||
|
||||
pub(crate) fn append_report(&self, report: Report) {
|
||||
self.report_list.borrow_mut().push(report);
|
||||
}
|
||||
|
||||
pub(crate) fn buffered_reports(&self) -> Vec<Report> {
|
||||
self.report_list.borrow().clone()
|
||||
}
|
||||
|
||||
/// Returns the window proxy if it has not been discarded.
|
||||
/// <https://html.spec.whatwg.org/multipage/#a-browsing-context-is-discarded>
|
||||
pub(crate) fn undiscarded_window_proxy(&self) -> Option<DomRoot<WindowProxy>> {
|
||||
|
@ -3106,6 +3143,8 @@ impl Window {
|
|||
current_event: DomRefCell::new(None),
|
||||
theme: Cell::new(theme),
|
||||
trusted_types: Default::default(),
|
||||
reporting_observer_list: Default::default(),
|
||||
report_list: Default::default(),
|
||||
});
|
||||
|
||||
unsafe {
|
||||
|
|
|
@ -36,6 +36,7 @@ use crate::dom::bindings::cell::{DomRefCell, Ref};
|
|||
use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{
|
||||
ImageBitmapOptions, ImageBitmapSource,
|
||||
};
|
||||
use crate::dom::bindings::codegen::Bindings::ReportingObserverBinding::Report;
|
||||
use crate::dom::bindings::codegen::Bindings::RequestBinding::RequestInit;
|
||||
use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
|
||||
use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType;
|
||||
|
@ -56,6 +57,7 @@ use crate::dom::globalscope::GlobalScope;
|
|||
use crate::dom::idbfactory::IDBFactory;
|
||||
use crate::dom::performance::Performance;
|
||||
use crate::dom::promise::Promise;
|
||||
use crate::dom::reportingobserver::ReportingObserver;
|
||||
use crate::dom::trustedscripturl::TrustedScriptURL;
|
||||
use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory;
|
||||
use crate::dom::types::ImageBitmap;
|
||||
|
@ -139,6 +141,12 @@ pub(crate) struct WorkerGlobalScope {
|
|||
|
||||
#[no_trace]
|
||||
insecure_requests_policy: InsecureRequestsPolicy,
|
||||
|
||||
/// <https://w3c.github.io/reporting/#windoworworkerglobalscope-registered-reporting-observer-list>
|
||||
reporting_observer_list: DomRefCell<Vec<DomRoot<ReportingObserver>>>,
|
||||
|
||||
/// <https://w3c.github.io/reporting/#windoworworkerglobalscope-reports>
|
||||
report_list: DomRefCell<Vec<Report>>,
|
||||
}
|
||||
|
||||
impl WorkerGlobalScope {
|
||||
|
@ -196,6 +204,8 @@ impl WorkerGlobalScope {
|
|||
timer_scheduler: RefCell::default(),
|
||||
insecure_requests_policy,
|
||||
trusted_types: Default::default(),
|
||||
reporting_observer_list: Default::default(),
|
||||
report_list: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,6 +269,35 @@ impl WorkerGlobalScope {
|
|||
self.policy_container.borrow_mut().set_csp_list(csp_list);
|
||||
}
|
||||
|
||||
pub(crate) fn append_reporting_observer(&self, reporting_observer: DomRoot<ReportingObserver>) {
|
||||
self.reporting_observer_list
|
||||
.borrow_mut()
|
||||
.push(reporting_observer);
|
||||
}
|
||||
|
||||
pub(crate) fn remove_reporting_observer(&self, reporting_observer: &ReportingObserver) {
|
||||
if let Some(index) = self
|
||||
.reporting_observer_list
|
||||
.borrow()
|
||||
.iter()
|
||||
.position(|observer| &**observer == reporting_observer)
|
||||
{
|
||||
self.reporting_observer_list.borrow_mut().remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn registered_reporting_observers(&self) -> Vec<DomRoot<ReportingObserver>> {
|
||||
self.reporting_observer_list.borrow().clone()
|
||||
}
|
||||
|
||||
pub(crate) fn append_report(&self, report: Report) {
|
||||
self.report_list.borrow_mut().push(report);
|
||||
}
|
||||
|
||||
pub(crate) fn buffered_reports(&self) -> Vec<Report> {
|
||||
self.report_list.borrow().clone()
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the [`TimerScheduler`] for this [`ServiceWorkerGlobalScope`].
|
||||
pub(crate) fn timer_scheduler(&self) -> RefMut<TimerScheduler> {
|
||||
self.timer_scheduler.borrow_mut()
|
||||
|
|
|
@ -26,6 +26,7 @@ use crate::dom::csppolicyviolationreport::{
|
|||
use crate::dom::event::{Event, EventBubbles, EventCancelable, EventComposed};
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::performanceresourcetiming::InitiatorType;
|
||||
use crate::dom::reportingobserver::ReportingObserver;
|
||||
use crate::dom::securitypolicyviolationevent::SecurityPolicyViolationEvent;
|
||||
use crate::dom::types::GlobalScope;
|
||||
use crate::fetch::create_a_potential_cors_request;
|
||||
|
@ -156,6 +157,24 @@ impl TaskOnce for CSPViolationReportTask {
|
|||
{
|
||||
self.post_csp_violation_to_report_uri(report_uri_directive);
|
||||
}
|
||||
// Step 3.5. If violation’s policy’s directive set contains a directive named "report-to" directive:
|
||||
if let Some(report_to_directive) = self
|
||||
.violation_policy
|
||||
.directive_set
|
||||
.iter()
|
||||
.find(|directive| directive.name == "report-to")
|
||||
{
|
||||
// Step 3.5.1. Let body be a new CSPViolationReportBody, initialized as follows:
|
||||
let body = self.violation_report.clone().convert();
|
||||
// Step 3.5.2. Let settings object be violation’s global object’s relevant settings object.
|
||||
// Step 3.5.3. Generate and queue a report with the following arguments:
|
||||
ReportingObserver::generate_and_queue_a_report(
|
||||
&self.global.root(),
|
||||
"csp-violation".into(),
|
||||
Some(body),
|
||||
report_to_directive.value.join(" ").into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -799,6 +799,10 @@ Dictionaries = {
|
|||
'derives': ['Clone', 'Copy'],
|
||||
},
|
||||
|
||||
'CSPViolationReportBody': {
|
||||
'derives': ['Clone', 'MallocSizeOf'],
|
||||
},
|
||||
|
||||
'FontFaceDescriptors': {
|
||||
'derives': ['Clone', 'MallocSizeOf']
|
||||
},
|
||||
|
@ -831,6 +835,14 @@ Dictionaries = {
|
|||
'derives': ['Clone'],
|
||||
},
|
||||
|
||||
'Report': {
|
||||
'derives': ['Clone', 'MallocSizeOf'],
|
||||
},
|
||||
|
||||
'ReportBody': {
|
||||
'derives': ['Clone', 'MallocSizeOf'],
|
||||
},
|
||||
|
||||
'StereoPannerOptions': {
|
||||
'derives': ['Clone', 'Copy'],
|
||||
},
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/* 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/. */
|
||||
|
||||
// https://w3c.github.io/webappsec-csp/#dictdef-cspviolationreportbody
|
||||
|
||||
dictionary CSPViolationReportBody : ReportBody {
|
||||
required USVString documentURL;
|
||||
USVString referrer;
|
||||
USVString blockedURL;
|
||||
required DOMString effectiveDirective;
|
||||
required DOMString originalPolicy;
|
||||
USVString sourceFile;
|
||||
DOMString sample;
|
||||
required SecurityPolicyViolationEventDisposition disposition;
|
||||
required unsigned short statusCode;
|
||||
unsigned long lineNumber;
|
||||
unsigned long columnNumber;
|
||||
};
|
35
components/script_bindings/webidls/ReportingObserver.webidl
Normal file
35
components/script_bindings/webidls/ReportingObserver.webidl
Normal file
|
@ -0,0 +1,35 @@
|
|||
/* 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/. */
|
||||
|
||||
// https://w3c.github.io/reporting/#interface-reporting-observer
|
||||
|
||||
dictionary ReportBody {
|
||||
};
|
||||
|
||||
dictionary Report {
|
||||
required DOMString type;
|
||||
required DOMString url;
|
||||
required DOMString destination;
|
||||
required double timestamp;
|
||||
required long attempts;
|
||||
// TODO(37328): Change this to parent class ReportBody
|
||||
CSPViolationReportBody body;
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
interface ReportingObserver {
|
||||
constructor(ReportingObserverCallback callback, optional ReportingObserverOptions options = {});
|
||||
undefined observe();
|
||||
undefined disconnect();
|
||||
ReportList takeRecords();
|
||||
};
|
||||
|
||||
callback ReportingObserverCallback = undefined (sequence<Report> reports, ReportingObserver observer);
|
||||
|
||||
dictionary ReportingObserverOptions {
|
||||
sequence<DOMString> types;
|
||||
boolean buffered = false;
|
||||
};
|
||||
|
||||
typedef sequence<Report> ReportList;
|
|
@ -1,5 +1,5 @@
|
|||
[post-redirect-stacktrace.https.html]
|
||||
expected: ERROR
|
||||
expected: TIMEOUT
|
||||
[StackTrace do not leak cross-origin post-redirect URL]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[revalidate-not-blocked-by-csp.html]
|
||||
[Request revalidation aren't blocked by CSP]
|
||||
expected: FAIL
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
[reporting-subresource-corp.tentative.https.html]
|
||||
expected: TIMEOUT
|
||||
[[document\] blocked due to DIP]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
[[document\] blocked during redirect]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
||||
[destination: script]
|
||||
expected: FAIL
|
||||
|
@ -12,22 +13,22 @@
|
|||
expected: FAIL
|
||||
|
||||
[[dedicated worker\] blocked due to DIP]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
||||
[[dedicated worker\] blocked during redirect]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
||||
[[shared worker\] same-origin]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
||||
[[shared worker\] blocked by CORP: same-origin]
|
||||
expected: FAIL
|
||||
|
||||
[[shared worker\] blocked due to DIP]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
||||
[[shared worker\] blocked during redirect]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
||||
[[between service worker and page\] same-origin]
|
||||
expected: FAIL
|
||||
|
@ -42,10 +43,13 @@
|
|||
expected: FAIL
|
||||
|
||||
[[document with service worker\] same-origin]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
||||
[[document with service worker\] blocked due to DIP]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
||||
[[document with service worker\] blocked during redirect]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
||||
[[dedicated worker\] same-origin]
|
||||
expected: NOTRUN
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[document-reporting-bypass-report-to.https.sub.html]
|
||||
expected: TIMEOUT
|
||||
[document policy violation observed]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
[Only the Reporting-Endpoints configured endpoint received reports.]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
[document-reporting-named-endpoints.https.sub.html]
|
||||
[csp violation report observed]
|
||||
expected: FAIL
|
||||
|
||||
expected: TIMEOUT
|
||||
[document policy violation observed]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
[Reporting endpoints received reports.]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[document-reporting-override-endpoint.https.sub.html]
|
||||
expected: TIMEOUT
|
||||
[document policy violation observed]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
[Only the second reporting endpoint received reports.]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
|
54
tests/wpt/meta/reporting/idlharness.any.js.ini
vendored
54
tests/wpt/meta/reporting/idlharness.any.js.ini
vendored
|
@ -50,33 +50,6 @@
|
|||
[Report interface: attribute body]
|
||||
expected: FAIL
|
||||
|
||||
[ReportingObserver interface: existence and properties of interface object]
|
||||
expected: FAIL
|
||||
|
||||
[ReportingObserver interface object length]
|
||||
expected: FAIL
|
||||
|
||||
[ReportingObserver interface object name]
|
||||
expected: FAIL
|
||||
|
||||
[ReportingObserver interface: existence and properties of interface prototype object]
|
||||
expected: FAIL
|
||||
|
||||
[ReportingObserver interface: existence and properties of interface prototype object's "constructor" property]
|
||||
expected: FAIL
|
||||
|
||||
[ReportingObserver interface: existence and properties of interface prototype object's @@unscopables property]
|
||||
expected: FAIL
|
||||
|
||||
[ReportingObserver interface: operation observe()]
|
||||
expected: FAIL
|
||||
|
||||
[ReportingObserver interface: operation disconnect()]
|
||||
expected: FAIL
|
||||
|
||||
[ReportingObserver interface: operation takeRecords()]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[idlharness.any.worker.html]
|
||||
[ReportBody interface: existence and properties of interface object]
|
||||
|
@ -129,30 +102,3 @@
|
|||
|
||||
[Report interface: attribute body]
|
||||
expected: FAIL
|
||||
|
||||
[ReportingObserver interface: existence and properties of interface object]
|
||||
expected: FAIL
|
||||
|
||||
[ReportingObserver interface object length]
|
||||
expected: FAIL
|
||||
|
||||
[ReportingObserver interface object name]
|
||||
expected: FAIL
|
||||
|
||||
[ReportingObserver interface: existence and properties of interface prototype object]
|
||||
expected: FAIL
|
||||
|
||||
[ReportingObserver interface: existence and properties of interface prototype object's "constructor" property]
|
||||
expected: FAIL
|
||||
|
||||
[ReportingObserver interface: existence and properties of interface prototype object's @@unscopables property]
|
||||
expected: FAIL
|
||||
|
||||
[ReportingObserver interface: operation observe()]
|
||||
expected: FAIL
|
||||
|
||||
[ReportingObserver interface: operation disconnect()]
|
||||
expected: FAIL
|
||||
|
||||
[ReportingObserver interface: operation takeRecords()]
|
||||
expected: FAIL
|
||||
|
|
4
tests/wpt/mozilla/meta/MANIFEST.json
vendored
4
tests/wpt/mozilla/meta/MANIFEST.json
vendored
|
@ -13640,14 +13640,14 @@
|
|||
]
|
||||
],
|
||||
"interfaces.https.html": [
|
||||
"eee8c799727b91e00b512795756b693a5f121f86",
|
||||
"8a9345525360e7a7ce69e84e394b65a4cbc0ab34",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
]
|
||||
],
|
||||
"interfaces.worker.js": [
|
||||
"e86f34f261442aeaa7074c525fb4b1206219769d",
|
||||
"f217fd8fb6b46144bc3576a081cc6ce5db3129d5",
|
||||
[
|
||||
"mozilla/interfaces.worker.html",
|
||||
{}
|
||||
|
|
|
@ -293,6 +293,7 @@ test_interfaces([
|
|||
"ReadableStreamBYOBReader",
|
||||
"ReadableByteStreamController",
|
||||
"ReadableStreamBYOBRequest",
|
||||
"ReportingObserver",
|
||||
"Request",
|
||||
"ResizeObserver",
|
||||
"ResizeObserverEntry",
|
||||
|
|
|
@ -103,6 +103,7 @@ test_interfaces([
|
|||
"ReadableStreamBYOBReader",
|
||||
"ReadableByteStreamController",
|
||||
"ReadableStreamBYOBRequest",
|
||||
"ReportingObserver",
|
||||
"Request",
|
||||
"Response",
|
||||
"SecurityPolicyViolationEvent",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue