blob: 46510407a8d9ea00c21fc7708dc2906cdd5d3442 [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/importer/importer.h"
#include "base/threading/thread.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_thread.h"
#include "chrome/browser/browsing_instance.h"
#include "chrome/browser/importer/firefox_profile_lock.h"
#include "chrome/browser/importer/importer_bridge.h"
#include "chrome/browser/renderer_host/site_instance.h"
#include "chrome/browser/search_engines/template_url.h"
#include "chrome/browser/search_engines/template_url_model.h"
#include "chrome/browser/tabs/tab_strip_model.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/webdata/web_data_service.h"
#include "chrome/common/notification_source.h"
#include "gfx/codec/png_codec.h"
#include "gfx/favicon_size.h"
#include "grit/generated_resources.h"
#include "skia/ext/image_operations.h"
#include "ui/base/l10n/l10n_util.h"
#include "webkit/glue/image_decoder.h"
// TODO(port): Port these files.
#if defined(OS_WIN)
#include "chrome/browser/ui/views/importer_lock_view.h"
#include "ui/base/message_box_win.h"
#include "views/window/window.h"
#elif defined(OS_MACOSX)
#include "chrome/browser/ui/cocoa/importer/importer_lock_dialog.h"
#elif defined(TOOLKIT_USES_GTK)
#include "chrome/browser/ui/gtk/import_lock_dialog_gtk.h"
#endif
using webkit_glue::PasswordForm;
// Importer.
void Importer::Cancel() { cancelled_ = true; }
Importer::Importer()
: cancelled_(false),
import_to_bookmark_bar_(false),
bookmark_bar_disabled_(false) {
}
Importer::~Importer() {
}
// static
bool Importer::ReencodeFavicon(const unsigned char* src_data, size_t src_len,
std::vector<unsigned char>* png_data) {
// Decode the favicon using WebKit's image decoder.
webkit_glue::ImageDecoder decoder(gfx::Size(kFavIconSize, kFavIconSize));
SkBitmap decoded = decoder.Decode(src_data, src_len);
if (decoded.empty())
return false; // Unable to decode.
if (decoded.width() != kFavIconSize || decoded.height() != kFavIconSize) {
// The bitmap is not the correct size, re-sample.
int new_width = decoded.width();
int new_height = decoded.height();
calc_favicon_target_size(&new_width, &new_height);
decoded = skia::ImageOperations::Resize(
decoded, skia::ImageOperations::RESIZE_LANCZOS3, new_width, new_height);
}
// Encode our bitmap as a PNG.
gfx::PNGCodec::EncodeBGRASkBitmap(decoded, false, png_data);
return true;
}
// ImporterHost.
ImporterHost::ImporterHost()
: profile_(NULL),
observer_(NULL),
task_(NULL),
importer_(NULL),
waiting_for_bookmarkbar_model_(false),
installed_bookmark_observer_(false),
is_source_readable_(true),
headless_(false),
parent_window_(NULL) {
}
ImporterHost::~ImporterHost() {
if (NULL != importer_)
importer_->Release();
if (installed_bookmark_observer_) {
DCHECK(profile_); // Only way for waiting_for_bookmarkbar_model_ to be true
// is if we have a profile.
profile_->GetBookmarkModel()->RemoveObserver(this);
}
}
void ImporterHost::Loaded(BookmarkModel* model) {
DCHECK(model->IsLoaded());
model->RemoveObserver(this);
waiting_for_bookmarkbar_model_ = false;
installed_bookmark_observer_ = false;
importer_->set_import_to_bookmark_bar(!model->HasBookmarks());
InvokeTaskIfDone();
}
void ImporterHost::BookmarkModelBeingDeleted(BookmarkModel* model) {
installed_bookmark_observer_ = false;
}
void ImporterHost::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
DCHECK(type == NotificationType::TEMPLATE_URL_MODEL_LOADED);
registrar_.RemoveAll();
InvokeTaskIfDone();
}
void ImporterHost::ShowWarningDialog() {
if (headless_) {
OnLockViewEnd(false);
} else {
#if defined(OS_WIN)
views::Window::CreateChromeWindow(NULL, gfx::Rect(),
new ImporterLockView(this))->Show();
#elif defined(TOOLKIT_USES_GTK)
ImportLockDialogGtk::Show(parent_window_, this);
#else
ImportLockDialogCocoa::ShowWarning(this);
#endif
}
}
void ImporterHost::OnLockViewEnd(bool is_continue) {
if (is_continue) {
// User chose to continue, then we check the lock again to make
// sure that Firefox has been closed. Try to import the settings
// if successful. Otherwise, show a warning dialog.
firefox_lock_->Lock();
if (firefox_lock_->HasAcquired()) {
is_source_readable_ = true;
InvokeTaskIfDone();
} else {
ShowWarningDialog();
}
} else {
// User chose to skip the import process. We should delete
// the task and notify the ImporterHost to finish.
delete task_;
task_ = NULL;
importer_ = NULL;
ImportEnded();
}
}
void ImporterHost::StartImportSettings(
const importer::ProfileInfo& profile_info,
Profile* target_profile,
uint16 items,
ProfileWriter* writer,
bool first_run) {
DCHECK(!profile_); // We really only support importing from one host at a
// time.
profile_ = target_profile;
// Preserves the observer and creates a task, since we do async import
// so that it doesn't block the UI. When the import is complete, observer
// will be notified.
writer_ = writer;
importer_ = ImporterList::CreateImporterByType(profile_info.browser_type);
// If we fail to create Importer, exit as we cannot do anything.
if (!importer_) {
ImportEnded();
return;
}
importer_->AddRef();
importer_->set_import_to_bookmark_bar(ShouldImportToBookmarkBar(first_run));
importer_->set_bookmark_bar_disabled(first_run);
scoped_refptr<ImporterBridge> bridge(
new InProcessImporterBridge(writer_.get(), this));
task_ = NewRunnableMethod(importer_, &Importer::StartImport,
profile_info, items, bridge);
CheckForFirefoxLock(profile_info, items, first_run);
#if defined(OS_WIN)
// For google toolbar import, we need the user to log in and store their GAIA
// credentials.
if (profile_info.browser_type == importer::GOOGLE_TOOLBAR5) {
if (!toolbar_importer_utils::IsGoogleGAIACookieInstalled()) {
ui::MessageBox(
NULL,
UTF16ToWide(l10n_util::GetStringUTF16(
IDS_IMPORTER_GOOGLE_LOGIN_TEXT)).c_str(),
L"",
MB_OK | MB_TOPMOST);
GURL url("https://www.google.com/accounts/ServiceLogin");
BrowserList::GetLastActive()->AddSelectedTabWithURL(
url, PageTransition::TYPED);
MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
this, &ImporterHost::OnLockViewEnd, false));
is_source_readable_ = false;
}
}
#endif
CheckForLoadedModels(items);
AddRef();
InvokeTaskIfDone();
}
void ImporterHost::Cancel() {
if (importer_)
importer_->Cancel();
}
void ImporterHost::SetObserver(Observer* observer) {
observer_ = observer;
}
void ImporterHost::InvokeTaskIfDone() {
if (waiting_for_bookmarkbar_model_ || !registrar_.IsEmpty() ||
!is_source_readable_)
return;
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, task_);
}
void ImporterHost::ImportItemStarted(importer::ImportItem item) {
if (observer_)
observer_->ImportItemStarted(item);
}
void ImporterHost::ImportItemEnded(importer::ImportItem item) {
if (observer_)
observer_->ImportItemEnded(item);
}
void ImporterHost::ImportStarted() {
if (observer_)
observer_->ImportStarted();
}
void ImporterHost::ImportEnded() {
firefox_lock_.reset(); // Release the Firefox profile lock.
if (observer_)
observer_->ImportEnded();
Release();
}
bool ImporterHost::ShouldImportToBookmarkBar(bool first_run) {
bool import_to_bookmark_bar = first_run;
if (profile_ && profile_->GetBookmarkModel()->IsLoaded()) {
import_to_bookmark_bar = (!profile_->GetBookmarkModel()->HasBookmarks());
}
return import_to_bookmark_bar;
}
void ImporterHost::CheckForFirefoxLock(
const importer::ProfileInfo& profile_info, uint16 items, bool first_run) {
if (profile_info.browser_type == importer::FIREFOX2 ||
profile_info.browser_type == importer::FIREFOX3) {
DCHECK(!firefox_lock_.get());
firefox_lock_.reset(new FirefoxProfileLock(profile_info.source_path));
if (!firefox_lock_->HasAcquired()) {
// If fail to acquire the lock, we set the source unreadable and
// show a warning dialog, unless running without UI.
is_source_readable_ = false;
ShowWarningDialog();
}
}
}
void ImporterHost::CheckForLoadedModels(uint16 items) {
// BookmarkModel should be loaded before adding IE favorites. So we observe
// the BookmarkModel if needed, and start the task after it has been loaded.
if ((items & importer::FAVORITES) && !writer_->BookmarkModelIsLoaded()) {
profile_->GetBookmarkModel()->AddObserver(this);
waiting_for_bookmarkbar_model_ = true;
installed_bookmark_observer_ = true;
}
// Observes the TemplateURLModel if needed to import search engines from the
// other browser. We also check to see if we're importing bookmarks because
// we can import bookmark keywords from Firefox as search engines.
if ((items & importer::SEARCH_ENGINES) || (items & importer::FAVORITES)) {
if (!writer_->TemplateURLModelIsLoaded()) {
TemplateURLModel* model = profile_->GetTemplateURLModel();
registrar_.Add(this, NotificationType::TEMPLATE_URL_MODEL_LOADED,
Source<TemplateURLModel>(model));
model->Load();
}
}
}
ExternalProcessImporterHost::ExternalProcessImporterHost()
: items_(0),
import_to_bookmark_bar_(false),
cancelled_(false),
import_process_launched_(false) {
}
void ExternalProcessImporterHost::Loaded(BookmarkModel* model) {
DCHECK(model->IsLoaded());
model->RemoveObserver(this);
waiting_for_bookmarkbar_model_ = false;
installed_bookmark_observer_ = false;
// Because the import process is running externally, the decision whether
// to import to the bookmark bar must be stored here so that it can be
// passed to the importer when the import task is invoked.
import_to_bookmark_bar_ = (!model->HasBookmarks());
InvokeTaskIfDone();
}
void ExternalProcessImporterHost::Cancel() {
cancelled_ = true;
if (import_process_launched_)
client_->Cancel();
ImportEnded(); // Tells the observer that we're done, and releases us.
}
void ExternalProcessImporterHost::StartImportSettings(
const importer::ProfileInfo& profile_info,
Profile* target_profile,
uint16 items,
ProfileWriter* writer,
bool first_run) {
DCHECK(!profile_);
profile_ = target_profile;
writer_ = writer;
profile_info_ = &profile_info;
items_ = items;
ImporterHost::AddRef(); // Balanced in ImporterHost::ImportEnded.
import_to_bookmark_bar_ = ShouldImportToBookmarkBar(first_run);
CheckForFirefoxLock(profile_info, items, first_run);
CheckForLoadedModels(items);
InvokeTaskIfDone();
}
void ExternalProcessImporterHost::InvokeTaskIfDone() {
if (waiting_for_bookmarkbar_model_ || !registrar_.IsEmpty() ||
!is_source_readable_ || cancelled_)
return;
// The in-process half of the bridge which catches data from the IPC pipe
// and feeds it to the ProfileWriter. The external process half of the
// bridge lives in the external process -- see ProfileImportThread.
// The ExternalProcessImporterClient created in the next line owns this
// bridge, and will delete it.
InProcessImporterBridge* bridge =
new InProcessImporterBridge(writer_.get(), this);
client_ = new ExternalProcessImporterClient(this, *profile_info_, items_,
bridge, import_to_bookmark_bar_);
import_process_launched_ = true;
client_->Start();
}
ExternalProcessImporterClient::ExternalProcessImporterClient(
ExternalProcessImporterHost* importer_host,
const importer::ProfileInfo& profile_info,
int items,
InProcessImporterBridge* bridge,
bool import_to_bookmark_bar)
: bookmarks_options_(0),
total_bookmarks_count_(0),
total_history_rows_count_(0),
total_fav_icons_count_(0),
process_importer_host_(importer_host),
profile_import_process_host_(NULL),
profile_info_(profile_info),
items_(items),
import_to_bookmark_bar_(import_to_bookmark_bar),
bridge_(bridge),
cancelled_(FALSE) {
bridge_->AddRef();
process_importer_host_->ImportStarted();
}
ExternalProcessImporterClient::~ExternalProcessImporterClient() {
bridge_->Release();
}
void ExternalProcessImporterClient::Start() {
AddRef(); // balanced in Cleanup.
BrowserThread::ID thread_id;
CHECK(BrowserThread::GetCurrentThreadIdentifier(&thread_id));
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
NewRunnableMethod(this,
&ExternalProcessImporterClient::StartProcessOnIOThread,
g_browser_process->resource_dispatcher_host(), thread_id));
}
void ExternalProcessImporterClient::StartProcessOnIOThread(
ResourceDispatcherHost* rdh,
BrowserThread::ID thread_id) {
profile_import_process_host_ =
new ProfileImportProcessHost(rdh, this, thread_id);
profile_import_process_host_->StartProfileImportProcess(profile_info_,
items_, import_to_bookmark_bar_);
}
void ExternalProcessImporterClient::Cancel() {
if (cancelled_)
return;
cancelled_ = true;
if (profile_import_process_host_) {
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
NewRunnableMethod(this,
&ExternalProcessImporterClient::CancelImportProcessOnIOThread));
}
Release();
}
void ExternalProcessImporterClient::CancelImportProcessOnIOThread() {
profile_import_process_host_->CancelProfileImportProcess();
}
void ExternalProcessImporterClient::NotifyItemFinishedOnIOThread(
importer::ImportItem import_item) {
profile_import_process_host_->ReportImportItemFinished(import_item);
}
void ExternalProcessImporterClient::OnProcessCrashed(int exit_code) {
if (cancelled_)
return;
process_importer_host_->Cancel();
}
void ExternalProcessImporterClient::Cleanup() {
if (cancelled_)
return;
if (process_importer_host_)
process_importer_host_->ImportEnded();
Release();
}
void ExternalProcessImporterClient::OnImportStart() {
if (cancelled_)
return;
bridge_->NotifyStarted();
}
void ExternalProcessImporterClient::OnImportFinished(bool succeeded,
std::string error_msg) {
if (cancelled_)
return;
if (!succeeded)
LOG(WARNING) << "Import failed. Error: " << error_msg;
Cleanup();
}
void ExternalProcessImporterClient::OnImportItemStart(int item_data) {
if (cancelled_)
return;
bridge_->NotifyItemStarted(static_cast<importer::ImportItem>(item_data));
}
void ExternalProcessImporterClient::OnImportItemFinished(int item_data) {
if (cancelled_)
return;
importer::ImportItem import_item =
static_cast<importer::ImportItem>(item_data);
bridge_->NotifyItemEnded(import_item);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
NewRunnableMethod(this,
&ExternalProcessImporterClient::NotifyItemFinishedOnIOThread,
import_item));
}
void ExternalProcessImporterClient::OnHistoryImportStart(
size_t total_history_rows_count) {
if (cancelled_)
return;
total_history_rows_count_ = total_history_rows_count;
history_rows_.reserve(total_history_rows_count);
}
void ExternalProcessImporterClient::OnHistoryImportGroup(
const std::vector<history::URLRow>& history_rows_group,
int visit_source) {
if (cancelled_)
return;
history_rows_.insert(history_rows_.end(), history_rows_group.begin(),
history_rows_group.end());
if (history_rows_.size() == total_history_rows_count_)
bridge_->SetHistoryItems(history_rows_,
static_cast<history::VisitSource>(visit_source));
}
void ExternalProcessImporterClient::OnHomePageImportReady(
const GURL& home_page) {
if (cancelled_)
return;
bridge_->AddHomePage(home_page);
}
void ExternalProcessImporterClient::OnBookmarksImportStart(
const std::wstring first_folder_name,
int options, size_t total_bookmarks_count) {
if (cancelled_)
return;
bookmarks_first_folder_name_ = first_folder_name;
bookmarks_options_ = options;
total_bookmarks_count_ = total_bookmarks_count;
bookmarks_.reserve(total_bookmarks_count);
}
void ExternalProcessImporterClient::OnBookmarksImportGroup(
const std::vector<ProfileWriter::BookmarkEntry>& bookmarks_group) {
if (cancelled_)
return;
// Collect sets of bookmarks from importer process until we have reached
// total_bookmarks_count_:
bookmarks_.insert(bookmarks_.end(), bookmarks_group.begin(),
bookmarks_group.end());
if (bookmarks_.size() == total_bookmarks_count_) {
bridge_->AddBookmarkEntries(bookmarks_, bookmarks_first_folder_name_,
bookmarks_options_);
}
}
void ExternalProcessImporterClient::OnFavIconsImportStart(
size_t total_fav_icons_count) {
if (cancelled_)
return;
total_fav_icons_count_ = total_fav_icons_count;
fav_icons_.reserve(total_fav_icons_count);
}
void ExternalProcessImporterClient::OnFavIconsImportGroup(
const std::vector<history::ImportedFavIconUsage>& fav_icons_group) {
if (cancelled_)
return;
fav_icons_.insert(fav_icons_.end(), fav_icons_group.begin(),
fav_icons_group.end());
if (fav_icons_.size() == total_fav_icons_count_)
bridge_->SetFavIcons(fav_icons_);
}
void ExternalProcessImporterClient::OnPasswordFormImportReady(
const webkit_glue::PasswordForm& form) {
if (cancelled_)
return;
bridge_->SetPasswordForm(form);
}
void ExternalProcessImporterClient::OnKeywordsImportReady(
const std::vector<TemplateURL>& template_urls,
int default_keyword_index, bool unique_on_host_and_path) {
if (cancelled_)
return;
std::vector<TemplateURL*> template_url_vec;
template_url_vec.reserve(template_urls.size());
std::vector<TemplateURL>::const_iterator iter;
for (iter = template_urls.begin();
iter != template_urls.end();
++iter) {
template_url_vec.push_back(new TemplateURL(*iter));
}
bridge_->SetKeywords(template_url_vec, default_keyword_index,
unique_on_host_and_path);
}