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';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Request Analyzer
|
|
|
|
*/
|
|
|
|
|
|
|
|
var requestAnalyzer = {};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Public Methods
|
|
|
|
*/
|
|
|
|
|
|
|
|
requestAnalyzer.isValidCandidate = function (requestDetails, tabDetails) {
|
2020-07-12 15:47:30 +02:00
|
|
|
let initiatorDomain, isWhitelisted;
|
2020-02-27 13:45:29 +01:00
|
|
|
|
|
|
|
initiatorDomain = helpers.extractDomainFromUrl(tabDetails.url, true);
|
|
|
|
|
|
|
|
if (initiatorDomain === null) {
|
|
|
|
initiatorDomain = Address.EXAMPLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
isWhitelisted = requestAnalyzer.whitelistedDomains[initiatorDomain];
|
|
|
|
|
|
|
|
if (isWhitelisted) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-05-02 06:58:40 +02:00
|
|
|
// Font Awesome injections in Chromium deactivated (https://gitlab.com/nobody42/localcdn/-/issues/67)
|
2020-08-19 09:58:28 +02:00
|
|
|
if (BrowserType.CHROMIUM) {
|
2020-07-12 15:47:30 +02:00
|
|
|
if (/(font-awesome|fontawesome)/.test(requestDetails.url)) {
|
|
|
|
console.warn('[ LocalCDN ] Font Awesome is not fully supported by your browser.');
|
|
|
|
return false;
|
2020-08-19 09:58:28 +02:00
|
|
|
} else if (requestDetails.url.startsWith('https://fonts.googleapis.com')) {
|
2020-07-12 15:47:30 +02:00
|
|
|
// also valid for Google Material icons
|
|
|
|
console.warn('[ LocalCDN ] Google Material Icons are not fully supported by your browser.');
|
|
|
|
return false;
|
|
|
|
}
|
2020-05-02 06:58:40 +02:00
|
|
|
}
|
|
|
|
|
2020-07-25 20:34:41 +02:00
|
|
|
// Disable LocalCDN if website is 'yandex.com' and CDN is 'yastatic.net', because website and CDN are the same.
|
|
|
|
if (tabDetails.url.includes('yandex.com') && requestDetails.url.includes('yastatic.net')) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-02-27 13:45:29 +01:00
|
|
|
// Only requests of type GET can be valid candidates.
|
|
|
|
return requestDetails.method === WebRequest.GET;
|
|
|
|
};
|
|
|
|
|
|
|
|
requestAnalyzer.getLocalTarget = function (requestDetails) {
|
|
|
|
let destinationUrl, destinationHost, destinationPath, hostMappings, basePath, resourceMappings;
|
2020-06-27 11:50:50 +02:00
|
|
|
let 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.
|
|
|
|
hostMappings = mappings[destinationHost];
|
|
|
|
|
|
|
|
// Resource mapping files are never locally available.
|
|
|
|
if (Resource.MAPPING_EXPRESSION.test(destinationPath)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
basePath = requestAnalyzer._matchBasePath(hostMappings, destinationPath);
|
|
|
|
resourceMappings = hostMappings[basePath];
|
|
|
|
|
|
|
|
if (!resourceMappings) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return either the local target's path or false.
|
2020-06-27 11:50:50 +02:00
|
|
|
return requestAnalyzer._findLocalTarget(resourceMappings, basePath, destinationHost, destinationPath, destinationSearchString);
|
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)) {
|
|
|
|
return basePath;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
2020-06-27 11:50:50 +02:00
|
|
|
requestAnalyzer._findLocalTarget = function (resourceMappings, basePath, channelHost, channelPath, destinationSearchString) {
|
2020-04-14 07:42:56 +02:00
|
|
|
let resourcePath, versionNumber, resourcePattern, filename, shorthandResource;
|
2020-02-27 13:45:29 +01:00
|
|
|
|
2020-08-30 18:56:36 +02:00
|
|
|
storageManager.type.get(Setting.LOGGING, function (items) {
|
2020-03-29 10:23:16 +02:00
|
|
|
requestAnalyzer.logging = items.enableLogging;
|
|
|
|
});
|
2020-02-27 13:45:29 +01:00
|
|
|
|
2020-03-29 10:23:16 +02:00
|
|
|
resourcePath = channelPath.replace(basePath, '');
|
2020-02-27 13:45:29 +01:00
|
|
|
versionNumber = resourcePath.match(Resource.VERSION_EXPRESSION);
|
|
|
|
resourcePattern = resourcePath.replace(versionNumber, Resource.VERSION_PLACEHOLDER);
|
|
|
|
|
2020-06-27 11:50:50 +02:00
|
|
|
shorthandResource = shorthands.specialFiles(channelHost, channelPath, destinationSearchString);
|
2020-04-14 07:42:56 +02:00
|
|
|
if (shorthandResource) {
|
2020-06-27 11:49:45 +02:00
|
|
|
if (requestAnalyzer.logging) {
|
|
|
|
console.log('[ LocalCDN ] Replaced resource: ' + shorthandResource.path);
|
|
|
|
}
|
2020-04-14 07:42:56 +02:00
|
|
|
return shorthandResource;
|
2020-04-05 08:55:04 +02:00
|
|
|
}
|
|
|
|
|
2020-02-27 13:45:29 +01:00
|
|
|
for (let resourceMold of Object.keys(resourceMappings)) {
|
|
|
|
if (resourcePattern.startsWith(resourceMold)) {
|
2020-08-23 07:35:37 +02:00
|
|
|
let targetPath, versionDelivered, versionRequested;
|
2020-02-27 13:45:29 +01:00
|
|
|
|
|
|
|
targetPath = resourceMappings[resourceMold].path;
|
|
|
|
targetPath = targetPath.replace(Resource.VERSION_PLACEHOLDER, versionNumber);
|
2020-03-20 08:14:31 +01:00
|
|
|
// Replace the requested version with the latest depending on major version
|
2020-07-30 07:56:48 +02:00
|
|
|
versionDelivered = helpers.setLastVersion(targetPath, versionNumber).toString();
|
|
|
|
targetPath = targetPath.replace(versionNumber, versionDelivered);
|
|
|
|
|
2020-07-30 08:20:02 +02:00
|
|
|
versionRequested = versionNumber === null ? 'latest' : versionNumber[0];
|
2020-03-16 14:50:13 +01:00
|
|
|
|
2020-08-23 07:35:37 +02:00
|
|
|
if (versionNumber === null) {
|
2020-07-30 07:56:48 +02:00
|
|
|
versionDelivered = targetPath.match(Resource.VERSION_EXPRESSION).toString();
|
2020-02-27 13:45:29 +01:00
|
|
|
}
|
|
|
|
|
2020-03-24 08:03:08 +01:00
|
|
|
// Get bundle name
|
2020-03-25 15:31:33 +01:00
|
|
|
let bundle = helpers.determineBundle(channelPath);
|
2020-08-19 09:58:28 +02:00
|
|
|
if (bundle !== '') {
|
2020-03-24 08:03:08 +01:00
|
|
|
filename = channelPath.split('/').pop();
|
2020-04-05 20:55:19 +02:00
|
|
|
targetPath = ( RegExp('.*\.css$').test(filename) ) ? targetPath + filename : targetPath + filename + 'm';
|
2020-03-24 08:03:08 +01:00
|
|
|
}
|
|
|
|
|
2020-03-29 10:23:16 +02:00
|
|
|
if (requestAnalyzer.logging) {
|
|
|
|
console.log('[ LocalCDN ] Replaced resource: ' + targetPath);
|
|
|
|
}
|
2020-02-27 13:45:29 +01:00
|
|
|
// Prepare and return a local target.
|
|
|
|
return {
|
|
|
|
'source': channelHost,
|
2020-07-30 07:56:48 +02:00
|
|
|
'versionRequested': versionRequested,
|
|
|
|
'versionDelivered': versionDelivered,
|
2020-03-24 08:03:08 +01:00
|
|
|
'path': targetPath,
|
|
|
|
'bundle': bundle
|
2020-02-27 13:45:29 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2020-03-29 10:23:16 +02:00
|
|
|
if (requestAnalyzer.logging) {
|
|
|
|
console.warn('[ LocalCDN ] Missing resource: ' + channelHost + channelPath);
|
|
|
|
}
|
2020-02-27 13:45:29 +01:00
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
requestAnalyzer._applyWhitelistedDomains = function () {
|
2020-08-30 18:56:36 +02:00
|
|
|
storageManager.type.get(Setting.WHITELISTED_DOMAINS, function (items) {
|
2020-02-27 13:45:29 +01:00
|
|
|
requestAnalyzer.whitelistedDomains = items.whitelistedDomains || {};
|
|
|
|
});
|
|
|
|
};
|
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-08-22 14:58:57 +02:00
|
|
|
requestAnalyzer._applyAllowedDomainsGoogleFonts = function () {
|
2020-08-30 18:56:36 +02:00
|
|
|
storageManager.type.get(Setting.ALLOWED_DOMAINS_GOOGLE_FONTS, function (items) {
|
2020-08-22 14:58:57 +02:00
|
|
|
requestAnalyzer.domainsGoogleFonts = items.allowedDomainsGoogleFonts || {};
|
|
|
|
});
|
|
|
|
};
|
2020-02-27 13:45:29 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Initializations
|
|
|
|
*/
|
|
|
|
|
|
|
|
requestAnalyzer.whitelistedDomains = {};
|
|
|
|
requestAnalyzer._applyWhitelistedDomains();
|
|
|
|
|
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
|
|
|
|
|
|
|
requestAnalyzer.domainsGoogleFonts = {};
|
|
|
|
requestAnalyzer._applyAllowedDomainsGoogleFonts();
|
2020-02-27 13:45:29 +01:00
|
|
|
/**
|
|
|
|
* Event Handlers
|
|
|
|
*/
|
|
|
|
|
|
|
|
chrome.storage.onChanged.addListener(requestAnalyzer._applyWhitelistedDomains);
|
2020-05-25 22:30:44 +02:00
|
|
|
chrome.storage.onChanged.addListener(requestAnalyzer._applyManipulateDOMDomains);
|
2020-08-22 14:58:57 +02:00
|
|
|
chrome.storage.onChanged.addListener(requestAnalyzer._applyAllowedDomainsGoogleFonts);
|