blitz-pdf 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -5
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/dist/api.d.ts +2 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +31 -6
- package/dist/api.js.map +1 -1
- package/dist/browser.d.ts +2 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +1 -1
- package/dist/browser.js.map +1 -1
- package/dist/node.d.ts +2 -1
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +6 -2
- package/dist/node.js.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/worker-host.d.ts +10 -5
- package/dist/worker-host.d.ts.map +1 -1
- package/dist/worker-host.js +149 -28
- package/dist/worker-host.js.map +1 -1
- package/dist/worker-protocol.d.ts +5 -0
- package/dist/worker-protocol.d.ts.map +1 -1
- package/dist/worker-runtime.d.ts +1 -1
- package/dist/worker-runtime.d.ts.map +1 -1
- package/dist/worker-runtime.js +22 -6
- package/dist/worker-runtime.js.map +1 -1
- package/licenses/RUST_DEPENDENCIES.txt +34 -40
- package/package.json +1 -1
- package/src/api.ts +32 -7
- package/src/browser.ts +2 -1
- package/src/node.ts +7 -2
- package/src/types.ts +15 -0
- package/src/worker-host.ts +148 -29
- package/src/worker-protocol.ts +5 -5
- package/src/worker-runtime.ts +20 -6
- package/vendor/README.md +19 -5
- package/vendor/blitz-dom/Cargo.toml +6 -6
- package/vendor/blitz-dom/src/accessibility.rs +175 -2
- package/vendor/blitz-dom/src/config.rs +8 -10
- package/vendor/blitz-dom/src/document.rs +66 -3
- package/vendor/blitz-dom/src/events/keyboard.rs +2 -6
- package/vendor/blitz-dom/src/layout/construct.rs +5 -300
- package/vendor/blitz-dom/src/layout/inline/build.rs +309 -0
- package/vendor/blitz-dom/src/layout/{inline.rs → inline/layout.rs} +1 -1
- package/vendor/blitz-dom/src/layout/inline/mod.rs +4 -0
- package/vendor/blitz-dom/src/layout/mod.rs +3 -1
- package/vendor/blitz-dom/src/layout/replaced.rs +13 -4
- package/vendor/blitz-dom/src/mutator.rs +47 -0
- package/vendor/blitz-dom/src/node/element.rs +17 -0
- package/vendor/blitz-dom/src/node/mod.rs +1 -0
- package/vendor/blitz-dom/src/node/node.rs +46 -5
- package/vendor/blitz-dom/src/node/text.rs +11 -11
- package/vendor/blitz-dom/src/resolve.rs +2 -2
- package/vendor/blitz-dom/src/stylo.rs +30 -22
- package/vendor/blitz-dom/src/util.rs +1 -1
- package/vendor/blitz-paint/Cargo.toml +4 -4
- package/wasm/blitz_pdf_bg.wasm +2 -2
|
@@ -141,10 +141,8 @@ pub fn compute_replaced_layout(
|
|
|
141
141
|
// element get no preferred aspect ratio at all.
|
|
142
142
|
let is_usable_ratio = |ratio: &f32| ratio.is_finite() && *ratio > 0.0;
|
|
143
143
|
let intrinsic = context.intrinsic_sizes;
|
|
144
|
-
let
|
|
145
|
-
|
|
146
|
-
.filter(is_usable_ratio)
|
|
147
|
-
.or(intrinsic.ratio.filter(is_usable_ratio));
|
|
144
|
+
let style_aspect_ratio = style.aspect_ratio().filter(is_usable_ratio);
|
|
145
|
+
let aspect_ratio: Option<f32> = style_aspect_ratio.or(intrinsic.ratio.filter(is_usable_ratio));
|
|
148
146
|
|
|
149
147
|
// Concrete object size per the CSS default sizing algorithm with no
|
|
150
148
|
// specified size (https://drafts.csswg.org/css-images/#default-sizing):
|
|
@@ -200,6 +198,17 @@ pub fn compute_replaced_layout(
|
|
|
200
198
|
None => default_size,
|
|
201
199
|
},
|
|
202
200
|
};
|
|
201
|
+
// The `aspect-ratio` property coerces the natural height to match the natural width, in the
|
|
202
|
+
// box-sizing box (https://github.com/w3c/csswg-drafts/issues/11236).
|
|
203
|
+
let inherent_size = match style_aspect_ratio {
|
|
204
|
+
Some(ratio) => Size {
|
|
205
|
+
width: inherent_size.width,
|
|
206
|
+
height: ((inherent_size.width + box_sizing_adjustment.width) / ratio
|
|
207
|
+
- box_sizing_adjustment.height)
|
|
208
|
+
.max(0.0),
|
|
209
|
+
},
|
|
210
|
+
None => inherent_size,
|
|
211
|
+
};
|
|
203
212
|
|
|
204
213
|
// Percentages are only resolved against zero in the inline axis.
|
|
205
214
|
// See https://www.w3.org/TR/css-sizing-3/#replaced-percentage-min-contribution
|
|
@@ -15,6 +15,7 @@ use blitz_traits::shell::Viewport;
|
|
|
15
15
|
use style::Atom;
|
|
16
16
|
use style::invalidation::element::restyle_hints::RestyleHint;
|
|
17
17
|
use style::stylesheets::OriginSet;
|
|
18
|
+
use style_dom::ElementState;
|
|
18
19
|
use thin_vec::ThinVec;
|
|
19
20
|
|
|
20
21
|
macro_rules! tag_and_attr {
|
|
@@ -347,6 +348,10 @@ impl DocumentMutator<'_> {
|
|
|
347
348
|
element.id = Some(Atom::from(value))
|
|
348
349
|
}
|
|
349
350
|
|
|
351
|
+
if *attr == local_name!("class") {
|
|
352
|
+
element.classes = crate::node::parse_classes(value);
|
|
353
|
+
}
|
|
354
|
+
|
|
350
355
|
if *attr == local_name!("value") {
|
|
351
356
|
if let Some(input_data) = element.text_input_data_mut() {
|
|
352
357
|
// Update text input value
|
|
@@ -452,6 +457,10 @@ impl DocumentMutator<'_> {
|
|
|
452
457
|
element.id = None;
|
|
453
458
|
}
|
|
454
459
|
|
|
460
|
+
if name.local == local_name!("class") {
|
|
461
|
+
element.classes.clear();
|
|
462
|
+
}
|
|
463
|
+
|
|
455
464
|
// As in `set_attribute`: taking one of these away can make the element
|
|
456
465
|
// unfocusable again.
|
|
457
466
|
if name.local == local_name!("tabindex")
|
|
@@ -670,6 +679,12 @@ impl DocumentMutator<'_> {
|
|
|
670
679
|
) {
|
|
671
680
|
let new_parent_is_in_document = self.doc.nodes[parent_id].flags.is_in_document();
|
|
672
681
|
self.mutations_occurred |= new_parent_is_in_document && !child_ids.is_empty();
|
|
682
|
+
|
|
683
|
+
// If one of the moved subtrees contains the focused node, the ancestors it is
|
|
684
|
+
// detached from stop matching :focus-within. Record them while the parent links
|
|
685
|
+
// are still intact; the state is transferred after the children are reattached.
|
|
686
|
+
let mut focus_within_old_chain: Option<Vec<NodeId>> = None;
|
|
687
|
+
|
|
673
688
|
// Detach the children from their old parents *before* inserting them into
|
|
674
689
|
// the new parent (matching DOM `insertBefore` semantics). If a child is
|
|
675
690
|
// being moved within the same parent then detaching it after insertion
|
|
@@ -677,6 +692,11 @@ impl DocumentMutator<'_> {
|
|
|
677
692
|
// parent's child list, and anchor indices would be computed against a
|
|
678
693
|
// child list that still contains the moved nodes.
|
|
679
694
|
for child_id in child_ids.iter().copied() {
|
|
695
|
+
let child = &self.doc.nodes[child_id];
|
|
696
|
+
if child.contains_focus() {
|
|
697
|
+
focus_within_old_chain = Some(self.doc.maybe_node_element_ancestors(child.parent));
|
|
698
|
+
}
|
|
699
|
+
|
|
680
700
|
let child = &mut self.doc.nodes[child_id];
|
|
681
701
|
let child_was_in_doc = child.flags.is_in_document();
|
|
682
702
|
self.mutations_occurred |= child_was_in_doc;
|
|
@@ -732,6 +752,33 @@ impl DocumentMutator<'_> {
|
|
|
732
752
|
}
|
|
733
753
|
}
|
|
734
754
|
|
|
755
|
+
// Transfer :focus-within from the old ancestor chain to the new one (the
|
|
756
|
+
// shared prefix stays). If the subtree left the document, the focus was
|
|
757
|
+
// cleared with it and only the old chain needs the state removed.
|
|
758
|
+
if let Some(old_chain) = focus_within_old_chain {
|
|
759
|
+
let new_chain = match self.doc.focus_node_id {
|
|
760
|
+
Some(_) => self.doc.node_element_ancestors(parent_id),
|
|
761
|
+
None => Vec::new(),
|
|
762
|
+
};
|
|
763
|
+
let same_count = old_chain
|
|
764
|
+
.iter()
|
|
765
|
+
.zip(&new_chain)
|
|
766
|
+
.take_while(|(o, n)| o == n)
|
|
767
|
+
.count();
|
|
768
|
+
for &id in old_chain.iter().skip(same_count) {
|
|
769
|
+
self.doc
|
|
770
|
+
.snapshot_node_and(id, ElementState::FOCUS_WITHIN, |node| {
|
|
771
|
+
node.remove_focus_within()
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
for &id in new_chain.iter().skip(same_count) {
|
|
775
|
+
self.doc
|
|
776
|
+
.snapshot_node_and(id, ElementState::FOCUS_WITHIN, |node| {
|
|
777
|
+
node.add_focus_within()
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
735
782
|
self.maybe_record_node(parent_id);
|
|
736
783
|
}
|
|
737
784
|
|
|
@@ -42,6 +42,10 @@ macro_rules! local_names {
|
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
pub(crate) fn parse_classes(value: &str) -> ThinVec<Atom> {
|
|
46
|
+
value.split_ascii_whitespace().map(Atom::from).collect()
|
|
47
|
+
}
|
|
48
|
+
|
|
45
49
|
pub struct ElementData {
|
|
46
50
|
/// The elements tag name, namespace and prefix
|
|
47
51
|
pub name: QualName,
|
|
@@ -49,6 +53,12 @@ pub struct ElementData {
|
|
|
49
53
|
/// The elements id attribute parsed as an atom (if it has one)
|
|
50
54
|
pub id: Option<Atom>,
|
|
51
55
|
|
|
56
|
+
/// The elements class attribute split into atoms (empty if it has none).
|
|
57
|
+
/// Kept in sync with `attrs` by `ElementData::new` and the mutator's
|
|
58
|
+
/// `set_attribute`/`remove_attribute`, so selector matching can compare
|
|
59
|
+
/// atoms instead of re-splitting and re-interning the attribute string.
|
|
60
|
+
pub classes: ThinVec<Atom>,
|
|
61
|
+
|
|
52
62
|
/// The element's attributes
|
|
53
63
|
pub attrs: Attributes,
|
|
54
64
|
|
|
@@ -300,6 +310,7 @@ impl Clone for ElementData {
|
|
|
300
310
|
Self {
|
|
301
311
|
name: self.name.clone(),
|
|
302
312
|
id: self.id.clone(),
|
|
313
|
+
classes: self.classes.clone(),
|
|
303
314
|
attrs: self.attrs.clone(),
|
|
304
315
|
is_focussable: self.is_focussable,
|
|
305
316
|
style_attribute: self.style_attribute.clone(),
|
|
@@ -404,10 +415,16 @@ impl ElementData {
|
|
|
404
415
|
.find(|attr| &attr.name.local == "id")
|
|
405
416
|
.map(|attr| attr.value.as_ref())
|
|
406
417
|
.map(|value: &str| Atom::from(value));
|
|
418
|
+
let classes = attrs
|
|
419
|
+
.iter()
|
|
420
|
+
.find(|attr| &attr.name.local == "class")
|
|
421
|
+
.map(|attr| parse_classes(&attr.value))
|
|
422
|
+
.unwrap_or_default();
|
|
407
423
|
|
|
408
424
|
let mut data = ElementData {
|
|
409
425
|
name,
|
|
410
426
|
id: id_attr_atom,
|
|
427
|
+
classes,
|
|
411
428
|
attrs: Attributes::new(attrs),
|
|
412
429
|
is_focussable: false,
|
|
413
430
|
style_attribute: Default::default(),
|
|
@@ -17,6 +17,7 @@ pub use attributes::{Attribute, Attributes};
|
|
|
17
17
|
pub use custom_widget::{
|
|
18
18
|
ComputedStyles, CustomWidgetData, CustomWidgetStatus, ProxyRenderContext, Widget,
|
|
19
19
|
};
|
|
20
|
+
pub(crate) use element::parse_classes;
|
|
20
21
|
pub use element::{
|
|
21
22
|
CanvasData, DocumentData, ElementData, ImageData, ImageResourceData, LayoutData,
|
|
22
23
|
ListItemLayout, ListItemLayoutPosition, Marker, RasterImageData, SpecialElementData,
|
|
@@ -122,6 +122,10 @@ pub struct Node {
|
|
|
122
122
|
|
|
123
123
|
// Flags
|
|
124
124
|
pub flags: NodeFlags,
|
|
125
|
+
/// Whether the node's box is out of flow (absolutely positioned and not `display: none`).
|
|
126
|
+
/// Written by the style traversal from the node's new primary style, so that layout
|
|
127
|
+
/// passes can test it without reading styles.
|
|
128
|
+
pub out_of_flow: AtomicBool,
|
|
125
129
|
|
|
126
130
|
/// Node type (Element, TextNode, etc) specific data.
|
|
127
131
|
///
|
|
@@ -418,6 +422,7 @@ impl Node {
|
|
|
418
422
|
sc_contribution_cache: RefCell::new(ThinVec::new()),
|
|
419
423
|
|
|
420
424
|
flags: NodeFlags::empty(),
|
|
425
|
+
out_of_flow: AtomicBool::new(false),
|
|
421
426
|
data,
|
|
422
427
|
}
|
|
423
428
|
}
|
|
@@ -715,6 +720,29 @@ impl Node {
|
|
|
715
720
|
.is_some_and(|data| data.element_state.contains(ElementState::HOVER))
|
|
716
721
|
}
|
|
717
722
|
|
|
723
|
+
/// Mark this node as containing focus (`:focus-within`). Set on the
|
|
724
|
+
/// focused element and all of its DOM ancestors.
|
|
725
|
+
pub fn add_focus_within(&mut self) {
|
|
726
|
+
if let Some(data) = self.element_data_mut() {
|
|
727
|
+
data.element_state.insert(ElementState::FOCUS_WITHIN);
|
|
728
|
+
}
|
|
729
|
+
self.mark_ancestors_dirty();
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/// Mark this node as no longer containing focus (`:focus-within`).
|
|
733
|
+
pub fn remove_focus_within(&mut self) {
|
|
734
|
+
if let Some(data) = self.element_data_mut() {
|
|
735
|
+
data.element_state.remove(ElementState::FOCUS_WITHIN);
|
|
736
|
+
}
|
|
737
|
+
self.mark_ancestors_dirty();
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/// Whether this node is the focused node or one of its DOM ancestors (`:focus-within`).
|
|
741
|
+
pub fn contains_focus(&self) -> bool {
|
|
742
|
+
self.element_data()
|
|
743
|
+
.is_some_and(|data| data.element_state.contains(ElementState::FOCUS_WITHIN))
|
|
744
|
+
}
|
|
745
|
+
|
|
718
746
|
pub fn focus(&mut self, shell_provider: Arc<dyn ShellProvider>) {
|
|
719
747
|
if let Some(data) = self.element_data_mut() {
|
|
720
748
|
data.element_state
|
|
@@ -1222,11 +1250,15 @@ impl Node {
|
|
|
1222
1250
|
/// block's hoisted child list rather than from its parent (`display: none` boxes
|
|
1223
1251
|
/// generate no box and are never hoisted).
|
|
1224
1252
|
pub fn is_out_of_flow(&self) -> bool {
|
|
1225
|
-
self.primary_styles()
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1253
|
+
self.primary_styles()
|
|
1254
|
+
.is_some_and(|s| style_is_out_of_flow(&s))
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
/// Record in [`Node::out_of_flow`] whether `style` (the node's new primary style)
|
|
1258
|
+
/// makes its box out of flow.
|
|
1259
|
+
pub(crate) fn record_out_of_flow(&self, style: &ComputedValues) {
|
|
1260
|
+
self.out_of_flow
|
|
1261
|
+
.store(style_is_out_of_flow(style), Ordering::Relaxed);
|
|
1230
1262
|
}
|
|
1231
1263
|
|
|
1232
1264
|
pub fn text_content(&self) -> String {
|
|
@@ -1947,6 +1979,15 @@ impl std::fmt::Debug for Node {
|
|
|
1947
1979
|
}
|
|
1948
1980
|
}
|
|
1949
1981
|
|
|
1982
|
+
/// Whether a box with `style` is out of flow: Taffy positions it from its containing
|
|
1983
|
+
/// block's hoisted child list rather than from its parent (`display: none` boxes generate
|
|
1984
|
+
/// no box and are never hoisted).
|
|
1985
|
+
pub(crate) fn style_is_out_of_flow(style: &ComputedValues) -> bool {
|
|
1986
|
+
let box_style = style.get_box();
|
|
1987
|
+
matches!(box_style.position, Position::Absolute | Position::Fixed)
|
|
1988
|
+
&& !box_style.display.is_none()
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1950
1991
|
#[cfg(test)]
|
|
1951
1992
|
mod test {
|
|
1952
1993
|
use style_dom::ElementState;
|
|
@@ -3,7 +3,7 @@ use blitz_traits::{
|
|
|
3
3
|
node_id::NodeId,
|
|
4
4
|
shell::ShellProvider,
|
|
5
5
|
};
|
|
6
|
-
use keyboard_types::{Key, Modifiers};
|
|
6
|
+
use keyboard_types::{Key, Modifiers, NamedKey};
|
|
7
7
|
use parley::{ContentWidths, FontContext, LayoutContext};
|
|
8
8
|
|
|
9
9
|
use crate::util::ACTION_MOD;
|
|
@@ -249,7 +249,7 @@ impl TextInputData {
|
|
|
249
249
|
}
|
|
250
250
|
return Some(GeneratedTextInputEvent::Select);
|
|
251
251
|
}
|
|
252
|
-
Key::ArrowLeft => {
|
|
252
|
+
Key::Named(NamedKey::ArrowLeft) => {
|
|
253
253
|
if action_mod {
|
|
254
254
|
if shift {
|
|
255
255
|
driver.select_word_left()
|
|
@@ -263,7 +263,7 @@ impl TextInputData {
|
|
|
263
263
|
}
|
|
264
264
|
return Some(GeneratedTextInputEvent::Select);
|
|
265
265
|
}
|
|
266
|
-
Key::ArrowRight => {
|
|
266
|
+
Key::Named(NamedKey::ArrowRight) => {
|
|
267
267
|
if action_mod {
|
|
268
268
|
if shift {
|
|
269
269
|
driver.select_word_right()
|
|
@@ -277,7 +277,7 @@ impl TextInputData {
|
|
|
277
277
|
}
|
|
278
278
|
return Some(GeneratedTextInputEvent::Select);
|
|
279
279
|
}
|
|
280
|
-
Key::ArrowUp => {
|
|
280
|
+
Key::Named(NamedKey::ArrowUp) => {
|
|
281
281
|
if shift {
|
|
282
282
|
driver.select_up()
|
|
283
283
|
} else {
|
|
@@ -285,7 +285,7 @@ impl TextInputData {
|
|
|
285
285
|
}
|
|
286
286
|
return Some(GeneratedTextInputEvent::Select);
|
|
287
287
|
}
|
|
288
|
-
Key::ArrowDown => {
|
|
288
|
+
Key::Named(NamedKey::ArrowDown) => {
|
|
289
289
|
if shift {
|
|
290
290
|
driver.select_down()
|
|
291
291
|
} else {
|
|
@@ -293,7 +293,7 @@ impl TextInputData {
|
|
|
293
293
|
}
|
|
294
294
|
return Some(GeneratedTextInputEvent::Select);
|
|
295
295
|
}
|
|
296
|
-
Key::Home => {
|
|
296
|
+
Key::Named(NamedKey::Home) => {
|
|
297
297
|
if action_mod {
|
|
298
298
|
if shift {
|
|
299
299
|
driver.select_to_text_start()
|
|
@@ -307,7 +307,7 @@ impl TextInputData {
|
|
|
307
307
|
}
|
|
308
308
|
return Some(GeneratedTextInputEvent::Select);
|
|
309
309
|
}
|
|
310
|
-
Key::End => {
|
|
310
|
+
Key::Named(NamedKey::End) => {
|
|
311
311
|
if action_mod {
|
|
312
312
|
if shift {
|
|
313
313
|
driver.select_to_text_end()
|
|
@@ -321,7 +321,7 @@ impl TextInputData {
|
|
|
321
321
|
}
|
|
322
322
|
return Some(GeneratedTextInputEvent::Select);
|
|
323
323
|
}
|
|
324
|
-
Key::Delete => {
|
|
324
|
+
Key::Named(NamedKey::Delete) => {
|
|
325
325
|
if action_mod {
|
|
326
326
|
driver.delete_word()
|
|
327
327
|
} else {
|
|
@@ -332,7 +332,7 @@ impl TextInputData {
|
|
|
332
332
|
|
|
333
333
|
// On macOS this is handled by the apple standard keybindings
|
|
334
334
|
#[cfg(not(target_os = "macos"))]
|
|
335
|
-
Key::Backspace => {
|
|
335
|
+
Key::Named(NamedKey::Backspace) => {
|
|
336
336
|
if action_mod {
|
|
337
337
|
driver.backdelete_word()
|
|
338
338
|
} else {
|
|
@@ -349,7 +349,7 @@ impl TextInputData {
|
|
|
349
349
|
return Some(GeneratedTextInputEvent::Submit);
|
|
350
350
|
}
|
|
351
351
|
}
|
|
352
|
-
Key::Enter => {
|
|
352
|
+
Key::Named(NamedKey::Enter) => {
|
|
353
353
|
if is_multiline {
|
|
354
354
|
driver.insert_or_replace_selection("\n");
|
|
355
355
|
return Some(GeneratedTextInputEvent::Input);
|
|
@@ -358,7 +358,7 @@ impl TextInputData {
|
|
|
358
358
|
}
|
|
359
359
|
}
|
|
360
360
|
Key::Character(s)
|
|
361
|
-
if !mods.contains(Modifiers::CONTROL) && !mods.contains(Modifiers::
|
|
361
|
+
if !mods.contains(Modifiers::CONTROL) && !mods.contains(Modifiers::META) =>
|
|
362
362
|
{
|
|
363
363
|
driver.insert_or_replace_selection(&s);
|
|
364
364
|
return Some(GeneratedTextInputEvent::Input);
|
|
@@ -26,10 +26,10 @@ use crate::{
|
|
|
26
26
|
LayoutPassState,
|
|
27
27
|
construct::{
|
|
28
28
|
ConstructionTask, ConstructionTaskData, ConstructionTaskResult,
|
|
29
|
-
ConstructionTaskResultData, LayoutChildren,
|
|
30
|
-
collect_layout_children,
|
|
29
|
+
ConstructionTaskResultData, LayoutChildren, collect_layout_children,
|
|
31
30
|
},
|
|
32
31
|
damage::{ALL_DAMAGE, CONSTRUCT_BOX, CONSTRUCT_DESCENDENT, CONSTRUCT_FC},
|
|
32
|
+
inline::build_inline_layout_into,
|
|
33
33
|
},
|
|
34
34
|
node::TextBrush,
|
|
35
35
|
};
|
|
@@ -26,6 +26,13 @@ use style::bloom::each_relevant_element_hash;
|
|
|
26
26
|
use style::color::AbsoluteColor;
|
|
27
27
|
use style::data::{ElementDataMut, ElementDataRef};
|
|
28
28
|
use style::global_style_data::STYLE_THREAD_POOL;
|
|
29
|
+
|
|
30
|
+
/// Serialises `Parallel` style traversals across documents. Stylo's per-worker
|
|
31
|
+
/// caches (`StyleSharingCache`, `StyleBloom`) live in native thread-locals that
|
|
32
|
+
/// a traversal borrows mutably for its whole duration, so two traversals in
|
|
33
|
+
/// flight on the shared pool at once panic with `already mutably borrowed`
|
|
34
|
+
/// (<https://github.com/DioxusLabs/blitz/issues/430>).
|
|
35
|
+
static PARALLEL_TRAVERSAL_LOCK: Mutex<()> = Mutex::new(());
|
|
29
36
|
use style::invalidation::element::restyle_hints::RestyleHint;
|
|
30
37
|
use style::properties::ComputedValues;
|
|
31
38
|
use style::properties::{Importance, PropertyDeclaration};
|
|
@@ -147,8 +154,13 @@ impl crate::document::BaseDocument {
|
|
|
147
154
|
// Style the elements, resolving their data
|
|
148
155
|
let mut traverser = RecalcStyle::new(context);
|
|
149
156
|
// `Sequential` bypasses Stylo's global pool. See `StyleThreading`.
|
|
150
|
-
let
|
|
151
|
-
|
|
157
|
+
let parallel = matches!(self.style_threading, StyleThreading::Parallel);
|
|
158
|
+
let _traversal_lock = parallel.then(|| {
|
|
159
|
+
PARALLEL_TRAVERSAL_LOCK
|
|
160
|
+
.lock()
|
|
161
|
+
.unwrap_or_else(|e| e.into_inner())
|
|
162
|
+
});
|
|
163
|
+
let pool_guard = parallel.then(|| STYLE_THREAD_POOL.pool());
|
|
152
164
|
let rayon_pool = pool_guard.as_ref().and_then(|g| g.as_ref());
|
|
153
165
|
style::driver::traverse_dom(&traverser, token, rayon_pool);
|
|
154
166
|
nodes_needing_style_image_flush =
|
|
@@ -474,7 +486,9 @@ impl selectors::Element for BlitzNode<'_> {
|
|
|
474
486
|
NonTSPseudoClass::Disabled => self.element_state().contains(ElementState::DISABLED),
|
|
475
487
|
NonTSPseudoClass::Enabled => self.element_state().contains(ElementState::ENABLED),
|
|
476
488
|
NonTSPseudoClass::Focus => self.element_state().contains(ElementState::FOCUS),
|
|
477
|
-
NonTSPseudoClass::FocusWithin =>
|
|
489
|
+
NonTSPseudoClass::FocusWithin => {
|
|
490
|
+
self.element_state().contains(ElementState::FOCUS_WITHIN)
|
|
491
|
+
}
|
|
478
492
|
NonTSPseudoClass::FocusVisible => false,
|
|
479
493
|
NonTSPseudoClass::Fullscreen => false,
|
|
480
494
|
NonTSPseudoClass::Hover => self.element_state().contains(ElementState::HOVER),
|
|
@@ -569,18 +583,11 @@ impl selectors::Element for BlitzNode<'_> {
|
|
|
569
583
|
search_name: &<Self::Impl as selectors::SelectorImpl>::Identifier,
|
|
570
584
|
case_sensitivity: selectors::attr::CaseSensitivity,
|
|
571
585
|
) -> bool {
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
if case_sensitivity.eq_atom(&atom, search_name) {
|
|
578
|
-
return true;
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
false
|
|
586
|
+
self.element_data().is_some_and(|data| {
|
|
587
|
+
data.classes
|
|
588
|
+
.iter()
|
|
589
|
+
.any(|class| case_sensitivity.eq_atom(class, search_name))
|
|
590
|
+
})
|
|
584
591
|
}
|
|
585
592
|
|
|
586
593
|
fn imported_part(
|
|
@@ -687,12 +694,9 @@ impl<'a> TElement for BlitzNode<'a> {
|
|
|
687
694
|
where
|
|
688
695
|
F: FnMut(&style::values::AtomIdent),
|
|
689
696
|
{
|
|
690
|
-
let
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
for pheme in class_attr.split_ascii_whitespace() {
|
|
694
|
-
let atom = Atom::from(pheme); // interns the string
|
|
695
|
-
callback(AtomIdent::cast(&atom));
|
|
697
|
+
if let Some(data) = self.element_data() {
|
|
698
|
+
for class in &data.classes {
|
|
699
|
+
callback(AtomIdent::cast(class));
|
|
696
700
|
}
|
|
697
701
|
}
|
|
698
702
|
}
|
|
@@ -703,7 +707,7 @@ impl<'a> TElement for BlitzNode<'a> {
|
|
|
703
707
|
{
|
|
704
708
|
if let Some(attrs) = self.data.attrs() {
|
|
705
709
|
for attr in attrs.iter() {
|
|
706
|
-
callback(
|
|
710
|
+
callback(GenericAtomIdent::cast(&attr.name.local));
|
|
707
711
|
}
|
|
708
712
|
}
|
|
709
713
|
}
|
|
@@ -1335,6 +1339,9 @@ impl<'dom> DomTraversal<BlitzNode<'dom>> for RecalcStyle<'_> {
|
|
|
1335
1339
|
recalc_style_at(self, context, el, &mut data, note_child);
|
|
1336
1340
|
|
|
1337
1341
|
sync_pseudo_element_styles(el, &data, &self.nodes_needing_style_image_flush);
|
|
1342
|
+
if let Some(style) = data.styles.get_primary() {
|
|
1343
|
+
el.record_out_of_flow(style);
|
|
1344
|
+
}
|
|
1338
1345
|
|
|
1339
1346
|
if !data.damage.is_empty() {
|
|
1340
1347
|
// Mark the ancestor chain so that the damage propagation pass
|
|
@@ -1449,6 +1456,7 @@ fn sync_pseudo_element_styles(
|
|
|
1449
1456
|
.push(pe_node_id);
|
|
1450
1457
|
}
|
|
1451
1458
|
}
|
|
1459
|
+
pe_node.record_out_of_flow(&pe_style);
|
|
1452
1460
|
pe_data.styles.primary = Some(pe_style);
|
|
1453
1461
|
pe_data.set_restyled();
|
|
1454
1462
|
}
|
|
@@ -5,7 +5,7 @@ use std::borrow::Cow;
|
|
|
5
5
|
use style::color::AbsoluteColor;
|
|
6
6
|
|
|
7
7
|
#[cfg(target_os = "macos")]
|
|
8
|
-
pub(crate) const ACTION_MOD: Modifiers = Modifiers::
|
|
8
|
+
pub(crate) const ACTION_MOD: Modifiers = Modifiers::META;
|
|
9
9
|
#[cfg(not(target_os = "macos"))]
|
|
10
10
|
pub(crate) const ACTION_MOD: Modifiers = Modifiers::CONTROL;
|
|
11
11
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name = "blitz-paint"
|
|
3
3
|
description = "Paint a Blitz Document using anyrender"
|
|
4
4
|
documentation = "https://docs.rs/blitz-paint"
|
|
5
|
-
version = "0.3.0-beta.
|
|
5
|
+
version = "0.3.0-beta.4"
|
|
6
6
|
license = "MIT OR Apache-2.0"
|
|
7
7
|
homepage = "https://github.com/dioxuslabs/blitz"
|
|
8
8
|
repository = "https://github.com/dioxuslabs/blitz"
|
|
@@ -26,15 +26,15 @@ scrollbars = ["blitz-dom/scrollbars"]
|
|
|
26
26
|
# Blitz dependencies
|
|
27
27
|
anyrender = { version = "0.14.0" }
|
|
28
28
|
anyrender_svg = { version = "0.15.0", optional = true }
|
|
29
|
-
blitz-traits = { version = "=0.3.0-beta.
|
|
30
|
-
blitz-dom = { version = "=0.3.0-beta.
|
|
29
|
+
blitz-traits = { version = "=0.3.0-beta.4", default-features = false, git = "https://github.com/DioxusLabs/blitz", rev = "f8723f495c7c6bfb611a923a21ed5c8e0fe95d55" }
|
|
30
|
+
blitz-dom = { version = "=0.3.0-beta.4", default-features = false, git = "https://github.com/DioxusLabs/blitz", rev = "f8723f495c7c6bfb611a923a21ed5c8e0fe95d55" }
|
|
31
31
|
|
|
32
32
|
# Servo dependencies
|
|
33
33
|
style = { version = "0.22.0", package = "stylo" }
|
|
34
34
|
euclid = { version = "0.22" }
|
|
35
35
|
|
|
36
36
|
# DioxusLabs dependencies
|
|
37
|
-
taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "
|
|
37
|
+
taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "4e59968eda29ceeb0bbaeb3993c8cd38baa3d0a6", default-features = false, features = ["std", "flexbox", "flexbox_balance", "grid", "block_layout", "content_size", "calc", "detailed_layout_info"] }
|
|
38
38
|
|
|
39
39
|
# Linebender + WGPU dependencies
|
|
40
40
|
parley = { version = "0.12", default-features = false, features = ["std", "bytemuck"] }
|
package/wasm/blitz_pdf_bg.wasm
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: package/wasm/blitz_pdf_bg.wasm
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 72552047 bytes
|
|
4
|
+
sha256: 50255e9cc08a644ef1e7d8935e4288a6fda60b15a508f601694063f449af6586
|