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