OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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/geolocation/geolocation_content_settings_table_model.h" | 5 #include "chrome/browser/geolocation/geolocation_content_settings_table_model.h" |
6 | 6 |
7 #include "app/l10n_util.h" | 7 #include "app/l10n_util.h" |
| 8 #include "app/l10n_util_collator.h" |
8 #include "app/table_model_observer.h" | 9 #include "app/table_model_observer.h" |
9 #include "base/utf_string_conversions.h" | 10 #include "base/utf_string_conversions.h" |
| 11 #include "chrome/common/url_constants.h" |
10 #include "grit/generated_resources.h" | 12 #include "grit/generated_resources.h" |
11 | 13 |
| 14 namespace { |
| 15 // Return -1, 0, or 1 depending on whether |origin1| should be sorted before, |
| 16 // equal to, or after |origin2|. |
| 17 int CompareOrigins(const GURL& origin1, const GURL& origin2) { |
| 18 if (origin1 == origin2) |
| 19 return 0; |
| 20 |
| 21 // Sort alphabetically by host name. |
| 22 std::string origin1_host(origin1.host()); |
| 23 std::string origin2_host(origin2.host()); |
| 24 if (origin1_host != origin2_host) |
| 25 return origin1_host < origin2_host ? -1 : 1; |
| 26 |
| 27 // We'll show non-HTTP schemes, so sort them alphabetically, but put HTTP |
| 28 // first. |
| 29 std::string origin1_scheme(origin1.scheme()); |
| 30 std::string origin2_scheme(origin2.scheme()); |
| 31 if (origin1_scheme != origin2_scheme) { |
| 32 if (origin1_scheme == chrome::kHttpScheme) |
| 33 return -1; |
| 34 if (origin2_scheme == chrome::kHttpScheme) |
| 35 return 1; |
| 36 return origin1_scheme < origin2_scheme ? -1 : 1; |
| 37 } |
| 38 |
| 39 // Sort by port number. This has to differ if the origins are really origins |
| 40 // (and not longer URLs). An unspecified port will be -1 and thus |
| 41 // automatically come first (which is what we want). |
| 42 int origin1_port = origin1.IntPort(); |
| 43 int origin2_port = origin2.IntPort(); |
| 44 DCHECK(origin1_port != origin2_port); |
| 45 return origin1_port < origin2_port ? -1 : 1; |
| 46 } |
| 47 } // namespace |
| 48 |
12 GeolocationContentSettingsTableModel::GeolocationContentSettingsTableModel( | 49 GeolocationContentSettingsTableModel::GeolocationContentSettingsTableModel( |
13 GeolocationContentSettingsMap* map) | 50 GeolocationContentSettingsMap* map) |
14 : map_(map), | 51 : map_(map), |
15 observer_(NULL) { | 52 observer_(NULL) { |
16 GeolocationContentSettingsMap::AllOriginsSettings settings( | 53 GeolocationContentSettingsMap::AllOriginsSettings settings( |
17 map_->GetAllOriginsSettings()); | 54 map_->GetAllOriginsSettings()); |
18 GeolocationContentSettingsMap::AllOriginsSettings::const_iterator i; | 55 GeolocationContentSettingsMap::AllOriginsSettings::const_iterator i; |
19 for (i = settings.begin(); i != settings.end(); ++i) | 56 for (i = settings.begin(); i != settings.end(); ++i) |
20 AddEntriesForOrigin(i->first, i->second); | 57 AddEntriesForOrigin(i->first, i->second); |
21 } | 58 } |
22 | 59 |
23 bool GeolocationContentSettingsTableModel::CanRemoveException(int row) const { | 60 bool GeolocationContentSettingsTableModel::CanRemoveExceptions( |
24 const Entry& entry = entries_[row]; | 61 const Rows& rows) const { |
25 return !(entry.origin == entry.embedding_origin && | 62 for (Rows::const_iterator i(rows.begin()); i != rows.end(); ++i) { |
26 static_cast<size_t>(row + 1) < entries_.size() && | 63 const Entry& entry = entries_[*i]; |
27 entries_[row + 1].origin == entry.origin && | 64 if ((entry.origin == entry.embedding_origin) && |
28 entry.setting == CONTENT_SETTING_DEFAULT); | 65 (entry.setting == CONTENT_SETTING_DEFAULT)) { |
| 66 for (size_t j = (*i) + 1; |
| 67 (j < entries_.size()) && (entries_[j].origin == entry.origin); ++j) { |
| 68 if (!rows.count(j)) |
| 69 return false; |
| 70 } |
| 71 } |
| 72 } |
| 73 return true; |
29 } | 74 } |
30 | 75 |
31 void GeolocationContentSettingsTableModel::RemoveException(int row) { | 76 void GeolocationContentSettingsTableModel::RemoveExceptions(const Rows& rows) { |
32 Entry& entry = entries_[row]; | 77 for (Rows::const_reverse_iterator i(rows.rbegin()); i != rows.rend(); ++i) { |
33 bool next_has_same_origin = static_cast<size_t>(row + 1) < entries_.size() && | 78 size_t row = *i; |
34 entries_[row + 1].origin == entry.origin; | 79 Entry* entry = &entries_[row]; |
35 bool has_children = entry.origin == entry.embedding_origin && | 80 GURL entry_origin(entry->origin); // Copy, not reference, since we'll erase |
36 next_has_same_origin; | 81 // |entry| before we're done with this. |
37 map_->SetContentSetting(entry.origin, entry.embedding_origin, | 82 bool next_has_same_origin = ((row + 1) < entries_.size()) && |
38 CONTENT_SETTING_DEFAULT); | 83 (entries_[row + 1].origin == entry_origin); |
39 if (has_children) { | 84 bool has_children = (entry_origin == entry->embedding_origin) && |
40 entry.setting = CONTENT_SETTING_DEFAULT; | 85 next_has_same_origin; |
41 if (observer_) | 86 map_->SetContentSetting(entry_origin, entry->embedding_origin, |
42 observer_->OnItemsChanged(row, 1); | 87 CONTENT_SETTING_DEFAULT); |
43 } else if (!next_has_same_origin && | 88 if (has_children) { |
44 row > 0 && | 89 entry->setting = CONTENT_SETTING_DEFAULT; |
45 entries_[row - 1].origin == entry.origin && | 90 if (observer_) |
46 entries_[row - 1].setting == CONTENT_SETTING_DEFAULT) { | 91 observer_->OnItemsChanged(row, 1); |
47 // If we remove the last non-default child of a default parent, we should | 92 continue; |
48 // remove the parent too. | 93 } |
49 entries_.erase(entries_.begin() + row - 1, entries_.begin() + row + 1); | 94 do { |
50 if (observer_) | 95 entries_.erase(entries_.begin() + row); // Note: |entry| is now garbage. |
51 observer_->OnItemsRemoved(row - 1, 2); | 96 if (observer_) |
52 } else { | 97 observer_->OnItemsRemoved(row, 1); |
53 entries_.erase(entries_.begin() + row); | 98 // If we remove the last non-default child of a default parent, we |
54 if (observer_) | 99 // should remove the parent too. We do these removals one-at-a-time |
55 observer_->OnItemsRemoved(row, 1); | 100 // because the table view will end up being called back as each row is |
| 101 // removed, in turn calling back to CanRemoveExceptions(), and if we've |
| 102 // already removed more entries than the view has, we'll have problems. |
| 103 if ((row == 0) || rows.count(row - 1)) |
| 104 break; |
| 105 entry = &entries_[--row]; |
| 106 } while (!next_has_same_origin && (entry->origin == entry_origin) && |
| 107 (entry->origin == entry->embedding_origin) && |
| 108 (entry->setting == CONTENT_SETTING_DEFAULT)); |
56 } | 109 } |
57 } | 110 } |
58 | 111 |
59 void GeolocationContentSettingsTableModel::RemoveAll() { | 112 void GeolocationContentSettingsTableModel::RemoveAll() { |
60 int old_row_count = RowCount(); | 113 int old_row_count = RowCount(); |
61 entries_.clear(); | 114 entries_.clear(); |
62 map_->ResetToDefault(); | 115 map_->ResetToDefault(); |
63 if (observer_) | 116 if (observer_) |
64 observer_->OnItemsRemoved(0, old_row_count); | 117 observer_->OnItemsRemoved(0, old_row_count); |
65 } | 118 } |
66 | 119 |
67 int GeolocationContentSettingsTableModel::RowCount() { | 120 int GeolocationContentSettingsTableModel::RowCount() { |
68 return entries_.size(); | 121 return entries_.size(); |
69 } | 122 } |
70 | 123 |
71 std::wstring GeolocationContentSettingsTableModel::GetText(int row, | 124 std::wstring GeolocationContentSettingsTableModel::GetText(int row, |
72 int column_id) { | 125 int column_id) { |
73 const Entry& entry = entries_[row]; | 126 const Entry& entry = entries_[row]; |
74 if (column_id == IDS_EXCEPTIONS_HOSTNAME_HEADER) { | 127 if (column_id == IDS_EXCEPTIONS_HOSTNAME_HEADER) { |
75 if (entry.origin == entry.embedding_origin) | 128 if (entry.origin == entry.embedding_origin) { |
76 return UTF8ToWide( | 129 return UTF8ToWide(GeolocationContentSettingsMap::OriginToString( |
77 GeolocationContentSettingsMap::OriginToString(entry.origin)); | 130 entry.origin)); |
78 if (entry.embedding_origin.is_empty()) | 131 } |
79 return ASCIIToWide(" ") + | 132 std::wstring indent(L" "); |
| 133 if (entry.embedding_origin.is_empty()) { |
| 134 // NOTE: As long as the user cannot add/edit entries from the exceptions |
| 135 // dialog, it's impossible to actually have a non-default setting for some |
| 136 // origin "embedded on any other site", so this row will never appear. If |
| 137 // we add the ability to add/edit exceptions, we'll need to decide when to |
| 138 // display this and how "removing" it will function. |
| 139 return indent + |
80 l10n_util::GetString(IDS_EXCEPTIONS_GEOLOCATION_EMBEDDED_ANY_OTHER); | 140 l10n_util::GetString(IDS_EXCEPTIONS_GEOLOCATION_EMBEDDED_ANY_OTHER); |
81 return ASCIIToWide(" ") + | 141 } |
82 l10n_util::GetStringF(IDS_EXCEPTIONS_GEOLOCATION_EMBEDDED_ON_HOST, | 142 return indent + l10n_util::GetStringF( |
83 UTF8ToWide(GeolocationContentSettingsMap::OriginToString( | 143 IDS_EXCEPTIONS_GEOLOCATION_EMBEDDED_ON_HOST, |
84 entry.embedding_origin))); | 144 UTF8ToWide(GeolocationContentSettingsMap::OriginToString( |
85 } else if (column_id == IDS_EXCEPTIONS_ACTION_HEADER) { | 145 entry.embedding_origin))); |
| 146 } |
| 147 |
| 148 if (column_id == IDS_EXCEPTIONS_ACTION_HEADER) { |
86 switch (entry.setting) { | 149 switch (entry.setting) { |
87 case CONTENT_SETTING_ALLOW: | 150 case CONTENT_SETTING_ALLOW: |
88 return l10n_util::GetString(IDS_EXCEPTIONS_ALLOW_BUTTON); | 151 return l10n_util::GetString(IDS_EXCEPTIONS_ALLOW_BUTTON); |
89 case CONTENT_SETTING_BLOCK: | 152 case CONTENT_SETTING_BLOCK: |
90 return l10n_util::GetString(IDS_EXCEPTIONS_BLOCK_BUTTON); | 153 return l10n_util::GetString(IDS_EXCEPTIONS_BLOCK_BUTTON); |
91 case CONTENT_SETTING_ASK: | 154 case CONTENT_SETTING_ASK: |
92 return l10n_util::GetString(IDS_EXCEPTIONS_ASK_BUTTON); | 155 return l10n_util::GetString(IDS_EXCEPTIONS_ASK_BUTTON); |
93 case CONTENT_SETTING_DEFAULT: | 156 case CONTENT_SETTING_DEFAULT: |
94 return l10n_util::GetString(IDS_EXCEPTIONS_NOT_SET_BUTTON); | 157 return l10n_util::GetString(IDS_EXCEPTIONS_NOT_SET_BUTTON); |
95 default: | 158 default: |
96 NOTREACHED(); | 159 break; |
97 } | 160 } |
98 } else { | |
99 NOTREACHED(); | |
100 } | 161 } |
| 162 |
| 163 NOTREACHED(); |
101 return std::wstring(); | 164 return std::wstring(); |
102 } | 165 } |
103 | 166 |
104 void GeolocationContentSettingsTableModel::SetObserver( | 167 void GeolocationContentSettingsTableModel::SetObserver( |
105 TableModelObserver* observer) { | 168 TableModelObserver* observer) { |
106 observer_ = observer; | 169 observer_ = observer; |
107 } | 170 } |
108 | 171 |
| 172 int GeolocationContentSettingsTableModel::CompareValues(int row1, |
| 173 int row2, |
| 174 int column_id) { |
| 175 DCHECK(row1 >= 0 && row1 < RowCount() && |
| 176 row2 >= 0 && row2 < RowCount()); |
| 177 |
| 178 const Entry& entry1 = entries_[row1]; |
| 179 const Entry& entry2 = entries_[row2]; |
| 180 |
| 181 // Sort top-level requesting origins, keeping all embedded (child) rules |
| 182 // together. |
| 183 int origin_comparison = CompareOrigins(entry1.origin, entry2.origin); |
| 184 if (origin_comparison == 0) { |
| 185 // The non-embedded rule comes before all embedded rules. |
| 186 bool entry1_origins_same = entry1.origin == entry1.embedding_origin; |
| 187 bool entry2_origins_same = entry2.origin == entry2.embedding_origin; |
| 188 if (entry1_origins_same != entry2_origins_same) |
| 189 return entry1_origins_same ? -1 : 1; |
| 190 |
| 191 // The "default" embedded rule comes after all other embedded rules. |
| 192 bool embedding_origin1_empty = entry1.embedding_origin.is_empty(); |
| 193 bool embedding_origin2_empty = entry2.embedding_origin.is_empty(); |
| 194 if (embedding_origin1_empty != embedding_origin2_empty) |
| 195 return embedding_origin2_empty ? -1 : 1; |
| 196 |
| 197 origin_comparison = |
| 198 CompareOrigins(entry1.embedding_origin, entry2.embedding_origin); |
| 199 } else if (column_id == IDS_EXCEPTIONS_ACTION_HEADER) { |
| 200 // The rows are in different origins. We need to find out how the top-level |
| 201 // origins will compare. |
| 202 while (entries_[row1].origin != entries_[row1].embedding_origin) |
| 203 --row1; |
| 204 while (entries_[row2].origin != entries_[row2].embedding_origin) |
| 205 --row2; |
| 206 } |
| 207 |
| 208 // The entries are at the same "scope". If we're sorting by action, then do |
| 209 // that now. |
| 210 if (column_id == IDS_EXCEPTIONS_ACTION_HEADER) { |
| 211 int compare_text = l10n_util::CompareStringWithCollator( |
| 212 GetCollator(), GetText(row1, column_id), GetText(row2, column_id)); |
| 213 if (compare_text != 0) |
| 214 return compare_text; |
| 215 } |
| 216 |
| 217 // Sort by the relevant origin. |
| 218 return origin_comparison; |
| 219 } |
| 220 |
109 void GeolocationContentSettingsTableModel::AddEntriesForOrigin( | 221 void GeolocationContentSettingsTableModel::AddEntriesForOrigin( |
110 const GURL& origin, | 222 const GURL& origin, |
111 const GeolocationContentSettingsMap::OneOriginSettings& settings) { | 223 const GeolocationContentSettingsMap::OneOriginSettings& settings) { |
112 GeolocationContentSettingsMap::OneOriginSettings::const_iterator parent = | 224 GeolocationContentSettingsMap::OneOriginSettings::const_iterator parent = |
113 settings.find(origin); | 225 settings.find(origin); |
114 | 226 |
115 // Add the "parent" entry for the non-embedded setting. | 227 // Add the "parent" entry for the non-embedded setting. |
116 entries_.push_back(Entry(origin, origin, | 228 entries_.push_back(Entry(origin, origin, |
117 (parent == settings.end()) ? CONTENT_SETTING_DEFAULT : parent->second)); | 229 (parent == settings.end()) ? CONTENT_SETTING_DEFAULT : parent->second)); |
118 | 230 |
119 // Add the "children" for any embedded settings. | 231 // Add the "children" for any embedded settings. |
120 GeolocationContentSettingsMap::OneOriginSettings::const_iterator i; | 232 GeolocationContentSettingsMap::OneOriginSettings::const_iterator i; |
121 for (i = settings.begin(); i != settings.end(); ++i) { | 233 for (i = settings.begin(); i != settings.end(); ++i) { |
122 // Skip the non-embedded setting which we already added above. | 234 // Skip the non-embedded setting which we already added above. |
123 if (i == parent) | 235 if (i == parent) |
124 continue; | 236 continue; |
125 | 237 |
126 entries_.push_back(Entry(origin, i->first, i->second)); | 238 entries_.push_back(Entry(origin, i->first, i->second)); |
127 } | 239 } |
128 } | 240 } |
129 | 241 |
130 // static | 242 // static |
131 GeolocationContentSettingsTableModel::Entry::Entry( | 243 GeolocationContentSettingsTableModel::Entry::Entry( |
132 const GURL& in_origin, const GURL& in_embedding_origin, | 244 const GURL& in_origin, const GURL& in_embedding_origin, |
133 ContentSetting in_setting) | 245 ContentSetting in_setting) |
134 : origin(in_origin), | 246 : origin(in_origin), |
135 embedding_origin(in_embedding_origin), | 247 embedding_origin(in_embedding_origin), |
136 setting(in_setting) { | 248 setting(in_setting) { |
137 } | 249 } |
OLD | NEW |