diff --git a/components/script/dom/bindings/frozenarray.rs b/components/script/dom/bindings/frozenarray.rs new file mode 100644 index 00000000000..5f2e6e395ad --- /dev/null +++ b/components/script/dom/bindings/frozenarray.rs @@ -0,0 +1,52 @@ +/* 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 js::conversions::ToJSValConvertible; +use js::jsapi::Heap; +use js::jsval::JSVal; +use js::rust::MutableHandleValue; + +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::utils::to_frozen_array; +use crate::script_runtime::JSContext; + +#[derive(JSTraceable)] +pub struct CachedFrozenArray { + frozen_value: DomRefCell>>, +} + +impl CachedFrozenArray { + pub fn new() -> CachedFrozenArray { + CachedFrozenArray { + frozen_value: DomRefCell::new(None), + } + } + + pub fn get_or_init Vec, T: ToJSValConvertible>( + &self, + f: F, + cx: JSContext, + mut retval: MutableHandleValue, + ) { + if let Some(inner) = &*self.frozen_value.borrow() { + retval.set(inner.get()); + return; + } + + let array = f(); + to_frozen_array(array.as_slice(), cx, retval); + + // Safety: need to create the Heap value in its final memory location before setting it. + *self.frozen_value.borrow_mut() = Some(Heap::default()); + self.frozen_value + .borrow() + .as_ref() + .unwrap() + .set(retval.get()); + } + + pub fn clear(&self) { + *self.frozen_value.borrow_mut() = None; + } +} diff --git a/components/script/dom/bindings/mod.rs b/components/script/dom/bindings/mod.rs index 69c327015b3..d291f8e353f 100644 --- a/components/script/dom/bindings/mod.rs +++ b/components/script/dom/bindings/mod.rs @@ -142,6 +142,7 @@ pub mod constructor; pub mod conversions; pub mod error; pub mod finalize; +pub mod frozenarray; pub mod guard; pub mod import; pub mod inheritance; diff --git a/components/script/dom/extendablemessageevent.rs b/components/script/dom/extendablemessageevent.rs index c4f0ccb8249..1bad29e3c78 100644 --- a/components/script/dom/extendablemessageevent.rs +++ b/components/script/dom/extendablemessageevent.rs @@ -8,17 +8,16 @@ use js::jsval::JSVal; use js::rust::{HandleObject, HandleValue, MutableHandleValue}; use servo_atoms::Atom; -use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::ExtendableEventBinding::ExtendableEvent_Binding::ExtendableEventMethods; use crate::dom::bindings::codegen::Bindings::ExtendableMessageEventBinding; use crate::dom::bindings::codegen::Bindings::ExtendableMessageEventBinding::ExtendableMessageEventMethods; use crate::dom::bindings::error::Fallible; +use crate::dom::bindings::frozenarray::CachedFrozenArray; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::reflect_dom_object_with_proto; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::DOMString; use crate::dom::bindings::trace::RootedTraceableBox; -use crate::dom::bindings::utils::to_frozen_array; use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; use crate::dom::extendableevent::ExtendableEvent; @@ -42,7 +41,7 @@ pub struct ExtendableMessageEvent { /// ports: Vec>, #[ignore_malloc_size_of = "mozjs"] - frozen_ports: DomRefCell>>, + frozen_ports: CachedFrozenArray, } #[allow(non_snake_case)] @@ -61,7 +60,7 @@ impl ExtendableMessageEvent { .into_iter() .map(|port| Dom::from_ref(&*port)) .collect(), - frozen_ports: DomRefCell::new(None), + frozen_ports: CachedFrozenArray::new(), } } @@ -208,25 +207,16 @@ impl ExtendableMessageEventMethods for ExtendableMessageEvent { } /// - fn Ports(&self, cx: JSContext, mut retval: MutableHandleValue) { - if let Some(ports) = &*self.frozen_ports.borrow() { - retval.set(ports.get()); - return; - } - - let ports: Vec> = self - .ports - .iter() - .map(|port| DomRoot::from_ref(&**port)) - .collect(); - to_frozen_array(ports.as_slice(), cx, retval); - - // Safety: need to create the Heap value in its final memory location before setting it. - *self.frozen_ports.borrow_mut() = Some(Heap::default()); - self.frozen_ports - .borrow() - .as_ref() - .unwrap() - .set(retval.get()); + fn Ports(&self, cx: JSContext, retval: MutableHandleValue) { + self.frozen_ports.get_or_init( + || { + self.ports + .iter() + .map(|port| DomRoot::from_ref(&**port)) + .collect() + }, + cx, + retval, + ); } } diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 4cac0ca870f..35000732eb9 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -31,7 +31,7 @@ use js::jsapi::{ InstantiateGlobalStencil, InstantiateOptions, JSContext, JSObject, JSScript, RuntimeCode, SetScriptPrivate, }; -use js::jsval::{JSVal, PrivateValue, UndefinedValue}; +use js::jsval::{PrivateValue, UndefinedValue}; use js::panic::maybe_resume_unwind; use js::rust::wrappers::{JS_ExecuteScript, JS_GetScriptPrivate}; use js::rust::{ @@ -81,6 +81,7 @@ use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use crate::dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods; use crate::dom::bindings::conversions::{root_from_object, root_from_object_static}; use crate::dom::bindings::error::{report_pending_exception, Error, ErrorInfo}; +use crate::dom::bindings::frozenarray::CachedFrozenArray; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::refcounted::{Trusted, TrustedPromise}; use crate::dom::bindings::reflector::DomObject; @@ -88,7 +89,6 @@ use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::bindings::settings_stack::{entry_global, incumbent_global, AutoEntryScript}; use crate::dom::bindings::str::DOMString; use crate::dom::bindings::structuredclone; -use crate::dom::bindings::utils::to_frozen_array; use crate::dom::bindings::weakref::{DOMTracker, WeakRef}; use crate::dom::blob::Blob; use crate::dom::broadcastchannel::BroadcastChannel; @@ -336,7 +336,7 @@ pub struct GlobalScope { // https://w3c.github.io/performance-timeline/#supportedentrytypes-attribute #[ignore_malloc_size_of = "mozjs"] - frozen_supported_performance_entry_types: DomRefCell>>, + frozen_supported_performance_entry_types: CachedFrozenArray, /// currect https state (from previous request) #[no_trace] @@ -798,7 +798,7 @@ impl GlobalScope { user_agent, gpu_id_hub, gpu_devices: DomRefCell::new(HashMapTracedValues::new()), - frozen_supported_performance_entry_types: DomRefCell::new(Default::default()), + frozen_supported_performance_entry_types: CachedFrozenArray::new(), https_state: Cell::new(HttpsState::None), console_group_stack: DomRefCell::new(Vec::new()), console_count_map: Default::default(), @@ -3072,29 +3072,17 @@ impl GlobalScope { } /// - pub fn supported_performance_entry_types( - &self, - cx: SafeJSContext, - mut retval: MutableHandleValue, - ) { - if let Some(types) = &*self.frozen_supported_performance_entry_types.borrow() { - retval.set(types.get()); - return; - } - - let types: Vec = VALID_ENTRY_TYPES - .iter() - .map(|t| DOMString::from(t.to_string())) - .collect(); - to_frozen_array(types.as_slice(), cx, retval); - - // Safety: need to create the Heap value in its final memory location before setting it. - *self.frozen_supported_performance_entry_types.borrow_mut() = Some(Heap::default()); - self.frozen_supported_performance_entry_types - .borrow() - .as_ref() - .unwrap() - .set(retval.get()); + pub fn supported_performance_entry_types(&self, cx: SafeJSContext, retval: MutableHandleValue) { + self.frozen_supported_performance_entry_types.get_or_init( + || { + VALID_ENTRY_TYPES + .iter() + .map(|t| DOMString::from(t.to_string())) + .collect() + }, + cx, + retval, + ); } pub fn is_headless(&self) -> bool { diff --git a/components/script/dom/messageevent.rs b/components/script/dom/messageevent.rs index 004c80aad31..7d2db88b4a1 100644 --- a/components/script/dom/messageevent.rs +++ b/components/script/dom/messageevent.rs @@ -14,12 +14,12 @@ use crate::dom::bindings::codegen::Bindings::MessageEventBinding; use crate::dom::bindings::codegen::Bindings::MessageEventBinding::MessageEventMethods; use crate::dom::bindings::codegen::UnionTypes::WindowProxyOrMessagePortOrServiceWorker; use crate::dom::bindings::error::Fallible; +use crate::dom::bindings::frozenarray::CachedFrozenArray; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::reflect_dom_object_with_proto; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::DOMString; use crate::dom::bindings::trace::RootedTraceableBox; -use crate::dom::bindings::utils::to_frozen_array; use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; @@ -64,7 +64,7 @@ pub struct MessageEvent { lastEventId: DomRefCell, ports: DomRefCell>>, #[ignore_malloc_size_of = "mozjs"] - frozen_ports: DomRefCell>>, + frozen_ports: CachedFrozenArray, } #[allow(non_snake_case)] @@ -87,7 +87,7 @@ impl MessageEvent { .map(|port| Dom::from_ref(&*port)) .collect(), ), - frozen_ports: DomRefCell::new(None), + frozen_ports: CachedFrozenArray::new(), } } @@ -302,27 +302,18 @@ impl MessageEventMethods for MessageEvent { } /// - fn Ports(&self, cx: JSContext, mut retval: MutableHandleValue) { - if let Some(ports) = &*self.frozen_ports.borrow() { - retval.set(ports.get()); - return; - } - - let ports: Vec> = self - .ports - .borrow() - .iter() - .map(|port| DomRoot::from_ref(&**port)) - .collect(); - to_frozen_array(ports.as_slice(), cx, retval); - - // Safety: need to create the Heap value in its final memory location before setting it. - *self.frozen_ports.borrow_mut() = Some(Heap::default()); - self.frozen_ports - .borrow() - .as_ref() - .unwrap() - .set(retval.get()); + fn Ports(&self, cx: JSContext, retval: MutableHandleValue) { + self.frozen_ports.get_or_init( + || { + self.ports + .borrow() + .iter() + .map(|port| DomRoot::from_ref(&**port)) + .collect() + }, + cx, + retval, + ); } /// @@ -347,7 +338,7 @@ impl MessageEventMethods for MessageEvent { .into_iter() .map(|port| Dom::from_ref(&*port)) .collect(); - *self.frozen_ports.borrow_mut() = None; + self.frozen_ports.clear(); self.event .init_event(Atom::from(type_), bubbles, cancelable); }