2020-02-27 13:45:29 +01:00
|
|
|
/**
|
|
|
|
* Request Analyzer
|
2020-06-30 18:41:58 +02:00
|
|
|
* Belongs to LocalCDN (since 2020-02-26)
|
|
|
|
* (Origin: Decentraleyes)
|
2020-02-27 13:45:29 +01:00
|
|
|
*
|
|
|
|
* @author Thomas Rientjes
|
|
|
|
* @since 2016-04-11
|
2020-04-14 07:43:25 +02:00
|
|
|
*
|
2020-06-30 18:41:58 +02:00
|
|
|
* @author nobody
|
2020-04-14 07:43:25 +02:00
|
|
|
* @since 2020-02-26
|
|
|
|
*
|
2020-02-27 13:45:29 +01:00
|
|
|
* @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';
|
|
|
|
|
2021-02-17 07:01:08 +01:00
|
|
|
|
2020-02-27 13:45:29 +01:00
|
|
|
/**
|
|
|
|
* Request Analyzer
|
|
|
|
*/
|
|
|
|
|
|
|
|
var requestAnalyzer = {};
|
|
|
|
|
2021-02-17 07:01:08 +01:00
|
|
|
|
2020-02-27 13:45:29 +01:00
|
|
|
/**
|
|
|
|
* Public Methods
|
|
|
|
*/
|
|
|
|
|
|
|
|
requestAnalyzer.isValidCandidate = function (requestDetails, tabDetails) {
|
2021-12-04 07:46:03 +01:00
|
|
|
let initiatorDomain, requestedDomain, isAllowlisted;
|
2020-02-27 13:45:29 +01:00
|
|
|
|
|
|
|
initiatorDomain = helpers.extractDomainFromUrl(tabDetails.url, true);
|
|
|
|
|
|
|
|
if (initiatorDomain === null) {
|
|
|
|
initiatorDomain = Address.EXAMPLE;
|
|
|
|
}
|
|
|
|
|
2021-12-04 07:46:03 +01:00
|
|
|
// If requested Domain not in mappings.js it is not relevant
|
|
|
|
requestedDomain = helpers.extractDomainFromUrl(requestDetails.url, true);
|
|
|
|
if (mappings['cdn'][requestedDomain] === undefined) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-08-07 08:39:10 +02:00
|
|
|
isAllowlisted = helpers.checkAllowlisted(initiatorDomain, requestAnalyzer.allowlistedDomains);
|
2020-10-17 07:09:30 +02:00
|
|
|
if (isAllowlisted) {
|
2020-02-27 13:45:29 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-05-02 06:58:40 +02:00
|
|
|
// Font Awesome injections in Chromium deactivated (https://gitlab.com/nobody42/localcdn/-/issues/67)
|
2021-12-18 09:49:24 +01:00
|
|
|
if (!BrowserType.FIREFOX) {
|
2023-01-17 06:12:18 +01:00
|
|
|
let requestType = requestAnalyzer.chromeSupport(requestDetails.url);
|
|
|
|
if (requestType !== '') {
|
|
|
|
console.warn(`${LogString.PREFIX} ${requestType} ${LogString.NOT_SUPPORTED}`);
|
|
|
|
log.append(tabDetails.url, requestDetails.url, `${requestType} ${LogString.NOT_SUPPORTED}`, true);
|
2020-07-12 15:47:30 +02:00
|
|
|
return false;
|
2021-12-04 07:46:03 +01:00
|
|
|
}
|
2020-05-02 06:58:40 +02:00
|
|
|
}
|
|
|
|
|
2021-12-04 07:46:03 +01:00
|
|
|
// Ignore requests if website is 'yandex.com' and CDN is 'yastatic.net', because website and CDN are the same.
|
2020-07-25 20:34:41 +02:00
|
|
|
if (tabDetails.url.includes('yandex.com') && requestDetails.url.includes('yastatic.net')) {
|
2021-11-27 08:03:00 +01:00
|
|
|
log.append(tabDetails.url, requestDetails.url, LogString.YANDEX, true);
|
2020-07-25 20:34:41 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-02-27 13:45:29 +01:00
|
|
|
// Only requests of type GET can be valid candidates.
|
|
|
|
return requestDetails.method === WebRequest.GET;
|
|
|
|
};
|
|
|
|
|
2023-01-17 06:12:18 +01:00
|
|
|
requestAnalyzer.chromeSupport = function (url) {
|
|
|
|
let value = '';
|
|
|
|
|
|
|
|
if (url.includes('font-awesome')) {
|
|
|
|
value = 'font-awesome';
|
|
|
|
} else if (url.includes('fontawesome')) {
|
|
|
|
value = 'font-awesome';
|
|
|
|
} else if (url.includes('fork-awesome')) {
|
|
|
|
value = 'fork-awesome';
|
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
|
|
|
};
|
|
|
|
|
2021-12-04 07:46:03 +01:00
|
|
|
requestAnalyzer.isGoogleMaterialIcons = function (url) {
|
|
|
|
return url.includes('Material+Icons') || url.includes('materialicons');
|
|
|
|
};
|
|
|
|
|
|
|
|
requestAnalyzer.isGoogleFont = function (domain) {
|
|
|
|
return domain.includes('fonts.googleapis.com') || domain.includes('fonts.gstatic.com');
|
|
|
|
};
|
|
|
|
|
2021-02-21 19:50:41 +01:00
|
|
|
requestAnalyzer.getLocalTarget = function (requestDetails, initiator) {
|
2021-02-17 07:01:08 +01:00
|
|
|
let destinationUrl, destinationHost, destinationPath, hostMappings, basePath,
|
|
|
|
resourceMappings, destinationSearchString;
|
2020-02-27 13:45:29 +01:00
|
|
|
|
2021-02-17 07:01:08 +01:00
|
|
|
destinationSearchString = '';
|
2020-02-27 13:45:29 +01:00
|
|
|
destinationUrl = new URL(requestDetails.url);
|
|
|
|
|
|
|
|
destinationHost = destinationUrl.host;
|
|
|
|
destinationPath = destinationUrl.pathname;
|
2020-06-27 11:50:50 +02:00
|
|
|
if (destinationUrl.search) {
|
|
|
|
destinationSearchString = destinationUrl.search;
|
|
|
|
}
|
2020-02-27 13:45:29 +01:00
|
|
|
|
|
|
|
// Use the proper mappings for the targeted host.
|
2020-10-11 12:52:17 +02:00
|
|
|
hostMappings = mappings.cdn[destinationHost];
|
2020-02-27 13:45:29 +01:00
|
|
|
|
|
|
|
// Resource mapping files are never locally available.
|
|
|
|
if (Resource.MAPPING_EXPRESSION.test(destinationPath)) {
|
2021-12-04 08:52:29 +01:00
|
|
|
return {
|
|
|
|
'result': false,
|
|
|
|
};
|
2020-02-27 13:45:29 +01:00
|
|
|
}
|
|
|
|
|
2021-12-05 07:30:31 +01:00
|
|
|
basePath = requestAnalyzer._matchBasePath(hostMappings, destinationPath)['result'];
|
2020-02-27 13:45:29 +01:00
|
|
|
resourceMappings = hostMappings[basePath];
|
|
|
|
|
|
|
|
if (!resourceMappings) {
|
2021-12-04 08:52:29 +01:00
|
|
|
return {
|
|
|
|
'result': false,
|
|
|
|
};
|
2020-02-27 13:45:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return either the local target's path or false.
|
2021-02-17 07:01:08 +01:00
|
|
|
// eslint-disable-next-line max-len
|
2021-11-27 08:03:00 +01:00
|
|
|
return requestAnalyzer._findLocalTarget(
|
|
|
|
resourceMappings,
|
|
|
|
basePath,
|
|
|
|
destinationHost,
|
|
|
|
destinationPath,
|
|
|
|
destinationSearchString,
|
2021-11-28 09:28:49 +01:00
|
|
|
initiator
|
|
|
|
);
|
2020-02-27 13:45:29 +01:00
|
|
|
};
|
|
|
|
|
2021-02-17 07:01:08 +01:00
|
|
|
|
2020-02-27 13:45:29 +01:00
|
|
|
/**
|
|
|
|
* Private Methods
|
|
|
|
*/
|
|
|
|
|
|
|
|
requestAnalyzer._matchBasePath = function (hostMappings, channelPath) {
|
|
|
|
for (let basePath of Object.keys(hostMappings)) {
|
|
|
|
if (channelPath.startsWith(basePath)) {
|
2021-12-05 07:30:31 +01:00
|
|
|
return {
|
|
|
|
'result': basePath,
|
|
|
|
};
|
2020-02-27 13:45:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-05 07:30:31 +01:00
|
|
|
return {
|
|
|
|
'result': false,
|
|
|
|
};
|
2020-02-27 13:45:29 +01:00
|
|
|
};
|
|
|
|
|
2021-02-17 07:01:08 +01:00
|
|
|
// eslint-disable-next-line max-len
|
2021-02-21 19:50:41 +01:00
|
|
|
requestAnalyzer._findLocalTarget = function (resourceMappings, basePath, channelHost, channelPath, destinationSearchString, initiator) {
|
2021-11-28 09:28:49 +01:00
|
|
|
let resourcePath, versionNumber, resourcePattern, shorthandResource;
|
2020-02-27 13:45:29 +01:00
|
|
|
|
2020-03-29 10:23:16 +02:00
|
|
|
resourcePath = channelPath.replace(basePath, '');
|
2022-12-18 07:45:41 +01:00
|
|
|
if (resourcePath.startsWith('bootstrap')) {
|
|
|
|
resourcePath = resourcePath.replace(Regex.TWITTER_BOOTSTRAP_ALPHA_BETA, '');
|
|
|
|
}
|
2021-04-29 06:29:12 +02:00
|
|
|
|
2021-04-28 05:01:46 +02:00
|
|
|
// Evaluate first in case of version 'latest' and numerals in resource
|
|
|
|
versionNumber = resourcePath.match(Resource.VERSION_EXPRESSION);
|
2021-04-29 06:29:12 +02:00
|
|
|
|
2021-04-28 05:01:46 +02:00
|
|
|
// Handle weird version expressions
|
2021-11-27 08:03:00 +01:00
|
|
|
if (!versionNumber && Resource.SINGLE_NUMBER_EXPRESSION.test(channelPath)) {
|
2022-09-19 18:17:30 +02:00
|
|
|
versionNumber = channelPath.match(/\d{1,2}/);
|
2022-07-17 07:29:47 +02:00
|
|
|
resourcePattern = resourcePath.replaceAll(versionNumber, Resource.VERSION_PLACEHOLDER);
|
2021-11-27 08:03:00 +01:00
|
|
|
versionNumber = [`${versionNumber}.0`];
|
2021-04-29 06:29:12 +02:00
|
|
|
} else {
|
2022-07-17 07:29:47 +02:00
|
|
|
resourcePattern = resourcePath.replaceAll(versionNumber, Resource.VERSION_PLACEHOLDER);
|
2020-10-10 08:07:02 +02:00
|
|
|
}
|
2020-02-27 13:45:29 +01:00
|
|
|
|
2020-06-27 11:50:50 +02:00
|
|
|
shorthandResource = shorthands.specialFiles(channelHost, channelPath, destinationSearchString);
|
2021-12-05 07:30:31 +01:00
|
|
|
if (shorthandResource['result'] !== false) {
|
2022-07-01 06:48:31 +02:00
|
|
|
console.log(`${LogString.PREFIX} ${LogString.REPLACED_RESOURCE} ${shorthandResource.path}`);
|
|
|
|
log.append(initiator, channelHost + channelPath, shorthandResource.path, false);
|
2020-04-14 07:42:56 +02:00
|
|
|
return shorthandResource;
|
2020-04-05 08:55:04 +02:00
|
|
|
}
|
2021-04-29 06:29:12 +02:00
|
|
|
|
2021-05-06 06:39:50 +02:00
|
|
|
if (resourcePattern === undefined) {
|
2021-12-04 07:46:03 +01:00
|
|
|
return {
|
|
|
|
'result': false,
|
|
|
|
};
|
2021-05-06 06:39:50 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 13:45:29 +01:00
|
|
|
for (let resourceMold of Object.keys(resourceMappings)) {
|
2021-11-28 09:28:49 +01:00
|
|
|
if (resourcePattern.startsWith(resourceMold)) {
|
|
|
|
let targetPath, versionDelivered, versionRequested, bundle;
|
|
|
|
targetPath = resourceMappings[resourceMold].path;
|
2022-07-17 07:29:47 +02:00
|
|
|
targetPath = targetPath.replaceAll(Resource.VERSION_PLACEHOLDER, versionNumber);
|
2021-11-28 09:28:49 +01:00
|
|
|
// Replace the requested version with the latest depending on major version
|
|
|
|
versionDelivered = targets.setLastVersion(targetPath, versionNumber);
|
2021-12-05 07:30:31 +01:00
|
|
|
if (versionDelivered === '') {
|
2021-12-04 07:46:03 +01:00
|
|
|
return {
|
|
|
|
'result': false,
|
|
|
|
};
|
2021-11-28 09:28:49 +01:00
|
|
|
}
|
2021-12-05 07:30:31 +01:00
|
|
|
|
2022-07-17 07:29:47 +02:00
|
|
|
targetPath = targetPath.replaceAll(versionNumber, versionDelivered);
|
2021-11-28 09:28:49 +01:00
|
|
|
|
|
|
|
if (versionNumber === null) {
|
|
|
|
versionDelivered = targetPath.match(Resource.VERSION_EXPRESSION).toString();
|
|
|
|
versionRequested = 'latest';
|
|
|
|
} else {
|
|
|
|
versionRequested = versionNumber[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get bundle name
|
|
|
|
bundle = targets.determineBundle(targetPath);
|
|
|
|
if (bundle !== '') {
|
|
|
|
targetPath = requestAnalyzer._getPathOfBundle(initiator, channelHost, channelPath, targetPath, bundle);
|
2022-10-24 06:38:10 +02:00
|
|
|
if (bundle === 'vex (Bundle)' && !targetPath.endsWith('.min.css') && targetPath.endsWith('.css')) {
|
|
|
|
targetPath = targetPath.replace('.css', '.min.css');
|
|
|
|
}
|
2021-11-28 09:28:49 +01:00
|
|
|
}
|
2021-12-05 07:30:31 +01:00
|
|
|
if (targetPath['result'] === false) {
|
2021-11-28 09:28:49 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prepare and return a local target.
|
|
|
|
return {
|
|
|
|
'source': channelHost,
|
|
|
|
'versionRequested': versionRequested,
|
|
|
|
'versionDelivered': versionDelivered,
|
|
|
|
'path': targetPath,
|
|
|
|
'bundle': bundle
|
|
|
|
};
|
2021-11-27 08:03:00 +01:00
|
|
|
}
|
2020-02-27 13:45:29 +01:00
|
|
|
}
|
2020-11-07 08:21:24 +01:00
|
|
|
|
2022-07-01 06:48:31 +02:00
|
|
|
if (!IgnoredHost[channelHost]) {
|
2021-11-27 08:03:00 +01:00
|
|
|
console.warn(`${LogString.PREFIX} ${LogString.MISSING_RESOURCE} ${channelHost}${channelPath}`);
|
2021-02-21 19:51:47 +01:00
|
|
|
log.append(initiator, channelHost + channelPath, '-', true);
|
2020-03-29 10:23:16 +02:00
|
|
|
}
|
2022-07-10 06:55:41 +02:00
|
|
|
|
2022-07-22 05:49:49 +02:00
|
|
|
if (Object.keys(mappings.cdn).includes(helpers.extractDomainFromUrl(initiator, true))) {
|
2022-07-10 06:55:41 +02:00
|
|
|
return {'result': 'blocked'};
|
|
|
|
}
|
|
|
|
|
2021-12-04 07:46:03 +01:00
|
|
|
return {
|
|
|
|
'result': false,
|
|
|
|
};
|
2020-02-27 13:45:29 +01:00
|
|
|
};
|
|
|
|
|
2021-11-28 09:28:49 +01:00
|
|
|
requestAnalyzer._getPathOfBundle = function (initiator, channelHost, channelPath, targetPath, bundle) {
|
2021-11-27 08:03:00 +01:00
|
|
|
let filename = channelPath.split('/').pop();
|
2022-07-30 06:06:12 +02:00
|
|
|
|
2021-11-27 08:03:00 +01:00
|
|
|
if (bundle === 'MathJax (Bundle)' && filename !== 'MathJax.js') {
|
2022-08-08 19:44:56 +02:00
|
|
|
filename = requestAnalyzer._handleMathJax(channelPath, channelHost, initiator);
|
2022-07-30 06:06:12 +02:00
|
|
|
} else if (bundle === 'TinyMCE (Bundle)' && filename !== 'tinymce.min.js') {
|
2022-08-08 19:44:56 +02:00
|
|
|
filename = requestAnalyzer._handleTinyMCE(channelPath, channelHost, initiator);
|
2022-07-30 06:06:12 +02:00
|
|
|
} else if (bundle === 'DataTables (Bundle)') {
|
2023-01-02 18:20:02 +01:00
|
|
|
filename = requestAnalyzer._handleUncompressedFiles(filename);
|
|
|
|
} else if (bundle === 'ScrollMagic (Bundle)' && !filename.endsWith('.min.js')) {
|
|
|
|
filename = requestAnalyzer._handleUncompressedFiles(filename);
|
2024-01-21 07:16:56 +01:00
|
|
|
} else if (bundle === 'Font Awesome (Fonts) (Bundle)') {
|
|
|
|
filename = requestAnalyzer._handleFontawesomeFiles(targetPath, filename);
|
2022-05-23 06:15:05 +02:00
|
|
|
}
|
2022-07-30 06:06:12 +02:00
|
|
|
|
|
|
|
if (filename === false) {
|
|
|
|
return {
|
|
|
|
'result': false,
|
|
|
|
};
|
2022-07-29 18:02:31 +02:00
|
|
|
}
|
2022-07-30 06:06:12 +02:00
|
|
|
|
2021-11-28 09:28:49 +01:00
|
|
|
return helpers.formatFilename(filename.endsWith('.js')
|
|
|
|
? `${targetPath + filename}m`
|
|
|
|
: targetPath + filename);
|
2021-11-27 09:16:20 +01:00
|
|
|
};
|
2021-11-27 08:03:00 +01:00
|
|
|
|
2022-08-08 19:44:56 +02:00
|
|
|
requestAnalyzer._handleMathJax = function (channelPath, channelHost, initiator) {
|
|
|
|
let filename = channelPath.replace(Resource.MATHJAX, '');
|
2022-07-30 06:06:12 +02:00
|
|
|
if (filename.startsWith('/npm/mathjax@3')) {
|
|
|
|
filename = filename.replace('/npm/mathjax@3/', '');
|
|
|
|
}
|
|
|
|
if (filename === 'config/TeX-AMS_HTML.js') {
|
|
|
|
filename = 'config/TeX-AMS_HTML-full.js';
|
|
|
|
}
|
|
|
|
if (!MathJaxFiles[filename] && !MathJax3Files[filename]) {
|
|
|
|
console.warn(`${LogString.PREFIX} ${LogString.MISSING_RESOURCE} ${channelHost + channelPath}`);
|
|
|
|
log.append(initiator, channelHost + channelPath, '-', true);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return filename;
|
|
|
|
};
|
|
|
|
|
2023-01-02 18:20:02 +01:00
|
|
|
requestAnalyzer._handleUncompressedFiles = function (filename) {
|
2022-07-30 06:06:12 +02:00
|
|
|
if (!filename.endsWith('.min.js') && filename.endsWith('.js')) {
|
|
|
|
return filename.replace('.js', '.min.js');
|
2022-07-30 06:14:53 +02:00
|
|
|
} else if (!filename.endsWith('.min.css') && filename.endsWith('.css')) {
|
|
|
|
return filename.replace('.css', '.min.css');
|
2022-07-30 06:06:12 +02:00
|
|
|
}
|
|
|
|
return filename;
|
|
|
|
};
|
|
|
|
|
2022-08-08 19:44:56 +02:00
|
|
|
requestAnalyzer._handleTinyMCE = function (channelPath, channelHost, initiator) {
|
|
|
|
let filename = channelPath.replace(Resource.TINYMCE, '');
|
2022-07-30 06:06:12 +02:00
|
|
|
if (filename.startsWith('plugins/')) {
|
|
|
|
console.warn(`${LogString.PREFIX} ${LogString.MISSING_RESOURCE} ${channelHost + channelPath}`);
|
|
|
|
log.append(initiator, channelHost + channelPath, '-', true);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return filename;
|
|
|
|
};
|
|
|
|
|
2024-01-21 07:16:56 +01:00
|
|
|
requestAnalyzer._handleFontawesomeFiles = function (targetPath, filename) {
|
|
|
|
if (targetPath === 'resources/font-awesome/4.7.0/fonts/') {
|
|
|
|
return filename.replace('fontawesome-webfont.woff', 'fontawesome-webfont.woff2');
|
|
|
|
}
|
|
|
|
return filename;
|
|
|
|
};
|
|
|
|
|
2020-10-17 07:09:30 +02:00
|
|
|
requestAnalyzer._applyAllowlistedDomains = function () {
|
|
|
|
storageManager.type.get(Setting.ALLOWLISTED_DOMAINS, function (items) {
|
|
|
|
requestAnalyzer.allowlistedDomains = items.allowlistedDomains || {};
|
2020-02-27 13:45:29 +01:00
|
|
|
});
|
|
|
|
};
|
2020-05-25 22:30:44 +02:00
|
|
|
requestAnalyzer._applyManipulateDOMDomains = function () {
|
2020-08-30 18:56:36 +02:00
|
|
|
storageManager.type.get(Setting.DOMAINS_MANIPULATE_DOM, function (items) {
|
2020-06-05 07:28:26 +02:00
|
|
|
requestAnalyzer.domainsManipulateDOM = items.domainsManipulateDOM || {};
|
2020-05-25 22:30:44 +02:00
|
|
|
});
|
|
|
|
};
|
2020-02-27 13:45:29 +01:00
|
|
|
|
2021-02-17 07:01:08 +01:00
|
|
|
|
2020-02-27 13:45:29 +01:00
|
|
|
/**
|
|
|
|
* Initializations
|
|
|
|
*/
|
|
|
|
|
2020-10-17 07:09:30 +02:00
|
|
|
requestAnalyzer.allowlistedDomains = {};
|
|
|
|
requestAnalyzer._applyAllowlistedDomains();
|
2020-02-27 13:45:29 +01:00
|
|
|
|
2020-06-05 07:28:26 +02:00
|
|
|
requestAnalyzer.domainsManipulateDOM = {};
|
2020-05-25 22:30:44 +02:00
|
|
|
requestAnalyzer._applyManipulateDOMDomains();
|
2020-08-22 14:58:57 +02:00
|
|
|
|
2021-02-17 07:01:08 +01:00
|
|
|
|
2020-02-27 13:45:29 +01:00
|
|
|
/**
|
|
|
|
* Event Handlers
|
|
|
|
*/
|
|
|
|
|
2020-10-17 07:09:30 +02:00
|
|
|
chrome.storage.onChanged.addListener(requestAnalyzer._applyAllowlistedDomains);
|
2020-05-25 22:30:44 +02:00
|
|
|
chrome.storage.onChanged.addListener(requestAnalyzer._applyManipulateDOMDomains);
|