@@ -6,7 +6,9 @@ use std::{
66
77use lol_html:: { element, end, html_content:: ContentType , rewrite_str, text, RewriteStrSettings } ;
88
9- use crate :: { css, import_map, js, share_url, RewriteContext } ;
9+ use crate :: {
10+ css, import_map, js, rewrite_wrapped_source, share_url, RewriteContext , RewriteOutput ,
11+ } ;
1012
1113use super :: { attr_policy_kind, fetch_url, link_rel_kind, srcset, target_url as resolve_target_url} ;
1214use super :: { blocked_element_kind, event_handler_attr_kind, meta_policy_kind, script_type_kind} ;
@@ -161,6 +163,7 @@ fn rewrite_element_attrs<H: lol_html::HandlerTypes>(
161163 if tag == "link" {
162164 backup_masked_attrs ( el, false ) ?;
163165 }
166+ rewrite_event_handler_attrs ( el, target_url, control_prefix) ?;
164167 if tag == "base" {
165168 rewrite_base_element ( el, target_url, control_prefix) ?;
166169 return Ok ( ( ) ) ;
@@ -172,7 +175,6 @@ fn rewrite_element_attrs<H: lol_html::HandlerTypes>(
172175 rewrite_link_attrs ( el, target_url, control_prefix) ?;
173176 return Ok ( ( ) ) ;
174177 }
175- rewrite_event_handler_attrs ( el) ?;
176178 rewrite_inline_style_attr ( el, target_url, control_prefix) ?;
177179 rewrite_srcdoc_attr ( el, & tag, runtime_prelude) ?;
178180 for attr in [ "href" , "xlink:href" , "src" , "poster" ] {
@@ -214,6 +216,8 @@ fn rewrite_base_element<H: lol_html::HandlerTypes>(
214216
215217fn rewrite_event_handler_attrs < H : lol_html:: HandlerTypes > (
216218 el : & mut lol_html:: html_content:: Element < ' _ , ' _ , H > ,
219+ target_url : & str ,
220+ control_prefix : & str ,
217221) -> lol_html:: HandlerResult {
218222 let handlers = attr_names ( el)
219223 . into_iter ( )
@@ -222,11 +226,28 @@ fn rewrite_event_handler_attrs<H: lol_html::HandlerTypes>(
222226 for name in handlers {
223227 let value = el. get_attribute ( & name) . unwrap_or_default ( ) ;
224228 el. remove_attribute ( & name) ;
225- el. set_attribute ( & format ! ( "data-zp-blocked-{name}" ) , & value) ?;
229+ let rewritten = rewrite_event_handler ( & value, target_url, control_prefix) ;
230+ if rewritten. ok {
231+ el. set_attribute ( & format ! ( "data-zp-event-{name}" ) , & rewritten. code ) ?;
232+ } else {
233+ el. set_attribute ( & format ! ( "data-zp-blocked-{name}" ) , & value) ?;
234+ }
226235 }
227236 Ok ( ( ) )
228237}
229238
239+ fn rewrite_event_handler ( source : & str , target_url : & str , control_prefix : & str ) -> RewriteOutput {
240+ let ctx = RewriteContext :: new ( target_url, control_prefix, "" , "" ) ;
241+ rewrite_wrapped_source (
242+ source,
243+ "function __zp_event__(event){\n " ,
244+ "\n }" ,
245+ false ,
246+ ctx. without_runtime_context ( ) ,
247+ true ,
248+ )
249+ }
250+
230251fn rewrite_inline_style_attr < H : lol_html:: HandlerTypes > (
231252 el : & mut lol_html:: html_content:: Element < ' _ , ' _ , H > ,
232253 target_url : & str ,
@@ -461,6 +482,11 @@ fn escape_inline_script_sentinel(code: &str) -> String {
461482fn drop_control_attrs < H : lol_html:: HandlerTypes > (
462483 el : & mut lol_html:: html_content:: Element < ' _ , ' _ , H > ,
463484) {
485+ for attr in attr_names ( el) {
486+ if attr. to_ascii_lowercase ( ) . starts_with ( "data-zp-event-" ) {
487+ el. remove_attribute ( & attr) ;
488+ }
489+ }
464490 for attr in [
465491 "data-zp-target-url" ,
466492 "data-zp-target-srcset" ,
@@ -862,7 +888,7 @@ mod tests {
862888 #[ test]
863889 fn rewrites_link_policy_with_lol_html ( ) {
864890 let out = rewrite_document (
865- r#"<head><link rel="preconnect" href="https://cdn.example/"><link rel="icon" href="/favicon.ico"><link rel="apple-touch-icon" href="touch.png"><link rel="stylesheet" href="/app.css"><link rel="stylesheet" href="data:text/css,x"></head>"# ,
891+ r#"<head><link rel="preconnect" href="https://cdn.example/"><link rel="icon" href="/favicon.ico"><link rel="apple-touch-icon" href="touch.png"><link rel="stylesheet" media="print" onload="this.media='all'; this.onload=null;" href="/app.css"><link rel="stylesheet" href="data:text/css,x"></head>"# ,
866892 DocumentOptions {
867893 target_url : "https://example.com/app/page.html" ,
868894 control_prefix : "/zp/" ,
@@ -882,6 +908,8 @@ mod tests {
882908 r#"data-zp-target-url="https://example.com/app/touch.png""# ,
883909 r#"href="/zp/api/fetch?url=https%3A%2F%2Fexample.com%2Fapp.css""# ,
884910 r#"data-zp-target-url="https://example.com/app.css""# ,
911+ r#"data-zp-event-onload=""# ,
912+ r#"__zp_runEvent"# ,
885913 r#"href="/zp/error/POLICY_BLOCKED""# ,
886914 r#"data-zp-blocked-url="data:text/css,x""# ,
887915 ] {
@@ -894,6 +922,8 @@ mod tests {
894922 r#"href="/favicon.ico""# ,
895923 r#"href="touch.png""# ,
896924 r#"href="/app.css""# ,
925+ r#" onload="# ,
926+ r#"data-zp-blocked-onload"# ,
897927 ] {
898928 assert ! (
899929 !out. contains( forbidden) ,
@@ -1056,7 +1086,7 @@ mod tests {
10561086 fn rewrites_script_style_importmap_and_srcdoc_with_lol_html ( ) {
10571087 let prelude = r#"<script nonce=zp>boot()</script><script nonce=zp src="/zp/assets/runtime-prelude.js"></script>"# ;
10581088 let out = rewrite_document (
1059- r#"<body onload="location.href='/boot'"><script src="/app.js" integrity="sha384-i" nonce="target-nonce"></script><script>window.location.href="<\/script>";</script><script type="module">import "./dep.js"; window.location.href;</script><script type="importmap">{"imports":{"a":"./a.js"}}</script><style>body{background:url("/bg.png")}</style><button onclick="return location.href"></button><iframe srcdoc="<p>x</p>"></iframe></body>"# ,
1089+ r#"<body onload="location.href='/boot'"><script src="/app.js" integrity="sha384-i" nonce="target-nonce"></script><script type="module" src="/entry.js"></script><script >window.location.href="<\/script>";</script><script type="module">import "./dep.js"; window.location.href;</script><script type="importmap">{"imports":{"a":"./a.js"}}</script><style>body{background:url("/bg.png")}</style><button onclick="return location.href"></button><iframe srcdoc="<p>x</p>"></iframe></body>"# ,
10601090 DocumentOptions {
10611091 target_url : "https://example.com/app/page.html" ,
10621092 control_prefix : "/zp/" ,
@@ -1070,7 +1100,9 @@ mod tests {
10701100
10711101 for want in [
10721102 r#"src="/zp/api/script?kind=classic&u=https%3A%2F%2Fexample.com%2Fapp.js&tab=tab-1&rt=rt-1""# ,
1103+ r#"src="/zp/api/script?kind=module&u=https%3A%2F%2Fexample.com%2Fentry.js&tab=tab-1&rt=rt-1""# ,
10731104 r#"data-zp-target-url="https://example.com/app.js""# ,
1105+ r#"data-zp-target-url="https://example.com/entry.js""# ,
10741106 r#"data-zp-integrity="sha384-i""# ,
10751107 r#"data-zp-target-nonce="target-nonce""# ,
10761108 r#"nonce="zp""# ,
@@ -1079,8 +1111,9 @@ mod tests {
10791111 r#"/zp/api/script?kind=module&u=https%3A%2F%2Fexample.com%2Fapp%2Fdep.js&tab=tab-1&rt=rt-1"# ,
10801112 r#""a":"/zp/api/script?kind=module\u0026rt=rt-1\u0026tab=tab-1\u0026u=https%3A%2F%2Fexample.com%2Fapp%2Fa.js""# ,
10811113 r#"url("/zp/api/fetch?url=https%3A%2F%2Fexample.com%2Fbg.png")"# ,
1082- r#"data-zp-blocked-onload="location.href='/boot'""# ,
1083- r#"data-zp-blocked-onclick="return location.href""# ,
1114+ r#"data-zp-event-onload=""# ,
1115+ r#"data-zp-event-onclick=""# ,
1116+ r#"__zp_runEvent"# ,
10841117 r#"srcdoc="<script nonce=zp>boot()</script>"# ,
10851118 ] {
10861119 assert ! ( out. contains( want) , "missing {want} in {out}" ) ;
@@ -1089,6 +1122,8 @@ mod tests {
10891122 for forbidden in [
10901123 r#" onload="# ,
10911124 r#" onclick="# ,
1125+ r#"data-zp-blocked-onload"# ,
1126+ r#"data-zp-blocked-onclick"# ,
10921127 r#" integrity="sha384-i""# ,
10931128 r#" nonce="target-nonce""# ,
10941129 r#"src="/app.js""# ,
0 commit comments