Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue with CSS arrow child selector symbol in <style> tag #353

Open
stuyam opened this issue Nov 10, 2017 · 8 comments
Open

Issue with CSS arrow child selector symbol in <style> tag #353

stuyam opened this issue Nov 10, 2017 · 8 comments

Comments

@stuyam
Copy link

stuyam commented Nov 10, 2017

I have a style tag at the top of the document that looks like this:

<style type="text/css" data-premailer="ignore">
  .example > .selector {
    background-color: red;
  }
</style>

It keeps resulting in the arrow css child selector being encoded into &gt; like this:

<style type="text/css" data-premailer="ignore">
  .example &gt; .selector {
    background-color: red;
  }
</style>

Does the input or output encoding need to be changed to prevent this or something? Have been trying for days to get this to work.

I appreciate any help I can get ❤️ really can't tell if it is a bug or not.

@dlouzan
Copy link

dlouzan commented Nov 13, 2019

@stuyam Recently I've bumped into the same issue while working on https://gitlab.com/gitlab-org/gitlab/merge_requests/17699. I independently opened fphilipe/premailer-rails#246 before finding this report.

Sadly I wasn't able to find any solution, and this really seems to be caused by premailer (deactivating premailer on our pipeline would render the output correctly).

We ended up rewriting the css rules to avoid the direct ancestor syntax.

@fphilipe
Copy link

fphilipe commented Dec 2, 2019

I tried reproducing this in premailer 1.11.1 with nokogiri 1.10.5 but couldn't. The > character is not being encoded.

I've found this issue on Nokogiri that has been fixed. On JRuby special characters were being encoded inside <style> tags.

Can you share some more details, @dlouzan? Otherwise I'd say this has been fixed.

@dlouzan
Copy link

dlouzan commented Dec 8, 2019

Hey @fphilipe, as I wrote in the original report in fphilipe/premailer-rails#246, I had tested this with the latest premailer-rails release.

Just in case, I have just tested again with gitlab's latest master (the MR I originally mentioned has been merged), I still see the same:

bundle list | grep -e 'premailer' -e 'nokogiri'
  * nokogiri (1.10.5)
  * premailer (1.11.1)
  * premailer-rails (1.10.3)

then if I add a rule like below to mailer_client_specific.scss:

.foo > .bar {
  color: fuchsia;
}

this is rendered in my local browser path /rails/mailers/notify/member_invite_accepted_email as:

.foo &gt; .bar {
  color: fuchsia;
}

The file is loaded as follows in _mailer.html.haml:

    -# Avoid premailer processing of client-specific styles (@media tag not supported)
    -# We need to inline the contents here because mail clients (e.g. iOS Mail, Outlook)
    -# do not support linked stylesheets.
    %style{ type: 'text/css', 'data-premailer': 'ignore' }
      = asset_to_string('mailer_client_specific.css').html_safe

which itself uses a helper on application_helper.rb:

  def asset_to_string(name)
    app = Rails.application
    if Rails.configuration.assets.compile
      app.assets.find_asset(name).to_s
    else
      controller.view_context.render(file: Rails.root.join('public/assets', app.assets_manifest.assets[name]).to_s)
    end
  end

Premailer itself is loaded by:

Premailer::Rails.config.merge!(
  generate_text_part: false,
  preserve_styles: true,
  remove_comments: true,
  remove_ids: false,
  remove_scripts: false,
  output_encoding: 'US-ASCII'
)

@dlouzan
Copy link

dlouzan commented Dec 8, 2019

@fphilipe In the whole example above, if I disable premailer for the project, the style is correctly generated.

@fphilipe
Copy link

fphilipe commented Dec 9, 2019

Are you by chance using XHTML? Could you share the HTML of your email, at least the bare structure including the <style> tag?

I believe that Nokogiri is escaping the > character. You might try wrapping the content of the <style> tag in a <![CDATA[...]]> section using Rails' cdata_section helper:

