OLD | NEW |
| (Empty) |
1 // Copyright (c) 2006-2008 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.h" | |
6 | |
7 #include <map> | |
8 | |
9 #include "base/file_util.h" | |
10 #include "base/gfx/image_operations.h" | |
11 #include "base/gfx/png_encoder.h" | |
12 #include "base/string_util.h" | |
13 #include "chrome/browser/bookmarks/bookmark_model.h" | |
14 #include "chrome/browser/browser_process.h" | |
15 #include "chrome/browser/firefox2_importer.h" | |
16 #include "chrome/browser/firefox3_importer.h" | |
17 #include "chrome/browser/firefox_importer_utils.h" | |
18 #include "chrome/browser/firefox_profile_lock.h" | |
19 #include "chrome/browser/ie_importer.h" | |
20 #include "chrome/browser/template_url_model.h" | |
21 #include "chrome/browser/shell_integration.h" | |
22 #include "chrome/browser/webdata/web_data_service.h" | |
23 #include "chrome/common/gfx/favicon_size.h" | |
24 #include "chrome/common/l10n_util.h" | |
25 #include "chrome/common/pref_names.h" | |
26 #include "chrome/common/pref_service.h" | |
27 #include "chrome/views/window.h" | |
28 #include "webkit/glue/image_decoder.h" | |
29 | |
30 #include "generated_resources.h" | |
31 | |
32 // ProfileWriter. | |
33 | |
34 bool ProfileWriter::BookmarkModelIsLoaded() const { | |
35 return profile_->GetBookmarkModel()->IsLoaded(); | |
36 } | |
37 | |
38 void ProfileWriter::AddBookmarkModelObserver(BookmarkModelObserver* observer) { | |
39 profile_->GetBookmarkModel()->AddObserver(observer); | |
40 } | |
41 | |
42 bool ProfileWriter::TemplateURLModelIsLoaded() const { | |
43 return profile_->GetTemplateURLModel()->loaded(); | |
44 } | |
45 | |
46 void ProfileWriter::AddTemplateURLModelObserver( | |
47 NotificationObserver* observer) { | |
48 TemplateURLModel* model = profile_->GetTemplateURLModel(); | |
49 NotificationService::current()->AddObserver( | |
50 observer, TEMPLATE_URL_MODEL_LOADED, | |
51 Source<TemplateURLModel>(model)); | |
52 model->Load(); | |
53 } | |
54 | |
55 void ProfileWriter::AddPasswordForm(const PasswordForm& form) { | |
56 profile_->GetWebDataService(Profile::EXPLICIT_ACCESS)->AddLogin(form); | |
57 } | |
58 | |
59 void ProfileWriter::AddIE7PasswordInfo(const IE7PasswordInfo& info) { | |
60 profile_->GetWebDataService(Profile::EXPLICIT_ACCESS)->AddIE7Login(info); | |
61 } | |
62 | |
63 void ProfileWriter::AddHistoryPage(const std::vector<history::URLRow>& page) { | |
64 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)-> | |
65 AddPagesWithDetails(page); | |
66 } | |
67 | |
68 void ProfileWriter::AddHomepage(const GURL& home_page) { | |
69 DCHECK(profile_); | |
70 | |
71 PrefService* prefs = profile_->GetPrefs(); | |
72 // NOTE: We set the kHomePage value, but keep the NewTab page as the homepage. | |
73 prefs->SetString(prefs::kHomePage, ASCIIToWide(home_page.spec())); | |
74 prefs->ScheduleSavePersistentPrefs(g_browser_process->file_thread()); | |
75 } | |
76 | |
77 void ProfileWriter::AddBookmarkEntry( | |
78 const std::vector<BookmarkEntry>& bookmark) { | |
79 BookmarkModel* model = profile_->GetBookmarkModel(); | |
80 DCHECK(model->IsLoaded()); | |
81 | |
82 bool show_bookmark_toolbar = false; | |
83 std::set<BookmarkNode*> groups_added_to; | |
84 for (std::vector<BookmarkEntry>::const_iterator it = bookmark.begin(); | |
85 it != bookmark.end(); ++it) { | |
86 // Don't insert this url if it exists in model or url is not valid. | |
87 if (model->GetNodeByURL(it->url) != NULL || !it->url.is_valid()) | |
88 continue; | |
89 | |
90 // Set up groups in BookmarkModel in such a way that path[i] is | |
91 // the subgroup of path[i-1]. Finally they construct a path in the | |
92 // model: | |
93 // path[0] \ path[1] \ ... \ path[size() - 1] | |
94 BookmarkNode* parent = | |
95 (it->in_toolbar ? model->GetBookmarkBarNode() : model->other_node()); | |
96 for (std::vector<std::wstring>::const_iterator i = it->path.begin(); | |
97 i != it->path.end(); ++i) { | |
98 BookmarkNode* child = NULL; | |
99 for (int index = 0; index < parent->GetChildCount(); ++index) { | |
100 BookmarkNode* node = parent->GetChild(index); | |
101 if ((node->GetType() == history::StarredEntry::BOOKMARK_BAR || | |
102 node->GetType() == history::StarredEntry::USER_GROUP) && | |
103 node->GetTitle() == *i) { | |
104 child = node; | |
105 break; | |
106 } | |
107 } | |
108 if (child == NULL) | |
109 child = model->AddGroup(parent, parent->GetChildCount(), *i); | |
110 parent = child; | |
111 } | |
112 groups_added_to.insert(parent); | |
113 model->AddURLWithCreationTime(parent, parent->GetChildCount(), | |
114 it->title, it->url, it->creation_time); | |
115 | |
116 // If some items are put into toolbar, it looks like the user was using | |
117 // it in their last browser. We turn on the bookmarks toolbar. | |
118 if (it->in_toolbar) | |
119 show_bookmark_toolbar = true; | |
120 } | |
121 | |
122 // Reset the date modified time of the groups we added to. We do this to | |
123 // make sure the 'recently added to' combobox in the bubble doesn't get random | |
124 // groups. | |
125 for (std::set<BookmarkNode*>::const_iterator i = groups_added_to.begin(); | |
126 i != groups_added_to.end(); ++i) { | |
127 model->ResetDateGroupModified(*i); | |
128 } | |
129 | |
130 if (show_bookmark_toolbar) | |
131 ShowBookmarkBar(); | |
132 } | |
133 | |
134 void ProfileWriter::AddFavicons( | |
135 const std::vector<history::ImportedFavIconUsage>& favicons) { | |
136 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)-> | |
137 SetImportedFavicons(favicons); | |
138 } | |
139 | |
140 typedef std::map<std::string, const TemplateURL*> HostPathMap; | |
141 | |
142 // Builds the key to use in HostPathMap for the specified TemplateURL. Returns | |
143 // an empty string if a host+path can't be generated for the TemplateURL. | |
144 // If an empty string is returned, it should not be added to HostPathMap. | |
145 static std::string BuildHostPathKey(const TemplateURL* t_url) { | |
146 if (t_url->url() && t_url->url()->SupportsReplacement()) { | |
147 GURL search_url(t_url->url()->ReplaceSearchTerms( | |
148 *t_url, L"random string", TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, | |
149 std::wstring())); | |
150 if (search_url.is_valid()) | |
151 return search_url.host() + search_url.path(); | |
152 } | |
153 return std::string(); | |
154 } | |
155 | |
156 // Builds a set that contains an entry of the host+path for each TemplateURL in | |
157 // the TemplateURLModel that has a valid search url. | |
158 static void BuildHostPathMap(const TemplateURLModel& model, | |
159 HostPathMap* host_path_map) { | |
160 std::vector<const TemplateURL*> template_urls = model.GetTemplateURLs(); | |
161 for (size_t i = 0; i < template_urls.size(); ++i) { | |
162 const std::string host_path = BuildHostPathKey(template_urls[i]); | |
163 if (!host_path.empty()) { | |
164 const TemplateURL* existing_turl = (*host_path_map)[host_path]; | |
165 if (!existing_turl || | |
166 (template_urls[i]->show_in_default_list() && | |
167 !existing_turl->show_in_default_list())) { | |
168 // If there are multiple TemplateURLs with the same host+path, favor | |
169 // those shown in the default list. If there are multiple potential | |
170 // defaults, favor the first one, which should be the more commonly used | |
171 // one. | |
172 (*host_path_map)[host_path] = template_urls[i]; | |
173 } | |
174 } // else case, TemplateURL doesn't have a search url, doesn't support | |
175 // replacement, or doesn't have valid GURL. Ignore it. | |
176 } | |
177 } | |
178 | |
179 void ProfileWriter::AddKeywords(const std::vector<TemplateURL*>& template_urls, | |
180 int default_keyword_index, | |
181 bool unique_on_host_and_path) { | |
182 TemplateURLModel* model = profile_->GetTemplateURLModel(); | |
183 HostPathMap host_path_map; | |
184 if (unique_on_host_and_path) | |
185 BuildHostPathMap(*model, &host_path_map); | |
186 | |
187 for (std::vector<TemplateURL*>::const_iterator i = template_urls.begin(); | |
188 i != template_urls.end(); ++i) { | |
189 TemplateURL* t_url = *i; | |
190 bool default_keyword = | |
191 default_keyword_index >= 0 && | |
192 (i - template_urls.begin() == default_keyword_index); | |
193 | |
194 // TemplateURLModel requires keywords to be unique. If there is already a | |
195 // TemplateURL with this keyword, don't import it again. | |
196 const TemplateURL* turl_with_keyword = | |
197 model->GetTemplateURLForKeyword(t_url->keyword()); | |
198 if (turl_with_keyword != NULL) { | |
199 if (default_keyword) | |
200 model->SetDefaultSearchProvider(turl_with_keyword); | |
201 delete t_url; | |
202 continue; | |
203 } | |
204 | |
205 // For search engines if there is already a keyword with the same | |
206 // host+path, we don't import it. This is done to avoid both duplicate | |
207 // search providers (such as two Googles, or two Yahoos) as well as making | |
208 // sure the search engines we provide aren't replaced by those from the | |
209 // imported browser. | |
210 if (unique_on_host_and_path && | |
211 host_path_map.find(BuildHostPathKey(t_url)) != host_path_map.end()) { | |
212 if (default_keyword) { | |
213 const TemplateURL* turl_with_host_path = | |
214 host_path_map[BuildHostPathKey(t_url)]; | |
215 if (turl_with_host_path) | |
216 model->SetDefaultSearchProvider(turl_with_host_path); | |
217 else | |
218 NOTREACHED(); // BuildHostPathMap should only insert non-null values. | |
219 } | |
220 delete t_url; | |
221 continue; | |
222 } | |
223 model->Add(t_url); | |
224 if (default_keyword) | |
225 model->SetDefaultSearchProvider(t_url); | |
226 } | |
227 } | |
228 | |
229 void ProfileWriter::ShowBookmarkBar() { | |
230 DCHECK(profile_); | |
231 | |
232 PrefService* prefs = profile_->GetPrefs(); | |
233 // Check whether the bookmark bar is shown in current pref. | |
234 if (!prefs->GetBoolean(prefs::kShowBookmarkBar)) { | |
235 // Set the pref and notify the notification service. | |
236 prefs->SetBoolean(prefs::kShowBookmarkBar, true); | |
237 prefs->ScheduleSavePersistentPrefs(g_browser_process->file_thread()); | |
238 Source<Profile> source(profile_); | |
239 NotificationService::current()->Notify( | |
240 NOTIFY_BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, source, | |
241 NotificationService::NoDetails()); | |
242 } | |
243 } | |
244 | |
245 // Importer. | |
246 | |
247 // static | |
248 bool Importer::ReencodeFavicon(const unsigned char* src_data, size_t src_len, | |
249 std::vector<unsigned char>* png_data) { | |
250 // Decode the favicon using WebKit's image decoder. | |
251 webkit_glue::ImageDecoder decoder(gfx::Size(kFavIconSize, kFavIconSize)); | |
252 SkBitmap decoded = decoder.Decode(src_data, src_len); | |
253 if (decoded.empty()) | |
254 return false; // Unable to decode. | |
255 | |
256 if (decoded.width() != kFavIconSize || decoded.height() != kFavIconSize) { | |
257 // The bitmap is not the correct size, re-sample. | |
258 int new_width = decoded.width(); | |
259 int new_height = decoded.height(); | |
260 calc_favicon_target_size(&new_width, &new_height); | |
261 decoded = gfx::ImageOperations::Resize( | |
262 decoded, gfx::ImageOperations::RESIZE_LANCZOS3, | |
263 gfx::Size(new_width, new_height)); | |
264 } | |
265 | |
266 // Encode our bitmap as a PNG. | |
267 SkAutoLockPixels decoded_lock(decoded); | |
268 PNGEncoder::Encode(reinterpret_cast<unsigned char*>(decoded.getPixels()), | |
269 PNGEncoder::FORMAT_BGRA, decoded.width(), | |
270 decoded.height(), decoded.width() * 4, false, png_data); | |
271 return true; | |
272 } | |
273 | |
274 // ImporterHost. | |
275 | |
276 ImporterHost::ImporterHost() | |
277 : observer_(NULL), | |
278 task_(NULL), | |
279 importer_(NULL), | |
280 file_loop_(g_browser_process->file_thread()->message_loop()), | |
281 waiting_for_bookmarkbar_model_(false), | |
282 waiting_for_template_url_model_(false), | |
283 is_source_readable_(true) { | |
284 DetectSourceProfiles(); | |
285 } | |
286 | |
287 ImporterHost::ImporterHost(MessageLoop* file_loop) | |
288 : observer_(NULL), | |
289 task_(NULL), | |
290 importer_(NULL), | |
291 file_loop_(file_loop), | |
292 waiting_for_bookmarkbar_model_(false), | |
293 waiting_for_template_url_model_(false), | |
294 is_source_readable_(true) { | |
295 DetectSourceProfiles(); | |
296 } | |
297 | |
298 ImporterHost::~ImporterHost() { | |
299 STLDeleteContainerPointers(source_profiles_.begin(), source_profiles_.end()); | |
300 } | |
301 | |
302 void ImporterHost::Loaded(BookmarkModel* model) { | |
303 model->RemoveObserver(this); | |
304 waiting_for_bookmarkbar_model_ = false; | |
305 InvokeTaskIfDone(); | |
306 } | |
307 | |
308 void ImporterHost::Observe(NotificationType type, | |
309 const NotificationSource& source, | |
310 const NotificationDetails& details) { | |
311 DCHECK(type == TEMPLATE_URL_MODEL_LOADED); | |
312 TemplateURLModel* model = Source<TemplateURLModel>(source).ptr(); | |
313 NotificationService::current()->RemoveObserver( | |
314 this, TEMPLATE_URL_MODEL_LOADED, | |
315 Source<TemplateURLModel>(model)); | |
316 waiting_for_template_url_model_ = false; | |
317 InvokeTaskIfDone(); | |
318 } | |
319 | |
320 void ImporterHost::ShowWarningDialog() { | |
321 ChromeViews::Window::CreateChromeWindow(GetActiveWindow(), gfx::Rect(), | |
322 new ImporterLockView(this))->Show(); | |
323 } | |
324 | |
325 void ImporterHost::OnLockViewEnd(bool is_continue) { | |
326 if (is_continue) { | |
327 // User chose to continue, then we check the lock again to make | |
328 // sure that Firefox has been closed. Try to import the settings | |
329 // if successful. Otherwise, show a warning dialog. | |
330 firefox_lock_->Lock(); | |
331 if (firefox_lock_->HasAcquired()) { | |
332 is_source_readable_ = true; | |
333 InvokeTaskIfDone(); | |
334 } else { | |
335 ShowWarningDialog(); | |
336 } | |
337 } else { | |
338 // User chose to skip the import process. We should delete | |
339 // the task and notify the ImporterHost to finish. | |
340 delete task_; | |
341 task_ = NULL; | |
342 importer_ = NULL; | |
343 ImportEnded(); | |
344 } | |
345 } | |
346 | |
347 void ImporterHost::StartImportSettings(const ProfileInfo& profile_info, | |
348 uint16 items, | |
349 ProfileWriter* writer, | |
350 bool first_run) { | |
351 // Preserves the observer and creates a task, since we do async import | |
352 // so that it doesn't block the UI. When the import is complete, observer | |
353 // will be notified. | |
354 writer_ = writer; | |
355 importer_ = CreateImporterByType(profile_info.browser_type); | |
356 importer_->set_first_run(first_run); | |
357 task_ = NewRunnableMethod(importer_, &Importer::StartImport, | |
358 profile_info, items, writer_.get(), this); | |
359 | |
360 // We should lock the Firefox profile directory to prevent corruption. | |
361 if (profile_info.browser_type == FIREFOX2 || | |
362 profile_info.browser_type == FIREFOX3) { | |
363 firefox_lock_.reset(new FirefoxProfileLock(profile_info.source_path)); | |
364 if (!firefox_lock_->HasAcquired()) { | |
365 // If fail to acquire the lock, we set the source unreadable and | |
366 // show a warning dialog. | |
367 is_source_readable_ = false; | |
368 ShowWarningDialog(); | |
369 } | |
370 } | |
371 | |
372 // BookmarkModel should be loaded before adding IE favorites. So we observe | |
373 // the BookmarkModel if needed, and start the task after it has been loaded. | |
374 if ((items & FAVORITES) && !writer_->BookmarkModelIsLoaded()) { | |
375 writer_->AddBookmarkModelObserver(this); | |
376 waiting_for_bookmarkbar_model_ = true; | |
377 } | |
378 | |
379 // Observes the TemplateURLModel if needed to import search engines from the | |
380 // other browser. We also check to see if we're importing bookmarks because | |
381 // we can import bookmark keywords from Firefox as search engines. | |
382 if ((items & SEARCH_ENGINES) || (items & FAVORITES)) { | |
383 if (!writer_->TemplateURLModelIsLoaded()) { | |
384 writer_->AddTemplateURLModelObserver(this); | |
385 waiting_for_template_url_model_ = true; | |
386 } | |
387 } | |
388 | |
389 AddRef(); | |
390 InvokeTaskIfDone(); | |
391 } | |
392 | |
393 void ImporterHost::Cancel() { | |
394 if (importer_) | |
395 importer_->Cancel(); | |
396 } | |
397 | |
398 void ImporterHost::SetObserver(Observer* observer) { | |
399 observer_ = observer; | |
400 } | |
401 | |
402 void ImporterHost::InvokeTaskIfDone() { | |
403 if (waiting_for_bookmarkbar_model_ || waiting_for_template_url_model_ || | |
404 !is_source_readable_) | |
405 return; | |
406 file_loop_->PostTask(FROM_HERE, task_); | |
407 } | |
408 | |
409 void ImporterHost::ImportItemStarted(ImportItem item) { | |
410 if (observer_) | |
411 observer_->ImportItemStarted(item); | |
412 } | |
413 | |
414 void ImporterHost::ImportItemEnded(ImportItem item) { | |
415 if (observer_) | |
416 observer_->ImportItemEnded(item); | |
417 } | |
418 | |
419 void ImporterHost::ImportStarted() { | |
420 if (observer_) | |
421 observer_->ImportStarted(); | |
422 } | |
423 | |
424 void ImporterHost::ImportEnded() { | |
425 firefox_lock_.reset(); // Release the Firefox profile lock. | |
426 if (observer_) | |
427 observer_->ImportEnded(); | |
428 Release(); | |
429 } | |
430 | |
431 Importer* ImporterHost::CreateImporterByType(ProfileType type) { | |
432 switch (type) { | |
433 case MS_IE: | |
434 return new IEImporter(); | |
435 case FIREFOX2: | |
436 return new Firefox2Importer(); | |
437 case FIREFOX3: | |
438 return new Firefox3Importer(); | |
439 } | |
440 NOTREACHED(); | |
441 return NULL; | |
442 } | |
443 | |
444 int ImporterHost::GetAvailableProfileCount() { | |
445 return static_cast<int>(source_profiles_.size()); | |
446 } | |
447 | |
448 std::wstring ImporterHost::GetSourceProfileNameAt(int index) const { | |
449 DCHECK(index < static_cast<int>(source_profiles_.size())); | |
450 return source_profiles_[index]->description; | |
451 } | |
452 | |
453 const ProfileInfo& ImporterHost::GetSourceProfileInfoAt(int index) const { | |
454 DCHECK(index < static_cast<int>(source_profiles_.size())); | |
455 return *source_profiles_[index]; | |
456 } | |
457 | |
458 void ImporterHost::DetectSourceProfiles() { | |
459 if (ShellIntegration::IsFirefoxDefaultBrowser()) { | |
460 DetectFirefoxProfiles(); | |
461 DetectIEProfiles(); | |
462 } else { | |
463 DetectIEProfiles(); | |
464 DetectFirefoxProfiles(); | |
465 } | |
466 } | |
467 | |
468 void ImporterHost::DetectIEProfiles() { | |
469 // IE always exists and don't have multiple profiles. | |
470 ProfileInfo* ie = new ProfileInfo(); | |
471 ie->description = l10n_util::GetString(IDS_IMPORT_FROM_IE); | |
472 ie->browser_type = MS_IE; | |
473 ie->source_path.clear(); | |
474 ie->app_path.clear(); | |
475 source_profiles_.push_back(ie); | |
476 } | |
477 | |
478 void ImporterHost::DetectFirefoxProfiles() { | |
479 // Detects which version of Firefox is installed. | |
480 int version = GetCurrentFirefoxMajorVersion(); | |
481 ProfileType firefox_type; | |
482 if (version == 2) { | |
483 firefox_type = FIREFOX2; | |
484 } else if (version == 3) { | |
485 firefox_type = FIREFOX3; | |
486 } else { | |
487 // Ignores other versions of firefox. | |
488 return; | |
489 } | |
490 | |
491 std::wstring ini_file = GetProfilesINI(); | |
492 DictionaryValue root; | |
493 ParseProfileINI(ini_file, &root); | |
494 | |
495 std::wstring source_path; | |
496 for (int i = 0; ; ++i) { | |
497 std::wstring current_profile = L"Profile" + IntToWString(i); | |
498 if (!root.HasKey(current_profile)) { | |
499 // Profiles are continuously numbered. So we exit when we can't | |
500 // find the i-th one. | |
501 break; | |
502 } | |
503 std::wstring is_relative, path, profile_path; | |
504 if (root.GetString(current_profile + L".IsRelative", &is_relative) && | |
505 root.GetString(current_profile + L".Path", &path)) { | |
506 ReplaceSubstringsAfterOffset(&path, 0, L"/", L"\\"); | |
507 | |
508 // IsRelative=1 means the folder path would be relative to the | |
509 // path of profiles.ini. IsRelative=0 refers to a custom profile | |
510 // location. | |
511 if (is_relative == L"1") { | |
512 profile_path = file_util::GetDirectoryFromPath(ini_file); | |
513 file_util::AppendToPath(&profile_path, path); | |
514 } else { | |
515 profile_path = path; | |
516 } | |
517 | |
518 // We only import the default profile when multiple profiles exist, | |
519 // since the other profiles are used mostly by developers for testing. | |
520 // Otherwise, Profile0 will be imported. | |
521 std::wstring is_default; | |
522 if ((root.GetString(current_profile + L".Default", &is_default) && | |
523 is_default == L"1") || i == 0) { | |
524 source_path = profile_path; | |
525 // We break out of the loop when we have found the default profile. | |
526 if (is_default == L"1") | |
527 break; | |
528 } | |
529 } | |
530 } | |
531 | |
532 if (!source_path.empty()) { | |
533 ProfileInfo* firefox = new ProfileInfo(); | |
534 firefox->description = l10n_util::GetString(IDS_IMPORT_FROM_FIREFOX); | |
535 firefox->browser_type = firefox_type; | |
536 firefox->source_path = source_path; | |
537 firefox->app_path = GetFirefoxInstallPath(); | |
538 source_profiles_.push_back(firefox); | |
539 } | |
540 } | |
541 | |
OLD | NEW |