Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(105)

Side by Side Diff: chrome/browser/safe_browsing/safe_browsing_database.cc

Issue 10802098: Cleanup histograms tracking prefix-set issues in safe-browsing. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/safe_browsing/safe_browsing_database.h" 5 #include "chrome/browser/safe_browsing/safe_browsing_database.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <iterator> 8 #include <iterator>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after
229 return a.full_hash.prefix < b.full_hash.prefix; 229 return a.full_hash.prefix < b.full_hash.prefix;
230 } 230 }
231 231
232 // As compared to the bloom filter, PrefixSet should have these 232 // As compared to the bloom filter, PrefixSet should have these
233 // properties: 233 // properties:
234 // - Any bloom filter miss should be a prefix set miss. 234 // - Any bloom filter miss should be a prefix set miss.
235 // - Any prefix set hit should be a bloom filter hit. 235 // - Any prefix set hit should be a bloom filter hit.
236 // - Bloom filter false positives are prefix set misses. 236 // - Bloom filter false positives are prefix set misses.
237 // The following is to log actual performance to verify this. 237 // The following is to log actual performance to verify this.
238 enum PrefixSetEvent { 238 enum PrefixSetEvent {
239 PREFIX_SET_EVENT_HIT, 239 // Hits to prefix set and bloom filter.
240 PREFIX_SET_EVENT_BLOOM_HIT, 240 PREFIX_SET_HIT,
241 PREFIX_SET_EVENT_BLOOM_MISS_PREFIX_HIT, 241 PREFIX_SET_BLOOM_HIT,
242 PREFIX_SET_EVENT_BLOOM_MISS_PREFIX_HIT_INVALID, 242 // These were to track bloom misses which hit the prefix set, with
243 // _INVALID to track where the item didn't appear to actually be in
244 // the prefix set. _INVALID was never hit.
245 PREFIX_SET_BLOOM_MISS_PREFIX_SET_HIT,
246 PREFIX_SET_BLOOM_MISS_PREFIX_SET_HIT_INVALID_OBSOLETE,
247 // GetPrefixes() after creation failed to get the same prefixes.
243 PREFIX_SET_GETPREFIXES_BROKEN, 248 PREFIX_SET_GETPREFIXES_BROKEN,
244 PREFIX_SET_GETPREFIXES_BROKEN_SIZE, 249 // Fine-grained tests which didn't provide any good direction.
245 PREFIX_SET_GETPREFIXES_FIRST_BROKEN, 250 PREFIX_SET_GETPREFIXES_BROKEN_SIZE_OBSOLETE,
246 PREFIX_SET_SBPREFIX_WAS_BROKEN, 251 PREFIX_SET_GETPREFIXES_FIRST_BROKEN_OBSOLETE,
247 PREFIX_SET_GETPREFIXES_BROKEN_SORTING, 252 PREFIX_SET_SBPREFIX_WAS_BROKEN_OBSOLETE,
248 PREFIX_SET_GETPREFIXES_BROKEN_DUPLICATION, 253 PREFIX_SET_GETPREFIXES_BROKEN_SORTING_OBSOLETE,
249 PREFIX_SET_GETPREFIX_UNSORTED_IS_DELTA, 254 PREFIX_SET_GETPREFIXES_BROKEN_DUPLICATION_OBSOLETE,
250 PREFIX_SET_GETPREFIX_UNSORTED_IS_INDEX, 255 PREFIX_SET_GETPREFIXES_UNSORTED_IS_DELTA_OBSOLETE,
251 PREFIX_SET_GETPREFIX_CHECKSUM_MISMATCH, 256 PREFIX_SET_GETPREFIXES_UNSORTED_IS_INDEX_OBSOLETE,
257 // Failed checksum when creating prefix set.
258 PREFIX_SET_CREATE_PREFIX_SET_CHECKSUM,
252 259
253 // Memory space for histograms is determined by the max. ALWAYS ADD 260 // Memory space for histograms is determined by the max. ALWAYS ADD
254 // NEW VALUES BEFORE THIS ONE. 261 // NEW VALUES BEFORE THIS ONE.
255 PREFIX_SET_EVENT_MAX 262 PREFIX_SET_EVENT_MAX
256 }; 263 };
257 264
258 void RecordPrefixSetInfo(PrefixSetEvent event_type) { 265 void RecordPrefixSetInfo(PrefixSetEvent event_type) {
259 UMA_HISTOGRAM_ENUMERATION("SB2.PrefixSetEvent", event_type, 266 UMA_HISTOGRAM_ENUMERATION("SB2.PrefixSetEvent", event_type,
260 PREFIX_SET_EVENT_MAX); 267 PREFIX_SET_EVENT_MAX);
261 } 268 }
262 269
270 // Helper to reduce code duplication.
271 safe_browsing::PrefixSet* CreateEmptyPrefixSet() {
272 return new safe_browsing::PrefixSet(std::vector<SBPrefix>());
273 }
274
263 // Generate a |PrefixSet| instance from the contents of 275 // Generate a |PrefixSet| instance from the contents of
264 // |add_prefixes|. Additionally performs various checks to make sure 276 // |add_prefixes|. Additionally performs various checks to make sure
265 // that the resulting prefix set is valid, so that the 277 // that the resulting prefix set is valid, so that histograms in
266 // PREFIX_SET_EVENT_BLOOM_MISS_PREFIX_HIT_INVALID histogram in
267 // ContainsBrowseUrl() can be trustworthy. 278 // ContainsBrowseUrl() can be trustworthy.
268 safe_browsing::PrefixSet* PrefixSetFromAddPrefixes( 279 safe_browsing::PrefixSet* PrefixSetFromAddPrefixes(
269 const SBAddPrefixes& add_prefixes) { 280 const SBAddPrefixes& add_prefixes) {
270 // TODO(shess): If |add_prefixes| were sorted by the prefix, it 281 // TODO(shess): If |add_prefixes| were sorted by the prefix, it
271 // could be passed directly to |PrefixSet()|, removing the need for 282 // could be passed directly to |PrefixSet()|, removing the need for
272 // |prefixes|. For now, |prefixes| is useful while debugging 283 // |prefixes|. For now, |prefixes| is useful while debugging
273 // things. 284 // things.
274 std::vector<SBPrefix> prefixes; 285 std::vector<SBPrefix> prefixes;
275 prefixes.reserve(add_prefixes.size()); 286 prefixes.reserve(add_prefixes.size());
276 for (SBAddPrefixes::const_iterator iter = add_prefixes.begin(); 287 for (SBAddPrefixes::const_iterator iter = add_prefixes.begin();
277 iter != add_prefixes.end(); ++iter) { 288 iter != add_prefixes.end(); ++iter) {
278 prefixes.push_back(iter->prefix); 289 prefixes.push_back(iter->prefix);
279 } 290 }
280 291
281 std::sort(prefixes.begin(), prefixes.end()); 292 std::sort(prefixes.begin(), prefixes.end());
282 prefixes.erase(std::unique(prefixes.begin(), prefixes.end()), 293 prefixes.erase(std::unique(prefixes.begin(), prefixes.end()),
283 prefixes.end()); 294 prefixes.end());
284 295
285 scoped_ptr<safe_browsing::PrefixSet> 296 scoped_ptr<safe_browsing::PrefixSet>
286 prefix_set(new safe_browsing::PrefixSet(prefixes)); 297 prefix_set(new safe_browsing::PrefixSet(prefixes));
287 298
288 std::vector<SBPrefix> restored; 299 std::vector<SBPrefix> restored;
289 prefix_set->GetPrefixes(&restored); 300 prefix_set->GetPrefixes(&restored);
290 301
291 // Expect them to be equal. 302 // Expect them to be equal.
292 if (restored.size() == prefixes.size() && 303 if (restored.size() == prefixes.size() &&
293 std::equal(prefixes.begin(), prefixes.end(), restored.begin())) 304 std::equal(prefixes.begin(), prefixes.end(), restored.begin()))
294 return prefix_set.release(); 305 return prefix_set.release();
295 306
296 // Log BROKEN for continuity with previous release, and SIZE to 307 // NOTE(shess): Past histograms have indicated that in a given day,
297 // distinguish which test failed. 308 // about 1 in 100,000 updates result in a PrefixSet which was
309 // inconsistent relative to the BloomFilter. Windows is about 5x
310 // more likely to build an inconsistent PrefixSet than Mac. A
311 // number of developers have reviewed the code, and I ran extensive
312 // fuzzing with random data, so at this point I'm trying to
313 // demonstrate memory corruption.
314 //
315 // Other findings from past instrumentation:
316 // - half of one percent of brokenness cases implied duplicate items
317 // in |prefixes|. Per the code above, this should not be
318 // possible.
319 // - about 1/20 of broken cases happened more than once for a given
320 // user. Note that empty updates generally don't hit this code at
321 // all, so that may not imply a specific input pattern breaking things.
322 // - about 1/3 of broken cases show a checksum mismatch between the
323 // checksum calculated while creating |prefix_set| and the
324 // checksum calculated immediately after creation. This is almost
325 // certainly memory corruption.
298 NOTREACHED(); 326 NOTREACHED();
299 RecordPrefixSetInfo(PREFIX_SET_GETPREFIXES_BROKEN); 327 RecordPrefixSetInfo(PREFIX_SET_GETPREFIXES_BROKEN);
300 if (restored.size() != prefixes.size())
301 RecordPrefixSetInfo(PREFIX_SET_GETPREFIXES_BROKEN_SIZE);
302 328
303 // Try to distinguish between updates from one broken user and a 329 // Broken because internal memory was corrupted during construction.
304 // distributed problem. 330 if (!prefix_set->CheckChecksum())
305 static bool logged_broken = false; 331 RecordPrefixSetInfo(PREFIX_SET_CREATE_PREFIX_SET_CHECKSUM);
306 if (!logged_broken) {
307 RecordPrefixSetInfo(PREFIX_SET_GETPREFIXES_FIRST_BROKEN);
308 logged_broken = true;
309 }
310 332
311 // This seems so very very unlikely. But if it ever were true, then 333 // TODO(shess): Test whether |prefixes| changed during construction.
312 // it could explain why GetPrefixes() seemed broken.
313 if (sizeof(int) != sizeof(int32))
314 RecordPrefixSetInfo(PREFIX_SET_SBPREFIX_WAS_BROKEN);
315 334
316 // Check if memory was corrupted during construction. 335 return CreateEmptyPrefixSet();
317 if (!prefix_set->CheckChecksum())
318 RecordPrefixSetInfo(PREFIX_SET_GETPREFIX_CHECKSUM_MISMATCH);
319
320 // Check whether |restored| is unsorted, or has duplication.
321 if (restored.size()) {
322 size_t unsorted_count = 0;
323 bool duplicates = false;
324 SBPrefix prev = restored[0];
325 for (size_t i = 0; i < restored.size(); prev = restored[i], ++i) {
326 if (prev > restored[i]) {
327 unsorted_count++;
328 UMA_HISTOGRAM_COUNTS("SB2.PrefixSetUnsortedDifference",
329 prev - restored[i]);
330
331 // When unsorted, how big is the set, and how far are we into
332 // it. If the set is very small or large, that might inform
333 // pursuit of a degenerate case. If the percentage is close
334 // to 0%, 100%, or 50%, then there might be an interesting
335 // degenerate case to explore.
336 UMA_HISTOGRAM_COUNTS("SB2.PrefixSetUnsortedSize", restored.size());
337 UMA_HISTOGRAM_PERCENTAGE("SB2.PrefixSetUnsortedPercent",
338 i * 100 / restored.size());
339
340 if (prefix_set->IsDeltaAt(i)) {
341 RecordPrefixSetInfo(PREFIX_SET_GETPREFIX_UNSORTED_IS_DELTA);
342
343 // Histograms require memory on the order of the number of
344 // buckets, making high-precision logging expensive. For
345 // now aim for a sense of the range of the problem.
346 UMA_HISTOGRAM_CUSTOM_COUNTS("SB2.PrefixSetUnsortedDelta",
347 prefix_set->DeltaAt(i), 1, 0xFFFF, 50);
348 } else {
349 RecordPrefixSetInfo(PREFIX_SET_GETPREFIX_UNSORTED_IS_INDEX);
350 }
351 }
352 if (prev == restored[i])
353 duplicates = true;
354 }
355
356 // Record findings.
357 if (unsorted_count) {
358 RecordPrefixSetInfo(PREFIX_SET_GETPREFIXES_BROKEN_SORTING);
359 UMA_HISTOGRAM_COUNTS_100("SB2.PrefixSetUnsorted", unsorted_count);
360 }
361 if (duplicates)
362 RecordPrefixSetInfo(PREFIX_SET_GETPREFIXES_BROKEN_DUPLICATION);
363
364 // Fix the problems noted. If |restored| was unsorted, then
365 // |duplicates| may give a false negative.
366 if (unsorted_count)
367 std::sort(restored.begin(), restored.end());
368 if (unsorted_count || duplicates)
369 restored.erase(std::unique(restored.begin(), restored.end()),
370 restored.end());
371 }
372
373 // NOTE(shess): The following could be done using a single
374 // uber-loop, but it's complicated by needing multiple parallel
375 // iterators. Didn't seem worthwhile for something that will only
376 // live for a short period and only fires for one in a million
377 // updates.
378
379 // Find elements in |restored| which are not in |prefixes|.
380 std::vector<SBPrefix> difference;
381 std::set_difference(restored.begin(), restored.end(),
382 prefixes.begin(), prefixes.end(),
383 std::back_inserter(difference));
384 if (difference.size())
385 UMA_HISTOGRAM_COUNTS_100("SB2.PrefixSetRestoredExcess", difference.size());
386
387 // Find elements in |prefixes| which are not in |restored|.
388 difference.clear();
389 std::set_difference(prefixes.begin(), prefixes.end(),
390 restored.begin(), restored.end(),
391 std::back_inserter(difference));
392 if (difference.size())
393 UMA_HISTOGRAM_COUNTS_100("SB2.PrefixSetRestoredShortfall",
394 difference.size());
395
396 return prefix_set.release();
397 } 336 }
398 337
399 } // namespace 338 } // namespace
400 339
401 // The default SafeBrowsingDatabaseFactory. 340 // The default SafeBrowsingDatabaseFactory.
402 class SafeBrowsingDatabaseFactoryImpl : public SafeBrowsingDatabaseFactory { 341 class SafeBrowsingDatabaseFactoryImpl : public SafeBrowsingDatabaseFactory {
403 public: 342 public:
404 virtual SafeBrowsingDatabase* CreateSafeBrowsingDatabase( 343 virtual SafeBrowsingDatabase* CreateSafeBrowsingDatabase(
405 bool enable_download_protection, 344 bool enable_download_protection,
406 bool enable_client_side_whitelist, 345 bool enable_client_side_whitelist,
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after
609 { 548 {
610 base::AutoLock locked(lookup_lock_); 549 base::AutoLock locked(lookup_lock_);
611 full_browse_hashes_.clear(); 550 full_browse_hashes_.clear();
612 pending_browse_hashes_.clear(); 551 pending_browse_hashes_.clear();
613 prefix_miss_cache_.clear(); 552 prefix_miss_cache_.clear();
614 // TODO(shess): This could probably be |bloom_filter_.reset()|. 553 // TODO(shess): This could probably be |bloom_filter_.reset()|.
615 browse_bloom_filter_ = new BloomFilter(BloomFilter::kBloomFilterMinSize * 554 browse_bloom_filter_ = new BloomFilter(BloomFilter::kBloomFilterMinSize *
616 BloomFilter::kBloomFilterSizeRatio); 555 BloomFilter::kBloomFilterSizeRatio);
617 // TODO(shess): It is simpler for the code to assume that presence 556 // TODO(shess): It is simpler for the code to assume that presence
618 // of a bloom filter always implies presence of a prefix set. 557 // of a bloom filter always implies presence of a prefix set.
619 prefix_set_.reset(new safe_browsing::PrefixSet(std::vector<SBPrefix>())); 558 prefix_set_.reset(CreateEmptyPrefixSet());
620 } 559 }
621 // Wants to acquire the lock itself. 560 // Wants to acquire the lock itself.
622 WhitelistEverything(&csd_whitelist_); 561 WhitelistEverything(&csd_whitelist_);
623 WhitelistEverything(&download_whitelist_); 562 WhitelistEverything(&download_whitelist_);
624 563
625 return true; 564 return true;
626 } 565 }
627 566
628 // TODO(lzheng): Remove matching_list, it is not used anywhere. 567 // TODO(lzheng): Remove matching_list, it is not used anywhere.
629 bool SafeBrowsingDatabaseNew::ContainsBrowseUrl( 568 bool SafeBrowsingDatabaseNew::ContainsBrowseUrl(
(...skipping 17 matching lines...) Expand all
647 base::AutoLock locked(lookup_lock_); 586 base::AutoLock locked(lookup_lock_);
648 587
649 if (!browse_bloom_filter_.get()) 588 if (!browse_bloom_filter_.get())
650 return false; 589 return false;
651 DCHECK(prefix_set_.get()); 590 DCHECK(prefix_set_.get());
652 591
653 // |prefix_set_| is empty until the first update, only log info if 592 // |prefix_set_| is empty until the first update, only log info if
654 // not empty. 593 // not empty.
655 const bool prefix_set_empty = !prefix_set_->GetSize(); 594 const bool prefix_set_empty = !prefix_set_->GetSize();
656 595
657 // Used to double-check in case of a hit mis-match.
658 std::vector<SBPrefix> restored;
659
660 size_t miss_count = 0; 596 size_t miss_count = 0;
661 for (size_t i = 0; i < full_hashes.size(); ++i) { 597 for (size_t i = 0; i < full_hashes.size(); ++i) {
662 bool found = prefix_set_->Exists(full_hashes[i].prefix); 598 bool found = prefix_set_->Exists(full_hashes[i].prefix);
663 599
664 if (browse_bloom_filter_->Exists(full_hashes[i].prefix)) { 600 if (browse_bloom_filter_->Exists(full_hashes[i].prefix)) {
665 if (!prefix_set_empty) { 601 if (!prefix_set_empty) {
666 RecordPrefixSetInfo(PREFIX_SET_EVENT_BLOOM_HIT); 602 RecordPrefixSetInfo(PREFIX_SET_BLOOM_HIT);
603 // This should be less than PREFIX_SET_BLOOM_HIT by the
604 // false positive rate.
667 if (found) 605 if (found)
668 RecordPrefixSetInfo(PREFIX_SET_EVENT_HIT); 606 RecordPrefixSetInfo(PREFIX_SET_HIT);
669 } 607 }
670 prefix_hits->push_back(full_hashes[i].prefix); 608 prefix_hits->push_back(full_hashes[i].prefix);
671 if (prefix_miss_cache_.count(full_hashes[i].prefix) > 0) 609 if (prefix_miss_cache_.count(full_hashes[i].prefix) > 0)
672 ++miss_count; 610 ++miss_count;
673 } else { 611 } else {
674 // Bloom filter misses should never be in prefix set. Re-create 612 // Bloom filter misses should never be in prefix set.
675 // the original prefixes and manually search for it, to check if
676 // there's a bug with how |Exists()| is implemented.
677 // |UpdateBrowseStore()| previously verified that
678 // |GetPrefixes()| returns the same prefixes as were passed to
679 // the constructor.
680 DCHECK(!found); 613 DCHECK(!found);
681 if (found && !prefix_set_empty) { 614 if (found)
682 if (restored.empty()) 615 RecordPrefixSetInfo(PREFIX_SET_BLOOM_MISS_PREFIX_SET_HIT);
683 prefix_set_->GetPrefixes(&restored);
684
685 // If the item is not in the re-created list, then there is an
686 // error in |PrefixSet::Exists()|. If the item is in the
687 // re-created list, then the bloom filter was wrong.
688 if (std::binary_search(restored.begin(), restored.end(),
689 full_hashes[i].prefix)) {
690 RecordPrefixSetInfo(PREFIX_SET_EVENT_BLOOM_MISS_PREFIX_HIT);
691 } else {
692 RecordPrefixSetInfo(PREFIX_SET_EVENT_BLOOM_MISS_PREFIX_HIT_INVALID);
693 }
694 }
695 } 616 }
696 } 617 }
697 618
698 // If all the prefixes are cached as 'misses', don't issue a GetHash. 619 // If all the prefixes are cached as 'misses', don't issue a GetHash.
699 if (miss_count == prefix_hits->size()) 620 if (miss_count == prefix_hits->size())
700 return false; 621 return false;
701 622
702 // Find the matching full-hash results. |full_browse_hashes_| are from the 623 // Find the matching full-hash results. |full_browse_hashes_| are from the
703 // database, |pending_browse_hashes_| are from GetHash requests between 624 // database, |pending_browse_hashes_| are from GetHash requests between
704 // updates. 625 // updates.
(...skipping 674 matching lines...) Expand 10 before | Expand all | Expand 10 after
1379 1300
1380 const base::TimeTicks before = base::TimeTicks::Now(); 1301 const base::TimeTicks before = base::TimeTicks::Now();
1381 browse_bloom_filter_ = BloomFilter::LoadFile(bloom_filter_filename_); 1302 browse_bloom_filter_ = BloomFilter::LoadFile(bloom_filter_filename_);
1382 DVLOG(1) << "SafeBrowsingDatabaseNew read bloom filter in " 1303 DVLOG(1) << "SafeBrowsingDatabaseNew read bloom filter in "
1383 << (base::TimeTicks::Now() - before).InMilliseconds() << " ms"; 1304 << (base::TimeTicks::Now() - before).InMilliseconds() << " ms";
1384 1305
1385 if (!browse_bloom_filter_.get()) 1306 if (!browse_bloom_filter_.get())
1386 RecordFailure(FAILURE_DATABASE_FILTER_READ); 1307 RecordFailure(FAILURE_DATABASE_FILTER_READ);
1387 1308
1388 // Use an empty prefix set until the first update. 1309 // Use an empty prefix set until the first update.
1389 prefix_set_.reset(new safe_browsing::PrefixSet(std::vector<SBPrefix>())); 1310 prefix_set_.reset(CreateEmptyPrefixSet());
1390 } 1311 }
1391 1312
1392 bool SafeBrowsingDatabaseNew::Delete() { 1313 bool SafeBrowsingDatabaseNew::Delete() {
1393 DCHECK_EQ(creation_loop_, MessageLoop::current()); 1314 DCHECK_EQ(creation_loop_, MessageLoop::current());
1394 1315
1395 const bool r1 = browse_store_->Delete(); 1316 const bool r1 = browse_store_->Delete();
1396 if (!r1) 1317 if (!r1)
1397 RecordFailure(FAILURE_DATABASE_STORE_DELETE); 1318 RecordFailure(FAILURE_DATABASE_STORE_DELETE);
1398 1319
1399 const bool r2 = download_store_.get() ? download_store_->Delete() : true; 1320 const bool r2 = download_store_.get() ? download_store_->Delete() : true;
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
1464 if (std::binary_search(new_whitelist.begin(), new_whitelist.end(), 1385 if (std::binary_search(new_whitelist.begin(), new_whitelist.end(),
1465 kill_switch)) { 1386 kill_switch)) {
1466 // The kill switch is whitelisted hence we whitelist all URLs. 1387 // The kill switch is whitelisted hence we whitelist all URLs.
1467 WhitelistEverything(whitelist); 1388 WhitelistEverything(whitelist);
1468 } else { 1389 } else {
1469 base::AutoLock locked(lookup_lock_); 1390 base::AutoLock locked(lookup_lock_);
1470 whitelist->second = false; 1391 whitelist->second = false;
1471 whitelist->first.swap(new_whitelist); 1392 whitelist->first.swap(new_whitelist);
1472 } 1393 }
1473 } 1394 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698