Merge pull request #193 from snshn/options-struct
Pass options object instead of using separate parameters
This commit is contained in:
commit
438ebd520a
15 changed files with 320 additions and 632 deletions
64
src/css.rs
64
src/css.rs
|
@ -2,6 +2,7 @@ use cssparser::{ParseError, Parser, ParserInput, SourcePosition, Token};
|
|||
use reqwest::blocking::Client;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::opts::Options;
|
||||
use crate::url::{data_to_data_url, get_url_fragment, is_http_url, resolve_url, url_with_fragment};
|
||||
use crate::utils::retrieve_asset;
|
||||
|
||||
|
@ -59,12 +60,10 @@ pub fn process_css<'a>(
|
|||
client: &Client,
|
||||
parent_url: &str,
|
||||
parser: &mut Parser,
|
||||
options: &Options,
|
||||
rule_name: &str,
|
||||
prop_name: &str,
|
||||
func_name: &str,
|
||||
opt_no_fonts: bool,
|
||||
opt_no_images: bool,
|
||||
opt_silent: bool,
|
||||
) -> Result<String, ParseError<'a, String>> {
|
||||
let mut result: String = str!();
|
||||
|
||||
|
@ -91,7 +90,7 @@ pub fn process_css<'a>(
|
|||
Token::Colon => result.push_str(":"),
|
||||
Token::Comma => result.push_str(","),
|
||||
Token::ParenthesisBlock | Token::SquareBracketBlock | Token::CurlyBracketBlock => {
|
||||
if opt_no_fonts && curr_rule == "font-face" {
|
||||
if options.no_fonts && curr_rule == "font-face" {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -114,12 +113,10 @@ pub fn process_css<'a>(
|
|||
client,
|
||||
parent_url,
|
||||
parser,
|
||||
options,
|
||||
rule_name,
|
||||
curr_prop.as_str(),
|
||||
func_name,
|
||||
opt_no_fonts,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
|
@ -149,7 +146,7 @@ pub fn process_css<'a>(
|
|||
// @import, @font-face, @charset, @media...
|
||||
Token::AtKeyword(ref value) => {
|
||||
curr_rule = str!(value);
|
||||
if opt_no_fonts && curr_rule == "font-face" {
|
||||
if options.no_fonts && curr_rule == "font-face" {
|
||||
continue;
|
||||
}
|
||||
result.push_str("@");
|
||||
|
@ -172,7 +169,13 @@ pub fn process_css<'a>(
|
|||
|
||||
let import_full_url = resolve_url(&parent_url, value).unwrap_or_default();
|
||||
let import_url_fragment = get_url_fragment(import_full_url.clone());
|
||||
match retrieve_asset(cache, client, &parent_url, &import_full_url, opt_silent) {
|
||||
match retrieve_asset(
|
||||
cache,
|
||||
client,
|
||||
&parent_url,
|
||||
&import_full_url,
|
||||
options.silent,
|
||||
) {
|
||||
Ok((import_contents, import_final_url, _import_media_type)) => {
|
||||
let import_data_url = data_to_data_url(
|
||||
"text/css",
|
||||
|
@ -181,9 +184,7 @@ pub fn process_css<'a>(
|
|||
client,
|
||||
&import_final_url,
|
||||
&String::from_utf8_lossy(&import_contents),
|
||||
opt_no_fonts,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
options,
|
||||
)
|
||||
.as_bytes(),
|
||||
&import_final_url,
|
||||
|
@ -212,7 +213,7 @@ pub fn process_css<'a>(
|
|||
continue;
|
||||
}
|
||||
|
||||
if opt_no_images && is_image_url_prop(curr_prop.as_str()) {
|
||||
if options.no_images && is_image_url_prop(curr_prop.as_str()) {
|
||||
result.push_str(enquote(str!(empty_image!()), false).as_str());
|
||||
} else {
|
||||
let resolved_url = resolve_url(&parent_url, value).unwrap_or_default();
|
||||
|
@ -222,7 +223,7 @@ pub fn process_css<'a>(
|
|||
client,
|
||||
&parent_url,
|
||||
&resolved_url,
|
||||
opt_silent,
|
||||
options.silent,
|
||||
) {
|
||||
Ok((data, final_url, media_type)) => {
|
||||
let data_url = data_to_data_url(&media_type, &data, &final_url);
|
||||
|
@ -265,7 +266,7 @@ pub fn process_css<'a>(
|
|||
if *has_sign && *unit_value >= 0. {
|
||||
result.push_str("+");
|
||||
}
|
||||
result.push_str(str!(unit_value * 100.).as_str());
|
||||
result.push_str(str!(unit_value * 100.0).as_str());
|
||||
result.push_str("%");
|
||||
}
|
||||
Token::Dimension {
|
||||
|
@ -309,7 +310,7 @@ pub fn process_css<'a>(
|
|||
if is_import {
|
||||
let full_url = resolve_url(&parent_url, value).unwrap_or_default();
|
||||
let url_fragment = get_url_fragment(full_url.clone());
|
||||
match retrieve_asset(cache, client, &parent_url, &full_url, opt_silent) {
|
||||
match retrieve_asset(cache, client, &parent_url, &full_url, options.silent) {
|
||||
Ok((css, final_url, _media_type)) => {
|
||||
let data_url = data_to_data_url(
|
||||
"text/css",
|
||||
|
@ -318,9 +319,7 @@ pub fn process_css<'a>(
|
|||
client,
|
||||
&final_url,
|
||||
&String::from_utf8_lossy(&css),
|
||||
opt_no_fonts,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
options,
|
||||
)
|
||||
.as_bytes(),
|
||||
&final_url,
|
||||
|
@ -339,12 +338,13 @@ pub fn process_css<'a>(
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if opt_no_images && is_image_url_prop(curr_prop.as_str()) {
|
||||
if options.no_images && is_image_url_prop(curr_prop.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 url_fragment = get_url_fragment(full_url.clone());
|
||||
match retrieve_asset(cache, client, &parent_url, &full_url, opt_silent) {
|
||||
match retrieve_asset(cache, client, &parent_url, &full_url, options.silent)
|
||||
{
|
||||
Ok((data, final_url, media_type)) => {
|
||||
let data_url = data_to_data_url(&media_type, &data, &final_url);
|
||||
let assembled_url: String =
|
||||
|
@ -377,12 +377,10 @@ pub fn process_css<'a>(
|
|||
client,
|
||||
parent_url,
|
||||
parser,
|
||||
options,
|
||||
curr_rule.as_str(),
|
||||
curr_prop.as_str(),
|
||||
function_name,
|
||||
opt_no_fonts,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
|
@ -407,24 +405,10 @@ pub fn embed_css(
|
|||
client: &Client,
|
||||
parent_url: &str,
|
||||
css: &str,
|
||||
opt_no_fonts: bool,
|
||||
opt_no_images: bool,
|
||||
opt_silent: bool,
|
||||
options: &Options,
|
||||
) -> String {
|
||||
let mut input = ParserInput::new(&css);
|
||||
let mut parser = Parser::new(&mut input);
|
||||
|
||||
process_css(
|
||||
cache,
|
||||
client,
|
||||
parent_url,
|
||||
&mut parser,
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
opt_no_fonts,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
)
|
||||
.unwrap()
|
||||
process_css(cache, client, parent_url, &mut parser, options, "", "", "").unwrap()
|
||||
}
|
||||
|
|
199
src/html.rs
199
src/html.rs
|
@ -15,6 +15,7 @@ use std::default::Default;
|
|||
|
||||
use crate::css::embed_css;
|
||||
use crate::js::attr_is_event_handler;
|
||||
use crate::opts::Options;
|
||||
use crate::url::{
|
||||
data_to_data_url, get_url_fragment, is_http_url, resolve_url, url_has_protocol,
|
||||
url_with_fragment,
|
||||
|
@ -73,8 +74,7 @@ pub fn embed_srcset(
|
|||
client: &Client,
|
||||
parent_url: &str,
|
||||
srcset: &str,
|
||||
opt_no_images: bool,
|
||||
opt_silent: bool,
|
||||
options: &Options,
|
||||
) -> String {
|
||||
let mut array: Vec<SrcSetItem> = vec![];
|
||||
let srcset_items: Vec<&str> = srcset.split(',').collect();
|
||||
|
@ -89,12 +89,12 @@ pub fn embed_srcset(
|
|||
let mut result: String = str!();
|
||||
let mut i: usize = array.len();
|
||||
for part in array {
|
||||
if opt_no_images {
|
||||
if options.no_images {
|
||||
result.push_str(empty_image!());
|
||||
} else {
|
||||
let image_full_url = resolve_url(&parent_url, part.path).unwrap_or_default();
|
||||
let image_url_fragment = get_url_fragment(image_full_url.clone());
|
||||
match retrieve_asset(cache, client, &parent_url, &image_full_url, opt_silent) {
|
||||
match retrieve_asset(cache, client, &parent_url, &image_full_url, options.silent) {
|
||||
Ok((image_data, image_final_url, image_media_type)) => {
|
||||
let image_data_url =
|
||||
data_to_data_url(&image_media_type, &image_data, &image_final_url);
|
||||
|
@ -137,29 +137,13 @@ pub fn walk_and_embed_assets(
|
|||
client: &Client,
|
||||
url: &str,
|
||||
node: &Handle,
|
||||
opt_no_css: bool,
|
||||
opt_no_fonts: bool,
|
||||
opt_no_frames: bool,
|
||||
opt_no_js: bool,
|
||||
opt_no_images: bool,
|
||||
opt_silent: bool,
|
||||
options: &Options,
|
||||
) {
|
||||
match node.data {
|
||||
NodeData::Document => {
|
||||
// Dig deeper
|
||||
for child in node.children.borrow().iter() {
|
||||
walk_and_embed_assets(
|
||||
cache,
|
||||
client,
|
||||
&url,
|
||||
child,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
walk_and_embed_assets(cache, client, &url, child, options);
|
||||
}
|
||||
}
|
||||
NodeData::Element {
|
||||
|
@ -245,7 +229,7 @@ pub fn walk_and_embed_assets(
|
|||
}
|
||||
}
|
||||
|
||||
if !opt_no_images && !link_href.is_empty() {
|
||||
if !options.no_images && !link_href.is_empty() {
|
||||
let link_href_full_url =
|
||||
resolve_url(&url, link_href).unwrap_or_default();
|
||||
let link_href_url_fragment =
|
||||
|
@ -255,7 +239,7 @@ pub fn walk_and_embed_assets(
|
|||
client,
|
||||
&url,
|
||||
&link_href_full_url,
|
||||
opt_silent,
|
||||
options.silent,
|
||||
) {
|
||||
Ok((
|
||||
link_href_data,
|
||||
|
@ -319,7 +303,7 @@ pub fn walk_and_embed_assets(
|
|||
}
|
||||
}
|
||||
|
||||
if !opt_no_css && !link_href.is_empty() {
|
||||
if !options.no_css && !link_href.is_empty() {
|
||||
let link_href_full_url =
|
||||
resolve_url(&url, link_href).unwrap_or_default();
|
||||
match retrieve_asset(
|
||||
|
@ -327,7 +311,7 @@ pub fn walk_and_embed_assets(
|
|||
client,
|
||||
&url,
|
||||
&link_href_full_url,
|
||||
opt_silent,
|
||||
options.silent,
|
||||
) {
|
||||
Ok((
|
||||
link_href_data,
|
||||
|
@ -343,9 +327,7 @@ pub fn walk_and_embed_assets(
|
|||
client,
|
||||
&link_href_final_url,
|
||||
&String::from_utf8_lossy(&link_href_data),
|
||||
opt_no_fonts,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
options,
|
||||
);
|
||||
let link_href_data_url = data_to_data_url(
|
||||
"text/css",
|
||||
|
@ -415,11 +397,16 @@ pub fn walk_and_embed_assets(
|
|||
}
|
||||
}
|
||||
|
||||
if !opt_no_images && !background.is_empty() {
|
||||
if !options.no_images && !background.is_empty() {
|
||||
let background_full_url = resolve_url(&url, background).unwrap_or_default();
|
||||
let background_url_fragment = get_url_fragment(background_full_url.clone());
|
||||
match retrieve_asset(cache, client, &url, &background_full_url, opt_silent)
|
||||
{
|
||||
match retrieve_asset(
|
||||
cache,
|
||||
client,
|
||||
&url,
|
||||
&background_full_url,
|
||||
options.silent,
|
||||
) {
|
||||
Ok((background_data, background_final_url, background_media_type)) => {
|
||||
let background_data_url = data_to_data_url(
|
||||
&background_media_type,
|
||||
|
@ -471,7 +458,7 @@ pub fn walk_and_embed_assets(
|
|||
}
|
||||
}
|
||||
|
||||
if opt_no_images {
|
||||
if options.no_images {
|
||||
// Add empty image src attribute
|
||||
attrs_mut.push(Attribute {
|
||||
name: QualName::new(None, ns!(), local_name!("src")),
|
||||
|
@ -496,7 +483,8 @@ pub fn walk_and_embed_assets(
|
|||
)
|
||||
.unwrap_or_default();
|
||||
let img_url_fragment = get_url_fragment(img_full_url.clone());
|
||||
match retrieve_asset(cache, client, &url, &img_full_url, opt_silent) {
|
||||
match retrieve_asset(cache, client, &url, &img_full_url, options.silent)
|
||||
{
|
||||
Ok((img_data, img_final_url, img_media_type)) => {
|
||||
let img_data_url = data_to_data_url(
|
||||
&img_media_type,
|
||||
|
@ -533,21 +521,13 @@ pub fn walk_and_embed_assets(
|
|||
attrs_mut.push(Attribute {
|
||||
name: QualName::new(None, ns!(), local_name!("srcset")),
|
||||
value: Tendril::from_slice(
|
||||
embed_srcset(
|
||||
cache,
|
||||
client,
|
||||
&url,
|
||||
&img_srcset,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
)
|
||||
.as_ref(),
|
||||
embed_srcset(cache, client, &url, &img_srcset, options).as_ref(),
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
"svg" => {
|
||||
if opt_no_images {
|
||||
if options.no_images {
|
||||
node.children.borrow_mut().clear();
|
||||
}
|
||||
}
|
||||
|
@ -573,7 +553,7 @@ pub fn walk_and_embed_assets(
|
|||
}
|
||||
}
|
||||
|
||||
if opt_no_images || input_image_src.is_empty() {
|
||||
if options.no_images || input_image_src.is_empty() {
|
||||
attrs_mut.push(Attribute {
|
||||
name: QualName::new(None, ns!(), local_name!("src")),
|
||||
value: Tendril::from_slice(if input_image_src.is_empty() {
|
||||
|
@ -592,7 +572,7 @@ pub fn walk_and_embed_assets(
|
|||
client,
|
||||
&url,
|
||||
&input_image_full_url,
|
||||
opt_silent,
|
||||
options.silent,
|
||||
) {
|
||||
Ok((
|
||||
input_image_data,
|
||||
|
@ -646,10 +626,10 @@ pub fn walk_and_embed_assets(
|
|||
}
|
||||
}
|
||||
|
||||
if !opt_no_images && !image_href.is_empty() {
|
||||
if !options.no_images && !image_href.is_empty() {
|
||||
let image_full_url = resolve_url(&url, image_href).unwrap_or_default();
|
||||
let image_url_fragment = get_url_fragment(image_full_url.clone());
|
||||
match retrieve_asset(cache, client, &url, &image_full_url, opt_silent) {
|
||||
match retrieve_asset(cache, client, &url, &image_full_url, options.silent) {
|
||||
Ok((image_data, image_final_url, image_media_type)) => {
|
||||
let image_data_url = data_to_data_url(
|
||||
&image_media_type,
|
||||
|
@ -693,7 +673,7 @@ pub fn walk_and_embed_assets(
|
|||
attr.value.push_slice(src_full_url.as_str());
|
||||
} else if attr_name.eq_ignore_ascii_case("srcset") {
|
||||
if get_node_name(&get_parent_node(&node)) == Some("picture") {
|
||||
if opt_no_images {
|
||||
if options.no_images {
|
||||
attr.value.clear();
|
||||
attr.value.push_slice(empty_image!());
|
||||
} else {
|
||||
|
@ -706,7 +686,7 @@ pub fn walk_and_embed_assets(
|
|||
client,
|
||||
&url,
|
||||
&srcset_full_url,
|
||||
opt_silent,
|
||||
options.silent,
|
||||
) {
|
||||
Ok((srcset_data, srcset_final_url, srcset_media_type)) => {
|
||||
let srcset_data_url = data_to_data_url(
|
||||
|
@ -744,7 +724,7 @@ pub fn walk_and_embed_assets(
|
|||
if attr_name.eq_ignore_ascii_case("href") {
|
||||
let attr_value = attr.value.trim();
|
||||
|
||||
if opt_no_js && attr_value.starts_with("javascript:") {
|
||||
if options.no_js && attr_value.trim().starts_with("javascript:") {
|
||||
attr.value.clear();
|
||||
// Replace with empty JS call to preserve original behavior
|
||||
attr.value.push_slice("javascript:;");
|
||||
|
@ -778,12 +758,13 @@ pub fn walk_and_embed_assets(
|
|||
}
|
||||
}
|
||||
|
||||
if opt_no_js {
|
||||
if options.no_js {
|
||||
// Empty inner content (src is already gone)
|
||||
node.children.borrow_mut().clear();
|
||||
} else if !script_src.is_empty() {
|
||||
let script_full_url = resolve_url(&url, script_src).unwrap_or_default();
|
||||
match retrieve_asset(cache, client, &url, &script_full_url, opt_silent) {
|
||||
match retrieve_asset(cache, client, &url, &script_full_url, options.silent)
|
||||
{
|
||||
Ok((script_data, script_final_url, _script_media_type)) => {
|
||||
// Only embed if we're able to validate integrity
|
||||
if script_integrity.is_empty()
|
||||
|
@ -814,22 +795,15 @@ pub fn walk_and_embed_assets(
|
|||
}
|
||||
}
|
||||
"style" => {
|
||||
if opt_no_css {
|
||||
if options.no_css {
|
||||
// Empty inner content of STYLE tags
|
||||
node.children.borrow_mut().clear();
|
||||
} else {
|
||||
for node in node.children.borrow_mut().iter_mut() {
|
||||
if let NodeData::Text { ref contents } = node.data {
|
||||
let mut tendril = contents.borrow_mut();
|
||||
let replacement = embed_css(
|
||||
cache,
|
||||
client,
|
||||
&url,
|
||||
tendril.as_ref(),
|
||||
opt_no_fonts,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
let replacement =
|
||||
embed_css(cache, client, &url, tendril.as_ref(), options);
|
||||
tendril.clear();
|
||||
tendril.push_slice(&replacement);
|
||||
}
|
||||
|
@ -855,7 +829,7 @@ pub fn walk_and_embed_assets(
|
|||
for attr in attrs_mut.iter_mut() {
|
||||
let attr_name: &str = &attr.name.local;
|
||||
if attr_name.eq_ignore_ascii_case("src") {
|
||||
if opt_no_frames {
|
||||
if options.no_frames {
|
||||
// Empty the src attribute
|
||||
attr.value.clear();
|
||||
continue;
|
||||
|
@ -870,7 +844,13 @@ pub fn walk_and_embed_assets(
|
|||
|
||||
let frame_full_url = resolve_url(&url, frame_src).unwrap_or_default();
|
||||
let frame_url_fragment = get_url_fragment(frame_full_url.clone());
|
||||
match retrieve_asset(cache, client, &url, &frame_full_url, opt_silent) {
|
||||
match retrieve_asset(
|
||||
cache,
|
||||
client,
|
||||
&url,
|
||||
&frame_full_url,
|
||||
options.silent,
|
||||
) {
|
||||
Ok((frame_data, frame_final_url, frame_media_type)) => {
|
||||
let frame_dom =
|
||||
html_to_dom(&String::from_utf8_lossy(&frame_data));
|
||||
|
@ -879,12 +859,7 @@ pub fn walk_and_embed_assets(
|
|||
client,
|
||||
&frame_final_url,
|
||||
&frame_dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
&options,
|
||||
);
|
||||
let mut frame_data: Vec<u8> = Vec::new();
|
||||
serialize(
|
||||
|
@ -931,7 +906,7 @@ pub fn walk_and_embed_assets(
|
|||
continue;
|
||||
}
|
||||
|
||||
if opt_no_images {
|
||||
if options.no_images {
|
||||
attr.value.clear();
|
||||
continue;
|
||||
}
|
||||
|
@ -945,7 +920,7 @@ pub fn walk_and_embed_assets(
|
|||
client,
|
||||
&url,
|
||||
&video_poster_full_url,
|
||||
opt_silent,
|
||||
options.silent,
|
||||
) {
|
||||
Ok((
|
||||
video_poster_data,
|
||||
|
@ -983,7 +958,7 @@ pub fn walk_and_embed_assets(
|
|||
}
|
||||
|
||||
// Process style attributes
|
||||
if opt_no_css {
|
||||
if options.no_css {
|
||||
// Get rid of style attributes
|
||||
let mut i = 0;
|
||||
while i < attrs_mut.len() {
|
||||
|
@ -1000,22 +975,15 @@ pub fn walk_and_embed_assets(
|
|||
.iter_mut()
|
||||
.filter(|a| a.name.local.as_ref().eq_ignore_ascii_case("style"))
|
||||
{
|
||||
let replacement = embed_css(
|
||||
cache,
|
||||
client,
|
||||
&url,
|
||||
attribute.value.as_ref(),
|
||||
opt_no_fonts,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
let replacement =
|
||||
embed_css(cache, client, &url, attribute.value.as_ref(), options);
|
||||
// let replacement = str!();
|
||||
attribute.value.clear();
|
||||
attribute.value.push_slice(&replacement);
|
||||
}
|
||||
}
|
||||
|
||||
if opt_no_js {
|
||||
if options.no_js {
|
||||
// Get rid of JS event attributes
|
||||
let mut js_attr_indexes = Vec::new();
|
||||
for (i, attr) in attrs_mut.iter_mut().enumerate() {
|
||||
|
@ -1031,22 +999,11 @@ pub fn walk_and_embed_assets(
|
|||
|
||||
// Dig deeper
|
||||
for child in node.children.borrow().iter() {
|
||||
walk_and_embed_assets(
|
||||
cache,
|
||||
client,
|
||||
&url,
|
||||
child,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
walk_and_embed_assets(cache, client, &url, child, options);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// Note: in case of opt_no_js being set to true, there's no need to worry about
|
||||
// Note: in case of options.no_js being set to true, there's no need to worry about
|
||||
// getting rid of comments that may contain scripts, e.g. <!--[if IE]><script>...
|
||||
// since that's not part of W3C standard and therefore gets ignored
|
||||
// by browsers other than IE [5, 9]
|
||||
|
@ -1073,15 +1030,7 @@ fn get_child_node_by_name(handle: &Handle, node_name: &str) -> Handle {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn stringify_document(
|
||||
handle: &Handle,
|
||||
opt_no_css: bool,
|
||||
opt_no_fonts: bool,
|
||||
opt_no_frames: bool,
|
||||
opt_no_js: bool,
|
||||
opt_no_images: bool,
|
||||
opt_isolate: bool,
|
||||
) -> String {
|
||||
pub fn stringify_document(handle: &Handle, options: &Options) -> String {
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, handle, SerializeOpts::default())
|
||||
.expect("unable to serialize DOM into buffer");
|
||||
|
@ -1089,20 +1038,19 @@ pub fn stringify_document(
|
|||
let mut result = String::from_utf8(buf).unwrap();
|
||||
|
||||
// Take care of CSP
|
||||
if opt_isolate || opt_no_css || opt_no_fonts || opt_no_frames || opt_no_js || opt_no_images {
|
||||
if options.isolate
|
||||
|| options.no_css
|
||||
|| options.no_fonts
|
||||
|| options.no_frames
|
||||
|| options.no_js
|
||||
|| options.no_images
|
||||
{
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
let mut dom = html_to_dom(&result);
|
||||
let doc = dom.get_document();
|
||||
let html = get_child_node_by_name(&doc, "html");
|
||||
let head = get_child_node_by_name(&html, "head");
|
||||
let csp_content: String = csp(
|
||||
opt_isolate,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
);
|
||||
let csp_content: String = csp(options);
|
||||
|
||||
let meta = dom.create_element(
|
||||
QualName::new(None, ns!(), local_name!("meta")),
|
||||
|
@ -1136,38 +1084,31 @@ pub fn stringify_document(
|
|||
result
|
||||
}
|
||||
|
||||
pub fn csp(
|
||||
opt_isolate: bool,
|
||||
opt_no_css: bool,
|
||||
opt_no_fonts: bool,
|
||||
opt_no_frames: bool,
|
||||
opt_no_js: bool,
|
||||
opt_no_images: bool,
|
||||
) -> String {
|
||||
pub fn csp(options: &Options) -> String {
|
||||
let mut string_list = vec![];
|
||||
|
||||
if opt_isolate {
|
||||
if options.isolate {
|
||||
string_list.push("default-src 'unsafe-inline' data:;");
|
||||
}
|
||||
|
||||
if opt_no_css {
|
||||
if options.no_css {
|
||||
string_list.push("style-src 'none';");
|
||||
}
|
||||
|
||||
if opt_no_fonts {
|
||||
if options.no_fonts {
|
||||
string_list.push("font-src 'none';");
|
||||
}
|
||||
|
||||
if opt_no_frames {
|
||||
if options.no_frames {
|
||||
string_list.push("frame-src 'none';");
|
||||
string_list.push("child-src 'none';");
|
||||
}
|
||||
|
||||
if opt_no_js {
|
||||
if options.no_js {
|
||||
string_list.push("script-src 'none';");
|
||||
}
|
||||
|
||||
if opt_no_images {
|
||||
if options.no_images {
|
||||
// Note: data: is needed for transparent pixels
|
||||
string_list.push("img-src data:;");
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
#[macro_use]
|
||||
extern crate clap;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
pub mod css;
|
||||
pub mod html;
|
||||
pub mod js;
|
||||
pub mod opts;
|
||||
pub mod url;
|
||||
pub mod utils;
|
||||
|
||||
|
|
47
src/main.rs
47
src/main.rs
|
@ -9,16 +9,12 @@ use std::process;
|
|||
use std::time::Duration;
|
||||
|
||||
use monolith::html::{html_to_dom, metadata_tag, stringify_document, walk_and_embed_assets};
|
||||
use monolith::opts::Options;
|
||||
use monolith::url::{data_url_to_data, is_data_url, is_file_url, is_http_url};
|
||||
use monolith::utils::retrieve_asset;
|
||||
|
||||
mod args;
|
||||
mod macros;
|
||||
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
use crate::args::AppArgs;
|
||||
|
||||
enum Output {
|
||||
Stdout(io::Stdout),
|
||||
File(fs::File),
|
||||
|
@ -48,8 +44,8 @@ impl Output {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
let app_args = AppArgs::get();
|
||||
let original_target: &str = &app_args.target;
|
||||
let options = Options::from_args();
|
||||
let original_target: &str = &options.target;
|
||||
let target_url: &str;
|
||||
let base_url;
|
||||
let dom;
|
||||
|
@ -89,30 +85,30 @@ fn main() {
|
|||
}
|
||||
|
||||
// Define output
|
||||
let mut output = Output::new(&app_args.output).expect("Could not prepare output");
|
||||
let mut output = Output::new(&options.output).expect("Could not prepare output");
|
||||
|
||||
// Initialize client
|
||||
let mut cache = HashMap::new();
|
||||
let mut header_map = HeaderMap::new();
|
||||
header_map.insert(
|
||||
USER_AGENT,
|
||||
HeaderValue::from_str(&app_args.user_agent).expect("Invalid User-Agent header specified"),
|
||||
HeaderValue::from_str(&options.user_agent).expect("Invalid User-Agent header specified"),
|
||||
);
|
||||
let timeout: u64 = if app_args.timeout > 0 {
|
||||
app_args.timeout
|
||||
let timeout: u64 = if options.timeout > 0 {
|
||||
options.timeout
|
||||
} else {
|
||||
std::u64::MAX / 4
|
||||
};
|
||||
let client = Client::builder()
|
||||
.timeout(Duration::from_secs(timeout))
|
||||
.danger_accept_invalid_certs(app_args.insecure)
|
||||
.danger_accept_invalid_certs(options.insecure)
|
||||
.default_headers(header_map)
|
||||
.build()
|
||||
.expect("Failed to initialize HTTP client");
|
||||
|
||||
// Retrieve target document
|
||||
if is_file_url(target_url) || is_http_url(target_url) {
|
||||
match retrieve_asset(&mut cache, &client, target_url, target_url, app_args.silent) {
|
||||
match retrieve_asset(&mut cache, &client, target_url, target_url, options.silent) {
|
||||
Ok((data, final_url, _media_type)) => {
|
||||
base_url = final_url;
|
||||
dom = html_to_dom(&String::from_utf8_lossy(&data));
|
||||
|
@ -135,32 +131,13 @@ fn main() {
|
|||
}
|
||||
|
||||
// Embed remote assets
|
||||
walk_and_embed_assets(
|
||||
&mut cache,
|
||||
&client,
|
||||
&base_url,
|
||||
&dom.document,
|
||||
app_args.no_css,
|
||||
app_args.no_fonts,
|
||||
app_args.no_frames,
|
||||
app_args.no_js,
|
||||
app_args.no_images,
|
||||
app_args.silent,
|
||||
);
|
||||
walk_and_embed_assets(&mut cache, &client, &base_url, &dom.document, &options);
|
||||
|
||||
// Serialize DOM tree
|
||||
let mut result: String = stringify_document(
|
||||
&dom.document,
|
||||
app_args.no_css,
|
||||
app_args.no_fonts,
|
||||
app_args.no_frames,
|
||||
app_args.no_js,
|
||||
app_args.no_images,
|
||||
app_args.isolate,
|
||||
);
|
||||
let mut result: String = stringify_document(&dom.document, &options);
|
||||
|
||||
// Add metadata tag
|
||||
if !app_args.no_metadata {
|
||||
if !options.no_metadata {
|
||||
let metadata_comment = metadata_tag(&base_url);
|
||||
result.insert_str(0, &metadata_comment);
|
||||
if metadata_comment.len() > 0 {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use clap::{App, Arg};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct AppArgs {
|
||||
pub struct Options {
|
||||
pub target: String,
|
||||
pub no_css: bool,
|
||||
pub no_fonts: bool,
|
||||
|
@ -21,8 +21,8 @@ const DEFAULT_NETWORK_TIMEOUT: u64 = 120;
|
|||
const DEFAULT_USER_AGENT: &str =
|
||||
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0";
|
||||
|
||||
impl AppArgs {
|
||||
pub fn get() -> AppArgs {
|
||||
impl Options {
|
||||
pub fn from_args() -> Options {
|
||||
let app = App::new(env!("CARGO_PKG_NAME"))
|
||||
.version(crate_version!())
|
||||
.author(crate_authors!("\n"))
|
||||
|
@ -34,7 +34,7 @@ impl AppArgs {
|
|||
.index(1)
|
||||
.help("URL or file path"),
|
||||
)
|
||||
// .args_from_usage("-a, --include-audio 'Removes audio sources'")
|
||||
// .args_from_usage("-a, --no-audio 'Removes audio sources'")
|
||||
.args_from_usage("-c, --no-css 'Removes CSS'")
|
||||
.args_from_usage("-f, --no-frames 'Removes frames and iframes'")
|
||||
.args_from_usage("-F, --no-fonts 'Removes fonts'")
|
||||
|
@ -47,33 +47,35 @@ impl AppArgs {
|
|||
.args_from_usage("-s, --silent 'Suppresses verbosity'")
|
||||
.args_from_usage("-t, --timeout=[60] 'Adjusts network request timeout'")
|
||||
.args_from_usage("-u, --user-agent=[Firefox] 'Sets custom User-Agent string'")
|
||||
// .args_from_usage("-v, --include-video 'Removes video sources'")
|
||||
// .args_from_usage("-v, --no-video 'Removes video sources'")
|
||||
.get_matches();
|
||||
let mut app_args = AppArgs::default();
|
||||
let mut options: Options = Options::default();
|
||||
|
||||
// Process the command
|
||||
app_args.target = app
|
||||
options.target = app
|
||||
.value_of("target")
|
||||
.expect("please set target")
|
||||
.to_string();
|
||||
app_args.no_css = app.is_present("no-css");
|
||||
app_args.no_fonts = app.is_present("no-fonts");
|
||||
app_args.no_frames = app.is_present("no-frames");
|
||||
app_args.no_images = app.is_present("no-images");
|
||||
app_args.no_js = app.is_present("no-js");
|
||||
app_args.insecure = app.is_present("insecure");
|
||||
app_args.no_metadata = app.is_present("no-metadata");
|
||||
app_args.isolate = app.is_present("isolate");
|
||||
app_args.silent = app.is_present("silent");
|
||||
app_args.timeout = app
|
||||
options.no_css = app.is_present("no-css");
|
||||
options.no_frames = app.is_present("no-frames");
|
||||
options.no_fonts = app.is_present("no-fonts");
|
||||
options.no_images = app.is_present("no-images");
|
||||
options.isolate = app.is_present("isolate");
|
||||
options.no_js = app.is_present("no-js");
|
||||
options.insecure = app.is_present("insecure");
|
||||
options.no_metadata = app.is_present("no-metadata");
|
||||
options.output = app.value_of("output").unwrap_or("").to_string();
|
||||
options.silent = app.is_present("silent");
|
||||
options.timeout = app
|
||||
.value_of("timeout")
|
||||
.unwrap_or(&DEFAULT_NETWORK_TIMEOUT.to_string())
|
||||
.parse::<u64>()
|
||||
.unwrap();
|
||||
app_args.output = app.value_of("output").unwrap_or("").to_string();
|
||||
app_args.user_agent = app
|
||||
options.user_agent = app
|
||||
.value_of("user-agent")
|
||||
.unwrap_or(DEFAULT_USER_AGENT)
|
||||
.to_string();
|
||||
app_args
|
||||
|
||||
options
|
||||
}
|
||||
}
|
|
@ -7,25 +7,26 @@
|
|||
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::css;
|
||||
use reqwest::blocking::Client;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::css;
|
||||
use crate::opts::Options;
|
||||
|
||||
#[test]
|
||||
fn empty_input() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
let options = Options::default();
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(cache, &client, "", "", false, false, false,),
|
||||
""
|
||||
);
|
||||
assert_eq!(css::embed_css(cache, &client, "", "", &options), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trim_if_empty() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
let options = Options::default();
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(
|
||||
|
@ -33,9 +34,7 @@ mod passing {
|
|||
&client,
|
||||
"https://doesntmatter.local/",
|
||||
"\t \t ",
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
&options,
|
||||
),
|
||||
""
|
||||
);
|
||||
|
@ -45,6 +44,9 @@ mod passing {
|
|||
fn style_exclude_unquoted_images() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
let mut options = Options::default();
|
||||
options.no_images = true;
|
||||
options.silent = true;
|
||||
|
||||
const STYLE: &str = "/* border: none;*/\
|
||||
background-image: url(https://somewhere.com/bg.png); \
|
||||
|
@ -60,9 +62,7 @@ mod passing {
|
|||
&client,
|
||||
"https://doesntmatter.local/",
|
||||
&STYLE,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
&options,
|
||||
),
|
||||
format!(
|
||||
"/* border: none;*/\
|
||||
|
@ -81,6 +81,9 @@ mod passing {
|
|||
fn style_exclude_single_quoted_images() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
let mut options = Options::default();
|
||||
options.no_images = true;
|
||||
options.silent = true;
|
||||
|
||||
const STYLE: &str = "/* border: none;*/\
|
||||
background-image: url('https://somewhere.com/bg.png'); \
|
||||
|
@ -91,7 +94,7 @@ mod passing {
|
|||
height: calc(100vh - 10pt)";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(cache, &client, "", &STYLE, false, true, true,),
|
||||
css::embed_css(cache, &client, "", &STYLE, &options),
|
||||
format!(
|
||||
"/* border: none;*/\
|
||||
background-image: url('{empty_image}'); \
|
||||
|
@ -109,6 +112,8 @@ mod passing {
|
|||
fn style_block() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
let mut options = Options::default();
|
||||
options.silent = true;
|
||||
|
||||
const CSS: &str = "\
|
||||
#id.class-name:not(:nth-child(3n+0)) {\n \
|
||||
|
@ -119,7 +124,7 @@ mod passing {
|
|||
html > body {}";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(cache, &client, "file:///", &CSS, false, false, true,),
|
||||
css::embed_css(cache, &client, "file:///", &CSS, &options),
|
||||
CSS
|
||||
);
|
||||
}
|
||||
|
@ -128,6 +133,8 @@ mod passing {
|
|||
fn attribute_selectors() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
let mut options = Options::default();
|
||||
options.silent = true;
|
||||
|
||||
const CSS: &str = "\
|
||||
[data-value] {
|
||||
|
@ -159,16 +166,15 @@ mod passing {
|
|||
}
|
||||
";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(cache, &client, "", &CSS, false, false, false,),
|
||||
CSS
|
||||
);
|
||||
assert_eq!(css::embed_css(cache, &client, "", &CSS, &options), CSS);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_string() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
let mut options = Options::default();
|
||||
options.silent = true;
|
||||
|
||||
const CSS: &str = "\
|
||||
@charset 'UTF-8';\n\
|
||||
|
@ -184,9 +190,7 @@ mod passing {
|
|||
&client,
|
||||
"https://doesntmatter.local/",
|
||||
&CSS,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
&options,
|
||||
),
|
||||
"\
|
||||
@charset 'UTF-8';\n\
|
||||
|
@ -202,6 +206,8 @@ mod passing {
|
|||
fn hash_urls() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
let mut options = Options::default();
|
||||
options.silent = true;
|
||||
|
||||
const CSS: &str = "\
|
||||
body {\n \
|
||||
|
@ -219,9 +225,7 @@ mod passing {
|
|||
&client,
|
||||
"https://doesntmatter.local/",
|
||||
&CSS,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
&options,
|
||||
),
|
||||
CSS
|
||||
);
|
||||
|
@ -231,6 +235,8 @@ mod passing {
|
|||
fn transform_percentages_and_degrees() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
let mut options = Options::default();
|
||||
options.silent = true;
|
||||
|
||||
const CSS: &str = "\
|
||||
div {\n \
|
||||
|
@ -246,9 +252,7 @@ mod passing {
|
|||
&client,
|
||||
"https://doesntmatter.local/",
|
||||
&CSS,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
&options,
|
||||
),
|
||||
CSS
|
||||
);
|
||||
|
@ -258,6 +262,8 @@ mod passing {
|
|||
fn unusual_indents() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
let mut options = Options::default();
|
||||
options.silent = true;
|
||||
|
||||
const CSS: &str = "\
|
||||
.is\\:good:hover {\n \
|
||||
|
@ -275,9 +281,7 @@ mod passing {
|
|||
&client,
|
||||
"https://doesntmatter.local/",
|
||||
&CSS,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
&options,
|
||||
),
|
||||
CSS
|
||||
);
|
||||
|
@ -287,6 +291,9 @@ mod passing {
|
|||
fn exclude_fonts() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
let mut options = Options::default();
|
||||
options.no_fonts = true;
|
||||
options.silent = true;
|
||||
|
||||
const CSS: &str = "\
|
||||
@font-face {\n \
|
||||
|
@ -328,9 +335,7 @@ mod passing {
|
|||
&client,
|
||||
"https://doesntmatter.local/",
|
||||
&CSS,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
&options,
|
||||
),
|
||||
CSS_OUT
|
||||
);
|
||||
|
|
|
@ -8,143 +8,72 @@
|
|||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::html;
|
||||
use crate::opts::Options;
|
||||
|
||||
#[test]
|
||||
fn isolated() {
|
||||
let opt_isolate: bool = true;
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let csp_content = html::csp(
|
||||
opt_isolate,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
);
|
||||
let mut options = Options::default();
|
||||
options.isolate = true;
|
||||
let csp_content = html::csp(&options);
|
||||
|
||||
assert_eq!(csp_content, "default-src 'unsafe-inline' data:;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_css() {
|
||||
let opt_isolate: bool = false;
|
||||
let opt_no_css: bool = true;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let csp_content = html::csp(
|
||||
opt_isolate,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
);
|
||||
let mut options = Options::default();
|
||||
options.no_css = true;
|
||||
let csp_content = html::csp(&options);
|
||||
|
||||
assert_eq!(csp_content, "style-src 'none';");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_fonts() {
|
||||
let opt_isolate: bool = false;
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = true;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let csp_content = html::csp(
|
||||
opt_isolate,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
);
|
||||
let mut options = Options::default();
|
||||
options.no_fonts = true;
|
||||
let csp_content = html::csp(&options);
|
||||
|
||||
assert_eq!(csp_content, "font-src 'none';");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_frames() {
|
||||
let opt_isolate: bool = false;
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = true;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let csp_content = html::csp(
|
||||
opt_isolate,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
);
|
||||
let mut options = Options::default();
|
||||
options.no_frames = true;
|
||||
let csp_content = html::csp(&options);
|
||||
|
||||
assert_eq!(csp_content, "frame-src 'none'; child-src 'none';");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_js() {
|
||||
let opt_isolate: bool = false;
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = true;
|
||||
let opt_no_images: bool = false;
|
||||
let csp_content = html::csp(
|
||||
opt_isolate,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
);
|
||||
let mut options = Options::default();
|
||||
options.no_js = true;
|
||||
let csp_content = html::csp(&options);
|
||||
|
||||
assert_eq!(csp_content, "script-src 'none';");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_image() {
|
||||
let opt_isolate: bool = false;
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = true;
|
||||
let csp_content = html::csp(
|
||||
opt_isolate,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
);
|
||||
fn no_images() {
|
||||
let mut options = Options::default();
|
||||
options.no_images = true;
|
||||
let csp_content = html::csp(&options);
|
||||
|
||||
assert_eq!(csp_content, "img-src data:;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all() {
|
||||
let opt_isolate: bool = true;
|
||||
let opt_no_css: bool = true;
|
||||
let opt_no_fonts: bool = true;
|
||||
let opt_no_frames: bool = true;
|
||||
let opt_no_js: bool = true;
|
||||
let opt_no_images: bool = true;
|
||||
let csp_content = html::csp(
|
||||
opt_isolate,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
);
|
||||
let mut options = Options::default();
|
||||
options.isolate = true;
|
||||
options.no_css = true;
|
||||
options.no_fonts = true;
|
||||
options.no_frames = true;
|
||||
options.no_js = true;
|
||||
options.no_images = true;
|
||||
let csp_content = html::csp(&options);
|
||||
|
||||
assert_eq!(csp_content, "default-src 'unsafe-inline' data:; style-src 'none'; font-src 'none'; frame-src 'none'; child-src 'none'; script-src 'none'; img-src data:;");
|
||||
}
|
||||
|
|
|
@ -7,16 +7,21 @@
|
|||
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::html;
|
||||
use reqwest::blocking::Client;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::html;
|
||||
use crate::opts::Options;
|
||||
|
||||
#[test]
|
||||
fn replace_with_empty_images() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
let srcset_value = "small.png 1x, large.png 2x";
|
||||
let embedded_css = html::embed_srcset(cache, &client, "", &srcset_value, true, true);
|
||||
let mut options = Options::default();
|
||||
options.no_images = true;
|
||||
options.silent = true;
|
||||
let embedded_css = html::embed_srcset(cache, &client, "", &srcset_value, &options);
|
||||
|
||||
assert_eq!(
|
||||
format!("{} 1x, {} 2x", empty_image!(), empty_image!()),
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::html;
|
||||
use html5ever::rcdom::{Handle, NodeData};
|
||||
|
||||
use crate::html;
|
||||
|
||||
#[test]
|
||||
fn get_node_name() {
|
||||
let html = "<!doctype html><html><HEAD></HEAD><body><div><P></P></div></body></html>";
|
||||
|
|
|
@ -8,29 +8,16 @@
|
|||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::html;
|
||||
use crate::opts::Options;
|
||||
|
||||
#[test]
|
||||
fn div_as_root_element() {
|
||||
let html = "<div><script src=\"some.js\"></script></div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_isolate: bool = false;
|
||||
let options = Options::default();
|
||||
|
||||
assert_eq!(
|
||||
html::stringify_document(
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_isolate,
|
||||
),
|
||||
html::stringify_document(&dom.document, &options),
|
||||
"<html><head></head><body><div><script src=\"some.js\"></script></div></body></html>"
|
||||
);
|
||||
}
|
||||
|
@ -42,23 +29,13 @@ mod passing {
|
|||
<meta http-equiv=\"Content-Security-Policy\" content=\"default-src https:\">\
|
||||
<div><script src=\"some.js\"></script></div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_isolate: bool = true;
|
||||
let mut options = Options::default();
|
||||
options.isolate = true;
|
||||
|
||||
assert_eq!(
|
||||
html::stringify_document(
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_isolate,
|
||||
&options
|
||||
),
|
||||
"<html>\
|
||||
<head>\
|
||||
|
@ -83,24 +60,11 @@ mod passing {
|
|||
<link rel=\"stylesheet\" href=\"main.css\"/>\
|
||||
<div style=\"display: none;\"></div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
|
||||
let opt_no_css: bool = true;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_isolate: bool = false;
|
||||
let mut options = Options::default();
|
||||
options.no_css = true;
|
||||
|
||||
assert_eq!(
|
||||
html::stringify_document(
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_isolate,
|
||||
),
|
||||
html::stringify_document(&dom.document, &options),
|
||||
"<!DOCTYPE html>\
|
||||
<html>\
|
||||
<head>\
|
||||
|
@ -120,23 +84,13 @@ mod passing {
|
|||
<link rel=\"something\"/>\
|
||||
<div><script src=\"some.js\"></script></div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = true;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_isolate: bool = false;
|
||||
let mut options = Options::default();
|
||||
options.no_frames = true;
|
||||
|
||||
assert_eq!(
|
||||
html::stringify_document(
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_isolate,
|
||||
&options
|
||||
),
|
||||
"<!DOCTYPE html>\
|
||||
<html>\
|
||||
|
@ -162,23 +116,18 @@ mod passing {
|
|||
<iframe src=\"some.html\"></iframe>\
|
||||
</div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
|
||||
let opt_isolate: bool = true;
|
||||
let opt_no_css: bool = true;
|
||||
let opt_no_fonts: bool = true;
|
||||
let opt_no_frames: bool = true;
|
||||
let opt_no_js: bool = true;
|
||||
let opt_no_images: bool = true;
|
||||
let mut options = Options::default();
|
||||
options.isolate = true;
|
||||
options.no_css = true;
|
||||
options.no_fonts = true;
|
||||
options.no_frames = true;
|
||||
options.no_js = true;
|
||||
options.no_images = true;
|
||||
|
||||
assert_eq!(
|
||||
html::stringify_document(
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_isolate,
|
||||
&options
|
||||
),
|
||||
"<!DOCTYPE html>\
|
||||
<html>\
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::html;
|
||||
use html5ever::serialize::{serialize, SerializeOpts};
|
||||
use reqwest::blocking::Client;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::html;
|
||||
use crate::opts::Options;
|
||||
|
||||
#[test]
|
||||
fn basic() {
|
||||
let cache = &mut HashMap::new();
|
||||
|
@ -20,27 +22,12 @@ mod passing {
|
|||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
let mut options = Options::default();
|
||||
options.silent = true;
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
@ -58,27 +45,12 @@ mod passing {
|
|||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
let mut options = Options::default();
|
||||
options.silent = true;
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
@ -96,27 +68,12 @@ mod passing {
|
|||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
let mut options = Options::default();
|
||||
options.silent = true;
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
@ -136,26 +93,13 @@ mod passing {
|
|||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let opt_no_css: bool = true;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
let mut options = Options::default();
|
||||
options.no_css = true;
|
||||
options.silent = true;
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
@ -182,27 +126,13 @@ mod passing {
|
|||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = true;
|
||||
let opt_silent = true;
|
||||
let mut options = Options::default();
|
||||
options.no_images = true;
|
||||
options.silent = true;
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
@ -233,27 +163,13 @@ mod passing {
|
|||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = true;
|
||||
let opt_silent = true;
|
||||
let mut options = Options::default();
|
||||
options.no_images = true;
|
||||
options.silent = true;
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
@ -271,26 +187,13 @@ mod passing {
|
|||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = true;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
let mut options = Options::default();
|
||||
options.no_frames = true;
|
||||
options.silent = true;
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
@ -308,26 +211,13 @@ mod passing {
|
|||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = true;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
let mut options = Options::default();
|
||||
options.no_frames = true;
|
||||
options.silent = true;
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
@ -348,27 +238,13 @@ mod passing {
|
|||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = true;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
let mut options = Options::default();
|
||||
options.no_js = true;
|
||||
options.silent = true;
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
@ -388,26 +264,17 @@ mod passing {
|
|||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
let opt_no_css: bool = true;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = true;
|
||||
let opt_no_js: bool = true;
|
||||
let opt_no_images: bool = true;
|
||||
let opt_silent = true;
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
let mut options = Options::default();
|
||||
options.no_css = true;
|
||||
options.no_frames = true;
|
||||
options.no_js = true;
|
||||
options.no_images = true;
|
||||
options.silent = true;
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
@ -433,26 +300,17 @@ mod passing {
|
|||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
let opt_no_css: bool = true;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = true;
|
||||
let opt_no_js: bool = true;
|
||||
let opt_no_images: bool = true;
|
||||
let opt_silent = true;
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
let mut options = Options::default();
|
||||
options.no_css = true;
|
||||
options.no_frames = true;
|
||||
options.no_js = true;
|
||||
options.no_images = true;
|
||||
options.silent = true;
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
|
|
@ -3,4 +3,5 @@ mod css;
|
|||
mod html;
|
||||
mod js;
|
||||
mod macros;
|
||||
mod opts;
|
||||
mod utils;
|
||||
|
|
30
src/tests/opts.rs
Normal file
30
src/tests/opts.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
// ██╔═══╝ ██╔══██║╚════██║╚════██║██║██║╚██╗██║██║ ██║
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::opts::Options;
|
||||
|
||||
#[test]
|
||||
fn defaults() {
|
||||
let options: Options = Options::default();
|
||||
|
||||
assert_eq!(options.target, str!());
|
||||
assert_eq!(options.no_css, false);
|
||||
assert_eq!(options.no_frames, false);
|
||||
assert_eq!(options.no_fonts, false);
|
||||
assert_eq!(options.no_images, false);
|
||||
assert_eq!(options.isolate, false);
|
||||
assert_eq!(options.no_js, false);
|
||||
assert_eq!(options.insecure, false);
|
||||
assert_eq!(options.no_metadata, false);
|
||||
assert_eq!(options.output, str!());
|
||||
assert_eq!(options.silent, false);
|
||||
assert_eq!(options.timeout, 0);
|
||||
assert_eq!(options.user_agent, "");
|
||||
}
|
||||
}
|
|
@ -7,9 +7,10 @@
|
|||
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::url;
|
||||
use url::ParseError;
|
||||
|
||||
use crate::url;
|
||||
|
||||
#[test]
|
||||
fn from_https_to_level_up_relative() -> Result<(), ParseError> {
|
||||
let resolved_url =
|
||||
|
|
|
@ -86,10 +86,11 @@ mod passing {
|
|||
|
||||
#[cfg(test)]
|
||||
mod failing {
|
||||
use crate::utils;
|
||||
use reqwest::blocking::Client;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::utils;
|
||||
|
||||
#[test]
|
||||
fn read_local_file_with_data_url_parent() {
|
||||
let cache = &mut HashMap::new();
|
||||
|
|
Loading…
Reference in a new issue