Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/net/html: incorrect handling nodes in the <head> section #42882

Closed
LazarenkoA opened this issue Nov 30, 2020 · 9 comments
Closed

x/net/html: incorrect handling nodes in the <head> section #42882

LazarenkoA opened this issue Nov 30, 2020 · 9 comments
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@LazarenkoA
Copy link

LazarenkoA commented Nov 30, 2020

What version of Go are you using (go version)?

go1.15.5 windows/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
set GO111MODULE=on
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\lazarenko.an\AppData\Local\go-build
set GOENV=C:\Users\lazarenko.an\AppData\Roaming\go\env
set GOEXE=.exe
set GOFLAGS= -mod=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMODCACHE=D:\GoMy\src\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPATH=D:\GoMy\src
set GOPRIVATE=
set GOPROXY=https://proxy.golang.org,direct
set GOROOT=c:\go
set GOSUMDB=sum.golang.org
set GOTMPDIR=
set GOTOOLDIR=c:\go\pkg\tool\windows_amd64
set GCCGO=gccgo
set AR=ar
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=D:\WorkGIT\MIRS_STORAGE\GO\GoGateway\go.mod
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -mthreads -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=C:\Users\LAZARE~1.AN\AppData\Local\Temp\go-build527580266=/tmp/go-build -gno-record-gcc-switches


What did you do?

func test2()  {
	doc, _ := html.Parse(strings.NewReader(Test_html()))

	var buf bytes.Buffer
	if err := html.Render(&buf, doc); err == nil {
		fmt.Println(buf.String())
	}
}
Test_html()
func Test_html() string {
	return `<!DOCTYPE html>
<html>
<head>
    <title>1C:Enterprise</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <link rel="shortcut icon" href="e1csys/mngsrv/favicon.ico" />
<style type="text/css">
BODY
{
    margin: 0;
    padding: 0;
    border: 0;
    font-family: 'MS Sans Serif';
    color: #594304;
}
</style>
    <script language="javascript" type="text/javascript">
    var BaseUrl = window.location.href.substr(
      0, window.location.href.lastIndexOf("/")) + "/";

    var base = "/tfresh/t2/int/hrmcorp_dev/ru";
    var lang = "ru_RU";
    var redirect = false;
    var openIDRelyingParty = false;
    var splashTopBrandingImage = false;
    var splashBottomBrandingImage = false;
    var hasBranding = false;
    var exitURL = "";
    var oidcAllowStandardAuthentication = "";
    var version = "8.3.17.1549"
    var vendorPrefix = "1c.";
    var ansQuery = undefined;

    var path = base;
    var url = window.location.protocol + "//" + window.location.host;
    if (path.slice(-1) !== '/') path += '/';
    if (base.length !== 0)
        url += path;

    var paramKeyPrefix = getParameterValue(window.location.href, 'sk');
    var keyPrefix = (paramKeyPrefix) ? paramKeyPrefix : generateKeyPrefix();

    if (!isStorageSupported())
    {
        url += '?sk=' + keyPrefix;
    }

    function generateKeyPrefix()
    {
        function s4()
        {
            return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
        }

        return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
    }

    function removeUrlParameter(strUrl, paramName)
    {
        var re = new RegExp('[&]?' + paramName + '=[^&]+', 'gi');

        var result = strUrl.replace(re, '');

        if (result.indexOf('?') === result.length - 1)
        {
            result = result.substr(0, result.indexOf('?'));
        }

        return result;
    }

    function getParameterValue(strUrl, paramName)
    {
        var re = new RegExp('[\?&]?' + paramName + '=([^&]+)', 'gi');

        var matches = re.exec(strUrl);

        if (matches && matches.length > 1 && matches[1])
        {
            return matches[1];
        }

        return null;
    }


    function writeToStorage(key, value, inQuote, encode)
    {
        if (value)
        {
            if (inQuote && value.length > 1 &&
                ((value.charAt(0) === '"' && value.charAt(value.length - 1) === '"') ||
                 (value.charAt(0) === '\'' && value.charAt(value.length - 1) === '\'')))
            {
                value = value.substring(1, value.length - 1);
            }
            if (encode)
                value = encodeURIComponent(value);

            if (!isStorageSupported())
            {
                var expiresDate = new Date();
                // Время жизни - 1 минута
                expiresDate = new Date(expiresDate.getTime() + 1000 * 60);

                document.cookie = keyPrefix + '_' + key + '=' + value + ';expires=' +
                                  expiresDate.toGMTString() + ';Path=' + path;
            }
            else
            {
                sessionStorage.setItem(key, value);
            }
        }
        else
        {
            if (!isStorageSupported())
            {
                var expiresDate = new Date();
                // Время жизни - 1 минута
                expiresDate = new Date(expiresDate.getTime() + 1000 * 60);

                document.cookie = keyPrefix + '_' + key + '=;expires=' +
                                  expiresDate.toGMTString() + 'Path=' + path;
            }
            else
            {
                sessionStorage.setItem(key, value);
            }
        }
    }

    function getFromStorage(key)
    {
        if (isStorageSupported())
        {
            return sessionStorage.getItem(key)
        }
        else
        {
            var cookies = document.cookie.split(";");

            for (var i = 0; i < cookies.length; i++)
            {
                var values = cookies[i].split("=");
                var str = values[0];
                str = str.replace(/(^\s*)|(\s*$)/g, "");

                if (str === keyPrefix + '_' + key)
                {
                    return values[1];
                }
            }
        }
        return null;
    }

    function deleteFromStorage(key)
    {
        if (isStorageSupported())
        {
            sessionStorage.removeItem(key);
        }
        else
        {
            document.cookie = keyPrefix + '_' + key +
                              "=;expires=Fri, 21 Dec 1976 04:31:24 GMT;Path="
                              + path;
        }
    }

    function isStorageSupported()
    {
        var testKey = 'test';

        try
        {
            var storage = window.sessionStorage;
            storage.setItem(testKey, '1');
            storage.removeItem(testKey);

            return true;
        }
        catch (error)
        {
            return false;
        }
    }

    function writeCookie(cookieName, value, inQuote, encode)
    {
        if (value)
        {
            if (inQuote && value.length > 1 &&
                ((value.charAt(0) === '"' && value.charAt(value.length - 1) === '"') ||
                 (value.charAt(0) === '\'' && value.charAt(value.length - 1) === '\'')))
            {
                value = value.substring(1, value.length - 1);
            }
            if (encode)
                value = encodeURIComponent(value);

            document.cookie = cookieName + '=' + value + ';Path=' + path;
        }
        else
        {
            document.cookie = cookieName + '=;Path=' + path;
        }
    }

    function getCookie(cookieName)
    {
        var cookies = document.cookie.split(";");
        for (var i = 0; i < cookies.length; i++)
        {
            var values = cookies[i].split("=");
            var str = values[0];
            str = str.replace(/(^\s*)|(\s*$)/g, "");
            if (str === cookieName)
                return values[1];
        }
        return null;
    }
    function deleteCookie(cookieName)
    {
        document.cookie = cookieName + "=;expires=Fri, 21 Dec 1976 04:31:24 GMT;Path=" + path;
    }

    function getHash ()
    {
        var hash = "";
        if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent) &&
           (/msie 6\.0/i).test(navigator.userAgent))
        {
            var str = window.location.href;
            var inStr = false;
            var strCh = "";
            for (var i = str.length - 1; i >= 0; i--)
            {
                var ch = str.charAt(i);
                if (inStr)
                {
                    if (ch === strCh)
                        inStr = false;
                    continue;
                }
                if (ch === "\"" || ch === "'")
                {
                    inStr = true;
                    strCh = ch;
                    continue;
                }
                if (ch === "#")
                {
                    hash = str.substr(i + 1);
                    break;
                }
            }
        }
        else
        {
            hash = window.location.hash;
        }
        return hash;
    }

    function getParams ()
    {
        if (window.location.search)
        {
            var params = decodeURIComponent(window.location.search).split("&");
            if (params && params[0].substring(0, 1) === "?")
                params[0] = params[0].substr(1);
            return params;
        }
        else
        {
            return [];
        }
    }

    function proceedOIDC (enableOpenIDAuth, openIDInfo)
    {
        if (getFromStorage('vrs_seance'))
        {
            setTimeout(continueLoadHTML.bind(this, true), 50);
            return false;
        }

        writeToStorage('vrs_oidcAllowStandardAuthentication', oidcAllowStandardAuthentication);

        if (enableOpenIDAuth)
            writeToStorage('vrs_oiadenable', '1');

        if (openIDInfo)
        {
            var openIDStr = JSON.parse(openIDInfo);
            for (var key in openIDStr)
            {
                var strIDStr = JSON.stringify(openIDStr);
                writeToStorage('vrs_oauth', strIDStr);
                setTimeout(continueLoadHTML.bind(this, false), 50);
                return false;
            }
        }
        return true;
    }

    function parseParam (param)
    {
        var arr = param.split("=");
        if (arr)
        {
            var paramName = arr[0].toUpperCase();
            var paramVal = null;
            if (arr.length > 2)
                paramVal = arr.slice(1).join('=');
            else if (arr.length > 1)
                paramVal = arr[1];
            var paramValUp = paramVal ? paramVal.toUpperCase() : paramVal;
            return {
                paramName: paramName,
                paramVal: paramVal,
                paramValUp: paramValUp
            };
        }
        return null;
    }

    function initMain()
    {
        var hash = getHash();
        if (hash)
        {
            hash = decodeURIComponent(hash);
            writeToStorage('vrs_hash', hash, false, true);
        }

        var params = getParams();
        var enableOpenIDAuth = openIDRelyingParty;
        var openIDLogout = false;

        for (var i = 0; i < params.length; i++)
        {
            var paramObj = parseParam(params[i]);
            if (!paramObj)
                continue;

            var paramName = paramObj.paramName;
            var paramVal = paramObj.paramVal;
            var paramValUp = paramObj.paramValUp;

            if (paramName === "O" && paramValUp)
            {
                var v = (paramValUp === "LOW") ? "true" : (paramValUp === "NORMAL") ? "false" : null;
                if (v !== null)
                    writeToStorage("vrs_slow", v);
            }
            else if (paramName === "C")
                writeToStorage('vrs_param', paramVal, true, true);
            else if (paramName === "N")
                writeToStorage('vrs_user', paramVal, true, true);
            else if (paramName === "P")
                writeToStorage('vrs_psw', paramVal, false, true);
            else if (paramName === "WA-")
                writeToStorage('vrs_nowa');
            else if (paramName === "DISABLESTARTUPMESSAGES")
                writeToStorage('vrs_disStartMsg');
            else if (paramName === "DISPLAYALLFUNCTIONS")
                writeToStorage('vrs_displayAllFunctions');
            else if (paramName === "TECHNICALSPECIALISTMODE")
                writeToStorage('vrs_displayAllFunctions');
            else if (paramName === "VL")
                writeToStorage('vrs_vl', paramVal);
            else if (paramName === "L")
                writeToStorage('vrs_ul', paramVal);
            else if (paramName === "DISPLAYPERFORMANCE")
                writeToStorage('vrs_perf');
            else if (paramName === "DISPLAYUSERNOTIFICATIONLIST")
                writeToStorage('vrs_displayusernotificationlist');
            else if (paramName === "ACCEPTVIDEOCALL")
                writeToStorage('vrs_acceptvideocall', paramVal, true, true);
            else if (paramName === "DEBUGGERURL")
                writeToStorage('vrs_deburl', paramVal, true, true);
            else if (paramName === "DEBUG")
                writeToStorage('vrs_debug', paramVal || 'TCP');
            else if (paramName === "TESTCLIENT")
                writeToStorage('vrs_test');
            else if (paramName === "TESTCLIENTID")
                writeToStorage('vrs_test_id', paramVal || '');
            else if (paramName === "Z")
                writeToStorage('vrs_zn', paramVal, true, true);
            else if (paramName === "OIDA-")
                enableOpenIDAuth = false;
            else if (paramName === "VRS-SEANCE")
                writeToStorage('vrs_seance', paramVal);
            else if (paramName === "AUTHOFF" && openIDRelyingParty)
                openIDLogout = true;
            else if (paramName === "USEPRIVILEGEDMODE")
                writeToStorage('vrs_privMode');
            else if (paramName === "VRSSESSION2")
                writeToStorage('vrs_session2', paramVal);
            else if (paramName === "DEBUGGCC")
            {
                var debugGcc = paramValUp || "1";
                if (debugGcc === "1" || debugGcc === "ON" || debugGcc === "TRUE")
                    writeCookie('vrs_dbggcc');
                else
                    deleteCookie('vrs_dbggcc');
            }
            else if (paramName === "SYSTEMWEBCLIENTSTAT")
                writeToStorage('vrs_webclientstat');
            else if (paramName === "EXECUTE")
                writeToStorage('vrs_execute', paramVal);
            else if (paramName === 'ENABLECHECKSERVERCALLS')
                writeToStorage('vrs_enablecheckservercalls');
            else if (paramName === 'OIDCSELECTEDPROVIDER')
                writeToStorage('vrs_sel_prov', paramVal);
            else if (paramName === 'DISABLESPLASH' && hasBranding)
                writeToStorage('vrs_disablesplash');
            else if (paramName === 'CLITYPE')
                writeToStorage('vrs_clitype', paramVal);
            else if (paramName === 'CLIKEY')
                writeToStorage('vrs_clikey', paramVal);
            else if (paramName === 'APPID')
                writeToStorage('vrs_appid', paramVal);
            else if (paramName === 'OIDCLASTPROV')
                writeToStorage('vrs_oidclastprov', paramVal);
            else if (paramName === 'AUTHMESSAGE' && paramVal)
                alert(paramVal);
            else if (paramName === 'MAINWINDOWMODE' && paramVal)
                writeToStorage('vrs_mainwindowmode', paramVal);
            else if (paramName === "DISABLEUNRECOVERABLEERRORMESSAGE")
                writeToStorage('vrs_disUnrecoverableMsg');
        }

        if (getFromStorage('vrs_nooida'))
            enableOpenIDAuth = false;
        else if(!enableOpenIDAuth)
            writeToStorage('vrs_nooida', '1');

        // Значение для локейла форматирования
        writeToStorage('vrs_lang', lang);

        if (splashTopBrandingImage)
            writeToStorage('vrs_splashTop');

        if (splashBottomBrandingImage)
            writeToStorage('vrs_splashBottom');

        if (ansQuery !== undefined)
            writeToStorage('vrs_ansquery', ansQuery);

        if (exitURL)
            writeToStorage('vrs_exitURL', exitURL, true, true);

        if (openIDLogout)
        {
            window.location.replace("e1cib/oid2rp?cmd=logout" + getZoneParam());
            return;
        }

        if (removeUrlParameter(window.location.search, 'sk') || hash || redirect)
        {
            window.location.replace(url);
            return;
        }

        var openIDInfo = document.getElementById('openidconnectconfig').innerHTML;
        if (openIDInfo && !proceedOIDC(enableOpenIDAuth, openIDInfo))
            return;

        if (getFromStorage('vrs_afteroidainit') === '1')
        {
            deleteFromStorage('vrs_afteroidainit');
        }
        else if (enableOpenIDAuth)
        {
            writeToStorage('vrs_oida', '1');
            writeToStorage('vrs_afteroidainit', '1');
            window.location.replace("e1cib/oid2rp?cmd=init" + getZoneParam());
            return;
        }
        setTimeout(continueLoadHTML.bind(this, true), 50);
    }

    function getZoneParam()
    {
        var zone = getFromStorage("vrs_zn");
        if (zone)
            return "&z=" + zone;
        else
            return "";
    }

    function getHttpRequest()
    {
        var httpRequest;
        if (window.XMLHttpRequest)
        {
            httpRequest = new XMLHttpRequest();
        }
        else if (window.ActiveXObject)
        {
            var progIDs = ["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"];
            for (var i = 0; i < progIDs.length; i++)
            {
                try
                {
                    httpRequest = new ActiveXObject(progIDs[i]);
                    break;
                }
                catch (e)
                {
                }
            }
        }
        return httpRequest;
    }

    function continueLoadHTML(isMainFormHTML)
    {
        var httpRequest = getHttpRequest();
        if (!httpRequest)
        {
            alert("You cannot use this browser to work with 1C:Enterprise software");
            return;
        }

        var url = (isMainFormHTML ? "mainform.html" : "authform.html") + "?sysver=" + version;
        httpRequest.open("GET", encodeURI(url), false);
        httpRequest.setRequestHeader("Content-Type", "text/html; charset=utf-8");
        httpRequest.setRequestHeader("Accept", "text/html");
        httpRequest.send("");
        var html = httpRequest.responseText;

        // Посылаем событие асинхронному расширению в Chrome перед doc.open,
        // который уничтожит все навешенные ранее обработчики
        if (/chrome/i.test(navigator.userAgent))
        {
            var startLoadWebClientEvent = new CustomEvent(vendorPrefix + "startLoadWebClient");
            window.dispatchEvent(startLoadWebClientEvent);
        }

        var doc = document;
        doc.open("text/html", "replace");
        doc.write(html);
        doc.close();
    }

    window.onerror = function(message)
    {
        alert(message);
        return true;
    }
    </script>
</head>
<body onload="initMain();">
    <div id="openidconnectconfig" style="display:none"></div>
</body>
</html>`
}

