From f43f3497afed947f2d8b155a2436dbf3c9a33516 Mon Sep 17 00:00:00 2001 From: Rostislav Wolny Date: Tue, 29 Jan 2019 18:44:23 +0100 Subject: [PATCH] Fix styles inlining so that more specific styles are preserved [MAILPOET-1730] --- lib/Util/CSS.php | 9 ++++----- tests/unit/Util/CSSTest.php | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/lib/Util/CSS.php b/lib/Util/CSS.php index 32f6fd8ed7..a6c5480b0f 100644 --- a/lib/Util/CSS.php +++ b/lib/Util/CSS.php @@ -236,10 +236,7 @@ class CSS { // Unserialize the style array, merge the rule's CSS into it... $nodeStyles = self::styleToArray($node->style); - $style = array_merge($nodeStyles, $rule['properties']); - - // !important node styles should take precedence over other styles - $style = array_merge($style, preg_grep("/important/i", $nodeStyles)); + $style = array_merge($rule['properties'], $nodeStyles); // And put the CSS back as a string! $node->style = self::arrayToStyle($style); @@ -256,10 +253,12 @@ class CSS { // Now a tricky part: do a second pass with only stuff marked !important // because !important properties do not care about specificity, except when fighting // against another !important property + // We need to start with a rule with lowest specificity + $rules = array_reverse($rules); foreach ($rules as $rule) { foreach($rule['properties'] as $key => $value) { if(strpos($value, '!important') !== false) { - foreach($html->find($rule['selector']) as $node) { + foreach($html->query($rule['selector']) as $node) { $style = self::styleToArray($node->style); $style[$key] = $value; $node->style = self::arrayToStyle($style); diff --git a/tests/unit/Util/CSSTest.php b/tests/unit/Util/CSSTest.php index a01f406546..903ae2b758 100644 --- a/tests/unit/Util/CSSTest.php +++ b/tests/unit/Util/CSSTest.php @@ -36,4 +36,40 @@ class CSSTest extends \MailPoetUnitTest { $this->assertEquals('brown', $parsed[3]['properties']['color']); $this->assertEquals('red', $parsed[4]['properties']['color']); } + + public function testItCanInlineARule() { + $styles = 'p { color: red; }'; + $content = '

Foo

'; + $html = $this->buildHtml($styles, $content); + $result_html = $this->css->inlineCSS(null, $html); + $this->assertContains('

', $result_html); + } + + public function testItInlinesMoreSpecificRule() { + $styles = 'p { color: red; } .blue { color: blue; }'; + $content = '

Foo

'; + $html = $this->buildHtml($styles, $content); + $result_html = $this->css->inlineCSS(null, $html); + $this->assertContains('

', $result_html); + } + + public function testItPreserveInlinedRule() { + $styles = 'p { color: red; }'; + $content = '

Foo

'; + $html = $this->buildHtml($styles, $content); + $result_html = $this->css->inlineCSS(null, $html); + $this->assertContains('

', $result_html); + } + + public function testItAlwaysInlinesGlobalImportantRule() { + $styles = 'p { color: red !important; }'; + $content = '

Foo

'; + $html = $this->buildHtml($styles, $content); + $result_html = $this->css->inlineCSS(null, $html); + $this->assertContains('

', $result_html); + } + + private function buildHtml($styles, $content) { + return "{$content}"; + } }