magiclen / html-minifier

This library can help you generate and minify your HTML code at the same time. It also supports to minify JS and CSS in `<style>`, `<script>` elements, and ignores the minification of `<pre>` elements.
MIT License
21 stars 5 forks source link

1.1.15 html-minifier panics when slicing html with inline javascript #3

Closed prixt closed 4 years ago

prixt commented 4 years ago

OS: Windows 10, Toolchain: stable-x86_64-pc-windows-msvc

Hello. I'm using html-minifier in the build script. After reading an html file using include_str! I insert javascript code into it using str::replace. And I'm getting these panics when building. I tested by removing the css and minifying the js before inserting it into the html, but the error still happens. This doesn't happen with v1.1.14.

Error backtrace

  process didn't exit successfully: `D:\MyProjects\Rust\soundsense-rs\target\debug\build\soundsense-rs-0bc9213e0ed51ce0\build-script-build` (exit code: 101)
--- stderr
thread 'main' panicked at 'begin <= end (3595 <= 3594) when slicing `"use strict";

let is_windows = null;
let channels = null;
function addSlider(channel_name) {
    channels.insertAdjacentElement(
        'beforeend',
        createSlider(channel_name)
    );
    let slider = document.getElementById(channel_name+`[...]', src\libcore\str\mod.rs:2056:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

D:\MyProjects\Rust\soundsense-rs>set RUST_BACKTRACE=1

D:\MyProjects\Rust\soundsense-rs>cargo build
   Compiling soundsense-rs v1.4.5 (D:\MyProjects\Rust\soundsense-rs)
error: failed to run custom build command for `soundsense-rs v1.4.5 (D:\MyProjects\Rust\soundsense-rs)```

Caused by:
  process didn't exit successfully: `D:\MyProjects\Rust\soundsense-rs\target\debug\build\soundsense-rs-0bc9213e0ed51ce0\build-script-build` (exit code: 101)
--- stderr
thread 'main' panicked at 'begin <= end (3595 <= 3594) when slicing `"use strict";

let is_windows = null;
let channels = null;
function addSlider(channel_name) {
    channels.insertAdjacentElement(
        'beforeend',
        createSlider(channel_name)
    );
    let slider = document.getElementById(channel_name+`[...]', src\libcore\str\mod.rs:2056:5
stack backtrace:
   0: backtrace::backtrace::trace_unsynchronized
             at C:\Users\VssAdministrator\.cargo\registry\src\github.com-1ecc6299db9ec823\backtrace-0.3.40\src\backtrace\mod.rs:66
   1: std::sys_common::backtrace::_print_fmt
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/src\libstd\sys_common\backtrace.rs:84
   2: std::sys_common::backtrace::_print::{{impl}}::fmt
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/src\libstd\sys_common\backtrace.rs:61
   3: core::fmt::write
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/src\libcore\fmt\mod.rs:1025
   4: std::io::Write::write_fmt<std::sys::windows::stdio::Stderr>
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/src\libstd\io\mod.rs:1426
   5: std::sys_common::backtrace::_print
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/src\libstd\sys_common\backtrace.rs:65
   6: std::sys_common::backtrace::print
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/src\libstd\sys_common\backtrace.rs:50
   7: std::panicking::default_hook::{{closure}}
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/src\libstd\panicking.rs:193
   8: std::panicking::default_hook
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/src\libstd\panicking.rs:210
   9: std::panicking::rust_panic_with_hook
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/src\libstd\panicking.rs:471
  10: std::panicking::begin_panic_handler
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/src\libstd\panicking.rs:375
  11: core::panicking::panic_fmt
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/src\libcore\panicking.rs:84
  12: core::str::slice_error_fail
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/src\libcore\str\mod.rs:0
  13: core::str::traits::{{impl}}::index::{{closure}}
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\src\libcore\str\mod.rs:1793
  14: core::option::Option<str*>::unwrap_or_else<str*,closure-0>
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\src\libcore\option.rs:422
  15: core::str::traits::{{impl}}::index
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\src\libcore\str\mod.rs:1793
  16: core::str::traits::{{impl}}::index<core::ops::range::Range<usize>>
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\src\libcore\str\mod.rs:1658
  17: minifier::js::token::check_if_number
             at C:\Users\pc\.cargo\registry\src\github.com-1ecc6299db9ec823\minifier-0.0.35\src\js\token.rs:804
  18: minifier::js::token::tokenize
             at C:\Users\pc\.cargo\registry\src\github.com-1ecc6299db9ec823\minifier-0.0.35\src\js\token.rs:900
  19: minifier::js::js::minify
             at C:\Users\pc\.cargo\registry\src\github.com-1ecc6299db9ec823\minifier-0.0.35\src\js\js.rs:210
  20: html_minifier::HTMLMinifier::digest<str*>
             at C:\Users\pc\.cargo\registry\src\github.com-1ecc6299db9ec823\html-minifier-1.1.15\src\lib.rs:388
  21: html_minifier::HTMLMinifier::minify<alloc::string::String>
             at C:\Users\pc\.cargo\registry\src\github.com-1ecc6299db9ec823\html-minifier-1.1.15\src\lib.rs:649
  22: html_minifier::minify<alloc::string::String>
             at C:\Users\pc\.cargo\registry\src\github.com-1ecc6299db9ec823\html-minifier-1.1.15\src\lib.rs:664
  23: build_script_build::main
             at .\build.rs:24
  24: std::rt::lang_start::{{closure}}<core::result::Result<(), alloc::boxed::Box<Error>>>
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\src\libstd\rt.rs:67
  25: std::rt::lang_start_internal::{{closure}}
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/src\libstd\rt.rs:52
  26: std::panicking::try::do_call<closure-0,i32>
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/src\libstd\panicking.rs:292
  27: panic_unwind::__rust_maybe_catch_panic
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/src\libpanic_unwind\lib.rs:78
  28: std::panicking::try
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/src\libstd\panicking.rs:270
  29: std::panic::catch_unwind
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/src\libstd\panic.rs:394
  30: std::rt::lang_start_internal
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/src\libstd\rt.rs:51
  31: std::rt::lang_start<core::result::Result<(), alloc::boxed::Box<Error>>>
             at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\src\libstd\rt.rs:67
  32: main
  33: invoke_main
             at d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78
  34: __scrt_common_main_seh
             at d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
  35: BaseThreadInitThunk
  36: RtlUserThreadStart
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

index.html

<!doctype html>
<html>
<head>
<!--{comment_end}
    <style type="text/css">{w3}</style>
    <style type="text/css">{range}</style>
    <script type="text/javascript">{js}</script>
{comment_start}-->
</head>
<body onload="main()">
<header class='w3-bar w3-light-grey w3-small'>
    <div class='w3-dropdown-hover'>
        <button class='w3-button'>Load</button>
        <div class='w3-dropdown-content w3-bar-block w3-border'>
        <button class='w3-bar-item w3-button'
            onclick="external.invoke('load_gamelog')">Load gamelog.txt</button>
        <button class='w3-bar-item w3-button'
            onclick="external.invoke('load_soundpack')">Load soundpack</button>
        <button class='w3-bar-item w3-button'
            onclick="external.invoke('load_ignore_list')">Load ignore.txt</button>
        </div>
    </div>
    <div class='w3-dropdown-hover'>
        <button class='w3-button'>Settings</button>
        <div class='w3-dropdown-content w3-bar-block w3-border'>
        <button class='w3-bar-item w3-button'
            onclick="external.invoke('set_default_paths')">Set current paths as default</button>
        <button class='w3-bar-item w3-button'
            onclick="external.invoke('remove_default_paths')">Delete default paths setting</button>
        <button class='w3-bar-item w3-button'
            onclick="external.invoke('set_default_volumes')">Set current volumes as default</button>
        <button class='w3-bar-item w3-button'
            onclick="external.invoke('remove_default_volumes')">Delete default volumes setting</button>
        </div>
    </div>
    <div class='w3-dropdown-hover w3-right'>
        <button class='w3-button'>Options</button>
        <div class='w3-dropdown-content w3-bar-block w3-border' style='right:0'>
            <button class='w3-bar-item w3-button'
                onclick="external.invoke('link_original')">&#x1f517; - zwei's original SoundSense & soundpack</button>
            <button class='w3-bar-item w3-button'
                onclick="external.invoke('link_fork')">&#x1f517; - jecowa's soundpack fork</button>
            <button class='w3-bar-item w3-button'
                onclick="external.invoke('link_source')">&#x1f517; - SoundSense-RS Github repository</button>
            <button class='w3-bar-item w3-button'
                onclick="external.invoke('show_about')">&#x2139; - About</button>
        </div>
    </div>
</header>
<div class="w3-block" id="channels"></div>
<footer class='w3-bottom' id='alerts'></footer>
<footer class="w3-bottom" id='errors'></footer>
</body>
</html>

script.js

"use strict";

let is_windows = null;
let channels = null;
function addSlider(channel_name) {
    channels.insertAdjacentElement(
        'beforeend',
        createSlider(channel_name)
    );
    let slider = document.getElementById(channel_name+"_slider");
    slider.style.cssText="padding: 0px 10px 0px 0px;";
    slider.addEventListener(is_windows?'change':'input',function(){
            external.invoke("change_volume:"+channel_name+":"+this.value);
        },
        false
    );
}
function createSlider(channel_name) {
    let slider = document.createElement("div");
    slider.className="w3-cell-row w3-border-bottom";
    slider.insertAdjacentHTML(
        'afterbegin',
        "<div class='w3-cell w3-cell-middle w3-center w3-padding-small' style='width:10%;min-width:90px'>"+
            "<h4>"+channel_name+"</h4>"+
        "</div>"+
        "<div class='w3-cell w3-cell-middle w3-rest w3-container w3-padding-small'>"+
            "<input type='range' id='"+channel_name+"_slider'"+
                "min='0' max='100' value='100'>"+
        "</div>"
    );
    return slider;
}
function setSliderValue(channel_name, value) {
    let slider = document.getElementById(channel_name+"_slider");
    if (slider != null) slider.value = value;
}
function clearSliders() {
    while (channels.firstChild)
        channels.removeChild(channels.firstChild);
}

let alerts_footer = null;
let alerts = null;
function addAlert(name, color, text) {
    removeAlert(name);
    let new_alert = createAlert(name, color, text);
    alerts[name] = new_alert;
    alerts_footer.insertAdjacentElement('beforeend', new_alert);
    if (alerts_footer.childElementCount > 10)
        removeAlert(alerts_footer.firstChild.name);
}
function removeAlert(name) {
    let alert = document.getElementById("alert_"+name);
    if (alert != null) {
        alerts_footer.removeChild(alert);
        alerts.delete(name);
    }
}
function createAlert(name, color, text) {
    let alert=document.createElement("div");
    alert.name = name;
    alert.id="alert_"+name;
    alert.className="w3-bar w3-animate-bottom w3-"+color;
    alert.style.cssText="padding: 2px 15px 2px 15px;";
    alert.innerHTML=text;
    alert.timer = 4.0;

    let cross = document.createElement("span");
    cross.className="w3-closebtn";
    cross.setAttribute("onclick", "removeAlert('"+name+"')");
    cross.innerHTML="&times;";

    alert.insertAdjacentElement('afterbegin',cross);

    return alert;
}

let error_footer = null;
function addError(name, text) {
    let new_error = createError(name, text);
    error_footer.insertAdjacentElement('afterbegin', new_error);
}
function removeError(id) {
    let error = document.getElementById(id);
    if (error != null) {
        error_footer.removeChild(error);
    }
}
function createError(name, text) {
    let error=document.createElement("div");
    error.name=name;
    error.id="error_"+name+toString(Math.floor(Math.random()*100000));
    error.className="w3-bar w3-animate-bottom w3-red";
    error.style.cssText="padding: 10px 15px 10px 15px;";
    error.innerHTML="<h3>"+name+"</h3><p>"+text+"</p>";

    let cross = document.createElement("span");
    cross.className="w3-closebtn";
    cross.setAttribute("onclick", "removeError('"+error.id+"')");
    cross.innerHTML="&times;";

    error.insertAdjacentElement('afterbegin',cross);

    return error;
}

function main() {
    channels = document.getElementById('channels');
    is_windows = /MSIE|Trident|Edge/.test(window.navigator.userAgent);
    alerts_footer = document.getElementById('alerts');
    error_footer = document.getElementById('errors');
    alerts = new Map();

    let prev = null;
    function step(now) {
        let dt = (prev!=null) ? (now-prev)*0.001 : 0.0;
        prev = now;
        for (let key in alerts) {
            let alert = alerts[key];
            alert.timer -= dt;
            if (alert.timer <= 1.0) alert.style.opacity = alert.timer;
            if (alert.timer <= 0.0) removeAlert(alert.name);
        }
        window.requestAnimationFrame(step);
    }
    window.requestAnimationFrame(step);
}

build.rs

use std::{
    env, error::Error, fs::File, io::Write, path::Path,
};

fn main() -> Result<(), Box<dyn Error>> {
    let dest_dir = Path::new(
            &env::var("OUT_DIR")?
        )
        .join("index.html");

    #[cfg(not(target_os="windows"))]
    let range_css = include_str!("web/range.css");
    #[cfg(all(target_os="windows", not (feature = "edge")))]
    let range_css = include_str!("web/range-windows.css");
    #[cfg(all(target_os="windows", feature = "edge"))]
    let range_css = include_str!("web/range-windows-edge.css");

    let index_html = include_str!("web/index.html")
        .replace("{comment_start}"  , "<!--")
        .replace("{comment_end}"    , "-->")
        .replace("{range}"          , range_css)
        .replace("{w3}"             , include_str!("web/w3.css"))
        .replace("{js}"             , include_str!("web/script.js"));
    let index_html = html_minifier::minify(index_html)?;

    File::create(dest_dir)?
        .write_all(index_html.as_bytes())?;

    #[cfg(target_os="windows")]
    winres::WindowsResource::new()
        .set_icon("icons/icon.ico")
        .compile()?;

    Ok(())
}
magiclen commented 4 years ago

I've noticed this issue since version 1.1.10. This bug is related to the minifier crate. For now, it would be more stable if you downgrade html-minifier to =1.1.9.

prixt commented 4 years ago

Understood. Thank you for the response!

magiclen commented 4 years ago

Thanks to the author of the minifier crate, that should be fixed now.

prixt commented 4 years ago

Yep, just checked the most recent release and it works!