aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
Diffstat (limited to 'components')
-rw-r--r--components/canvas/canvas_data.rs9
-rw-r--r--components/constellation/constellation.rs2
-rw-r--r--components/devtools/actors/watcher.rs69
-rw-r--r--components/devtools/actors/worker.rs29
-rw-r--r--components/devtools/lib.rs71
-rw-r--r--components/layout/context.rs61
-rw-r--r--components/layout/display_list/mod.rs21
-rw-r--r--components/layout/display_list/stacking_context.rs9
-rw-r--r--components/layout/flexbox/layout.rs9
-rw-r--r--components/layout/flow/float.rs3
-rw-r--r--components/layout/flow/inline/line.rs3
-rw-r--r--components/layout/flow/inline/mod.rs3
-rw-r--r--components/layout/flow/mod.rs4
-rw-r--r--components/layout/flow/root.rs2
-rw-r--r--components/layout/fragment_tree/box_fragment.rs106
-rw-r--r--components/layout/fragment_tree/fragment.rs17
-rw-r--r--components/layout/fragment_tree/fragment_tree.rs21
-rw-r--r--components/layout/fragment_tree/positioning_fragment.rs2
-rw-r--r--components/layout/positioned.rs36
-rw-r--r--components/layout/replaced.rs6
-rw-r--r--components/layout/style_ext.rs105
-rw-r--r--components/layout/table/layout.rs6
-rw-r--r--components/layout/taffy/layout.rs5
-rw-r--r--components/script/canvas_state.rs87
-rw-r--r--components/script/dom/canvasrenderingcontext2d.rs36
-rw-r--r--components/script/dom/defaultteereadrequest.rs26
-rw-r--r--components/script/dom/defaultteeunderlyingsource.rs15
-rw-r--r--components/script/dom/document.rs10
-rw-r--r--components/script/dom/htmlformelement.rs10
-rw-r--r--components/script/dom/htmlinputelement.rs6
-rw-r--r--components/script/dom/htmlscriptelement.rs2
-rw-r--r--components/script/dom/node.rs110
-rw-r--r--components/script/dom/offscreencanvasrenderingcontext2d.rs2
-rw-r--r--components/script/dom/readablebytestreamcontroller.rs14
-rw-r--r--components/script/dom/readablestream.rs50
-rw-r--r--components/script/dom/readablestreambyobreader.rs4
-rw-r--r--components/script/dom/readablestreamdefaultcontroller.rs29
-rw-r--r--components/script/dom/readablestreamdefaultreader.rs4
-rw-r--r--components/script/dom/readablestreamgenericreader.rs15
-rw-r--r--components/script/dom/servoparser/html.rs13
-rw-r--r--components/script/dom/underlyingsourcecontainer.rs30
-rw-r--r--components/script/dom/window.rs12
-rw-r--r--components/script/dom/worker.rs12
-rw-r--r--components/script/dom/writablestream.rs120
-rw-r--r--components/script/dom/writablestreamdefaultcontroller.rs242
-rw-r--r--components/script/microtask.rs2
-rw-r--r--components/script/script_thread.rs3
-rw-r--r--components/script_bindings/codegen/Bindings.conf4
-rw-r--r--components/shared/devtools/lib.rs1
49 files changed, 1045 insertions, 413 deletions
diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs
index 99d6273813e..2667b7f6b44 100644
--- a/components/canvas/canvas_data.rs
+++ b/components/canvas/canvas_data.rs
@@ -28,6 +28,10 @@ use webrender_api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey
use crate::raqote_backend::Repetition;
+// Asserts on WR texture cache update for zero sized image with raw data.
+// https://github.com/servo/webrender/blob/main/webrender/src/texture_cache.rs#L1475
+const MIN_WR_IMAGE_SIZE: Size2D<u64> = Size2D::new(1, 1);
+
fn to_path(path: &[PathSegment], mut builder: Box<dyn GenericPathBuilder>) -> Path {
let mut build_ref = PathBuilderRef {
builder: &mut builder,
@@ -595,6 +599,7 @@ impl<'a> CanvasData<'a> {
compositor_api: CrossProcessCompositorApi,
font_context: Arc<FontContext>,
) -> CanvasData<'a> {
+ let size = size.max(MIN_WR_IMAGE_SIZE);
let backend = create_backend();
let draw_target = backend.create_drawtarget(size);
let image_key = compositor_api.generate_image_key().unwrap();
@@ -1402,7 +1407,9 @@ impl<'a> CanvasData<'a> {
}
pub fn recreate(&mut self, size: Option<Size2D<u64>>) {
- let size = size.unwrap_or_else(|| self.drawtarget.get_size().to_u64());
+ let size = size
+ .unwrap_or_else(|| self.drawtarget.get_size().to_u64())
+ .max(MIN_WR_IMAGE_SIZE);
self.drawtarget = self
.backend
.create_drawtarget(Size2D::new(size.width, size.height));
diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs
index 05081fe0ba7..2175028a81b 100644
--- a/components/constellation/constellation.rs
+++ b/components/constellation/constellation.rs
@@ -3744,8 +3744,8 @@ where
fn handle_load_complete_msg(&mut self, webview_id: WebViewId, pipeline_id: PipelineId) {
let mut webdriver_reset = false;
if let Some((expected_pipeline_id, ref reply_chan)) = self.webdriver.load_channel {
- debug!("Sending load for {:?} to WebDriver", expected_pipeline_id);
if expected_pipeline_id == pipeline_id {
+ debug!("Sending load for {:?} to WebDriver", expected_pipeline_id);
let _ = reply_chan.send(WebDriverLoadStatus::Complete);
webdriver_reset = true;
}
diff --git a/components/devtools/actors/watcher.rs b/components/devtools/actors/watcher.rs
index 6a84499b6dd..b0b2c755fd8 100644
--- a/components/devtools/actors/watcher.rs
+++ b/components/devtools/actors/watcher.rs
@@ -20,8 +20,10 @@ use serde_json::{Map, Value};
use self::network_parent::{NetworkParentActor, NetworkParentActorMsg};
use super::thread::ThreadActor;
+use super::worker::WorkerMsg;
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
use crate::actors::browsing_context::{BrowsingContextActor, BrowsingContextActorMsg};
+use crate::actors::root::RootActor;
use crate::actors::watcher::target_configuration::{
TargetConfigurationActor, TargetConfigurationActorMsg,
};
@@ -29,8 +31,8 @@ use crate::actors::watcher::thread_configuration::{
ThreadConfigurationActor, ThreadConfigurationActorMsg,
};
use crate::protocol::JsonPacketStream;
-use crate::resource::ResourceAvailable;
-use crate::{EmptyReplyMsg, StreamId};
+use crate::resource::{ResourceAvailable, ResourceAvailableReply};
+use crate::{EmptyReplyMsg, StreamId, WorkerActor};
pub mod network_parent;
pub mod target_configuration;
@@ -55,7 +57,7 @@ impl SessionContext {
supported_targets: HashMap::from([
("frame", true),
("process", false),
- ("worker", false),
+ ("worker", true),
("service_worker", false),
("shared_worker", false),
]),
@@ -103,11 +105,18 @@ pub enum SessionContextType {
}
#[derive(Serialize)]
+#[serde(untagged)]
+enum TargetActorMsg {
+ BrowsingContext(BrowsingContextActorMsg),
+ Worker(WorkerMsg),
+}
+
+#[derive(Serialize)]
struct WatchTargetsReply {
from: String,
#[serde(rename = "type")]
type_: String,
- target: BrowsingContextActorMsg,
+ target: TargetActorMsg,
}
#[derive(Serialize)]
@@ -212,16 +221,38 @@ impl Actor for WatcherActor {
_id: StreamId,
) -> Result<ActorMessageStatus, ()> {
let target = registry.find::<BrowsingContextActor>(&self.browsing_context_actor);
+ let root = registry.find::<RootActor>("root");
Ok(match msg_type {
"watchTargets" => {
- let msg = WatchTargetsReply {
- from: self.name(),
- type_: "target-available-form".into(),
- target: target.encodable(),
- };
- let _ = stream.write_json_packet(&msg);
+ // As per logs we either get targetType as "frame" or "worker"
+ let target_type = msg
+ .get("targetType")
+ .and_then(Value::as_str)
+ .unwrap_or("frame"); // default to "frame"
+
+ if target_type == "frame" {
+ let msg = WatchTargetsReply {
+ from: self.name(),
+ type_: "target-available-form".into(),
+ target: TargetActorMsg::BrowsingContext(target.encodable()),
+ };
+ let _ = stream.write_json_packet(&msg);
- target.frame_update(stream);
+ target.frame_update(stream);
+ } else if target_type == "worker" {
+ for worker_name in &root.workers {
+ let worker = registry.find::<WorkerActor>(worker_name);
+ let worker_msg = WatchTargetsReply {
+ from: self.name(),
+ type_: "target-available-form".into(),
+ target: TargetActorMsg::Worker(worker.encodable()),
+ };
+ let _ = stream.write_json_packet(&worker_msg);
+ }
+ } else {
+ warn!("Unexpected target_type: {}", target_type);
+ return Ok(ActorMessageStatus::Ignored);
+ }
// Messages that contain a `type` field are used to send event callbacks, but they
// don't count as a reply. Since every message needs to be responded, we send an
@@ -267,6 +298,22 @@ impl Actor for WatcherActor {
let thread_actor = registry.find::<ThreadActor>(&target.thread);
let sources = thread_actor.source_manager.sources();
target.resources_available(sources.iter().collect(), "source".into());
+
+ for worker_name in &root.workers {
+ let worker = registry.find::<WorkerActor>(worker_name);
+ let thread = registry.find::<ThreadActor>(&worker.thread);
+ let worker_sources = thread.source_manager.sources();
+
+ let msg = ResourceAvailableReply {
+ from: worker.name(),
+ type_: "resources-available-array".into(),
+ array: vec![(
+ "source".to_string(),
+ worker_sources.iter().cloned().collect(),
+ )],
+ };
+ let _ = stream.write_json_packet(&msg);
+ }
},
"console-message" | "error-message" => {},
_ => warn!("resource {} not handled yet", resource),
diff --git a/components/devtools/actors/worker.rs b/components/devtools/actors/worker.rs
index 42c9d9a9c28..68ff56fb3b2 100644
--- a/components/devtools/actors/worker.rs
+++ b/components/devtools/actors/worker.rs
@@ -17,7 +17,7 @@ use servo_url::ServoUrl;
use crate::StreamId;
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
use crate::protocol::JsonPacketStream;
-use crate::resource::ResourceAvailable;
+use crate::resource::{ResourceAvailable, ResourceAvailableReply};
#[derive(Clone, Copy)]
#[allow(dead_code)]
@@ -48,8 +48,10 @@ impl WorkerActor {
url: self.url.to_string(),
traits: WorkerTraits {
is_parent_intercept_enabled: false,
+ supports_top_level_target_flag: false,
},
type_: self.type_ as u32,
+ target_type: "worker".to_string(),
}
}
}
@@ -131,6 +133,28 @@ impl Actor for WorkerActor {
}
}
+impl WorkerActor {
+ pub(crate) fn resource_available<T: Serialize>(&self, resource: T, resource_type: String) {
+ self.resources_available(vec![resource], resource_type);
+ }
+
+ pub(crate) fn resources_available<T: Serialize>(
+ &self,
+ resources: Vec<T>,
+ resource_type: String,
+ ) {
+ let msg = ResourceAvailableReply::<T> {
+ from: self.name(),
+ type_: "resources-available-array".into(),
+ array: vec![(resource_type, resources)],
+ };
+
+ for stream in self.streams.borrow_mut().values_mut() {
+ let _ = stream.write_json_packet(&msg);
+ }
+ }
+}
+
#[derive(Serialize)]
struct DetachedReply {
from: String,
@@ -160,6 +184,7 @@ struct ConnectReply {
#[serde(rename_all = "camelCase")]
struct WorkerTraits {
is_parent_intercept_enabled: bool,
+ supports_top_level_target_flag: bool,
}
#[derive(Serialize)]
@@ -173,4 +198,6 @@ pub(crate) struct WorkerMsg {
traits: WorkerTraits,
#[serde(rename = "type")]
type_: u32,
+ #[serde(rename = "targetType")]
+ target_type: String,
}
diff --git a/components/devtools/lib.rs b/components/devtools/lib.rs
index 4d1e0222177..5fb9485e9d3 100644
--- a/components/devtools/lib.rs
+++ b/components/devtools/lib.rs
@@ -510,35 +510,54 @@ impl DevtoolsInstance {
fn handle_script_source_info(&mut self, pipeline_id: PipelineId, source_info: SourceInfo) {
let mut actors = self.actors.lock().unwrap();
- let browsing_context_id = match self.pipelines.get(&pipeline_id) {
- Some(id) => id,
- None => return,
- };
+ if let Some(worker_id) = source_info.worker_id {
+ let Some(worker_actor_name) = self.actor_workers.get(&worker_id) else {
+ return;
+ };
- let actor_name = match self.browsing_contexts.get(browsing_context_id) {
- Some(name) => name,
- None => return,
- };
+ let thread_actor_name = actors.find::<WorkerActor>(worker_actor_name).thread.clone();
- let thread_actor_name = actors
- .find::<BrowsingContextActor>(actor_name)
- .thread
- .clone();
-
- let thread_actor = actors.find_mut::<ThreadActor>(&thread_actor_name);
- thread_actor
- .source_manager
- .add_source(source_info.url.clone());
-
- let source = SourceData {
- actor: thread_actor_name.clone(),
- url: source_info.url.to_string(),
- is_black_boxed: false,
- };
+ let thread_actor = actors.find_mut::<ThreadActor>(&thread_actor_name);
+ thread_actor
+ .source_manager
+ .add_source(source_info.url.clone());
+
+ let source = SourceData {
+ actor: thread_actor_name.clone(),
+ url: source_info.url.to_string(),
+ is_black_boxed: false,
+ };
- // Notify browsing context about the new source
- let browsing_context = actors.find::<BrowsingContextActor>(actor_name);
- browsing_context.resource_available(source, "source".into());
+ let worker_actor = actors.find::<WorkerActor>(worker_actor_name);
+ worker_actor.resource_available(source, "source".into());
+ } else {
+ let Some(browsing_context_id) = self.pipelines.get(&pipeline_id) else {
+ return;
+ };
+ let Some(actor_name) = self.browsing_contexts.get(browsing_context_id) else {
+ return;
+ };
+
+ let thread_actor_name = actors
+ .find::<BrowsingContextActor>(actor_name)
+ .thread
+ .clone();
+
+ let thread_actor = actors.find_mut::<ThreadActor>(&thread_actor_name);
+ thread_actor
+ .source_manager
+ .add_source(source_info.url.clone());
+
+ let source = SourceData {
+ actor: thread_actor_name.clone(),
+ url: source_info.url.to_string(),
+ is_black_boxed: false,
+ };
+
+ // Notify browsing context about the new source
+ let browsing_context = actors.find::<BrowsingContextActor>(actor_name);
+ browsing_context.resource_available(source, "source".into());
+ }
}
}
diff --git a/components/layout/context.rs b/components/layout/context.rs
index 71372ffe224..62f8a8cdae9 100644
--- a/components/layout/context.rs
+++ b/components/layout/context.rs
@@ -61,6 +61,20 @@ impl Drop for LayoutContext<'_> {
}
}
+#[derive(Debug)]
+pub enum ResolveImageError {
+ LoadError,
+ ImagePending,
+ ImageRequested,
+ OnlyMetadata,
+ InvalidUrl,
+ MissingNode,
+ ImageMissingFromImageSet,
+ FailedToResolveImageFromImageSet,
+ NotImplementedYet(&'static str),
+ None,
+}
+
impl LayoutContext<'_> {
#[inline(always)]
pub fn shared_context(&self) -> &SharedStyleContext {
@@ -72,7 +86,7 @@ impl LayoutContext<'_> {
node: OpaqueNode,
url: ServoUrl,
use_placeholder: UsePlaceholder,
- ) -> Option<ImageOrMetadataAvailable> {
+ ) -> Result<ImageOrMetadataAvailable, ResolveImageError> {
// Check for available image or start tracking.
let cache_result = self.image_cache.get_cached_image_status(
url.clone(),
@@ -82,7 +96,7 @@ impl LayoutContext<'_> {
);
match cache_result {
- ImageCacheResult::Available(img_or_meta) => Some(img_or_meta),
+ ImageCacheResult::Available(img_or_meta) => Ok(img_or_meta),
// Image has been requested, is still pending. Return no image for this paint loop.
// When the image loads it will trigger a reflow and/or repaint.
ImageCacheResult::Pending(id) => {
@@ -93,7 +107,7 @@ impl LayoutContext<'_> {
origin: self.origin.clone(),
};
self.pending_images.lock().push(image);
- None
+ Result::Err(ResolveImageError::ImagePending)
},
// Not yet requested - request image or metadata from the cache
ImageCacheResult::ReadyForRequest(id) => {
@@ -104,10 +118,10 @@ impl LayoutContext<'_> {
origin: self.origin.clone(),
};
self.pending_images.lock().push(image);
- None
+ Result::Err(ResolveImageError::ImageRequested)
},
// Image failed to load, so just return nothing
- ImageCacheResult::LoadError => None,
+ ImageCacheResult::LoadError => Result::Err(ResolveImageError::LoadError),
}
}
@@ -136,31 +150,34 @@ impl LayoutContext<'_> {
node: OpaqueNode,
url: ServoUrl,
use_placeholder: UsePlaceholder,
- ) -> Option<WebRenderImageInfo> {
+ ) -> Result<WebRenderImageInfo, ResolveImageError> {
if let Some(existing_webrender_image) = self
.webrender_image_cache
.read()
.get(&(url.clone(), use_placeholder))
{
- return Some(*existing_webrender_image);
+ return Ok(*existing_webrender_image);
}
-
- match self.get_or_request_image_or_meta(node, url.clone(), use_placeholder) {
- Some(ImageOrMetadataAvailable::ImageAvailable { image, .. }) => {
+ let image_or_meta =
+ self.get_or_request_image_or_meta(node, url.clone(), use_placeholder)?;
+ match image_or_meta {
+ ImageOrMetadataAvailable::ImageAvailable { image, .. } => {
self.handle_animated_image(node, image.clone());
let image_info = WebRenderImageInfo {
size: Size2D::new(image.width, image.height),
key: image.id,
};
if image_info.key.is_none() {
- Some(image_info)
+ Ok(image_info)
} else {
let mut webrender_image_cache = self.webrender_image_cache.write();
webrender_image_cache.insert((url, use_placeholder), image_info);
- Some(image_info)
+ Ok(image_info)
}
},
- None | Some(ImageOrMetadataAvailable::MetadataAvailable(..)) => None,
+ ImageOrMetadataAvailable::MetadataAvailable(..) => {
+ Result::Err(ResolveImageError::OnlyMetadata)
+ },
}
}
@@ -168,11 +185,15 @@ impl LayoutContext<'_> {
&self,
node: Option<OpaqueNode>,
image: &'a Image,
- ) -> Option<ResolvedImage<'a>> {
+ ) -> Result<ResolvedImage<'a>, ResolveImageError> {
match image {
// TODO: Add support for PaintWorklet and CrossFade rendering.
- Image::None | Image::CrossFade(_) | Image::PaintWorklet(_) => None,
- Image::Gradient(gradient) => Some(ResolvedImage::Gradient(gradient)),
+ Image::None => Result::Err(ResolveImageError::None),
+ Image::CrossFade(_) => Result::Err(ResolveImageError::NotImplementedYet("CrossFade")),
+ Image::PaintWorklet(_) => {
+ Result::Err(ResolveImageError::NotImplementedYet("PaintWorklet"))
+ },
+ Image::Gradient(gradient) => Ok(ResolvedImage::Gradient(gradient)),
Image::Url(image_url) => {
// FIXME: images won’t always have in intrinsic width or
// height when support for SVG is added, or a WebRender
@@ -180,18 +201,20 @@ impl LayoutContext<'_> {
//
// FIXME: It feels like this should take into account the pseudo
// element and not just the node.
- let image_url = image_url.url()?;
+ let image_url = image_url.url().ok_or(ResolveImageError::InvalidUrl)?;
+ let node = node.ok_or(ResolveImageError::MissingNode)?;
let webrender_info = self.get_webrender_image_for_url(
- node?,
+ node,
image_url.clone().into(),
UsePlaceholder::No,
)?;
- Some(ResolvedImage::Image(webrender_info))
+ Ok(ResolvedImage::Image(webrender_info))
},
Image::ImageSet(image_set) => {
image_set
.items
.get(image_set.selected_index)
+ .ok_or(ResolveImageError::ImageMissingFromImageSet)
.and_then(|image| {
self.resolve_image(node, &image.image)
.map(|info| match info {
diff --git a/components/layout/display_list/mod.rs b/components/layout/display_list/mod.rs
index fa313b306f4..3908da69ce1 100644
--- a/components/layout/display_list/mod.rs
+++ b/components/layout/display_list/mod.rs
@@ -39,7 +39,7 @@ use webrender_api::{
use wr::units::LayoutVector2D;
use crate::context::{LayoutContext, ResolvedImage};
-use crate::display_list::conversions::ToWebRender;
+pub use crate::display_list::conversions::ToWebRender;
use crate::display_list::stacking_context::StackingContextSection;
use crate::fragment_tree::{
BackgroundMode, BoxFragment, Fragment, FragmentFlags, FragmentTree, SpecificLayoutInfo, Tag,
@@ -711,7 +711,12 @@ impl<'a> BuilderForBoxFragment<'a> {
fn build(&mut self, builder: &mut DisplayListBuilder, section: StackingContextSection) {
if self.is_hit_test_for_scrollable_overflow {
- self.build_hit_test(builder, self.fragment.scrollable_overflow().to_webrender());
+ self.build_hit_test(
+ builder,
+ self.fragment
+ .reachable_scrollable_overflow_region()
+ .to_webrender(),
+ );
return;
}
@@ -838,8 +843,8 @@ impl<'a> BuilderForBoxFragment<'a> {
// Reverse because the property is top layer first, we want to paint bottom layer first.
for (index, image) in b.background_image.0.iter().enumerate().rev() {
match builder.context.resolve_image(node, image) {
- None => {},
- Some(ResolvedImage::Gradient(gradient)) => {
+ Err(_) => {},
+ Ok(ResolvedImage::Gradient(gradient)) => {
let intrinsic = NaturalSizes::empty();
let Some(layer) =
&background::layout_layer(self, painter, builder, index, intrinsic)
@@ -875,7 +880,7 @@ impl<'a> BuilderForBoxFragment<'a> {
},
}
},
- Some(ResolvedImage::Image(image_info)) => {
+ Ok(ResolvedImage::Image(image_info)) => {
// FIXME: https://drafts.csswg.org/css-images-4/#the-image-resolution
let dppx = 1.0;
let intrinsic = NaturalSizes::from_width_and_height(
@@ -1063,8 +1068,8 @@ impl<'a> BuilderForBoxFragment<'a> {
.context
.resolve_image(node, &border.border_image_source)
{
- None => return false,
- Some(ResolvedImage::Image(image_info)) => {
+ Err(_) => return false,
+ Ok(ResolvedImage::Image(image_info)) => {
let Some(key) = image_info.key else {
return false;
};
@@ -1073,7 +1078,7 @@ impl<'a> BuilderForBoxFragment<'a> {
height = image_info.size.height as f32;
NinePatchBorderSource::Image(key, ImageRendering::Auto)
},
- Some(ResolvedImage::Gradient(gradient)) => {
+ Ok(ResolvedImage::Gradient(gradient)) => {
match gradient::build(&self.fragment.style, gradient, border_image_size, builder) {
WebRenderGradient::Linear(gradient) => {
NinePatchBorderSource::Gradient(gradient)
diff --git a/components/layout/display_list/stacking_context.rs b/components/layout/display_list/stacking_context.rs
index 0c0def9a563..b044b713260 100644
--- a/components/layout/display_list/stacking_context.rs
+++ b/components/layout/display_list/stacking_context.rs
@@ -530,7 +530,7 @@ impl StackingContext {
if effects.filter.0.is_empty() &&
effects.opacity == 1.0 &&
effects.mix_blend_mode == ComputedMixBlendMode::Normal &&
- !style.has_transform_or_perspective(FragmentFlags::empty()) &&
+ !style.has_effective_transform_or_perspective(FragmentFlags::empty()) &&
style.clone_clip_path() == ClipPath::None
{
return false;
@@ -1477,7 +1477,7 @@ impl BoxFragment {
y: overflow.y.into(),
};
- let content_rect = self.scrollable_overflow().to_webrender();
+ let content_rect = self.reachable_scrollable_overflow_region().to_webrender();
let scroll_tree_node_id = display_list.define_scroll_frame(
parent_scroll_node_id,
@@ -1584,7 +1584,10 @@ impl BoxFragment {
&self,
containing_block_rect: &PhysicalRect<Au>,
) -> Option<ReferenceFrameData> {
- if !self.style.has_transform_or_perspective(self.base.flags) {
+ if !self
+ .style
+ .has_effective_transform_or_perspective(self.base.flags)
+ {
return None;
}
diff --git a/components/layout/flexbox/layout.rs b/components/layout/flexbox/layout.rs
index 77069236787..a5540123681 100644
--- a/components/layout/flexbox/layout.rs
+++ b/components/layout/flexbox/layout.rs
@@ -1774,7 +1774,9 @@ impl FlexItem<'_> {
non_stretch_layout_result: Option<&mut FlexItemLayoutResult>,
) -> Option<FlexItemLayoutResult> {
let containing_block = flex_context.containing_block;
- let mut positioning_context = PositioningContext::new_for_style(self.box_.style())
+ let independent_formatting_context = &self.box_.independent_formatting_context;
+ let mut positioning_context = independent_formatting_context
+ .new_positioning_context()
.unwrap_or_else(|| {
PositioningContext::new_for_subtree(
flex_context
@@ -1783,7 +1785,6 @@ impl FlexItem<'_> {
)
});
- let independent_formatting_context = &self.box_.independent_formatting_context;
let item_writing_mode = independent_formatting_context.style().writing_mode;
let item_is_horizontal = item_writing_mode.is_horizontal();
let flex_axis = flex_context.config.flex_axis;
@@ -2616,7 +2617,9 @@ impl FlexItemBox {
cross_size_stretches_to_container_size: bool,
intrinsic_sizing_mode: IntrinsicSizingMode,
) -> Au {
- let mut positioning_context = PositioningContext::new_for_style(self.style())
+ let mut positioning_context = self
+ .independent_formatting_context
+ .new_positioning_context()
.unwrap_or_else(|| {
PositioningContext::new_for_subtree(
flex_context
diff --git a/components/layout/flow/float.rs b/components/layout/flow/float.rs
index 0570ce0d0f4..dbc50c07603 100644
--- a/components/layout/flow/float.rs
+++ b/components/layout/flow/float.rs
@@ -913,11 +913,10 @@ impl FloatBox {
positioning_context: &mut PositioningContext,
containing_block: &ContainingBlock,
) -> BoxFragment {
- let style = self.contents.style().clone();
positioning_context.layout_maybe_position_relative_fragment(
layout_context,
containing_block,
- &style,
+ &self.contents.base,
|positioning_context| {
self.contents
.layout_float_or_atomic_inline(
diff --git a/components/layout/flow/inline/line.rs b/components/layout/flow/inline/line.rs
index c42f32c9242..e65eaed2367 100644
--- a/components/layout/flow/inline/line.rs
+++ b/components/layout/flow/inline/line.rs
@@ -326,13 +326,12 @@ impl LineItemLayout<'_, '_> {
let inline_box = self.layout.ifc.inline_boxes.get(identifier);
let inline_box = &*(inline_box.borrow());
- let style = &inline_box.base.style;
let space_above_baseline = inline_box_state.calculate_space_above_baseline();
let block_start_offset =
self.calculate_inline_box_block_start(inline_box_state, space_above_baseline);
let positioning_context_or_start_offset_in_parent =
- match PositioningContext::new_for_style(style) {
+ match inline_box.base.new_positioning_context() {
Some(positioning_context) => Either::Left(positioning_context),
None => Either::Right(self.current_positioning_context_mut().len()),
};
diff --git a/components/layout/flow/inline/mod.rs b/components/layout/flow/inline/mod.rs
index dabb9773410..25fbaa324b1 100644
--- a/components/layout/flow/inline/mod.rs
+++ b/components/layout/flow/inline/mod.rs
@@ -2004,7 +2004,8 @@ impl IndependentFormattingContext {
bidi_level: Level,
) {
// We need to know the inline size of the atomic before deciding whether to do the line break.
- let mut child_positioning_context = PositioningContext::new_for_style(self.style())
+ let mut child_positioning_context = self
+ .new_positioning_context()
.unwrap_or_else(|| PositioningContext::new_for_subtree(true));
let IndependentFloatOrAtomicLayoutResult {
mut fragment,
diff --git a/components/layout/flow/mod.rs b/components/layout/flow/mod.rs
index d983e8592c4..983282dc389 100644
--- a/components/layout/flow/mod.rs
+++ b/components/layout/flow/mod.rs
@@ -779,7 +779,7 @@ impl BlockLevelBox {
ArcRefCell::new(positioning_context.layout_maybe_position_relative_fragment(
layout_context,
containing_block,
- &base.style,
+ base,
|positioning_context| {
layout_in_flow_non_replaced_block_level_same_formatting_context(
layout_context,
@@ -798,7 +798,7 @@ impl BlockLevelBox {
positioning_context.layout_maybe_position_relative_fragment(
layout_context,
containing_block,
- independent.style(),
+ &independent.base,
|positioning_context| {
independent.layout_in_flow_block_level(
layout_context,
diff --git a/components/layout/flow/root.rs b/components/layout/flow/root.rs
index 187726595f8..c6498eeed63 100644
--- a/components/layout/flow/root.rs
+++ b/components/layout/flow/root.rs
@@ -411,7 +411,7 @@ impl BoxTree {
let scrollable_overflow = root_fragments
.iter()
.fold(PhysicalRect::zero(), |acc, child| {
- let child_overflow = child.scrollable_overflow();
+ let child_overflow = child.scrollable_overflow_for_parent();
// https://drafts.csswg.org/css-overflow/#scrolling-direction
// We want to clip scrollable overflow on box-start and inline-start
diff --git a/components/layout/fragment_tree/box_fragment.rs b/components/layout/fragment_tree/box_fragment.rs
index e87826ec3ca..65ad1c4aa93 100644
--- a/components/layout/fragment_tree/box_fragment.rs
+++ b/components/layout/fragment_tree/box_fragment.rs
@@ -2,11 +2,12 @@
* 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 app_units::Au;
+use app_units::{Au, MAX_AU, MIN_AU};
use atomic_refcell::AtomicRefCell;
use base::print_tree::PrintTree;
use malloc_size_of_derive::MallocSizeOf;
use servo_arc::Arc as ServoArc;
+use servo_geometry::f32_rect_to_au_rect;
use style::Zero;
use style::computed_values::border_collapse::T as BorderCollapse;
use style::computed_values::overflow_x::T as ComputedOverflow;
@@ -16,6 +17,7 @@ use style::properties::ComputedValues;
use style::values::specified::box_::DisplayOutside;
use super::{BaseFragment, BaseFragmentInfo, CollapsedBlockMargins, Fragment};
+use crate::display_list::ToWebRender;
use crate::formatting_contexts::Baselines;
use crate::geom::{
AuOrAuto, LengthPercentageOrAuto, PhysicalPoint, PhysicalRect, PhysicalSides, ToLogical,
@@ -116,7 +118,7 @@ impl BoxFragment {
) -> BoxFragment {
let scrollable_overflow_from_children =
children.iter().fold(PhysicalRect::zero(), |acc, child| {
- acc.union(&child.scrollable_overflow())
+ acc.union(&child.scrollable_overflow_for_parent())
});
BoxFragment {
@@ -267,30 +269,96 @@ impl BoxFragment {
pub fn scrollable_overflow_for_parent(&self) -> PhysicalRect<Au> {
let mut overflow = self.border_rect();
- if self.style.establishes_scroll_container(self.base.flags) {
- return overflow;
+ if !self.style.establishes_scroll_container(self.base.flags) {
+ // https://www.w3.org/TR/css-overflow-3/#scrollable
+ // Only include the scrollable overflow of a child box if it has overflow: visible.
+ let scrollable_overflow = self.scrollable_overflow();
+ let bottom_right = PhysicalPoint::new(
+ overflow.max_x().max(scrollable_overflow.max_x()),
+ overflow.max_y().max(scrollable_overflow.max_y()),
+ );
+
+ let overflow_style = self.style.effective_overflow(self.base.flags);
+ if overflow_style.y == ComputedOverflow::Visible {
+ overflow.origin.y = overflow.origin.y.min(scrollable_overflow.origin.y);
+ overflow.size.height = bottom_right.y - overflow.origin.y;
+ }
+
+ if overflow_style.x == ComputedOverflow::Visible {
+ overflow.origin.x = overflow.origin.x.min(scrollable_overflow.origin.x);
+ overflow.size.width = bottom_right.x - overflow.origin.x;
+ }
}
- // https://www.w3.org/TR/css-overflow-3/#scrollable
- // Only include the scrollable overflow of a child box if it has overflow: visible.
- let scrollable_overflow = self.scrollable_overflow();
- let bottom_right = PhysicalPoint::new(
- overflow.max_x().max(scrollable_overflow.max_x()),
- overflow.max_y().max(scrollable_overflow.max_y()),
- );
+ // <https://drafts.csswg.org/css-overflow-3/#scrollable-overflow-region>
+ // > ...accounting for transforms by projecting each box onto the plane of
+ // > the element that establishes its 3D rendering context. [CSS3-TRANSFORMS]
+ // Both boxes and its scrollable overflow (if it is included) should be transformed accordingly.
+ //
+ // TODO(stevennovaryo): We are supposed to handle perspective transform and 3d context, but it is yet to happen.
+ if self
+ .style
+ .has_effective_transform_or_perspective(self.base.flags)
+ {
+ if let Some(transform) =
+ self.calculate_transform_matrix(&self.border_rect().to_untyped())
+ {
+ if let Some(transformed_overflow_box) =
+ transform.outer_transformed_rect(&overflow.to_webrender().to_rect())
+ {
+ overflow =
+ f32_rect_to_au_rect(transformed_overflow_box.to_untyped()).cast_unit();
+ }
+ }
+ }
+
+ overflow
+ }
- let overflow_style = self.style.effective_overflow(self.base.flags);
- if overflow_style.y == ComputedOverflow::Visible {
- overflow.origin.y = overflow.origin.y.min(scrollable_overflow.origin.y);
- overflow.size.height = bottom_right.y - overflow.origin.y;
+ /// <https://drafts.csswg.org/css-overflow/#unreachable-scrollable-overflow-region>
+ /// > area beyond the scroll origin in either axis is considered the unreachable scrollable overflow region
+ ///
+ /// Return the clipped the scrollable overflow based on its scroll origin, determined by overflow direction.
+ /// For an element, the clip rect is the padding rect and for viewport, it is the initial containing block.
+ pub fn clip_unreachable_scrollable_overflow_region(
+ &self,
+ scrollable_overflow: PhysicalRect<Au>,
+ clipping_rect: PhysicalRect<Au>,
+ ) -> PhysicalRect<Au> {
+ let scrolling_direction = self.style.overflow_direction();
+ let mut scrollable_overflow_box = scrollable_overflow.to_box2d();
+ let mut clipping_box = clipping_rect.to_box2d();
+
+ if scrolling_direction.rightward {
+ clipping_box.max.x = MAX_AU;
+ } else {
+ clipping_box.min.x = MIN_AU;
}
- if overflow_style.x == ComputedOverflow::Visible {
- overflow.origin.x = overflow.origin.x.min(scrollable_overflow.origin.x);
- overflow.size.width = bottom_right.x - overflow.origin.x;
+ if scrolling_direction.downward {
+ clipping_box.max.y = MAX_AU;
+ } else {
+ clipping_box.min.y = MIN_AU;
}
- overflow
+ scrollable_overflow_box = scrollable_overflow_box.intersection_unchecked(&clipping_box);
+
+ match scrollable_overflow_box.is_negative() {
+ true => PhysicalRect::zero(),
+ false => scrollable_overflow_box.to_rect(),
+ }
+ }
+
+ /// <https://drafts.csswg.org/css-overflow/#unreachable-scrollable-overflow-region>
+ /// > area beyond the scroll origin in either axis is considered the unreachable scrollable overflow region
+ ///
+ /// Return the clipped the scrollable overflow based on its scroll origin, determined by overflow direction.
+ /// This will coincides with the scrollport if the fragment is a scroll container.
+ pub fn reachable_scrollable_overflow_region(&self) -> PhysicalRect<Au> {
+ self.clip_unreachable_scrollable_overflow_region(
+ self.scrollable_overflow(),
+ self.padding_rect(),
+ )
}
pub(crate) fn calculate_resolved_insets_if_positioned(&self) -> PhysicalSides<AuOrAuto> {
diff --git a/components/layout/fragment_tree/fragment.rs b/components/layout/fragment_tree/fragment.rs
index 7708b0893ee..1c5324fa1c4 100644
--- a/components/layout/fragment_tree/fragment.rs
+++ b/components/layout/fragment_tree/fragment.rs
@@ -170,17 +170,28 @@ impl Fragment {
}
}
- pub fn scrolling_area(&self) -> PhysicalRect<Au> {
+ pub fn unclipped_scrolling_area(&self) -> PhysicalRect<Au> {
match self {
Fragment::Box(fragment) | Fragment::Float(fragment) => {
let fragment = fragment.borrow();
fragment.offset_by_containing_block(&fragment.scrollable_overflow())
},
- _ => self.scrollable_overflow(),
+ _ => self.scrollable_overflow_for_parent(),
+ }
+ }
+
+ pub fn scrolling_area(&self) -> PhysicalRect<Au> {
+ match self {
+ Fragment::Box(fragment) | Fragment::Float(fragment) => {
+ let fragment = fragment.borrow();
+ fragment
+ .offset_by_containing_block(&fragment.reachable_scrollable_overflow_region())
+ },
+ _ => self.scrollable_overflow_for_parent(),
}
}
- pub fn scrollable_overflow(&self) -> PhysicalRect<Au> {
+ pub fn scrollable_overflow_for_parent(&self) -> PhysicalRect<Au> {
match self {
Fragment::Box(fragment) | Fragment::Float(fragment) => {
fragment.borrow().scrollable_overflow_for_parent()
diff --git a/components/layout/fragment_tree/fragment_tree.rs b/components/layout/fragment_tree/fragment_tree.rs
index 3a082c99389..1499a50dacf 100644
--- a/components/layout/fragment_tree/fragment_tree.rs
+++ b/components/layout/fragment_tree/fragment_tree.rs
@@ -138,11 +138,26 @@ impl FragmentTree {
.find_map(|child| child.find(&info, 0, &mut process_func))
}
+ /// <https://drafts.csswg.org/cssom-view/#scrolling-area>
+ ///
+ /// Scrolling area for a viewport that is clipped according to overflow direction of root element.
pub fn get_scrolling_area_for_viewport(&self) -> PhysicalRect<Au> {
let mut scroll_area = self.initial_containing_block;
- for fragment in self.root_fragments.iter() {
- scroll_area = fragment.scrolling_area().union(&scroll_area);
+ if let Some(root_fragment) = self.root_fragments.first() {
+ for fragment in self.root_fragments.iter() {
+ scroll_area = fragment.unclipped_scrolling_area().union(&scroll_area);
+ }
+ match root_fragment {
+ Fragment::Box(fragment) | Fragment::Float(fragment) => fragment
+ .borrow()
+ .clip_unreachable_scrollable_overflow_region(
+ scroll_area,
+ self.initial_containing_block,
+ ),
+ _ => scroll_area,
+ }
+ } else {
+ scroll_area
}
- scroll_area
}
}
diff --git a/components/layout/fragment_tree/positioning_fragment.rs b/components/layout/fragment_tree/positioning_fragment.rs
index 1fe968eb484..0cf525a3479 100644
--- a/components/layout/fragment_tree/positioning_fragment.rs
+++ b/components/layout/fragment_tree/positioning_fragment.rs
@@ -56,7 +56,7 @@ impl PositioningFragment {
let scrollable_overflow = children.iter().fold(PhysicalRect::zero(), |acc, child| {
acc.union(
&child
- .scrollable_overflow()
+ .scrollable_overflow_for_parent()
.translate(content_origin.to_vector()),
)
});
diff --git a/components/layout/positioned.rs b/components/layout/positioned.rs
index 5f08e4e86c5..6bfe2af87ef 100644
--- a/components/layout/positioned.rs
+++ b/components/layout/positioned.rs
@@ -29,6 +29,7 @@ use crate::geom::{
PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize, PhysicalVec, Size, Sizes, ToLogical,
ToLogicalWithContainingBlock,
};
+use crate::layout_box_base::LayoutBoxBase;
use crate::sizing::ContentSizes;
use crate::style_ext::{Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, DisplayInside};
use crate::{
@@ -103,6 +104,20 @@ impl AbsolutelyPositionedBox {
}
}
+impl IndependentFormattingContext {
+ #[inline]
+ pub(crate) fn new_positioning_context(&self) -> Option<PositioningContext> {
+ self.base.new_positioning_context()
+ }
+}
+
+impl LayoutBoxBase {
+ #[inline]
+ pub(crate) fn new_positioning_context(&self) -> Option<PositioningContext> {
+ PositioningContext::new_for_style(&self.style, &self.base_fragment_info.flags)
+ }
+}
+
impl PositioningContext {
pub(crate) fn new_for_containing_block_for_all_descendants() -> Self {
Self {
@@ -130,14 +145,10 @@ impl PositioningContext {
self.for_nearest_positioned_ancestor.is_some()
}
- pub(crate) fn new_for_style(style: &ComputedValues) -> Option<Self> {
- // NB: We never make PositioningContexts for replaced elements, which is why we always
- // pass false here.
- if style.establishes_containing_block_for_all_descendants(FragmentFlags::empty()) {
+ fn new_for_style(style: &ComputedValues, flags: &FragmentFlags) -> Option<Self> {
+ if style.establishes_containing_block_for_all_descendants(*flags) {
Some(Self::new_for_containing_block_for_all_descendants())
- } else if style
- .establishes_containing_block_for_absolute_descendants(FragmentFlags::empty())
- {
+ } else if style.establishes_containing_block_for_absolute_descendants(*flags) {
Some(Self {
for_nearest_positioned_ancestor: Some(Vec::new()),
for_nearest_containing_block_for_all_descendants: Vec::new(),
@@ -213,12 +224,12 @@ impl PositioningContext {
&mut self,
layout_context: &LayoutContext,
containing_block: &ContainingBlock,
- style: &ComputedValues,
+ base: &LayoutBoxBase,
fragment_layout_fn: impl FnOnce(&mut Self) -> BoxFragment,
) -> BoxFragment {
// Try to create a context, but if one isn't necessary, simply create the fragment
// using the given closure and the current `PositioningContext`.
- let mut new_context = match Self::new_for_style(style) {
+ let mut new_context = match base.new_positioning_context() {
Some(new_context) => new_context,
None => return fragment_layout_fn(self),
};
@@ -229,9 +240,8 @@ impl PositioningContext {
// If the new context has any hoisted boxes for the nearest containing block for
// pass them up the tree.
self.append(new_context);
-
- if style.clone_position() == Position::Relative {
- new_fragment.content_rect.origin += relative_adjustement(style, containing_block)
+ if base.style.clone_position() == Position::Relative {
+ new_fragment.content_rect.origin += relative_adjustement(&base.style, containing_block)
.to_physical_vector(containing_block.style.writing_mode)
}
@@ -586,7 +596,7 @@ impl HoistedAbsolutelyPositionedBox {
.sizes
}));
- let mut positioning_context = PositioningContext::new_for_style(&style).unwrap();
+ let mut positioning_context = context.new_positioning_context().unwrap();
let mut new_fragment = {
let content_size: LogicalVec2<Au>;
let fragments;
diff --git a/components/layout/replaced.rs b/components/layout/replaced.rs
index 6a6b1979ff9..b82fb947074 100644
--- a/components/layout/replaced.rs
+++ b/components/layout/replaced.rs
@@ -220,13 +220,13 @@ impl ReplacedContents {
image_url.clone().into(),
UsePlaceholder::No,
) {
- Some(ImageOrMetadataAvailable::ImageAvailable { image, .. }) => {
+ Ok(ImageOrMetadataAvailable::ImageAvailable { image, .. }) => {
(Some(image.clone()), image.width as f32, image.height as f32)
},
- Some(ImageOrMetadataAvailable::MetadataAvailable(metadata, _id)) => {
+ Ok(ImageOrMetadataAvailable::MetadataAvailable(metadata, _id)) => {
(None, metadata.width as f32, metadata.height as f32)
},
- None => return None,
+ Err(_) => return None,
};
return Some(Self {
diff --git a/components/layout/style_ext.rs b/components/layout/style_ext.rs
index c28511766b2..023db6b07f1 100644
--- a/components/layout/style_ext.rs
+++ b/components/layout/style_ext.rs
@@ -12,7 +12,7 @@ use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode;
use style::computed_values::position::T as ComputedPosition;
use style::computed_values::transform_style::T as ComputedTransformStyle;
use style::computed_values::unicode_bidi::T as UnicodeBidi;
-use style::logical_geometry::{Direction as AxisDirection, WritingMode};
+use style::logical_geometry::{Direction as AxisDirection, PhysicalSide, WritingMode};
use style::properties::ComputedValues;
use style::properties::longhands::backface_visibility::computed_value::T as BackfaceVisiblity;
use style::properties::longhands::box_sizing::computed_value::T as BoxSizing;
@@ -280,6 +280,16 @@ impl Default for BorderStyleColor {
}
}
+/// <https://drafts.csswg.org/cssom-view/#overflow-directions>
+/// > A scrolling box of a viewport or element has two overflow directions,
+/// > which are the block-end and inline-end directions for that viewport or element.
+pub(crate) struct OverflowDirection {
+ /// Whether block-end or inline-end direction is [PhysicalSide::Right].
+ pub rightward: bool,
+ /// Whether block-end or inline-end direction is [PhysicalSide::Bottom].
+ pub downward: bool,
+}
+
pub(crate) trait ComputedValuesExt {
fn physical_box_offsets(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>>;
fn box_offsets(&self, writing_mode: WritingMode) -> LogicalSides<LengthPercentageOrAuto<'_>>;
@@ -320,7 +330,8 @@ pub(crate) trait ComputedValuesExt {
containing_block_writing_mode: WritingMode,
) -> LogicalSides<LengthPercentageOrAuto<'_>>;
fn is_transformable(&self, fragment_flags: FragmentFlags) -> bool;
- fn has_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool;
+ fn has_transform_or_perspective_style(&self) -> bool;
+ fn has_effective_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool;
fn z_index_applies(&self, fragment_flags: FragmentFlags) -> bool;
fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32;
fn effective_overflow(&self, fragment_flags: FragmentFlags) -> AxesOverflow;
@@ -353,6 +364,7 @@ pub(crate) trait ComputedValuesExt {
writing_mode: WritingMode,
) -> bool;
fn is_inline_box(&self, fragment_flags: FragmentFlags) -> bool;
+ fn overflow_direction(&self) -> OverflowDirection;
}
impl ComputedValuesExt for ComputedValues {
@@ -522,15 +534,20 @@ impl ComputedValuesExt for ComputedValues {
!self.is_inline_box(fragment_flags)
}
- /// Returns true if this style has a transform, or perspective property set and
+ /// Returns true if this style has a transform or perspective property set.
+ fn has_transform_or_perspective_style(&self) -> bool {
+ !self.get_box().transform.0.is_empty() ||
+ self.get_box().scale != GenericScale::None ||
+ self.get_box().rotate != GenericRotate::None ||
+ self.get_box().translate != GenericTranslate::None ||
+ self.get_box().perspective != Perspective::None
+ }
+
+ /// Returns true if this style has a transform or perspective property set, and
/// it applies to this element.
- fn has_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool {
- self.is_transformable(fragment_flags) &&
- (!self.get_box().transform.0.is_empty() ||
- self.get_box().scale != GenericScale::None ||
- self.get_box().rotate != GenericRotate::None ||
- self.get_box().translate != GenericTranslate::None ||
- self.get_box().perspective != Perspective::None)
+ #[inline]
+ fn has_effective_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool {
+ self.is_transformable(fragment_flags) && self.has_transform_or_perspective_style()
}
/// Whether the `z-index` property applies to this fragment.
@@ -705,7 +722,6 @@ impl ComputedValuesExt for ComputedValues {
if self.is_transformable(fragment_flags) &&
(!self.get_box().transform.0.is_empty() ||
self.get_box().transform_style == ComputedTransformStyle::Preserve3d ||
- self.get_box().perspective != Perspective::None ||
will_change_bits
.intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE))
{
@@ -795,29 +811,43 @@ impl ComputedValuesExt for ComputedValues {
&self,
fragment_flags: FragmentFlags,
) -> bool {
- if self.has_transform_or_perspective(fragment_flags) {
- return true;
- }
-
- if !self.get_effects().filter.0.is_empty() {
- return true;
- }
-
- // See <https://drafts.csswg.org/css-transforms-2/#transform-style-property>.
- if self.is_transformable(fragment_flags) &&
- self.get_box().transform_style == ComputedTransformStyle::Preserve3d
- {
- return true;
- }
// From <https://www.w3.org/TR/css-will-change/#valdef-will-change-custom-ident>:
// > If any non-initial value of a property would cause the element to generate a
// > containing block for fixed positioned elements, specifying that property in will-change
// > must cause the element to generate a containing block for fixed positioned elements.
let will_change_bits = self.clone_will_change().bits;
- if will_change_bits.intersects(WillChangeBits::FIXPOS_CB_NON_SVG) ||
- (will_change_bits
- .intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE) &&
- self.is_transformable(fragment_flags))
+
+ // From <https://drafts.csswg.org/css-transforms-1/#transform-rendering>:
+ // > any value other than `none` for the `transform` property also causes the element
+ // > to establish a containing block for all descendants.
+ //
+ // From <https://www.w3.org/TR/css-transforms-2/#individual-transforms>
+ // > all other values […] create a stacking context and containing block for all
+ // > descendants, per usual for transforms.
+ //
+ // From <https://drafts.csswg.org/css-transforms-2/#perspective-property>:
+ // > The use of this property with any value other than `none` […] establishes a
+ // > containing block for all descendants, just like the `transform` property does.
+ //
+ // From <https://drafts.csswg.org/css-transforms-2/#transform-style-property>:
+ // > A computed value of `preserve-3d` for `transform-style` on a transformable element
+ // > establishes both a stacking context and a containing block for all descendants.
+ if self.is_transformable(fragment_flags) &&
+ (self.has_transform_or_perspective_style() ||
+ self.get_box().transform_style == ComputedTransformStyle::Preserve3d ||
+ will_change_bits
+ .intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE))
+ {
+ return true;
+ }
+
+ // From <https://www.w3.org/TR/filter-effects-1/#propdef-filter>:
+ // > A value other than none for the filter property results in the creation of a containing
+ // > block for absolute and fixed positioned descendants unless the element it applies to is
+ // > a document root element in the current browsing context.
+ if !fragment_flags.contains(FragmentFlags::IS_ROOT_ELEMENT) &&
+ (!self.get_effects().filter.0.is_empty() ||
+ will_change_bits.intersects(WillChangeBits::FIXPOS_CB_NON_SVG))
{
return true;
}
@@ -961,6 +991,23 @@ impl ComputedValuesExt for ComputedValues {
};
has_percentage(box_offsets.block_start) || has_percentage(box_offsets.block_end)
}
+
+ // <https://drafts.csswg.org/cssom-view/#overflow-directions>
+ fn overflow_direction(&self) -> OverflowDirection {
+ let inline_end_direction = self.writing_mode.inline_end_physical_side();
+ let block_end_direction = self.writing_mode.block_end_physical_side();
+
+ let rightward = inline_end_direction == PhysicalSide::Right ||
+ block_end_direction == PhysicalSide::Right;
+ let downward = inline_end_direction == PhysicalSide::Bottom ||
+ block_end_direction == PhysicalSide::Bottom;
+
+ // TODO(stevennovaryo): We should consider the flex-container's CSS (e.g. flow-direction: column-reverse).
+ OverflowDirection {
+ rightward,
+ downward,
+ }
+ }
}
pub(crate) enum LayoutStyle<'a> {
diff --git a/components/layout/table/layout.rs b/components/layout/table/layout.rs
index 2261f7d165c..0cbe3e9ca76 100644
--- a/components/layout/table/layout.rs
+++ b/components/layout/table/layout.rs
@@ -1503,7 +1503,7 @@ impl<'a> TableLayout<'a> {
layout_context: &LayoutContext,
parent_positioning_context: &mut PositioningContext,
) -> BoxFragment {
- let mut positioning_context = PositioningContext::new_for_style(caption.context.style());
+ let mut positioning_context = caption.context.new_positioning_context();
let containing_block = &ContainingBlock {
size: ContainingBlockSize {
inline: self.table_width + self.pbm.padding_border_sums.inline,
@@ -2325,7 +2325,7 @@ impl<'a> RowFragmentLayout<'a> {
Self {
row: table_row,
rect,
- positioning_context: PositioningContext::new_for_style(&table_row.base.style),
+ positioning_context: table_row.base.new_positioning_context(),
containing_block,
fragments: Vec::new(),
}
@@ -2410,7 +2410,7 @@ impl RowGroupFragmentLayout {
let row_group = row_group.borrow();
(
dimensions.get_row_group_rect(&row_group),
- PositioningContext::new_for_style(&row_group.base.style),
+ row_group.base.new_positioning_context(),
)
};
Self {
diff --git a/components/layout/taffy/layout.rs b/components/layout/taffy/layout.rs
index a7581136bf2..3777c902053 100644
--- a/components/layout/taffy/layout.rs
+++ b/components/layout/taffy/layout.rs
@@ -251,8 +251,9 @@ impl taffy::LayoutPartialTree for TaffyContainerContext<'_> {
style,
};
let layout = {
- let mut child_positioning_context =
- PositioningContext::new_for_style(style).unwrap_or_else(|| {
+ let mut child_positioning_context = independent_context
+ .new_positioning_context()
+ .unwrap_or_else(|| {
PositioningContext::new_for_subtree(
self.positioning_context
.collects_for_nearest_positioned_ancestor(),
diff --git a/components/script/canvas_state.rs b/components/script/canvas_state.rs
index 408c94c124a..e9892818e92 100644
--- a/components/script/canvas_state.rs
+++ b/components/script/canvas_state.rs
@@ -152,6 +152,8 @@ pub(crate) struct CanvasState {
canvas_id: CanvasId,
#[no_trace]
image_key: ImageKey,
+ #[no_trace]
+ size: Cell<Size2D<u64>>,
state: DomRefCell<CanvasContextState>,
origin_clean: Cell<bool>,
#[ignore_malloc_size_of = "Arc"]
@@ -176,6 +178,7 @@ impl CanvasState {
profiled_ipc::channel(global.time_profiler_chan().clone()).unwrap();
let script_to_constellation_chan = global.script_to_constellation_chan();
debug!("Asking constellation to create new canvas thread.");
+ let size = adjust_canvas_size(size);
script_to_constellation_chan
.send(ScriptToConstellationMessage::CreateCanvasPaintThread(
size, sender,
@@ -194,6 +197,7 @@ impl CanvasState {
CanvasState {
ipc_renderer,
canvas_id,
+ size: Cell::new(size),
state: DomRefCell::new(CanvasContextState::new()),
origin_clean: Cell::new(true),
image_cache: global.image_cache(),
@@ -221,7 +225,15 @@ impl CanvasState {
self.canvas_id
}
+ pub(crate) fn is_paintable(&self) -> bool {
+ !self.size.get().is_empty()
+ }
+
pub(crate) fn send_canvas_2d_msg(&self, msg: Canvas2dMsg) {
+ if !self.is_paintable() {
+ return;
+ }
+
self.ipc_renderer
.send(CanvasMsg::Canvas2d(msg, self.get_canvas_id()))
.unwrap()
@@ -229,6 +241,10 @@ impl CanvasState {
/// Updates WR image and blocks on completion
pub(crate) fn update_rendering(&self) {
+ if !self.is_paintable() {
+ return;
+ }
+
let (sender, receiver) = ipc::channel().unwrap();
self.ipc_renderer
.send(CanvasMsg::Canvas2d(
@@ -239,16 +255,27 @@ impl CanvasState {
receiver.recv().unwrap();
}
- // https://html.spec.whatwg.org/multipage/#concept-canvas-set-bitmap-dimensions
+ /// <https://html.spec.whatwg.org/multipage/#concept-canvas-set-bitmap-dimensions>
pub(crate) fn set_bitmap_dimensions(&self, size: Size2D<u64>) {
self.reset_to_initial_state();
+
+ self.size.replace(adjust_canvas_size(size));
+
self.ipc_renderer
- .send(CanvasMsg::Recreate(Some(size), self.get_canvas_id()))
+ .send(CanvasMsg::Recreate(
+ Some(self.size.get()),
+ self.get_canvas_id(),
+ ))
.unwrap();
}
pub(crate) fn reset(&self) {
self.reset_to_initial_state();
+
+ if !self.is_paintable() {
+ return;
+ }
+
self.ipc_renderer
.send(CanvasMsg::Recreate(None, self.get_canvas_id()))
.unwrap();
@@ -347,7 +374,6 @@ impl CanvasState {
pub(crate) fn get_rect(&self, canvas_size: Size2D<u64>, rect: Rect<u64>) -> Vec<u8> {
assert!(self.origin_is_clean());
-
assert!(Rect::from_size(canvas_size).contains_rect(&rect));
let (sender, receiver) = ipc::channel().unwrap();
@@ -398,18 +424,22 @@ impl CanvasState {
dw: Option<f64>,
dh: Option<f64>,
) -> ErrorResult {
+ if !self.is_paintable() {
+ return Ok(());
+ }
+
let result = match image {
CanvasImageSource::HTMLCanvasElement(ref canvas) => {
- // https://html.spec.whatwg.org/multipage/#check-the-usability-of-the-image-argument
- if !canvas.is_valid() {
+ // <https://html.spec.whatwg.org/multipage/#check-the-usability-of-the-image-argument>
+ if canvas.get_size().is_empty() {
return Err(Error::InvalidState);
}
self.draw_html_canvas_element(canvas, htmlcanvas, sx, sy, sw, sh, dx, dy, dw, dh)
},
CanvasImageSource::OffscreenCanvas(ref canvas) => {
- // https://html.spec.whatwg.org/multipage/#check-the-usability-of-the-image-argument
- if !canvas.is_valid() {
+ // <https://html.spec.whatwg.org/multipage/#check-the-usability-of-the-image-argument>
+ if canvas.get_size().is_empty() {
return Err(Error::InvalidState);
}
@@ -528,11 +558,6 @@ impl CanvasState {
dw: Option<f64>,
dh: Option<f64>,
) -> ErrorResult {
- // 1. Check the usability of the image argument
- if !canvas.is_valid() {
- return Err(Error::InvalidState);
- }
-
let canvas_size = canvas.get_size();
let dw = dw.unwrap_or(canvas_size.width as f64);
let dh = dh.unwrap_or(canvas_size.height as f64);
@@ -1403,13 +1428,13 @@ impl CanvasState {
},
};
- ImageData::new(
- global,
- size.width,
- size.height,
- Some(self.get_rect(canvas_size, read_rect)),
- can_gc,
- )
+ let data = if self.is_paintable() {
+ Some(self.get_rect(canvas_size, read_rect))
+ } else {
+ None
+ };
+
+ ImageData::new(global, size.width, size.height, data, can_gc)
}
// https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata
@@ -1445,6 +1470,10 @@ impl CanvasState {
dirty_width: i32,
dirty_height: i32,
) {
+ if !self.is_paintable() {
+ return;
+ }
+
// FIXME(nox): There are many arithmetic operations here that can
// overflow or underflow, this should probably be audited.
@@ -2013,3 +2042,23 @@ where
style.font_family.to_css_string()
)
}
+
+fn adjust_canvas_size(size: Size2D<u64>) -> Size2D<u64> {
+ // Firefox limits width/height to 32767 pixels and Chromium to 65535 pixels,
+ // but slows down dramatically before it reaches that limit.
+ // We limit by area instead, giving us larger maximum dimensions,
+ // in exchange for a smaller maximum canvas size.
+ const MAX_CANVAS_AREA: u64 = 32768 * 8192;
+ // Max width/height to 65535 in CSS pixels.
+ const MAX_CANVAS_SIZE: u64 = 65535;
+
+ if !size.is_empty() &&
+ size.greater_than(Size2D::new(MAX_CANVAS_SIZE, MAX_CANVAS_SIZE))
+ .none() &&
+ size.area() < MAX_CANVAS_AREA
+ {
+ size
+ } else {
+ Size2D::zero()
+ }
+}
diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs
index 73052e6906e..38bd38ad511 100644
--- a/components/script/dom/canvasrenderingcontext2d.rs
+++ b/components/script/dom/canvasrenderingcontext2d.rs
@@ -4,7 +4,7 @@
use canvas_traits::canvas::{Canvas2dMsg, CanvasId, CanvasMsg, FromScriptMsg};
use dom_struct::dom_struct;
-use euclid::default::{Point2D, Rect, Size2D};
+use euclid::default::Size2D;
use profile_traits::ipc;
use script_bindings::inheritance::Castable;
use script_layout_interface::HTMLCanvasDataSource;
@@ -74,23 +74,12 @@ impl CanvasRenderingContext2D {
reflect_dom_object(boxed, global, can_gc)
}
- // https://html.spec.whatwg.org/multipage/#concept-canvas-set-bitmap-dimensions
- pub(crate) fn set_bitmap_dimensions(&self, size: Size2D<u32>) {
- self.reset_to_initial_state();
- self.canvas_state
- .get_ipc_renderer()
- .send(CanvasMsg::Recreate(
- Some(size.to_u64()),
- self.canvas_state.get_canvas_id(),
- ))
- .unwrap();
- }
-
// https://html.spec.whatwg.org/multipage/#reset-the-rendering-context-to-its-default-state
fn reset_to_initial_state(&self) {
self.canvas_state.reset_to_initial_state();
}
+ /// <https://html.spec.whatwg.org/multipage/#concept-canvas-set-bitmap-dimensions>
pub(crate) fn set_canvas_bitmap_dimensions(&self, size: Size2D<u64>) {
self.canvas_state.set_bitmap_dimensions(size);
}
@@ -106,20 +95,17 @@ impl CanvasRenderingContext2D {
pub(crate) fn send_canvas_2d_msg(&self, msg: Canvas2dMsg) {
self.canvas_state.send_canvas_2d_msg(msg)
}
-
- pub(crate) fn get_rect(&self, rect: Rect<u32>) -> Vec<u8> {
- let rect = Rect::new(
- Point2D::new(rect.origin.x as u64, rect.origin.y as u64),
- Size2D::new(rect.size.width as u64, rect.size.height as u64),
- );
- self.canvas_state.get_rect(self.canvas.size(), rect)
- }
}
impl LayoutCanvasRenderingContextHelpers for LayoutDom<'_, CanvasRenderingContext2D> {
fn canvas_data_source(self) -> HTMLCanvasDataSource {
let canvas_state = &self.unsafe_get().canvas_state;
- HTMLCanvasDataSource::Image(canvas_state.image_key())
+
+ if canvas_state.is_paintable() {
+ HTMLCanvasDataSource::Image(canvas_state.image_key())
+ } else {
+ HTMLCanvasDataSource::Empty
+ }
}
}
@@ -139,13 +125,11 @@ impl CanvasContext for CanvasRenderingContext2D {
}
fn resize(&self) {
- self.set_bitmap_dimensions(self.size().cast())
+ self.set_canvas_bitmap_dimensions(self.size().cast())
}
fn get_image_data(&self) -> Option<Snapshot> {
- let size = self.size();
-
- if size.is_empty() {
+ if !self.canvas_state.is_paintable() {
return None;
}
diff --git a/components/script/dom/defaultteereadrequest.rs b/components/script/dom/defaultteereadrequest.rs
index debc084e068..94e285da72b 100644
--- a/components/script/dom/defaultteereadrequest.rs
+++ b/components/script/dom/defaultteereadrequest.rs
@@ -21,7 +21,7 @@ use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::dom::readablestream::ReadableStream;
use crate::microtask::Microtask;
-use crate::script_runtime::CanGc;
+use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
#[derive(JSTraceable, MallocSizeOf)]
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
@@ -32,8 +32,8 @@ pub(crate) struct DefaultTeeReadRequestMicrotask {
}
impl DefaultTeeReadRequestMicrotask {
- pub(crate) fn microtask_chunk_steps(&self, can_gc: CanGc) {
- self.tee_read_request.chunk_steps(&self.chunk, can_gc)
+ pub(crate) fn microtask_chunk_steps(&self, cx: SafeJSContext, can_gc: CanGc) {
+ self.tee_read_request.chunk_steps(cx, &self.chunk, can_gc)
}
}
@@ -94,8 +94,14 @@ impl DefaultTeeReadRequest {
}
/// Call into cancel of the stream,
/// <https://streams.spec.whatwg.org/#readable-stream-cancel>
- pub(crate) fn stream_cancel(&self, reason: SafeHandleValue, can_gc: CanGc) {
- self.stream.cancel(reason, can_gc);
+ pub(crate) fn stream_cancel(
+ &self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
+ reason: SafeHandleValue,
+ can_gc: CanGc,
+ ) {
+ self.stream.cancel(cx, global, reason, can_gc);
}
/// Enqueue a microtask to perform the chunk steps
/// <https://streams.spec.whatwg.org/#ref-for-read-request-chunk-steps%E2%91%A2>
@@ -115,13 +121,13 @@ impl DefaultTeeReadRequest {
}
/// <https://streams.spec.whatwg.org/#ref-for-read-request-chunk-steps%E2%91%A2>
#[allow(clippy::borrowed_box)]
- pub(crate) fn chunk_steps(&self, chunk: &Box<Heap<JSVal>>, can_gc: CanGc) {
+ pub(crate) fn chunk_steps(&self, cx: SafeJSContext, chunk: &Box<Heap<JSVal>>, can_gc: CanGc) {
+ let global = &self.stream.global();
// Set readAgain to false.
self.read_again.set(false);
// Let chunk1 and chunk2 be chunk.
let chunk1 = chunk;
let chunk2 = chunk;
- let cx = GlobalScope::get_cx();
rooted!(in(*cx) let chunk1_value = chunk1.get());
rooted!(in(*cx) let chunk2_value = chunk2.get());
@@ -131,9 +137,7 @@ impl DefaultTeeReadRequest {
rooted!(in(*cx) let mut clone_result = UndefinedValue());
let data = structuredclone::write(cx, chunk2_value.handle(), None).unwrap();
// If cloneResult is an abrupt completion,
- if structuredclone::read(&self.stream.global(), data, clone_result.handle_mut())
- .is_err()
- {
+ if structuredclone::read(global, data, clone_result.handle_mut()).is_err() {
// Perform ! ReadableStreamDefaultControllerError(branch_1.[[controller]], cloneResult.[[Value]]).
self.readable_stream_default_controller_error(
&self.branch_1,
@@ -148,7 +152,7 @@ impl DefaultTeeReadRequest {
can_gc,
);
// Resolve cancelPromise with ! ReadableStreamCancel(stream, cloneResult.[[Value]]).
- self.stream_cancel(clone_result.handle(), can_gc);
+ self.stream_cancel(cx, global, clone_result.handle(), can_gc);
// Return.
return;
} else {
diff --git a/components/script/dom/defaultteeunderlyingsource.rs b/components/script/dom/defaultteeunderlyingsource.rs
index 5895297d982..7935c388842 100644
--- a/components/script/dom/defaultteeunderlyingsource.rs
+++ b/components/script/dom/defaultteeunderlyingsource.rs
@@ -19,7 +19,7 @@ use crate::dom::defaultteereadrequest::DefaultTeeReadRequest;
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::dom::readablestreamdefaultreader::ReadRequest;
-use crate::script_runtime::CanGc;
+use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
#[derive(JSTraceable, MallocSizeOf)]
pub(crate) enum TeeCancelAlgorithm {
@@ -156,6 +156,8 @@ impl DefaultTeeUnderlyingSource {
#[allow(unsafe_code)]
pub(crate) fn cancel_algorithm(
&self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
reason: SafeHandleValue,
can_gc: CanGc,
) -> Option<Result<Rc<Promise>, Error>> {
@@ -169,7 +171,7 @@ impl DefaultTeeUnderlyingSource {
// If canceled_2 is true,
if self.canceled_2.get() {
- self.resolve_cancel_promise(can_gc);
+ self.resolve_cancel_promise(cx, global, can_gc);
}
// Return cancelPromise.
Some(Ok(self.cancel_promise.clone()))
@@ -183,7 +185,7 @@ impl DefaultTeeUnderlyingSource {
// If canceled_1 is true,
if self.canceled_1.get() {
- self.resolve_cancel_promise(can_gc);
+ self.resolve_cancel_promise(cx, global, can_gc);
}
// Return cancelPromise.
Some(Ok(self.cancel_promise.clone()))
@@ -192,9 +194,8 @@ impl DefaultTeeUnderlyingSource {
}
#[allow(unsafe_code)]
- fn resolve_cancel_promise(&self, can_gc: CanGc) {
+ fn resolve_cancel_promise(&self, cx: SafeJSContext, global: &GlobalScope, can_gc: CanGc) {
// Let compositeReason be ! CreateArrayFromList(« reason_1, reason_2 »).
- let cx = GlobalScope::get_cx();
rooted_vec!(let mut reasons_values);
reasons_values.push(self.reason_1.get());
reasons_values.push(self.reason_2.get());
@@ -204,7 +205,9 @@ impl DefaultTeeUnderlyingSource {
rooted!(in(*cx) let reasons_value = ObjectValue(reasons.get()));
// Let cancelResult be ! ReadableStreamCancel(stream, compositeReason).
- let cancel_result = self.stream.cancel(reasons_value.handle(), can_gc);
+ let cancel_result = self
+ .stream
+ .cancel(cx, global, reasons_value.handle(), can_gc);
// Resolve cancelPromise with cancelResult.
self.cancel_promise.resolve_native(&cancel_result, can_gc);
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index 02bdd343d89..ec2ad98c464 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -1108,6 +1108,12 @@ impl Document {
self.scripting_enabled
}
+ /// Return whether scripting is enabled or not
+ /// <https://html.spec.whatwg.org/multipage/#concept-n-noscript>
+ pub(crate) fn scripting_enabled(&self) -> bool {
+ self.has_browsing_context()
+ }
+
/// Return the element that currently has focus.
// https://w3c.github.io/uievents/#events-focusevent-doc-focus
pub(crate) fn get_focused_element(&self) -> Option<DomRoot<Element>> {
@@ -1174,7 +1180,9 @@ impl Document {
let node = elem.upcast::<Node>();
elem.set_focus_state(false);
// FIXME: pass appropriate relatedTarget
- self.fire_focus_event(FocusEventType::Blur, node, None, can_gc);
+ if node.is_connected() {
+ self.fire_focus_event(FocusEventType::Blur, node, None, can_gc);
+ }
// Notify the embedder to hide the input method.
if elem.input_method_type().is_some() {
diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs
index ce6dcca66f3..2421b683bf7 100644
--- a/components/script/dom/htmlformelement.rs
+++ b/components/script/dom/htmlformelement.rs
@@ -1270,8 +1270,14 @@ impl HTMLFormElement {
return;
}
- let controls = self.controls.borrow();
- for child in controls.iter() {
+ let controls: Vec<_> = self
+ .controls
+ .borrow()
+ .iter()
+ .map(|c| c.as_rooted())
+ .collect();
+
+ for child in controls {
let child = child.upcast::<Node>();
match child.type_id() {
diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs
index 1999c7193ff..aff8168b8fe 100644
--- a/components/script/dom/htmlinputelement.rs
+++ b/components/script/dom/htmlinputelement.rs
@@ -1257,6 +1257,7 @@ impl HTMLInputElementMethods<crate::DomTypeHolder> for HTMLInputElement {
// https://html.spec.whatwg.org/multipage/#dom-input-checked
fn SetChecked(&self, checked: bool) {
self.update_checked_state(checked, true);
+ update_related_validity_states(self, CanGc::note())
}
// https://html.spec.whatwg.org/multipage/#dom-input-readonly
@@ -1697,7 +1698,7 @@ fn radio_group_iter<'a>(
) -> impl Iterator<Item = DomRoot<HTMLInputElement>> + 'a {
root.traverse_preorder(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<HTMLInputElement>)
- .filter(move |r| &**r == elem || in_same_group(r, form, group, None))
+ .filter(move |r| &**r == elem || in_same_group(r, form, group, Some(root)))
}
fn broadcast_radio_checked(broadcaster: &HTMLInputElement, group: Option<&Atom>) {
@@ -2580,6 +2581,9 @@ impl VirtualMethods for HTMLInputElement {
self.upcast::<Element>()
.check_ancestors_disabled_state_for_form_control();
+ if self.input_type() == InputType::Radio {
+ self.radio_group_updated(self.radio_group_name().as_ref());
+ }
update_related_validity_states(self, can_gc);
}
diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs
index 9452dcb17a6..58853f600d2 100644
--- a/components/script/dom/htmlscriptelement.rs
+++ b/components/script/dom/htmlscriptelement.rs
@@ -1008,12 +1008,12 @@ impl HTMLScriptElement {
Ok(script) => script,
};
- // TODO: we need to handle this for worker
if let Some(chan) = self.global().devtools_chan() {
let pipeline_id = self.global().pipeline_id();
let source_info = SourceInfo {
url: script.url.clone(),
external: script.external,
+ worker_id: None,
};
let _ = chan.send(ScriptToDevtoolsControlMsg::ScriptSourceLoaded(
pipeline_id,
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs
index 45a107ae673..b56126076da 100644
--- a/components/script/dom/node.rs
+++ b/components/script/dom/node.rs
@@ -2246,9 +2246,6 @@ impl Node {
};
// Step 4.
- Node::adopt(node, &parent.owner_document(), can_gc);
-
- // Step 5.
Node::insert(
node,
parent,
@@ -2257,7 +2254,7 @@ impl Node {
can_gc,
);
- // Step 6.
+ // Step 5.
Ok(DomRoot::from_ref(node))
}
@@ -2269,49 +2266,64 @@ impl Node {
suppress_observers: SuppressObserver,
can_gc: CanGc,
) {
- node.owner_doc().add_script_and_layout_blocker();
- debug_assert!(*node.owner_doc() == *parent.owner_doc());
debug_assert!(child.is_none_or(|child| Some(parent) == child.GetParentNode().as_deref()));
- // Step 1.
- let count = if node.is::<DocumentFragment>() {
- node.children_count()
+ // Step 1. Let nodes be node’s children, if node is a DocumentFragment node; otherwise « node ».
+ rooted_vec!(let mut new_nodes);
+ let new_nodes = if let NodeTypeId::DocumentFragment(_) = node.type_id() {
+ new_nodes.extend(node.children().map(|node| Dom::from_ref(&*node)));
+ new_nodes.r()
} else {
- 1
+ from_ref(&node)
};
- // Step 2.
- if let Some(child) = child {
- if !parent.ranges_is_empty() {
- let index = child.index();
- // Steps 2.1-2.
- parent.ranges().increase_above(parent, index, count);
- }
+
+ // Step 2. Let count be nodes’s size.
+ let count = new_nodes.len();
+
+ // Step 3. If count is 0, then return.
+ if count == 0 {
+ return;
}
- rooted_vec!(let mut new_nodes);
- let new_nodes = if let NodeTypeId::DocumentFragment(_) = node.type_id() {
- // Step 3.
- new_nodes.extend(node.children().map(|kid| Dom::from_ref(&*kid)));
- // Step 4.
- for kid in &*new_nodes {
+
+ // Script and layout blockers must be added after any early return.
+ // `node.owner_doc()` may change during the algorithm.
+ let parent_document = parent.owner_doc();
+ let from_document = node.owner_doc();
+ from_document.add_script_and_layout_blocker();
+ parent_document.add_script_and_layout_blocker();
+
+ // Step 4. If node is a DocumentFragment node:
+ if let NodeTypeId::DocumentFragment(_) = node.type_id() {
+ // Step 4.1. Remove its children with the suppress observers flag set.
+ for kid in new_nodes {
Node::remove(kid, node, SuppressObserver::Suppressed, can_gc);
}
- // Step 5.
- vtable_for(node).children_changed(&ChildrenMutation::replace_all(new_nodes.r(), &[]));
+ vtable_for(node).children_changed(&ChildrenMutation::replace_all(new_nodes, &[]));
+ // Step 4.2. Queue a tree mutation record for node with « », nodes, null, and null.
let mutation = LazyCell::new(|| Mutation::ChildList {
added: None,
- removed: Some(new_nodes.r()),
+ removed: Some(new_nodes),
prev: None,
next: None,
});
MutationObserver::queue_a_mutation_record(node, mutation);
+ }
- new_nodes.r()
- } else {
- // Step 3.
- from_ref(&node)
- };
- // Step 6.
+ // Step 5. If child is non-null:
+ // 1. For each live range whose start node is parent and start offset is
+ // greater than child’s index, increase its start offset by count.
+ // 2. For each live range whose end node is parent and end offset is
+ // greater than child’s index, increase its end offset by count.
+ if let Some(child) = child {
+ if !parent.ranges_is_empty() {
+ parent
+ .ranges()
+ .increase_above(parent, child.index(), count.try_into().unwrap());
+ }
+ }
+
+ // Step 6. Let previousSibling be child’s previous sibling or parent’s last child if child is null.
let previous_sibling = match suppress_observers {
SuppressObserver::Unsuppressed => match child {
Some(child) => child.GetPreviousSibling(),
@@ -2319,9 +2331,14 @@ impl Node {
},
SuppressObserver::Suppressed => None,
};
- // Step 7.
+
+ // Step 7. For each node in nodes, in tree order:
for kid in new_nodes {
- // Step 7.1.
+ // Step 7.1. Adopt node into parent’s node document.
+ Node::adopt(kid, &parent.owner_document(), can_gc);
+
+ // Step 7.2. If child is null, then append node to parent’s children.
+ // Step 7.3. Otherwise, insert node into parent’s children before child’s index.
parent.add_child(kid, child, can_gc);
// Step 7.4 If parent is a shadow host whose shadow root’s slot assignment is "named"
@@ -2350,12 +2367,17 @@ impl Node {
kid.GetRootNode(&GetRootNodeOptions::empty())
.assign_slottables_for_a_tree();
- // Step 7.7.
+ // Step 7.7. For each shadow-including inclusive descendant inclusiveDescendant of node,
+ // in shadow-including tree order:
for descendant in kid
.traverse_preorder(ShadowIncluding::Yes)
.filter_map(DomRoot::downcast::<Element>)
{
+ // Step 7.7.1. Run the insertion steps with inclusiveDescendant.
+ // This is done in `parent.add_child()`.
+
// Step 7.7.2, whatwg/dom#833
+ // Enqueue connected reactions for custom elements or try upgrade.
if descendant.is_custom() {
if descendant.is_connected() {
ScriptThread::enqueue_callback_reaction(
@@ -2369,13 +2391,18 @@ impl Node {
}
}
}
+
if let SuppressObserver::Unsuppressed = suppress_observers {
+ // Step 9. Run the children changed steps for parent.
+ // TODO(xiaochengh): If we follow the spec and move it out of the if block, some WPT fail. Investigate.
vtable_for(parent).children_changed(&ChildrenMutation::insert(
previous_sibling.as_deref(),
new_nodes,
child,
));
+ // Step 8. If suppress observers flag is unset, then queue a tree mutation record for parent
+ // with nodes, « », previousSibling, and child.
let mutation = LazyCell::new(|| Mutation::ChildList {
added: Some(new_nodes),
removed: None,
@@ -2408,7 +2435,7 @@ impl Node {
// 2) post_connection_steps from Node::insert,
// we use a delayed task that will run as soon as Node::insert removes its
// script/layout blocker.
- node.owner_doc().add_delayed_task(task!(PostConnectionSteps: move || {
+ parent_document.add_delayed_task(task!(PostConnectionSteps: move || {
// Step 12. For each node of staticNodeList, if node is connected, then run the
// post-connection steps with node.
for node in static_node_list.iter().map(Trusted::root).filter(|n| n.is_connected()) {
@@ -2416,7 +2443,8 @@ impl Node {
}
}));
- node.owner_doc().remove_script_and_layout_blocker();
+ parent_document.remove_script_and_layout_blocker();
+ from_document.remove_script_and_layout_blocker();
}
/// <https://dom.spec.whatwg.org/#concept-node-replace-all>
@@ -3239,10 +3267,16 @@ impl NodeMethods<crate::DomTypeHolder> for Node {
// Step 9.
let previous_sibling = child.GetPreviousSibling();
- // Step 10.
+ // NOTE: All existing browsers assume that adoption is performed here, which does not follow the DOM spec.
+ // However, if we follow the spec and delay adoption to inside `Node::insert()`, then the mutation records will
+ // be different, and we will fail WPT dom/nodes/MutationObserver-childList.html.
let document = self.owner_document();
Node::adopt(node, &document, can_gc);
+ // Step 10. Let removedNodes be the empty set.
+ // Step 11. If child’s parent is non-null:
+ // 1. Set removedNodes to « child ».
+ // 2. Remove child with the suppress observers flag set.
let removed_child = if node != child {
// Step 11.
Node::remove(child, self, SuppressObserver::Suppressed, can_gc);
diff --git a/components/script/dom/offscreencanvasrenderingcontext2d.rs b/components/script/dom/offscreencanvasrenderingcontext2d.rs
index 2f9b52640e6..b2d0f3201ca 100644
--- a/components/script/dom/offscreencanvasrenderingcontext2d.rs
+++ b/components/script/dom/offscreencanvasrenderingcontext2d.rs
@@ -65,7 +65,7 @@ impl OffscreenCanvasRenderingContext2D {
}
pub(crate) fn set_canvas_bitmap_dimensions(&self, size: Size2D<u64>) {
- self.context.set_bitmap_dimensions(size.cast());
+ self.context.set_canvas_bitmap_dimensions(size.cast());
}
pub(crate) fn send_canvas_2d_msg(&self, msg: Canvas2dMsg) {
diff --git a/components/script/dom/readablebytestreamcontroller.rs b/components/script/dom/readablebytestreamcontroller.rs
index 340e6d04eab..8f28a9a1215 100644
--- a/components/script/dom/readablebytestreamcontroller.rs
+++ b/components/script/dom/readablebytestreamcontroller.rs
@@ -1612,7 +1612,7 @@ impl ReadableByteStreamController {
let realm = enter_realm(&*global);
let comp = InRealm::Entered(&realm);
let result = underlying_source
- .call_pull_algorithm(controller, can_gc)
+ .call_pull_algorithm(controller, &global, can_gc)
.unwrap_or_else(|| {
let promise = Promise::new(&global, can_gc);
promise.resolve_native(&(), can_gc);
@@ -1781,6 +1781,8 @@ impl ReadableByteStreamController {
/// <https://streams.spec.whatwg.org/#rbs-controller-private-cancel>
pub(crate) fn perform_cancel_steps(
&self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
reason: SafeHandleValue,
can_gc: CanGc,
) -> Rc<Promise> {
@@ -1794,13 +1796,12 @@ impl ReadableByteStreamController {
.underlying_source
.get()
.expect("Controller should have a source when the cancel steps are called into.");
- let global = self.global();
// Let result be the result of performing this.[[cancelAlgorithm]], passing in reason.
let result = underlying_source
- .call_cancel_algorithm(reason, can_gc)
+ .call_cancel_algorithm(cx, global, reason, can_gc)
.unwrap_or_else(|| {
- let promise = Promise::new(&global, can_gc);
+ let promise = Promise::new(global, can_gc);
promise.resolve_native(&(), can_gc);
Ok(promise)
});
@@ -1808,11 +1809,10 @@ impl ReadableByteStreamController {
let promise = result.unwrap_or_else(|error| {
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut rval = UndefinedValue());
- // TODO: check if `self.global()` is the right globalscope.
error
.clone()
- .to_jsval(cx, &self.global(), rval.handle_mut(), can_gc);
- let promise = Promise::new(&global, can_gc);
+ .to_jsval(cx, global, rval.handle_mut(), can_gc);
+ let promise = Promise::new(global, can_gc);
promise.reject_native(&rval.handle(), can_gc);
promise
});
diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs
index 37899f18fec..51393ab33ae 100644
--- a/components/script/dom/readablestream.rs
+++ b/components/script/dom/readablestream.rs
@@ -3,10 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::cell::{Cell, RefCell};
+use std::collections::HashMap;
use std::collections::VecDeque;
use std::ptr::{self};
use std::rc::Rc;
-use std::collections::HashMap;
use base::id::{MessagePortId, MessagePortIndex};
use constellation_traits::MessagePortImpl;
@@ -22,12 +22,14 @@ use js::typedarray::ArrayBufferViewU8;
use crate::dom::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategy;
use crate::dom::bindings::codegen::Bindings::ReadableStreamBinding::{
- ReadableStreamGetReaderOptions, ReadableStreamMethods, ReadableStreamReaderMode, StreamPipeOptions
+ ReadableStreamGetReaderOptions, ReadableStreamMethods, ReadableStreamReaderMode,
+ StreamPipeOptions,
};
use script_bindings::str::DOMString;
use crate::dom::domexception::{DOMErrorName, DOMException};
use script_bindings::conversions::StringificationBehavior;
+use super::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategySize;
use crate::dom::bindings::codegen::Bindings::ReadableStreamDefaultReaderBinding::ReadableStreamDefaultReaderMethods;
use crate::dom::bindings::codegen::Bindings::ReadableStreamDefaultControllerBinding::ReadableStreamDefaultController_Binding::ReadableStreamDefaultControllerMethods;
use crate::dom::bindings::codegen::Bindings::UnderlyingSourceBinding::UnderlyingSource as JsUnderlyingSource;
@@ -640,7 +642,7 @@ impl PipeTo {
.reader
.get_stream()
.expect("Reader should have a stream.");
- source.cancel(error.handle(), can_gc)
+ source.cancel(cx, global, error.handle(), can_gc)
},
ShutdownAction::WritableStreamDefaultWriterCloseWithErrorPropagation => {
self.writer.close_with_error_propagation(cx, global, can_gc)
@@ -766,19 +768,19 @@ impl PartialEq for ReaderType {
/// <https://streams.spec.whatwg.org/#create-readable-stream>
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
-fn create_readable_stream(
+pub(crate) fn create_readable_stream(
global: &GlobalScope,
underlying_source_type: UnderlyingSourceType,
- queuing_strategy: QueuingStrategy,
+ queuing_strategy: Option<Rc<QueuingStrategySize>>,
+ high_water_mark: Option<f64>,
can_gc: CanGc,
) -> DomRoot<ReadableStream> {
// If highWaterMark was not passed, set it to 1.
- let high_water_mark = queuing_strategy.highWaterMark.unwrap_or(1.0);
+ let high_water_mark = high_water_mark.unwrap_or(1.0);
// If sizeAlgorithm was not passed, set it to an algorithm that returns 1.
- let size_algorithm = queuing_strategy
- .size
- .unwrap_or(extract_size_algorithm(&QueuingStrategy::empty(), can_gc));
+ let size_algorithm =
+ queuing_strategy.unwrap_or(extract_size_algorithm(&QueuingStrategy::empty(), can_gc));
// Assert: ! IsNonNegativeNumber(highWaterMark) is true.
assert!(high_water_mark >= 0.0);
@@ -1437,19 +1439,24 @@ impl ReadableStream {
/// <https://streams.spec.whatwg.org/#readable-stream-cancel>
#[allow(unsafe_code)]
- pub(crate) fn cancel(&self, reason: SafeHandleValue, can_gc: CanGc) -> Rc<Promise> {
+ pub(crate) fn cancel(
+ &self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
+ reason: SafeHandleValue,
+ can_gc: CanGc,
+ ) -> Rc<Promise> {
// Set stream.[[disturbed]] to true.
self.disturbed.set(true);
// If stream.[[state]] is "closed", return a promise resolved with undefined.
if self.is_closed() {
- return Promise::new_resolved(&self.global(), GlobalScope::get_cx(), (), can_gc);
+ return Promise::new_resolved(global, cx, (), can_gc);
}
// If stream.[[state]] is "errored", return a promise rejected with stream.[[storedError]].
if self.is_errored() {
- let promise = Promise::new(&self.global(), can_gc);
+ let promise = Promise::new(global, can_gc);
unsafe {
- let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut rval = UndefinedValue());
self.stored_error.to_jsval(*cx, rval.handle_mut());
promise.reject_native(&rval.handle(), can_gc);
@@ -1473,11 +1480,11 @@ impl ReadableStream {
Some(ControllerType::Default(controller)) => controller
.get()
.expect("Stream should have controller.")
- .perform_cancel_steps(reason, can_gc),
+ .perform_cancel_steps(cx, global, reason, can_gc),
Some(ControllerType::Byte(controller)) => controller
.get()
.expect("Stream should have controller.")
- .perform_cancel_steps(reason, can_gc),
+ .perform_cancel_steps(cx, global, reason, can_gc),
None => {
panic!("Stream does not have a controller.");
},
@@ -1587,7 +1594,8 @@ impl ReadableStream {
let branch_1 = create_readable_stream(
&self.global(),
underlying_source_type_branch_1,
- QueuingStrategy::empty(),
+ None,
+ None,
can_gc,
);
tee_source_1.set_branch_1(&branch_1);
@@ -1597,7 +1605,8 @@ impl ReadableStream {
let branch_2 = create_readable_stream(
&self.global(),
underlying_source_type_branch_2,
- QueuingStrategy::empty(),
+ None,
+ None,
can_gc,
);
tee_source_1.set_branch_2(&branch_2);
@@ -1908,16 +1917,17 @@ impl ReadableStreamMethods<crate::DomTypeHolder> for ReadableStream {
}
/// <https://streams.spec.whatwg.org/#rs-cancel>
- fn Cancel(&self, _cx: SafeJSContext, reason: SafeHandleValue, can_gc: CanGc) -> Rc<Promise> {
+ fn Cancel(&self, cx: SafeJSContext, reason: SafeHandleValue, can_gc: CanGc) -> Rc<Promise> {
+ let global = self.global();
if self.is_locked() {
// If ! IsReadableStreamLocked(this) is true,
// return a promise rejected with a TypeError exception.
- let promise = Promise::new(&self.global(), can_gc);
+ let promise = Promise::new(&global, can_gc);
promise.reject_error(Error::Type("stream is not locked".to_owned()), can_gc);
promise
} else {
// Return ! ReadableStreamCancel(this, reason).
- self.cancel(reason, can_gc)
+ self.cancel(cx, &global, reason, can_gc)
}
}
diff --git a/components/script/dom/readablestreambyobreader.rs b/components/script/dom/readablestreambyobreader.rs
index 16827c1add6..3ccfb255009 100644
--- a/components/script/dom/readablestreambyobreader.rs
+++ b/components/script/dom/readablestreambyobreader.rs
@@ -401,8 +401,8 @@ impl ReadableStreamBYOBReaderMethods<crate::DomTypeHolder> for ReadableStreamBYO
}
/// <https://streams.spec.whatwg.org/#generic-reader-cancel>
- fn Cancel(&self, _cx: SafeJSContext, reason: SafeHandleValue, can_gc: CanGc) -> Rc<Promise> {
- self.generic_cancel(&self.global(), reason, can_gc)
+ fn Cancel(&self, cx: SafeJSContext, reason: SafeHandleValue, can_gc: CanGc) -> Rc<Promise> {
+ self.generic_cancel(cx, &self.global(), reason, can_gc)
}
}
diff --git a/components/script/dom/readablestreamdefaultcontroller.rs b/components/script/dom/readablestreamdefaultcontroller.rs
index 66ba3d209c7..c52fb712a03 100644
--- a/components/script/dom/readablestreamdefaultcontroller.rs
+++ b/components/script/dom/readablestreamdefaultcontroller.rs
@@ -540,7 +540,7 @@ impl ReadableStreamDefaultController {
let realm = enter_realm(&*global);
let comp = InRealm::Entered(&realm);
let result = underlying_source
- .call_pull_algorithm(controller, can_gc)
+ .call_pull_algorithm(controller, &global, can_gc)
.unwrap_or_else(|| {
let promise = Promise::new(&global, can_gc);
promise.resolve_native(&(), can_gc);
@@ -563,6 +563,8 @@ impl ReadableStreamDefaultController {
/// <https://streams.spec.whatwg.org/#rs-default-controller-private-cancel>
pub(crate) fn perform_cancel_steps(
&self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
reason: SafeHandleValue,
can_gc: CanGc,
) -> Rc<Promise> {
@@ -573,24 +575,21 @@ impl ReadableStreamDefaultController {
.underlying_source
.get()
.expect("Controller should have a source when the cancel steps are called into.");
- let global = self.global();
-
// Let result be the result of performing this.[[cancelAlgorithm]], passing reason.
let result = underlying_source
- .call_cancel_algorithm(reason, can_gc)
+ .call_cancel_algorithm(cx, global, reason, can_gc)
.unwrap_or_else(|| {
- let promise = Promise::new(&global, can_gc);
+ let promise = Promise::new(global, can_gc);
promise.resolve_native(&(), can_gc);
Ok(promise)
});
let promise = result.unwrap_or_else(|error| {
- let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut rval = UndefinedValue());
- // TODO: check if `self.global()` is the right globalscope.
+
error
.clone()
- .to_jsval(cx, &self.global(), rval.handle_mut(), can_gc);
- let promise = Promise::new(&global, can_gc);
+ .to_jsval(cx, global, rval.handle_mut(), can_gc);
+ let promise = Promise::new(global, can_gc);
promise.reject_native(&rval.handle(), can_gc);
promise
});
@@ -812,7 +811,7 @@ impl ReadableStreamDefaultController {
}
/// <https://streams.spec.whatwg.org/#readable-stream-default-controller-get-desired-size>
- fn get_desired_size(&self) -> Option<f64> {
+ pub(crate) fn get_desired_size(&self) -> Option<f64> {
let stream = self.stream.get()?;
// If state is "errored", return null.
@@ -832,7 +831,7 @@ impl ReadableStreamDefaultController {
}
/// <https://streams.spec.whatwg.org/#readable-stream-default-controller-can-close-or-enqueue>
- fn can_close_or_enqueue(&self) -> bool {
+ pub(crate) fn can_close_or_enqueue(&self) -> bool {
let Some(stream) = self.stream.get() else {
return false;
};
@@ -865,6 +864,14 @@ impl ReadableStreamDefaultController {
stream.error(e, can_gc);
}
+
+ /// <https://streams.spec.whatwg.org/#rs-default-controller-has-backpressure>
+ #[allow(unused)]
+ pub(crate) fn has_backpressure(&self) -> bool {
+ // If ! ReadableStreamDefaultControllerShouldCallPull(controller) is true, return false.
+ // Otherwise, return true.
+ !self.should_call_pull()
+ }
}
impl ReadableStreamDefaultControllerMethods<crate::DomTypeHolder>
diff --git a/components/script/dom/readablestreamdefaultreader.rs b/components/script/dom/readablestreamdefaultreader.rs
index f490627a2ee..7fd243b0b56 100644
--- a/components/script/dom/readablestreamdefaultreader.rs
+++ b/components/script/dom/readablestreamdefaultreader.rs
@@ -605,8 +605,8 @@ impl ReadableStreamDefaultReaderMethods<crate::DomTypeHolder> for ReadableStream
}
/// <https://streams.spec.whatwg.org/#generic-reader-cancel>
- fn Cancel(&self, _cx: SafeJSContext, reason: SafeHandleValue, can_gc: CanGc) -> Rc<Promise> {
- self.generic_cancel(&self.global(), reason, can_gc)
+ fn Cancel(&self, cx: SafeJSContext, reason: SafeHandleValue, can_gc: CanGc) -> Rc<Promise> {
+ self.generic_cancel(cx, &self.global(), reason, can_gc)
}
}
diff --git a/components/script/dom/readablestreamgenericreader.rs b/components/script/dom/readablestreamgenericreader.rs
index b437605953b..8ba1149bcb5 100644
--- a/components/script/dom/readablestreamgenericreader.rs
+++ b/components/script/dom/readablestreamgenericreader.rs
@@ -16,7 +16,7 @@ use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::dom::readablestreambyobreader::ReadableStreamBYOBReader;
use crate::dom::readablestreamdefaultreader::ReadableStreamDefaultReader;
-use crate::script_runtime::CanGc;
+use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
/// <https://streams.spec.whatwg.org/#readablestreamgenericreader>
pub(crate) trait ReadableStreamGenericReader {
@@ -61,7 +61,13 @@ pub(crate) trait ReadableStreamGenericReader {
}
/// <https://streams.spec.whatwg.org/#readable-stream-reader-generic-cancel>
- fn reader_generic_cancel(&self, reason: SafeHandleValue, can_gc: CanGc) -> Rc<Promise> {
+ fn reader_generic_cancel(
+ &self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
+ reason: SafeHandleValue,
+ can_gc: CanGc,
+ ) -> Rc<Promise> {
// Let stream be reader.[[stream]].
let stream = self.get_stream();
@@ -70,7 +76,7 @@ pub(crate) trait ReadableStreamGenericReader {
stream.expect("Reader should have a stream when generic cancel is called into.");
// Return ! ReadableStreamCancel(stream, reason).
- stream.cancel(reason, can_gc)
+ stream.cancel(cx, global, reason, can_gc)
}
/// <https://streams.spec.whatwg.org/#readable-stream-reader-generic-release>
@@ -135,6 +141,7 @@ pub(crate) trait ReadableStreamGenericReader {
// <https://streams.spec.whatwg.org/#generic-reader-cancel>
fn generic_cancel(
&self,
+ cx: SafeJSContext,
global: &GlobalScope,
reason: SafeHandleValue,
can_gc: CanGc,
@@ -147,7 +154,7 @@ pub(crate) trait ReadableStreamGenericReader {
promise
} else {
// Return ! ReadableStreamReaderGenericCancel(this, reason).
- self.reader_generic_cancel(reason, can_gc)
+ self.reader_generic_cancel(cx, global, reason, can_gc)
}
}
diff --git a/components/script/dom/servoparser/html.rs b/components/script/dom/servoparser/html.rs
index 97856256268..07848c87678 100644
--- a/components/script/dom/servoparser/html.rs
+++ b/components/script/dom/servoparser/html.rs
@@ -11,11 +11,12 @@ use html5ever::buffer_queue::BufferQueue;
use html5ever::serialize::TraversalScope::IncludeNode;
use html5ever::serialize::{AttrRef, Serialize, Serializer, TraversalScope};
use html5ever::tokenizer::{Tokenizer as HtmlTokenizer, TokenizerOpts};
-use html5ever::tree_builder::{TreeBuilder, TreeBuilderOpts};
+use html5ever::tree_builder::{QuirksMode as HTML5EverQuirksMode, TreeBuilder, TreeBuilderOpts};
use html5ever::{QualName, local_name, ns};
use markup5ever::TokenizerResult;
use script_bindings::trace::CustomTraceable;
use servo_url::ServoUrl;
+use style::context::QuirksMode as StyleContextQuirksMode;
use xml5ever::LocalName;
use crate::dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
@@ -58,9 +59,17 @@ impl Tokenizer {
parsing_algorithm,
};
+ let quirks_mode = match document.quirks_mode() {
+ StyleContextQuirksMode::Quirks => HTML5EverQuirksMode::Quirks,
+ StyleContextQuirksMode::LimitedQuirks => HTML5EverQuirksMode::LimitedQuirks,
+ StyleContextQuirksMode::NoQuirks => HTML5EverQuirksMode::NoQuirks,
+ };
+
let options = TreeBuilderOpts {
ignore_missing_rules: true,
- scripting_enabled: document.has_browsing_context(),
+ scripting_enabled: document.scripting_enabled(),
+ iframe_srcdoc: document.url().as_str() == "about:srcdoc",
+ quirks_mode,
..Default::default()
};
diff --git a/components/script/dom/underlyingsourcecontainer.rs b/components/script/dom/underlyingsourcecontainer.rs
index cf396825d4f..541a831693a 100644
--- a/components/script/dom/underlyingsourcecontainer.rs
+++ b/components/script/dom/underlyingsourcecontainer.rs
@@ -20,7 +20,7 @@ use crate::dom::defaultteeunderlyingsource::DefaultTeeUnderlyingSource;
use crate::dom::globalscope::GlobalScope;
use crate::dom::messageport::MessagePort;
use crate::dom::promise::Promise;
-use crate::script_runtime::CanGc;
+use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
/// <https://streams.spec.whatwg.org/#underlying-source-api>
/// The `Js` variant corresponds to
@@ -43,6 +43,11 @@ pub(crate) enum UnderlyingSourceType {
Tee(Dom<DefaultTeeUnderlyingSource>),
/// Transfer, with the port used in some of the algorithms.
Transfer(Dom<MessagePort>),
+ /// A struct representing a JS object as underlying source,
+ /// and the actual JS object for use as `thisArg` in callbacks.
+ /// This is used for the `TransformStream` API.
+ #[allow(unused)]
+ Transform(/* Dom<TransformStream>, Rc<Promise>*/),
}
impl UnderlyingSourceType {
@@ -110,6 +115,8 @@ impl UnderlyingSourceContainer {
#[allow(unsafe_code)]
pub(crate) fn call_cancel_algorithm(
&self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
reason: SafeHandleValue,
can_gc: CanGc,
) -> Option<Result<Rc<Promise>, Error>> {
@@ -128,9 +135,13 @@ impl UnderlyingSourceContainer {
}
None
},
- UnderlyingSourceType::Tee(tee_underlyin_source) => {
+ UnderlyingSourceType::Tee(tee_underlying_source) => {
// Call the cancel algorithm for the appropriate branch.
- tee_underlyin_source.cancel_algorithm(reason, can_gc)
+ tee_underlying_source.cancel_algorithm(cx, global, reason, can_gc)
+ },
+ UnderlyingSourceType::Transform() => {
+ // Return ! TransformStreamDefaultSourceCancelAlgorithm(stream, reason).
+ todo!();
},
UnderlyingSourceType::Transfer(port) => {
// Let cancelAlgorithm be the following steps, taking a reason argument:
@@ -163,6 +174,7 @@ impl UnderlyingSourceContainer {
pub(crate) fn call_pull_algorithm(
&self,
controller: Controller,
+ _global: &GlobalScope,
can_gc: CanGc,
) -> Option<Result<Rc<Promise>, Error>> {
match &self.underlying_source_type {
@@ -180,9 +192,9 @@ impl UnderlyingSourceContainer {
}
None
},
- UnderlyingSourceType::Tee(tee_underlyin_source) => {
+ UnderlyingSourceType::Tee(tee_underlying_source) => {
// Call the pull algorithm for the appropriate branch.
- Some(Ok(tee_underlyin_source.pull_algorithm(can_gc)))
+ Some(Ok(tee_underlying_source.pull_algorithm(can_gc)))
},
UnderlyingSourceType::Transfer(port) => {
// Let pullAlgorithm be the following steps:
@@ -201,6 +213,10 @@ impl UnderlyingSourceContainer {
Some(Ok(promise))
},
// Note: other source type have no pull steps for now.
+ UnderlyingSourceType::Transform() => {
+ // Return ! TransformStreamDefaultSourcePullAlgorithm(stream).
+ todo!();
+ },
_ => None,
}
}
@@ -264,6 +280,10 @@ impl UnderlyingSourceContainer {
// from <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformreadable
None
},
+ UnderlyingSourceType::Transform() => {
+ // Some(transform_underlying_source.start_algorithm())
+ todo!();
+ },
_ => None,
}
}
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index 932a9ec7f2d..e210476a5df 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -1389,7 +1389,17 @@ impl WindowMethods<crate::DomTypeHolder> for Window {
// https://drafts.csswg.org/cssom-view/#dom-window-resizeto
fn ResizeTo(&self, width: i32, height: i32) {
// Step 1
- //TODO determine if this operation is allowed
+ let window_proxy = match self.window_proxy.get() {
+ Some(proxy) => proxy,
+ None => return,
+ };
+
+ // If target is not an auxiliary browsing context that was created by a script
+ // (as opposed to by an action of the user), then return.
+ if !window_proxy.is_auxiliary() {
+ return;
+ }
+
let dpr = self.device_pixel_ratio();
let size = Size2D::new(width, height).to_f32() * dpr;
self.send_to_embedder(EmbedderMsg::ResizeTo(self.webview_id(), size.to_i32()));
diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs
index 429234c7a8e..6ad56026db6 100644
--- a/components/script/dom/worker.rs
+++ b/components/script/dom/worker.rs
@@ -8,7 +8,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use constellation_traits::{StructuredSerializedData, WorkerScriptLoadOrigin};
use crossbeam_channel::{Sender, unbounded};
-use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg, WorkerId};
+use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg, SourceInfo, WorkerId};
use dom_struct::dom_struct;
use ipc_channel::ipc;
use js::jsapi::{Heap, JSObject};
@@ -213,6 +213,16 @@ impl WorkerMethods<crate::DomTypeHolder> for Worker {
devtools_sender.clone(),
page_info,
));
+
+ let source_info = SourceInfo {
+ url: worker_url.clone(),
+ external: true, // Worker scripts are always external.
+ worker_id: Some(worker_id),
+ };
+ let _ = chan.send(ScriptToDevtoolsControlMsg::ScriptSourceLoaded(
+ pipeline_id,
+ source_info,
+ ));
}
}
diff --git a/components/script/dom/writablestream.rs b/components/script/dom/writablestream.rs
index e7e9ce906a6..8c2b2434cd2 100644
--- a/components/script/dom/writablestream.rs
+++ b/components/script/dom/writablestream.rs
@@ -19,6 +19,7 @@ use js::rust::{
};
use script_bindings::codegen::GenericBindings::MessagePortBinding::MessagePortMethods;
+use super::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategySize;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategy;
use crate::dom::bindings::codegen::Bindings::UnderlyingSinkBinding::UnderlyingSink;
@@ -209,6 +210,11 @@ impl WritableStream {
self.controller.set(Some(controller));
}
+ #[allow(unused)]
+ pub(crate) fn get_default_controller(&self) -> DomRoot<WritableStreamDefaultController> {
+ self.controller.get().expect("Controller should be set.")
+ }
+
pub(crate) fn is_writable(&self) -> bool {
matches!(self.state.get(), WritableStreamState::Writable)
}
@@ -873,7 +879,6 @@ impl WritableStream {
backpressure_promise: backpressure_promise.clone(),
port: Dom::from_ref(port),
},
- &UnderlyingSink::empty(),
1.0,
size_algorithm,
can_gc,
@@ -892,9 +897,102 @@ impl WritableStream {
// Perform ! SetUpWritableStreamDefaultController
controller
- .setup(cx, &global, self, &None, can_gc)
+ .setup(cx, &global, self, can_gc)
.expect("Setup for transfer cannot fail");
}
+ /// <https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller-from-underlying-sink>
+ #[allow(clippy::too_many_arguments)]
+ pub(crate) fn setup_from_underlying_sink(
+ &self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
+ stream: &WritableStream,
+ underlying_sink_obj: SafeHandleObject,
+ underlying_sink: &UnderlyingSink,
+ strategy_hwm: f64,
+ strategy_size: Rc<QueuingStrategySize>,
+ can_gc: CanGc,
+ ) -> Result<(), Error> {
+ // Let controller be a new WritableStreamDefaultController.
+
+ // Let startAlgorithm be an algorithm that returns undefined.
+
+ // Let writeAlgorithm be an algorithm that returns a promise resolved with undefined.
+
+ // Let closeAlgorithm be an algorithm that returns a promise resolved with undefined.
+
+ // Let abortAlgorithm be an algorithm that returns a promise resolved with undefined.
+
+ // If underlyingSinkDict["start"] exists, then set startAlgorithm to an algorithm which
+ // returns the result of invoking underlyingSinkDict["start"] with argument
+ // list « controller », exception behavior "rethrow", and callback this value underlyingSink.
+
+ // If underlyingSinkDict["write"] exists, then set writeAlgorithm to an algorithm which
+ // takes an argument chunk and returns the result of invoking underlyingSinkDict["write"]
+ // with argument list « chunk, controller » and callback this value underlyingSink.
+
+ // If underlyingSinkDict["close"] exists, then set closeAlgorithm to an algorithm which
+ // returns the result of invoking underlyingSinkDict["close"] with argument
+ // list «» and callback this value underlyingSink.
+
+ // If underlyingSinkDict["abort"] exists, then set abortAlgorithm to an algorithm which
+ // takes an argument reason and returns the result of invoking underlyingSinkDict["abort"]
+ // with argument list « reason » and callback this value underlyingSink.
+ let controller = WritableStreamDefaultController::new(
+ global,
+ UnderlyingSinkType::new_js(
+ underlying_sink.abort.clone(),
+ underlying_sink.start.clone(),
+ underlying_sink.close.clone(),
+ underlying_sink.write.clone(),
+ ),
+ strategy_hwm,
+ strategy_size,
+ can_gc,
+ );
+
+ // Note: this must be done before `setup`,
+ // otherwise `thisOb` is null in the start callback.
+ controller.set_underlying_sink_this_object(underlying_sink_obj);
+
+ // Perform ? SetUpWritableStreamDefaultController
+ controller.setup(cx, global, stream, can_gc)
+ }
+}
+
+/// <https://streams.spec.whatwg.org/#create-writable-stream>
+#[cfg_attr(crown, allow(crown::unrooted_must_root))]
+#[allow(unused)]
+pub(crate) fn create_writable_stream(
+ cx: SafeJSContext,
+ global: &GlobalScope,
+ can_gc: CanGc,
+ writable_high_water_mark: f64,
+ writable_size_algorithm: Rc<QueuingStrategySize>,
+ underlying_sink_type: UnderlyingSinkType,
+) -> Fallible<DomRoot<WritableStream>> {
+ // Assert: ! IsNonNegativeNumber(highWaterMark) is true.
+ assert!(writable_high_water_mark >= 0.0);
+
+ // Let stream be a new WritableStream.
+ // Perform ! InitializeWritableStream(stream).
+ let stream = WritableStream::new_with_proto(global, None, can_gc);
+
+ // Let controller be a new WritableStreamDefaultController.
+ let controller = WritableStreamDefaultController::new(
+ global,
+ underlying_sink_type,
+ writable_high_water_mark,
+ writable_size_algorithm,
+ can_gc,
+ );
+
+ // Perform ? SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm,
+ // closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm).
+ controller.setup(cx, global, &stream, can_gc)?;
+
+ // Return stream.
+ Ok(stream)
}
impl WritableStreamMethods<crate::DomTypeHolder> for WritableStream {
@@ -939,22 +1037,18 @@ impl WritableStreamMethods<crate::DomTypeHolder> for WritableStream {
// Let highWaterMark be ? ExtractHighWaterMark(strategy, 1).
let high_water_mark = extract_high_water_mark(strategy, 1.0)?;
- // Perform ? SetUpWritableStreamDefaultControllerFromUnderlyingSink
- let controller = WritableStreamDefaultController::new(
+ // Perform ? SetUpWritableStreamDefaultControllerFromUnderlyingSink(this, underlyingSink,
+ // underlyingSinkDict, highWaterMark, sizeAlgorithm).
+ stream.setup_from_underlying_sink(
+ cx,
global,
- UnderlyingSinkType::Js,
+ &stream,
+ underlying_sink_obj.handle(),
&underlying_sink_dict,
high_water_mark,
size_algorithm,
can_gc,
- );
-
- // Note: this must be done before `setup`,
- // otherwise `thisOb` is null in the start callback.
- controller.set_underlying_sink_this_object(underlying_sink_obj.handle());
-
- // Perform ? SetUpWritableStreamDefaultController
- controller.setup(cx, global, &stream, &underlying_sink_dict.start, can_gc)?;
+ )?;
Ok(stream)
}
diff --git a/components/script/dom/writablestreamdefaultcontroller.rs b/components/script/dom/writablestreamdefaultcontroller.rs
index 751f5d8d976..301404ffdb2 100644
--- a/components/script/dom/writablestreamdefaultcontroller.rs
+++ b/components/script/dom/writablestreamdefaultcontroller.rs
@@ -14,11 +14,11 @@ use js::rust::{HandleObject as SafeHandleObject, HandleValue as SafeHandleValue,
use super::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategySize;
use crate::dom::bindings::callback::ExceptionHandling;
use crate::dom::bindings::codegen::Bindings::UnderlyingSinkBinding::{
- UnderlyingSink, UnderlyingSinkAbortCallback, UnderlyingSinkCloseCallback,
- UnderlyingSinkStartCallback, UnderlyingSinkWriteCallback,
+ UnderlyingSinkAbortCallback, UnderlyingSinkCloseCallback, UnderlyingSinkStartCallback,
+ UnderlyingSinkWriteCallback,
};
use crate::dom::bindings::codegen::Bindings::WritableStreamDefaultControllerBinding::WritableStreamDefaultControllerMethods;
-use crate::dom::bindings::error::{Error, ErrorToJsval};
+use crate::dom::bindings::error::{Error, ErrorToJsval, Fallible};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::globalscope::GlobalScope;
@@ -268,15 +268,46 @@ impl Callback for WriteAlgorithmRejectionHandler {
/// The type of sink algorithms we are using.
#[derive(JSTraceable, PartialEq)]
+#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
pub enum UnderlyingSinkType {
/// Algorithms are provided by Js callbacks.
- Js,
+ Js {
+ /// <https://streams.spec.whatwg.org/#writablestreamdefaultcontroller-abortalgorithm>
+ abort: RefCell<Option<Rc<UnderlyingSinkAbortCallback>>>,
+
+ start: RefCell<Option<Rc<UnderlyingSinkStartCallback>>>,
+
+ /// <https://streams.spec.whatwg.org/#writablestreamdefaultcontroller-closealgorithm>
+ close: RefCell<Option<Rc<UnderlyingSinkCloseCallback>>>,
+
+ /// <https://streams.spec.whatwg.org/#writablestreamdefaultcontroller-writealgorithm>
+ write: RefCell<Option<Rc<UnderlyingSinkWriteCallback>>>,
+ },
/// Algorithms supporting streams transfer are implemented in Rust.
/// The promise and port used in those algorithms are stored here.
Transfer {
backpressure_promise: Rc<RefCell<Option<Rc<Promise>>>>,
port: Dom<MessagePort>,
},
+ /// Algorithms supporting transform streams are implemented in Rust.
+ #[allow(unused)]
+ Transform(/*Dom<TransformStream>, Rc<Promise>*/),
+}
+
+impl UnderlyingSinkType {
+ pub(crate) fn new_js(
+ abort: Option<Rc<UnderlyingSinkAbortCallback>>,
+ start: Option<Rc<UnderlyingSinkStartCallback>>,
+ close: Option<Rc<UnderlyingSinkCloseCallback>>,
+ write: Option<Rc<UnderlyingSinkWriteCallback>>,
+ ) -> Self {
+ UnderlyingSinkType::Js {
+ abort: RefCell::new(abort),
+ start: RefCell::new(start),
+ close: RefCell::new(close),
+ write: RefCell::new(write),
+ }
+ }
}
/// <https://streams.spec.whatwg.org/#ws-default-controller-class>
@@ -284,21 +315,11 @@ pub enum UnderlyingSinkType {
pub struct WritableStreamDefaultController {
reflector_: Reflector,
- #[ignore_malloc_size_of = "Rc is hard"]
+ /// The type of underlying sink used. Besides the default JS one,
+ /// there will be others for stream transfer, and for transform stream.
+ #[ignore_malloc_size_of = "underlying_sink_type"]
underlying_sink_type: UnderlyingSinkType,
- /// <https://streams.spec.whatwg.org/#writablestreamdefaultcontroller-abortalgorithm>
- #[ignore_malloc_size_of = "Rc is hard"]
- abort: RefCell<Option<Rc<UnderlyingSinkAbortCallback>>>,
-
- /// <https://streams.spec.whatwg.org/#writablestreamdefaultcontroller-closealgorithm>
- #[ignore_malloc_size_of = "Rc is hard"]
- close: RefCell<Option<Rc<UnderlyingSinkCloseCallback>>>,
-
- /// <https://streams.spec.whatwg.org/#writablestreamdefaultcontroller-writealgorithm>
- #[ignore_malloc_size_of = "Rc is hard"]
- write: RefCell<Option<Rc<UnderlyingSinkWriteCallback>>>,
-
/// The JS object used as `this` when invoking sink algorithms.
#[ignore_malloc_size_of = "mozjs"]
underlying_sink_obj: Heap<*mut JSObject>,
@@ -325,7 +346,6 @@ impl WritableStreamDefaultController {
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
fn new_inherited(
underlying_sink_type: UnderlyingSinkType,
- underlying_sink: &UnderlyingSink,
strategy_hwm: f64,
strategy_size: Rc<QueuingStrategySize>,
) -> WritableStreamDefaultController {
@@ -334,9 +354,6 @@ impl WritableStreamDefaultController {
underlying_sink_type,
queue: Default::default(),
stream: Default::default(),
- abort: RefCell::new(underlying_sink.abort.clone()),
- close: RefCell::new(underlying_sink.close.clone()),
- write: RefCell::new(underlying_sink.write.clone()),
underlying_sink_obj: Default::default(),
strategy_hwm,
strategy_size: RefCell::new(Some(strategy_size)),
@@ -344,10 +361,10 @@ impl WritableStreamDefaultController {
}
}
+ #[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) fn new(
global: &GlobalScope,
underlying_sink_type: UnderlyingSinkType,
- underlying_sink: &UnderlyingSink,
strategy_hwm: f64,
strategy_size: Rc<QueuingStrategySize>,
can_gc: CanGc,
@@ -355,7 +372,6 @@ impl WritableStreamDefaultController {
reflect_dom_object(
Box::new(WritableStreamDefaultController::new_inherited(
underlying_sink_type,
- underlying_sink,
strategy_hwm,
strategy_size,
)),
@@ -375,27 +391,44 @@ impl WritableStreamDefaultController {
/// <https://streams.spec.whatwg.org/#writable-stream-default-controller-clear-algorithms>
fn clear_algorithms(&self) {
- // Set controller.[[writeAlgorithm]] to undefined.
- self.write.borrow_mut().take();
+ match &self.underlying_sink_type {
+ UnderlyingSinkType::Js {
+ abort,
+ start: _,
+ close,
+ write,
+ } => {
+ // Set controller.[[writeAlgorithm]] to undefined.
+ write.borrow_mut().take();
- // Set controller.[[closeAlgorithm]] to undefined.
- self.close.borrow_mut().take();
+ // Set controller.[[closeAlgorithm]] to undefined.
+ close.borrow_mut().take();
- // Set controller.[[abortAlgorithm]] to undefined.
- self.abort.borrow_mut().take();
+ // Set controller.[[abortAlgorithm]] to undefined.
+ abort.borrow_mut().take();
+ },
+ UnderlyingSinkType::Transfer {
+ backpressure_promise,
+ ..
+ } => {
+ backpressure_promise.borrow_mut().take();
+ },
+ UnderlyingSinkType::Transform() => {
+ return;
+ },
+ }
// Set controller.[[strategySizeAlgorithm]] to undefined.
self.strategy_size.borrow_mut().take();
}
- /// <https://streams.spec.whatwg.org/#set-up-writable-stream-default-controllerr>
+ /// <https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller>
#[allow(unsafe_code)]
pub(crate) fn setup(
&self,
cx: SafeJSContext,
global: &GlobalScope,
stream: &WritableStream,
- start: &Option<Rc<UnderlyingSinkStartCallback>>,
can_gc: CanGc,
) -> Result<(), Error> {
// Assert: stream implements WritableStream.
@@ -436,40 +469,7 @@ impl WritableStreamDefaultController {
// Let startResult be the result of performing startAlgorithm. (This may throw an exception.)
// Let startPromise be a promise resolved with startResult.
- let start_promise = if let Some(start) = start {
- rooted!(in(*cx) let mut result_object = ptr::null_mut::<JSObject>());
- rooted!(in(*cx) let mut result: JSVal);
- rooted!(in(*cx) let this_object = self.underlying_sink_obj.get());
- start.Call_(
- &this_object.handle(),
- self,
- result.handle_mut(),
- ExceptionHandling::Rethrow,
- can_gc,
- )?;
- let is_promise = unsafe {
- if result.is_object() {
- result_object.set(result.to_object());
- IsPromiseObject(result_object.handle().into_handle())
- } else {
- false
- }
- };
- if is_promise {
- let promise = Promise::new_with_js_promise(result_object.handle(), cx);
- promise
- } else {
- Promise::new_resolved(global, cx, result.get(), can_gc)
- }
- } else {
- // Note: we are either here because the Js algorithm is none,
- // or because we are suppporting a stream transfer as
- // part of #abstract-opdef-setupcrossrealmtransformwritable
- // and the logic is the same for both.
-
- // Let startAlgorithm be an algorithm that returns undefined.
- Promise::new_resolved(global, cx, (), can_gc)
- };
+ let start_promise = self.start_algorithm(cx, global, can_gc)?;
let rooted_default_controller = DomRoot::from_ref(self);
@@ -509,6 +509,64 @@ impl WritableStreamDefaultController {
self.advance_queue_if_needed(cx, global, can_gc);
}
+ #[allow(unsafe_code)]
+ fn start_algorithm(
+ &self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
+ can_gc: CanGc,
+ ) -> Fallible<Rc<Promise>> {
+ match &self.underlying_sink_type {
+ UnderlyingSinkType::Js {
+ start,
+ abort: _,
+ close: _,
+ write: _,
+ } => {
+ let algo = start.borrow().clone();
+ let start_promise = if let Some(start) = algo {
+ rooted!(in(*cx) let mut result_object = ptr::null_mut::<JSObject>());
+ rooted!(in(*cx) let mut result: JSVal);
+ rooted!(in(*cx) let this_object = self.underlying_sink_obj.get());
+ start.Call_(
+ &this_object.handle(),
+ self,
+ result.handle_mut(),
+ ExceptionHandling::Rethrow,
+ can_gc,
+ )?;
+ let is_promise = unsafe {
+ if result.is_object() {
+ result_object.set(result.to_object());
+ IsPromiseObject(result_object.handle().into_handle())
+ } else {
+ false
+ }
+ };
+ if is_promise {
+ let promise = Promise::new_with_js_promise(result_object.handle(), cx);
+ promise
+ } else {
+ Promise::new_resolved(global, cx, result.get(), can_gc)
+ }
+ } else {
+ // Let startAlgorithm be an algorithm that returns undefined.
+ Promise::new_resolved(global, cx, (), can_gc)
+ };
+
+ Ok(start_promise)
+ },
+ UnderlyingSinkType::Transfer { .. } => {
+ // Let startAlgorithm be an algorithm that returns undefined.
+ Ok(Promise::new_resolved(global, cx, (), can_gc))
+ },
+ UnderlyingSinkType::Transform() => {
+ // Let startAlgorithm be an algorithm that returns startPromise.
+ todo!()
+ },
+ }
+ }
+
/// <https://streams.spec.whatwg.org/#ref-for-abstract-opdef-writablestreamcontroller-abortsteps>
pub(crate) fn abort_steps(
&self,
@@ -517,10 +575,15 @@ impl WritableStreamDefaultController {
reason: SafeHandleValue,
can_gc: CanGc,
) -> Rc<Promise> {
- let result = match self.underlying_sink_type {
- UnderlyingSinkType::Js => {
+ let result = match &self.underlying_sink_type {
+ UnderlyingSinkType::Js {
+ abort,
+ start: _,
+ close: _,
+ write: _,
+ } => {
rooted!(in(*cx) let this_object = self.underlying_sink_obj.get());
- let algo = self.abort.borrow().clone();
+ let algo = abort.borrow().clone();
// Let result be the result of performing this.[[abortAlgorithm]], passing reason.
let result = if let Some(algo) = algo {
algo.Call_(
@@ -538,7 +601,7 @@ impl WritableStreamDefaultController {
promise
})
},
- UnderlyingSinkType::Transfer { ref port, .. } => {
+ UnderlyingSinkType::Transfer { port, .. } => {
// The steps from the `abortAlgorithm` at
// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable>
@@ -559,6 +622,10 @@ impl WritableStreamDefaultController {
}
promise
},
+ UnderlyingSinkType::Transform() => {
+ // Return ! TransformStreamDefaultSinkAbortAlgorithm(stream, reason).
+ todo!()
+ },
};
// Perform ! WritableStreamDefaultControllerClearAlgorithms(controller).
@@ -575,10 +642,15 @@ impl WritableStreamDefaultController {
global: &GlobalScope,
can_gc: CanGc,
) -> Rc<Promise> {
- match self.underlying_sink_type {
- UnderlyingSinkType::Js => {
+ match &self.underlying_sink_type {
+ UnderlyingSinkType::Js {
+ abort: _,
+ start: _,
+ close: _,
+ write,
+ } => {
rooted!(in(*cx) let this_object = self.underlying_sink_obj.get());
- let algo = self.write.borrow().clone();
+ let algo = write.borrow().clone();
let result = if let Some(algo) = algo {
algo.Call_(
&this_object.handle(),
@@ -597,9 +669,8 @@ impl WritableStreamDefaultController {
})
},
UnderlyingSinkType::Transfer {
- ref backpressure_promise,
- ref port,
- ..
+ backpressure_promise,
+ port,
} => {
// The steps from the `writeAlgorithm` at
// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable>
@@ -636,6 +707,10 @@ impl WritableStreamDefaultController {
.append_native_handler(&handler, comp, can_gc);
result_promise
},
+ UnderlyingSinkType::Transform() => {
+ // Return ! TransformStreamDefaultSinkWriteAlgorithm(stream, chunk).
+ todo!()
+ },
}
}
@@ -646,11 +721,16 @@ impl WritableStreamDefaultController {
global: &GlobalScope,
can_gc: CanGc,
) -> Rc<Promise> {
- match self.underlying_sink_type {
- UnderlyingSinkType::Js => {
+ match &self.underlying_sink_type {
+ UnderlyingSinkType::Js {
+ abort: _,
+ start: _,
+ close,
+ write: _,
+ } => {
rooted!(in(*cx) let mut this_object = ptr::null_mut::<JSObject>());
this_object.set(self.underlying_sink_obj.get());
- let algo = self.close.borrow().clone();
+ let algo = close.borrow().clone();
let result = if let Some(algo) = algo {
algo.Call_(&this_object.handle(), ExceptionHandling::Rethrow, can_gc)
} else {
@@ -662,7 +742,7 @@ impl WritableStreamDefaultController {
promise
})
},
- UnderlyingSinkType::Transfer { ref port, .. } => {
+ UnderlyingSinkType::Transfer { port, .. } => {
// The steps from the `closeAlgorithm` at
// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable>
@@ -677,6 +757,10 @@ impl WritableStreamDefaultController {
// Return a promise resolved with undefined.
Promise::new_resolved(global, cx, (), can_gc)
},
+ UnderlyingSinkType::Transform() => {
+ // Return ! TransformStreamDefaultSinkCloseAlgorithm(stream).
+ todo!()
+ },
}
}
diff --git a/components/script/microtask.rs b/components/script/microtask.rs
index 57f558c1aac..453e587e892 100644
--- a/components/script/microtask.rs
+++ b/components/script/microtask.rs
@@ -147,7 +147,7 @@ impl MicrotaskQueue {
MutationObserver::notify_mutation_observers(can_gc);
},
Microtask::ReadableStreamTeeReadRequest(ref task) => {
- task.microtask_chunk_steps(can_gc)
+ task.microtask_chunk_steps(cx, can_gc)
},
}
}
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index f78b5bf281b..9c93bef22df 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -331,8 +331,7 @@ pub struct ScriptThread {
#[no_trace]
layout_factory: Arc<dyn LayoutFactory>,
- // Mouse down point.
- // In future, this shall be mouse_down_point for primary button
+ /// The screen coordinates where the primary mouse button was pressed.
#[no_trace]
relative_mouse_down_point: Cell<Point2D<f32, DevicePixel>>,
}
diff --git a/components/script_bindings/codegen/Bindings.conf b/components/script_bindings/codegen/Bindings.conf
index c457bf70b85..c50bc31a7f5 100644
--- a/components/script_bindings/codegen/Bindings.conf
+++ b/components/script_bindings/codegen/Bindings.conf
@@ -764,6 +764,10 @@ DOMInterfaces = {
'inRealms': ['Abort', 'Close', 'Write'],
},
+'TransformStreamDefaultController': {
+ 'canGc': ['Enqueue', 'Error', 'Terminate'],
+},
+
'WorkerNavigator': {
'canGc': ['Languages'],
},
diff --git a/components/shared/devtools/lib.rs b/components/shared/devtools/lib.rs
index 59857dc0d00..0cf99d22658 100644
--- a/components/shared/devtools/lib.rs
+++ b/components/shared/devtools/lib.rs
@@ -551,4 +551,5 @@ impl fmt::Display for ShadowRootMode {
pub struct SourceInfo {
pub url: ServoUrl,
pub external: bool,
+ pub worker_id: Option<WorkerId>,
}