add support for image inputs

This commit is contained in:
Sunshine 2020-04-03 03:30:52 -04:00
parent 5ba6e33fa8
commit 29836d979a
No known key found for this signature in database
GPG key ID: B80CA68703CD8AB1
6 changed files with 119 additions and 46 deletions

View file

@ -18,9 +18,6 @@ const CSS_PROPS_WITH_IMAGE_URLS: &[&str] = &[
"mask-image",
];
const TRANSPARENT_PIXEL: &str = "data:image/png;base64,\
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=";
pub fn is_image_url_prop(prop_name: &str) -> bool {
CSS_PROPS_WITH_IMAGE_URLS
.iter()
@ -185,7 +182,7 @@ pub fn process_css<'a>(
}
if opt_no_images && is_image_url_prop(curr_prop.as_str()) {
result.push_str(enquote(str!(TRANSPARENT_PIXEL), false).as_str());
result.push_str(enquote(str!(empty_image!()), false).as_str());
} else {
let resolved_url = resolve_url(&parent_url, value).unwrap_or_default();
let (data_url, _final_url) = retrieve_asset(
@ -294,7 +291,7 @@ pub fn process_css<'a>(
);
} else {
if opt_no_images && is_image_url_prop(curr_prop.as_str()) {
result.push_str(enquote(str!(TRANSPARENT_PIXEL), false).as_str());
result.push_str(enquote(str!(empty_image!()), false).as_str());
} else {
let full_url = resolve_url(&parent_url, value).unwrap_or_default();
let (data_url, _final_url) = retrieve_asset(

View file

@ -20,9 +20,6 @@ const ICON_VALUES: &[&str] = &[
"fluid-icon",
];
const TRANSPARENT_PIXEL: &str = "data:image/png;base64,\
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=";
pub fn get_parent_node(node: &Handle) -> Handle {
let parent = node.parent.take().clone();
parent.and_then(|node| node.upgrade()).unwrap()
@ -270,7 +267,7 @@ pub fn walk_and_embed_assets(
if opt_no_images {
attrs_mut.push(Attribute {
name: QualName::new(None, ns!(), local_name!("src")),
value: Tendril::from_slice(TRANSPARENT_PIXEL),
value: Tendril::from_slice(empty_image!()),
});
} else if let Some((data_url, _)) = found_datasrc
.iter()
@ -297,6 +294,58 @@ pub fn walk_and_embed_assets(
});
}
}
"input" => {
let mut is_image: bool = false;
for attr in attrs_mut.iter_mut() {
let attr_name: &str = &attr.name.local;
if attr_name == "type" {
is_image = attr.value.to_string().eq_ignore_ascii_case("image");
}
}
if is_image {
let mut found_src: Option<Attribute> = None;
let mut i = 0;
while i < attrs_mut.len() {
let attr_name = attrs_mut[i].name.local.as_ref();
if attr_name.eq_ignore_ascii_case("src") {
found_src = Some(attrs_mut.remove(i));
} else {
i += 1;
}
}
// If images are disabled, clear both sources
if opt_no_images {
attrs_mut.push(Attribute {
name: QualName::new(None, ns!(), local_name!("src")),
value: Tendril::from_slice(empty_image!()),
});
} else if let Some((data_url, _)) = found_src
.iter()
.map(|attr| attr.value.trim())
.filter(|src| !src.is_empty()) // Skip if empty
.next()
.and_then(|src| resolve_url(&url, src).ok()) // Make absolute
.and_then(|abs_src| // Download and convert to data_url
retrieve_asset(
cache,
client,
&url,
&abs_src,
true,
"",
opt_silent,
).ok())
{
// Add new data_url src attribute
attrs_mut.push(Attribute {
name: QualName::new(None, ns!(), local_name!("src")),
value: Tendril::from_slice(data_url.as_ref()),
});
}
}
}
"source" => {
for attr in attrs_mut.iter_mut() {
let attr_name: &str = &attr.name.local;
@ -310,7 +359,7 @@ pub fn walk_and_embed_assets(
if get_node_name(&get_parent_node(&node)) == Some("picture") {
if opt_no_images {
attr.value.clear();
attr.value.push_slice(TRANSPARENT_PIXEL);
attr.value.push_slice(empty_image!());
} else {
let srcset_full_url =
resolve_url(&url, attr.value.trim()).unwrap_or_default();

View file

@ -7,3 +7,11 @@ macro_rules! str {
ToString::to_string(&$val)
};
}
#[macro_export]
macro_rules! empty_image {
() => {
"data:image/png;base64,\
iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAQAAADY4iz3AAAAEUlEQVR42mNkwAkYR6UolgIACvgADsuK6xYAAAAASUVORK5CYII="
};
}

View file

@ -162,15 +162,18 @@ fn passing_remove_images_from_data_url() -> Result<(), Box<dyn std::error::Error
// STDOUT should contain HTML with no images
assert_eq!(
std::str::from_utf8(&out.stdout).unwrap(),
"<html>\
format!(
"<html>\
<head>\
<meta http-equiv=\"Content-Security-Policy\" content=\"img-src data:;\"></meta>\
</head>\
<body>\
<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=\">\
<img src=\"{empty_image}\">\
Hi\
</body>\
</html>\n"
</html>\n",
empty_image = empty_image!()
)
);
// STDERR should be empty
@ -229,7 +232,8 @@ fn passing_local_file_target_input() -> Result<(), Box<dyn std::error::Error>> {
// STDOUT should contain HTML from the local file
assert_eq!(
std::str::from_utf8(&out.stdout).unwrap(),
"<!DOCTYPE html><html lang=\"en\"><head>\n \
"\
<!DOCTYPE html><html lang=\"en\"><head>\n \
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n \
<title>Local HTML file</title>\n \
<link href=\"data:text/css;base64,Ym9keSB7CiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjMDAwOwogICAgY29sb3I6ICNmZmY7Cn0K\" rel=\"stylesheet\" type=\"text/css\">\n \
@ -238,16 +242,19 @@ fn passing_local_file_target_input() -> Result<(), Box<dyn std::error::Error>> {
<a href=\"file://local-file.html/\">Tricky href</a>\n \
<a href=\"https://github.com/Y2Z/monolith\">Remote URL</a>\n \
<script src=\"data:application/javascript;base64,ZG9jdW1lbnQuYm9keS5zdHlsZS5iYWNrZ3JvdW5kQ29sb3IgPSAiZ3JlZW4iOwpkb2N1bWVudC5ib2R5LnN0eWxlLmNvbG9yID0gInJlZCI7Cg==\"></script>\n\n\n\n\
</body></html>\n"
</body></html>\n\
"
);
// STDERR should contain list of retrieved file URLs
assert_eq!(
std::str::from_utf8(&out.stderr).unwrap(),
format!(
"{file}{cwd}/src/tests/data/local-file.html\n\
"\
{file}{cwd}/src/tests/data/local-file.html\n\
{file}{cwd}/src/tests/data/local-style.css\n\
{file}{cwd}/src/tests/data/local-script.js\n",
{file}{cwd}/src/tests/data/local-script.js\n\
",
file = file_url_protocol,
cwd = cwd_normalized
)
@ -286,17 +293,22 @@ fn passing_local_file_target_input_absolute_target_path() -> Result<(), Box<dyn
// STDOUT should contain HTML from the local file
assert_eq!(
std::str::from_utf8(&out.stdout).unwrap(),
"<!DOCTYPE html><html lang=\"en\"><head>\
format!(
"\
<!DOCTYPE html><html lang=\"en\"><head>\
<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'unsafe-inline' data:; style-src 'none'; script-src 'none'; img-src data:;\"></meta>\n \
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n \
<title>Local HTML file</title>\n \
<link href=\"\" rel=\"stylesheet\" type=\"text/css\">\n \
<link href=\"\" rel=\"stylesheet\" type=\"text/css\">\n</head>\n\n<body>\n \
<img alt=\"\" src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=\">\n \
<img alt=\"\" src=\"{empty_image}\">\n \
<a href=\"file://local-file.html/\">Tricky href</a>\n \
<a href=\"https://github.com/Y2Z/monolith\">Remote URL</a>\n \
<script src=\"\"></script>\n\n\n\n\
</body></html>\n"
</body></html>\n\
",
empty_image = empty_image!()
)
);
// STDERR should contain only the target file
@ -342,17 +354,22 @@ fn passing_local_file_url_target_input() -> Result<(), Box<dyn std::error::Error
// STDOUT should contain HTML from the local file
assert_eq!(
std::str::from_utf8(&out.stdout).unwrap(),
"<!DOCTYPE html><html lang=\"en\"><head>\
format!(
"\
<!DOCTYPE html><html lang=\"en\"><head>\
<meta http-equiv=\"Content-Security-Policy\" content=\"style-src 'none'; script-src 'none'; img-src data:;\"></meta>\n \
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n \
<title>Local HTML file</title>\n \
<link href=\"\" rel=\"stylesheet\" type=\"text/css\">\n \
<link href=\"\" rel=\"stylesheet\" type=\"text/css\">\n</head>\n\n<body>\n \
<img alt=\"\" src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=\">\n \
<img alt=\"\" src=\"{empty_image}\">\n \
<a href=\"file://local-file.html/\">Tricky href</a>\n \
<a href=\"https://github.com/Y2Z/monolith\">Remote URL</a>\n \
<script src=\"\"></script>\n\n\n\n\
</body></html>\n"
</body></html>\n\
",
empty_image = empty_image!()
)
);
// STDERR should contain list of retrieved file URLs
@ -458,7 +475,8 @@ fn passing_css_import_string() -> Result<(), Box<dyn std::error::Error>> {
let mut file_html = NamedTempFile::new()?;
writeln!(
file_html,
"<style>\n\
"\
<style>\n\
@charset 'UTF-8';\n\
\n\
@import '{file}{css_path}';\n\
@ -466,7 +484,8 @@ fn passing_css_import_string() -> Result<(), Box<dyn std::error::Error>> {
@import url({file}{css_path});\n\
\n\
@import url('{file}{css_path}')\n\
</style>\n",
</style>\n\
",
file = file_url_prefix,
css_path = str!(file_css.path().to_str().unwrap()).replace("\\", "/"),
)?;

View file

@ -40,13 +40,16 @@ height: calc(100vh - 10pt)";
true,
true,
),
"/* border: none;*/\
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='); \
list-style: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=');\
format!(
"/* border: none;*/\
background-image: url('{empty_image}'); \
list-style: url('{empty_image}');\
width:99.998%; \
margin-top: -20px; \
line-height: -1; \
height: calc(100vh - 10pt)"
height: calc(100vh - 10pt)",
empty_image = empty_image!()
)
);
}
@ -64,21 +67,17 @@ line-height: -1; \
height: calc(100vh - 10pt)";
assert_eq!(
css::embed_css(
cache,
&client,
"",
&STYLE,
true,
true,
),
"/* border: none;*/\
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='); \
list-style: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=');\
css::embed_css(cache, &client, "", &STYLE, true, true,),
format!(
"/* border: none;*/\
background-image: url('{empty_image}'); \
list-style: url('{empty_image}');\
width:99.998%; \
margin-top: -20px; \
line-height: -1; \
height: calc(100vh - 10pt)"
height: calc(100vh - 10pt)",
empty_image = empty_image!()
)
);
}

View file

@ -197,18 +197,19 @@ fn passing_no_images() {
assert_eq!(
buf.iter().map(|&c| c as char).collect::<String>(),
"<html>\
format!(
"<html>\
<head>\
<link rel=\"icon\" href=\"\">\
</head>\
<body>\
<div>\
<img src=\"data:image/png;base64,\
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0\
lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=\">\
<img src=\"{empty_image}\">\
</div>\
</body>\
</html>"
</html>",
empty_image = empty_image!()
)
);
}