| Index: test/WebRequest.cpp | 
| diff --git a/test/WebRequest.cpp b/test/WebRequest.cpp | 
| index 46d6614ef3d340634ac94ae6c1e4b0ec356e8c3b..2e2e6e6b1b6d04193643cd32003b0ccbf8f1636a 100644 | 
| --- a/test/WebRequest.cpp | 
| +++ b/test/WebRequest.cpp | 
| @@ -18,6 +18,10 @@ | 
| #include <sstream> | 
| #include "BaseJsTest.h" | 
| #include "../src/Thread.h" | 
| +#include <atomic> | 
| +#include <mutex> | 
| + | 
| +using namespace AdblockPlus; | 
|  | 
| namespace | 
| { | 
| @@ -74,6 +78,198 @@ TEST_F(MockWebRequestTest, SuccessfulRequest) | 
| ASSERT_EQ("{\"Foo\":\"Bar\"}", jsEngine->Evaluate("JSON.stringify(foo.responseHeaders)")->AsString()); | 
| } | 
|  | 
| +TEST_F(MockWebRequestTest, ConnectionIsAllowedOnJsEngine) | 
| +{ | 
| +  std::atomic<int> isConnectionAllowedCalledTimes(0); | 
| +  jsEngine->SetIsConnectionAllowedCallback([&isConnectionAllowedCalledTimes]()->bool | 
| +  { | 
| +    ++isConnectionAllowedCalledTimes; | 
| +    return true; | 
| +  }); | 
| +  jsEngine->Evaluate("_webRequest.GET('http://example.com/', {X: 'Y'}, function(result) {foo = result;} )"); | 
| +  ASSERT_TRUE(jsEngine->Evaluate("this.foo")->IsUndefined()); | 
| +  AdblockPlus::Sleep(200); | 
| +  EXPECT_EQ(1u, isConnectionAllowedCalledTimes); | 
| +  EXPECT_EQ(AdblockPlus::WebRequest::NS_OK, jsEngine->Evaluate("foo.status")->AsInt()); | 
| +  EXPECT_EQ(123, jsEngine->Evaluate("foo.responseStatus")->AsInt()); | 
| +  EXPECT_EQ("http://example.com/\nX\nY", jsEngine->Evaluate("foo.responseText")->AsString()); | 
| +  EXPECT_EQ("{\"Foo\":\"Bar\"}", jsEngine->Evaluate("JSON.stringify(foo.responseHeaders)")->AsString()); | 
| +} | 
| + | 
| +TEST_F(MockWebRequestTest, ConnectionIsNotAllowedOnJsEngine) | 
| +{ | 
| +  std::atomic<int> isConnectionAllowedCalledTimes(0); | 
| +  jsEngine->SetIsConnectionAllowedCallback([&isConnectionAllowedCalledTimes]()->bool | 
| +  { | 
| +    ++isConnectionAllowedCalledTimes; | 
| +    return false; | 
| +  }); | 
| +  jsEngine->Evaluate("_webRequest.GET('http://example.com/', {X: 'Y'}, function(result) {foo = result;} )"); | 
| +  ASSERT_TRUE(jsEngine->Evaluate("this.foo")->IsUndefined()); | 
| +  AdblockPlus::Sleep(200); | 
| +  EXPECT_EQ(1u, isConnectionAllowedCalledTimes); | 
| +  EXPECT_EQ(AdblockPlus::WebRequest::NS_ERROR_CONNECTION_REFUSED, jsEngine->Evaluate("foo.status")->AsInt()); | 
| +  EXPECT_EQ(0, jsEngine->Evaluate("foo.responseStatus")->AsInt()); | 
| +  EXPECT_EQ("", jsEngine->Evaluate("foo.responseText")->AsString()); | 
| +  EXPECT_EQ("{}", jsEngine->Evaluate("JSON.stringify(foo.responseHeaders)")->AsString()); | 
| +} | 
| + | 
| +namespace | 
| +{ | 
| +  class SyncStrings | 
| +  { | 
| +  public: | 
| +    void Add(const std::string* value) | 
| +    { | 
| +      std::lock_guard<std::mutex> lock(mutex); | 
| +      strings.emplace_back(!!value, value ? *value : ""); | 
| +    } | 
| +    std::vector<std::pair<bool, std::string>> GetStrings() const | 
| +    { | 
| +      std::lock_guard<std::mutex> lock(mutex); | 
| +      return strings; | 
| +    } | 
| +    void Clear() | 
| +    { | 
| +      std::lock_guard<std::mutex> lock(mutex); | 
| +      strings.clear(); | 
| +    } | 
| +  private: | 
| +    mutable std::mutex mutex; | 
| +    std::vector<std::pair<bool, std::string>> strings; | 
| +  }; | 
| +} | 
| + | 
| +TEST_F(MockWebRequestTest, ConnectionIsAllowedOnFilterEngine1) | 
| +{ | 
| +  FilterEngine::CreateParameters createParams; | 
| +  std::string predefinedAllowedConnectionType = "non-metered"; | 
| +  createParams.preconfiguredPrefs.emplace("allowed_connection_type", jsEngine->NewValue(predefinedAllowedConnectionType)); | 
| +  auto receivedConnectionTypes = std::make_shared<SyncStrings>(); | 
| +  createParams.isConnectionAllowed = [receivedConnectionTypes](const std::string* allowedConnectionType)->bool { | 
| +    receivedConnectionTypes->Add(allowedConnectionType); | 
| +    return true; | 
| +  }; | 
| +  auto filterEngine = FilterEngine::Create(jsEngine, createParams); | 
| +  jsEngine->Evaluate("_webRequest.GET('http://example.com/', {X: 'Y'}, function(result) {foo = result;} )"); | 
| +  ASSERT_TRUE(jsEngine->Evaluate("this.foo")->IsUndefined()); | 
| +  AdblockPlus::Sleep(200); | 
| +  auto receivedConnectionTypesStrings = receivedConnectionTypes->GetStrings(); | 
| +  EXPECT_FALSE(receivedConnectionTypesStrings.empty()); | 
| +  for (const auto& connectionType : receivedConnectionTypesStrings) | 
| +  { | 
| +    EXPECT_TRUE(connectionType.first); | 
| +    EXPECT_EQ(predefinedAllowedConnectionType, connectionType.second); | 
| +  } | 
| +  EXPECT_EQ(AdblockPlus::WebRequest::NS_OK, jsEngine->Evaluate("foo.status")->AsInt()); | 
| +  EXPECT_EQ(123, jsEngine->Evaluate("foo.responseStatus")->AsInt()); | 
| +  EXPECT_EQ("http://example.com/\nX\nY", jsEngine->Evaluate("foo.responseText")->AsString()); | 
| +  EXPECT_EQ("{\"Foo\":\"Bar\"}", jsEngine->Evaluate("JSON.stringify(foo.responseHeaders)")->AsString()); | 
| +} | 
| + | 
| +TEST_F(MockWebRequestTest, ConnectionIsAllowedOnFilterEngine2) | 
| +{ | 
| +  FilterEngine::CreateParameters createParams; | 
| +  auto receivedConnectionTypes = std::make_shared<SyncStrings>(); | 
| +  createParams.isConnectionAllowed = [receivedConnectionTypes](const std::string* allowedConnectionType)->bool { | 
| +    receivedConnectionTypes->Add(allowedConnectionType); | 
| +    return true; | 
| +  }; | 
| +  auto filterEngine = FilterEngine::Create(jsEngine, createParams); | 
| +  jsEngine->Evaluate("_webRequest.GET('http://example.com/', {X: 'Y'}, function(result) {foo = result;} )"); | 
| +  ASSERT_TRUE(jsEngine->Evaluate("this.foo")->IsUndefined()); | 
| +  AdblockPlus::Sleep(200); | 
| +  auto receivedConnectionTypesStrings = receivedConnectionTypes->GetStrings(); | 
| +  EXPECT_FALSE(receivedConnectionTypesStrings.empty()); | 
| +  for (const auto& connectionType : receivedConnectionTypesStrings) | 
| +  { | 
| +    EXPECT_FALSE(connectionType.first); | 
| +  } | 
| +  EXPECT_EQ(AdblockPlus::WebRequest::NS_OK, jsEngine->Evaluate("foo.status")->AsInt()); | 
| +  EXPECT_EQ(123, jsEngine->Evaluate("foo.responseStatus")->AsInt()); | 
| +  EXPECT_EQ("http://example.com/\nX\nY", jsEngine->Evaluate("foo.responseText")->AsString()); | 
| +  EXPECT_EQ("{\"Foo\":\"Bar\"}", jsEngine->Evaluate("JSON.stringify(foo.responseHeaders)")->AsString()); | 
| +} | 
| + | 
| +TEST_F(MockWebRequestTest, ConnectionIsAllowedOnFilterEngine3) | 
| +{ | 
| +  // initially allowed connection type is not defined | 
| +  FilterEngine::CreateParameters createParams; | 
| +  auto receivedConnectionTypes = std::make_shared<SyncStrings>(); | 
| +  createParams.isConnectionAllowed = [receivedConnectionTypes](const std::string* allowedConnectionType)->bool { | 
| +    receivedConnectionTypes->Add(allowedConnectionType); | 
| +    return true; | 
| +  }; | 
| +  auto filterEngine = FilterEngine::Create(jsEngine, createParams); | 
| + | 
| +  jsEngine->Evaluate("_webRequest.GET('http://example.com/', {X: 'Y'}, function(result) {foo = result;} )"); | 
| +  ASSERT_TRUE(jsEngine->Evaluate("this.foo")->IsUndefined()); | 
| +  AdblockPlus::Sleep(200); | 
| +  auto receivedConnectionTypesStrings = receivedConnectionTypes->GetStrings(); | 
| +  EXPECT_FALSE(receivedConnectionTypesStrings.empty()); | 
| +  for (const auto& connectionType : receivedConnectionTypesStrings) | 
| +  { | 
| +    EXPECT_FALSE(connectionType.first); | 
| +  } | 
| +  EXPECT_EQ(AdblockPlus::WebRequest::NS_OK, jsEngine->Evaluate("foo.status")->AsInt()); | 
| +  EXPECT_EQ(123, jsEngine->Evaluate("foo.responseStatus")->AsInt()); | 
| +  EXPECT_EQ("http://example.com/\nX\nY", jsEngine->Evaluate("foo.responseText")->AsString()); | 
| +  EXPECT_EQ("{\"Foo\":\"Bar\"}", jsEngine->Evaluate("JSON.stringify(foo.responseHeaders)")->AsString()); | 
| + | 
| +  // set allowed connection type | 
| +  std::string allowedConnectionType = "test-connection"; | 
| +  filterEngine->SetAllowedConnectionType(&allowedConnectionType); | 
| +  receivedConnectionTypes->Clear(); | 
| +  jsEngine->Evaluate("_webRequest.GET('http://example.com/', {X: 'Y'}, function(result) {foo = result;} )"); | 
| +  AdblockPlus::Sleep(200); | 
| +  receivedConnectionTypesStrings = receivedConnectionTypes->GetStrings(); | 
| +  EXPECT_FALSE(receivedConnectionTypesStrings.empty()); | 
| +  for (const auto& connectionType : receivedConnectionTypesStrings) | 
| +  { | 
| +    EXPECT_TRUE(connectionType.first); | 
| +    EXPECT_EQ(allowedConnectionType, connectionType.second); | 
| +  } | 
| + | 
| +  // remove allowed connection type | 
| +  filterEngine->SetAllowedConnectionType(nullptr); | 
| +  receivedConnectionTypes->Clear(); | 
| +  jsEngine->Evaluate("_webRequest.GET('http://example.com/', {X: 'Y'}, function(result) {foo = result;} )"); | 
| +  AdblockPlus::Sleep(200); | 
| +  receivedConnectionTypesStrings = receivedConnectionTypes->GetStrings(); | 
| +  EXPECT_FALSE(receivedConnectionTypesStrings.empty()); | 
| +  for (const auto& connectionType : receivedConnectionTypesStrings) | 
| +  { | 
| +    EXPECT_FALSE(connectionType.first); | 
| +  } | 
| +} | 
| + | 
| +TEST_F(MockWebRequestTest, ConnectionIsNotAllowedOnFilterEngine) | 
| +{ | 
| +  FilterEngine::CreateParameters createParams; | 
| +  std::string predefinedAllowedConnectionType = "non-metered"; | 
| +  createParams.preconfiguredPrefs.emplace("allowed_connection_type", jsEngine->NewValue(predefinedAllowedConnectionType)); | 
| +  auto receivedConnectionTypes = std::make_shared<SyncStrings>(); | 
| +  createParams.isConnectionAllowed = [receivedConnectionTypes](const std::string* allowedConnectionType)->bool { | 
| +    receivedConnectionTypes->Add(allowedConnectionType); | 
| +    return false; | 
| +  }; | 
| +  auto filterEngine = FilterEngine::Create(jsEngine, createParams); | 
| +  jsEngine->Evaluate("_webRequest.GET('http://example.com/', {X: 'Y'}, function(result) {foo = result;} )"); | 
| +  ASSERT_TRUE(jsEngine->Evaluate("this.foo")->IsUndefined()); | 
| +  AdblockPlus::Sleep(200); | 
| +  auto receivedConnectionTypesStrings = receivedConnectionTypes->GetStrings(); | 
| +  EXPECT_FALSE(receivedConnectionTypesStrings.empty()); | 
| +  for (const auto& connectionType : receivedConnectionTypesStrings) | 
| +  { | 
| +    EXPECT_TRUE(connectionType.first); | 
| +    EXPECT_EQ(predefinedAllowedConnectionType, connectionType.second); | 
| +  } | 
| +  EXPECT_EQ(AdblockPlus::WebRequest::NS_ERROR_CONNECTION_REFUSED, jsEngine->Evaluate("foo.status")->AsInt()); | 
| +  EXPECT_EQ(0, jsEngine->Evaluate("foo.responseStatus")->AsInt()); | 
| +  EXPECT_EQ("", jsEngine->Evaluate("foo.responseText")->AsString()); | 
| +  EXPECT_EQ("{}", jsEngine->Evaluate("JSON.stringify(foo.responseHeaders)")->AsString()); | 
| +} | 
| + | 
| #if defined(HAVE_CURL) || defined(_WIN32) | 
| TEST_F(DefaultWebRequestTest, RealWebRequest) | 
| { | 
| @@ -132,7 +328,7 @@ TEST_F(DefaultWebRequestTest, DummyWebRequest) | 
|  | 
| TEST_F(DefaultWebRequestTest, XMLHttpRequest) | 
| { | 
| -  AdblockPlus::FilterEngine filterEngine(jsEngine); | 
| +  auto filterEngine = AdblockPlus::FilterEngine::Create(jsEngine); | 
|  | 
| jsEngine->Evaluate("\ | 
| var result;\ | 
|  |