importer.cc revision 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7
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/importer/importer.h" 6 7#include "app/l10n_util.h" 8#include "base/thread.h" 9#include "base/values.h" 10#include "chrome/browser/bookmarks/bookmark_model.h" 11#include "chrome/browser/browser_list.h" 12#include "chrome/browser/browser_process.h" 13#include "chrome/browser/browser_thread.h" 14#include "chrome/browser/browsing_instance.h" 15#include "chrome/browser/importer/firefox_profile_lock.h" 16#include "chrome/browser/importer/importer_bridge.h" 17#include "chrome/browser/renderer_host/site_instance.h" 18#include "chrome/browser/search_engines/template_url.h" 19#include "chrome/browser/search_engines/template_url_model.h" 20#include "chrome/browser/tabs/tab_strip_model.h" 21#include "chrome/browser/ui/browser_navigator.h" 22#include "chrome/browser/webdata/web_data_service.h" 23#include "chrome/common/notification_service.h" 24#include "gfx/codec/png_codec.h" 25#include "gfx/favicon_size.h" 26#include "grit/generated_resources.h" 27#include "skia/ext/image_operations.h" 28#include "webkit/glue/image_decoder.h" 29 30// TODO(port): Port these files. 31#if defined(OS_WIN) 32#include "app/win_util.h" 33#include "chrome/browser/views/importer_lock_view.h" 34#include "views/window/window.h" 35#elif defined(OS_MACOSX) 36#include "chrome/browser/cocoa/importer_lock_dialog.h" 37#elif defined(TOOLKIT_USES_GTK) 38#include "chrome/browser/gtk/import_lock_dialog_gtk.h" 39#endif 40 41using webkit_glue::PasswordForm; 42 43// Importer. 44 45Importer::Importer() 46 : cancelled_(false), 47 import_to_bookmark_bar_(false), 48 bookmark_bar_disabled_(false) { 49} 50 51Importer::~Importer() { 52} 53 54// static 55bool Importer::ReencodeFavicon(const unsigned char* src_data, size_t src_len, 56 std::vector<unsigned char>* png_data) { 57 // Decode the favicon using WebKit's image decoder. 58 webkit_glue::ImageDecoder decoder(gfx::Size(kFavIconSize, kFavIconSize)); 59 SkBitmap decoded = decoder.Decode(src_data, src_len); 60 if (decoded.empty()) 61 return false; // Unable to decode. 62 63 if (decoded.width() != kFavIconSize || decoded.height() != kFavIconSize) { 64 // The bitmap is not the correct size, re-sample. 65 int new_width = decoded.width(); 66 int new_height = decoded.height(); 67 calc_favicon_target_size(&new_width, &new_height); 68 decoded = skia::ImageOperations::Resize( 69 decoded, skia::ImageOperations::RESIZE_LANCZOS3, new_width, new_height); 70 } 71 72 // Encode our bitmap as a PNG. 73 gfx::PNGCodec::EncodeBGRASkBitmap(decoded, false, png_data); 74 return true; 75} 76 77// ImporterHost. 78 79ImporterHost::ImporterHost() 80 : profile_(NULL), 81 observer_(NULL), 82 task_(NULL), 83 importer_(NULL), 84 waiting_for_bookmarkbar_model_(false), 85 installed_bookmark_observer_(false), 86 is_source_readable_(true), 87 headless_(false), 88 parent_window_(NULL) { 89 importer_list_.DetectSourceProfiles(); 90} 91 92ImporterHost::~ImporterHost() { 93 if (NULL != importer_) 94 importer_->Release(); 95 if (installed_bookmark_observer_) { 96 DCHECK(profile_); // Only way for waiting_for_bookmarkbar_model_ to be true 97 // is if we have a profile. 98 profile_->GetBookmarkModel()->RemoveObserver(this); 99 } 100} 101 102void ImporterHost::Loaded(BookmarkModel* model) { 103 DCHECK(model->IsLoaded()); 104 model->RemoveObserver(this); 105 waiting_for_bookmarkbar_model_ = false; 106 installed_bookmark_observer_ = false; 107 108 importer_->set_import_to_bookmark_bar(!model->HasBookmarks()); 109 InvokeTaskIfDone(); 110} 111 112void ImporterHost::BookmarkModelBeingDeleted(BookmarkModel* model) { 113 installed_bookmark_observer_ = false; 114} 115 116void ImporterHost::Observe(NotificationType type, 117 const NotificationSource& source, 118 const NotificationDetails& details) { 119 DCHECK(type == NotificationType::TEMPLATE_URL_MODEL_LOADED); 120 registrar_.RemoveAll(); 121 InvokeTaskIfDone(); 122} 123 124void ImporterHost::ShowWarningDialog() { 125 if (headless_) { 126 OnLockViewEnd(false); 127 } else { 128#if defined(OS_WIN) 129 views::Window::CreateChromeWindow(NULL, gfx::Rect(), 130 new ImporterLockView(this))->Show(); 131#elif defined(TOOLKIT_USES_GTK) 132 ImportLockDialogGtk::Show(parent_window_, this); 133#else 134 ImportLockDialogCocoa::ShowWarning(this); 135#endif 136 } 137} 138 139void ImporterHost::OnLockViewEnd(bool is_continue) { 140 if (is_continue) { 141 // User chose to continue, then we check the lock again to make 142 // sure that Firefox has been closed. Try to import the settings 143 // if successful. Otherwise, show a warning dialog. 144 firefox_lock_->Lock(); 145 if (firefox_lock_->HasAcquired()) { 146 is_source_readable_ = true; 147 InvokeTaskIfDone(); 148 } else { 149 ShowWarningDialog(); 150 } 151 } else { 152 // User chose to skip the import process. We should delete 153 // the task and notify the ImporterHost to finish. 154 delete task_; 155 task_ = NULL; 156 importer_ = NULL; 157 ImportEnded(); 158 } 159} 160 161void ImporterHost::StartImportSettings( 162 const importer::ProfileInfo& profile_info, 163 Profile* target_profile, 164 uint16 items, 165 ProfileWriter* writer, 166 bool first_run) { 167 DCHECK(!profile_); // We really only support importing from one host at a 168 // time. 169 profile_ = target_profile; 170 // Preserves the observer and creates a task, since we do async import 171 // so that it doesn't block the UI. When the import is complete, observer 172 // will be notified. 173 writer_ = writer; 174 importer_ = importer_list_.CreateImporterByType(profile_info.browser_type); 175 // If we fail to create Importer, exit as we cannot do anything. 176 if (!importer_) { 177 ImportEnded(); 178 return; 179 } 180 181 importer_->AddRef(); 182 183 importer_->set_import_to_bookmark_bar(ShouldImportToBookmarkBar(first_run)); 184 importer_->set_bookmark_bar_disabled(first_run); 185 scoped_refptr<ImporterBridge> bridge( 186 new InProcessImporterBridge(writer_.get(), this)); 187 task_ = NewRunnableMethod(importer_, &Importer::StartImport, 188 profile_info, items, bridge); 189 190 CheckForFirefoxLock(profile_info, items, first_run); 191 192#if defined(OS_WIN) 193 // For google toolbar import, we need the user to log in and store their GAIA 194 // credentials. 195 if (profile_info.browser_type == importer::GOOGLE_TOOLBAR5) { 196 if (!toolbar_importer_utils::IsGoogleGAIACookieInstalled()) { 197 win_util::MessageBox( 198 NULL, 199 l10n_util::GetString(IDS_IMPORTER_GOOGLE_LOGIN_TEXT).c_str(), 200 L"", 201 MB_OK | MB_TOPMOST); 202 203 GURL url("https://www.google.com/accounts/ServiceLogin"); 204 BrowserList::GetLastActive()->AddSelectedTabWithURL( 205 url, PageTransition::TYPED); 206 207 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( 208 this, &ImporterHost::OnLockViewEnd, false)); 209 210 is_source_readable_ = false; 211 } 212 } 213#endif 214 215 CheckForLoadedModels(items); 216 AddRef(); 217 InvokeTaskIfDone(); 218} 219 220void ImporterHost::Cancel() { 221 if (importer_) 222 importer_->Cancel(); 223} 224 225void ImporterHost::SetObserver(Observer* observer) { 226 observer_ = observer; 227} 228 229void ImporterHost::InvokeTaskIfDone() { 230 if (waiting_for_bookmarkbar_model_ || !registrar_.IsEmpty() || 231 !is_source_readable_) 232 return; 233 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, task_); 234} 235 236void ImporterHost::ImportItemStarted(importer::ImportItem item) { 237 if (observer_) 238 observer_->ImportItemStarted(item); 239} 240 241void ImporterHost::ImportItemEnded(importer::ImportItem item) { 242 if (observer_) 243 observer_->ImportItemEnded(item); 244} 245 246void ImporterHost::ImportStarted() { 247 if (observer_) 248 observer_->ImportStarted(); 249} 250 251void ImporterHost::ImportEnded() { 252 firefox_lock_.reset(); // Release the Firefox profile lock. 253 if (observer_) 254 observer_->ImportEnded(); 255 Release(); 256} 257 258bool ImporterHost::ShouldImportToBookmarkBar(bool first_run) { 259 bool import_to_bookmark_bar = first_run; 260 if (profile_ && profile_->GetBookmarkModel()->IsLoaded()) { 261 import_to_bookmark_bar = (!profile_->GetBookmarkModel()->HasBookmarks()); 262 } 263 return import_to_bookmark_bar; 264} 265 266void ImporterHost::CheckForFirefoxLock( 267 const importer::ProfileInfo& profile_info, uint16 items, bool first_run) { 268 if (profile_info.browser_type == importer::FIREFOX2 || 269 profile_info.browser_type == importer::FIREFOX3) { 270 DCHECK(!firefox_lock_.get()); 271 firefox_lock_.reset(new FirefoxProfileLock(profile_info.source_path)); 272 if (!firefox_lock_->HasAcquired()) { 273 // If fail to acquire the lock, we set the source unreadable and 274 // show a warning dialog, unless running without UI. 275 is_source_readable_ = false; 276 ShowWarningDialog(); 277 } 278 } 279} 280 281void ImporterHost::CheckForLoadedModels(uint16 items) { 282 // BookmarkModel should be loaded before adding IE favorites. So we observe 283 // the BookmarkModel if needed, and start the task after it has been loaded. 284 if ((items & importer::FAVORITES) && !writer_->BookmarkModelIsLoaded()) { 285 profile_->GetBookmarkModel()->AddObserver(this); 286 waiting_for_bookmarkbar_model_ = true; 287 installed_bookmark_observer_ = true; 288 } 289 290 // Observes the TemplateURLModel if needed to import search engines from the 291 // other browser. We also check to see if we're importing bookmarks because 292 // we can import bookmark keywords from Firefox as search engines. 293 if ((items & importer::SEARCH_ENGINES) || (items & importer::FAVORITES)) { 294 if (!writer_->TemplateURLModelIsLoaded()) { 295 TemplateURLModel* model = profile_->GetTemplateURLModel(); 296 registrar_.Add(this, NotificationType::TEMPLATE_URL_MODEL_LOADED, 297 Source<TemplateURLModel>(model)); 298 model->Load(); 299 } 300 } 301} 302 303ExternalProcessImporterHost::ExternalProcessImporterHost() 304 : items_(0), 305 import_to_bookmark_bar_(false), 306 cancelled_(false), 307 import_process_launched_(false) { 308} 309 310void ExternalProcessImporterHost::Loaded(BookmarkModel* model) { 311 DCHECK(model->IsLoaded()); 312 model->RemoveObserver(this); 313 waiting_for_bookmarkbar_model_ = false; 314 installed_bookmark_observer_ = false; 315 316 // Because the import process is running externally, the decision whether 317 // to import to the bookmark bar must be stored here so that it can be 318 // passed to the importer when the import task is invoked. 319 import_to_bookmark_bar_ = (!model->HasBookmarks()); 320 InvokeTaskIfDone(); 321} 322 323void ExternalProcessImporterHost::Cancel() { 324 cancelled_ = true; 325 if (import_process_launched_) 326 client_->Cancel(); 327 ImportEnded(); // Tells the observer that we're done, and releases us. 328} 329 330void ExternalProcessImporterHost::StartImportSettings( 331 const importer::ProfileInfo& profile_info, 332 Profile* target_profile, 333 uint16 items, 334 ProfileWriter* writer, 335 bool first_run) { 336 DCHECK(!profile_); 337 profile_ = target_profile; 338 writer_ = writer; 339 profile_info_ = &profile_info; 340 items_ = items; 341 342 ImporterHost::AddRef(); // Balanced in ImporterHost::ImportEnded. 343 344 import_to_bookmark_bar_ = ShouldImportToBookmarkBar(first_run); 345 CheckForFirefoxLock(profile_info, items, first_run); 346 CheckForLoadedModels(items); 347 348 InvokeTaskIfDone(); 349} 350 351void ExternalProcessImporterHost::InvokeTaskIfDone() { 352 if (waiting_for_bookmarkbar_model_ || !registrar_.IsEmpty() || 353 !is_source_readable_ || cancelled_) 354 return; 355 356 // The in-process half of the bridge which catches data from the IPC pipe 357 // and feeds it to the ProfileWriter. The external process half of the 358 // bridge lives in the external process -- see ProfileImportThread. 359 // The ExternalProcessImporterClient created in the next line owns this 360 // bridge, and will delete it. 361 InProcessImporterBridge* bridge = 362 new InProcessImporterBridge(writer_.get(), this); 363 client_ = new ExternalProcessImporterClient(this, *profile_info_, items_, 364 bridge, import_to_bookmark_bar_); 365 import_process_launched_ = true; 366 client_->Start(); 367} 368 369ExternalProcessImporterClient::ExternalProcessImporterClient( 370 ExternalProcessImporterHost* importer_host, 371 const importer::ProfileInfo& profile_info, 372 int items, 373 InProcessImporterBridge* bridge, 374 bool import_to_bookmark_bar) 375 : bookmarks_options_(0), 376 total_bookmarks_count_(0), 377 total_history_rows_count_(0), 378 total_fav_icons_count_(0), 379 process_importer_host_(importer_host), 380 profile_import_process_host_(NULL), 381 profile_info_(profile_info), 382 items_(items), 383 import_to_bookmark_bar_(import_to_bookmark_bar), 384 bridge_(bridge), 385 cancelled_(FALSE) { 386 bridge_->AddRef(); 387 process_importer_host_->ImportStarted(); 388} 389 390ExternalProcessImporterClient::~ExternalProcessImporterClient() { 391 bridge_->Release(); 392} 393 394void ExternalProcessImporterClient::Start() { 395 AddRef(); // balanced in Cleanup. 396 BrowserThread::ID thread_id; 397 CHECK(BrowserThread::GetCurrentThreadIdentifier(&thread_id)); 398 BrowserThread::PostTask( 399 BrowserThread::IO, FROM_HERE, 400 NewRunnableMethod(this, 401 &ExternalProcessImporterClient::StartProcessOnIOThread, 402 g_browser_process->resource_dispatcher_host(), thread_id)); 403} 404 405void ExternalProcessImporterClient::StartProcessOnIOThread( 406 ResourceDispatcherHost* rdh, 407 BrowserThread::ID thread_id) { 408 profile_import_process_host_ = 409 new ProfileImportProcessHost(rdh, this, thread_id); 410 profile_import_process_host_->StartProfileImportProcess(profile_info_, 411 items_, import_to_bookmark_bar_); 412} 413 414void ExternalProcessImporterClient::Cancel() { 415 if (cancelled_) 416 return; 417 418 cancelled_ = true; 419 if (profile_import_process_host_) { 420 BrowserThread::PostTask( 421 BrowserThread::IO, FROM_HERE, 422 NewRunnableMethod(this, 423 &ExternalProcessImporterClient::CancelImportProcessOnIOThread)); 424 } 425 Release(); 426} 427 428void ExternalProcessImporterClient::CancelImportProcessOnIOThread() { 429 profile_import_process_host_->CancelProfileImportProcess(); 430} 431 432void ExternalProcessImporterClient::NotifyItemFinishedOnIOThread( 433 importer::ImportItem import_item) { 434 profile_import_process_host_->ReportImportItemFinished(import_item); 435} 436 437void ExternalProcessImporterClient::OnProcessCrashed() { 438 if (cancelled_) 439 return; 440 441 process_importer_host_->Cancel(); 442} 443 444void ExternalProcessImporterClient::Cleanup() { 445 if (cancelled_) 446 return; 447 448 if (process_importer_host_) 449 process_importer_host_->ImportEnded(); 450 Release(); 451} 452 453void ExternalProcessImporterClient::OnImportStart() { 454 if (cancelled_) 455 return; 456 457 bridge_->NotifyStarted(); 458} 459 460void ExternalProcessImporterClient::OnImportFinished(bool succeeded, 461 std::string error_msg) { 462 if (cancelled_) 463 return; 464 465 if (!succeeded) 466 LOG(WARNING) << "Import failed. Error: " << error_msg; 467 Cleanup(); 468} 469 470void ExternalProcessImporterClient::OnImportItemStart(int item_data) { 471 if (cancelled_) 472 return; 473 474 bridge_->NotifyItemStarted(static_cast<importer::ImportItem>(item_data)); 475} 476 477void ExternalProcessImporterClient::OnImportItemFinished(int item_data) { 478 if (cancelled_) 479 return; 480 481 importer::ImportItem import_item = 482 static_cast<importer::ImportItem>(item_data); 483 bridge_->NotifyItemEnded(import_item); 484 BrowserThread::PostTask( 485 BrowserThread::IO, FROM_HERE, 486 NewRunnableMethod(this, 487 &ExternalProcessImporterClient::NotifyItemFinishedOnIOThread, 488 import_item)); 489} 490 491void ExternalProcessImporterClient::OnHistoryImportStart( 492 size_t total_history_rows_count) { 493 if (cancelled_) 494 return; 495 496 total_history_rows_count_ = total_history_rows_count; 497 history_rows_.reserve(total_history_rows_count); 498} 499 500void ExternalProcessImporterClient::OnHistoryImportGroup( 501 const std::vector<history::URLRow>& history_rows_group, 502 int visit_source) { 503 if (cancelled_) 504 return; 505 506 history_rows_.insert(history_rows_.end(), history_rows_group.begin(), 507 history_rows_group.end()); 508 if (history_rows_.size() == total_history_rows_count_) 509 bridge_->SetHistoryItems(history_rows_, 510 static_cast<history::VisitSource>(visit_source)); 511} 512 513void ExternalProcessImporterClient::OnHomePageImportReady( 514 const GURL& home_page) { 515 if (cancelled_) 516 return; 517 518 bridge_->AddHomePage(home_page); 519} 520 521void ExternalProcessImporterClient::OnBookmarksImportStart( 522 const std::wstring first_folder_name, 523 int options, size_t total_bookmarks_count) { 524 if (cancelled_) 525 return; 526 527 bookmarks_first_folder_name_ = first_folder_name; 528 bookmarks_options_ = options; 529 total_bookmarks_count_ = total_bookmarks_count; 530 bookmarks_.reserve(total_bookmarks_count); 531} 532 533void ExternalProcessImporterClient::OnBookmarksImportGroup( 534 const std::vector<ProfileWriter::BookmarkEntry>& bookmarks_group) { 535 if (cancelled_) 536 return; 537 538 // Collect sets of bookmarks from importer process until we have reached 539 // total_bookmarks_count_: 540 bookmarks_.insert(bookmarks_.end(), bookmarks_group.begin(), 541 bookmarks_group.end()); 542 if (bookmarks_.size() == total_bookmarks_count_) { 543 bridge_->AddBookmarkEntries(bookmarks_, bookmarks_first_folder_name_, 544 bookmarks_options_); 545 } 546} 547 548void ExternalProcessImporterClient::OnFavIconsImportStart( 549 size_t total_fav_icons_count) { 550 if (cancelled_) 551 return; 552 553 total_fav_icons_count_ = total_fav_icons_count; 554 fav_icons_.reserve(total_fav_icons_count); 555} 556 557void ExternalProcessImporterClient::OnFavIconsImportGroup( 558 const std::vector<history::ImportedFavIconUsage>& fav_icons_group) { 559 if (cancelled_) 560 return; 561 562 fav_icons_.insert(fav_icons_.end(), fav_icons_group.begin(), 563 fav_icons_group.end()); 564 if (fav_icons_.size() == total_fav_icons_count_) 565 bridge_->SetFavIcons(fav_icons_); 566} 567 568void ExternalProcessImporterClient::OnPasswordFormImportReady( 569 const webkit_glue::PasswordForm& form) { 570 if (cancelled_) 571 return; 572 573 bridge_->SetPasswordForm(form); 574} 575 576void ExternalProcessImporterClient::OnKeywordsImportReady( 577 const std::vector<TemplateURL>& template_urls, 578 int default_keyword_index, bool unique_on_host_and_path) { 579 if (cancelled_) 580 return; 581 582 std::vector<TemplateURL*> template_url_vec; 583 template_url_vec.reserve(template_urls.size()); 584 std::vector<TemplateURL>::const_iterator iter; 585 for (iter = template_urls.begin(); 586 iter != template_urls.end(); 587 ++iter) { 588 template_url_vec.push_back(new TemplateURL(*iter)); 589 } 590 bridge_->SetKeywords(template_url_vec, default_keyword_index, 591 unique_on_host_and_path); 592} 593