2020-05-02 13:56:49 +02:00
/ * *
* Remove integrity checks from embedded CSS and JavaScript files
* Belongs to LocalCDN .
*
2020-06-30 18:41:58 +02:00
* @ author nobody
2020-05-02 13:56:49 +02:00
* @ 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 ) {
2020-08-09 10:10:12 +02:00
if ( ! BrowserType . FIREFOX ) {
// Chromium (and other) browsers do not support webRequest.filterResponseData
2020-06-25 07:54:17 +02:00
// 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 ;
2020-05-25 22:53:46 +02:00
initiatorDomain = helpers . extractDomainFromUrl ( details . url , true ) || Address . EXAMPLE ;
2020-05-27 18:33:29 +02:00
listedToManipulateDOM = stateManager . _domainIsListed ( initiatorDomain , "manipulate-dom" ) ;
2020-06-25 07:54:17 +02:00
negateHtmlFilter = stateManager . getInvertOption ;
if ( ( negateHtmlFilter || listedToManipulateDOM ) && ! ( negateHtmlFilter && listedToManipulateDOM ) ) {
filtering = true ;
} else {
filtering = false ;
}
2020-05-25 22:53:46 +02:00
2020-05-02 13:56:49 +02:00
// by Jaap (https://gitlab.com/Jaaap)
let header = details . responseHeaders . find ( h => h . name . toLowerCase ( ) === 'content-type' ) ;
2020-06-25 07:54:17 +02:00
if ( header && filtering ) {
2020-05-02 13:56:49 +02:00
2020-10-17 07:09:30 +02:00
let mimeType , isAllowlisted ;
2020-05-02 13:56:49 +02:00
mimeType = header . value . replace ( /;.*/ , '' ) . toLowerCase ( ) ;
2020-10-17 07:09:30 +02:00
isAllowlisted = stateManager . _domainIsListed ( initiatorDomain ) ;
2020-05-02 13:56:49 +02:00
2020-10-17 07:09:30 +02:00
if ( ! isAllowlisted && mimeType === 'text/html' ) {
2020-05-02 13:56:49 +02:00
2020-05-19 17:38:07 +02:00
let asciiDecoder , decoder , encoder , charset , isFirstData , filter ;
2020-05-19 17:26:16 +02:00
2020-05-21 19:44:33 +02:00
charset = /charset\s*=/ . test ( header . value ) && header . value . replace ( /^.*?charset\s*=\s*/ , '' ) . replace ( /["']?/g , '' ) ;
2020-05-22 07:19:01 +02:00
// Check if charset is supported by TextDecoder()
2020-05-23 16:56:16 +02:00
if ( /charset\s*=/ . test ( header . value ) && ! EncodingTypes [ charset . toString ( ) . toLowerCase ( ) ] ) {
console . error ( '[ LocalCDN ] Unsupported charset: ' + charset ) ;
return ;
2020-05-22 07:19:01 +02:00
}
2020-05-23 16:56:16 +02:00
asciiDecoder = new TextDecoder ( 'ASCII' ) ;
encoder = new TextEncoder ( ) ;
2020-05-19 17:26:16 +02:00
isFirstData = true ;
filter = browser . webRequest . filterResponseData ( details . requestId ) ;
2020-05-02 13:56:49 +02:00
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 => {
2020-05-19 17:26:16 +02:00
if ( isFirstData ) {
if ( ! charset ) {
//content-type has no charset declared
let htmlHead = asciiDecoder . decode ( evt . data , { stream : false } ) ;
2020-05-25 06:19:17 +02:00
let charsetMatch = htmlHead . match ( /<meta.*charset=["']?([^>"'\/]+)["'].*[>\/]/i ) ;
2020-05-19 17:26:16 +02:00
charset = charsetMatch ? charsetMatch [ 1 ] : "UTF-8" ;
}
decoder = new TextDecoder ( charset ) ;
}
2020-05-02 13:56:49 +02:00
//remove crossorigin and integrity attributes
let str = decoder . decode ( evt . data , { stream : true } ) . replace ( /<(link|script)[^>]+>/ig , m => {
2020-05-19 17:26:16 +02:00
if ( cdnDomainsRE . test ( m ) ) {
2020-12-06 07:52:25 +01:00
return m . replace ( /\s+(integrity|crossorigin)(="[^"]*"|='[^']*'|=[^"'`=>\s]+|)/ig , '' ) ;
2020-05-19 17:26:16 +02:00
}
return m ;
} ) ;
2020-05-02 13:56:49 +02:00
filter . write ( encoder . encode ( str ) ) ;
2020-05-19 17:26:16 +02:00
isFirstData = false ;
2020-05-02 13:56:49 +02:00
}
filter . onstop = evt => {
let str = decoder . decode ( ) ; //end-of-stream
filter . write ( encoder . encode ( str ) ) ;
filter . close ( ) ;
}
}
2020-05-03 07:32:08 +02:00
return { responseHeaders : details . responseHeaders } ;
2020-05-02 13:56:49 +02:00
}
} ;
/ * *
* Initializations
* /
2020-10-17 07:09:30 +02:00
let allowlistedDomains = { } ;
2020-10-11 12:52:17 +02:00
let cdnDomainsRE = new RegExp ( '//(' + Object . keys ( mappings . cdn ) . map ( m => m . replace ( /\W/g , '\\$&' ) ) . join ( '|' ) + ')/' ) ;
2020-05-02 13:56:49 +02:00
/ * *
* Event Handlers
* /
chrome . webRequest . onHeadersReceived . addListener (
manipulateDOM . _removeCrossOriginAndIntegrityAttr ,
{ 'types' : [ WebRequestType . MAIN _FRAME ] , 'urls' : [ Address . ANY ] } ,
2020-05-19 17:26:16 +02:00
[ WebRequest . BLOCKING , WebRequest . RESPONSE _HEADERS ]
2020-05-02 13:56:49 +02:00
) ;