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/importer/profile_writer.h" 6 7 #include <map> 8 #include <set> 9 #include <string> 10 11 #include "base/prefs/pref_service.h" 12 #include "base/strings/string_number_conversions.h" 13 #include "base/strings/stringprintf.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "base/threading/thread.h" 16 #include "chrome/browser/bookmarks/bookmark_model_factory.h" 17 #include "chrome/browser/chrome_notification_types.h" 18 #include "chrome/browser/favicon/favicon_service.h" 19 #include "chrome/browser/favicon/favicon_service_factory.h" 20 #include "chrome/browser/history/history_service.h" 21 #include "chrome/browser/history/history_service_factory.h" 22 #include "chrome/browser/password_manager/password_store_factory.h" 23 #include "chrome/browser/profiles/profile.h" 24 #include "chrome/browser/search_engines/template_url_service_factory.h" 25 #include "chrome/browser/webdata/web_data_service_factory.h" 26 #include "chrome/common/importer/imported_bookmark_entry.h" 27 #include "chrome/common/importer/imported_favicon_usage.h" 28 #include "chrome/common/pref_names.h" 29 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" 30 #include "components/bookmarks/browser/bookmark_model.h" 31 #include "components/password_manager/core/browser/password_store.h" 32 #include "components/search_engines/template_url.h" 33 #include "components/search_engines/template_url_service.h" 34 35 #if defined(OS_WIN) 36 #include "chrome/browser/webdata/web_data_service_factory.h" 37 #include "components/password_manager/core/browser/webdata/password_web_data_service_win.h" 38 #endif 39 40 namespace { 41 42 // Generates a unique folder name. If |folder_name| is not unique, then this 43 // repeatedly tests for '|folder_name| + (i)' until a unique name is found. 44 base::string16 GenerateUniqueFolderName(BookmarkModel* model, 45 const base::string16& folder_name) { 46 // Build a set containing the bookmark bar folder names. 47 std::set<base::string16> existing_folder_names; 48 const BookmarkNode* bookmark_bar = model->bookmark_bar_node(); 49 for (int i = 0; i < bookmark_bar->child_count(); ++i) { 50 const BookmarkNode* node = bookmark_bar->GetChild(i); 51 if (node->is_folder()) 52 existing_folder_names.insert(node->GetTitle()); 53 } 54 55 // If the given name is unique, use it. 56 if (existing_folder_names.find(folder_name) == existing_folder_names.end()) 57 return folder_name; 58 59 // Otherwise iterate until we find a unique name. 60 for (size_t i = 1; i <= existing_folder_names.size(); ++i) { 61 base::string16 name = folder_name + base::ASCIIToUTF16(" (") + 62 base::IntToString16(i) + base::ASCIIToUTF16(")"); 63 if (existing_folder_names.find(name) == existing_folder_names.end()) 64 return name; 65 } 66 67 NOTREACHED(); 68 return folder_name; 69 } 70 71 // Shows the bookmarks toolbar. 72 void ShowBookmarkBar(Profile* profile) { 73 profile->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true); 74 } 75 76 } // namespace 77 78 ProfileWriter::ProfileWriter(Profile* profile) : profile_(profile) {} 79 80 bool ProfileWriter::BookmarkModelIsLoaded() const { 81 return BookmarkModelFactory::GetForProfile(profile_)->loaded(); 82 } 83 84 bool ProfileWriter::TemplateURLServiceIsLoaded() const { 85 return TemplateURLServiceFactory::GetForProfile(profile_)->loaded(); 86 } 87 88 void ProfileWriter::AddPasswordForm(const autofill::PasswordForm& form) { 89 PasswordStoreFactory::GetForProfile( 90 profile_, Profile::EXPLICIT_ACCESS)->AddLogin(form); 91 } 92 93 #if defined(OS_WIN) 94 void ProfileWriter::AddIE7PasswordInfo(const IE7PasswordInfo& info) { 95 WebDataServiceFactory::GetPasswordWebDataForProfile( 96 profile_, Profile::EXPLICIT_ACCESS)->AddIE7Login(info); 97 } 98 #endif 99 100 void ProfileWriter::AddHistoryPage(const history::URLRows& page, 101 history::VisitSource visit_source) { 102 HistoryServiceFactory::GetForProfile(profile_, Profile::EXPLICIT_ACCESS)-> 103 AddPagesWithDetails(page, visit_source); 104 } 105 106 void ProfileWriter::AddHomepage(const GURL& home_page) { 107 DCHECK(profile_); 108 109 PrefService* prefs = profile_->GetPrefs(); 110 // NOTE: We set the kHomePage value, but keep the NewTab page as the homepage. 111 const PrefService::Preference* pref = prefs->FindPreference(prefs::kHomePage); 112 if (pref && !pref->IsManaged()) { 113 prefs->SetString(prefs::kHomePage, home_page.spec()); 114 } 115 } 116 117 void ProfileWriter::AddBookmarks( 118 const std::vector<ImportedBookmarkEntry>& bookmarks, 119 const base::string16& top_level_folder_name) { 120 if (bookmarks.empty()) 121 return; 122 123 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile_); 124 DCHECK(model->loaded()); 125 126 // If the bookmark bar is currently empty, we should import directly to it. 127 // Otherwise, we should import everything to a subfolder. 128 const BookmarkNode* bookmark_bar = model->bookmark_bar_node(); 129 bool import_to_top_level = bookmark_bar->empty(); 130 131 // Reorder bookmarks so that the toolbar entries come first. 132 std::vector<ImportedBookmarkEntry> toolbar_bookmarks; 133 std::vector<ImportedBookmarkEntry> reordered_bookmarks; 134 for (std::vector<ImportedBookmarkEntry>::const_iterator it = 135 bookmarks.begin(); 136 it != bookmarks.end(); ++it) { 137 if (it->in_toolbar) 138 toolbar_bookmarks.push_back(*it); 139 else 140 reordered_bookmarks.push_back(*it); 141 } 142 reordered_bookmarks.insert(reordered_bookmarks.begin(), 143 toolbar_bookmarks.begin(), 144 toolbar_bookmarks.end()); 145 146 // If the user currently has no bookmarks in the bookmark bar, make sure that 147 // at least some of the imported bookmarks end up there. Otherwise, we'll end 148 // up with just a single folder containing the imported bookmarks, which makes 149 // for unnecessary nesting. 150 bool add_all_to_top_level = import_to_top_level && toolbar_bookmarks.empty(); 151 152 model->BeginExtensiveChanges(); 153 154 std::set<const BookmarkNode*> folders_added_to; 155 const BookmarkNode* top_level_folder = NULL; 156 for (std::vector<ImportedBookmarkEntry>::const_iterator bookmark = 157 reordered_bookmarks.begin(); 158 bookmark != reordered_bookmarks.end(); ++bookmark) { 159 // Disregard any bookmarks with invalid urls. 160 if (!bookmark->is_folder && !bookmark->url.is_valid()) 161 continue; 162 163 const BookmarkNode* parent = NULL; 164 if (import_to_top_level && (add_all_to_top_level || bookmark->in_toolbar)) { 165 // Add directly to the bookmarks bar. 166 parent = bookmark_bar; 167 } else { 168 // Add to a folder that will contain all the imported bookmarks not added 169 // to the bar. The first time we do so, create the folder. 170 if (!top_level_folder) { 171 base::string16 name = 172 GenerateUniqueFolderName(model,top_level_folder_name); 173 top_level_folder = model->AddFolder(bookmark_bar, 174 bookmark_bar->child_count(), 175 name); 176 } 177 parent = top_level_folder; 178 } 179 180 // Ensure any enclosing folders are present in the model. The bookmark's 181 // enclosing folder structure should be 182 // path[0] > path[1] > ... > path[size() - 1] 183 for (std::vector<base::string16>::const_iterator folder_name = 184 bookmark->path.begin(); 185 folder_name != bookmark->path.end(); ++folder_name) { 186 if (bookmark->in_toolbar && parent == bookmark_bar && 187 folder_name == bookmark->path.begin()) { 188 // If we're importing directly to the bookmarks bar, skip over the 189 // folder named "Bookmarks Toolbar" (or any non-Firefox equivalent). 190 continue; 191 } 192 193 const BookmarkNode* child = NULL; 194 for (int index = 0; index < parent->child_count(); ++index) { 195 const BookmarkNode* node = parent->GetChild(index); 196 if (node->is_folder() && node->GetTitle() == *folder_name) { 197 child = node; 198 break; 199 } 200 } 201 if (!child) 202 child = model->AddFolder(parent, parent->child_count(), *folder_name); 203 parent = child; 204 } 205 206 folders_added_to.insert(parent); 207 if (bookmark->is_folder) { 208 model->AddFolder(parent, parent->child_count(), bookmark->title); 209 } else { 210 model->AddURLWithCreationTimeAndMetaInfo(parent, 211 parent->child_count(), 212 bookmark->title, 213 bookmark->url, 214 bookmark->creation_time, 215 NULL); 216 } 217 } 218 219 // In order to keep the imported-to folders from appearing in the 'recently 220 // added to' combobox, reset their modified times. 221 for (std::set<const BookmarkNode*>::const_iterator i = 222 folders_added_to.begin(); 223 i != folders_added_to.end(); ++i) { 224 model->ResetDateFolderModified(*i); 225 } 226 227 model->EndExtensiveChanges(); 228 229 // If the user was previously using a toolbar, we should show the bar. 230 if (import_to_top_level && !add_all_to_top_level) 231 ShowBookmarkBar(profile_); 232 } 233 234 void ProfileWriter::AddFavicons( 235 const std::vector<ImportedFaviconUsage>& favicons) { 236 FaviconServiceFactory::GetForProfile(profile_, Profile::EXPLICIT_ACCESS)-> 237 SetImportedFavicons(favicons); 238 } 239 240 typedef std::map<std::string, TemplateURL*> HostPathMap; 241 242 // Returns the key for the map built by BuildHostPathMap. If url_string is not 243 // a valid URL, an empty string is returned, otherwise host+path is returned. 244 static std::string HostPathKeyForURL(const GURL& url) { 245 return url.is_valid() ? url.host() + url.path() : std::string(); 246 } 247 248 // Builds the key to use in HostPathMap for the specified TemplateURL. Returns 249 // an empty string if a host+path can't be generated for the TemplateURL. 250 // If an empty string is returned, the TemplateURL should not be added to 251 // HostPathMap. 252 // 253 // If |try_url_if_invalid| is true, and |t_url| isn't valid, a string is built 254 // from the raw TemplateURL string. Use a value of true for |try_url_if_invalid| 255 // when checking imported URLs as the imported URL may not be valid yet may 256 // match the host+path of one of the default URLs. This is used to catch the 257 // case of IE using an invalid OSDD URL for Live Search, yet the host+path 258 // matches our prepopulate data. IE's URL for Live Search is something like 259 // 'http://...{Language}...'. As {Language} is not a valid OSDD parameter value 260 // the TemplateURL is invalid. 261 static std::string BuildHostPathKey(const TemplateURL* t_url, 262 const SearchTermsData& search_terms_data, 263 bool try_url_if_invalid) { 264 if (try_url_if_invalid && !t_url->url_ref().IsValid(search_terms_data)) 265 return HostPathKeyForURL(GURL(t_url->url())); 266 267 if (t_url->url_ref().SupportsReplacement(search_terms_data)) { 268 return HostPathKeyForURL(GURL( 269 t_url->url_ref().ReplaceSearchTerms( 270 TemplateURLRef::SearchTermsArgs(base::ASCIIToUTF16("x")), 271 search_terms_data))); 272 } 273 return std::string(); 274 } 275 276 // Builds a set that contains an entry of the host+path for each TemplateURL in 277 // the TemplateURLService that has a valid search url. 278 static void BuildHostPathMap(TemplateURLService* model, 279 HostPathMap* host_path_map) { 280 TemplateURLService::TemplateURLVector template_urls = 281 model->GetTemplateURLs(); 282 for (size_t i = 0; i < template_urls.size(); ++i) { 283 const std::string host_path = BuildHostPathKey( 284 template_urls[i], model->search_terms_data(), false); 285 if (!host_path.empty()) { 286 const TemplateURL* existing_turl = (*host_path_map)[host_path]; 287 if (!existing_turl || 288 (template_urls[i]->show_in_default_list() && 289 !existing_turl->show_in_default_list())) { 290 // If there are multiple TemplateURLs with the same host+path, favor 291 // those shown in the default list. If there are multiple potential 292 // defaults, favor the first one, which should be the more commonly used 293 // one. 294 (*host_path_map)[host_path] = template_urls[i]; 295 } 296 } // else case, TemplateURL doesn't have a search url, doesn't support 297 // replacement, or doesn't have valid GURL. Ignore it. 298 } 299 } 300 301 void ProfileWriter::AddKeywords(ScopedVector<TemplateURL> template_urls, 302 bool unique_on_host_and_path) { 303 TemplateURLService* model = 304 TemplateURLServiceFactory::GetForProfile(profile_); 305 HostPathMap host_path_map; 306 if (unique_on_host_and_path) 307 BuildHostPathMap(model, &host_path_map); 308 309 for (ScopedVector<TemplateURL>::iterator i = template_urls.begin(); 310 i != template_urls.end(); ++i) { 311 // TemplateURLService requires keywords to be unique. If there is already a 312 // TemplateURL with this keyword, don't import it again. 313 if (model->GetTemplateURLForKeyword((*i)->keyword()) != NULL) 314 continue; 315 316 // For search engines if there is already a keyword with the same 317 // host+path, we don't import it. This is done to avoid both duplicate 318 // search providers (such as two Googles, or two Yahoos) as well as making 319 // sure the search engines we provide aren't replaced by those from the 320 // imported browser. 321 if (unique_on_host_and_path && 322 (host_path_map.find(BuildHostPathKey( 323 *i, model->search_terms_data(), true)) != host_path_map.end())) 324 continue; 325 326 // Only add valid TemplateURLs to the model. 327 if ((*i)->url_ref().IsValid(model->search_terms_data())) { 328 model->Add(*i); // Takes ownership. 329 *i = NULL; // Prevent the vector from deleting *i later. 330 } 331 } 332 } 333 334 void ProfileWriter::AddAutofillFormDataEntries( 335 const std::vector<autofill::AutofillEntry>& autofill_entries) { 336 scoped_refptr<autofill::AutofillWebDataService> web_data_service = 337 WebDataServiceFactory::GetAutofillWebDataForProfile( 338 profile_, Profile::EXPLICIT_ACCESS); 339 if (web_data_service.get()) 340 web_data_service->UpdateAutofillEntries(autofill_entries); 341 } 342 343 ProfileWriter::~ProfileWriter() {} 344