Skip to content

Commit

Permalink
feat(html/minifier): Compress javascript: URLs (#6185)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait committed Oct 18, 2022
1 parent a81cc9a commit 8f00d1a
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 7 deletions.
51 changes: 45 additions & 6 deletions crates/swc_html_minifier/src/lib.rs
Expand Up @@ -173,6 +173,8 @@ static ALLOW_TO_TRIM_HTML_ATTRIBUTES: &[(&str, &str)] = &[
("object", "usemap"),
];

static ALLOW_TO_TRIM_SVG_ATTRIBUTES: &[(&str, &str)] = &[("a", "href")];

static COMMA_SEPARATED_HTML_ATTRIBUTES: &[(&str, &str)] = &[
("img", "srcset"),
("source", "srcset"),
Expand Down Expand Up @@ -398,6 +400,9 @@ impl Minifier<'_> {
Namespace::HTML => {
ALLOW_TO_TRIM_HTML_ATTRIBUTES.contains(&(&element.tag_name, attribute_name))
}
Namespace::SVG => {
ALLOW_TO_TRIM_SVG_ATTRIBUTES.contains(&(&element.tag_name, attribute_name))
}
_ => false,
}
}
Expand Down Expand Up @@ -658,6 +663,16 @@ impl Minifier<'_> {
}
}

fn is_javascript_url_element(&self, element: &Element) -> bool {
match (element.namespace, &element.tag_name) {
(Namespace::HTML | Namespace::SVG, &js_word!("a")) => return true,
(Namespace::HTML, &js_word!("iframe")) => return true,
_ => {}
}

false
}