What did you expect to see?

<html>
<head>
    <title>1C:Enterprise</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <link rel="shortcut icon" href="e1csys/mngsrv/favicon.ico" />
<style type="text/css">
.....

What did you see instead?

<html><head></head><body onload="initMain();">


    <title>1С:Предприятие</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <link rel="shortcut icon" href="e1csys/mngsrv/favicon.ico"/>
<style type="text/css">

.....

PS
Possibly related to issues #23064

@gopherbot gopherbot added this to the Unreleased milestone Nov 30, 2020
@kennygrant
Copy link
Contributor

Can you reproduce with a much smaller file instead? There probably isn't a need to reproduce all the js here if you can still see it with a very short html example. Ideally just one complete file to reproduce it would be best.

This problem doesn't reproduce on play.golang.org for me using the code given above:

https://play.golang.org/p/oKEquK8zNeo

Nor on Go 1.15.5 on darwin/amd64 or Go 1.16 darwin/arm64 with latest x/net/html

Are you able to see it on another machine?

What happens if you import the latest golang.org/x/net/html are you sure you're on the latest version of that?

@LazarenkoA
Copy link
Author

LazarenkoA commented Dec 1, 2020

Can you reproduce with a much smaller file instead?

unfortunately the error is not always repeated

I was inattentive, the error from the previous example is not repeated, but it is repeated from the example below

func main() {
	res, _ := http.Get("https://analytics.demo.1c.ru/analytics")
	defer res.Body.Close()

	if res.StatusCode != 200 {
		log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
	}

	doc, _ := html.Parse(res.Body)
	var buf bytes.Buffer
	if err := html.Render(&buf, doc); err == nil {
		fmt.Println(buf.String())
	}

	res.Body.Close()
}

html code that returns https://analytics.demo.1c.ru/analytics same as in the original example

@LazarenkoA LazarenkoA reopened this Dec 1, 2020
@kennygrant
Copy link
Contributor

kennygrant commented Dec 1, 2020

You shouldn't need network calls to reproduce this and it complicates things.

Try to make one go file with some hard-coded HTML which reproduces the problem. For example try something like this locally (adding all your html if you want to start with):

https://play.golang.org/p/3O9vs3cdUC5

Then try running that locally to see what you get.

If you can reproduce with static html, post the html which does so here.

If you can't reproduce with static html the problem is likely elsewhere (perhaps html being mangled on the way to you somehow).

