Skip to content

Commit b744025

Browse files
committed
Add error handling for CSS parsing
1 parent 974def2 commit b744025

File tree

5 files changed

+58
-16
lines changed

5 files changed

+58
-16
lines changed

example/lib/main.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,13 @@ class _MyHomePageState extends State<MyHomePage> {
295295
onImageError: (exception, stackTrace) {
296296
print(exception);
297297
},
298+
onCSSParseError: (css, messages) {
299+
print("css that errored: $css");
300+
print("error messages:");
301+
messages.forEach((element) {
302+
print(element);
303+
});
304+
},
298305
),
299306
),
300307
);

lib/flutter_html.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class Html extends StatelessWidget {
5353
this.onLinkTap,
5454
this.customRender = const {},
5555
this.customImageRenders = const {},
56+
this.onCSSParseError,
5657
this.onImageError,
5758
this.onMathError,
5859
this.shrinkWrap = false,
@@ -71,6 +72,7 @@ class Html extends StatelessWidget {
7172
this.onLinkTap,
7273
this.customRender = const {},
7374
this.customImageRenders = const {},
75+
this.onCSSParseError,
7476
this.onImageError,
7577
this.onMathError,
7678
this.shrinkWrap = false,
@@ -99,6 +101,9 @@ class Html extends StatelessWidget {
99101
/// See the README for more details.
100102
final Map<ImageSourceMatcher, ImageRender> customImageRenders;
101103

104+
/// A function that defines what to do when CSS fails to parse
105+
final OnCSSParseError? onCSSParseError;
106+
102107
/// A function that defines what to do when an image errors
103108
final ImageErrorListener? onImageError;
104109

@@ -148,6 +153,7 @@ class Html extends StatelessWidget {
148153
htmlData: doc,
149154
onLinkTap: onLinkTap,
150155
onImageTap: onImageTap,
156+
onCSSParseError: onCSSParseError,
151157
onImageError: onImageError,
152158
onMathError: onMathError,
153159
shrinkWrap: shrinkWrap,

lib/html_parser.dart

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ typedef OnMathError = Widget Function(
2828
String exception,
2929
String exceptionWithType,
3030
);
31+
typedef OnCSSParseError = String? Function(
32+
String css,
33+
List<cssparser.Message> errors,
34+
);
3135
typedef CustomRender = dynamic Function(
3236
RenderContext context,
3337
Widget parsedChild,
@@ -38,6 +42,7 @@ class HtmlParser extends StatelessWidget {
3842
final dom.Document htmlData;
3943
final OnTap? onLinkTap;
4044
final OnTap? onImageTap;
45+
final OnCSSParseError? onCSSParseError;
4146
final ImageErrorListener? onImageError;
4247
final OnMathError? onMathError;
4348
final bool shrinkWrap;
@@ -54,6 +59,7 @@ class HtmlParser extends StatelessWidget {
5459
required this.htmlData,
5560
required this.onLinkTap,
5661
required this.onImageTap,
62+
required this.onCSSParseError,
5763
required this.onImageError,
5864
required this.onMathError,
5965
required this.shrinkWrap,
@@ -66,7 +72,7 @@ class HtmlParser extends StatelessWidget {
6672

6773
@override
6874
Widget build(BuildContext context) {
69-
Map<String, Map<String, List<css.Expression>>> declarations = _getExternalCSSDeclarations(htmlData.getElementsByTagName("style"));
75+
Map<String, Map<String, List<css.Expression>>> declarations = _getExternalCSSDeclarations(htmlData.getElementsByTagName("style"), onCSSParseError);
7076
StyledElement lexedTree = lexDomTree(
7177
htmlData,
7278
customRender.keys.toList(),
@@ -77,7 +83,7 @@ class HtmlParser extends StatelessWidget {
7783
if (declarations.isNotEmpty) {
7884
externalCSSStyledTree = _applyExternalCSS(declarations, lexedTree);
7985
}
80-
StyledElement inlineStyledTree = _applyInlineStyles(externalCSSStyledTree ?? lexedTree);
86+
StyledElement inlineStyledTree = _applyInlineStyles(externalCSSStyledTree ?? lexedTree, onCSSParseError);
8187
StyledElement customStyledTree = _applyCustomStyles(style, inlineStyledTree);
8288
StyledElement cascadedStyledTree = _cascadeStyles(style, customStyledTree);
8389
StyledElement cleanedTree = cleanTree(cascadedStyledTree);
@@ -194,13 +200,13 @@ class HtmlParser extends StatelessWidget {
194200
}
195201
}
196202

197-
static Map<String, Map<String, List<css.Expression>>> _getExternalCSSDeclarations(List<dom.Element> styles) {
203+
static Map<String, Map<String, List<css.Expression>>> _getExternalCSSDeclarations(List<dom.Element> styles, OnCSSParseError? errorHandler) {
198204
String fullCSS = "";
199205
for (final e in styles) {
200206
fullCSS = fullCSS + e.innerHtml;
201207
}
202208
if (fullCSS.isNotEmpty) {
203-
final declarations = parseExternalCSS(fullCSS);
209+
final declarations = parseExternalCSS(fullCSS, errorHandler);
204210
return declarations;
205211
} else {
206212
return {};
@@ -219,12 +225,15 @@ class HtmlParser extends StatelessWidget {
219225
return tree;
220226
}
221227

222-
static StyledElement _applyInlineStyles(StyledElement tree) {
228+
static StyledElement _applyInlineStyles(StyledElement tree, OnCSSParseError? errorHandler) {
223229
if (tree.attributes.containsKey("style")) {
224-
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+
}
225234
}
226235

227-
tree.children.forEach(_applyInlineStyles);
236+
tree.children.forEach((e) => _applyInlineStyles(e, errorHandler));
228237
return tree;
229238
}
230239

lib/src/css_parser.dart

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:csslib/visitor.dart' as css;
55
import 'package:csslib/parser.dart' as cssparser;
66
import 'package:flutter/cupertino.dart';
77
import 'package:flutter/material.dart';
8+
import 'package:flutter_html/flutter_html.dart';
89
import 'package:flutter_html/src/utils.dart';
910
import 'package:flutter_html/style.dart';
1011

@@ -102,15 +103,33 @@ Style declarationsToStyle(Map<String, List<css.Expression>> declarations) {
102103
return style;
103104
}
104105

105-
Style inlineCSSToStyle(String? inlineStyle) {
106-
final sheet = cssparser.parse("*{$inlineStyle}");
107-
final declarations = DeclarationVisitor().getDeclarations(sheet);
108-
return declarationsToStyle(declarations["*"]!);
106+
Style? inlineCSSToStyle(String? inlineStyle, OnCSSParseError? errorHandler) {
107+
var errors = <cssparser.Message>[];
108+
final sheet = cssparser.parse("*{$inlineStyle}", errors: errors);
109+
if (errors.isEmpty) {
110+
final declarations = DeclarationVisitor().getDeclarations(sheet);
111+
return declarationsToStyle(declarations["*"]!);
112+
} else if (errorHandler != null) {
113+
String? newCSS = errorHandler.call(inlineStyle ?? "", errors);
114+
if (newCSS != null) {
115+
return inlineCSSToStyle(newCSS, errorHandler);
116+
}
117+
}
118+
return null;
109119
}
110120

111-
Map<String, Map<String, List<css.Expression>>> parseExternalCSS(String css) {
112-
final sheet = cssparser.parse(css);
113-
return DeclarationVisitor().getDeclarations(sheet);
121+
Map<String, Map<String, List<css.Expression>>> parseExternalCSS(String css, OnCSSParseError? errorHandler) {
122+
var errors = <cssparser.Message>[];
123+
final sheet = cssparser.parse(css, errors: errors);
124+
if (errors.isEmpty) {
125+
return DeclarationVisitor().getDeclarations(sheet);
126+
} else if (errorHandler != null) {
127+
String? newCSS = errorHandler.call(css, errors);
128+
if (newCSS != null) {
129+
return parseExternalCSS(newCSS, errorHandler);
130+
}
131+
}
132+
return {};
114133
}
115134

116135
class DeclarationVisitor extends css.Visitor {

lib/style.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:ui';
22

33
import 'package:flutter/material.dart';
4+
import 'package:flutter_html/flutter_html.dart';
45
import 'package:flutter_html/src/css_parser.dart';
56

67
///This class represents all the available CSS attributes
@@ -215,8 +216,8 @@ class Style {
215216
}
216217
}
217218

218-
static Map<String, Style> fromCSS(String css) {
219-
final declarations = parseExternalCSS(css);
219+
static Map<String, Style> fromCSS(String css, OnCSSParseError? onCSSParseError) {
220+
final declarations = parseExternalCSS(css, onCSSParseError);
220221
Map<String, Style> styleMap = {};
221222
declarations.forEach((key, value) {
222223
styleMap[key] = declarationsToStyle(value);

0 commit comments

Comments
 (0)