From ec7d661e437090feba590669cbc22c4ea0d64f3a Mon Sep 17 00:00:00 2001 From: mizzick Date: Wed, 2 Aug 2017 22:09:22 +0300 Subject: [PATCH] #799 Custom filters --- Extension/_locales/en/messages.json | 15 ++++ Extension/lib/content-message-handler.js | 3 + Extension/lib/filter/antibanner.js | 101 ++++++++++++++++++++--- Extension/lib/filter/subscription.js | 96 ++++++++++++++++++++- Extension/lib/pages/options.js | 9 ++ Extension/lib/ui-service.js | 33 ++++++++ Extension/lib/utils/notifier.js | 2 + Extension/pages/options.html | 4 + 8 files changed, 252 insertions(+), 11 deletions(-) diff --git a/Extension/_locales/en/messages.json b/Extension/_locales/en/messages.json index 31afff493f..f198e08a72 100644 --- a/Extension/_locales/en/messages.json +++ b/Extension/_locales/en/messages.json @@ -167,6 +167,9 @@ "options_update_antibanner_filters": { "message": "Check for filter updates" }, + "options_add_custom_filter": { + "message": "Add custom filter" + }, "options_edit_antibanner_filters": { "message": "All filters" }, @@ -542,6 +545,18 @@ "alert_popup_filter_enabled_text": { "message": "\u00ab$1\u00bb has been activated automatically" }, + "alert_popup_custom_filter_added_title": { + "message": "Custom filter downloaded" + }, + "alert_popup_custom_filter_added_text": { + "message": "Custom filter downloaded successfully" + }, + "alert_popup_custom_filter_error_title": { + "message": "Custom filter error" + }, + "alert_popup_custom_filter_error_text": { + "message": "Custom filter downloading error" + }, "filter_download_title": { "message": "Filters database is loading..." }, diff --git a/Extension/lib/content-message-handler.js b/Extension/lib/content-message-handler.js index da8221ad3b..55ce7f7035 100644 --- a/Extension/lib/content-message-handler.js +++ b/Extension/lib/content-message-handler.js @@ -195,6 +195,9 @@ case 'checkAntiBannerFiltersUpdate': adguard.ui.checkFiltersUpdates(); break; + case 'addCustomFilter': + adguard.ui.addCustomFilter(message.url); + break; case 'changeDefaultWhiteListMode': adguard.whitelist.changeDefaultWhiteListMode(message.enabled); break; diff --git a/Extension/lib/filter/antibanner.js b/Extension/lib/filter/antibanner.js index abb10d76d9..2300ee7ed3 100644 --- a/Extension/lib/filter/antibanner.js +++ b/Extension/lib/filter/antibanner.js @@ -231,10 +231,11 @@ adguard.antiBannerService = (function (adguard) { /** * Select filters for update. It depends on the time of last update. * @param forceUpdate Force update flag. - * @returns {Array} + * @returns object */ function selectFilterIdsToUpdate(forceUpdate) { var filterIds = []; + var customFilterIds = []; var filters = adguard.subscriptions.getFilters(); for (var i = 0; i < filters.length; i++) { var filter = filters[i]; @@ -242,11 +243,19 @@ adguard.antiBannerService = (function (adguard) { // Check filters update period (or forceUpdate flag) var needUpdate = forceUpdate || (!filter.lastCheckTime || (Date.now() - filter.lastCheckTime) >= UPDATE_FILTERS_PERIOD); if (needUpdate) { - filterIds.push(filter.filterId); + if (filter.customUrl) { + customFilterIds.push(filter.filterId); + } else { + filterIds.push(filter.filterId); + } } } } - return filterIds; + + return { + filterIds: filterIds, + customFilterIds: customFilterIds + }; } /** @@ -274,16 +283,19 @@ adguard.antiBannerService = (function (adguard) { adguard.console.info("Start checking filters updates"); // Select filters for update - var filterIdsToUpdate = selectFilterIdsToUpdate(forceUpdate); + var toUpdate = selectFilterIdsToUpdate(forceUpdate); + var filterIdsToUpdate = toUpdate.filterIds; + var customFilterIdsToUpdate = toUpdate.customFilterIds; - if (filterIdsToUpdate.length === 0) { + var totalToUpdate = filterIdsToUpdate.length + customFilterIdsToUpdate.length; + if (totalToUpdate === 0) { if (successCallback) { successCallback([]); return; } } - adguard.console.info("Checking updates for {0} filters", filterIdsToUpdate.length); + adguard.console.info("Checking updates for {0} filters", totalToUpdate); // Load filters with changed version var loadFiltersFromBackendCallback = function (filterMetadataList) { @@ -296,7 +308,10 @@ adguard.antiBannerService = (function (adguard) { filters.push(filter); } } - successCallback(filters); + + updateCustomFilters(customFilterIdsToUpdate, function (customFilters) { + successCallback(filters.concat(customFilters)); + }); } else { errorCallback(); } @@ -326,6 +341,42 @@ adguard.antiBannerService = (function (adguard) { loadFiltersMetadataFromBackend(filterIdsToUpdate, onLoadFilterMetadataList); }; + /** + * Update filters with custom urls + * + * @param customFilterIds + * @param callback + */ + function updateCustomFilters (customFilterIds, callback) { + if (customFilterIds.length === 0) { + callback([]); + return; + } + + var dfds = []; + var filters = []; + for (var i = 0; i < customFilterIds.length; i++) { + var filter = adguard.subscriptions.getFilter(customFilterIds[i]); + filters.push(filter); + + dfds.push((function (filterUrl) { + var dfd = new adguard.utils.Promise(); + + adguard.subscriptions.updateCustomFilter(filterUrl, function () { + dfd.resolve(); + }); + + return dfd; + })(filter.customUrl)); + + } + + adguard.utils.Promise.all(dfds).then(function () { + adguard.console.info("Custom filters updated"); + callback(filters); + }); + } + /** * Resets all filters versions */ @@ -1462,9 +1513,7 @@ adguard.filters = (function (adguard) { var filterMetadata = findFilterMetadataBySubscriptionUrl(subscriptionUrl); if (filterMetadata) { - addAndEnableFilters([filterMetadata.filterId]); - } else { // Load filter rules @@ -1477,6 +1526,36 @@ adguard.filters = (function (adguard) { } }; + /** + * Loads filter rules from url, then tries to parse header to filter metadata + * and adds filter object to subscriptions from it. + * These custom filters will have special attribute customUrl, from there it could be downloaded and updated. + * + * @param url custom url, there rules are + * @param successCallback + * @param errorCallback + */ + var loadCustomFilter = function (url, successCallback, errorCallback) { + adguard.console.info('Downloading custom filter from {0}', url); + + adguard.subscriptions.updateCustomFilter(url, function (filterId) { + if (filterId) { + antiBannerService.addAntiBannerFilter(filterId, function (success) { + if (success) { + enableFilter(filterId); + + adguard.console.info('Custom filter downloaded and enabled'); + successCallback(); + } else { + errorCallback(); + } + }); + } else { + errorCallback(); + } + }); + }; + return { start: start, @@ -1497,7 +1576,9 @@ adguard.filters = (function (adguard) { removeFilter: removeFilter, findFilterMetadataBySubscriptionUrl: findFilterMetadataBySubscriptionUrl, - processAbpSubscriptionUrl: processAbpSubscriptionUrl + processAbpSubscriptionUrl: processAbpSubscriptionUrl, + + loadCustomFilter: loadCustomFilter }; })(adguard); \ No newline at end of file diff --git a/Extension/lib/filter/subscription.js b/Extension/lib/filter/subscription.js index d36d4766da..95c7ba981c 100644 --- a/Extension/lib/filter/subscription.js +++ b/Extension/lib/filter/subscription.js @@ -107,6 +107,99 @@ adguard.subscriptions = (function (adguard) { return new SubscriptionFilter(filterId, groupId, defaultName, defaultDescription, homepage, version, timeUpdated, displayNumber, languages, expires, subscriptionUrl); }; + /** + * Parses filter metadata from rules header + * + * @param rules + * @returns object + */ + var parseFilterDataFromHeader = function (rules) { + function parseTag(tagName) { + var result = ''; + + //Look up only 50 first lines + for (var i = 0; i < 50; i++) { + var r = rules[i]; + + var search = '! ' + tagName + ': '; + var indexOf = r.indexOf(search); + if (indexOf >= 0) { + result = r.substring(indexOf + search.length); + } + } + + return result; + } + + return { + name: parseTag('Title'), + description: parseTag('Description'), + homepage: parseTag('Homepage'), + version: parseTag('Version'), + timeUpdated: parseTag('TimeUpdated'), + expires: parseTag('Expires') + } + }; + + var addFilterId = function () { + var max = 0; + filters.forEach(function (f) { + if (f.filterId > max) { + max = f.filterId; + } + }); + + return max >= 1000 ? max + 1 : 1000; + }; + + /** + * Adds or updates custom filter + * + * @param url subscriptionUrl + * @param callback + */ + var updateCustomFilter = function (url, callback) { + + adguard.backend.loadFilterRulesBySubscriptionUrl(url, function (rules) { + //Check if filter from this url was added before + var filter = filters.find(function (f) { + return f.customUrl === url; + }); + + if (!filter) { + var filterData = parseFilterDataFromHeader(rules); + + var filterId = addFilterId(); + var groupId = 0; + var defaultName = filterData.name; + var defaultDescription = filterData.description; + var homepage = filterData.homepage; + var version = filterData.version; + var timeUpdated = parseTimeUpdated(filterData.timeUpdated); + var expires = filterData.expires - 0; + var subscriptionUrl = url; + var languages = []; + var displayNumber = 0; + + filter = new SubscriptionFilter(filterId, groupId, defaultName, defaultDescription, homepage, version, timeUpdated, displayNumber, languages, expires, subscriptionUrl); + filter.loaded = true; + //custom filters have special field + filter.customUrl = url; + + filters.push(filter); + filtersMap[filter.filterId] = filter; + } + + adguard.rulesStorage.write(filter.filterId, rules, function () { + callback(filter.filterId); + }); + + }, function (request, cause) { + adguard.console.error("Error download filter by url {0}, cause: {1} {2}", url, request.statusText, cause || ""); + callback(); + }); + }; + /** * Load groups and filters metadata * @@ -287,7 +380,8 @@ adguard.subscriptions = (function (adguard) { getGroups: getGroups, getFilters: getFilters, getFilter: getFilter, - createSubscriptionFilterFromJSON: createSubscriptionFilterFromJSON + createSubscriptionFilterFromJSON: createSubscriptionFilterFromJSON, + updateCustomFilter: updateCustomFilter }; })(adguard); diff --git a/Extension/lib/pages/options.js b/Extension/lib/pages/options.js index 21874890fe..3ea4344b3d 100644 --- a/Extension/lib/pages/options.js +++ b/Extension/lib/pages/options.js @@ -739,6 +739,14 @@ var AntiBannerFilters = function (options) { }); } + function addCustomFilter(e) { + e.preventDefault(); + + var url = $('#customFilterUrl').val(); + contentPage.sendMessage({type: 'addCustomFilter', url: url}, function () { + }); + } + function setLastUpdatedTimeText(lastUpdateTime) { if (lastUpdateTime && lastUpdateTime > loadedFiltersInfo.lastUpdateTime) { loadedFiltersInfo.lastUpdateTime = lastUpdateTime; @@ -788,6 +796,7 @@ var AntiBannerFilters = function (options) { // Bind events $(document).on('change', '.filters-list [name="filterId"]', toggleFilterState); $('#updateAntiBannerFilters').on('click', updateAntiBannerFilters); + $('#addCustomFilter').on('click', addCustomFilter); updateRulesCountInfo(options.rulesInfo); diff --git a/Extension/lib/ui-service.js b/Extension/lib/ui-service.js index 138e1cc248..4d198a1e99 100644 --- a/Extension/lib/ui-service.js +++ b/Extension/lib/ui-service.js @@ -469,6 +469,20 @@ adguard.ui = (function (adguard) { // jshint ignore:line }; } + function getCustomFilterDownloadResultMessage(success) { + var title = success + ? adguard.i18n.getMessage("alert_popup_custom_filter_added_title") + : adguard.i18n.getMessage("alert_popup_custom_filter_error_title"); + var text = success + ? adguard.i18n.getMessage("alert_popup_custom_filter_added_text") + : adguard.i18n.getMessage("alert_popup_custom_filter_error_text"); + + return { + title: title, + text: text + }; + } + var updateTabIconAndContextMenu = function (tab, reloadFrameData) { if (reloadFrameData) { adguard.frames.reloadFrameData(tab); @@ -586,6 +600,14 @@ adguard.ui = (function (adguard) { // jshint ignore:line }); }; + var addCustomFilter = function (url) { + adguard.filters.loadCustomFilter(url, function () { + adguard.listeners.notifyListeners(adguard.listeners.ADDED_CUSTOM_FILTER_SHOW_POPUP); + }, function () { + adguard.listeners.notifyListeners(adguard.listeners.ERROR_DOWNLOAD_CUSTOM_FILTER_SHOW_POPUP); + }); + }; + var openAssistant = function (selectElement) { var options = getAssistantOptions(); @@ -721,6 +743,16 @@ adguard.ui = (function (adguard) { // jshint ignore:line } }); + //custom filters events + adguard.listeners.addListener(function (event) { + if (event === adguard.listeners.ADDED_CUSTOM_FILTER_SHOW_POPUP || + event === adguard.listeners.ERROR_DOWNLOAD_CUSTOM_FILTER_SHOW_POPUP) { + + var result = getCustomFilterDownloadResultMessage(event === adguard.listeners.ADDED_CUSTOM_FILTER_SHOW_POPUP); + showAlertMessagePopup(result.title, result.text); + } + }); + //close all page on unload adguard.unload.when(closeAllPages); @@ -740,6 +772,7 @@ adguard.ui = (function (adguard) { // jshint ignore:line changeApplicationFilteringDisabled: changeApplicationFilteringDisabled, checkFiltersUpdates: checkFiltersUpdates, + addCustomFilter: addCustomFilter, openAssistant: openAssistant, openTab: openTab, diff --git a/Extension/lib/utils/notifier.js b/Extension/lib/utils/notifier.js index c1242807dd..08142336e7 100644 --- a/Extension/lib/utils/notifier.js +++ b/Extension/lib/utils/notifier.js @@ -40,6 +40,8 @@ adguard.listeners = (function () { UPDATE_USER_FILTER_RULES: "event.update.user.filter.rules", UPDATE_WHITELIST_FILTER_RULES: "event.update.whitelist.filter.rules", CONTENT_BLOCKER_UPDATED: "event.content.blocker.updated", + ADDED_CUSTOM_FILTER_SHOW_POPUP: "event.custom.filter.added.show.popup", + ERROR_DOWNLOAD_CUSTOM_FILTER_SHOW_POPUP: "event.custom.filter.error.show.popup", // Log events TAB_ADDED: 'log.tab.added', TAB_CLOSE: 'log.tab.close', diff --git a/Extension/pages/options.html b/Extension/pages/options.html index 344f2e324f..38e8bb3451 100644 --- a/Extension/pages/options.html +++ b/Extension/pages/options.html @@ -101,6 +101,10 @@
+
+ + +