diff --git a/include/AdblockPlus.h b/include/AdblockPlus.h index 4ec66280..d80f71bd 100644 --- a/include/AdblockPlus.h +++ b/include/AdblockPlus.h @@ -32,6 +32,7 @@ #include #include +#include #include #include #include diff --git a/include/AdblockPlus/FilterEngine.h b/include/AdblockPlus/FilterEngine.h index dc533098..c7d4af48 100644 --- a/include/AdblockPlus/FilterEngine.h +++ b/include/AdblockPlus/FilterEngine.h @@ -187,7 +187,6 @@ namespace AdblockPlus * It handles: * - Filter management and matching. * - Subscription management and synchronization. - * - Update checks for the application. */ class FilterEngine { @@ -225,18 +224,6 @@ namespace AdblockPlus */ typedef int32_t ContentTypeMask; - /** - * Callback type invoked when an update becomes available. - * The parameter is the download URL of the update. - */ - typedef std::function UpdateAvailableCallback; - - /** - * Callback type invoked when a manually triggered update check finishes. - * The parameter is an optional error message. - */ - typedef std::function UpdateCheckDoneCallback; - /** * Callback type invoked when the filters change. * The first parameter is the action event code (see @@ -287,6 +274,12 @@ namespace AdblockPlus */ typedef std::function OnCreatedCallback; + /** + * Callback type for evaluating JS expression. + * The parameter is the JS file name containing the expression. + */ + typedef std::function EvaluateCallback; + /** * Asynchronously constructs FilterEngine. * @param jsEngine `JsEngine` instance used to run JavaScript code @@ -296,6 +289,7 @@ namespace AdblockPlus * @param parameters optional creation parameters. */ static void CreateAsync(const JsEnginePtr& jsEngine, + const EvaluateCallback& evaluateCallback, const OnCreatedCallback& onCreated, const CreationParameters& parameters = CreationParameters()); @@ -476,32 +470,6 @@ namespace AdblockPlus */ std::string GetHostFromURL(const std::string& url) const; - /** - * Sets the callback invoked when an application update becomes available. - * @param callback Callback to invoke. - */ - void SetUpdateAvailableCallback(const UpdateAvailableCallback& callback); - - /** - * Removes the callback invoked when an application update becomes - * available. - */ - void RemoveUpdateAvailableCallback(); - - /** - * Forces an immediate update check. - * `FilterEngine` will automatically check for updates in regular intervals, - * so applications should only call this when the user triggers an update - * check manually. - * @param callback Optional callback to invoke when the update check is - * finished. The string parameter will be empty when the update check - * succeeded, or contain an error message if it failed. - * Note that the callback will be invoked whether updates are - * available or not - to react to updates being available, use - * `FilterEngine::SetUpdateAvailableCallback()`. - */ - void ForceUpdateCheck(const UpdateCheckDoneCallback& callback = UpdateCheckDoneCallback()); - /** * Sets the callback invoked when the filters change. * @param callback Callback to invoke. @@ -558,7 +526,6 @@ namespace AdblockPlus private: JsEnginePtr jsEngine; bool firstRun; - int updateCheckId; static const std::map contentTypes; explicit FilterEngine(const JsEnginePtr& jsEngine); diff --git a/include/AdblockPlus/Platform.h b/include/AdblockPlus/Platform.h index dd835471..c7272150 100644 --- a/include/AdblockPlus/Platform.h +++ b/include/AdblockPlus/Platform.h @@ -25,8 +25,12 @@ #include "AppInfo.h" #include "Scheduler.h" #include "FilterEngine.h" +#include "Updater.h" #include #include +#include +#include +#include namespace AdblockPlus { @@ -108,6 +112,11 @@ namespace AdblockPlus */ FilterEngine& GetFilterEngine(); + /** + * Retrieves the Updater component instance. + */ + Updater& GetUpdater(); + typedef std::function WithTimerCallback; virtual void WithTimer(const WithTimerCallback&); @@ -130,6 +139,10 @@ namespace AdblockPlus std::mutex modulesMutex; std::shared_ptr jsEngine; std::shared_future filterEngine; + std::shared_ptr updater; + std::set evaluatedJsSources; + std::mutex evaluatedJsSourcesMutex; + std::function GetEvaluateCallback(); }; /** diff --git a/include/AdblockPlus/Updater.h b/include/AdblockPlus/Updater.h new file mode 100644 index 00000000..6b9593ad --- /dev/null +++ b/include/AdblockPlus/Updater.h @@ -0,0 +1,100 @@ +/* + * This file is part of Adblock Plus , + * Copyright (C) 2006-present eyeo GmbH + * + * Adblock Plus is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * Adblock Plus is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Adblock Plus. If not, see . + */ + +#ifndef ADBLOCK_PLUS_UPDATER_H +#define ADBLOCK_PLUS_UPDATER_H + +#include +#include +#include +#include + +namespace AdblockPlus +{ + /** + * Component of libadblockplus responsible for Update checks for the application. + */ + class Updater + { + public: + /** + * Callback type invoked when an update becomes available. + * The parameter is the download URL of the update. + */ + typedef std::function UpdateAvailableCallback; + + /** + * Callback type invoked when a manually triggered update check finishes. + * The parameter is an optional error message. + */ + typedef std::function UpdateCheckDoneCallback; + + /** + * Sets the callback invoked when an application update becomes available. + * @param callback Callback to invoke. + */ + void SetUpdateAvailableCallback(const UpdateAvailableCallback& callback); + + /** + * Removes the callback invoked when an application update becomes + * available. + */ + void RemoveUpdateAvailableCallback(); + + /** + * Callback type for evaluating JS expression. + * The parameter is the JS file name containing the expression. + */ + typedef std::function EvaluateCallback; + + /** + * Forces an immediate update check. + * `Updater` will automatically check for updates in regular intervals, + * so applications should only call this when the user triggers an update + * check manually. + * @param callback Optional callback to invoke when the update check is + * finished. The string parameter will be empty when the update check + * succeeded, or contain an error message if it failed. + * Note that the callback will be invoked whether updates are + * available or not - to react to updates being available, use + * `Updater::SetUpdateAvailableCallback()`. + */ + void ForceUpdateCheck(const UpdateCheckDoneCallback& callback = UpdateCheckDoneCallback()); + + /** + * Retrieves a preference value. + * @param pref Preference name. + * @return Preference value, or `null` if it doesn't exist. + */ + JsValue GetPref(const std::string& pref) const; + + /** + * Sets a preference value. + * @param pref Preference name. + * @param value New value of the preference. + */ + void SetPref(const std::string& pref, const JsValue& value); + + explicit Updater(const JsEnginePtr& jsEngine, const EvaluateCallback& callback); + + private: + JsEnginePtr jsEngine; + int updateCheckId; + }; +} + +#endif diff --git a/lib/api.js b/lib/api.js index bfcc6794..7092d5f0 100644 --- a/lib/api.js +++ b/lib/api.js @@ -28,7 +28,6 @@ let API = (() => const {ElemHide} = require("elemHide"); const {Synchronizer} = require("synchronizer"); const {Prefs} = require("prefs"); - const {checkForUpdates} = require("updater"); const {Notification} = require("notification"); return { @@ -188,6 +187,7 @@ let API = (() => { Notification.markAsShown(id); }, + checkFilterMatch(url, contentTypeMask, documentUrl) { let requestHost = extractHostFromURL(url); @@ -213,11 +213,6 @@ let API = (() => Prefs[pref] = value; }, - forceUpdateCheck(eventName) - { - checkForUpdates(eventName ? _triggerEvent.bind(null, eventName) : null); - }, - getHostFromUrl(url) { return extractHostFromURL(url); diff --git a/lib/apiUpdater.js b/lib/apiUpdater.js new file mode 100644 index 00000000..ba2c3d47 --- /dev/null +++ b/lib/apiUpdater.js @@ -0,0 +1,41 @@ +/* + * This file is part of Adblock Plus , + * Copyright (C) 2006-present eyeo GmbH + * + * Adblock Plus is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * Adblock Plus is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Adblock Plus. If not, see . + */ + +"use strict"; + +let API_UPDATER = (() => +{ + const {Prefs} = require("prefs"); + const {checkForUpdates} = require("updater"); + + return { + getPref(pref) + { + return Prefs[pref]; + }, + + setPref(pref, value) + { + Prefs[pref] = value; + }, + + forceUpdateCheck(eventName) + { + checkForUpdates(eventName ? _triggerEvent.bind(null, eventName) : null); + } + }; +})(); diff --git a/lib/compat.js b/lib/compat.js index a433b3b5..4c6f0606 100644 --- a/lib/compat.js +++ b/lib/compat.js @@ -333,6 +333,13 @@ XMLHttpRequest.prototype = for (let i = 0; i < list.length; i++) list[i].call(this, event); }; + + if (this._url.includes("update.json")) + { + window._webRequest.GET(this._url, this._requestHeaders, onGetDone); + return; + } + // HACK (#5066): the code checking whether the connection is // allowed is temporary, the actual check should be in the core // when we make a decision whether to update a subscription with diff --git a/lib/prefs.js b/lib/prefs.js index b9ac2c46..fa6fd584 100644 --- a/lib/prefs.js +++ b/lib/prefs.js @@ -146,10 +146,11 @@ let Prefs = exports.Prefs = { } }; -// Update the default prefs with what was preconfigured -for (let key in _preconfiguredPrefs) - if (preconfigurable.indexOf(key) != -1) - defaults[key] = _preconfiguredPrefs[key]; +if (typeof _preconfiguredPrefs !== "undefined") + // Update the default prefs with what was preconfigured + for (let key in _preconfiguredPrefs) + if (preconfigurable.indexOf(key) != -1) + defaults[key] = _preconfiguredPrefs[key]; // Define defaults for (let key in defaults) diff --git a/libadblockplus.gyp b/libadblockplus.gyp index 45138cd9..b9192474 100644 --- a/libadblockplus.gyp +++ b/libadblockplus.gyp @@ -44,6 +44,7 @@ 'src/DefaultWebRequest.cpp', 'src/FileSystemJsObject.cpp', 'src/FilterEngine.cpp', + 'src/Updater.cpp', 'src/GlobalJsObject.cpp', 'src/JsContext.cpp', 'src/JsEngine.cpp', @@ -159,6 +160,7 @@ ], 'load_after_files': [ 'lib/api.js', + 'lib/apiUpdater.js', 'lib/publicSuffixList.js', 'lib/punycode.js', 'lib/basedomain.js', @@ -207,7 +209,8 @@ 'test/Prefs.cpp', 'test/ReferrerMapping.cpp', 'test/UpdateCheck.cpp', - 'test/WebRequest.cpp' + 'test/WebRequest.cpp', + 'test/UpdaterAndFilterEngineCreation.cpp' ], 'msvs_settings': { 'VCLinkerTool': { diff --git a/src/FilterEngine.cpp b/src/FilterEngine.cpp index 179a0dbe..ca3522ca 100644 --- a/src/FilterEngine.cpp +++ b/src/FilterEngine.cpp @@ -30,7 +30,40 @@ using namespace AdblockPlus; -extern std::string jsSources[]; +namespace +{ + const std::string filterEngineJsFiles[] = + { + "compat.js", + "info.js", + "io.js", + "prefs.js", + "utils.js", + "elemHideHitRegistration.js", + "events.js", + "coreUtils.js", + "filterNotifier.js", + "init.js", + "common.js", + "filterClasses.js", + "subscriptionClasses.js", + "filterStorage.js", + "elemHide.js", + "elemHideEmulation.js", + "matcher.js", + "filterListener.js", + "downloader.js", + "notification.js", + "notificationShowRegistration.js", + "synchronizer.js", + "filterUpdateRegistration.js", + "subscriptions.xml", + "api.js", + "publicSuffixList.js", + "punycode.js", + "basedomain.js" + }; +} Filter::Filter(JsValue&& value) : JsValue(std::move(value)) @@ -183,11 +216,13 @@ bool Subscription::operator==(const Subscription& subscription) const } FilterEngine::FilterEngine(const JsEnginePtr& jsEngine) - : jsEngine(jsEngine), firstRun(false), updateCheckId(0) + : jsEngine(jsEngine), firstRun(false) { + } void FilterEngine::CreateAsync(const JsEnginePtr& jsEngine, + const EvaluateCallback& evaluateCallback, const FilterEngine::OnCreatedCallback& onCreated, const FilterEngine::CreationParameters& params) { @@ -256,9 +291,11 @@ void FilterEngine::CreateAsync(const JsEnginePtr& jsEngine, preconfiguredPrefsObject.SetProperty(pref.first, pref.second); } jsEngine->SetGlobalProperty("_preconfiguredPrefs", preconfiguredPrefsObject); + // Load adblockplus scripts - for (int i = 0; !jsSources[i].empty(); i += 2) - jsEngine->Evaluate(jsSources[i + 1], jsSources[i]); + for (const auto& filterEngineJsFile: filterEngineJsFiles) + evaluateCallback(filterEngineJsFile); + } namespace @@ -491,40 +528,6 @@ std::string FilterEngine::GetHostFromURL(const std::string& url) const return func.Call(jsEngine->NewValue(url)).AsString(); } -void FilterEngine::SetUpdateAvailableCallback( - const FilterEngine::UpdateAvailableCallback& callback) -{ - jsEngine->SetEventCallback("updateAvailable", [this, callback](JsValueList&& params) - { - if (params.size() >= 1 && !params[0].IsNull()) - callback(params[0].AsString()); - }); -} - -void FilterEngine::RemoveUpdateAvailableCallback() -{ - jsEngine->RemoveEventCallback("updateAvailable"); -} - -void FilterEngine::ForceUpdateCheck( - const FilterEngine::UpdateCheckDoneCallback& callback) -{ - JsValue func = jsEngine->Evaluate("API.forceUpdateCheck"); - JsValueList params; - if (callback) - { - std::string eventName = "_updateCheckDone" + std::to_string(++updateCheckId); - jsEngine->SetEventCallback(eventName, [this, eventName, callback](JsValueList&& params) - { - std::string error(params.size() >= 1 && !params[0].IsNull() ? params[0].AsString() : ""); - callback(error); - jsEngine->RemoveEventCallback(eventName); - }); - params.push_back(jsEngine->NewValue(eventName)); - } - func.Call(params); -} - void FilterEngine::SetFilterChangeCallback(const FilterChangeCallback& callback) { jsEngine->SetEventCallback("filterChange", [this, callback](JsValueList&& params) diff --git a/src/Platform.cpp b/src/Platform.cpp index ffe6e690..79122c44 100644 --- a/src/Platform.cpp +++ b/src/Platform.cpp @@ -26,6 +26,9 @@ using namespace AdblockPlus; + +extern std::string jsSources[]; + namespace { template @@ -64,6 +67,25 @@ JsEngine& Platform::GetJsEngine() return *jsEngine; } +std::function Platform::GetEvaluateCallback() +{ + // GetEvaluateCallback() method assumes that jsEngine is already created + return [this](const std::string& filename) + { + std::lock_guard lock(evaluatedJsSourcesMutex); + if (evaluatedJsSources.find(filename) != evaluatedJsSources.end()) + return; //NO-OP, file was already evaluated + + for (int i = 0; !jsSources[i].empty(); i += 2) + if (jsSources[i] == filename) + { + jsEngine->Evaluate(jsSources[i + 1], jsSources[i]); + evaluatedJsSources.insert(filename); + break; + } + }; +} + void Platform::CreateFilterEngineAsync(const FilterEngine::CreationParameters& parameters, const OnFilterEngineCreatedCallback& onCreated) { @@ -77,12 +99,17 @@ void Platform::CreateFilterEngineAsync(const FilterEngine::CreationParameters& p } GetJsEngine(); // ensures that JsEngine is instantiated - FilterEngine::CreateAsync(jsEngine, [this, onCreated, filterEnginePromise](const FilterEnginePtr& filterEngine) - { - filterEnginePromise->set_value(filterEngine); - if (onCreated) - onCreated(*filterEngine); - }, parameters); + FilterEngine::CreateAsync( + jsEngine, + GetEvaluateCallback(), + [this, onCreated, filterEnginePromise](const FilterEnginePtr& filterEngine) + { + filterEnginePromise->set_value(filterEngine); + if (onCreated) + onCreated(*filterEngine); + }, + parameters + ); } FilterEngine& Platform::GetFilterEngine() @@ -91,6 +118,20 @@ FilterEngine& Platform::GetFilterEngine() return *std::shared_future(filterEngine).get(); } +Updater& Platform::GetUpdater() +{ + { + std::lock_guard lock(modulesMutex); + if (updater) + return *updater; + } + GetJsEngine(); // ensures that JsEngine is instantiated + std::lock_guard lock(modulesMutex); + if (!updater) + updater = std::make_shared(jsEngine, GetEvaluateCallback()); + return *updater; +} + void Platform::WithTimer(const WithTimerCallback& callback) { if (timer && callback) diff --git a/src/Updater.cpp b/src/Updater.cpp new file mode 100644 index 00000000..c64b0881 --- /dev/null +++ b/src/Updater.cpp @@ -0,0 +1,95 @@ +/* + * This file is part of Adblock Plus , + * Copyright (C) 2006-present eyeo GmbH + * + * Adblock Plus is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * Adblock Plus is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Adblock Plus. If not, see . + */ + +#include +#include "JsContext.h" + +using namespace AdblockPlus; + +namespace +{ + const std::string updaterJsFiles[] = + { + "compat.js", + "info.js", + "prefs.js", + "utils.js", + "events.js", + "coreUtils.js", + "common.js", + "downloader.js", + "updater.js", + "apiUpdater.js" + }; +} + +Updater::Updater(const JsEnginePtr& jsEngine, const EvaluateCallback& evaluateCallback) + : jsEngine(jsEngine), updateCheckId(0) +{ + // Lock the JS engine while we are loading scripts, no timeouts should fire + // until we are done. + const JsContext context(*jsEngine); + for (const auto& updaterJsFile: updaterJsFiles) + evaluateCallback(updaterJsFile); +} + +void Updater::SetUpdateAvailableCallback(const Updater::UpdateAvailableCallback& callback) +{ + jsEngine->SetEventCallback("updateAvailable", [this, callback](JsValueList&& params) + { + if (params.size() >= 1 && !params[0].IsNull()) + callback(params[0].AsString()); + }); +} + +void Updater::RemoveUpdateAvailableCallback() +{ + jsEngine->RemoveEventCallback("updateAvailable"); +} + +void Updater::ForceUpdateCheck(const Updater::UpdateCheckDoneCallback& callback) +{ + JsValue func = jsEngine->Evaluate("API_UPDATER.forceUpdateCheck"); + JsValueList params; + if (callback) + { + std::string eventName = "_updateCheckDone" + std::to_string(++updateCheckId); + jsEngine->SetEventCallback(eventName, [this, eventName, callback](JsValueList&& params) + { + std::string error(params.size() >= 1 && !params[0].IsNull() ? params[0].AsString() : ""); + callback(error); + jsEngine->RemoveEventCallback(eventName); + }); + params.push_back(jsEngine->NewValue(eventName)); + } + func.Call(params); +} + +JsValue Updater::GetPref(const std::string& pref) const +{ + JsValue func = jsEngine->Evaluate("API_UPDATER.getPref"); + return func.Call(jsEngine->NewValue(pref)); +} + +void Updater::SetPref(const std::string& pref, const JsValue& value) +{ + JsValue func = jsEngine->Evaluate("API_UPDATER.setPref"); + JsValueList params; + params.push_back(jsEngine->NewValue(pref)); + params.push_back(value); + func.Call(params); +} diff --git a/test/UpdateCheck.cpp b/test/UpdateCheck.cpp index 08d843fe..7c57675d 100644 --- a/test/UpdateCheck.cpp +++ b/test/UpdateCheck.cpp @@ -48,7 +48,7 @@ namespace updateCallbackCalled = false; } - void CreateFilterEngine() + void CreateUpdater() { LazyFileSystem* fileSystem; ThrowingPlatformCreationParameters platformParams; @@ -64,7 +64,7 @@ namespace eventCallbackParams = std::move(params); }); - ::CreateFilterEngine(*fileSystem, *platform); + platform->GetUpdater(); } // Returns a URL or the empty string if there is no such request. @@ -87,7 +87,7 @@ namespace void ForceUpdateCheck() { - platform->GetFilterEngine().ForceUpdateCheck([this](const std::string& error) + platform->GetUpdater().ForceUpdateCheck([this](const std::string& error) { updateCallbackCalled = true; updateError = error; @@ -107,7 +107,7 @@ TEST_F(UpdateCheckTest, RequestFailure) appInfo.applicationVersion = "2"; appInfo.developmentBuild = false; - CreateFilterEngine(); + CreateUpdater(); ForceUpdateCheck(); auto requestUrl = ProcessPendingUpdateWebRequest(); @@ -116,7 +116,7 @@ TEST_F(UpdateCheckTest, RequestFailure) ASSERT_TRUE(updateCallbackCalled); ASSERT_FALSE(updateError.empty()); - std::string expectedUrl(platform->GetFilterEngine().GetPref("update_url_release").AsString()); + std::string expectedUrl(platform->GetUpdater().GetPref("update_url_release").AsString()); std::string platform = GetJsEngine().Evaluate("require('info').platform").AsString(); std::string platformVersion = GetJsEngine().Evaluate("require('info').platformVersion").AsString(); @@ -144,7 +144,7 @@ TEST_F(UpdateCheckTest, UpdateAvailable) appInfo.applicationVersion = "2"; appInfo.developmentBuild = true; - CreateFilterEngine(); + CreateUpdater(); ForceUpdateCheck(); auto requestUrl = ProcessPendingUpdateWebRequest(); @@ -155,7 +155,7 @@ TEST_F(UpdateCheckTest, UpdateAvailable) ASSERT_TRUE(updateCallbackCalled); ASSERT_TRUE(updateError.empty()); - std::string expectedUrl(platform->GetFilterEngine().GetPref("update_url_devbuild").AsString()); + std::string expectedUrl(platform->GetUpdater().GetPref("update_url_devbuild").AsString()); std::string platform = GetJsEngine().Evaluate("require('info').platform").AsString(); std::string platformVersion = GetJsEngine().Evaluate("require('info').platformVersion").AsString(); @@ -183,7 +183,7 @@ TEST_F(UpdateCheckTest, ApplicationUpdateAvailable) appInfo.applicationVersion = "2"; appInfo.developmentBuild = true; - CreateFilterEngine(); + CreateUpdater(); ForceUpdateCheck(); ProcessPendingUpdateWebRequest(); @@ -206,7 +206,7 @@ TEST_F(UpdateCheckTest, WrongApplication) appInfo.applicationVersion = "2"; appInfo.developmentBuild = true; - CreateFilterEngine(); + CreateUpdater(); ForceUpdateCheck(); ProcessPendingUpdateWebRequest(); @@ -228,7 +228,7 @@ TEST_F(UpdateCheckTest, WrongVersion) appInfo.applicationVersion = "2"; appInfo.developmentBuild = true; - CreateFilterEngine(); + CreateUpdater(); ForceUpdateCheck(); ProcessPendingUpdateWebRequest(); @@ -250,7 +250,7 @@ TEST_F(UpdateCheckTest, WrongURL) appInfo.applicationVersion = "2"; appInfo.developmentBuild = true; - CreateFilterEngine(); + CreateUpdater(); ForceUpdateCheck(); ProcessPendingUpdateWebRequest(); @@ -274,10 +274,10 @@ TEST_F(UpdateCheckTest, SetRemoveUpdateAvailableCallback) appInfo.name = "test"; appInfo.version = "1.0.1"; - CreateFilterEngine(); + CreateUpdater(); int timesCalled = 0; - platform->GetFilterEngine().SetUpdateAvailableCallback([×Called](const std::string&)->void + platform->GetUpdater().SetUpdateAvailableCallback([×Called](const std::string&)->void { ++timesCalled; }); @@ -288,11 +288,11 @@ TEST_F(UpdateCheckTest, SetRemoveUpdateAvailableCallback) EXPECT_EQ(1, timesCalled); - // FilterEngine::SetUpdateAvailableCallback overriddes previously installed on JsEngine + // Updater::SetUpdateAvailableCallback overriddes previously installed on JsEngine // handler for "updateAvailable" event. EXPECT_FALSE(eventCallbackCalled); - platform->GetFilterEngine().RemoveUpdateAvailableCallback(); + platform->GetUpdater().RemoveUpdateAvailableCallback(); ForceUpdateCheck(); // ensure that the was the corresponding request diff --git a/test/UpdaterAndFilterEngineCreation.cpp b/test/UpdaterAndFilterEngineCreation.cpp new file mode 100644 index 00000000..cb42543b --- /dev/null +++ b/test/UpdaterAndFilterEngineCreation.cpp @@ -0,0 +1,158 @@ +/* + * This file is part of Adblock Plus , + * Copyright (C) 2006-present eyeo GmbH + * + * Adblock Plus is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * Adblock Plus is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Adblock Plus. If not, see . + */ + +#include +#include +#include + +#include + +#include "BaseJsTest.h" + +using namespace AdblockPlus; + +namespace +{ + class UpdaterAndFilterEngineCreationTest : public BaseJsTest + { + protected: + + static const size_t COUNT = 100; + const std::string PROP_NAME = "patternsbackupinterval"; + std::array updaterAddrArray; + std::array filterAddrArray; + DelayedWebRequest::SharedTasks webRequestTasks; + DelayedTimer::SharedTasks timerTasks; + + void SetUp() + { + LazyFileSystem* fileSystem; + ThrowingPlatformCreationParameters platformParams; + platformParams.logSystem.reset(new LazyLogSystem()); + platformParams.timer = DelayedTimer::New(timerTasks); + platformParams.fileSystem.reset(fileSystem = new LazyFileSystem()); + platformParams.webRequest = DelayedWebRequest::New(webRequestTasks); + platform.reset(new Platform(std::move(platformParams))); + std::uninitialized_fill(updaterAddrArray.begin(), updaterAddrArray.end(), nullptr); + std::uninitialized_fill(filterAddrArray.begin(), filterAddrArray.end(), nullptr); + } + + void CallGetUpdaterSimultaneously() + { + CallGetSimultaneously(true, false); + } + + void CallGetFilterEngineSimultaneously() + { + CallGetSimultaneously(false, true); + } + + void CallGetUpdaterAndGetFilterEngineSimultaneously() + { + CallGetSimultaneously(true, true); + } + + private: + + void CallGetSimultaneously(bool isUpdater, bool isFilterEngine) + { + AsyncExecutor asyncExecutor; + for (size_t idx = 0; idx < COUNT; ++idx) + asyncExecutor.Dispatch(([this, idx, isUpdater, isFilterEngine]() -> void { + if (isUpdater && isFilterEngine) + { + // some randomization in order of calling gets + if (idx % 2) + { + updaterAddrArray[idx] = &(platform->GetUpdater()); + filterAddrArray[idx] = &(platform->GetFilterEngine()); + } + else + { + filterAddrArray[idx] = &(platform->GetFilterEngine()); + updaterAddrArray[idx] = &(platform->GetUpdater()); + } + } + else if (isUpdater) + updaterAddrArray[idx] = &(platform->GetUpdater()); + else if (isFilterEngine) + filterAddrArray[idx] = &(platform->GetFilterEngine()); + })); + } + }; +} + +TEST_F(UpdaterAndFilterEngineCreationTest, TestFilterEngineSingleInstance) +{ + CallGetFilterEngineSimultaneously(); + FilterEngine* filterEngineAddr = filterAddrArray[0]; + EXPECT_NE(nullptr, filterEngineAddr); + for (size_t i = 1; i < COUNT; ++i) + ASSERT_EQ(filterEngineAddr, filterAddrArray[i]); +} + +TEST_F(UpdaterAndFilterEngineCreationTest, TestUpdaterSingleInstance) +{ + CallGetUpdaterSimultaneously(); + Updater* updaterAddr = updaterAddrArray[0]; + EXPECT_NE(nullptr, updaterAddr); + for (size_t i = 1; i < COUNT; ++i) + ASSERT_EQ(updaterAddr, updaterAddrArray[i]); +} + +TEST_F(UpdaterAndFilterEngineCreationTest, TestUpdaterAndFilterEngineCreationsDontCollide) +{ + CallGetUpdaterAndGetFilterEngineSimultaneously(); + EXPECT_NE(nullptr, filterAddrArray[0]); + EXPECT_NE(nullptr, updaterAddrArray[0]); + EXPECT_EQ(filterAddrArray[0], filterAddrArray[COUNT - 1]); + EXPECT_EQ(updaterAddrArray[0], updaterAddrArray[COUNT - 1]); +} + +TEST_F(UpdaterAndFilterEngineCreationTest, TestUpdaterAndFilterEngineCreationOrder1) +{ + Updater& updater = platform->GetUpdater(); + FilterEngine& filterEngine = platform->GetFilterEngine(); + + int propFromUpdater = updater.GetPref(PROP_NAME).AsInt(); + int propFromFilterEngine = filterEngine.GetPref(PROP_NAME).AsInt(); + EXPECT_EQ(propFromUpdater, propFromFilterEngine); + + int newPropValue = 8; + updater.SetPref(PROP_NAME, GetJsEngine().NewValue(newPropValue)); + propFromUpdater = updater.GetPref(PROP_NAME).AsInt(); + propFromFilterEngine = filterEngine.GetPref(PROP_NAME).AsInt(); + EXPECT_EQ(newPropValue, propFromUpdater); + EXPECT_EQ(newPropValue, propFromFilterEngine); +} + +TEST_F(UpdaterAndFilterEngineCreationTest, TestUpdaterAndFilterEngineCreationOrder2) +{ + FilterEngine& filterEngine = platform->GetFilterEngine(); + Updater& updater = platform->GetUpdater(); + + int propFromFilterEngine = filterEngine.GetPref(PROP_NAME).AsInt(); + int propFromUpdater = updater.GetPref(PROP_NAME).AsInt(); + EXPECT_EQ(propFromUpdater, propFromFilterEngine); + + int newPropValue = 18; + filterEngine.SetPref(PROP_NAME, GetJsEngine().NewValue(newPropValue)); + propFromFilterEngine = filterEngine.GetPref(PROP_NAME).AsInt(); + propFromUpdater = updater.GetPref(PROP_NAME).AsInt(); + EXPECT_EQ(newPropValue, propFromUpdater); + EXPECT_EQ(newPropValue, propFromFilterEngine); +}