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