implement support for BASE tag
This commit is contained in:
parent
03cdc0e0b2
commit
1d6392cb28
2 changed files with 102 additions and 6 deletions
92
src/html.rs
92
src/html.rs
|
@ -29,6 +29,31 @@ struct SrcSetItem<'a> {
|
||||||
|
|
||||||
const ICON_VALUES: &[&str] = &["icon", "shortcut icon"];
|
const ICON_VALUES: &[&str] = &["icon", "shortcut icon"];
|
||||||
|
|
||||||
|
pub fn add_base_tag(document: &Handle, url: String) -> RcDom {
|
||||||
|
let mut buf: Vec<u8> = Vec::new();
|
||||||
|
serialize(&mut buf, document, SerializeOpts::default())
|
||||||
|
.expect("unable to serialize DOM into buffer");
|
||||||
|
let result = String::from_utf8(buf).unwrap();
|
||||||
|
|
||||||
|
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 favicon_node = dom.create_element(
|
||||||
|
QualName::new(None, ns!(), local_name!("base")),
|
||||||
|
vec![Attribute {
|
||||||
|
name: QualName::new(None, ns!(), local_name!("href")),
|
||||||
|
value: format_tendril!("{}", url),
|
||||||
|
}],
|
||||||
|
Default::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Insert BASE tag into HEAD
|
||||||
|
head.children.borrow_mut().push(favicon_node.clone());
|
||||||
|
|
||||||
|
dom
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_favicon(document: &Handle, favicon_data_url: String) -> RcDom {
|
pub fn add_favicon(document: &Handle, favicon_data_url: String) -> RcDom {
|
||||||
let mut buf: Vec<u8> = Vec::new();
|
let mut buf: Vec<u8> = Vec::new();
|
||||||
serialize(&mut buf, document, SerializeOpts::default())
|
serialize(&mut buf, document, SerializeOpts::default())
|
||||||
|
@ -54,7 +79,7 @@ pub fn add_favicon(document: &Handle, favicon_data_url: String) -> RcDom {
|
||||||
Default::default(),
|
Default::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Append favicon node to HEAD
|
// Insert favicon LINK tag into HEAD
|
||||||
head.children.borrow_mut().push(favicon_node.clone());
|
head.children.borrow_mut().push(favicon_node.clone());
|
||||||
|
|
||||||
dom
|
dom
|
||||||
|
@ -205,6 +230,56 @@ pub fn has_proper_integrity(data: &[u8], integrity: &str) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_base_tag(handle: &Handle) -> bool {
|
||||||
|
let mut found_base_tag: bool = false;
|
||||||
|
|
||||||
|
match handle.data {
|
||||||
|
NodeData::Document => {
|
||||||
|
// Dig deeper
|
||||||
|
for child in handle.children.borrow().iter() {
|
||||||
|
if has_base_tag(child) {
|
||||||
|
found_base_tag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NodeData::Element {
|
||||||
|
ref name,
|
||||||
|
ref attrs,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
match name.local.as_ref() {
|
||||||
|
"base" => {
|
||||||
|
let attrs_mut = &mut attrs.borrow_mut();
|
||||||
|
|
||||||
|
for attr in attrs_mut.iter_mut() {
|
||||||
|
if &attr.name.local == "href" {
|
||||||
|
if !attr.value.trim().is_empty() {
|
||||||
|
found_base_tag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found_base_tag {
|
||||||
|
// Dig deeper
|
||||||
|
for child in handle.children.borrow().iter() {
|
||||||
|
if has_base_tag(child) {
|
||||||
|
found_base_tag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
found_base_tag
|
||||||
|
}
|
||||||
|
|
||||||
pub fn has_favicon(handle: &Handle) -> bool {
|
pub fn has_favicon(handle: &Handle) -> bool {
|
||||||
let mut found_favicon: bool = false;
|
let mut found_favicon: bool = false;
|
||||||
|
|
||||||
|
@ -600,6 +675,7 @@ pub fn walk_and_embed_assets(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LinkType::Unknown => {
|
LinkType::Unknown => {
|
||||||
|
// Make sure that all other LINKs' href attributes are full URLs
|
||||||
for attr in attrs_mut.iter_mut() {
|
for attr in attrs_mut.iter_mut() {
|
||||||
let attr_name: &str = &attr.name.local;
|
let attr_name: &str = &attr.name.local;
|
||||||
if attr_name.eq_ignore_ascii_case("href") {
|
if attr_name.eq_ignore_ascii_case("href") {
|
||||||
|
@ -612,6 +688,20 @@ pub fn walk_and_embed_assets(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"base" => {
|
||||||
|
if is_http_url(url) {
|
||||||
|
// Ensure BASE href is a full URL, not a relative one
|
||||||
|
for attr in attrs_mut.iter_mut() {
|
||||||
|
let attr_name: &str = &attr.name.local;
|
||||||
|
if attr_name.eq_ignore_ascii_case("href") {
|
||||||
|
let href_full_url =
|
||||||
|
resolve_url(&url, attr.value.trim()).unwrap_or_default();
|
||||||
|
attr.value.clear();
|
||||||
|
attr.value.push_slice(&href_full_url.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
"body" => {
|
"body" => {
|
||||||
// Find and remove background attribute(s), keep value of the last found one
|
// Find and remove background attribute(s), keep value of the last found one
|
||||||
let mut background: String = str!();
|
let mut background: String = str!();
|
||||||
|
|
16
src/main.rs
16
src/main.rs
|
@ -9,7 +9,8 @@ use std::process;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use monolith::html::{
|
use monolith::html::{
|
||||||
add_favicon, has_favicon, html_to_dom, metadata_tag, stringify_document, walk_and_embed_assets,
|
add_base_tag, add_favicon, has_base_tag, has_favicon, html_to_dom, metadata_tag,
|
||||||
|
stringify_document, walk_and_embed_assets,
|
||||||
};
|
};
|
||||||
use monolith::opts::Options;
|
use monolith::opts::Options;
|
||||||
use monolith::url::{
|
use monolith::url::{
|
||||||
|
@ -141,6 +142,14 @@ fn main() {
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Embed remote assets
|
||||||
|
walk_and_embed_assets(&mut cache, &client, &base_url, &dom.document, &options, 0);
|
||||||
|
|
||||||
|
// Take care of BASE tag
|
||||||
|
if is_http_url(base_url.clone()) && !has_base_tag(&dom.document) {
|
||||||
|
dom = add_base_tag(&dom.document, base_url.clone());
|
||||||
|
}
|
||||||
|
|
||||||
// Request and embed /favicon.ico (unless it's already linked in the document)
|
// Request and embed /favicon.ico (unless it's already linked in the document)
|
||||||
if !options.no_images && is_http_url(target_url) && !has_favicon(&dom.document) {
|
if !options.no_images && is_http_url(target_url) && !has_favicon(&dom.document) {
|
||||||
let favicon_ico_url: String = resolve_url(&base_url, "/favicon.ico").unwrap();
|
let favicon_ico_url: String = resolve_url(&base_url, "/favicon.ico").unwrap();
|
||||||
|
@ -163,15 +172,12 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Embed remote assets
|
|
||||||
walk_and_embed_assets(&mut cache, &client, &base_url, &dom.document, &options, 0);
|
|
||||||
|
|
||||||
// Serialize DOM tree
|
// Serialize DOM tree
|
||||||
let mut result: String = stringify_document(&dom.document, &options);
|
let mut result: String = stringify_document(&dom.document, &options);
|
||||||
|
|
||||||
// Add metadata tag
|
// Add metadata tag
|
||||||
if !options.no_metadata {
|
if !options.no_metadata {
|
||||||
let metadata_comment = metadata_tag(&base_url);
|
let metadata_comment: String = metadata_tag(&base_url);
|
||||||
result.insert_str(0, &metadata_comment);
|
result.insert_str(0, &metadata_comment);
|
||||||
if metadata_comment.len() > 0 {
|
if metadata_comment.len() > 0 {
|
||||||
result.insert_str(metadata_comment.len(), "\n");
|
result.insert_str(metadata_comment.len(), "\n");
|
||||||
|
|
Loading…
Reference in a new issue