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 #include "chrome/browser/importer/profile_writer.h" 6 7 #include "base/string_util.h" 8 #include "base/threading/thread.h" 9 #include "base/utf_string_conversions.h" 10 #include "chrome/browser/bookmarks/bookmark_model.h" 11 #include "chrome/browser/password_manager/password_store.h" 12 #include "chrome/browser/prefs/pref_service.h" 13 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/search_engines/template_url.h" 15 #include "chrome/browser/search_engines/template_url_model.h" 16 #include "chrome/common/pref_names.h" 17 #include "content/common/notification_service.h" 18 19 ProfileWriter::BookmarkEntry::BookmarkEntry() 20 : in_toolbar(false), 21 is_folder(false) {} 22 23 ProfileWriter::BookmarkEntry::~BookmarkEntry() {} 24 25 ProfileWriter::ProfileWriter(Profile* profile) : profile_(profile) {} 26 27 bool ProfileWriter::BookmarkModelIsLoaded() const { 28 return profile_->GetBookmarkModel()->IsLoaded(); 29 } 30 31 bool ProfileWriter::TemplateURLModelIsLoaded() const { 32 return profile_->GetTemplateURLModel()->loaded(); 33 } 34 35 void ProfileWriter::AddPasswordForm(const webkit_glue::PasswordForm& form) { 36 profile_->GetPasswordStore(Profile::EXPLICIT_ACCESS)->AddLogin(form); 37 } 38 39 #if defined(OS_WIN) 40 void ProfileWriter::AddIE7PasswordInfo(const IE7PasswordInfo& info) { 41 profile_->GetWebDataService(Profile::EXPLICIT_ACCESS)->AddIE7Login(info); 42 } 43 #endif 44 45 void ProfileWriter::AddHistoryPage(const std::vector<history::URLRow>& page, 46 history::VisitSource visit_source) { 47 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)-> 48 AddPagesWithDetails(page, visit_source); 49 } 50 51 void ProfileWriter::AddHomepage(const GURL& home_page) { 52 DCHECK(profile_); 53 54 PrefService* prefs = profile_->GetPrefs(); 55 // NOTE: We set the kHomePage value, but keep the NewTab page as the homepage. 56 const PrefService::Preference* pref = prefs->FindPreference(prefs::kHomePage); 57 if (pref && !pref->IsManaged()) { 58 prefs->SetString(prefs::kHomePage, home_page.spec()); 59 prefs->ScheduleSavePersistentPrefs(); 60 } 61 } 62 63 void ProfileWriter::AddBookmarkEntry( 64 const std::vector<BookmarkEntry>& bookmark, 65 const string16& first_folder_name, 66 int options) { 67 BookmarkModel* model = profile_->GetBookmarkModel(); 68 DCHECK(model->IsLoaded()); 69 70 bool import_to_bookmark_bar = ((options & IMPORT_TO_BOOKMARK_BAR) != 0); 71 string16 real_first_folder = import_to_bookmark_bar ? first_folder_name : 72 GenerateUniqueFolderName(model, first_folder_name); 73 74 bool show_bookmark_toolbar = false; 75 std::set<const BookmarkNode*> folders_added_to; 76 bool import_mode = false; 77 if (bookmark.size() > 1) { 78 model->BeginImportMode(); 79 import_mode = true; 80 } 81 for (std::vector<BookmarkEntry>::const_iterator it = bookmark.begin(); 82 it != bookmark.end(); ++it) { 83 // Don't insert this url if it isn't valid. 84 if (!it->is_folder && !it->url.is_valid()) 85 continue; 86 87 // We suppose that bookmarks are unique by Title, URL, and Folder. Since 88 // checking for uniqueness may not be always the user's intention we have 89 // this as an option. 90 if (options & ADD_IF_UNIQUE && DoesBookmarkExist(model, *it, 91 real_first_folder, import_to_bookmark_bar)) 92 continue; 93 94 // Set up folders in BookmarkModel in such a way that path[i] is 95 // the subfolder of path[i-1]. Finally they construct a path in the 96 // model: 97 // path[0] \ path[1] \ ... \ path[size() - 1] 98 const BookmarkNode* parent = 99 (it->in_toolbar ? model->GetBookmarkBarNode() : model->other_node()); 100 for (std::vector<string16>::const_iterator i = it->path.begin(); 101 i != it->path.end(); ++i) { 102 const BookmarkNode* child = NULL; 103 const string16& folder_name = (!import_to_bookmark_bar && 104 !it->in_toolbar && (i == it->path.begin())) ? real_first_folder : *i; 105 106 for (int index = 0; index < parent->child_count(); ++index) { 107 const BookmarkNode* node = parent->GetChild(index); 108 if ((node->type() == BookmarkNode::BOOKMARK_BAR || 109 node->type() == BookmarkNode::FOLDER) && 110 node->GetTitle() == folder_name) { 111 child = node; 112 break; 113 } 114 } 115 if (child == NULL) 116 child = model->AddFolder(parent, parent->child_count(), folder_name); 117 parent = child; 118 } 119 folders_added_to.insert(parent); 120 if (it->is_folder) { 121 model->AddFolder(parent, parent->child_count(), it->title); 122 } else { 123 model->AddURLWithCreationTime(parent, parent->child_count(), 124 it->title, it->url, it->creation_time); 125 } 126 127 // If some items are put into toolbar, it looks like the user was using 128 // it in their last browser. We turn on the bookmarks toolbar. 129 if (it->in_toolbar) 130 show_bookmark_toolbar = true; 131 } 132 133 // Reset the date modified time of the folders we added to. We do this to 134 // make sure the 'recently added to' combobox in the bubble doesn't get random 135 // folders. 136 for (std::set<const BookmarkNode*>::const_iterator i = 137 folders_added_to.begin(); 138 i != folders_added_to.end(); ++i) { 139 model->ResetDateFolderModified(*i); 140 } 141 142 if (import_mode) { 143 model->EndImportMode(); 144 } 145 146 if (show_bookmark_toolbar && !(options & BOOKMARK_BAR_DISABLED)) 147 ShowBookmarkBar(); 148 } 149 150 void ProfileWriter::AddFavicons( 151 const std::vector<history::ImportedFaviconUsage>& favicons) { 152 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS)-> 153 SetImportedFavicons(favicons); 154 } 155 156 typedef std::map<std::string, const TemplateURL*> HostPathMap; 157 158 // Returns the key for the map built by BuildHostPathMap. If url_string is not 159 // a valid URL, an empty string is returned, otherwise host+path is returned. 160 static std::string HostPathKeyForURL(const GURL& url) { 161 return url.is_valid() ? url.host() + url.path() : std::string(); 162 } 163 164 // Builds the key to use in HostPathMap for the specified TemplateURL. Returns 165 // an empty string if a host+path can't be generated for the TemplateURL. 166 // If an empty string is returned, the TemplateURL should not be added to 167 // HostPathMap. 168 // 169 // If |try_url_if_invalid| is true, and |t_url| isn't valid, a string is built 170 // from the raw TemplateURL string. Use a value of true for |try_url_if_invalid| 171 // when checking imported URLs as the imported URL may not be valid yet may 172 // match the host+path of one of the default URLs. This is used to catch the 173 // case of IE using an invalid OSDD URL for Live Search, yet the host+path 174 // matches our prepopulate data. IE's URL for Live Search is something like 175 // 'http://...{Language}...'. As {Language} is not a valid OSDD parameter value 176 // the TemplateURL is invalid. 177 static std::string BuildHostPathKey(const TemplateURL* t_url, 178 bool try_url_if_invalid) { 179 if (t_url->url()) { 180 if (try_url_if_invalid && !t_url->url()->IsValid()) 181 return HostPathKeyForURL(GURL(t_url->url()->url())); 182 183 if (t_url->url()->SupportsReplacement()) { 184 return HostPathKeyForURL(GURL( 185 t_url->url()->ReplaceSearchTerms( 186 *t_url, ASCIIToUTF16("random string"), 187 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, string16()))); 188 } 189 } 190 return std::string(); 191 } 192 193 // Builds a set that contains an entry of the host+path for each TemplateURL in 194 // the TemplateURLModel that has a valid search url. 195 static void BuildHostPathMap(const TemplateURLModel& model, 196 HostPathMap* host_path_map) { 197 std::vector<const TemplateURL*> template_urls = model.GetTemplateURLs(); 198 for (size_t i = 0; i < template_urls.size(); ++i) { 199 const std::string host_path = BuildHostPathKey(template_urls[i], false); 200 if (!host_path.empty()) { 201 const TemplateURL* existing_turl = (*host_path_map)[host_path]; 202 if (!existing_turl || 203 (template_urls[i]->show_in_default_list() && 204 !existing_turl->show_in_default_list())) { 205 // If there are multiple TemplateURLs with the same host+path, favor 206 // those shown in the default list. If there are multiple potential 207 // defaults, favor the first one, which should be the more commonly used 208 // one. 209 (*host_path_map)[host_path] = template_urls[i]; 210 } 211 } // else case, TemplateURL doesn't have a search url, doesn't support 212 // replacement, or doesn't have valid GURL. Ignore it. 213 } 214 } 215 216 void ProfileWriter::AddKeywords(const std::vector<TemplateURL*>& template_urls, 217 int default_keyword_index, 218 bool unique_on_host_and_path) { 219 TemplateURLModel* model = profile_->GetTemplateURLModel(); 220 HostPathMap host_path_map; 221 if (unique_on_host_and_path) 222 BuildHostPathMap(*model, &host_path_map); 223 224 for (std::vector<TemplateURL*>::const_iterator i = template_urls.begin(); 225 i != template_urls.end(); ++i) { 226 TemplateURL* t_url = *i; 227 bool default_keyword = 228 default_keyword_index >= 0 && 229 (i - template_urls.begin() == default_keyword_index); 230 231 // TemplateURLModel requires keywords to be unique. If there is already a 232 // TemplateURL with this keyword, don't import it again. 233 const TemplateURL* turl_with_keyword = 234 model->GetTemplateURLForKeyword(t_url->keyword()); 235 if (turl_with_keyword != NULL) { 236 if (default_keyword) 237 model->SetDefaultSearchProvider(turl_with_keyword); 238 delete t_url; 239 continue; 240 } 241 242 // For search engines if there is already a keyword with the same 243 // host+path, we don't import it. This is done to avoid both duplicate 244 // search providers (such as two Googles, or two Yahoos) as well as making 245 // sure the search engines we provide aren't replaced by those from the 246 // imported browser. 247 if (unique_on_host_and_path && 248 host_path_map.find( 249 BuildHostPathKey(t_url, true)) != host_path_map.end()) { 250 if (default_keyword) { 251 const TemplateURL* turl_with_host_path = 252 host_path_map[BuildHostPathKey(t_url, true)]; 253 if (turl_with_host_path) 254 model->SetDefaultSearchProvider(turl_with_host_path); 255 else 256 NOTREACHED(); // BuildHostPathMap should only insert non-null values. 257 } 258 delete t_url; 259 continue; 260 } 261 if (t_url->url() && t_url->url()->IsValid()) { 262 model->Add(t_url); 263 if (default_keyword && TemplateURL::SupportsReplacement(t_url)) 264 model->SetDefaultSearchProvider(t_url); 265 } else { 266 // Don't add invalid TemplateURLs to the model. 267 delete t_url; 268 } 269 } 270 } 271 272 void ProfileWriter::ShowBookmarkBar() { 273 DCHECK(profile_); 274 275 PrefService* prefs = profile_->GetPrefs(); 276 // Check whether the bookmark bar is shown in current pref. 277 if (!prefs->GetBoolean(prefs::kShowBookmarkBar)) { 278 // Set the pref and notify the notification service. 279 prefs->SetBoolean(prefs::kShowBookmarkBar, true); 280 prefs->ScheduleSavePersistentPrefs(); 281 Source<Profile> source(profile_); 282 NotificationService::current()->Notify( 283 NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, source, 284 NotificationService::NoDetails()); 285 } 286 } 287 288 ProfileWriter::~ProfileWriter() {} 289 290 string16 ProfileWriter::GenerateUniqueFolderName( 291 BookmarkModel* model, 292 const string16& folder_name) { 293 // Build a set containing the folder names of the other folder. 294 std::set<string16> other_folder_names; 295 const BookmarkNode* other = model->other_node(); 296 297 for (int i = 0, child_count = other->child_count(); i < child_count; ++i) { 298 const BookmarkNode* node = other->GetChild(i); 299 if (node->is_folder()) 300 other_folder_names.insert(node->GetTitle()); 301 } 302 303 if (other_folder_names.find(folder_name) == other_folder_names.end()) 304 return folder_name; // Name is unique, use it. 305 306 // Otherwise iterate until we find a unique name. 307 for (int i = 1; i < 100; ++i) { 308 string16 name = folder_name + UTF8ToUTF16(base::StringPrintf(" (%d)", i)); 309 if (other_folder_names.find(name) == other_folder_names.end()) 310 return name; 311 } 312 313 return folder_name; 314 } 315 316 bool ProfileWriter::DoesBookmarkExist( 317 BookmarkModel* model, 318 const BookmarkEntry& entry, 319 const string16& first_folder_name, 320 bool import_to_bookmark_bar) { 321 std::vector<const BookmarkNode*> nodes_with_same_url; 322 model->GetNodesByURL(entry.url, &nodes_with_same_url); 323 if (nodes_with_same_url.empty()) 324 return false; 325 326 for (size_t i = 0; i < nodes_with_same_url.size(); ++i) { 327 const BookmarkNode* node = nodes_with_same_url[i]; 328 if (entry.title != node->GetTitle()) 329 continue; 330 331 // Does the path match? 332 bool found_match = true; 333 const BookmarkNode* parent = node->parent(); 334 for (std::vector<string16>::const_reverse_iterator path_it = 335 entry.path.rbegin(); 336 (path_it != entry.path.rend()) && found_match; ++path_it) { 337 const string16& folder_name = 338 (!import_to_bookmark_bar && path_it + 1 == entry.path.rend()) ? 339 first_folder_name : *path_it; 340 if (NULL == parent || *path_it != folder_name) 341 found_match = false; 342 else 343 parent = parent->parent(); 344 } 345 346 // We need a post test to differentiate checks such as 347 // /home/hello and /hello. The parent should either by the other folder 348 // node, or the bookmarks bar, depending upon import_to_bookmark_bar and 349 // entry.in_toolbar. 350 if (found_match && 351 ((import_to_bookmark_bar && entry.in_toolbar && parent != 352 model->GetBookmarkBarNode()) || 353 ((!import_to_bookmark_bar || !entry.in_toolbar) && 354 parent != model->other_node()))) { 355 found_match = false; 356 } 357 358 if (found_match) 359 return true; // Found a match with the same url path and title. 360 } 361 return false; 362 } 363