fn is_preserved_comment(&self, data: &str) -> bool {
if let Some(preserve_comments) = &self.options.preserve_comments {
return preserve_comments.iter().any(|regex| regex.is_match(data));
Expand Down Expand Up @@ -2419,18 +2434,42 @@ impl VisitMut for Minifier<'_> {
_ if self.is_trimable_separated_attribute(current_element, &n.name) => {
let mut value = value.to_string();

if self.options.normalize_attributes {
value = value.trim().to_string();
}
let fallback = |n: &mut Attribute| {
if self.options.normalize_attributes {
n.value = Some(value.trim().into());
}
};

if self.need_minify_css() && n.name == js_word!("style") && !value.is_empty() {
if let Some(minified) =
self.minify_css(value, CssMinificationMode::ListOfDeclarations)
let value = value.trim();

if let Some(minified) = self
.minify_css(value.to_string(), CssMinificationMode::ListOfDeclarations)
{
n.value = Some(minified.into());
} else {
fallback(n);
}
} else if self.need_minify_js()
&& self.is_javascript_url_element(current_element)
{
if value.trim().to_lowercase().starts_with("javascript:") {
value = value.trim().chars().skip(11).collect();

if let Some(minified) = self.minify_js(value, false, true) {
let mut with_javascript =
String::with_capacity(11 + minified.len());

with_javascript.push_str("javascript:");
with_javascript.push_str(&minified);

n.value = Some(with_javascript.into());
}
} else {
fallback(n);
}
} else {
n.value = Some(value.into());
fallback(n);
}
}
_ if self.options.minify_additional_attributes.is_some() => {
Expand Down
@@ -0,0 +1,3 @@
{
"normalizeAttributes": false
}
@@ -0,0 +1,21 @@
<!doctype html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<a href=" javascript: alert( 'test' ) ">a</a>
<a href=" JAVASCRIPT: alert( 'test' ) ">b</a>
<iframe src="javascript: alert( 'test' ) " frameborder="0"></iframe>
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<!-- A link around a shape -->
<a href="javascript: alert( 'test' ) ">
<circle cx="50" cy="40" r="35" />
</a>
<!-- A link around a text -->
<a href=" javascript: alert( 'test' ) ">
<text x="50" y="90" text-anchor="middle">&lt;circle&gt;</text>
</a>
</svg>
</body>
</html>
@@ -0,0 +1,13 @@
<!doctype html><html lang=en><title>Document</title><a href='javascript:alert("test")'>a</a>
<a href='javascript:alert("test")'>b</a>
<iframe src='javascript:alert("test")' frameborder=0></iframe>
<svg viewBox="0 0 100 100">

<a href='javascript:alert("test")'>
<circle cx=50 cy=40 r=35 />
</a>

<a href='javascript:alert("test")'>
<text x=50 y=90 text-anchor=middle>&lt;circle></text>
</a>
</svg>
@@ -0,0 +1,22 @@
<!doctype html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<a href=" javascript: alert( 'test' ) ">a</a>
<a href=" JAVASCRIPT: alert( 'test' ) ">b</a>
<a href=" JAVASCRIPT: broken;;( ">b</a>
<iframe src="javascript: alert( 'test' ) " frameborder="0"></iframe>
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<!-- A link around a shape -->
<a href="javascript: alert( 'test' ) ">
<circle cx="50" cy="40" r="35" />
</a>
<!-- A link around a text -->
<a href=" javascript: alert( 'test' ) ">
<text x="50" y="90" text-anchor="middle">&lt;circle&gt;</text>
</a>
</svg>
</body>
</html>
@@ -0,0 +1,14 @@
<!doctype html><html lang=en><title>Document</title><a href='javascript:alert("test")'>a</a>
<a href='javascript:alert("test")'>b</a>
<a href=" JAVASCRIPT: broken;;( ">b</a>
<iframe src='javascript:alert("test")' frameborder=0></iframe>
<svg viewBox="0 0 100 100">

<a href='javascript:alert("test")'>
<circle cx=50 cy=40 r=35 />
</a>

<a href='javascript:alert("test")'>
<text x=50 y=90 text-anchor=middle>&lt;circle></text>
</a>
</svg>
Expand Up @@ -286,7 +286,7 @@
</svg>

<svg>
<a href=" test.html " ondragexit='alert("testtest")' rel="nofollow noindex">Test</a>
<a href=test.html ondragexit='alert("testtest")' rel="nofollow noindex">Test</a>
</svg>

<svg viewBox="0 0 100 100" width=160 height=60 y=30>
Expand Down

1 comment on commit 8f00d1a

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: 8f00d1a Previous: 41093b0 Ratio
es/full/minify/libraries/antd 1800709158 ns/iter (± 38890459) 1947116812 ns/iter (± 57266737) 0.92
es/full/minify/libraries/d3 388164960 ns/iter (± 11991389) 415278392 ns/iter (± 27625194) 0.93
es/full/minify/libraries/echarts 1520225719 ns/iter (± 37278534) 1618382624 ns/iter (± 56434405) 0.94
es/full/minify/libraries/jquery 99995648 ns/iter (± 5137842) 101189389 ns/iter (± 6390662) 0.99
es/full/minify/libraries/lodash 110506896 ns/iter (± 8643418) 110837073 ns/iter (± 4895977) 1.00
es/full/minify/libraries/moment 57088122 ns/iter (± 536126) 58537819 ns/iter (± 1568351) 0.98
es/full/minify/libraries/react 19719555 ns/iter (± 1420530) 20076472 ns/iter (± 996483) 0.98
es/full/minify/libraries/terser 314548684 ns/iter (± 12543095) 361825615 ns/iter (± 19836127) 0.87
es/full/minify/libraries/three 564957937 ns/iter (± 107315057) 643517767 ns/iter (± 28558633) 0.88
es/full/minify/libraries/typescript 3396568614 ns/iter (± 57650840) 3802784919 ns/iter (± 492403221) 0.89
es/full/minify/libraries/victory 814522039 ns/iter (± 27780768) 912334212 ns/iter (± 54130646) 0.89
es/full/minify/libraries/vue 152276767 ns/iter (± 9636268) 166430808 ns/iter (± 11248521) 0.91
es/full/codegen/es3 34317 ns/iter (± 551) 35187 ns/iter (± 4671) 0.98
es/full/codegen/es5 34294 ns/iter (± 830) 36162 ns/iter (± 5648) 0.95
es/full/codegen/es2015 34253 ns/iter (± 1570) 37473 ns/iter (± 5488) 0.91
es/full/codegen/es2016 34369 ns/iter (± 680) 38919 ns/iter (± 7237) 0.88
es/full/codegen/es2017 35012 ns/iter (± 2705) 38312 ns/iter (± 7447) 0.91
es/full/codegen/es2018 34676 ns/iter (± 1583) 36559 ns/iter (± 5444) 0.95
es/full/codegen/es2019 34643 ns/iter (± 1861) 38168 ns/iter (± 5357) 0.91
es/full/codegen/es2020 34889 ns/iter (± 2549) 36801 ns/iter (± 5798) 0.95
es/full/all/es3 215058960 ns/iter (± 19691599) 217099922 ns/iter (± 12824230) 0.99
es/full/all/es5 202871274 ns/iter (± 27387408) 214007170 ns/iter (± 20873001) 0.95
es/full/all/es2015 174807699 ns/iter (± 12542677) 170815819 ns/iter (± 24019257) 1.02
es/full/all/es2016 164798007 ns/iter (± 18819019) 164077194 ns/iter (± 15470486) 1.00
es/full/all/es2017 171933400 ns/iter (± 19929431) 160948136 ns/iter (± 9292730) 1.07
es/full/all/es2018 168173052 ns/iter (± 15790482) 162927717 ns/iter (± 12501176) 1.03
es/full/all/es2019 166898599 ns/iter (± 18117798) 166608397 ns/iter (± 25164250) 1.00
es/full/all/es2020 158483321 ns/iter (± 21210884) 183160135 ns/iter (± 26644117) 0.87
es/full/parser 748265 ns/iter (± 124506) 875800 ns/iter (± 131697) 0.85
es/full/base/fixer 27255 ns/iter (± 3657) 27390 ns/iter (± 2853) 1.00
es/full/base/resolver_and_hygiene 97323 ns/iter (± 14006) 98030 ns/iter (± 8447) 0.99
serialization of ast node 213 ns/iter (± 19) 227 ns/iter (± 36) 0.94
serialization of serde 220 ns/iter (± 34) 213 ns/iter (± 21) 1.03

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.