@LazarenkoA
Copy link
Author

If it was so easy I wouldn't text here. The behavior is so strange and I can't get what it depends on. For example, I call web-site and get html (string variable). I show it in the console - there is the same value as in Test_html().
In this situation the problem is repeated.

	res, _ := http.Get("https://analytics.demo.1c.ru/analytics")
	defer res.Body.Close()

	bytesHTML, _ := ioutil.ReadAll(res.Body)
	testHTML := string(bytesHTML)
	fmt.Println(testHTML)

	if res.StatusCode != 200 {
		log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
	}

	//testHTML = Test_html() 

	fmt.Println("------------------------------------------------------------")

	doc, _ := html.Parse(strings.NewReader(testHTML))
	var buf bytes.Buffer
	if err := html.Render(&buf, doc); err == nil {
		fmt.Println(buf.String())
	}

If uncomment the code testHTML = Test_html() - the problem isn't repeated (but there is the same html what I showed before). It is mistical

@LazarenkoA
Copy link
Author

I don't know whether it will help or not, but I output html.Node

doc, _ := html.Parse(strings.NewReader(testHTML))
pp.Println(doc)
Good
&html.Node{
  Parent:     (*html.Node)(nil),
  FirstChild: &html.Node{
    Parent:     &html.Node{...},
    FirstChild: &html.Node{
      Parent:     &html.Node{...},
      FirstChild: &html.Node{
        Parent:      &html.Node{...},
        FirstChild:  (*html.Node)(nil),
        LastChild:   (*html.Node)(nil),
        PrevSibling: (*html.Node)(nil),
        NextSibling: &html.Node{
          Parent:     &html.Node{...},
          FirstChild: &html.Node{
            Parent:      &html.Node{...},
            FirstChild:  (*html.Node)(nil),
            LastChild:   (*html.Node)(nil),
            PrevSibling: (*html.Node)(nil),
            NextSibling: (*html.Node)(nil),
            Type:        0x00000001,
            DataAtom:    0x00000000,
            Data:        "1C:Enterprise",
            Namespace:   "",
            Attr:        []html.Attribute(nil),
          },
          LastChild:   &html.Node{...},
          PrevSibling: &html.Node{...},
          NextSibling: &html.Node{
            Parent:      &html.Node{...},
            FirstChild:  (*html.Node)(nil),
            LastChild:   (*html.Node)(nil),
            PrevSibling: &html.Node{...},
            NextSibling: &html.Node{
              Parent:      &html.Node{...},
              FirstChild:  (*html.Node)(nil),
              LastChild:   (*html.Node)(nil),
              PrevSibling: &html.Node{...},
              NextSibling: &html.Node{
                Parent:      &html.Node{...},
                FirstChild:  (*html.Node)(nil),
                LastChild:   (*html.Node)(nil),
                PrevSibling: &html.Node{...},
                NextSibling: &html.Node{
                  Parent:      &html.Node{...},
                  FirstChild:  (*html.Node)(nil),
                  LastChild:   (*html.Node)(nil),
                  PrevSibling: &html.Node{...},
                  NextSibling: &html.Node{
                    Parent:      &html.Node{...},
                    FirstChild:  (*html.Node)(nil),
                    LastChild:   (*html.Node)(nil),
                    PrevSibling: &html.Node{...},
                    NextSibling: &html.Node{
                      Parent:      &html.Node{...},
                      FirstChild:  (*html.Node)(nil),
                      LastChild:   (*html.Node)(nil),
                      PrevSibling: &html.Node{...},
                      NextSibling: &html.Node{
                        Parent:      &html.Node{...},
                        FirstChild:  (*html.Node)(nil),
                        LastChild:   (*html.Node)(nil),
                        PrevSibling: &html.Node{...},
                        NextSibling: &html.Node{
                          Parent:     &html.Node{...},
                          FirstChild: &html.Node{
                            Parent:      &html.Node{...},
                            FirstChild:  (*html.Node)(nil),
                            LastChild:   (*html.Node)(nil),
                            PrevSibling: (*html.Node)(nil),
                            NextSibling: (*html.Node)(nil),
                            Type:        0x00000001,
                            DataAtom:    0x00000000,
                            Data:        "\nBODY\n{\n    margin: 0;\n    padding: 0;\n    border: 0;\n    font-family: 'MS Sans Serif';\n    color: #594304;\n}\n",
                            Namespace:   "",
                            Attr:        []html.Attribute(nil),
                          },
                          LastChild:   &html.Node{...},
                          PrevSibling: &html.Node{...},
                          NextSibling: &html.Node{
                            Parent:      &html.Node{...},
                            FirstChild:  (*html.Node)(nil),
                            LastChild:   (*html.Node)(nil),
                            PrevSibling: &html.Node{...},
                            NextSibling: &html.Node{
                              Parent:     &html.Node{...},
                              FirstChild: &html.Node{
                                Parent:      &html.Node{...},
                                FirstChild:  (*html.Node)(nil),
                                LastChild:   (*html.Node)(nil),
                                PrevSibling: (*html.Node)(nil),
                                NextSibling: (*html.Node)(nil),
                                Type:        0x00000001,
                                DataAtom:    0x00000000,
                                Data:        "\n    var BaseUrl = window.location.href.substr(\n      0, window.location.href.lastIndexOf(\"/\")) + \"/\";\n\n    var base = \"/tfresh/t2/int/hrmcorp_dev/ru\";\n    var lang = \"ru_RU\";\n    var redirect = false;\n    var openIDRelyingParty = false;\n    var splashTopBrandingImage = false;\n    var splashBottomBrandingImage = false;\n    var hasBranding = false;\n    var exitURL = \"\";\n    var oidcAllowStandardAuthentication = \"\";\n    var version = \"8.3.17.1549\"\n    var vendorPrefix = \"1c.\";\n    var ansQuery = undefined;\n\n    var path = base;\n    var url = window.location.protocol + \"//\" + window.location.host;\n    if (path.slice(-1) !== '/') path += '/';\n    if (base.length !== 0)\n        url += path;\n\n    var paramKeyPrefix = getParameterValue(window.location.href, 'sk');\n    var keyPrefix = (paramKeyPrefix) ? paramKeyPrefix : generateKeyPrefix();\n\n    if (!isStorageSupported())\n    {\n        url += '?sk=' + keyPrefix;\n    }\n\n    function generateKeyPrefix()\n    {\n        function s4()\n        {\n            return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);\n        }\n\n        return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();\n    }\n\n    function removeUrlParameter(strUrl, paramName)\n    {\n        var re = new RegExp('[&]?' + paramName + '=[^&]+', 'gi');\n\n        var result = strUrl.replace(re, '');\n\n        if (result.indexOf('?') === result.length - 1)\n        {\n            result = result.substr(0, result.indexOf('?'));\n        }\n\n        return result;\n    }\n\n    function getParameterValue(strUrl, paramName)\n    {\n        var re = new RegExp('[\\?&]?' + paramName + '=([^&]+)', 'gi');\n\n        var matches = re.exec(strUrl);\n\n        if (matches && matches.length > 1 && matches[1])\n        {\n            return matches[1];\n        }\n\n        return null;\n    }\n\n\n    function writeToStorage(key, value, inQuote, encode)\n    {\n        if (value)\n        {\n            if (inQuote && value.length > 1 &&\n                ((value.charAt(0) === '\"' && value.charAt(value.length - 1) === '\"') ||\n                 (value.charAt(0) === '\\'' && value.charAt(value.length - 1) === '\\'')))\n            {\n                value = value.substring(1, value.length - 1);\n            }\n            if (encode)\n                value = encodeURIComponent(value);\n\n            if (!isStorageSupported())\n            {\n                var expiresDate = new Date();\n                // Время жизни - 1 минута\n                expiresDate = new Date(expiresDate.getTime() + 1000 * 60);\n\n                document.cookie = keyPrefix + '_' + key + '=' + value + ';expires=' +\n                                  expiresDate.toGMTString() + ';Path=' + path;\n            }\n            else\n            {\n                sessionStorage.setItem(key, value);\n            }\n        }\n        else\n        {\n            if (!isStorageSupported())\n            {\n                var expiresDate = new Date();\n                // Время жизни - 1 минута\n                expiresDate = new Date(expiresDate.getTime() + 1000 * 60);\n\n                document.cookie = keyPrefix + '_' + key + '=;expires=' +\n                                  expiresDate.toGMTString() + 'Path=' + path;\n            }\n            else\n            {\n                sessionStorage.setItem(key, value);\n            }\n        }\n    }\n\n    function getFromStorage(key)\n    {\n        if (isStorageSupported())\n        {\n            return sessionStorage.getItem(key)\n        }\n        else\n        {\n            var cookies = document.cookie.split(\";\");\n\n            for (var i = 0; i < cookies.length; i++)\n            {\n                var values = cookies[i].split(\"=\");\n                var str = values[0];\n                str = str.replace(/(^\\s*)|(\\s*$)/g, \"\");\n\n                if (str === keyPrefix + '_' + key)\n                {\n                    return values[1];\n                }\n            }\n        }\n        return null;\n    }\n\n    function deleteFromStorage(key)\n    {\n        if (isStorageSupported())\n        {\n            sessionStorage.removeItem(key);\n        }\n        else\n        {\n            document.cookie = keyPrefix + '_' + key +\n                              \"=;expires=Fri, 21 Dec 1976 04:31:24 GMT;Path=\"\n                              + path;\n        }\n    }\n\n    function isStorageSupported()\n    {\n        var testKey = 'test';\n\n        try\n        {\n            var storage = window.sessionStorage;\n            storage.setItem(testKey, '1');\n            storage.removeItem(testKey);\n\n            return true;\n        }\n        catch (error)\n        {\n            return false;\n        }\n    }\n\n    function writeCookie(cookieName, value, inQuote, encode)\n    {\n        if (value)\n        {\n            if (inQuote && value.length > 1 &&\n                ((value.charAt(0) === '\"' && value.charAt(value.length - 1) === '\"') ||\n                 (value.charAt(0) === '\\'' && value.charAt(value.length - 1) === '\\'')))\n            {\n                value = value.substring(1, value.length - 1);\n            }\n            if (encode)\n                value = encodeURIComponent(value);\n\n            document.cookie = cookieName + '=' + value + ';Path=' + path;\n        }\n        else\n        {\n            document.cookie = cookieName + '=;Path=' + path;\n        }\n    }\n\n    function getCookie(cookieName)\n    {\n        var cookies = document.cookie.split(\";\");\n        for (var i = 0; i < cookies.length; i++)\n        {\n            var values = cookies[i].split(\"=\");\n            var str = values[0];\n            str = str.replace(/(^\\s*)|(\\s*$)/g, \"\");\n            if (str === cookieName)\n                return values[1];\n        }\n        return null;\n    }\n    function deleteCookie(cookieName)\n    {\n        document.cookie = cookieName + \"=;expires=Fri, 21 Dec 1976 04:31:24 GMT;Path=\" + path;\n    }\n\n    function getHash ()\n    {\n        var hash = \"\";\n        if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent) &&\n           (/msie 6\\.0/i).test(navigator.userAgent))\n        {\n            var str = window.location.href;\n            var inStr = false;\n            var strCh = \"\";\n            for (var i = str.length - 1; i >= 0; i--)\n            {\n                var ch = str.charAt(i);\n                if (inStr)\n                {\n                    if (ch === strCh)\n                        inStr = false;\n                    continue;\n                }\n                if (ch === \"\\\"\" || ch === \"'\")\n                {\n                    inStr = true;\n                    strCh = ch;\n                    continue;\n                }\n                if (ch === \"#\")\n                {\n                    hash = str.substr(i + 1);\n                    break;\n                }\n            }\n        }\n        else\n        {\n            hash = window.location.hash;\n        }\n        return hash;\n    }\n\n    function getParams ()\n    {\n        if (window.location.search)\n        {\n            var params = decodeURIComponent(window.location.search).split(\"&\");\n            if (params && params[0].substring(0, 1) === \"?\")\n                params[0] = params[0].substr(1);\n            return params;\n        }\n        else\n        {\n            return [];\n        }\n    }\n\n    function proceedOIDC (enableOpenIDAuth, openIDInfo)\n    {\n        if (getFromStorage('vrs_seance'))\n        {\n            setTimeout(continueLoadHTML.bind(this, true), 50);\n            return false;\n        }\n\n        writeToStorage('vrs_oidcAllowStandardAuthentication', oidcAllowStandardAuthentication);\n\n        if (enableOpenIDAuth)\n            writeToStorage('vrs_oiadenable', '1');\n\n        if (openIDInfo)\n        {\n            var openIDStr = JSON.parse(openIDInfo);\n            for (var key in openIDStr)\n            {\n                var strIDStr = JSON.stringify(openIDStr);\n                writeToStorage('vrs_oauth', strIDStr);\n                setTimeout(continueLoadHTML.bind(this, false), 50);\n                return false;\n            }\n        }\n        return true;\n    }\n\n    function parseParam (param)\n    {\n        var arr = param.split(\"=\");\n        if (arr)\n        {\n            var paramName = arr[0].toUpperCase();\n            var paramVal = null;\n            if (arr.length > 2)\n                paramVal = arr.slice(1).join('=');\n            else if (arr.length > 1)\n                paramVal = arr[1];\n            var paramValUp = paramVal ? paramVal.toUpperCase() : paramVal;\n            return {\n                paramName: paramName,\n                paramVal: paramVal,\n                paramValUp: paramValUp\n            };\n        }\n        return null;\n    }\n\n    function initMain()\n    {\n        var hash = getHash();\n        if (hash)\n        {\n            hash = decodeURIComponent(hash);\n            writeToStorage('vrs_hash', hash, false, true);\n        }\n\n        var params = getParams();\n        var enableOpenIDAuth = openIDRelyingParty;\n        var openIDLogout = false;\n\n        for (var i = 0; i < params.length; i++)\n        {\n            var paramObj = parseParam(params[i]);\n            if (!paramObj)\n                continue;\n\n            var paramName = paramObj.paramName;\n            var paramVal = paramObj.paramVal;\n            var paramValUp = paramObj.paramValUp;\n\n            if (paramName === \"O\" && paramValUp)\n            {\n                var v = (paramValUp === \"LOW\") ? \"true\" : (paramValUp === \"NORMAL\") ? \"false\" : null;\n                if (v !== null)\n                    writeToStorage(\"vrs_slow\", v);\n            }\n            else if (paramName === \"C\")\n                writeToStorage('vrs_param', paramVal, true, true);\n            else if (paramName === \"N\")\n                writeToStorage('vrs_user', paramVal, true, true);\n            else if (paramName === \"P\")\n                writeToStorage('vrs_psw', paramVal, false, true);\n            else if (paramName === \"WA-\")\n                writeToStorage('vrs_nowa');\n            else if (paramName === \"DISABLESTARTUPMESSAGES\")\n                writeToStorage('vrs_disStartMsg');\n            else if (paramName === \"DISPLAYALLFUNCTIONS\")\n                writeToStorage('vrs_displayAllFunctions');\n            else if (paramName === \"TECHNICALSPECIALISTMODE\")\n                writeToStorage('vrs_displayAllFunctions');\n            else if (paramName === \"VL\")\n                writeToStorage('vrs_vl', paramVal);\n            else if (paramName === \"L\")\n                writeToStorage('vrs_ul', paramVal);\n            else if (paramName === \"DISPLAYPERFORMANCE\")\n                writeToStorage('vrs_perf');\n            else if (paramName === \"DISPLAYUSERNOTIFICATIONLIST\")\n                writeToStorage('vrs_displayusernotificationlist');\n            else if (paramName === \"ACCEPTVIDEOCALL\")\n                writeToStorage('vrs_acceptvideocall', paramVal, true, true);\n            else if (paramName === \"DEBUGGERURL\")\n                writeToStorage('vrs_deburl', paramVal, true, true);\n            else if (paramName === \"DEBUG\")\n                writeToStorage('vrs_debug', paramVal || 'TCP');\n            else if (paramName === \"TESTCLIENT\")\n                writeToStorage('vrs_test');\n            else if (paramName === \"TESTCLIENTID\")\n                writeToStorage('vrs_test_id', paramVal || '');\n            else if (paramName === \"Z\")\n                writeToStorage('vrs_zn', paramVal, true, true);\n            else if (paramName === \"OIDA-\")\n                enableOpenIDAuth = false;\n            else if (paramName === \"VRS-SEANCE\")\n                writeToStorage('vrs_seance', paramVal);\n            else if (paramName === \"AUTHOFF\" && openIDRelyingParty)\n                openIDLogout = true;\n            else if (paramName === \"USEPRIVILEGEDMODE\")\n                writeToStorage('vrs_privMode');\n            else if (paramName === \"VRSSESSION2\")\n                writeToStorage('vrs_session2', paramVal);\n            else if (paramName === \"DEBUGGCC\")\n            {\n                var debugGcc = paramValUp || \"1\";\n                if (debugGcc === \"1\" || debugGcc === \"ON\" || debugGcc === \"TRUE\")\n                    writeCookie('vrs_dbggcc');\n                else\n                    deleteCookie('vrs_dbggcc');\n            }\n            else if (paramName === \"SYSTEMWEBCLIENTSTAT\")\n                writeToStorage('vrs_webclientstat');\n            else if (paramName === \"EXECUTE\")\n                writeToStorage('vrs_execute', paramVal);\n            else if (paramName === 'ENABLECHECKSERVERCALLS')\n                writeToStorage('vrs_enablecheckservercalls');\n            else if (paramName === 'OIDCSELECTEDPROVIDER')\n                writeToStorage('vrs_sel_prov', paramVal);\n            else if (paramName === 'DISABLESPLASH' && hasBranding)\n                writeToStorage('vrs_disablesplash');\n            else if (paramName === 'CLITYPE')\n                writeToStorage('vrs_clitype', paramVal);\n            else if (paramName === 'CLIKEY')\n                writeToStorage('vrs_clikey', paramVal);\n            else if (paramName === 'APPID')\n                writeToStorage('vrs_appid', paramVal);\n            else if (paramName === 'OIDCLASTPROV')\n                writeToStorage('vrs_oidclastprov', paramVal);\n            else if (paramName === 'AUTHMESSAGE' && paramVal)\n                alert(paramVal);\n            else if (paramName === 'MAINWINDOWMODE' && paramVal)\n                writeToStorage('vrs_mainwindowmode', paramVal);\n            else if (paramName === \"DISABLEUNRECOVERABLEERRORMESSAGE\")\n                writeToStorage('vrs_disUnrecoverableMsg');\n        }\n\n        if (getFromStorage('vrs_nooida'))\n            enableOpenIDAuth = false;\n        else if(!enableOpenIDAuth)\n            writeToStorage('vrs_nooida', '1');\n\n        // Значение для локейла форматирования\n        writeToStorage('vrs_lang', lang);\n\n        if (splashTopBrandingImage)\n            writeToStorage('vrs_splashTop');\n\n        if (splashBottomBrandingImage)\n            writeToStorage('vrs_splashBottom');\n\n        if (ansQuery !== undefined)\n            writeToStorage('vrs_ansquery', ansQuery);\n\n        if (exitURL)\n            writeToStorage('vrs_exitURL', exitURL, true, true);\n\n        if (openIDLogout)\n        {\n            window.location.replace(\"e1cib/oid2rp?cmd=logout\" + getZoneParam());\n            return;\n        }\n\n        if (removeUrlParameter(window.location.search, 'sk') || hash || redirect)\n        {\n            window.location.replace(url);\n            return;\n        }\n\n        var openIDInfo = document.getElementById('openidconnectconfig').innerHTML;\n        if (openIDInfo && !proceedOIDC(enableOpenIDAuth, openIDInfo))\n            return;\n\n        if (getFromStorage('vrs_afteroidainit') === '1')\n        {\n            deleteFromStorage('vrs_afteroidainit');\n        }\n        else if (enableOpenIDAuth)\n        {\n            writeToStorage('vrs_oida', '1');\n            writeToStorage('vrs_afteroidainit', '1');\n            window.location.replace(\"e1cib/oid2rp?cmd=init\" + getZoneParam());\n            return;\n        }\n        setTimeout(continueLoadHTML.bind(this, true), 50);\n    }\n\n    function getZoneParam()\n    {\n        var zone = getFromStorage(\"vrs_zn\");\n        if (zone)\n            return \"&z=\" + zone;\n        else\n            return \"\";\n    }\n\n    function getHttpRequest()\n    {\n        var httpRequest;\n        if (window.XMLHttpRequest)\n        {\n            httpRequest = new XMLHttpRequest();\n        }\n        else if (window.ActiveXObject)\n        {\n            var progIDs = [\"Msxml2.XMLHTTP\", \"Microsoft.XMLHTTP\"];\n            for (var i = 0; i < progIDs.length; i++)\n            {\n                try\n                {\n                    httpRequest = new ActiveXObject(progIDs[i]);\n                    break;\n                }\n                catch (e)\n                {\n                }\n            }\n        }\n        return httpRequest;\n    }\n\n    function continueLoadHTML(isMainFormHTML)\n    {\n        var httpRequest = getHttpRequest();\n        if (!httpRequest)\n        {\n            alert(\"You cannot use this browser to work with 1C:Enterprise software\");\n            return;\n        }\n\n        var url = (isMainFormHTML ? \"mainform.html\" : \"authform.html\") + \"?sysver=\" + version;\n        httpRequest.open(\"GET\", encodeURI(url), false);\n        httpRequest.setRequestHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n        httpRequest.setRequestHeader(\"Accept\", \"text/html\");\n        httpRequest.send(\"\");\n        var html = httpRequest.responseText;\n\n        // Посылаем событие асинхронному расширению в Chrome перед doc.open,\n        // который уничтожит все навешенные ранее обработчики\n        if (/chrome/i.test(navigator.userAgent))\n        {\n            var startLoadWebClientEvent = new CustomEvent(vendorPrefix + \"startLoadWebClient\");\n            window.dispatchEvent(startLoadWebClientEvent);\n        }\n\n        var doc = document;\n        doc.open(\"text/html\", \"replace\");\n        doc.write(html);\n        doc.close();\n    }\n\n    window.onerror = function(message)\n    {\n        alert(message);\n        return true;\n    }\n    ",
                                Namespace:   "",
                                Attr:        []html.Attribute(nil),
                              },
                              LastChild:   &html.Node{...},
                              PrevSibling: &html.Node{...},
                              NextSibling: &html.Node{
                                Parent:      &html.Node{...},
                                FirstChild:  (*html.Node)(nil),
                                LastChild:   (*html.Node)(nil),
                                PrevSibling: &html.Node{...},
                                NextSibling: (*html.Node)(nil),
                                Type:        0x00000001,
                                DataAtom:    0x00000000,
                                Data:        "\n",
                                Namespace:   "",
                                Attr:        []html.Attribute(nil),
                              },
                              Type:      0x00000003,
                              DataAtom:  0x00021806,
                              Data:      "script",
                              Namespace: "",
                              Attr:      []html.Attribute{
                                html.Attribute{
                                  Namespace: "",
                                  Key:       "language",
                                  Val:       "javascript",
                                },
                                html.Attribute{
                                  Namespace: "",
                                  Key:       "type",
                                  Val:       "text/javascript",
                                },
                              },
                            },
                            Type:      0x00000001,
                            DataAtom:  0x00000000,
                            Data:      "\n    ",
                            Namespace: "",
                            Attr:      []html.Attribute(nil),
                          },
                          Type:      0x00000003,
                          DataAtom:  0x0006ff05,
                          Data:      "style",
                          Namespace: "",
                          Attr:      []html.Attribute{
                            html.Attribute{
                              Namespace: "",
                              Key:       "type",
                              Val:       "text/css",
                            },
                          },
                        },
                        Type:      0x00000001,
                        DataAtom:  0x00000000,
                        Data:      "\n",
                        Namespace: "",
                        Attr:      []html.Attribute(nil),
                      },
                      Type:      0x00000003,
                      DataAtom:  0x00017404,
                      Data:      "link",
                      Namespace: "",
                      Attr:      []html.Attribute{
                        html.Attribute{
                          Namespace: "",
                          Key:       "rel",
                          Val:       "shortcut icon",
                        },
                        html.Attribute{
                          Namespace: "",
                          Key:       "href",
                          Val:       "e1csys/mngsrv/favicon.ico",
                        },
                      },
                    },
                    Type:      0x00000001,
                    DataAtom:  0x00000000,
                    Data:      "\n    ",
                    Namespace: "",
                    Attr:      []html.Attribute(nil),
                  },
                  Type:      0x00000003,
                  DataAtom:  0x0004b804,
                  Data:      "meta",
                  Namespace: "",
                  Attr:      []html.Attribute{
                    html.Attribute{
                      Namespace: "",
                      Key:       "http-equiv",
                      Val:       "X-UA-Compatible",
                    },
                    html.Attribute{
                      Namespace: "",
                      Key:       "content",
                      Val:       "IE=edge",
                    },
                  },
                },
                Type:      0x00000001,
                DataAtom:  0x00000000,
                Data:      "\n    ",
                Namespace: "",
                Attr:      []html.Attribute(nil),
              },
              Type:      0x00000003,
              DataAtom:  0x0004b804,
              Data:      "meta",
              Namespace: "",
              Attr:      []html.Attribute{
                html.Attribute{
                  Namespace: "",
                  Key:       "http-equiv",
                  Val:       "Content-Type",
                },
                html.Attribute{
                  Namespace: "",
                  Key:       "content",
                  Val:       "text/html; charset=UTF-8",
                },
              },
            },
            Type:      0x00000001,
            DataAtom:  0x00000000,
            Data:      "\n    ",
            Namespace: "",
            Attr:      []html.Attribute(nil),
          },
          Type:      0x00000003,
          DataAtom:  0x00011005,
          Data:      "title",
          Namespace: "",
          Attr:      []html.Attribute(nil),
        },
        Type:      0x00000001,
        DataAtom:  0x00000000,
        Data:      "\n    ",
        Namespace: "",
        Attr:      []html.Attribute(nil),
      },
      LastChild:   &html.Node{...},
      PrevSibling: (*html.Node)(nil),
      NextSibling: &html.Node{
        Parent:      &html.Node{...},
        FirstChild:  (*html.Node)(nil),
        LastChild:   (*html.Node)(nil),
        PrevSibling: &html.Node{...},
        NextSibling: &html.Node{
          Parent:     &html.Node{...},
          FirstChild: &html.Node{
            Parent:      &html.Node{...},
            FirstChild:  (*html.Node)(nil),
            LastChild:   (*html.Node)(nil),
            PrevSibling: (*html.Node)(nil),
            NextSibling: &html.Node{
              Parent:      &html.Node{...},
              FirstChild:  (*html.Node)(nil),
              LastChild:   (*html.Node)(nil),
              PrevSibling: &html.Node{...},
              NextSibling: &html.Node{
                Parent:      &html.Node{...},
                FirstChild:  (*html.Node)(nil),
                LastChild:   (*html.Node)(nil),
                PrevSibling: &html.Node{...},
                NextSibling: (*html.Node)(nil),
                Type:        0x00000001,
                DataAtom:    0x00000000,
                Data:        "\n\n",
                Namespace:   "",
                Attr:        []html.Attribute(nil),
              },
              Type:      0x00000003,
              DataAtom:  0x00016b03,
              Data:      "div",
              Namespace: "",
              Attr:      []html.Attribute{
                html.Attribute{
                  Namespace: "",
                  Key:       "id",
                  Val:       "openidconnectconfig",
                },
                html.Attribute{
                  Namespace: "",
                  Key:       "style",
                  Val:       "display:none",
                },
              },
            },
            Type:      0x00000001,
            DataAtom:  0x00000000,
            Data:      "\n    ",
            Namespace: "",
            Attr:      []html.Attribute(nil),
          },
          LastChild:   &html.Node{...},
          PrevSibling: &html.Node{...},
          NextSibling: (*html.Node)(nil),
          Type:        0x00000003,
          DataAtom:    0x00002804,
          Data:        "body",
          Namespace:   "",
          Attr:        []html.Attribute{
            html.Attribute{
              Namespace: "",
              Key:       "onload",
              Val:       "initMain();",
            },
          },
        },
        Type:      0x00000001,
        DataAtom:  0x00000000,
        Data:      "\n",
        Namespace: "",
        Attr:      []html.Attribute(nil),
      },
      Type:      0x00000003,
      DataAtom:  0x00033104,
      Data:      "head",
      Namespace: "",
      Attr:      []html.Attribute(nil),
    },
    LastChild:   &html.Node{...},
    PrevSibling: (*html.Node)(nil),
    NextSibling: (*html.Node)(nil),
    Type:        0x00000003,
    DataAtom:    0x00005604,
    Data:        "html",
    Namespace:   "",
    Attr:        []html.Attribute(nil),
  },
  LastChild:   &html.Node{...},
  PrevSibling: (*html.Node)(nil),
  NextSibling: (*html.Node)(nil),
  Type:        0x00000002,
  DataAtom:    0x00000000,
  Data:        "",
  Namespace:   "",
  Attr:        []html.Attribute(nil),
}
Bad
&html.Node{
  Parent:     (*html.Node)(nil),
  FirstChild: &html.Node{
    Parent:     &html.Node{...},
    FirstChild: &html.Node{
      Parent:      &html.Node{...},
      FirstChild:  (*html.Node)(nil),
      LastChild:   (*html.Node)(nil),
      PrevSibling: (*html.Node)(nil),
      NextSibling: &html.Node{
        Parent:     &html.Node{...},
        FirstChild: &html.Node{
          Parent:      &html.Node{...},
          FirstChild:  (*html.Node)(nil),
          LastChild:   (*html.Node)(nil),
          PrevSibling: (*html.Node)(nil),
          NextSibling: &html.Node{
            Parent:     &html.Node{...},
            FirstChild: &html.Node{
              Parent:      &html.Node{...},
              FirstChild:  (*html.Node)(nil),
              LastChild:   (*html.Node)(nil),
              PrevSibling: (*html.Node)(nil),
              NextSibling: (*html.Node)(nil),
              Type:        0x00000001,
              DataAtom:    0x00000000,
              Data:        "1С:Предприятие",
              Namespace:   "",
              Attr:        []html.Attribute(nil),
            },
            LastChild:   &html.Node{...},
            PrevSibling: &html.Node{...},
            NextSibling: &html.Node{
              Parent:      &html.Node{...},
              FirstChild:  (*html.Node)(nil),
              LastChild:   (*html.Node)(nil),
              PrevSibling: &html.Node{...},
              NextSibling: &html.Node{
                Parent:      &html.Node{...},
                FirstChild:  (*html.Node)(nil),
                LastChild:   (*html.Node)(nil),
                PrevSibling: &html.Node{...},
                NextSibling: &html.Node{
                  Parent:      &html.Node{...},
                  FirstChild:  (*html.Node)(nil),
                  LastChild:   (*html.Node)(nil),
                  PrevSibling: &html.Node{...},
                  NextSibling: &html.Node{
                    Parent:      &html.Node{...},
                    FirstChild:  (*html.Node)(nil),
                    LastChild:   (*html.Node)(nil),
                    PrevSibling: &html.Node{...},
                    NextSibling: &html.Node{
                      Parent:      &html.Node{...},
                      FirstChild:  (*html.Node)(nil),
                      LastChild:   (*html.Node)(nil),
                      PrevSibling: &html.Node{...},
                      NextSibling: &html.Node{
                        Parent:      &html.Node{...},
                        FirstChild:  (*html.Node)(nil),
                        LastChild:   (*html.Node)(nil),
                        PrevSibling: &html.Node{...},
                        NextSibling: &html.Node{
                          Parent:      &html.Node{...},
                          FirstChild:  (*html.Node)(nil),
                          LastChild:   (*html.Node)(nil),
                          PrevSibling: &html.Node{...},
                          NextSibling: &html.Node{
                            Parent:     &html.Node{...},
                            FirstChild: &html.Node{
                              Parent:      &html.Node{...},
                              FirstChild:  (*html.Node)(nil),
                              LastChild:   (*html.Node)(nil),
                              PrevSibling: (*html.Node)(nil),
                              NextSibling: (*html.Node)(nil),
                              Type:        0x00000001,
                              DataAtom:    0x00000000,
                              Data:        "\nBODY\n{\n    margin: 0;\n    padding: 0;\n    border: 0;\n    font-family: 'MS Sans Serif';\n    color: #594304;\n}\n",
                              Namespace:   "",
                              Attr:        []html.Attribute(nil),
                            },
                            LastChild:   &html.Node{...},
                            PrevSibling: &html.Node{...},
                            NextSibling: &html.Node{
                              Parent:      &html.Node{...},
                              FirstChild:  (*html.Node)(nil),
                              LastChild:   (*html.Node)(nil),
                              PrevSibling: &html.Node{...},
                              NextSibling: &html.Node{
                                Parent:     &html.Node{...},
                                FirstChild: &html.Node{
                                  Parent:      &html.Node{...},
                                  FirstChild:  (*html.Node)(nil),
                                  LastChild:   (*html.Node)(nil),
                                  PrevSibling: (*html.Node)(nil),
                                  NextSibling: (*html.Node)(nil),
                                  Type:        0x00000001,
                                  DataAtom:    0x00000000,
                                  Data:        "\n    var BaseUrl = window.location.href.substr(\n      0, window.location.href.lastIndexOf(\"/\")) + \"/\";\n\n    var base = \"/analytics/en\";\n    var lang = \"en\";\n    var redirect = true;\n    var openIDRelyingParty = false;\n    var splashTopBrandingImage = false;\n    var splashBottomBrandingImage = false;\n    var hasBranding = false;\n    var exitURL = \"\";\n    var oidcAllowStandardAuthentication = \"\";\n    var version = \"8.3.17.1549\"\n    var vendorPrefix = \"1c.\";\n    var ansQuery = undefined;\n\n    var path = base;\n    var url = window.location.protocol + \"//\" + window.location.host;\n    if (path.slice(-1) !== '/') path += '/';\n    if (base.length !== 0)\n        url += path;\n\n    var paramKeyPrefix = getParameterValue(window.location.href, 'sk');\n    var keyPrefix = (paramKeyPrefix) ? paramKeyPrefix : generateKeyPrefix();\n\n    if (!isStorageSupported())\n    {\n        url += '?sk=' + keyPrefix;\n    }\n\n    function generateKeyPrefix()\n    {\n        function s4()\n        {\n            return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);\n        }\n\n        return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();\n    }\n\n    function removeUrlParameter(strUrl, paramName)\n    {\n        var re = new RegExp('[&]?' + paramName + '=[^&]+', 'gi');\n\n        var result = strUrl.replace(re, '');\n\n        if (result.indexOf('?') === result.length - 1)\n        {\n            result = result.substr(0, result.indexOf('?'));\n        }\n\n        return result;\n    }\n\n    function getParameterValue(strUrl, paramName)\n    {\n        var re = new RegExp('[\\?&]?' + paramName + '=([^&]+)', 'gi');\n\n        var matches = re.exec(strUrl);\n\n        if (matches && matches.length > 1 && matches[1])\n        {\n            return matches[1];\n        }\n\n        return null;\n    }\n\n\n    function writeToStorage(key, value, inQuote, encode)\n    {\n        if (value)\n        {\n            if (inQuote && value.length > 1 &&\n                ((value.charAt(0) === '\"' && value.charAt(value.length - 1) === '\"') ||\n                 (value.charAt(0) === '\\'' && value.charAt(value.length - 1) === '\\'')))\n            {\n                value = value.substring(1, value.length - 1);\n            }\n            if (encode)\n                value = encodeURIComponent(value);\n\n            if (!isStorageSupported())\n            {\n                var expiresDate = new Date();\n                // Время жизни - 1 минута\n                expiresDate = new Date(expiresDate.getTime() + 1000 * 60);\n\n                document.cookie = keyPrefix + '_' + key + '=' + value + ';expires=' +\n                                  expiresDate.toGMTString() + ';Path=' + path;\n            }\n            else\n            {\n                sessionStorage.setItem(key, value);\n            }\n        }\n        else\n        {\n            if (!isStorageSupported())\n            {\n                var expiresDate = new Date();\n                // Время жизни - 1 минута\n                expiresDate = new Date(expiresDate.getTime() + 1000 * 60);\n\n                document.cookie = keyPrefix + '_' + key + '=;expires=' +\n                                  expiresDate.toGMTString() + 'Path=' + path;\n            }\n            else\n            {\n                sessionStorage.setItem(key, value);\n            }\n        }\n    }\n\n    function getFromStorage(key)\n    {\n        if (isStorageSupported())\n        {\n            return sessionStorage.getItem(key)\n        }\n        else\n        {\n            var cookies = document.cookie.split(\";\");\n\n            for (var i = 0; i < cookies.length; i++)\n            {\n                var values = cookies[i].split(\"=\");\n                var str = values[0];\n                str = str.replace(/(^\\s*)|(\\s*$)/g, \"\");\n\n                if (str === keyPrefix + '_' + key)\n                {\n                    return values[1];\n                }\n            }\n        }\n        return null;\n    }\n\n    function deleteFromStorage(key)\n    {\n        if (isStorageSupported())\n        {\n            sessionStorage.removeItem(key);\n        }\n        else\n        {\n            document.cookie = keyPrefix + '_' + key +\n                              \"=;expires=Fri, 21 Dec 1976 04:31:24 GMT;Path=\"\n                              + path;\n        }\n    }\n\n    function isStorageSupported()\n    {\n        var testKey = 'test';\n\n        try\n        {\n            var storage = window.sessionStorage;\n            storage.setItem(testKey, '1');\n            storage.removeItem(testKey);\n\n            return true;\n        }\n        catch (error)\n        {\n            return false;\n        }\n    }\n\n    function writeCookie(cookieName, value, inQuote, encode)\n    {\n        if (value)\n        {\n            if (inQuote && value.length > 1 &&\n                ((value.charAt(0) === '\"' && value.charAt(value.length - 1) === '\"') ||\n                 (value.charAt(0) === '\\'' && value.charAt(value.length - 1) === '\\'')))\n            {\n                value = value.substring(1, value.length - 1);\n            }\n            if (encode)\n                value = encodeURIComponent(value);\n\n            document.cookie = cookieName + '=' + value + ';Path=' + path;\n        }\n        else\n        {\n            document.cookie = cookieName + '=;Path=' + path;\n        }\n    }\n\n    function getCookie(cookieName)\n    {\n        var cookies = document.cookie.split(\";\");\n        for (var i = 0; i < cookies.length; i++)\n        {\n            var values = cookies[i].split(\"=\");\n            var str = values[0];\n            str = str.replace(/(^\\s*)|(\\s*$)/g, \"\");\n            if (str === cookieName)\n                return values[1];\n        }\n        return null;\n    }\n    function deleteCookie(cookieName)\n    {\n        document.cookie = cookieName + \"=;expires=Fri, 21 Dec 1976 04:31:24 GMT;Path=\" + path;\n    }\n\n    function getHash ()\n    {\n        var hash = \"\";\n        if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent) &&\n           (/msie 6\\.0/i).test(navigator.userAgent))\n        {\n            var str = window.location.href;\n            var inStr = false;\n            var strCh = \"\";\n            for (var i = str.length - 1; i >= 0; i--)\n            {\n                var ch = str.charAt(i);\n                if (inStr)\n                {\n                    if (ch === strCh)\n                        inStr = false;\n                    continue;\n                }\n                if (ch === \"\\\"\" || ch === \"'\")\n                {\n                    inStr = true;\n                    strCh = ch;\n                    continue;\n                }\n                if (ch === \"#\")\n                {\n                    hash = str.substr(i + 1);\n                    break;\n                }\n            }\n        }\n        else\n        {\n            hash = window.location.hash;\n        }\n        return hash;\n    }\n\n    function getParams ()\n    {\n        if (window.location.search)\n        {\n            var params = decodeURIComponent(window.location.search).split(\"&\");\n            if (params && params[0].substring(0, 1) === \"?\")\n                params[0] = params[0].substr(1);\n            return params;\n        }\n        else\n        {\n            return [];\n        }\n    }\n\n    function proceedOIDC (enableOpenIDAuth, openIDInfo)\n    {\n        if (getFromStorage('vrs_seance'))\n        {\n            setTimeout(continueLoadHTML.bind(this, true), 50);\n            return false;\n        }\n\n        writeToStorage('vrs_oidcAllowStandardAuthentication', oidcAllowStandardAuthentication);\n\n        if (enableOpenIDAuth)\n            writeToStorage('vrs_oiadenable', '1');\n\n        if (openIDInfo)\n        {\n            var openIDStr = JSON.parse(openIDInfo);\n            for (var key in openIDStr)\n            {\n                var strIDStr = JSON.stringify(openIDStr);\n                writeToStorage('vrs_oauth', strIDStr);\n                setTimeout(continueLoadHTML.bind(this, false), 50);\n                return false;\n            }\n        }\n        return true;\n    }\n\n    function parseParam (param)\n    {\n        var arr = param.split(\"=\");\n        if (arr)\n        {\n            var paramName = arr[0].toUpperCase();\n            var paramVal = null;\n            if (arr.length > 2)\n                paramVal = arr.slice(1).join('=');\n            else if (arr.length > 1)\n                paramVal = arr[1];\n            var paramValUp = paramVal ? paramVal.toUpperCase() : paramVal;\n            return {\n                paramName: paramName,\n                paramVal: paramVal,\n                paramValUp: paramValUp\n            };\n        }\n        return null;\n    }\n\n    function initMain()\n    {\n        var hash = getHash();\n        if (hash)\n        {\n            hash = decodeURIComponent(hash);\n            writeToStorage('vrs_hash', hash, false, true);\n        }\n\n        var params = getParams();\n        var enableOpenIDAuth = openIDRelyingParty;\n        var openIDLogout = false;\n\n        for (var i = 0; i < params.length; i++)\n        {\n            var paramObj = parseParam(params[i]);\n            if (!paramObj)\n                continue;\n\n            var paramName = paramObj.paramName;\n            var paramVal = paramObj.paramVal;\n            var paramValUp = paramObj.paramValUp;\n\n            if (paramName === \"O\" && paramValUp)\n            {\n                var v = (paramValUp === \"LOW\") ? \"true\" : (paramValUp === \"NORMAL\") ? \"false\" : null;\n                if (v !== null)\n                    writeToStorage(\"vrs_slow\", v);\n            }\n            else if (paramName === \"C\")\n                writeToStorage('vrs_param', paramVal, true, true);\n            else if (paramName === \"N\")\n                writeToStorage('vrs_user', paramVal, true, true);\n            else if (paramName === \"P\")\n                writeToStorage('vrs_psw', paramVal, false, true);\n            else if (paramName === \"WA-\")\n                writeToStorage('vrs_nowa');\n            else if (paramName === \"DISABLESTARTUPMESSAGES\")\n                writeToStorage('vrs_disStartMsg');\n            else if (paramName === \"DISPLAYALLFUNCTIONS\")\n                writeToStorage('vrs_displayAllFunctions');\n            else if (paramName === \"TECHNICALSPECIALISTMODE\")\n                writeToStorage('vrs_displayAllFunctions');\n            else if (paramName === \"VL\")\n                writeToStorage('vrs_vl', paramVal);\n            else if (paramName === \"L\")\n                writeToStorage('vrs_ul', paramVal);\n            else if (paramName === \"DISPLAYPERFORMANCE\")\n                writeToStorage('vrs_perf');\n            else if (paramName === \"DISPLAYUSERNOTIFICATIONLIST\")\n                writeToStorage('vrs_displayusernotificationlist');\n            else if (paramName === \"ACCEPTVIDEOCALL\")\n                writeToStorage('vrs_acceptvideocall', paramVal, true, true);\n            else if (paramName === \"DEBUGGERURL\")\n                writeToStorage('vrs_deburl', paramVal, true, true);\n            else if (paramName === \"DEBUG\")\n                writeToStorage('vrs_debug', paramVal || 'TCP');\n            else if (paramName === \"TESTCLIENT\")\n                writeToStorage('vrs_test');\n            else if (paramName === \"TESTCLIENTID\")\n                writeToStorage('vrs_test_id', paramVal || '');\n            else if (paramName === \"Z\")\n                writeToStorage('vrs_zn', paramVal, true, true);\n            else if (paramName === \"OIDA-\")\n                enableOpenIDAuth = false;\n            else if (paramName === \"VRS-SEANCE\")\n                writeToStorage('vrs_seance', paramVal);\n            else if (paramName === \"AUTHOFF\" && openIDRelyingParty)\n                openIDLogout = true;\n            else if (paramName === \"USEPRIVILEGEDMODE\")\n                writeToStorage('vrs_privMode');\n            else if (paramName === \"VRSSESSION2\")\n                writeToStorage('vrs_session2', paramVal);\n            else if (paramName === \"DEBUGGCC\")\n            {\n                var debugGcc = paramValUp || \"1\";\n                if (debugGcc === \"1\" || debugGcc === \"ON\" || debugGcc === \"TRUE\")\n                    writeCookie('vrs_dbggcc');\n                else\n                    deleteCookie('vrs_dbggcc');\n            }\n            else if (paramName === \"SYSTEMWEBCLIENTSTAT\")\n                writeToStorage('vrs_webclientstat');\n            else if (paramName === \"EXECUTE\")\n                writeToStorage('vrs_execute', paramVal);\n            else if (paramName === 'ENABLECHECKSERVERCALLS')\n                writeToStorage('vrs_enablecheckservercalls');\n            else if (paramName === 'OIDCSELECTEDPROVIDER')\n                writeToStorage('vrs_sel_prov', paramVal);\n            else if (paramName === 'DISABLESPLASH' && hasBranding)\n                writeToStorage('vrs_disablesplash');\n            else if (paramName === 'CLITYPE')\n                writeToStorage('vrs_clitype', paramVal);\n            else if (paramName === 'CLIKEY')\n                writeToStorage('vrs_clikey', paramVal);\n            else if (paramName === 'APPID')\n                writeToStorage('vrs_appid', paramVal);\n            else if (paramName === 'OIDCLASTPROV')\n                writeToStorage('vrs_oidclastprov', paramVal);\n            else if (paramName === 'AUTHMESSAGE' && paramVal)\n                alert(paramVal);\n            else if (paramName === 'MAINWINDOWMODE' && paramVal)\n                writeToStorage('vrs_mainwindowmode', paramVal);\n            else if (paramName === \"DISABLEUNRECOVERABLEERRORMESSAGE\")\n                writeToStorage('vrs_disUnrecoverableMsg');\n        }\n\n        if (getFromStorage('vrs_nooida'))\n            enableOpenIDAuth = false;\n        else if(!enableOpenIDAuth)\n            writeToStorage('vrs_nooida', '1');\n\n        // Значение для локейла форматирования\n        writeToStorage('vrs_lang', lang);\n\n        if (splashTopBrandingImage)\n            writeToStorage('vrs_splashTop');\n\n        if (splashBottomBrandingImage)\n            writeToStorage('vrs_splashBottom');\n\n        if (ansQuery !== undefined)\n            writeToStorage('vrs_ansquery', ansQuery);\n\n        if (exitURL)\n            writeToStorage('vrs_exitURL', exitURL, true, true);\n\n        if (openIDLogout)\n        {\n            window.location.replace(\"e1cib/oid2rp?cmd=logout\" + getZoneParam());\n            return;\n        }\n\n        if (removeUrlParameter(window.location.search, 'sk') || hash || redirect)\n        {\n            window.location.replace(url);\n            return;\n        }\n\n        var openIDInfo = document.getElementById('openidconnectconfig').innerHTML;\n        if (openIDInfo && !proceedOIDC(enableOpenIDAuth, openIDInfo))\n            return;\n\n        if (getFromStorage('vrs_afteroidainit') === '1')\n        {\n            deleteFromStorage('vrs_afteroidainit');\n        }\n        else if (enableOpenIDAuth)\n        {\n            writeToStorage('vrs_oida', '1');\n            writeToStorage('vrs_afteroidainit', '1');\n            window.location.replace(\"e1cib/oid2rp?cmd=init\" + getZoneParam());\n            return;\n        }\n        setTimeout(continueLoadHTML.bind(this, true), 50);\n    }\n\n    function getZoneParam()\n    {\n        var zone = getFromStorage(\"vrs_zn\");\n        if (zone)\n            return \"&z=\" + zone;\n        else\n            return \"\";\n    }\n\n    function getHttpRequest()\n    {\n        var httpRequest;\n        if (window.XMLHttpRequest)\n        {\n            httpRequest = new XMLHttpRequest();\n        }\n        else if (window.ActiveXObject)\n        {\n            var progIDs = [\"Msxml2.XMLHTTP\", \"Microsoft.XMLHTTP\"];\n            for (var i = 0; i < progIDs.length; i++)\n            {\n                try\n                {\n                    httpRequest = new ActiveXObject(progIDs[i]);\n                    break;\n                }\n                catch (e)\n                {\n                }\n            }\n        }\n        return httpRequest;\n    }\n\n    function continueLoadHTML(isMainFormHTML)\n    {\n        var httpRequest = getHttpRequest();\n        if (!httpRequest)\n        {\n            alert(\"Данный браузер не может быть использован для работы с программой 1С:Предприятие\");\n            return;\n        }\n\n        var url = (isMainFormHTML ? \"mainform.html\" : \"authform.html\") + \"?sysver=\" + version;\n        httpRequest.open(\"GET\", encodeURI(url), false);\n        httpRequest.setRequestHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n        httpRequest.setRequestHeader(\"Accept\", \"text/html\");\n        httpRequest.send(\"\");\n        var html = httpRequest.responseText;\n\n        // Посылаем событие асинхронному расширению в Chrome перед doc.open,\n        // который уничтожит все навешенные ранее обработчики\n        if (/chrome/i.test(navigator.userAgent))\n        {\n            var startLoadWebClientEvent = new CustomEvent(vendorPrefix + \"startLoadWebClient\");\n            window.dispatchEvent(startLoadWebClientEvent);\n        }\n\n        var doc = document;\n        doc.open(\"text/html\", \"replace\");\n        doc.write(html);\n        doc.close();\n    }\n\n    window.onerror = function(message)\n    {\n        alert(message);\n        return true;\n    }\n    ",
                                  Namespace:   "",
                                  Attr:        []html.Attribute(nil),
                                },
                                LastChild:   &html.Node{...},
                                PrevSibling: &html.Node{...},
                                NextSibling: &html.Node{
                                  Parent:      &html.Node{...},
                                  FirstChild:  (*html.Node)(nil),
                                  LastChild:   (*html.Node)(nil),
                                  PrevSibling: &html.Node{...},
                                  NextSibling: &html.Node{
                                    Parent:      &html.Node{...},
                                    FirstChild:  (*html.Node)(nil),
                                    LastChild:   (*html.Node)(nil),
                                    PrevSibling: &html.Node{...},
                                    NextSibling: &html.Node{
                                      Parent:      &html.Node{...},
                                      FirstChild:  (*html.Node)(nil),
                                      LastChild:   (*html.Node)(nil),
                                      PrevSibling: &html.Node{...},
                                      NextSibling: (*html.Node)(nil),
                                      Type:        0x00000001,
                                      DataAtom:    0x00000000,
                                      Data:        "\n\n",
                                      Namespace:   "",
                                      Attr:        []html.Attribute(nil),
                                    },
                                    Type:      0x00000003,
                                    DataAtom:  0x00016b03,
                                    Data:      "div",
                                    Namespace: "",
                                    Attr:      []html.Attribute{
                                      html.Attribute{
                                        Namespace: "",
                                        Key:       "id",
                                        Val:       "openidconnectconfig",
                                      },
                                      html.Attribute{
                                        Namespace: "",
                                        Key:       "style",
                                        Val:       "display:none",
                                      },
                                    },
                                  },
                                  Type:      0x00000001,
                                  DataAtom:  0x00000000,
                                  Data:      "\n\n\n    ",
                                  Namespace: "",
                                  Attr:      []html.Attribute(nil),
                                },
                                Type:      0x00000003,
                                DataAtom:  0x00021806,
                                Data:      "script",
                                Namespace: "",
                                Attr:      []html.Attribute{
                                  html.Attribute{
                                    Namespace: "",
                                    Key:       "language",
                                    Val:       "javascript",
                                  },
                                  html.Attribute{
                                    Namespace: "",
                                    Key:       "type",
                                    Val:       "text/javascript",
                                  },
                                },
                              },
                              Type:      0x00000001,
                              DataAtom:  0x00000000,
                              Data:      "\n    ",
                              Namespace: "",
                              Attr:      []html.Attribute(nil),
                            },
                            Type:      0x00000003,
                            DataAtom:  0x0006ff05,
                            Data:      "style",
                            Namespace: "",
                            Attr:      []html.Attribute{
                              html.Attribute{
                                Namespace: "",
                                Key:       "type",
                                Val:       "text/css",
                              },
                            },
                          },
                          Type:      0x00000001,
                          DataAtom:  0x00000000,
                          Data:      "\n",
                          Namespace: "",
                          Attr:      []html.Attribute(nil),
                        },
                        Type:      0x00000003,
                        DataAtom:  0x00017404,
                        Data:      "link",
                        Namespace: "",
                        Attr:      []html.Attribute{
                          html.Attribute{
                            Namespace: "",
                            Key:       "rel",
                            Val:       "shortcut icon",
                          },
                          html.Attribute{
                            Namespace: "",
                            Key:       "href",
                            Val:       "e1csys/mngsrv/favicon.ico",
                          },
                        },
                      },
                      Type:      0x00000001,
                      DataAtom:  0x00000000,
                      Data:      "\n    ",
                      Namespace: "",
                      Attr:      []html.Attribute(nil),
                    },
                    Type:      0x00000003,
                    DataAtom:  0x0004b804,
                    Data:      "meta",
                    Namespace: "",
                    Attr:      []html.Attribute{
                      html.Attribute{
                        Namespace: "",
                        Key:       "http-equiv",
                        Val:       "X-UA-Compatible",
                      },
                      html.Attribute{
                        Namespace: "",
                        Key:       "content",
                        Val:       "IE=edge",
                      },
                    },
                  },
                  Type:      0x00000001,
                  DataAtom:  0x00000000,
                  Data:      "\n    ",
                  Namespace: "",
                  Attr:      []html.Attribute(nil),
                },
                Type:      0x00000003,
                DataAtom:  0x0004b804,
                Data:      "meta",
                Namespace: "",
                Attr:      []html.Attribute{
                  html.Attribute{
                    Namespace: "",
                    Key:       "http-equiv",
                    Val:       "Content-Type",
                  },
                  html.Attribute{
                    Namespace: "",
                    Key:       "content",
                    Val:       "text/html; charset=UTF-8",
                  },
                },
              },
              Type:      0x00000001,
              DataAtom:  0x00000000,
              Data:      "\n    ",
              Namespace: "",
              Attr:      []html.Attribute(nil),
            },
            Type:      0x00000003,
            DataAtom:  0x00011005,
            Data:      "title",
            Namespace: "",
            Attr:      []html.Attribute(nil),
          },
          Type:      0x00000001,
          DataAtom:  0x00000000,
          Data:      "\ufeff\n\n\n    ",
          Namespace: "",
          Attr:      []html.Attribute(nil),
        },
        LastChild:   &html.Node{...},
        PrevSibling: &html.Node{...},
        NextSibling: (*html.Node)(nil),
        Type:        0x00000003,
        DataAtom:    0x00002804,
        Data:        "body",
        Namespace:   "",
        Attr:        []html.Attribute{
          html.Attribute{
            Namespace: "",
            Key:       "onload",
            Val:       "initMain();",
          },
        },
      },
      Type:      0x00000003,
      DataAtom:  0x00033104,
      Data:      "head",
      Namespace: "",
      Attr:      []html.Attribute(nil),
    },
    LastChild:   &html.Node{...},
    PrevSibling: (*html.Node)(nil),
    NextSibling: (*html.Node)(nil),
    Type:        0x00000003,
    DataAtom:    0x00005604,
    Data:        "html",
    Namespace:   "",
    Attr:        []html.Attribute(nil),
  },
  LastChild:   &html.Node{...},
  PrevSibling: (*html.Node)(nil),
  NextSibling: (*html.Node)(nil),
  Type:        0x00000002,
  DataAtom:    0x00000000,
  Data:        "",
  Namespace:   "",
  Attr:        []html.Attribute(nil),
}

