blitz-pdf 0.1.2 → 0.1.4

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.
Files changed (64) hide show
  1. package/README.md +26 -0
  2. package/THIRD_PARTY_NOTICES.md +5 -2
  3. package/docs/layout-regressions.md +65 -0
  4. package/docs/pagination-regressions.md +89 -0
  5. package/package.json +2 -1
  6. package/vendor/README.md +35 -0
  7. package/vendor/blitz-dom/Cargo.toml +113 -0
  8. package/vendor/blitz-dom/LICENSE-APACHE +176 -0
  9. package/vendor/blitz-dom/LICENSE-MIT +23 -0
  10. package/vendor/blitz-dom/assets/default.css +1142 -0
  11. package/vendor/blitz-dom/assets/moz-bullet-font.otf +0 -0
  12. package/vendor/blitz-dom/src/accessibility.rs +255 -0
  13. package/vendor/blitz-dom/src/config.rs +97 -0
  14. package/vendor/blitz-dom/src/cssom.rs +971 -0
  15. package/vendor/blitz-dom/src/debug.rs +206 -0
  16. package/vendor/blitz-dom/src/document.rs +3395 -0
  17. package/vendor/blitz-dom/src/events/driver.rs +427 -0
  18. package/vendor/blitz-dom/src/events/focus.rs +40 -0
  19. package/vendor/blitz-dom/src/events/ime.rs +31 -0
  20. package/vendor/blitz-dom/src/events/keyboard.rs +174 -0
  21. package/vendor/blitz-dom/src/events/mod.rs +315 -0
  22. package/vendor/blitz-dom/src/events/pointer.rs +849 -0
  23. package/vendor/blitz-dom/src/font_metrics.rs +177 -0
  24. package/vendor/blitz-dom/src/form.rs +463 -0
  25. package/vendor/blitz-dom/src/html.rs +38 -0
  26. package/vendor/blitz-dom/src/iframe.rs +186 -0
  27. package/vendor/blitz-dom/src/layout/construct.rs +1328 -0
  28. package/vendor/blitz-dom/src/layout/damage.rs +642 -0
  29. package/vendor/blitz-dom/src/layout/inline.rs +1160 -0
  30. package/vendor/blitz-dom/src/layout/list.rs +237 -0
  31. package/vendor/blitz-dom/src/layout/mod.rs +969 -0
  32. package/vendor/blitz-dom/src/layout/paint_tree.rs +462 -0
  33. package/vendor/blitz-dom/src/layout/replaced.rs +439 -0
  34. package/vendor/blitz-dom/src/layout/table.rs +967 -0
  35. package/vendor/blitz-dom/src/layout/text_transform.rs +979 -0
  36. package/vendor/blitz-dom/src/layout/writing_mode.rs +437 -0
  37. package/vendor/blitz-dom/src/lib.rs +160 -0
  38. package/vendor/blitz-dom/src/mutator.rs +1818 -0
  39. package/vendor/blitz-dom/src/net.rs +649 -0
  40. package/vendor/blitz-dom/src/node/attributes.rs +60 -0
  41. package/vendor/blitz-dom/src/node/custom_widget.rs +178 -0
  42. package/vendor/blitz-dom/src/node/element.rs +1106 -0
  43. package/vendor/blitz-dom/src/node/mod.rs +30 -0
  44. package/vendor/blitz-dom/src/node/node.rs +2036 -0
  45. package/vendor/blitz-dom/src/node/scrollbar.rs +237 -0
  46. package/vendor/blitz-dom/src/node/serialize.rs +225 -0
  47. package/vendor/blitz-dom/src/node/stylo_data.rs +142 -0
  48. package/vendor/blitz-dom/src/node/svg.rs +265 -0
  49. package/vendor/blitz-dom/src/node/text.rs +804 -0
  50. package/vendor/blitz-dom/src/query_selector.rs +275 -0
  51. package/vendor/blitz-dom/src/resolve.rs +474 -0
  52. package/vendor/blitz-dom/src/resolved_style.rs +797 -0
  53. package/vendor/blitz-dom/src/scrolling.rs +780 -0
  54. package/vendor/blitz-dom/src/selection.rs +122 -0
  55. package/vendor/blitz-dom/src/stylo.rs +1518 -0
  56. package/vendor/blitz-dom/src/stylo_device.rs +88 -0
  57. package/vendor/blitz-dom/src/stylo_to_cursor_icon.rs +49 -0
  58. package/vendor/blitz-dom/src/stylo_to_kurbo.rs +129 -0
  59. package/vendor/blitz-dom/src/stylo_to_parley.rs +655 -0
  60. package/vendor/blitz-dom/src/traversal.rs +600 -0
  61. package/vendor/blitz-dom/src/tree.rs +136 -0
  62. package/vendor/blitz-dom/src/url.rs +63 -0
  63. package/vendor/blitz-dom/src/util.rs +256 -0
  64. package/wasm/blitz_pdf_bg.wasm +0 -0