%style{ type: 'text/css', 'data-premailer': 'ignore' }
  = cdata_section(asset_to_string('mailer_client_specific.css').html_safe)

EDIT: Just noticed that you did link to your HTML file. It is indeed XHTML. Thus, either switch to HTML5 or use the CDATA section.

@dlouzan
Copy link

dlouzan commented Dec 9, 2019

Thanks for taking the time to check this 💯

What does seem to work at least in the browser is using the HTML5 doctype! Encoding is correct in that case. XHTML was the existing doctype in gitlab before I created the original MR, but tbh I did not try all possible combinations. Might be time to switch. If only there existed a stylesheet_embed_tag helper that took care of this... Every change in the styles means careful testing with litmus.com ¯\_(ツ)_/¯

As for your hint about trying cdata_section, that didn't fix it and actually seems to somehow break something internally with the rendering, because I end up with an embedded style element with just part of my changes and missing all the rest of the styles:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
<meta content="width=device-width, initial-scale=1" name="viewport" />
<meta content="IE=edge" http-equiv="X-UA-Compatible" />
<title>Invitation accepted</title>
<style data-premailer="ignore" type="text/css">
 .bar {
  color: fuchsia;
}
]]&gt;
</style>

<style>body {
margin: 0 !important; background-color: #fafafa; padding: 0; text-align: center; min-width: 640px; width: 100%; height: 100%; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
</style></head>
<body style="text-align: center; min-width: 640px; width: 100%; height: 100%; font-family: &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif; margin: 0; padding: 0;" bgcolor="#fafafa">
...

In any case I don't think I'd be able to use a solution that embeds the contents in CDATA, mail clients are really picky in what they accept, I'm pretty sure this wouldn't work everywhere. The whole point of that asset_to_string helper was to be able to embed the contents of the stylesheet directly without linking, which we learned the hard way doesn't work in all mail clients.

@dlouzan
Copy link

dlouzan commented Dec 9, 2019

@fphilipe As another potential workaround, are you aware of any flag option, similar to 'data-premailer': 'ignore' that we could pass the helper so that nokogiri does the right thing in this case? 🙇

@fphilipe
Copy link

fphilipe commented Dec 9, 2019

Dug a bit deeper. The culprit is the following line:

@processed_doc.to_xhtml(:encoding => @options[:output_encoding]).gsub(/&\#(xD|13);/i, "\r")

Additionally, premailer is doing some suspicious manipulation of CDATA sections:

# Fix for removing any CDATA tags from both style and script tags inserted per
# https://github.com/sparklemotion/nokogiri/issues/311 and
# https://github.com/premailer/premailer/issues/199
%w(style script).each do |tag|
doc.search(tag).children.each do |child|
child.swap(child.text()) if child.cdata?
end
end

Lastly, Nokogiri itself does some funny things to CDATA sections:

cdata = <<XML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<style data-premailer="ignore">
<!--/*--><![CDATA[/*><!--*/
html > body {
  color: fuchsia;
}
/*]]>*/-->
</style>
</head>
<body>
</body>
</html>
XML

n = Nokogiri::HTML(cdata)
puts n

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style data-premailer="ignore">
<!--/*--><![CDATA[/*><!--*/
html > body {
  color: fuchsia;
}
/*]]>*/-->
</style>
</head>
<body>
</body>
</html>

puts n.to_xhtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en" xml:lang="en">
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style data-premailer="ignore"><![CDATA[
<!--/*--><![CDATA[/*><!--*/
html > body {
  color: fuchsia;
}
/*]]]]><![CDATA[>*/-->
]]></style>
</head>
<body>
</body>
</html>

I found this issue: sparklemotion/nokogiri#311


In summary, I'd stay far away from XHTML. It's a thing of the past and there's no reason in (almost) 2020 to be using it still.

(At the same time, Nokogiri does not support HTML5, only HTML4.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants