@@ -28,6 +28,10 @@ typedef OnMathError = Widget Function(
28
28
String exception,
29
29
String exceptionWithType,
30
30
);
31
+ typedef OnCssParseError = String ? Function (
32
+ String css,
33
+ List <cssparser.Message > errors,
34
+ );
31
35
typedef CustomRender = dynamic Function (
32
36
RenderContext context,
33
37
Widget parsedChild,
@@ -38,6 +42,7 @@ class HtmlParser extends StatelessWidget {
38
42
final dom.Document htmlData;
39
43
final OnTap ? onLinkTap;
40
44
final OnTap ? onImageTap;
45
+ final OnCssParseError ? onCssParseError;
41
46
final ImageErrorListener ? onImageError;
42
47
final OnMathError ? onMathError;
43
48
final bool shrinkWrap;
@@ -54,6 +59,7 @@ class HtmlParser extends StatelessWidget {
54
59
required this .htmlData,
55
60
required this .onLinkTap,
56
61
required this .onImageTap,
62
+ required this .onCssParseError,
57
63
required this .onImageError,
58
64
required this .onMathError,
59
65
required this .shrinkWrap,
@@ -66,15 +72,20 @@ class HtmlParser extends StatelessWidget {
66
72
67
73
@override
68
74
Widget build (BuildContext context) {
75
+ Map <String , Map <String , List <css.Expression >>> declarations = _getExternalCssDeclarations (htmlData.getElementsByTagName ("style" ), onCssParseError);
69
76
StyledElement lexedTree = lexDomTree (
70
77
htmlData,
71
78
customRender.keys.toList (),
72
79
tagsList,
73
80
navigationDelegateForIframe,
74
81
);
75
- StyledElement inlineStyledTree = applyInlineStyles (lexedTree);
76
- StyledElement customStyledTree = _applyCustomStyles (inlineStyledTree);
77
- StyledElement cascadedStyledTree = _cascadeStyles (customStyledTree);
82
+ StyledElement ? externalCssStyledTree;
83
+ if (declarations.isNotEmpty) {
84
+ externalCssStyledTree = _applyExternalCss (declarations, lexedTree);
85
+ }
86
+ StyledElement inlineStyledTree = _applyInlineStyles (externalCssStyledTree ?? lexedTree, onCssParseError);
87
+ StyledElement customStyledTree = _applyCustomStyles (style, inlineStyledTree);
88
+ StyledElement cascadedStyledTree = _cascadeStyles (style, customStyledTree);
78
89
StyledElement cleanedTree = cleanTree (cascadedStyledTree);
79
90
InlineSpan parsedTree = parseTree (
80
91
RenderContext (
@@ -108,8 +119,8 @@ class HtmlParser extends StatelessWidget {
108
119
return htmlparser.parse (data);
109
120
}
110
121
111
- /// [parseCSS ] converts a string of CSS to a CSS stylesheet using the dart `csslib` library.
112
- static css.StyleSheet parseCSS (String data) {
122
+ /// [parseCss ] converts a string of CSS to a CSS stylesheet using the dart `csslib` library.
123
+ static css.StyleSheet parseCss (String data) {
113
124
return cssparser.parse (data);
114
125
}
115
126
@@ -189,34 +200,62 @@ class HtmlParser extends StatelessWidget {
189
200
}
190
201
}
191
202
192
- static StyledElement applyInlineStyles (StyledElement tree) {
203
+ static Map <String , Map <String , List <css.Expression >>> _getExternalCssDeclarations (List <dom.Element > styles, OnCssParseError ? errorHandler) {
204
+ String fullCss = "" ;
205
+ for (final e in styles) {
206
+ fullCss = fullCss + e.innerHtml;
207
+ }
208
+ if (fullCss.isNotEmpty) {
209
+ final declarations = parseExternalCss (fullCss, errorHandler);
210
+ return declarations;
211
+ } else {
212
+ return {};
213
+ }
214
+ }
215
+
216
+ static StyledElement _applyExternalCss (Map <String , Map <String , List <css.Expression >>> declarations, StyledElement tree) {
217
+ declarations.forEach ((key, style) {
218
+ if (tree.matchesSelector (key)) {
219
+ tree.style = tree.style.merge (declarationsToStyle (style));
220
+ }
221
+ });
222
+
223
+ tree.children.forEach ((e) => _applyExternalCss (declarations, e));
224
+
225
+ return tree;
226
+ }
227
+
228
+ static StyledElement _applyInlineStyles (StyledElement tree, OnCssParseError ? errorHandler) {
193
229
if (tree.attributes.containsKey ("style" )) {
194
- tree.style = tree.style.merge (inlineCSSToStyle (tree.attributes['style' ]));
230
+ final newStyle = inlineCssToStyle (tree.attributes['style' ], errorHandler);
231
+ if (newStyle != null ) {
232
+ tree.style = tree.style.merge (newStyle);
233
+ }
195
234
}
196
235
197
- tree.children.forEach (applyInlineStyles );
236
+ tree.children.forEach ((e) => _applyInlineStyles (e, errorHandler) );
198
237
return tree;
199
238
}
200
239
201
240
/// [applyCustomStyles] applies the [Style] objects passed into the [Html]
202
241
/// widget onto the [StyledElement] tree, no cascading of styles is done at this point.
203
- StyledElement _applyCustomStyles (StyledElement tree) {
242
+ static StyledElement _applyCustomStyles (Map < String , Style > style, StyledElement tree) {
204
243
style.forEach ((key, style) {
205
244
if (tree.matchesSelector (key)) {
206
245
tree.style = tree.style.merge (style);
207
246
}
208
247
});
209
- tree.children.forEach (_applyCustomStyles);
248
+ tree.children.forEach ((e) => _applyCustomStyles (style, e) );
210
249
211
250
return tree;
212
251
}
213
252
214
253
/// [_cascadeStyles] cascades all of the inherited styles down the tree, applying them to each
215
254
/// child that doesn't specify a different style.
216
- StyledElement _cascadeStyles (StyledElement tree) {
255
+ static StyledElement _cascadeStyles (Map < String , Style > style, StyledElement tree) {
217
256
tree.children.forEach ((child) {
218
257
child.style = tree.style.copyOnlyInherited (child.style);
219
- _cascadeStyles (child);
258
+ _cascadeStyles (style, child);
220
259
});
221
260
222
261
return tree;
@@ -704,7 +743,7 @@ class HtmlParser extends StatelessWidget {
704
743
tree.children.forEach ((child) {
705
744
if (child is EmptyContentElement || child is EmptyLayoutElement ) {
706
745
toRemove.add (child);
707
- } else if (child is TextContentElement && (child.text! .isEmpty)) {
746
+ } else if (child is TextContentElement && (child.text! .trim (). isEmpty)) {
708
747
toRemove.add (child);
709
748
} else if (child is TextContentElement &&
710
749
child.style.whiteSpace != WhiteSpace .PRE &&
0 commit comments