@@ -0,0 +1,186 @@
1
+ //! Loading of `<iframe>` elements into sub-documents.
2
+
3
+ use std::sync::Arc;
4
+ use std::sync::mpsc::Sender;
5
+
6
+ use blitz_traits::navigation::{NavigationOptions, NavigationProvider};
7
+ use blitz_traits::net::{AbortController, AbortSignal, Url};
8
+ use blitz_traits::node_id::NodeId;
9
+ use blitz_traits::shell::ShellProvider;
10
+
11
+ use crate::document::DocumentEvent;
12
+ use crate::net::{DocumentSrcHandler, ResourceHandler, stamped_request};
13
+ use crate::{BaseDocument, DocumentConfig, UserAgentStylesheet, local_name};
14
+
15
+ /// Maximum nesting depth of documents-within-documents. Iframes nested deeper
16
+ /// than this are not loaded (this guards against infinitely recursive
17
+ /// self-embedding pages).
18
+ pub(crate) const MAX_SUBDOCUMENT_DEPTH: usize = 10;
19
+
20
+ /// Per-iframe load state: the abort controller covering the current
21
+ /// "generation" of the iframe's content (the HTML fetch and, once loaded, the
22
+ /// sub-document's own sub-resource fetches), plus the id of the in-flight HTML
23
+ /// request (if any) so stale responses can be discarded.
24
+ pub(crate) struct IframeLoad {
25
+ pub(crate) request_id: Option<usize>,
26
+ pub(crate) abort_controller: AbortController,
27
+ }
28
+
29
+ /// A [`NavigationProvider`] given to iframe sub-documents. It routes
30
+ /// navigations (e.g. link clicks within the iframe) back to the parent
31
+ /// document, which reloads the iframe's sub-document with the new URL.
32
+ struct IframeNavigationProvider {
33
+ node_id: NodeId,
34
+ tx: Sender<DocumentEvent>,
35
+ shell_provider: Arc<dyn ShellProvider>,
36
+ }
37
+
38
+ impl NavigationProvider for IframeNavigationProvider {
39
+ fn navigate_to(&self, options: NavigationOptions) {
40
+ let _ = self.tx.send(DocumentEvent::NavigateIframe {
41
+ node_id: self.node_id,
42
+ url: options.url,
43
+ });
44
+ self.shell_provider.request_redraw();
45
+ }
46
+ }
47
+
48
+ impl BaseDocument {
49
+ /// Build the [`DocumentConfig`] for an iframe sub-document, inheriting
50
+ /// providers and settings from this (parent) document.
51
+ fn iframe_document_config(
52
+ &self,
53
+ node_id: NodeId,
54
+ base_url: Option<String>,
55
+ abort_signal: AbortSignal,
56
+ ) -> DocumentConfig {
57
+ DocumentConfig {
58
+ viewport: None,
59
+ layout_viewport: None,
60
+ round_layout: self.round_layout,
61
+ base_url,
62
+ ua_stylesheets: vec![UserAgentStylesheet::default_sheet()],
63
+ net_provider: Some(self.net_provider.clone()),
64
+ navigation_provider: Some(Arc::new(IframeNavigationProvider {
65
+ node_id,
66
+ tx: self.tx.clone(),
67
+ shell_provider: self.shell_provider.clone(),
68
+ })),
69
+ shell_provider: Some(self.shell_provider.clone()),
70
+ html_parser_provider: Some(self.html_parser_provider.clone()),
71
+ font_ctx: Some(self.font_ctx.lock().unwrap().clone()),
72
+ media_type: Some(self.media_type.clone()),
73
+ style_threading: self.style_threading,
74
+ incremental: Some(self.incremental_layout),
75
+ abort_signal: Some(abort_signal),
76
+ subdocument_depth: self.subdocument_depth + 1,
77
+ }
78
+ }
79
+
80
+ /// Abort the current generation of the iframe's content (in-flight HTML
81
+ /// fetch and/or the loaded sub-document's sub-resource fetches) and start
82
+ /// a new generation.
83
+ fn new_iframe_generation(&mut self, node_id: NodeId, request_id: Option<usize>) -> AbortSignal {
84
+ if let Some(load) = self.iframe_loads.remove(&node_id) {
85
+ load.abort_controller.abort();
86
+ }
87
+ let abort_controller = AbortController::default();
88
+ let signal = abort_controller.signal.clone();
89
+ self.iframe_loads.insert(
90
+ node_id,
91
+ IframeLoad {
92
+ request_id,
93
+ abort_controller,
94
+ },
95
+ );
96
+ signal
97
+ }
98
+
99
+ /// Parse `html` and attach the resulting document as `node_id`'s
100
+ /// sub-document.
101
+ pub(crate) fn attach_iframe_document(
102
+ &mut self,
103
+ node_id: NodeId,
104
+ html: &str,
105
+ base_url: Option<String>,
106
+ abort_signal: AbortSignal,
107
+ ) {
108
+ let config = self.iframe_document_config(node_id, base_url, abort_signal);
109
+ let sub_doc = self
110
+ .html_parser_provider
111
+ .clone()
112
+ .parse_document(html, config);
113
+ self.set_sub_document(node_id, sub_doc);
114
+ self.shell_provider.request_redraw();
115
+ }
116
+
117
+ /// Attach a sub-document parsed from the iframe's `srcdoc` attribute.
118
+ /// Relative URLs within a `srcdoc` document resolve against the parent
119
+ /// document's base URL.
120
+ pub(crate) fn load_iframe_srcdoc(&mut self, node_id: NodeId, srcdoc: &str) {
121
+ let signal = self.new_iframe_generation(node_id, None);
122
+ let base_url = Some(self.url.to_string());
123
+ self.attach_iframe_document(node_id, srcdoc, base_url, signal);
124
+ }
125
+
126
+ /// Start fetching HTML for an iframe from `url`. The parsed document is
127
+ /// attached when the response arrives (via [`DocumentEvent::ResourceLoad`]).
128
+ pub(crate) fn start_iframe_load(&mut self, node_id: NodeId, url: Url) {
129
+ let handler = ResourceHandler::new(
130
+ self.tx.clone(),
131
+ self.id(),
132
+ Some(node_id),
133
+ self.shell_provider.clone(),
134
+ DocumentSrcHandler,
135
+ );
136
+ let signal = self.new_iframe_generation(node_id, Some(handler.request_id()));
137
+ self.net_provider.fetch(
138
+ self.id(),
139
+ stamped_request(url, Some(&signal)),
140
+ Box::new(handler),
141
+ );
142
+ }
143
+
144
+ /// Handle a navigation originating from within an iframe's sub-document
145
+ /// (e.g. a link click) by loading the new URL into the iframe.
146
+ pub(crate) fn navigate_iframe(&mut self, node_id: NodeId, url: Url) {
147
+ let Some(node) = self.get_node(node_id) else {
148
+ return;
149
+ };
150
+ if !node.flags.is_in_document() {
151
+ return;
152
+ }
153
+ self.start_iframe_load(node_id, url);
154
+ }
155
+
156
+ /// Apply fetched iframe HTML, discarding stale responses (the iframe may
157
+ /// have been removed or re-navigated since the request was issued).
158
+ pub(crate) fn apply_iframe_html(
159
+ &mut self,
160
+ node_id: NodeId,
161
+ request_id: usize,
162
+ resolved_url: Option<String>,
163
+ html: &str,
164
+ ) {
165
+ let is_current = self
166
+ .iframe_loads
167
+ .get(&node_id)
168
+ .is_some_and(|load| load.request_id == Some(request_id));
169
+ if !is_current {
170
+ return;
171
+ }
172
+ let load = self.iframe_loads.get_mut(&node_id).unwrap();
173
+ load.request_id = None;
174
+ let signal = load.abort_controller.signal.clone();
175
+
176
+ let node_is_iframe = self
177
+ .get_node(node_id)
178
+ .and_then(|node| node.element_data())
179
+ .is_some_and(|el| el.name.local == local_name!("iframe"));
180
+ if !node_is_iframe {
181
+ return;
182
+ }
183
+
184
+ self.attach_iframe_document(node_id, html, resolved_url, signal);
185
+ }
186
+ }