@@ -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+ );
3135typedef 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,15 +72,20 @@ class HtmlParser extends StatelessWidget {
6672
6773 @override
6874 Widget build (BuildContext context) {
75+ Map <String , Map <String , List <css.Expression >>> declarations = _getExternalCssDeclarations (htmlData.getElementsByTagName ("style" ), onCssParseError);
6976 StyledElement lexedTree = lexDomTree (
7077 htmlData,
7178 customRender.keys.toList (),
7279 tagsList,
7380 navigationDelegateForIframe,
7481 );
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);
7889 StyledElement cleanedTree = cleanTree (cascadedStyledTree);
7990 InlineSpan parsedTree = parseTree (
8091 RenderContext (
@@ -108,8 +119,8 @@ class HtmlParser extends StatelessWidget {
108119 return htmlparser.parse (data);
109120 }
110121
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) {
113124 return cssparser.parse (data);
114125 }
115126
@@ -189,34 +200,62 @@ class HtmlParser extends StatelessWidget {
189200 }
190201 }
191202
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) {
193229 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+ }
195234 }
196235
197- tree.children.forEach (applyInlineStyles );
236+ tree.children.forEach ((e) => _applyInlineStyles (e, errorHandler) );
198237 return tree;
199238 }
200239
201240 /// [applyCustomStyles] applies the [Style] objects passed into the [Html]
202241 /// 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) {
204243 style.forEach ((key, style) {
205244 if (tree.matchesSelector (key)) {
206245 tree.style = tree.style.merge (style);
207246 }
208247 });
209- tree.children.forEach (_applyCustomStyles);
248+ tree.children.forEach ((e) => _applyCustomStyles (style, e) );
210249
211250 return tree;
212251 }
213252
214253 /// [_cascadeStyles] cascades all of the inherited styles down the tree, applying them to each
215254 /// child that doesn't specify a different style.
216- StyledElement _cascadeStyles (StyledElement tree) {
255+ static StyledElement _cascadeStyles (Map < String , Style > style, StyledElement tree) {
217256 tree.children.forEach ((child) {
218257 child.style = tree.style.copyOnlyInherited (child.style);
219- _cascadeStyles (child);
258+ _cascadeStyles (style, child);
220259 });
221260
222261 return tree;
@@ -704,7 +743,7 @@ class HtmlParser extends StatelessWidget {
704743 tree.children.forEach ((child) {
705744 if (child is EmptyContentElement || child is EmptyLayoutElement ) {
706745 toRemove.add (child);
707- } else if (child is TextContentElement && (child.text! .isEmpty)) {
746+ } else if (child is TextContentElement && (child.text! .trim (). isEmpty)) {
708747 toRemove.add (child);
709748 } else if (child is TextContentElement &&
710749 child.style.whiteSpace != WhiteSpace .PRE &&
0 commit comments