1// Copyright (c) 2011 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
6#include "chrome/browser/content_settings/content_settings_base_provider.h"
7
8#include "base/command_line.h"
9#include "base/logging.h"
10#include "chrome/common/chrome_switches.h"
11#include "googleurl/src/gurl.h"
12#include "net/base/net_util.h"
13
14namespace {
15
16// True if a given content settings type requires additional resource
17// identifiers.
18const bool kRequiresResourceIdentifier[CONTENT_SETTINGS_NUM_TYPES] = {
19  false,  // CONTENT_SETTINGS_TYPE_COOKIES
20  false,  // CONTENT_SETTINGS_TYPE_IMAGES
21  false,  // CONTENT_SETTINGS_TYPE_JAVASCRIPT
22  true,   // CONTENT_SETTINGS_TYPE_PLUGINS
23  false,  // CONTENT_SETTINGS_TYPE_POPUPS
24  false,  // Not used for Geolocation
25  false,  // Not used for Notifications
26};
27
28}  // namespace
29
30namespace content_settings {
31
32ExtendedContentSettings::ExtendedContentSettings() {}
33
34ExtendedContentSettings::ExtendedContentSettings(
35    const ExtendedContentSettings& rhs)
36    : content_settings(rhs.content_settings),
37      content_settings_for_resources(rhs.content_settings_for_resources) {
38}
39
40ExtendedContentSettings::~ExtendedContentSettings() {}
41
42BaseProvider::BaseProvider(bool is_incognito)
43    : is_incognito_(is_incognito) {
44}
45
46BaseProvider::~BaseProvider() {}
47
48bool BaseProvider::RequiresResourceIdentifier(
49    ContentSettingsType content_type) const {
50  if (CommandLine::ForCurrentProcess()->HasSwitch(
51      switches::kEnableResourceContentSettings)) {
52    return kRequiresResourceIdentifier[content_type];
53  } else {
54    return false;
55  }
56}
57
58bool BaseProvider::AllDefault(
59    const ExtendedContentSettings& settings) const {
60  for (size_t i = 0; i < arraysize(settings.content_settings.settings); ++i) {
61    if (settings.content_settings.settings[i] != CONTENT_SETTING_DEFAULT)
62      return false;
63  }
64  return settings.content_settings_for_resources.empty();
65}
66
67ContentSetting BaseProvider::GetContentSetting(
68    const GURL& requesting_url,
69    const GURL& embedding_url,
70    ContentSettingsType content_type,
71    const ResourceIdentifier& resource_identifier) const {
72  // Support for embedding_patterns is not implemented yet.
73  DCHECK(requesting_url == embedding_url);
74
75  if (!RequiresResourceIdentifier(content_type) ||
76      (RequiresResourceIdentifier(content_type) && resource_identifier.empty()))
77    return GetNonDefaultContentSettings(requesting_url).settings[content_type];
78
79  // Resolve content settings with resource identifier.
80  // 1. Check for pattern that exactly match the url/host
81  //     1.1 In the content-settings-map
82  //     1.2 In the incognito content-settings-map
83  // 3. Shorten the url subdomain by subdomain and try to find a pattern in
84  //     3.1 OTR content-settings-map
85  //     3.2 content-settings-map
86  base::AutoLock auto_lock(lock_);
87  const std::string host(net::GetHostOrSpecFromURL(requesting_url));
88  ContentSettingsTypeResourceIdentifierPair
89      requested_setting(content_type, resource_identifier);
90
91  // Check for exact matches first.
92  HostContentSettings::const_iterator i(host_content_settings_.find(host));
93  if (i != host_content_settings_.end() &&
94      i->second.content_settings_for_resources.find(requested_setting) !=
95      i->second.content_settings_for_resources.end()) {
96    return i->second.content_settings_for_resources.find(
97        requested_setting)->second;
98  }
99
100  // If this map is not for an incognito profile, these searches will never
101  // match. The additional incognito exceptions always overwrite the
102  // regular ones.
103  i = incognito_settings_.find(host);
104  if (i != incognito_settings_.end() &&
105      i->second.content_settings_for_resources.find(requested_setting) !=
106      i->second.content_settings_for_resources.end()) {
107    return i->second.content_settings_for_resources.find(
108        requested_setting)->second;
109  }
110
111  // Match patterns starting with the most concrete pattern match.
112  for (std::string key =
113       std::string(ContentSettingsPattern::kDomainWildcard) + host; ; ) {
114    HostContentSettings::const_iterator i(incognito_settings_.find(key));
115    if (i != incognito_settings_.end() &&
116        i->second.content_settings_for_resources.find(requested_setting) !=
117        i->second.content_settings_for_resources.end()) {
118      return i->second.content_settings_for_resources.find(
119          requested_setting)->second;
120    }
121
122    i = host_content_settings_.find(key);
123    if (i != host_content_settings_.end() &&
124        i->second.content_settings_for_resources.find(requested_setting) !=
125        i->second.content_settings_for_resources.end()) {
126      return i->second.content_settings_for_resources.find(
127          requested_setting)->second;
128    }
129
130    const size_t next_dot =
131        key.find('.', ContentSettingsPattern::kDomainWildcardLength);
132    if (next_dot == std::string::npos)
133      break;
134    key.erase(ContentSettingsPattern::kDomainWildcardLength,
135              next_dot - ContentSettingsPattern::kDomainWildcardLength + 1);
136  }
137
138  return CONTENT_SETTING_DEFAULT;
139}
140
141void BaseProvider::GetAllContentSettingsRules(
142    ContentSettingsType content_type,
143    const ResourceIdentifier& resource_identifier,
144    Rules* content_setting_rules) const {
145  DCHECK(content_setting_rules);
146  content_setting_rules->clear();
147
148  const HostContentSettings* map_to_return =
149      is_incognito_ ? &incognito_settings_ : &host_content_settings_;
150  ContentSettingsTypeResourceIdentifierPair requested_setting(
151      content_type, resource_identifier);
152
153  base::AutoLock auto_lock(lock_);
154  for (HostContentSettings::const_iterator i(map_to_return->begin());
155       i != map_to_return->end(); ++i) {
156    ContentSetting setting;
157    if (RequiresResourceIdentifier(content_type)) {
158      if (i->second.content_settings_for_resources.find(requested_setting) !=
159          i->second.content_settings_for_resources.end()) {
160        setting = i->second.content_settings_for_resources.find(
161            requested_setting)->second;
162      } else {
163        setting = CONTENT_SETTING_DEFAULT;
164      }
165    } else {
166     setting = i->second.content_settings.settings[content_type];
167    }
168    if (setting != CONTENT_SETTING_DEFAULT) {
169      // Use of push_back() relies on the map iterator traversing in order of
170      // ascending keys.
171      content_setting_rules->push_back(Rule(ContentSettingsPattern(i->first),
172                                            ContentSettingsPattern(i->first),
173                                            setting));
174    }
175  }
176}
177
178ContentSettings BaseProvider::GetNonDefaultContentSettings(
179    const GURL& url) const {
180  base::AutoLock auto_lock(lock_);
181
182  const std::string host(net::GetHostOrSpecFromURL(url));
183  ContentSettings output;
184  for (int j = 0; j < CONTENT_SETTINGS_NUM_TYPES; ++j)
185    output.settings[j] = CONTENT_SETTING_DEFAULT;
186
187  // Check for exact matches first.
188  HostContentSettings::const_iterator i(host_content_settings_.find(host));
189  if (i != host_content_settings_.end())
190    output = i->second.content_settings;
191
192  // If this map is not for an incognito profile, these searches will never
193  // match. The additional incognito exceptions always overwrite the
194  // regular ones.
195  i = incognito_settings_.find(host);
196  if (i != incognito_settings_.end()) {
197    for (int j = 0; j < CONTENT_SETTINGS_NUM_TYPES; ++j)
198      if (i->second.content_settings.settings[j] != CONTENT_SETTING_DEFAULT)
199        output.settings[j] = i->second.content_settings.settings[j];
200  }
201
202  // Match patterns starting with the most concrete pattern match.
203  for (std::string key =
204       std::string(ContentSettingsPattern::kDomainWildcard) + host; ; ) {
205    HostContentSettings::const_iterator i(incognito_settings_.find(key));
206    if (i != incognito_settings_.end()) {
207      for (int j = 0; j < CONTENT_SETTINGS_NUM_TYPES; ++j) {
208        if (output.settings[j] == CONTENT_SETTING_DEFAULT)
209          output.settings[j] = i->second.content_settings.settings[j];
210      }
211    }
212    i = host_content_settings_.find(key);
213    if (i != host_content_settings_.end()) {
214      for (int j = 0; j < CONTENT_SETTINGS_NUM_TYPES; ++j) {
215        if (output.settings[j] == CONTENT_SETTING_DEFAULT)
216          output.settings[j] = i->second.content_settings.settings[j];
217      }
218    }
219    const size_t next_dot =
220        key.find('.', ContentSettingsPattern::kDomainWildcardLength);
221    if (next_dot == std::string::npos)
222      break;
223    key.erase(ContentSettingsPattern::kDomainWildcardLength,
224              next_dot - ContentSettingsPattern::kDomainWildcardLength + 1);
225  }
226
227  return output;
228}
229
230void BaseProvider::UpdateContentSettingsMap(
231    const ContentSettingsPattern& requesting_pattern,
232    const ContentSettingsPattern& embedding_pattern,
233    ContentSettingsType content_type,
234    const ResourceIdentifier& resource_identifier,
235    ContentSetting content_setting) {
236  std::string pattern_str(requesting_pattern.CanonicalizePattern());
237  HostContentSettings* content_settings_map = host_content_settings();
238  ExtendedContentSettings& extended_settings =
239      (*content_settings_map)[pattern_str];
240  extended_settings.content_settings.settings[content_type] = content_setting;
241}
242
243// static
244ContentSetting BaseProvider::ClickToPlayFixup(ContentSettingsType content_type,
245                                              ContentSetting setting) {
246  if (setting == CONTENT_SETTING_ASK &&
247      content_type == CONTENT_SETTINGS_TYPE_PLUGINS &&
248      !CommandLine::ForCurrentProcess()->HasSwitch(
249          switches::kEnableClickToPlay)) {
250    return CONTENT_SETTING_BLOCK;
251  }
252  return setting;
253}
254
255}  // namespace content_settings
256