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