as you can see, the structures differ after Parse, although it is the same html, apparently there is some feature

@toothrot
Copy link
Contributor

toothrot commented Dec 8, 2020

/cc @namusyaka @nigeltao

@toothrot toothrot added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Dec 8, 2020
@namusyaka
Copy link
Member

Will take a look at this in a few days.

@namusyaka namusyaka self-assigned this Dec 16, 2020
@namusyaka
Copy link
Member

@LazarenkoA Looks like UTF8BOM is attached to the HTML document you are getting over the network. For example, you should get the result you want by removing the BOM in advance as follows:

package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"

	"golang.org/x/net/html"
	"golang.org/x/text/encoding/unicode"
	"golang.org/x/text/transform"
)

var utf8BOM = []byte{0xef, 0xbb, 0xbf}

func main() {
	res, _ := http.Get("https://analytics.demo.1c.ru/analytics")
	defer res.Body.Close()

	if res.StatusCode != 200 {
		log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
	}

	b, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Fatal(err)
	}

	if bytes.HasPrefix(b, utf8BOM) {
		r := transform.NewReader(bytes.NewBuffer(b), unicode.UTF8BOM.NewDecoder())
		var err error
		b, err = ioutil.ReadAll(r)
		if err != nil {
			log.Fatal(err)
		}
	}

	doc, _ := html.Parse(bytes.NewReader(b))
	var buf bytes.Buffer
	if err := html.Render(&buf, doc); err == nil {
		fmt.Println(buf.String())
	}

	res.Body.Close()
}

Also, according to the description in https://pkg.go.dev/golang.org/x/net/html:

Tokenization is done by creating a Tokenizer for an io.Reader r. It is the caller's responsibility to ensure that r provides UTF-8 encoded HTML.

I guess we can mention the UTF8BOM in our document but at least we don't need to take care of the BOM in our implementation.

@LazarenkoA
Copy link
Author

thank you very much

@golang golang locked and limited conversation to collaborators Dec 21, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

5 participants