@@ -20,12 +20,17 @@ typedef OnTap = void Function(
20
20
Map <String , String > attributes,
21
21
dom.Element ? element,
22
22
);
23
+ typedef OnCssParseError = String ? Function (
24
+ String css,
25
+ List <cssparser.Message > errors,
26
+ );
23
27
24
28
class HtmlParser extends StatelessWidget {
25
29
final Key ? key;
26
30
final dom.Document htmlData;
27
31
final OnTap ? onLinkTap;
28
32
final OnTap ? onImageTap;
33
+ final OnCssParseError ? onCssParseError;
29
34
final ImageErrorListener ? onImageError;
30
35
final bool shrinkWrap;
31
36
@@ -39,6 +44,7 @@ class HtmlParser extends StatelessWidget {
39
44
required this .htmlData,
40
45
required this .onLinkTap,
41
46
required this .onImageTap,
47
+ required this .onCssParseError,
42
48
required this .onImageError,
43
49
required this .shrinkWrap,
44
50
required this .style,
@@ -48,16 +54,21 @@ class HtmlParser extends StatelessWidget {
48
54
49
55
@override
50
56
Widget build (BuildContext context) {
57
+ Map <String , Map <String , List <css.Expression >>> declarations = _getExternalCssDeclarations (htmlData.getElementsByTagName ("style" ), onCssParseError);
51
58
StyledElement lexedTree = lexDomTree (
52
59
htmlData,
53
60
customRenders.keys.toList (),
54
61
tagsList,
55
62
context,
56
63
this ,
57
64
);
58
- StyledElement inlineStyledTree = applyInlineStyles (lexedTree);
59
- StyledElement customStyledTree = _applyCustomStyles (inlineStyledTree);
60
- StyledElement cascadedStyledTree = _cascadeStyles (customStyledTree);
65
+ StyledElement ? externalCssStyledTree;
66
+ if (declarations.isNotEmpty) {
67
+ externalCssStyledTree = _applyExternalCss (declarations, lexedTree);
68
+ }
69
+ StyledElement inlineStyledTree = _applyInlineStyles (externalCssStyledTree ?? lexedTree, onCssParseError);
70
+ StyledElement customStyledTree = _applyCustomStyles (style, inlineStyledTree);
71
+ StyledElement cascadedStyledTree = _cascadeStyles (style, customStyledTree);
61
72
StyledElement cleanedTree = cleanTree (cascadedStyledTree);
62
73
InlineSpan parsedTree = parseTree (
63
74
RenderContext (
@@ -91,8 +102,8 @@ class HtmlParser extends StatelessWidget {
91
102
return htmlparser.parse (data);
92
103
}
93
104
94
- /// [parseCSS ] converts a string of CSS to a CSS stylesheet using the dart `csslib` library.
95
- static css.StyleSheet parseCSS (String data) {
105
+ /// [parseCss ] converts a string of CSS to a CSS stylesheet using the dart `csslib` library.
106
+ static css.StyleSheet parseCss (String data) {
96
107
return cssparser.parse (data);
97
108
}
98
109
@@ -187,34 +198,62 @@ class HtmlParser extends StatelessWidget {
187
198
}
188
199
}
189
200
190
- static StyledElement applyInlineStyles (StyledElement tree) {
201
+ static Map <String , Map <String , List <css.Expression >>> _getExternalCssDeclarations (List <dom.Element > styles, OnCssParseError ? errorHandler) {
202
+ String fullCss = "" ;
203
+ for (final e in styles) {
204
+ fullCss = fullCss + e.innerHtml;
205
+ }
206
+ if (fullCss.isNotEmpty) {
207
+ final declarations = parseExternalCss (fullCss, errorHandler);
208
+ return declarations;
209
+ } else {
210
+ return {};
211
+ }
212
+ }
213
+
214
+ static StyledElement _applyExternalCss (Map <String , Map <String , List <css.Expression >>> declarations, StyledElement tree) {
215
+ declarations.forEach ((key, style) {
216
+ if (tree.matchesSelector (key)) {
217
+ tree.style = tree.style.merge (declarationsToStyle (style));
218
+ }
219
+ });
220
+
221
+ tree.children.forEach ((e) => _applyExternalCss (declarations, e));
222
+
223
+ return tree;
224
+ }
225
+
226
+ static StyledElement _applyInlineStyles (StyledElement tree, OnCssParseError ? errorHandler) {
191
227
if (tree.attributes.containsKey ("style" )) {
192
- tree.style = tree.style.merge (inlineCSSToStyle (tree.attributes['style' ]));
228
+ final newStyle = inlineCssToStyle (tree.attributes['style' ], errorHandler);
229
+ if (newStyle != null ) {
230
+ tree.style = tree.style.merge (newStyle);
231
+ }
193
232
}
194
233
195
- tree.children.forEach (applyInlineStyles );
234
+ tree.children.forEach ((e) => _applyInlineStyles (e, errorHandler) );
196
235
return tree;
197
236
}
198
237
199
238
/// [applyCustomStyles] applies the [Style] objects passed into the [Html]
200
239
/// widget onto the [StyledElement] tree, no cascading of styles is done at this point.
201
- StyledElement _applyCustomStyles (StyledElement tree) {
240
+ static StyledElement _applyCustomStyles (Map < String , Style > style, StyledElement tree) {
202
241
style.forEach ((key, style) {
203
242
if (tree.matchesSelector (key)) {
204
243
tree.style = tree.style.merge (style);
205
244
}
206
245
});
207
- tree.children.forEach (_applyCustomStyles);
246
+ tree.children.forEach ((e) => _applyCustomStyles (style, e) );
208
247
209
248
return tree;
210
249
}
211
250
212
251
/// [_cascadeStyles] cascades all of the inherited styles down the tree, applying them to each
213
252
/// child that doesn't specify a different style.
214
- StyledElement _cascadeStyles (StyledElement tree) {
253
+ static StyledElement _cascadeStyles (Map < String , Style > style, StyledElement tree) {
215
254
tree.children.forEach ((child) {
216
255
child.style = tree.style.copyOnlyInherited (child.style);
217
- _cascadeStyles (child);
256
+ _cascadeStyles (style, child);
218
257
});
219
258
220
259
return tree;
@@ -526,7 +565,7 @@ class HtmlParser extends StatelessWidget {
526
565
tree.children.forEach ((child) {
527
566
if (child is EmptyContentElement || child is EmptyLayoutElement ) {
528
567
toRemove.add (child);
529
- } else if (child is TextContentElement && (child.text! .isEmpty)) {
568
+ } else if (child is TextContentElement && (child.text! .trim (). isEmpty)) {
530
569
toRemove.add (child);
531
570
} else if (child is TextContentElement &&
532
571
child.style.whiteSpace != WhiteSpace .PRE &&
0 commit comments