Skip to content

Commit 136076c

Browse files
lizidevnicoburns
authored andcommitted
feat: Support for the picture element
1 parent bb50af6 commit 136076c

File tree

14 files changed

+759
-58
lines changed

14 files changed

+759
-58
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ style_traits = { version = "0.4", package = "stylo_traits" }
3131
style_config = { version = "0.4", package = "stylo_config" }
3232
style_dom = { version = "0.4", package = "stylo_dom" }
3333
selectors = { version = "0.29", package = "selectors" }
34+
cssparser = "0.35"
35+
mime = "0.3"
3436

3537
markup5ever = "0.16.2" # needs to match stylo web_atoms version
3638
html5ever = "0.32" # needs to match stylo web_atoms version

packages/blitz-dom/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ euclid = { workspace = true }
3939
atomic_refcell = { workspace = true }
4040
markup5ever = { workspace = true }
4141
smallvec = { workspace = true }
42+
cssparser = { workspace = true }
43+
mime = { workspace = true }
4244

4345
# DioxusLabs dependencies
4446
taffy = { workspace = true }

packages/blitz-dom/src/document.rs

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ use cursor_icon::CursorIcon;
1818
use markup5ever::local_name;
1919
use parley::FontContext;
2020
use peniko::{Blob, kurbo};
21+
use style_traits::ParsingMode;
22+
// use quadtree_rs::Quadtree;
2123
use selectors::{Element, matching::QuirksMode};
2224
use slab::Slab;
2325
use std::any::Any;
@@ -40,9 +42,12 @@ use style::values::computed::Overflow;
4042
use style::{
4143
dom::{TDocument, TNode},
4244
media_queries::{Device, MediaList},
45+
parser::ParserContext,
4346
selector_parser::SnapshotMap,
4447
shared_lock::{SharedRwLock, StylesheetGuards},
45-
stylesheets::{AllowImportRules, DocumentStyleSheet, Origin, Stylesheet, UrlExtraData},
48+
stylesheets::{
49+
AllowImportRules, CssRuleType, DocumentStyleSheet, Origin, Stylesheet, UrlExtraData,
50+
},
4651
stylist::Stylist,
4752
};
4853
use taffy::AvailableSpace;
@@ -153,6 +158,9 @@ pub struct BaseDocument {
153158

154159
pub changed: HashSet<usize>,
155160

161+
// All image nodes.
162+
image_nodes: HashSet<usize>,
163+
156164
/// A map from control node ID's to their associated forms node ID's
157165
pub controls_to_form: HashMap<usize, usize>,
158166

@@ -246,6 +254,7 @@ impl BaseDocument {
246254
mousedown_node_id: None,
247255
is_animating: false,
248256
changed: HashSet::new(),
257+
image_nodes: HashSet::new(),
249258
controls_to_form: HashMap::new(),
250259
net_provider: Arc::new(DummyNetProvider),
251260
navigation_provider: Arc::new(DummyNavigationProvider),
@@ -414,6 +423,11 @@ impl BaseDocument {
414423

415424
// Mark the new node as changed.
416425
self.changed.insert(id);
426+
427+
if self.is_img_node(id) {
428+
self.image_nodes.insert(id);
429+
}
430+
417431
id
418432
}
419433

@@ -569,10 +583,13 @@ impl BaseDocument {
569583

570584
match kind {
571585
ImageType::Image => {
572-
node.element_data_mut().unwrap().special_data =
573-
SpecialElementData::Image(Box::new(ImageData::Raster(
574-
RasterImageData::new(width, height, image_data),
586+
if let SpecialElementData::Image(context) =
587+
&mut node.element_data_mut().unwrap().special_data
588+
{
589+
context.data = Some(ImageData::Raster(RasterImageData::new(
590+
width, height, image_data,
575591
)));
592+
}
576593

577594
// Clear layout cache
578595
node.cache.clear();
@@ -595,8 +612,11 @@ impl BaseDocument {
595612

596613
match kind {
597614
ImageType::Image => {
598-
node.element_data_mut().unwrap().special_data =
599-
SpecialElementData::Image(Box::new(ImageData::Svg(tree)));
615+
if let SpecialElementData::Image(context) =
616+
&mut node.element_data_mut().unwrap().special_data
617+
{
618+
context.data = Some(ImageData::Svg(tree));
619+
}
600620

601621
// Clear layout cache
602622
node.cache.clear();
@@ -892,6 +912,7 @@ impl BaseDocument {
892912
self.stylist.set_device(device, &guards)
893913
};
894914
self.stylist.force_stylesheet_origins_dirty(origins);
915+
self.environment_changes();
895916
}
896917

897918
pub fn stylist_device(&mut self) -> &Device {
@@ -1092,6 +1113,42 @@ impl BaseDocument {
10921113
false
10931114
})
10941115
}
1116+
1117+
/// Used to determine whether a document matches a media query string,
1118+
/// and to monitor a document to detect when it matches (or stops matching) that media query.
1119+
///
1120+
/// https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia
1121+
pub fn match_media(&self, media_query_string: &str) -> bool {
1122+
let mut input = cssparser::ParserInput::new(media_query_string);
1123+
let mut parser = cssparser::Parser::new(&mut input);
1124+
1125+
let url_data = UrlExtraData::from(
1126+
self.base_url
1127+
.clone()
1128+
.unwrap_or_else(|| "about:blank".parse::<Url>().unwrap()),
1129+
);
1130+
let quirks_mode = self.stylist.quirks_mode();
1131+
let context = ParserContext::new(
1132+
Origin::Author,
1133+
&url_data,
1134+
Some(CssRuleType::Style),
1135+
ParsingMode::all(),
1136+
quirks_mode,
1137+
Default::default(),
1138+
None,
1139+
None,
1140+
);
1141+
1142+
let media_list = MediaList::parse(&context, &mut parser);
1143+
media_list.evaluate(self.stylist.device(), quirks_mode)
1144+
}
1145+
1146+
fn environment_changes(&mut self) {
1147+
let image_nodes = self.image_nodes.clone();
1148+
for node_id in image_nodes.into_iter() {
1149+
self.environment_changes_with_image(node_id);
1150+
}
1151+
}
10951152
}
10961153

10971154
impl AsRef<BaseDocument> for BaseDocument {

packages/blitz-dom/src/layout/construct.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ use style::{
2020
use crate::{
2121
BaseDocument, ElementData, Node, NodeData,
2222
node::{
23-
ListItemLayout, ListItemLayoutPosition, Marker, NodeFlags, NodeKind, SpecialElementData,
24-
TextBrush, TextInputData, TextLayout,
23+
ImageContext, ImageSource, ListItemLayout, ListItemLayoutPosition, Marker, NodeFlags,
24+
NodeKind, SpecialElementData, TextBrush, TextInputData, TextLayout,
2525
},
2626
stylo_to_parley,
2727
};
@@ -110,7 +110,11 @@ pub(crate) fn collect_layout_children(
110110
.unwrap()
111111
.element_data_mut()
112112
.unwrap()
113-
.special_data = SpecialElementData::Image(Box::new(svg.into()));
113+
.special_data =
114+
SpecialElementData::Image(Box::new(ImageContext::new_with_data(
115+
ImageSource::new("about:blank".to_string()),
116+
svg.into(),
117+
)));
114118
}
115119
Err(err) => {
116120
println!("{container_node_id} SVG parse failed");

packages/blitz-dom/src/layout/mod.rs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -227,23 +227,29 @@ impl LayoutPartialTree for BaseDocument {
227227
.and_then(|val| val.parse::<f32>().ok()),
228228
};
229229

230-
// Get image's native sizespecial_data
230+
// Get image's native size
231231
let inherent_size = match &element_data.special_data {
232-
SpecialElementData::Image(image_data) => match &**image_data {
233-
ImageData::Raster(image) => taffy::Size {
234-
width: image.width as f32,
235-
height: image.height as f32,
236-
},
237-
#[cfg(feature = "svg")]
238-
ImageData::Svg(svg) => {
239-
let size = svg.size();
240-
taffy::Size {
241-
width: size.width(),
242-
height: size.height(),
232+
SpecialElementData::Image(context) => {
233+
if let Some(image_data) = &context.data {
234+
match image_data {
235+
ImageData::Raster(image) => taffy::Size {
236+
width: image.width as f32,
237+
height: image.height as f32,
238+
},
239+
#[cfg(feature = "svg")]
240+
ImageData::Svg(svg) => {
241+
let size = svg.size();
242+
taffy::Size {
243+
width: size.width(),
244+
height: size.height(),
245+
}
246+
}
247+
ImageData::None => taffy::Size::ZERO,
243248
}
249+
} else {
250+
taffy::Size::ZERO
244251
}
245-
ImageData::None => taffy::Size::ZERO,
246-
},
252+
}
247253
SpecialElementData::Canvas(_) => taffy::Size::ZERO,
248254
SpecialElementData::None => taffy::Size::ZERO,
249255
_ => unreachable!(),

packages/blitz-dom/src/mutator.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,11 @@ impl DocumentMutator<'_> {
215215
element.flush_style_attribute(&self.doc.guard, self.doc.base_url.clone());
216216
} else if (tag, attr) == tag_and_attr!("input", "checked") {
217217
set_input_checked_state(element, value.to_string());
218-
} else if (tag, attr) == tag_and_attr!("img", "src") {
218+
} else if *tag == local_name!("img")
219+
&& (*attr == local_name!("src") || *attr == local_name!("srcset"))
220+
{
219221
self.load_image(node_id);
222+
// self.doc.load_image(node_id);
220223
} else if (tag, attr) == tag_and_attr!("canvas", "src") {
221224
self.load_custom_paint_src(node_id);
222225
}
@@ -582,6 +585,14 @@ impl<'doc> DocumentMutator<'doc> {
582585
}
583586
}
584587

588+
// fn maybe_load_image(&mut self, node_ids: &[usize]) {
589+
// for id in node_ids.iter() {
590+
// if self.doc.is_img_node(*id) {
591+
// self.doc.load_image(*id);
592+
// }
593+
// }
594+
// }
595+
585596
fn load_custom_paint_src(&mut self, target_id: usize) {
586597
let node = &mut self.doc.nodes[target_id];
587598
if let Some(raw_src) = node.attr(local_name!("src")) {

packages/blitz-dom/src/node/element.rs

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use blitz_traits::net::AbortController;
12
use color::{AlphaColor, Srgb};
23
use markup5ever::{LocalName, QualName, local_name};
34
use parley::{FontContext, LayoutContext};
@@ -14,7 +15,7 @@ use style::{
1415
};
1516
use url::Url;
1617

17-
use super::{Attribute, Attributes};
18+
use super::{Attribute, Attributes, ImageSource};
1819
use crate::layout::table::TableContext;
1920

2021
#[derive(Debug, Clone)]
@@ -73,7 +74,7 @@ pub enum SpecialElementType {
7374
pub enum SpecialElementData {
7475
Stylesheet(DocumentStyleSheet),
7576
/// An \<img\> element's image data
76-
Image(Box<ImageData>),
77+
Image(Box<ImageContext>),
7778
/// A \<canvas\> element's custom paint source
7879
Canvas(CanvasData),
7980
/// Pre-computed table layout data
@@ -137,15 +138,15 @@ impl ElementData {
137138
}
138139

139140
pub fn image_data(&self) -> Option<&ImageData> {
140-
match &self.special_data {
141-
SpecialElementData::Image(data) => Some(&**data),
141+
match self.special_data {
142+
SpecialElementData::Image(ref context) => context.data.as_ref(),
142143
_ => None,
143144
}
144145
}
145146

146147
pub fn image_data_mut(&mut self) -> Option<&mut ImageData> {
147148
match self.special_data {
148-
SpecialElementData::Image(ref mut data) => Some(&mut **data),
149+
SpecialElementData::Image(ref mut context) => context.data.as_mut(),
149150
_ => None,
150151
}
151152
}
@@ -302,6 +303,44 @@ impl From<usvg::Tree> for ImageData {
302303
}
303304
}
304305

306+
#[derive(Debug)]
307+
pub struct ImageContext {
308+
pub selected_source: ImageSource,
309+
pub data: Option<ImageData>,
310+
pub controller: Option<AbortController>,
311+
}
312+
313+
impl Clone for ImageContext {
314+
fn clone(&self) -> Self {
315+
Self {
316+
selected_source: self.selected_source.clone(),
317+
data: self.data.clone(),
318+
controller: None,
319+
}
320+
}
321+
}
322+
323+
impl ImageContext {
324+
pub(crate) fn new_with_controller(
325+
selected_source: ImageSource,
326+
controller: AbortController,
327+
) -> Self {
328+
Self {
329+
selected_source,
330+
data: None,
331+
controller: Some(controller),
332+
}
333+
}
334+
335+
pub fn new_with_data(selected_source: ImageSource, data: ImageData) -> Self {
336+
Self {
337+
selected_source,
338+
data: Some(data),
339+
controller: None,
340+
}
341+
}
342+
}
343+
305344
#[derive(Debug, Clone, PartialEq)]
306345
pub enum Status {
307346
Ok,
@@ -374,12 +413,18 @@ impl std::fmt::Debug for SpecialElementData {
374413
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
375414
match self {
376415
SpecialElementData::Stylesheet(_) => f.write_str("NodeSpecificData::Stylesheet"),
377-
SpecialElementData::Image(data) => match **data {
378-
ImageData::Raster(_) => f.write_str("NodeSpecificData::Image(Raster)"),
379-
#[cfg(feature = "svg")]
380-
ImageData::Svg(_) => f.write_str("NodeSpecificData::Image(Svg)"),
381-
ImageData::None => f.write_str("NodeSpecificData::Image(None)"),
382-
},
416+
SpecialElementData::Image(context) => {
417+
if let Some(image_data) = &context.data {
418+
match image_data {
419+
ImageData::Raster(_) => f.write_str("NodeSpecificData::Image(Raster)"),
420+
#[cfg(feature = "svg")]
421+
ImageData::Svg(_) => f.write_str("NodeSpecificData::Image(Svg)"),
422+
ImageData::None => f.write_str("NodeSpecificData::Image(None)"),
423+
}
424+
} else {
425+
f.write_str("NodeSpecificData::Image(None)")
426+
}
427+
}
383428
SpecialElementData::Canvas(_) => f.write_str("NodeSpecificData::Canvas"),
384429
SpecialElementData::TableRoot(_) => f.write_str("NodeSpecificData::TableRoot"),
385430
SpecialElementData::TextInput(_) => f.write_str("NodeSpecificData::TextInput"),

0 commit comments

Comments
 (0)