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.
- package/README.md +26 -0
- package/THIRD_PARTY_NOTICES.md +5 -2
- package/docs/layout-regressions.md +65 -0
- package/docs/pagination-regressions.md +89 -0
- package/package.json +2 -1
- package/vendor/README.md +35 -0
- package/vendor/blitz-dom/Cargo.toml +113 -0
- package/vendor/blitz-dom/LICENSE-APACHE +176 -0
- package/vendor/blitz-dom/LICENSE-MIT +23 -0
- package/vendor/blitz-dom/assets/default.css +1142 -0
- package/vendor/blitz-dom/assets/moz-bullet-font.otf +0 -0
- package/vendor/blitz-dom/src/accessibility.rs +255 -0
- package/vendor/blitz-dom/src/config.rs +97 -0
- package/vendor/blitz-dom/src/cssom.rs +971 -0
- package/vendor/blitz-dom/src/debug.rs +206 -0
- package/vendor/blitz-dom/src/document.rs +3395 -0
- package/vendor/blitz-dom/src/events/driver.rs +427 -0
- package/vendor/blitz-dom/src/events/focus.rs +40 -0
- package/vendor/blitz-dom/src/events/ime.rs +31 -0
- package/vendor/blitz-dom/src/events/keyboard.rs +174 -0
- package/vendor/blitz-dom/src/events/mod.rs +315 -0
- package/vendor/blitz-dom/src/events/pointer.rs +849 -0
- package/vendor/blitz-dom/src/font_metrics.rs +177 -0
- package/vendor/blitz-dom/src/form.rs +463 -0
- package/vendor/blitz-dom/src/html.rs +38 -0
- package/vendor/blitz-dom/src/iframe.rs +186 -0
- package/vendor/blitz-dom/src/layout/construct.rs +1328 -0
- package/vendor/blitz-dom/src/layout/damage.rs +642 -0
- package/vendor/blitz-dom/src/layout/inline.rs +1160 -0
- package/vendor/blitz-dom/src/layout/list.rs +237 -0
- package/vendor/blitz-dom/src/layout/mod.rs +969 -0
- package/vendor/blitz-dom/src/layout/paint_tree.rs +462 -0
- package/vendor/blitz-dom/src/layout/replaced.rs +439 -0
- package/vendor/blitz-dom/src/layout/table.rs +967 -0
- package/vendor/blitz-dom/src/layout/text_transform.rs +979 -0
- package/vendor/blitz-dom/src/layout/writing_mode.rs +437 -0
- package/vendor/blitz-dom/src/lib.rs +160 -0
- package/vendor/blitz-dom/src/mutator.rs +1818 -0
- package/vendor/blitz-dom/src/net.rs +649 -0
- package/vendor/blitz-dom/src/node/attributes.rs +60 -0
- package/vendor/blitz-dom/src/node/custom_widget.rs +178 -0
- package/vendor/blitz-dom/src/node/element.rs +1106 -0
- package/vendor/blitz-dom/src/node/mod.rs +30 -0
- package/vendor/blitz-dom/src/node/node.rs +2036 -0
- package/vendor/blitz-dom/src/node/scrollbar.rs +237 -0
- package/vendor/blitz-dom/src/node/serialize.rs +225 -0
- package/vendor/blitz-dom/src/node/stylo_data.rs +142 -0
- package/vendor/blitz-dom/src/node/svg.rs +265 -0
- package/vendor/blitz-dom/src/node/text.rs +804 -0
- package/vendor/blitz-dom/src/query_selector.rs +275 -0
- package/vendor/blitz-dom/src/resolve.rs +474 -0
- package/vendor/blitz-dom/src/resolved_style.rs +797 -0
- package/vendor/blitz-dom/src/scrolling.rs +780 -0
- package/vendor/blitz-dom/src/selection.rs +122 -0
- package/vendor/blitz-dom/src/stylo.rs +1518 -0
- package/vendor/blitz-dom/src/stylo_device.rs +88 -0
- package/vendor/blitz-dom/src/stylo_to_cursor_icon.rs +49 -0
- package/vendor/blitz-dom/src/stylo_to_kurbo.rs +129 -0
- package/vendor/blitz-dom/src/stylo_to_parley.rs +655 -0
- package/vendor/blitz-dom/src/traversal.rs +600 -0
- package/vendor/blitz-dom/src/tree.rs +136 -0
- package/vendor/blitz-dom/src/url.rs +63 -0
- package/vendor/blitz-dom/src/util.rs +256 -0
- package/wasm/blitz_pdf_bg.wasm +0 -0
|
@@ -0,0 +1,971 @@
|
|
|
1
|
+
//! CSSOM stylesheet access (`document.styleSheets`, `CSSStyleSheet`,
|
|
2
|
+
//! `CSSRuleList`, `CSSRule` and friends) backed by stylo's stylesheet objects.
|
|
3
|
+
//!
|
|
4
|
+
//! Stylesheets are identified by the `NodeId` of their owner node (`<style>` or
|
|
5
|
+
//! `<link>`), and rules by a *path* of indices from the stylesheet's top-level
|
|
6
|
+
//! rule list down through nested rule lists (`@media`, `@supports`, nested
|
|
7
|
+
//! style rules, `@keyframes`, ...). An empty path denotes the stylesheet's own
|
|
8
|
+
//! top-level rule list.
|
|
9
|
+
|
|
10
|
+
use cssparser::Parser;
|
|
11
|
+
use selectors::matching::QuirksMode;
|
|
12
|
+
use selectors::parser::{ParseRelative, SelectorList};
|
|
13
|
+
use style::font_face::FontFaceRule;
|
|
14
|
+
use style::invalidation::stylesheets::RuleChangeKind;
|
|
15
|
+
use style::parser::ParserContext;
|
|
16
|
+
use style::properties::font_face::DescriptorId as FontFaceDescriptorId;
|
|
17
|
+
use style::properties::{
|
|
18
|
+
Importance, PropertyDeclarationBlock, PropertyId, SourcePropertyDeclaration,
|
|
19
|
+
parse_one_declaration_into,
|
|
20
|
+
};
|
|
21
|
+
use style::selector_parser::SelectorParser;
|
|
22
|
+
use style::servo_arc::Arc as ServoArc;
|
|
23
|
+
use style::shared_lock::{Locked, SharedRwLockReadGuard, ToCssWithGuard};
|
|
24
|
+
use style::stylesheets::keyframes_rule::{Keyframe, KeyframesRule};
|
|
25
|
+
use style::stylesheets::{
|
|
26
|
+
AllowImportRules, CssRule, CssRuleRef, CssRuleType, CssRuleTypes, CssRules, DocumentStyleSheet,
|
|
27
|
+
Origin, OriginSet, RulesMutateError, StylesheetInDocument,
|
|
28
|
+
};
|
|
29
|
+
use style_traits::{CssStringWriter, ParsingMode, ToCss};
|
|
30
|
+
// For `SelectorList::to_css_string`
|
|
31
|
+
use cssparser::ToCss as _;
|
|
32
|
+
|
|
33
|
+
use blitz_traits::node_id::NodeId;
|
|
34
|
+
|
|
35
|
+
use crate::BaseDocument;
|
|
36
|
+
use crate::net::StylesheetLoader;
|
|
37
|
+
|
|
38
|
+
/// Errors from CSSOM mutation operations, mirroring the `DOMException` names
|
|
39
|
+
/// which the corresponding JavaScript APIs throw.
|
|
40
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
41
|
+
pub enum CssomError {
|
|
42
|
+
/// The owner node has no associated stylesheet, or the rule path does not
|
|
43
|
+
/// resolve to a rule (list).
|
|
44
|
+
NotFound,
|
|
45
|
+
/// `SyntaxError`
|
|
46
|
+
Syntax,
|
|
47
|
+
/// `IndexSizeError`
|
|
48
|
+
IndexSize,
|
|
49
|
+
/// `HierarchyRequestError`
|
|
50
|
+
HierarchyRequest,
|
|
51
|
+
/// `InvalidStateError`
|
|
52
|
+
InvalidState,
|
|
53
|
+
/// `NotSupportedError`
|
|
54
|
+
NotSupported,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
impl CssomError {
|
|
58
|
+
/// The `DOMException` name corresponding to this error
|
|
59
|
+
pub fn dom_exception_name(self) -> &'static str {
|
|
60
|
+
match self {
|
|
61
|
+
Self::NotFound => "NotFoundError",
|
|
62
|
+
Self::Syntax => "SyntaxError",
|
|
63
|
+
Self::IndexSize => "IndexSizeError",
|
|
64
|
+
Self::HierarchyRequest => "HierarchyRequestError",
|
|
65
|
+
Self::InvalidState => "InvalidStateError",
|
|
66
|
+
Self::NotSupported => "NotSupportedError",
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
impl From<RulesMutateError> for CssomError {
|
|
72
|
+
fn from(err: RulesMutateError) -> Self {
|
|
73
|
+
match err {
|
|
74
|
+
RulesMutateError::Syntax => Self::Syntax,
|
|
75
|
+
RulesMutateError::IndexSize => Self::IndexSize,
|
|
76
|
+
RulesMutateError::HierarchyRequest => Self::HierarchyRequest,
|
|
77
|
+
RulesMutateError::InvalidState => Self::InvalidState,
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/// A snapshot of the CSSOM-visible attributes of a rule
|
|
83
|
+
#[derive(Debug, Clone)]
|
|
84
|
+
pub struct CssRuleInfo {
|
|
85
|
+
/// The name of the CSSOM interface for the rule (e.g. `"CSSStyleRule"`)
|
|
86
|
+
pub interface: &'static str,
|
|
87
|
+
/// The legacy `CSSRule.type` constant (0 for rules without one)
|
|
88
|
+
pub rule_type: u16,
|
|
89
|
+
/// `CSSRule.cssText`
|
|
90
|
+
pub css_text: String,
|
|
91
|
+
/// Whether the rule has a child rule list (`cssRules`)
|
|
92
|
+
pub has_child_rules: bool,
|
|
93
|
+
/// Whether the rule has a `style` declaration block
|
|
94
|
+
pub has_style: bool,
|
|
95
|
+
/// Additional interface-specific string attributes (e.g. `selectorText`)
|
|
96
|
+
pub attributes: Vec<(&'static str, String)>,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// A rule list that CSSOM rule paths can index into
|
|
100
|
+
#[derive(Clone)]
|
|
101
|
+
enum RuleList {
|
|
102
|
+
Css(ServoArc<Locked<CssRules>>),
|
|
103
|
+
Keyframes(ServoArc<Locked<KeyframesRule>>),
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/// A rule resolved from a CSSOM rule path
|
|
107
|
+
#[derive(Clone)]
|
|
108
|
+
enum RuleHandle {
|
|
109
|
+
Rule(CssRule),
|
|
110
|
+
Keyframe(ServoArc<Locked<Keyframe>>),
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/// The declarations of a rule (`CSSRule.style`)
|
|
114
|
+
enum DeclarationTarget {
|
|
115
|
+
Block {
|
|
116
|
+
block: ServoArc<Locked<PropertyDeclarationBlock>>,
|
|
117
|
+
rule_type: CssRuleType,
|
|
118
|
+
},
|
|
119
|
+
FontFace(ServoArc<Locked<FontFaceRule>>),
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
fn child_rule_list(rule: &CssRule, guard: &SharedRwLockReadGuard) -> Option<RuleList> {
|
|
123
|
+
let rules = match rule {
|
|
124
|
+
CssRule::Style(r) => r.read_with(guard).rules.clone()?,
|
|
125
|
+
CssRule::Media(r) => r.rules.clone(),
|
|
126
|
+
CssRule::Supports(r) => r.rules.clone(),
|
|
127
|
+
CssRule::Container(r) => r.rules.clone(),
|
|
128
|
+
CssRule::Page(r) => r.read_with(guard).rules.clone(),
|
|
129
|
+
CssRule::Document(r) => r.rules.clone(),
|
|
130
|
+
CssRule::LayerBlock(r) => r.rules.clone(),
|
|
131
|
+
CssRule::Scope(r) => r.rules.clone(),
|
|
132
|
+
CssRule::StartingStyle(r) => r.rules.clone(),
|
|
133
|
+
CssRule::Keyframes(r) => return Some(RuleList::Keyframes(r.clone())),
|
|
134
|
+
_ => return None,
|
|
135
|
+
};
|
|
136
|
+
Some(RuleList::Css(rules))
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
fn declaration_target(
|
|
140
|
+
handle: &RuleHandle,
|
|
141
|
+
guard: &SharedRwLockReadGuard,
|
|
142
|
+
) -> Option<DeclarationTarget> {
|
|
143
|
+
let (block, rule_type) = match handle {
|
|
144
|
+
RuleHandle::Keyframe(k) => (k.read_with(guard).block.clone(), CssRuleType::Keyframe),
|
|
145
|
+
RuleHandle::Rule(rule) => match rule {
|
|
146
|
+
CssRule::Style(r) => (r.read_with(guard).block.clone(), CssRuleType::Style),
|
|
147
|
+
CssRule::Page(r) => (r.read_with(guard).block.clone(), CssRuleType::Page),
|
|
148
|
+
CssRule::Margin(r) => (r.block.clone(), CssRuleType::Margin),
|
|
149
|
+
CssRule::NestedDeclarations(r) => (
|
|
150
|
+
r.read_with(guard).block.clone(),
|
|
151
|
+
CssRuleType::NestedDeclarations,
|
|
152
|
+
),
|
|
153
|
+
CssRule::PositionTry(r) => (r.read_with(guard).block.clone(), CssRuleType::PositionTry),
|
|
154
|
+
CssRule::FontFace(r) => return Some(DeclarationTarget::FontFace(r.clone())),
|
|
155
|
+
_ => return None,
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
Some(DeclarationTarget::Block { block, rule_type })
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
fn css_rule_interface(rule: &CssRule) -> &'static str {
|
|
162
|
+
match rule {
|
|
163
|
+
CssRule::Style(_) => "CSSStyleRule",
|
|
164
|
+
CssRule::Namespace(_) => "CSSNamespaceRule",
|
|
165
|
+
CssRule::Import(_) => "CSSImportRule",
|
|
166
|
+
CssRule::Media(_) => "CSSMediaRule",
|
|
167
|
+
CssRule::CustomMedia(_) => "CSSCustomMediaRule",
|
|
168
|
+
CssRule::Container(_) => "CSSContainerRule",
|
|
169
|
+
CssRule::FontFace(_) => "CSSFontFaceRule",
|
|
170
|
+
CssRule::FontFeatureValues(_) => "CSSFontFeatureValuesRule",
|
|
171
|
+
CssRule::FontPaletteValues(_) => "CSSFontPaletteValuesRule",
|
|
172
|
+
CssRule::CounterStyle(_) => "CSSCounterStyleRule",
|
|
173
|
+
CssRule::Keyframes(_) => "CSSKeyframesRule",
|
|
174
|
+
CssRule::Margin(_) => "CSSMarginRule",
|
|
175
|
+
CssRule::Supports(_) => "CSSSupportsRule",
|
|
176
|
+
CssRule::Page(_) => "CSSPageRule",
|
|
177
|
+
CssRule::Property(_) => "CSSPropertyRule",
|
|
178
|
+
CssRule::Document(_) => "CSSMozDocumentRule",
|
|
179
|
+
CssRule::LayerBlock(_) => "CSSLayerBlockRule",
|
|
180
|
+
CssRule::LayerStatement(_) => "CSSLayerStatementRule",
|
|
181
|
+
CssRule::Scope(_) => "CSSScopeRule",
|
|
182
|
+
CssRule::StartingStyle(_) => "CSSStartingStyleRule",
|
|
183
|
+
CssRule::AppearanceBase(_) => "CSSAppearanceBaseRule",
|
|
184
|
+
CssRule::PositionTry(_) => "CSSPositionTryRule",
|
|
185
|
+
CssRule::NestedDeclarations(_) => "CSSNestedDeclarations",
|
|
186
|
+
CssRule::ViewTransition(_) => "CSSViewTransitionRule",
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/// Join the CSS serializations of `items` with `", "`
|
|
191
|
+
fn comma_separated<'a, T: ToCss + 'a>(items: impl IntoIterator<Item = &'a T>) -> String {
|
|
192
|
+
let mut css = CssStringWriter::new();
|
|
193
|
+
for (i, item) in items.into_iter().enumerate() {
|
|
194
|
+
if i > 0 {
|
|
195
|
+
css.push_str(", ");
|
|
196
|
+
}
|
|
197
|
+
let _ = item.to_css(&mut style_traits::CssWriter::new(&mut css));
|
|
198
|
+
}
|
|
199
|
+
css
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
fn rule_attributes(rule: &CssRule, guard: &SharedRwLockReadGuard) -> Vec<(&'static str, String)> {
|
|
203
|
+
match rule {
|
|
204
|
+
CssRule::Style(r) => {
|
|
205
|
+
vec![("selectorText", r.read_with(guard).selectors.to_css_string())]
|
|
206
|
+
}
|
|
207
|
+
CssRule::Namespace(r) => vec![
|
|
208
|
+
("namespaceURI", r.url.to_string()),
|
|
209
|
+
(
|
|
210
|
+
"prefix",
|
|
211
|
+
r.prefix.as_ref().map(|p| p.to_string()).unwrap_or_default(),
|
|
212
|
+
),
|
|
213
|
+
],
|
|
214
|
+
CssRule::Import(r) => {
|
|
215
|
+
let r = r.read_with(guard);
|
|
216
|
+
vec![
|
|
217
|
+
("href", r.url.as_str().to_string()),
|
|
218
|
+
(
|
|
219
|
+
"media",
|
|
220
|
+
r.stylesheet
|
|
221
|
+
.media(guard)
|
|
222
|
+
.map(|m| m.to_css_string())
|
|
223
|
+
.unwrap_or_default(),
|
|
224
|
+
),
|
|
225
|
+
]
|
|
226
|
+
}
|
|
227
|
+
CssRule::Media(r) => vec![(
|
|
228
|
+
"conditionText",
|
|
229
|
+
r.media_queries.read_with(guard).to_css_string(),
|
|
230
|
+
)],
|
|
231
|
+
CssRule::Supports(r) => vec![("conditionText", r.condition.to_css_string())],
|
|
232
|
+
CssRule::Container(r) => vec![("conditionText", r.conditions.to_css_string())],
|
|
233
|
+
CssRule::Page(r) => vec![("selectorText", r.read_with(guard).selectors.to_css_string())],
|
|
234
|
+
CssRule::Keyframes(r) => vec![("name", r.read_with(guard).name.to_css_string())],
|
|
235
|
+
CssRule::LayerBlock(r) => vec![(
|
|
236
|
+
"name",
|
|
237
|
+
r.name
|
|
238
|
+
.as_ref()
|
|
239
|
+
.map(|n| n.to_css_string())
|
|
240
|
+
.unwrap_or_default(),
|
|
241
|
+
)],
|
|
242
|
+
CssRule::LayerStatement(r) => vec![("nameList", comma_separated(r.names.iter()))],
|
|
243
|
+
CssRule::Property(r) => {
|
|
244
|
+
let mut attrs = vec![("name", r.name.to_css_string())];
|
|
245
|
+
for (attr, id) in [
|
|
246
|
+
("syntax", style::properties::property::DescriptorId::Syntax),
|
|
247
|
+
(
|
|
248
|
+
"inherits",
|
|
249
|
+
style::properties::property::DescriptorId::Inherits,
|
|
250
|
+
),
|
|
251
|
+
(
|
|
252
|
+
"initialValue",
|
|
253
|
+
style::properties::property::DescriptorId::InitialValue,
|
|
254
|
+
),
|
|
255
|
+
] {
|
|
256
|
+
let mut css = CssStringWriter::new();
|
|
257
|
+
let _ = r.descriptors.get(id, &mut css);
|
|
258
|
+
attrs.push((attr, css));
|
|
259
|
+
}
|
|
260
|
+
attrs
|
|
261
|
+
}
|
|
262
|
+
CssRule::CounterStyle(r) => vec![("name", r.read_with(guard).name().to_css_string())],
|
|
263
|
+
CssRule::FontFeatureValues(r) => {
|
|
264
|
+
vec![("fontFamily", comma_separated(r.family_names.iter()))]
|
|
265
|
+
}
|
|
266
|
+
CssRule::FontPaletteValues(r) => vec![
|
|
267
|
+
("name", r.name.to_css_string()),
|
|
268
|
+
("fontFamily", comma_separated(r.family_names.iter())),
|
|
269
|
+
(
|
|
270
|
+
"basePalette",
|
|
271
|
+
r.base_palette
|
|
272
|
+
.as_ref()
|
|
273
|
+
.map(|b| b.to_css_string())
|
|
274
|
+
.unwrap_or_default(),
|
|
275
|
+
),
|
|
276
|
+
("overrideColors", comma_separated(r.override_colors.iter())),
|
|
277
|
+
],
|
|
278
|
+
CssRule::Margin(r) => vec![("name", format!("{:?}", r.rule_type))],
|
|
279
|
+
CssRule::PositionTry(r) => vec![("name", r.read_with(guard).name.to_css_string())],
|
|
280
|
+
_ => Vec::new(),
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
impl BaseDocument {
|
|
285
|
+
/// The owner nodes (`<style>` / `<link>` elements) of the document's author
|
|
286
|
+
/// stylesheets (`document.styleSheets`).
|
|
287
|
+
///
|
|
288
|
+
/// Ordered by node id, which matches document order for stylesheets
|
|
289
|
+
/// created while parsing (and is the order the stylist uses too, see
|
|
290
|
+
/// `add_stylesheet_for_node`).
|
|
291
|
+
pub fn stylesheet_owner_nodes(&self) -> impl Iterator<Item = NodeId> + '_ {
|
|
292
|
+
self.nodes_to_stylesheet.keys().copied()
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/// Whether `node_id` currently owns a stylesheet
|
|
296
|
+
pub fn node_has_stylesheet(&self, node_id: NodeId) -> bool {
|
|
297
|
+
self.nodes_to_stylesheet.contains_key(&node_id)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/// A counter which changes whenever the set of stylesheets (or the
|
|
301
|
+
/// stylesheet object owned by some node) changes. CSSOM wrappers use it to
|
|
302
|
+
/// detect that rule data cached on the script side is stale.
|
|
303
|
+
pub fn stylesheet_generation(&self) -> u64 {
|
|
304
|
+
self.stylesheet_generation
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/// Whether the stylesheet owned by `node_id` has been disabled through the
|
|
308
|
+
/// CSSOM (`CSSStyleSheet.disabled` / `HTMLStyleElement.disabled` /
|
|
309
|
+
/// `HTMLLinkElement.disabled`). `false` if the node owns no stylesheet.
|
|
310
|
+
pub fn stylesheet_disabled(&self, node_id: NodeId) -> bool {
|
|
311
|
+
self.nodes_to_stylesheet
|
|
312
|
+
.get(&node_id)
|
|
313
|
+
.is_some_and(|sheet| sheet.0.disabled())
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/// Set the disabled flag of the stylesheet owned by `node_id`. A disabled
|
|
317
|
+
/// stylesheet stays associated with its owner node but its rules no longer
|
|
318
|
+
/// apply to the document.
|
|
319
|
+
///
|
|
320
|
+
/// Returns `false` if the node owns no stylesheet.
|
|
321
|
+
pub fn set_stylesheet_disabled(&mut self, node_id: NodeId, disabled: bool) -> bool {
|
|
322
|
+
let Some(sheet) = self.nodes_to_stylesheet.get(&node_id) else {
|
|
323
|
+
return false;
|
|
324
|
+
};
|
|
325
|
+
if sheet.0.set_disabled(disabled) {
|
|
326
|
+
self.stylist
|
|
327
|
+
.force_stylesheet_origins_dirty(OriginSet::all());
|
|
328
|
+
}
|
|
329
|
+
true
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
fn stylesheet_loader(&self) -> StylesheetLoader {
|
|
333
|
+
StylesheetLoader {
|
|
334
|
+
tx: self.tx.clone(),
|
|
335
|
+
doc_id: self.id(),
|
|
336
|
+
net_provider: self.net_provider.clone(),
|
|
337
|
+
shell_provider: self.shell_provider.clone(),
|
|
338
|
+
abort_signal: self.abort_signal.clone(),
|
|
339
|
+
import_depth: 0,
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/// Resolve `path` to the rule it denotes. The rule's ancestors (outermost
|
|
344
|
+
/// first) are passed to `on_ancestor` as they are traversed.
|
|
345
|
+
fn resolve_rule(
|
|
346
|
+
sheet: &DocumentStyleSheet,
|
|
347
|
+
guard: &SharedRwLockReadGuard,
|
|
348
|
+
path: &[usize],
|
|
349
|
+
on_ancestor: impl FnMut(&CssRule),
|
|
350
|
+
) -> Option<RuleHandle> {
|
|
351
|
+
let (index, parent_path) = path.split_last()?;
|
|
352
|
+
let list = Self::resolve_rule_list(sheet, guard, parent_path, on_ancestor)?;
|
|
353
|
+
Some(match list {
|
|
354
|
+
RuleList::Css(rules) => RuleHandle::Rule(rules.read_with(guard).0.get(*index)?.clone()),
|
|
355
|
+
RuleList::Keyframes(keyframes) => {
|
|
356
|
+
RuleHandle::Keyframe(keyframes.read_with(guard).keyframes.get(*index)?.clone())
|
|
357
|
+
}
|
|
358
|
+
})
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/// Resolve `path` to the rule list owned by the rule it denotes (or the
|
|
362
|
+
/// stylesheet's top-level rule list for an empty path). The ancestor rules
|
|
363
|
+
/// of that list (outermost first, including the rule at `path` itself) are
|
|
364
|
+
/// passed to `on_ancestor` as they are traversed.
|
|
365
|
+
fn resolve_rule_list(
|
|
366
|
+
sheet: &DocumentStyleSheet,
|
|
367
|
+
guard: &SharedRwLockReadGuard,
|
|
368
|
+
path: &[usize],
|
|
369
|
+
mut on_ancestor: impl FnMut(&CssRule),
|
|
370
|
+
) -> Option<RuleList> {
|
|
371
|
+
let mut list = RuleList::Css(sheet.contents(guard).rules.clone());
|
|
372
|
+
for &index in path {
|
|
373
|
+
let RuleList::Css(rules) = &list else {
|
|
374
|
+
return None;
|
|
375
|
+
};
|
|
376
|
+
let rules = rules.read_with(guard);
|
|
377
|
+
let rule = rules.0.get(index)?;
|
|
378
|
+
let child_list = child_rule_list(rule, guard)?;
|
|
379
|
+
on_ancestor(rule);
|
|
380
|
+
list = child_list;
|
|
381
|
+
}
|
|
382
|
+
Some(list)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/// [`Self::resolve_rule_list`], also collecting the ancestor rules
|
|
386
|
+
fn resolve_rule_list_with_ancestors(
|
|
387
|
+
sheet: &DocumentStyleSheet,
|
|
388
|
+
guard: &SharedRwLockReadGuard,
|
|
389
|
+
path: &[usize],
|
|
390
|
+
) -> Option<(Vec<CssRule>, RuleList)> {
|
|
391
|
+
let mut ancestors = Vec::with_capacity(path.len());
|
|
392
|
+
let list =
|
|
393
|
+
Self::resolve_rule_list(sheet, guard, path, |rule| ancestors.push(rule.clone()))?;
|
|
394
|
+
Some((ancestors, list))
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/// The number of rules in the rule list at `path` (`CSSRuleList.length`)
|
|
398
|
+
pub fn stylesheet_rule_count(&self, node_id: NodeId, path: &[usize]) -> Option<usize> {
|
|
399
|
+
let sheet = self.nodes_to_stylesheet.get(&node_id)?;
|
|
400
|
+
let guard = self.guard.read();
|
|
401
|
+
let list = Self::resolve_rule_list(sheet, &guard, path, |_| {})?;
|
|
402
|
+
Some(match list {
|
|
403
|
+
RuleList::Css(rules) => rules.read_with(&guard).0.len(),
|
|
404
|
+
RuleList::Keyframes(keyframes) => keyframes.read_with(&guard).keyframes.len(),
|
|
405
|
+
})
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/// The CSSOM-visible attributes of the rule at `path`
|
|
409
|
+
pub fn stylesheet_rule_info(&self, node_id: NodeId, path: &[usize]) -> Option<CssRuleInfo> {
|
|
410
|
+
let sheet = self.nodes_to_stylesheet.get(&node_id)?;
|
|
411
|
+
let guard = self.guard.read();
|
|
412
|
+
let handle = Self::resolve_rule(sheet, &guard, path, |_| {})?;
|
|
413
|
+
let mut css_text = CssStringWriter::new();
|
|
414
|
+
let has_style = declaration_target(&handle, &guard).is_some();
|
|
415
|
+
Some(match handle {
|
|
416
|
+
RuleHandle::Rule(rule) => {
|
|
417
|
+
let _ = rule.to_css(&guard, &mut css_text);
|
|
418
|
+
CssRuleInfo {
|
|
419
|
+
interface: css_rule_interface(&rule),
|
|
420
|
+
rule_type: rule.rule_type() as u16,
|
|
421
|
+
css_text,
|
|
422
|
+
has_child_rules: child_rule_list(&rule, &guard).is_some(),
|
|
423
|
+
has_style,
|
|
424
|
+
attributes: rule_attributes(&rule, &guard),
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
RuleHandle::Keyframe(keyframe) => {
|
|
428
|
+
let keyframe = keyframe.read_with(&guard);
|
|
429
|
+
let _ = keyframe.to_css(&guard, &mut css_text);
|
|
430
|
+
CssRuleInfo {
|
|
431
|
+
interface: "CSSKeyframeRule",
|
|
432
|
+
rule_type: CssRuleType::Keyframe as u16,
|
|
433
|
+
css_text,
|
|
434
|
+
has_child_rules: false,
|
|
435
|
+
has_style,
|
|
436
|
+
attributes: vec![("keyText", keyframe.selector.to_css_string())],
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
})
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/// Insert a rule parsed from `rule` at `index` in the rule list at `path`
|
|
443
|
+
/// (`CSSStyleSheet.insertRule` / `CSSGroupingRule.insertRule` /
|
|
444
|
+
/// `CSSKeyframesRule.appendRule`). Returns the index of the inserted rule.
|
|
445
|
+
pub fn stylesheet_insert_rule(
|
|
446
|
+
&mut self,
|
|
447
|
+
node_id: NodeId,
|
|
448
|
+
path: &[usize],
|
|
449
|
+
rule: &str,
|
|
450
|
+
index: usize,
|
|
451
|
+
) -> Result<usize, CssomError> {
|
|
452
|
+
let sheet = self
|
|
453
|
+
.nodes_to_stylesheet
|
|
454
|
+
.get(&node_id)
|
|
455
|
+
.ok_or(CssomError::NotFound)?
|
|
456
|
+
.clone();
|
|
457
|
+
let loader = self.stylesheet_loader();
|
|
458
|
+
let lock = &self.guard;
|
|
459
|
+
|
|
460
|
+
let (ancestors, list) = {
|
|
461
|
+
let guard = lock.read();
|
|
462
|
+
Self::resolve_rule_list_with_ancestors(&sheet, &guard, path)
|
|
463
|
+
.ok_or(CssomError::NotFound)?
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
let (new_rule, index) = match &list {
|
|
467
|
+
RuleList::Css(rules) => {
|
|
468
|
+
let new_rule = {
|
|
469
|
+
let guard = lock.read();
|
|
470
|
+
let contents = sheet.contents(&guard);
|
|
471
|
+
let containing_rule_types =
|
|
472
|
+
ancestors
|
|
473
|
+
.iter()
|
|
474
|
+
.fold(CssRuleTypes::default(), |types, rule| {
|
|
475
|
+
CssRuleTypes::from_bits(types.bits() | rule.rule_type().bit())
|
|
476
|
+
});
|
|
477
|
+
let parse_relative_rule_type = ancestors.last().map(|rule| rule.rule_type());
|
|
478
|
+
rules.read_with(&guard).parse_rule_for_insert(
|
|
479
|
+
lock,
|
|
480
|
+
rule,
|
|
481
|
+
contents,
|
|
482
|
+
index,
|
|
483
|
+
containing_rule_types,
|
|
484
|
+
parse_relative_rule_type,
|
|
485
|
+
Some(&loader),
|
|
486
|
+
AllowImportRules::Yes,
|
|
487
|
+
)?
|
|
488
|
+
};
|
|
489
|
+
{
|
|
490
|
+
let mut guard = lock.write();
|
|
491
|
+
rules
|
|
492
|
+
.write_with(&mut guard)
|
|
493
|
+
.0
|
|
494
|
+
.insert(index, new_rule.clone());
|
|
495
|
+
}
|
|
496
|
+
(new_rule, index)
|
|
497
|
+
}
|
|
498
|
+
RuleList::Keyframes(keyframes) => {
|
|
499
|
+
let keyframe = {
|
|
500
|
+
let guard = lock.read();
|
|
501
|
+
let contents = sheet.contents(&guard);
|
|
502
|
+
Keyframe::parse(rule, contents, lock).map_err(|_| CssomError::Syntax)?
|
|
503
|
+
};
|
|
504
|
+
let index = {
|
|
505
|
+
let mut guard = lock.write();
|
|
506
|
+
let keyframes = &mut keyframes.write_with(&mut guard).keyframes;
|
|
507
|
+
keyframes.push(keyframe);
|
|
508
|
+
keyframes.len() - 1
|
|
509
|
+
};
|
|
510
|
+
(CssRule::Keyframes(keyframes.clone()), index)
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
let guard = lock.read();
|
|
515
|
+
if let CssRule::FontFace(_) = &new_rule {
|
|
516
|
+
crate::net::fetch_font_face_rules(
|
|
517
|
+
std::iter::once(&new_rule),
|
|
518
|
+
self.tx.clone(),
|
|
519
|
+
self.id(),
|
|
520
|
+
Some(node_id),
|
|
521
|
+
&self.net_provider,
|
|
522
|
+
&self.shell_provider,
|
|
523
|
+
&guard,
|
|
524
|
+
self.abort_signal.as_ref(),
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
let (change_kind, ancestors) = match &list {
|
|
528
|
+
RuleList::Css(_) => (RuleChangeKind::Insertion, ancestors.as_slice()),
|
|
529
|
+
// The keyframes rule itself changed; its ancestors exclude it
|
|
530
|
+
RuleList::Keyframes(_) => (
|
|
531
|
+
RuleChangeKind::Generic,
|
|
532
|
+
&ancestors[..ancestors.len().saturating_sub(1)],
|
|
533
|
+
),
|
|
534
|
+
};
|
|
535
|
+
let ancestor_refs: Vec<CssRuleRef> = ancestors.iter().map(CssRuleRef::from).collect();
|
|
536
|
+
self.stylist
|
|
537
|
+
.rule_changed(&sheet, &new_rule, &guard, change_kind, &ancestor_refs);
|
|
538
|
+
Ok(index)
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/// Remove the rule at `index` from the rule list at `path`
|
|
542
|
+
/// (`CSSStyleSheet.deleteRule` / `CSSGroupingRule.deleteRule` /
|
|
543
|
+
/// `CSSKeyframesRule.deleteRule`).
|
|
544
|
+
pub fn stylesheet_delete_rule(
|
|
545
|
+
&mut self,
|
|
546
|
+
node_id: NodeId,
|
|
547
|
+
path: &[usize],
|
|
548
|
+
index: usize,
|
|
549
|
+
) -> Result<(), CssomError> {
|
|
550
|
+
let sheet = self
|
|
551
|
+
.nodes_to_stylesheet
|
|
552
|
+
.get(&node_id)
|
|
553
|
+
.ok_or(CssomError::NotFound)?
|
|
554
|
+
.clone();
|
|
555
|
+
let lock = &self.guard;
|
|
556
|
+
|
|
557
|
+
let (ancestors, list) = {
|
|
558
|
+
let guard = lock.read();
|
|
559
|
+
Self::resolve_rule_list_with_ancestors(&sheet, &guard, path)
|
|
560
|
+
.ok_or(CssomError::NotFound)?
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
let (removed_rule, change_kind, ancestors) = match &list {
|
|
564
|
+
RuleList::Css(rules) => {
|
|
565
|
+
let mut guard = lock.write();
|
|
566
|
+
let rules = rules.write_with(&mut guard);
|
|
567
|
+
let removed = rules.0.get(index).cloned().ok_or(CssomError::IndexSize)?;
|
|
568
|
+
rules.remove_rule(index)?;
|
|
569
|
+
(removed, RuleChangeKind::Removal, ancestors.as_slice())
|
|
570
|
+
}
|
|
571
|
+
RuleList::Keyframes(keyframes) => {
|
|
572
|
+
let mut guard = lock.write();
|
|
573
|
+
let list = &mut keyframes.write_with(&mut guard).keyframes;
|
|
574
|
+
if index >= list.len() {
|
|
575
|
+
return Err(CssomError::IndexSize);
|
|
576
|
+
}
|
|
577
|
+
list.remove(index);
|
|
578
|
+
(
|
|
579
|
+
CssRule::Keyframes(keyframes.clone()),
|
|
580
|
+
RuleChangeKind::Generic,
|
|
581
|
+
&ancestors[..ancestors.len().saturating_sub(1)],
|
|
582
|
+
)
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
let guard = lock.read();
|
|
587
|
+
let ancestor_refs: Vec<CssRuleRef> = ancestors.iter().map(CssRuleRef::from).collect();
|
|
588
|
+
self.stylist
|
|
589
|
+
.rule_changed(&sheet, &removed_rule, &guard, change_kind, &ancestor_refs);
|
|
590
|
+
Ok(())
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/// The declarations of the rule at `path`, for read-only access
|
|
594
|
+
fn rule_declaration_target(
|
|
595
|
+
&self,
|
|
596
|
+
node_id: NodeId,
|
|
597
|
+
path: &[usize],
|
|
598
|
+
guard: &SharedRwLockReadGuard,
|
|
599
|
+
) -> Option<DeclarationTarget> {
|
|
600
|
+
let sheet = self.nodes_to_stylesheet.get(&node_id)?;
|
|
601
|
+
let handle = Self::resolve_rule(sheet, guard, path, |_| {})?;
|
|
602
|
+
declaration_target(&handle, guard)
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/// The declarations of the rule at `path`, along with what is needed to
|
|
606
|
+
/// notify the stylist after mutating them: the stylesheet, the rule to
|
|
607
|
+
/// report as changed and its ancestors.
|
|
608
|
+
fn rule_declaration_target_mut(
|
|
609
|
+
&self,
|
|
610
|
+
node_id: NodeId,
|
|
611
|
+
path: &[usize],
|
|
612
|
+
) -> Option<(DocumentStyleSheet, Vec<CssRule>, CssRule, DeclarationTarget)> {
|
|
613
|
+
let sheet = self.nodes_to_stylesheet.get(&node_id)?.clone();
|
|
614
|
+
let guard = self.guard.read();
|
|
615
|
+
let mut ancestors = Vec::with_capacity(path.len());
|
|
616
|
+
let handle = Self::resolve_rule(&sheet, &guard, path, |rule| ancestors.push(rule.clone()))?;
|
|
617
|
+
let target = declaration_target(&handle, &guard)?;
|
|
618
|
+
// For keyframes, the rule to report as changed is the owning
|
|
619
|
+
// `@keyframes` rule
|
|
620
|
+
let (changed_rule, ancestors) = match handle {
|
|
621
|
+
RuleHandle::Rule(rule) => (rule, ancestors),
|
|
622
|
+
RuleHandle::Keyframe(_) => {
|
|
623
|
+
let mut ancestors = ancestors;
|
|
624
|
+
let rule = ancestors.pop()?;
|
|
625
|
+
(rule, ancestors)
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
Some((sheet, ancestors, changed_rule, target))
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/// The serialized declarations of the rule at `path` (`CSSRule.style.cssText`)
|
|
632
|
+
pub fn stylesheet_rule_style_css_text(
|
|
633
|
+
&self,
|
|
634
|
+
node_id: NodeId,
|
|
635
|
+
path: &[usize],
|
|
636
|
+
) -> Option<String> {
|
|
637
|
+
let guard = self.guard.read();
|
|
638
|
+
let target = self.rule_declaration_target(node_id, path, &guard)?;
|
|
639
|
+
let mut css = CssStringWriter::new();
|
|
640
|
+
match target {
|
|
641
|
+
DeclarationTarget::Block { block, .. } => {
|
|
642
|
+
let _ = block.read_with(&guard).to_css(&mut css);
|
|
643
|
+
}
|
|
644
|
+
DeclarationTarget::FontFace(rule) => {
|
|
645
|
+
let _ = rule
|
|
646
|
+
.read_with(&guard)
|
|
647
|
+
.descriptors
|
|
648
|
+
.to_css(&mut style_traits::CssWriter::new(&mut css));
|
|
649
|
+
// `Descriptors::to_css` emits a trailing space after each declaration
|
|
650
|
+
let trimmed = css.trim_end().len();
|
|
651
|
+
css.truncate(trimmed);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
Some(css)
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/// The names of the declarations of the rule at `path`, in order
|
|
658
|
+
/// (`CSSRule.style.length` / `.item()`)
|
|
659
|
+
pub fn stylesheet_rule_style_property_names(
|
|
660
|
+
&self,
|
|
661
|
+
node_id: NodeId,
|
|
662
|
+
path: &[usize],
|
|
663
|
+
) -> Option<Vec<String>> {
|
|
664
|
+
let guard = self.guard.read();
|
|
665
|
+
let target = self.rule_declaration_target(node_id, path, &guard)?;
|
|
666
|
+
Some(match target {
|
|
667
|
+
DeclarationTarget::Block { block, .. } => block
|
|
668
|
+
.read_with(&guard)
|
|
669
|
+
.declarations()
|
|
670
|
+
.iter()
|
|
671
|
+
.map(|decl| decl.id().name().into_owned())
|
|
672
|
+
.collect(),
|
|
673
|
+
DeclarationTarget::FontFace(rule) => {
|
|
674
|
+
let descriptors = &rule.read_with(&guard).descriptors;
|
|
675
|
+
(0..descriptors.len())
|
|
676
|
+
.filter_map(|i| descriptors.at(i))
|
|
677
|
+
.map(|id| id.name().to_string())
|
|
678
|
+
.collect()
|
|
679
|
+
}
|
|
680
|
+
})
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/// The serialized value of `property` in the declarations of the rule at
|
|
684
|
+
/// `path`, and whether it is `!important`
|
|
685
|
+
/// (`CSSRule.style.getPropertyValue()` / `.getPropertyPriority()`).
|
|
686
|
+
/// Returns an empty string for unset or unknown properties.
|
|
687
|
+
pub fn stylesheet_rule_style_get_property(
|
|
688
|
+
&self,
|
|
689
|
+
node_id: NodeId,
|
|
690
|
+
path: &[usize],
|
|
691
|
+
property: &str,
|
|
692
|
+
) -> Option<(String, bool)> {
|
|
693
|
+
let guard = self.guard.read();
|
|
694
|
+
let target = self.rule_declaration_target(node_id, path, &guard)?;
|
|
695
|
+
let mut css = CssStringWriter::new();
|
|
696
|
+
let important = match target {
|
|
697
|
+
DeclarationTarget::Block { block, .. } => {
|
|
698
|
+
let Ok(property_id) = PropertyId::parse_enabled_for_all_content(property) else {
|
|
699
|
+
return Some((String::new(), false));
|
|
700
|
+
};
|
|
701
|
+
let block = block.read_with(&guard);
|
|
702
|
+
let _ = block.property_value_to_css(&property_id, &mut css);
|
|
703
|
+
block.property_priority(&property_id).important()
|
|
704
|
+
}
|
|
705
|
+
DeclarationTarget::FontFace(rule) => {
|
|
706
|
+
let Ok(id) = FontFaceDescriptorId::from_ident(property) else {
|
|
707
|
+
return Some((String::new(), false));
|
|
708
|
+
};
|
|
709
|
+
let _ = rule.read_with(&guard).descriptors.get(id, &mut css);
|
|
710
|
+
false
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
Some((css, important))
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/// Set `property` to `value` in the declarations of the rule at `path`
|
|
717
|
+
/// (`CSSRule.style.setProperty()`). Invalid declarations are ignored, per
|
|
718
|
+
/// CSSOM. An empty `value` removes the property.
|
|
719
|
+
pub fn stylesheet_rule_style_set_property(
|
|
720
|
+
&mut self,
|
|
721
|
+
node_id: NodeId,
|
|
722
|
+
path: &[usize],
|
|
723
|
+
property: &str,
|
|
724
|
+
value: &str,
|
|
725
|
+
important: bool,
|
|
726
|
+
) -> Result<(), CssomError> {
|
|
727
|
+
if value.trim().is_empty() {
|
|
728
|
+
self.stylesheet_rule_style_remove_property(node_id, path, property)?;
|
|
729
|
+
return Ok(());
|
|
730
|
+
}
|
|
731
|
+
let (sheet, ancestors, rule, target) = self
|
|
732
|
+
.rule_declaration_target_mut(node_id, path)
|
|
733
|
+
.ok_or(CssomError::NotFound)?;
|
|
734
|
+
let url_data = self.url.url_extra_data();
|
|
735
|
+
let lock = &self.guard;
|
|
736
|
+
|
|
737
|
+
let change_kind = match target {
|
|
738
|
+
DeclarationTarget::Block { block, rule_type } => {
|
|
739
|
+
let Ok(property_id) = PropertyId::parse_enabled_for_all_content(property) else {
|
|
740
|
+
return Ok(());
|
|
741
|
+
};
|
|
742
|
+
let mut source = SourcePropertyDeclaration::default();
|
|
743
|
+
if parse_one_declaration_into(
|
|
744
|
+
&mut source,
|
|
745
|
+
property_id,
|
|
746
|
+
value,
|
|
747
|
+
Origin::Author,
|
|
748
|
+
&url_data,
|
|
749
|
+
None,
|
|
750
|
+
ParsingMode::DEFAULT,
|
|
751
|
+
QuirksMode::NoQuirks,
|
|
752
|
+
rule_type,
|
|
753
|
+
)
|
|
754
|
+
.is_err()
|
|
755
|
+
{
|
|
756
|
+
return Ok(());
|
|
757
|
+
}
|
|
758
|
+
let importance = if important {
|
|
759
|
+
Importance::Important
|
|
760
|
+
} else {
|
|
761
|
+
Importance::Normal
|
|
762
|
+
};
|
|
763
|
+
let mut guard = lock.write();
|
|
764
|
+
block
|
|
765
|
+
.write_with(&mut guard)
|
|
766
|
+
.extend(source.drain(), importance);
|
|
767
|
+
match rule_type {
|
|
768
|
+
CssRuleType::PositionTry => RuleChangeKind::PositionTryDeclarations,
|
|
769
|
+
_ => RuleChangeKind::StyleRuleDeclarations,
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
DeclarationTarget::FontFace(font_face) => {
|
|
773
|
+
let Ok(id) = FontFaceDescriptorId::from_ident(property) else {
|
|
774
|
+
return Ok(());
|
|
775
|
+
};
|
|
776
|
+
let context = ParserContext::new(
|
|
777
|
+
Origin::Author,
|
|
778
|
+
&url_data,
|
|
779
|
+
Some(CssRuleType::FontFace),
|
|
780
|
+
ParsingMode::DEFAULT,
|
|
781
|
+
QuirksMode::NoQuirks,
|
|
782
|
+
Default::default(),
|
|
783
|
+
None,
|
|
784
|
+
None,
|
|
785
|
+
Default::default(),
|
|
786
|
+
);
|
|
787
|
+
let mut parser = Parser::new(value);
|
|
788
|
+
let mut guard = lock.write();
|
|
789
|
+
let descriptors = &mut font_face.write_with(&mut guard).descriptors;
|
|
790
|
+
if descriptors.set(id, &context, &mut parser).is_err() {
|
|
791
|
+
return Ok(());
|
|
792
|
+
}
|
|
793
|
+
RuleChangeKind::Generic
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
|
|
797
|
+
let guard = lock.read();
|
|
798
|
+
let ancestor_refs: Vec<CssRuleRef> = ancestors.iter().map(CssRuleRef::from).collect();
|
|
799
|
+
self.stylist
|
|
800
|
+
.rule_changed(&sheet, &rule, &guard, change_kind, &ancestor_refs);
|
|
801
|
+
Ok(())
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
/// Remove `property` from the declarations of the rule at `path`
|
|
805
|
+
/// (`CSSRule.style.removeProperty()`). Returns the removed property's
|
|
806
|
+
/// previous serialized value.
|
|
807
|
+
pub fn stylesheet_rule_style_remove_property(
|
|
808
|
+
&mut self,
|
|
809
|
+
node_id: NodeId,
|
|
810
|
+
path: &[usize],
|
|
811
|
+
property: &str,
|
|
812
|
+
) -> Result<String, CssomError> {
|
|
813
|
+
let (sheet, ancestors, rule, target) = self
|
|
814
|
+
.rule_declaration_target_mut(node_id, path)
|
|
815
|
+
.ok_or(CssomError::NotFound)?;
|
|
816
|
+
let lock = &self.guard;
|
|
817
|
+
let mut removed_value = CssStringWriter::new();
|
|
818
|
+
|
|
819
|
+
let change_kind = match target {
|
|
820
|
+
DeclarationTarget::Block { block, rule_type } => {
|
|
821
|
+
let Ok(property_id) = PropertyId::parse_enabled_for_all_content(property) else {
|
|
822
|
+
return Ok(removed_value);
|
|
823
|
+
};
|
|
824
|
+
let mut guard = lock.write();
|
|
825
|
+
let block = block.write_with(&mut guard);
|
|
826
|
+
let _ = block.property_value_to_css(&property_id, &mut removed_value);
|
|
827
|
+
let Some(first_declaration) = block.first_declaration_to_remove(&property_id)
|
|
828
|
+
else {
|
|
829
|
+
return Ok(removed_value);
|
|
830
|
+
};
|
|
831
|
+
block.remove_property(&property_id, first_declaration);
|
|
832
|
+
match rule_type {
|
|
833
|
+
CssRuleType::PositionTry => RuleChangeKind::PositionTryDeclarations,
|
|
834
|
+
_ => RuleChangeKind::StyleRuleDeclarations,
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
DeclarationTarget::FontFace(font_face) => {
|
|
838
|
+
let Ok(id) = FontFaceDescriptorId::from_ident(property) else {
|
|
839
|
+
return Ok(removed_value);
|
|
840
|
+
};
|
|
841
|
+
let mut guard = lock.write();
|
|
842
|
+
let descriptors = &mut font_face.write_with(&mut guard).descriptors;
|
|
843
|
+
let _ = descriptors.get(id, &mut removed_value);
|
|
844
|
+
if !descriptors.remove(id) {
|
|
845
|
+
return Ok(removed_value);
|
|
846
|
+
}
|
|
847
|
+
RuleChangeKind::Generic
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
let guard = lock.read();
|
|
852
|
+
let ancestor_refs: Vec<CssRuleRef> = ancestors.iter().map(CssRuleRef::from).collect();
|
|
853
|
+
self.stylist
|
|
854
|
+
.rule_changed(&sheet, &rule, &guard, change_kind, &ancestor_refs);
|
|
855
|
+
Ok(removed_value)
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/// Replace the selectors of the style rule at `path` with those parsed
|
|
859
|
+
/// from `selectors` (`CSSStyleRule.selectorText` setter). Per CSSOM, an
|
|
860
|
+
/// invalid selector list leaves the rule unchanged (without throwing).
|
|
861
|
+
pub fn stylesheet_rule_set_selector_text(
|
|
862
|
+
&mut self,
|
|
863
|
+
node_id: NodeId,
|
|
864
|
+
path: &[usize],
|
|
865
|
+
selectors: &str,
|
|
866
|
+
) -> Result<(), CssomError> {
|
|
867
|
+
let sheet = self
|
|
868
|
+
.nodes_to_stylesheet
|
|
869
|
+
.get(&node_id)
|
|
870
|
+
.ok_or(CssomError::NotFound)?
|
|
871
|
+
.clone();
|
|
872
|
+
let lock = &self.guard;
|
|
873
|
+
|
|
874
|
+
let (ancestors, style_rule) = {
|
|
875
|
+
let guard = lock.read();
|
|
876
|
+
let mut ancestors = Vec::with_capacity(path.len());
|
|
877
|
+
let handle =
|
|
878
|
+
Self::resolve_rule(&sheet, &guard, path, |rule| ancestors.push(rule.clone()))
|
|
879
|
+
.ok_or(CssomError::NotFound)?;
|
|
880
|
+
let RuleHandle::Rule(CssRule::Style(style_rule)) = handle else {
|
|
881
|
+
return Err(CssomError::NotSupported);
|
|
882
|
+
};
|
|
883
|
+
(ancestors, style_rule)
|
|
884
|
+
};
|
|
885
|
+
|
|
886
|
+
// Nested style rules take relative selectors (`> .a`), resolved
|
|
887
|
+
// against the innermost enclosing style rule or `@scope`, mirroring
|
|
888
|
+
// stylo's `NestingContext`.
|
|
889
|
+
let parse_relative = ancestors
|
|
890
|
+
.iter()
|
|
891
|
+
.rev()
|
|
892
|
+
.find_map(|rule| match rule.rule_type() {
|
|
893
|
+
CssRuleType::Style => Some(ParseRelative::ForNesting),
|
|
894
|
+
CssRuleType::Scope => Some(ParseRelative::ForScope),
|
|
895
|
+
_ => None,
|
|
896
|
+
})
|
|
897
|
+
.unwrap_or(ParseRelative::No);
|
|
898
|
+
|
|
899
|
+
let new_selectors = {
|
|
900
|
+
let guard = lock.read();
|
|
901
|
+
let contents = sheet.contents(&guard);
|
|
902
|
+
let selector_parser = SelectorParser {
|
|
903
|
+
stylesheet_origin: contents.origin,
|
|
904
|
+
namespaces: &contents.namespaces,
|
|
905
|
+
url_data: &contents.url_data,
|
|
906
|
+
for_supports_rule: false,
|
|
907
|
+
};
|
|
908
|
+
let mut parser = Parser::new(selectors);
|
|
909
|
+
let Ok(new_selectors) =
|
|
910
|
+
SelectorList::parse(&selector_parser, &mut parser, parse_relative)
|
|
911
|
+
else {
|
|
912
|
+
return Ok(());
|
|
913
|
+
};
|
|
914
|
+
new_selectors
|
|
915
|
+
};
|
|
916
|
+
|
|
917
|
+
{
|
|
918
|
+
let mut guard = lock.write();
|
|
919
|
+
style_rule.write_with(&mut guard).selectors = new_selectors;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
let guard = lock.read();
|
|
923
|
+
let rule = CssRule::Style(style_rule);
|
|
924
|
+
let ancestor_refs: Vec<CssRuleRef> = ancestors.iter().map(CssRuleRef::from).collect();
|
|
925
|
+
self.stylist.rule_changed(
|
|
926
|
+
&sheet,
|
|
927
|
+
&rule,
|
|
928
|
+
&guard,
|
|
929
|
+
RuleChangeKind::Generic,
|
|
930
|
+
&ancestor_refs,
|
|
931
|
+
);
|
|
932
|
+
Ok(())
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/// Replace the declarations of the rule at `path` with those parsed from
|
|
936
|
+
/// `css` (`CSSRule.style.cssText` setter).
|
|
937
|
+
pub fn stylesheet_rule_style_set_css_text(
|
|
938
|
+
&mut self,
|
|
939
|
+
node_id: NodeId,
|
|
940
|
+
path: &[usize],
|
|
941
|
+
css: &str,
|
|
942
|
+
) -> Result<(), CssomError> {
|
|
943
|
+
let (sheet, ancestors, rule, target) = self
|
|
944
|
+
.rule_declaration_target_mut(node_id, path)
|
|
945
|
+
.ok_or(CssomError::NotFound)?;
|
|
946
|
+
let DeclarationTarget::Block { block, rule_type } = target else {
|
|
947
|
+
return Err(CssomError::NotSupported);
|
|
948
|
+
};
|
|
949
|
+
let new_block = style::properties::declaration_block::parse_style_attribute(
|
|
950
|
+
css,
|
|
951
|
+
&self.url.url_extra_data(),
|
|
952
|
+
None,
|
|
953
|
+
QuirksMode::NoQuirks,
|
|
954
|
+
rule_type,
|
|
955
|
+
);
|
|
956
|
+
let lock = &self.guard;
|
|
957
|
+
{
|
|
958
|
+
let mut guard = lock.write();
|
|
959
|
+
*block.write_with(&mut guard) = new_block;
|
|
960
|
+
}
|
|
961
|
+
let guard = lock.read();
|
|
962
|
+
let ancestor_refs: Vec<CssRuleRef> = ancestors.iter().map(CssRuleRef::from).collect();
|
|
963
|
+
let change_kind = match rule_type {
|
|
964
|
+
CssRuleType::PositionTry => RuleChangeKind::PositionTryDeclarations,
|
|
965
|
+
_ => RuleChangeKind::StyleRuleDeclarations,
|
|
966
|
+
};
|
|
967
|
+
self.stylist
|
|
968
|
+
.rule_changed(&sheet, &rule, &guard, change_kind, &ancestor_refs);
|
|
969
|
+
Ok(())
|
|
970
|
+
}
|
|
971
|
+
}
|