| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "components/subresource_filter/content/common/document_subresource_filt
er.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/callback.h" | |
| 9 #include "base/files/file.h" | |
| 10 #include "base/macros.h" | |
| 11 #include "base/memory/ref_counted.h" | |
| 12 #include "base/strings/string_piece.h" | |
| 13 #include "components/subresource_filter/core/common/memory_mapped_ruleset.h" | |
| 14 #include "components/subresource_filter/core/common/test_ruleset_creator.h" | |
| 15 #include "components/subresource_filter/core/common/test_ruleset_utils.h" | |
| 16 #include "testing/gtest/include/gtest/gtest.h" | |
| 17 #include "third_party/WebKit/public/platform/WebURL.h" | |
| 18 #include "third_party/WebKit/public/platform/WebURLRequest.h" | |
| 19 #include "url/gurl.h" | |
| 20 | |
| 21 namespace subresource_filter { | |
| 22 | |
| 23 namespace { | |
| 24 | |
| 25 constexpr auto kDisabled = ActivationLevel::DISABLED; | |
| 26 constexpr auto kDryRun = ActivationLevel::DRYRUN; | |
| 27 constexpr auto kEnabled = ActivationLevel::ENABLED; | |
| 28 | |
| 29 const char kTestAlphaURL[] = "http://example.com/alpha"; | |
| 30 const char kTestAlphaDataURI[] = "data:text/plain,alpha"; | |
| 31 const char kTestBetaURL[] = "http://example.com/beta"; | |
| 32 | |
| 33 const char kTestAlphaURLPathSuffix[] = "alpha"; | |
| 34 | |
| 35 class TestCallbackReceiver { | |
| 36 public: | |
| 37 TestCallbackReceiver() = default; | |
| 38 base::OnceClosure closure() { | |
| 39 return base::BindOnce(&TestCallbackReceiver::CallbackMethod, | |
| 40 base::Unretained(this)); | |
| 41 } | |
| 42 size_t callback_count() const { return callback_count_; } | |
| 43 | |
| 44 private: | |
| 45 void CallbackMethod() { ++callback_count_; } | |
| 46 | |
| 47 size_t callback_count_ = 0; | |
| 48 | |
| 49 DISALLOW_COPY_AND_ASSIGN(TestCallbackReceiver); | |
| 50 }; | |
| 51 | |
| 52 } // namespace | |
| 53 | |
| 54 // Tests for DocumentSubresourceFilter class. ---------------------------------- | |
| 55 | |
| 56 class DocumentSubresourceFilterTest : public ::testing::Test { | |
| 57 public: | |
| 58 DocumentSubresourceFilterTest() {} | |
| 59 | |
| 60 protected: | |
| 61 void SetUp() override { | |
| 62 ASSERT_NO_FATAL_FAILURE( | |
| 63 SetTestRulesetToDisallowURLsWithPathSuffix(kTestAlphaURLPathSuffix)); | |
| 64 } | |
| 65 | |
| 66 void SetTestRulesetToDisallowURLsWithPathSuffix(base::StringPiece suffix) { | |
| 67 testing::TestRulesetPair test_ruleset_pair; | |
| 68 ASSERT_NO_FATAL_FAILURE( | |
| 69 test_ruleset_creator_.CreateRulesetToDisallowURLsWithPathSuffix( | |
| 70 suffix, &test_ruleset_pair)); | |
| 71 ruleset_ = new MemoryMappedRuleset( | |
| 72 testing::TestRuleset::Open(test_ruleset_pair.indexed)); | |
| 73 } | |
| 74 | |
| 75 const MemoryMappedRuleset* ruleset() { return ruleset_.get(); } | |
| 76 | |
| 77 private: | |
| 78 testing::TestRulesetCreator test_ruleset_creator_; | |
| 79 scoped_refptr<const MemoryMappedRuleset> ruleset_; | |
| 80 | |
| 81 DISALLOW_COPY_AND_ASSIGN(DocumentSubresourceFilterTest); | |
| 82 }; | |
| 83 | |
| 84 TEST_F(DocumentSubresourceFilterTest, DryRun) { | |
| 85 blink::WebURLRequest::RequestContext request_context = | |
| 86 blink::WebURLRequest::RequestContextImage; | |
| 87 TestCallbackReceiver first_disallowed_load_callback_receiver; | |
| 88 | |
| 89 ActivationState activation_state(kDryRun); | |
| 90 activation_state.measure_performance = true; | |
| 91 DocumentSubresourceFilter filter( | |
| 92 url::Origin(), activation_state, ruleset(), | |
| 93 first_disallowed_load_callback_receiver.closure()); | |
| 94 | |
| 95 EXPECT_EQ(blink::WebDocumentSubresourceFilter::WouldDisallow, | |
| 96 filter.getLoadPolicy(GURL(kTestAlphaURL), request_context)); | |
| 97 EXPECT_EQ(blink::WebDocumentSubresourceFilter::Allow, | |
| 98 filter.getLoadPolicy(GURL(kTestAlphaDataURI), request_context)); | |
| 99 EXPECT_EQ(blink::WebDocumentSubresourceFilter::Allow, | |
| 100 filter.getLoadPolicy(GURL(kTestBetaURL), request_context)); | |
| 101 EXPECT_EQ(blink::WebDocumentSubresourceFilter::WouldDisallow, | |
| 102 filter.GetLoadPolicyForSubdocument(GURL(kTestAlphaURL))); | |
| 103 EXPECT_EQ(blink::WebDocumentSubresourceFilter::Allow, | |
| 104 filter.GetLoadPolicyForSubdocument(GURL(kTestBetaURL))); | |
| 105 | |
| 106 const auto& statistics = filter.statistics(); | |
| 107 EXPECT_EQ(5, statistics.num_loads_total); | |
| 108 EXPECT_EQ(4, statistics.num_loads_evaluated); | |
| 109 EXPECT_EQ(2, statistics.num_loads_matching_rules); | |
| 110 EXPECT_EQ(0, statistics.num_loads_disallowed); | |
| 111 | |
| 112 EXPECT_EQ(0u, first_disallowed_load_callback_receiver.callback_count()); | |
| 113 } | |
| 114 | |
| 115 TEST_F(DocumentSubresourceFilterTest, Enabled) { | |
| 116 auto test_impl = [this](bool measure_performance) { | |
| 117 blink::WebURLRequest::RequestContext request_context = | |
| 118 blink::WebURLRequest::RequestContextImage; | |
| 119 ActivationState activation_state(kEnabled); | |
| 120 activation_state.measure_performance = measure_performance; | |
| 121 DocumentSubresourceFilter filter(url::Origin(), activation_state, ruleset(), | |
| 122 base::OnceClosure()); | |
| 123 | |
| 124 EXPECT_EQ(blink::WebDocumentSubresourceFilter::Disallow, | |
| 125 filter.getLoadPolicy(GURL(kTestAlphaURL), request_context)); | |
| 126 EXPECT_EQ(blink::WebDocumentSubresourceFilter::Allow, | |
| 127 filter.getLoadPolicy(GURL(kTestAlphaDataURI), request_context)); | |
| 128 EXPECT_EQ(blink::WebDocumentSubresourceFilter::Allow, | |
| 129 filter.getLoadPolicy(GURL(kTestBetaURL), request_context)); | |
| 130 EXPECT_EQ(blink::WebDocumentSubresourceFilter::Disallow, | |
| 131 filter.GetLoadPolicyForSubdocument(GURL(kTestAlphaURL))); | |
| 132 EXPECT_EQ(blink::WebDocumentSubresourceFilter::Allow, | |
| 133 filter.GetLoadPolicyForSubdocument(GURL(kTestBetaURL))); | |
| 134 | |
| 135 const auto& statistics = filter.statistics(); | |
| 136 EXPECT_EQ(5, statistics.num_loads_total); | |
| 137 EXPECT_EQ(4, statistics.num_loads_evaluated); | |
| 138 EXPECT_EQ(2, statistics.num_loads_matching_rules); | |
| 139 EXPECT_EQ(2, statistics.num_loads_disallowed); | |
| 140 | |
| 141 if (!measure_performance) { | |
| 142 EXPECT_TRUE(statistics.evaluation_total_cpu_duration.is_zero()); | |
| 143 EXPECT_TRUE(statistics.evaluation_total_wall_duration.is_zero()); | |
| 144 } | |
| 145 // Otherwise, don't expect |total_duration| to be non-zero, although it | |
| 146 // practically is (when timer is supported). | |
| 147 }; | |
| 148 | |
| 149 test_impl(true /* measure_performance */); | |
| 150 test_impl(false /* measure_performance */); | |
| 151 } | |
| 152 | |
| 153 TEST_F(DocumentSubresourceFilterTest, | |
| 154 CallbackFiredExactlyOnceAfterFirstDisallowedLoad) { | |
| 155 TestCallbackReceiver first_disallowed_load_callback_receiver; | |
| 156 | |
| 157 ActivationState activation_state(kEnabled); | |
| 158 activation_state.measure_performance = true; | |
| 159 DocumentSubresourceFilter filter( | |
| 160 url::Origin(), activation_state, ruleset(), | |
| 161 first_disallowed_load_callback_receiver.closure()); | |
| 162 | |
| 163 EXPECT_EQ(0u, first_disallowed_load_callback_receiver.callback_count()); | |
| 164 filter.reportDisallowedLoad(); | |
| 165 EXPECT_EQ(1u, first_disallowed_load_callback_receiver.callback_count()); | |
| 166 filter.reportDisallowedLoad(); | |
| 167 EXPECT_EQ(1u, first_disallowed_load_callback_receiver.callback_count()); | |
| 168 } | |
| 169 | |
| 170 // Tests for ComputeActivationState functions. --------------------------------- | |
| 171 | |
| 172 class SubresourceFilterComputeActivationStateTest : public ::testing::Test { | |
| 173 public: | |
| 174 SubresourceFilterComputeActivationStateTest() {} | |
| 175 | |
| 176 protected: | |
| 177 void SetUp() override { | |
| 178 constexpr int32_t kDocument = proto::ACTIVATION_TYPE_DOCUMENT; | |
| 179 constexpr int32_t kGenericBlock = proto::ACTIVATION_TYPE_GENERICBLOCK; | |
| 180 | |
| 181 std::vector<proto::UrlRule> rules; | |
| 182 rules.push_back(testing::CreateWhitelistRuleForDocument( | |
| 183 "child1.com", kDocument, {"parent1.com", "parent2.com"})); | |
| 184 rules.push_back(testing::CreateWhitelistRuleForDocument( | |
| 185 "child2.com", kGenericBlock, {"parent1.com", "parent2.com"})); | |
| 186 rules.push_back(testing::CreateWhitelistRuleForDocument( | |
| 187 "child3.com", kDocument | kGenericBlock, | |
| 188 {"parent1.com", "parent2.com"})); | |
| 189 | |
| 190 testing::TestRulesetPair test_ruleset_pair; | |
| 191 ASSERT_NO_FATAL_FAILURE(test_ruleset_creator_.CreateRulesetWithRules( | |
| 192 rules, &test_ruleset_pair)); | |
| 193 ruleset_ = new MemoryMappedRuleset( | |
| 194 testing::TestRuleset::Open(test_ruleset_pair.indexed)); | |
| 195 } | |
| 196 | |
| 197 static ActivationState MakeState( | |
| 198 bool filtering_disabled_for_document, | |
| 199 bool generic_blocking_rules_disabled = false, | |
| 200 ActivationLevel activation_level = kEnabled) { | |
| 201 ActivationState activation_state(activation_level); | |
| 202 activation_state.filtering_disabled_for_document = | |
| 203 filtering_disabled_for_document; | |
| 204 activation_state.generic_blocking_rules_disabled = | |
| 205 generic_blocking_rules_disabled; | |
| 206 return activation_state; | |
| 207 }; | |
| 208 | |
| 209 const MemoryMappedRuleset* ruleset() { return ruleset_.get(); } | |
| 210 | |
| 211 private: | |
| 212 testing::TestRulesetCreator test_ruleset_creator_; | |
| 213 scoped_refptr<const MemoryMappedRuleset> ruleset_; | |
| 214 | |
| 215 DISALLOW_COPY_AND_ASSIGN(SubresourceFilterComputeActivationStateTest); | |
| 216 }; | |
| 217 | |
| 218 TEST_F(SubresourceFilterComputeActivationStateTest, | |
| 219 ActivationBitsCorrectlyPropagateToChildDocument) { | |
| 220 // Make sure that the |generic_blocking_rules_disabled| flag is disregarded | |
| 221 // when |filtering_disabled_for_document| is true. | |
| 222 ASSERT_EQ(MakeState(true, false), MakeState(true, true)); | |
| 223 | |
| 224 // TODO(pkalinnikov): Find a short way to express all these tests. | |
| 225 const struct { | |
| 226 const char* document_url; | |
| 227 const char* parent_document_origin; | |
| 228 ActivationState parent_activation; | |
| 229 ActivationState expected_activation_state; | |
| 230 } kTestCases[] = { | |
| 231 {"http://example.com", "http://example.com", MakeState(false, false), | |
| 232 MakeState(false, false)}, | |
| 233 {"http://example.com", "http://example.com", MakeState(false, true), | |
| 234 MakeState(false, true)}, | |
| 235 {"http://example.com", "http://example.com", MakeState(true, false), | |
| 236 MakeState(true)}, | |
| 237 {"http://example.com", "http://example.com", MakeState(true, true), | |
| 238 MakeState(true)}, | |
| 239 | |
| 240 {"http://child1.com", "http://parrrrent1.com", MakeState(false, false), | |
| 241 MakeState(false, false)}, | |
| 242 {"http://child1.com", "http://parent1.com", MakeState(false, false), | |
| 243 MakeState(true, false)}, | |
| 244 {"http://child1.com", "http://parent2.com", MakeState(false, false), | |
| 245 MakeState(true, false)}, | |
| 246 {"http://child1.com", "http://parent2.com", MakeState(true, false), | |
| 247 MakeState(true)}, | |
| 248 {"http://child1.com", "http://parent2.com", MakeState(false, true), | |
| 249 MakeState(true)}, | |
| 250 | |
| 251 {"http://child2.com", "http://parent1.com", MakeState(false, false), | |
| 252 MakeState(false, true)}, | |
| 253 {"http://child2.com", "http://parent1.com", MakeState(false, true), | |
| 254 MakeState(false, true)}, | |
| 255 {"http://child2.com", "http://parent1.com", MakeState(true, false), | |
| 256 MakeState(true)}, | |
| 257 {"http://child2.com", "http://parent1.com", MakeState(true, true), | |
| 258 MakeState(true)}, | |
| 259 | |
| 260 {"http://child3.com", "http://parent1.com", MakeState(false, false), | |
| 261 MakeState(true)}, | |
| 262 {"http://child3.com", "http://parent1.com", MakeState(false, true), | |
| 263 MakeState(true)}, | |
| 264 {"http://child3.com", "http://parent1.com", MakeState(true, false), | |
| 265 MakeState(true)}, | |
| 266 {"http://child3.com", "http://parent1.com", MakeState(true, true), | |
| 267 MakeState(true)}, | |
| 268 }; | |
| 269 | |
| 270 for (size_t i = 0, size = arraysize(kTestCases); i != size; ++i) { | |
| 271 SCOPED_TRACE(::testing::Message() << "Test number: " << i); | |
| 272 const auto& test_case = kTestCases[i]; | |
| 273 | |
| 274 GURL document_url(test_case.document_url); | |
| 275 url::Origin parent_document_origin(GURL(test_case.parent_document_origin)); | |
| 276 ActivationState activation_state = | |
| 277 ComputeActivationState(document_url, parent_document_origin, | |
| 278 test_case.parent_activation, ruleset()); | |
| 279 EXPECT_EQ(test_case.expected_activation_state, activation_state); | |
| 280 } | |
| 281 } | |
| 282 | |
| 283 TEST_F(SubresourceFilterComputeActivationStateTest, | |
| 284 ActivationStateCorrectlyPropagatesDownDocumentHierarchy) { | |
| 285 const struct { | |
| 286 std::vector<std::string> ancestor_document_urls; | |
| 287 ActivationLevel activation_level; | |
| 288 ActivationState expected_activation_state; | |
| 289 } kTestCases[] = { | |
| 290 {{"http://example.com"}, kEnabled, MakeState(false)}, | |
| 291 {std::vector<std::string>(2, "http://example.com"), kEnabled, | |
| 292 MakeState(false)}, | |
| 293 {std::vector<std::string>(4, "http://example.com"), kEnabled, | |
| 294 MakeState(false)}, | |
| 295 | |
| 296 {std::vector<std::string>(4, "http://example.com"), kEnabled, | |
| 297 MakeState(false, false, kEnabled)}, | |
| 298 {std::vector<std::string>(4, "http://example.com"), kDisabled, | |
| 299 MakeState(false, false, kDisabled)}, | |
| 300 {std::vector<std::string>(4, "http://example.com"), kDryRun, | |
| 301 MakeState(false, false, kDryRun)}, | |
| 302 | |
| 303 {{"http://ex.com", "http://child1.com", "http://parent1.com", | |
| 304 "http://root.com"}, | |
| 305 kEnabled, | |
| 306 MakeState(true)}, | |
| 307 | |
| 308 {{"http://ex.com", "http://child1.com", "http://parent1.com", | |
| 309 "http://root.com"}, | |
| 310 kEnabled, | |
| 311 MakeState(true)}, | |
| 312 | |
| 313 {{"http://ex.com", "http://child2.com", "http://parent1.com", | |
| 314 "http://root.com"}, | |
| 315 kEnabled, | |
| 316 MakeState(false, true)}, | |
| 317 | |
| 318 {{"http://ex.com", "http://ex.com", "http://child3.com", | |
| 319 "http://parent1.com", "http://root.com"}, | |
| 320 kDryRun, | |
| 321 MakeState(true, false, kDryRun)}, | |
| 322 }; | |
| 323 | |
| 324 for (size_t i = 0, size = arraysize(kTestCases); i != size; ++i) { | |
| 325 const auto& test_case = kTestCases[i]; | |
| 326 SCOPED_TRACE(::testing::Message() << "Test number: " << i); | |
| 327 | |
| 328 std::vector<GURL> ancestor_document_urls; | |
| 329 for (const auto& url_string : test_case.ancestor_document_urls) | |
| 330 ancestor_document_urls.emplace_back(url_string); | |
| 331 | |
| 332 ActivationState activation_state = ComputeActivationState( | |
| 333 test_case.activation_level, false, ancestor_document_urls, ruleset()); | |
| 334 EXPECT_EQ(test_case.expected_activation_state, activation_state); | |
| 335 } | |
| 336 } | |
| 337 | |
| 338 } // namespace subresource_filter | |
| OLD | NEW |