/**
 * Remove integrity checks from embedded CSS and JavaScript files
 * Belongs to LocalCDN.
 *
 * @author      nobody
 * @since       2020-02-27
 *
 * @license     MPL 2.0
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/.
 */

'use strict';


/**
 * Manipulate DOM
 */

var manipulateDOM = {};


/**
 * Private Methods
 */

manipulateDOM._removeCrossOriginAndIntegrityAttr = function (details) {

    if(!BrowserType.FIREFOX) {
        // Chromium (and other) browsers do not support webRequest.filterResponseData
        // https://bugs.chromium.org/p/chromium/issues/detail?id=487422
        console.warn('[ LocalCDN ] browser.webRequest.filterResponseData not supported by your browser.');
        return;
    }

    let initiatorDomain, listedToManipulateDOM, negateHtmlFilter, filtering;

    initiatorDomain = helpers.extractDomainFromUrl(details.url, true) || Address.EXAMPLE;
    listedToManipulateDOM = stateManager._domainIsListed(initiatorDomain, "manipulate-dom");
    negateHtmlFilter = stateManager.getInvertOption;

    if( ( negateHtmlFilter || listedToManipulateDOM ) && !( negateHtmlFilter && listedToManipulateDOM ) ) {
        filtering = true;
    } else {
        filtering = false;
    }

    // by Jaap (https://gitlab.com/Jaaap)
    let header = details.responseHeaders.find(h => h.name.toLowerCase() === 'content-type');

    if (header && filtering) {

        let mimeType, isAllowlisted;

        mimeType = header.value.replace(/;.*/, '').toLowerCase();
        isAllowlisted = stateManager._domainIsListed(initiatorDomain);

        if (!isAllowlisted && mimeType === 'text/html') {

            let asciiDecoder, decoder, encoder, charset, isFirstData, filter;

            charset = /charset\s*=/.test(header.value) && header.value.replace(/^.*?charset\s*=\s*/, '').replace(/["']?/g, '');

            // Check if charset is supported by TextDecoder()
            if(/charset\s*=/.test(header.value) && !EncodingTypes[charset.toString().toLowerCase()]){
                console.error('[ LocalCDN ] Unsupported charset: ' + charset);
                return;
            }

            asciiDecoder = new TextDecoder('ASCII');
            encoder = new TextEncoder();
            isFirstData = true;
            filter = browser.webRequest.filterResponseData(details.requestId);

            header.value = 'text/html; charset=UTF-8';

            //Note that this will not work if the '<script crossorigin="anonymous" src="dfgsfgd.com">' string is divided into two chunks, but we want to flush this data asap.
            filter.ondata = evt => {
                if (isFirstData) {
                    if (!charset) {
                        //content-type has no charset declared
                        let htmlHead = asciiDecoder.decode(evt.data, {stream: false});
                        let charsetMatch = htmlHead.match(/<meta.*charset=["']?([^>"'\/]+)["'].*[>\/]/i);
                        charset = charsetMatch ? charsetMatch[1] : "UTF-8";
                    }
                    decoder = new TextDecoder(charset);
                }
                //remove crossorigin and integrity attributes
                let str = decoder.decode(evt.data, {stream: true}).replace(/<(link|script)[^>]+>/ig, m => {
                    if (cdnDomainsRE.test(m)) {
                        return m.replace(/\s+(integrity|crossorigin)(="[^"]*"|='[^']*'|=[^"'`=\s]+|)/ig, '');
                    }
                    return m;
                });
                filter.write(encoder.encode(str));
                isFirstData = false;
            }

            filter.onstop = evt => {
                let str = decoder.decode(); //end-of-stream
                filter.write(encoder.encode(str));
                filter.close();
            }
        }
        return {responseHeaders: details.responseHeaders};

    }
};


/**
 * Initializations
 */

 let allowlistedDomains = {};
 let cdnDomainsRE = new RegExp('//(' + Object.keys(mappings.cdn).map(m => m.replace(/\W/g, '\\$&')).join('|') + ')/');


/**
* Event Handlers
*/

chrome.webRequest.onHeadersReceived.addListener(
    manipulateDOM._removeCrossOriginAndIntegrityAttr,
    {'types': [WebRequestType.MAIN_FRAME], 'urls': [Address.ANY]},
    [WebRequest.BLOCKING, WebRequest.RESPONSE_HEADERS]
);