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