Skip to content

Commit 46f593b

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

File tree

14 files changed

+758
-58
lines changed

14 files changed

+758
-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: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,15 @@ use style::values::computed::Overflow;
4040
use style::{
4141
dom::{TDocument, TNode},
4242
media_queries::{Device, MediaList},
43+
parser::ParserContext,
4344
selector_parser::SnapshotMap,
4445
shared_lock::{SharedRwLock, StylesheetGuards},
45-
stylesheets::{AllowImportRules, DocumentStyleSheet, Origin, Stylesheet, UrlExtraData},
46+
stylesheets::{
47+
AllowImportRules, CssRuleType, DocumentStyleSheet, Origin, Stylesheet, UrlExtraData,
48+
},
4649
stylist::Stylist,
4750
};
51+
use style_traits::ParsingMode;
4852
use taffy::AvailableSpace;
4953
use url::Url;
5054

@@ -153,6 +157,9 @@ pub struct BaseDocument {
153157

154158
pub changed: HashSet<usize>,
155159

160+
// All image nodes.
161+
image_nodes: HashSet<usize>,
162+
156163
/// A map from control node ID's to their associated forms node ID's
157164
pub controls_to_form: HashMap<usize, usize>,
158165

@@ -246,6 +253,7 @@ impl BaseDocument {
246253
mousedown_node_id: None,
247254
is_animating: false,
248255
changed: HashSet::new(),
256+
image_nodes: HashSet::new(),
249257
controls_to_form: HashMap::new(),
250258
net_provider: Arc::new(DummyNetProvider),
251259
navigation_provider: Arc::new(DummyNavigationProvider),
@@ -414,6 +422,11 @@ impl BaseDocument {
414422

415423
// Mark the new node as changed.
416424
self.changed.insert(id);
425+
426+
if self.is_img_node(id) {
427+
self.image_nodes.insert(id);
428+
}
429+
417430
id
418431
}
419432

@@ -569,10 +582,13 @@ impl BaseDocument {
569582

570583
match kind {
571584
ImageType::Image => {
572-
node.element_data_mut().unwrap().special_data =
573-
SpecialElementData::Image(Box::new(ImageData::Raster(
574-
RasterImageData::new(width, height, image_data),
585+
if let SpecialElementData::Image(context) =
586+
&mut node.element_data_mut().unwrap().special_data
587+
{
588+
context.data = Some(ImageData::Raster(RasterImageData::new(
589+
width, height, image_data,
575590
)));
591+
}
576592

577593
// Clear layout cache
578594
node.cache.clear();
@@ -595,8 +611,11 @@ impl BaseDocument {
595611

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

601620
// Clear layout cache
602621
node.cache.clear();
@@ -892,6 +911,7 @@ impl BaseDocument {
892911
self.stylist.set_device(device, &guards)
893912
};
894913
self.stylist.force_stylesheet_origins_dirty(origins);
914+
self.environment_changes();
895915
}
896916

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

10971153
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)