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
3// found in the LICENSE file.
4
5#include "chrome/browser/content_settings/content_settings_origin_identifier_value_map.h"
6
7#include "base/compiler_specific.h"
8#include "base/logging.h"
9#include "base/memory/scoped_ptr.h"
10#include "base/synchronization/lock.h"
11#include "base/values.h"
12#include "chrome/browser/content_settings/content_settings_rule.h"
13#include "chrome/browser/content_settings/content_settings_utils.h"
14#include "chrome/common/content_settings_types.h"
15#include "url/gurl.h"
16
17namespace content_settings {
18
19namespace {
20
21// This iterator is used for iterating the rules for |content_type| and
22// |resource_identifier| in the precedence order of the rules.
23class RuleIteratorImpl : public RuleIterator {
24 public:
25  // |RuleIteratorImpl| takes the ownership of |auto_lock|.
26  RuleIteratorImpl(
27      const OriginIdentifierValueMap::Rules::const_iterator& current_rule,
28      const OriginIdentifierValueMap::Rules::const_iterator& rule_end,
29      base::AutoLock* auto_lock)
30      : current_rule_(current_rule),
31        rule_end_(rule_end),
32        auto_lock_(auto_lock) {
33  }
34  virtual ~RuleIteratorImpl() {}
35
36  virtual bool HasNext() const OVERRIDE {
37    return (current_rule_ != rule_end_);
38  }
39
40  virtual Rule Next() OVERRIDE {
41    DCHECK(current_rule_ != rule_end_);
42    DCHECK(current_rule_->second.get());
43    Rule to_return(current_rule_->first.primary_pattern,
44                   current_rule_->first.secondary_pattern,
45                   current_rule_->second.get()->DeepCopy());
46    ++current_rule_;
47    return to_return;
48  }
49
50 private:
51  OriginIdentifierValueMap::Rules::const_iterator current_rule_;
52  OriginIdentifierValueMap::Rules::const_iterator rule_end_;
53  scoped_ptr<base::AutoLock> auto_lock_;
54};
55
56}  // namespace
57
58OriginIdentifierValueMap::EntryMapKey::EntryMapKey(
59    ContentSettingsType content_type,
60    const ResourceIdentifier& resource_identifier)
61    : content_type(content_type),
62      resource_identifier(resource_identifier) {
63}
64
65bool OriginIdentifierValueMap::EntryMapKey::operator<(
66    const OriginIdentifierValueMap::EntryMapKey& other) const {
67  if (content_type != other.content_type)
68    return content_type < other.content_type;
69  return (resource_identifier < other.resource_identifier);
70}
71
72OriginIdentifierValueMap::PatternPair::PatternPair(
73    const ContentSettingsPattern& primary_pattern,
74    const ContentSettingsPattern& secondary_pattern)
75    : primary_pattern(primary_pattern),
76      secondary_pattern(secondary_pattern) {
77}
78
79bool OriginIdentifierValueMap::PatternPair::operator<(
80    const OriginIdentifierValueMap::PatternPair& other) const {
81  // Note that this operator is the other way around than
82  // |ContentSettingsPattern::operator<|. It sorts patterns with higher
83  // precedence first.
84  if (primary_pattern > other.primary_pattern)
85    return true;
86  else if (other.primary_pattern > primary_pattern)
87    return false;
88  return (secondary_pattern > other.secondary_pattern);
89}
90
91RuleIterator* OriginIdentifierValueMap::GetRuleIterator(
92    ContentSettingsType content_type,
93    const ResourceIdentifier& resource_identifier,
94    base::Lock* lock) const {
95  EntryMapKey key(content_type, resource_identifier);
96  // We access |entries_| here, so we need to lock |lock_| first. The lock must
97  // be passed to the |RuleIteratorImpl| in a locked state, so that nobody can
98  // access |entries_| after |find()| but before the |RuleIteratorImpl| is
99  // created.
100  scoped_ptr<base::AutoLock> auto_lock;
101  if (lock)
102    auto_lock.reset(new base::AutoLock(*lock));
103  EntryMap::const_iterator it = entries_.find(key);
104  if (it == entries_.end())
105    return new EmptyRuleIterator();
106  return new RuleIteratorImpl(it->second.begin(),
107                              it->second.end(),
108                              auto_lock.release());
109}
110
111size_t OriginIdentifierValueMap::size() const {
112  size_t size = 0;
113  EntryMap::const_iterator it;
114  for (it = entries_.begin(); it != entries_.end(); ++it)
115    size += it->second.size();
116  return size;
117}
118
119OriginIdentifierValueMap::OriginIdentifierValueMap() {}
120
121OriginIdentifierValueMap::~OriginIdentifierValueMap() {}
122
123base::Value* OriginIdentifierValueMap::GetValue(
124    const GURL& primary_url,
125    const GURL& secondary_url,
126    ContentSettingsType content_type,
127    const ResourceIdentifier& resource_identifier) const {
128  EntryMapKey key(content_type, resource_identifier);
129  EntryMap::const_iterator it = entries_.find(key);
130  if (it == entries_.end())
131      return NULL;
132
133  // Iterate the entries in until a match is found. Since the rules are stored
134  // in the order of decreasing precedence, the most specific match is found
135  // first.
136  Rules::const_iterator entry;
137  for (entry = it->second.begin(); entry != it->second.end(); ++entry) {
138    if (entry->first.primary_pattern.Matches(primary_url) &&
139        entry->first.secondary_pattern.Matches(secondary_url)) {
140      return entry->second.get();
141    }
142  }
143  return NULL;
144}
145
146void OriginIdentifierValueMap::SetValue(
147    const ContentSettingsPattern& primary_pattern,
148    const ContentSettingsPattern& secondary_pattern,
149    ContentSettingsType content_type,
150    const ResourceIdentifier& resource_identifier,
151    base::Value* value) {
152  DCHECK(primary_pattern.IsValid());
153  DCHECK(secondary_pattern.IsValid());
154  DCHECK(value);
155  EntryMapKey key(content_type, resource_identifier);
156  PatternPair patterns(primary_pattern, secondary_pattern);
157  // This will create the entry and the linked_ptr if needed.
158  entries_[key][patterns].reset(value);
159}
160
161void OriginIdentifierValueMap::DeleteValue(
162      const ContentSettingsPattern& primary_pattern,
163      const ContentSettingsPattern& secondary_pattern,
164      ContentSettingsType content_type,
165      const ResourceIdentifier& resource_identifier) {
166  EntryMapKey key(content_type, resource_identifier);
167  PatternPair patterns(primary_pattern, secondary_pattern);
168  entries_[key].erase(patterns);
169  if (entries_[key].empty()) {
170    entries_.erase(key);
171  }
172}
173
174void OriginIdentifierValueMap::DeleteValues(
175      ContentSettingsType content_type,
176      const ResourceIdentifier& resource_identifier) {
177  EntryMapKey key(content_type, resource_identifier);
178  entries_.erase(key);
179}
180
181void OriginIdentifierValueMap::clear() {
182  // Delete all owned value objects.
183  entries_.clear();
184}
185
186}  // namespace content_settings
187