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_host.h"
6
7#include "base/utf_string_conversions.h"
8#include "chrome/browser/bookmarks/bookmark_model.h"
9#include "chrome/browser/browser_process.h"
10#include "chrome/browser/importer/firefox_profile_lock.h"
11#include "chrome/browser/importer/importer.h"
12#include "chrome/browser/importer/importer_lock_dialog.h"
13#include "chrome/browser/importer/importer_progress_observer.h"
14#include "chrome/browser/importer/importer_type.h"
15#include "chrome/browser/importer/in_process_importer_bridge.h"
16#include "chrome/browser/importer/toolbar_importer_utils.h"
17#include "chrome/browser/profiles/profile.h"
18#include "chrome/browser/search_engines/template_url.h"
19#include "chrome/browser/search_engines/template_url_model.h"
20#include "chrome/browser/ui/browser_list.h"
21#include "content/browser/browser_thread.h"
22#include "content/common/notification_source.h"
23#include "grit/generated_resources.h"
24#include "ui/base/l10n/l10n_util.h"
25
26#if defined(OS_WIN)
27// TODO(port): Port this file.
28#include "ui/base/message_box_win.h"
29#endif
30
31ImporterHost::ImporterHost()
32    : profile_(NULL),
33      task_(NULL),
34      importer_(NULL),
35      waiting_for_bookmarkbar_model_(false),
36      installed_bookmark_observer_(false),
37      is_source_readable_(true),
38      headless_(false),
39      parent_window_(NULL),
40      observer_(NULL) {
41}
42
43void ImporterHost::ShowWarningDialog() {
44  if (headless_)
45    OnImportLockDialogEnd(false);
46  else
47    importer::ShowImportLockDialog(parent_window_, this);
48}
49
50void ImporterHost::OnImportLockDialogEnd(bool is_continue) {
51  if (is_continue) {
52    // User chose to continue, then we check the lock again to make
53    // sure that Firefox has been closed. Try to import the settings
54    // if successful. Otherwise, show a warning dialog.
55    firefox_lock_->Lock();
56    if (firefox_lock_->HasAcquired()) {
57      is_source_readable_ = true;
58      InvokeTaskIfDone();
59    } else {
60      ShowWarningDialog();
61    }
62  } else {
63    // User chose to skip the import process. We should delete
64    // the task and notify the ImporterHost to finish.
65    delete task_;
66    task_ = NULL;
67    importer_ = NULL;
68    NotifyImportEnded();
69  }
70}
71
72void ImporterHost::SetObserver(importer::ImporterProgressObserver* observer) {
73  observer_ = observer;
74}
75
76void ImporterHost::NotifyImportStarted() {
77  if (observer_)
78    observer_->ImportStarted();
79}
80
81void ImporterHost::NotifyImportItemStarted(importer::ImportItem item) {
82  if (observer_)
83    observer_->ImportItemStarted(item);
84}
85
86void ImporterHost::NotifyImportItemEnded(importer::ImportItem item) {
87  if (observer_)
88    observer_->ImportItemEnded(item);
89}
90
91void ImporterHost::NotifyImportEnded() {
92  firefox_lock_.reset();  // Release the Firefox profile lock.
93  if (observer_)
94    observer_->ImportEnded();
95  Release();
96}
97
98void ImporterHost::StartImportSettings(
99    const importer::SourceProfile& source_profile,
100    Profile* target_profile,
101    uint16 items,
102    ProfileWriter* writer,
103    bool first_run) {
104  // We really only support importing from one host at a time.
105  DCHECK(!profile_);
106
107  profile_ = target_profile;
108  // Preserves the observer and creates a task, since we do async import so that
109  // it doesn't block the UI. When the import is complete, observer will be
110  // notified.
111  writer_ = writer;
112  importer_ = importer::CreateImporterByType(source_profile.importer_type);
113  // If we fail to create the Importer, exit, as we cannot do anything.
114  if (!importer_) {
115    NotifyImportEnded();
116    return;
117  }
118
119  importer_->AddRef();
120  importer_->set_import_to_bookmark_bar(ShouldImportToBookmarkBar(first_run));
121  importer_->set_bookmark_bar_disabled(first_run);
122
123  scoped_refptr<InProcessImporterBridge> bridge(
124      new InProcessImporterBridge(writer_.get(), this));
125  task_ = NewRunnableMethod(
126      importer_, &Importer::StartImport, source_profile, items, bridge);
127
128  CheckForFirefoxLock(source_profile, items, first_run);
129
130#if defined(OS_WIN)
131  // For google toolbar import, we need the user to log in and store their GAIA
132  // credentials.
133  if (source_profile.importer_type == importer::GOOGLE_TOOLBAR5) {
134    if (!toolbar_importer_utils::IsGoogleGAIACookieInstalled()) {
135      ui::MessageBox(
136          NULL,
137          UTF16ToWide(l10n_util::GetStringUTF16(
138              IDS_IMPORTER_GOOGLE_LOGIN_TEXT)).c_str(),
139          L"",
140          MB_OK | MB_TOPMOST);
141
142      GURL url("https://www.google.com/accounts/ServiceLogin");
143      BrowserList::GetLastActive()->AddSelectedTabWithURL(
144          url, PageTransition::TYPED);
145
146      MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
147          this, &ImporterHost::OnImportLockDialogEnd, false));
148
149      is_source_readable_ = false;
150    }
151  }
152#endif
153
154  CheckForLoadedModels(items);
155  AddRef();
156  InvokeTaskIfDone();
157}
158
159void ImporterHost::Cancel() {
160  if (importer_)
161    importer_->Cancel();
162}
163
164ImporterHost::~ImporterHost() {
165  if (NULL != importer_)
166    importer_->Release();
167
168  if (installed_bookmark_observer_) {
169    DCHECK(profile_);  // Only way for waiting_for_bookmarkbar_model_ to be true
170                       // is if we have a profile.
171    profile_->GetBookmarkModel()->RemoveObserver(this);
172  }
173}
174
175bool ImporterHost::ShouldImportToBookmarkBar(bool first_run) {
176  bool import_to_bookmark_bar = first_run;
177  if (profile_ && profile_->GetBookmarkModel()->IsLoaded()) {
178    import_to_bookmark_bar = (!profile_->GetBookmarkModel()->HasBookmarks());
179  }
180  return import_to_bookmark_bar;
181}
182
183void ImporterHost::CheckForFirefoxLock(
184    const importer::SourceProfile& source_profile,
185    uint16 items,
186    bool first_run) {
187  if (source_profile.importer_type == importer::FIREFOX2 ||
188      source_profile.importer_type == importer::FIREFOX3) {
189    DCHECK(!firefox_lock_.get());
190    firefox_lock_.reset(new FirefoxProfileLock(source_profile.source_path));
191    if (!firefox_lock_->HasAcquired()) {
192      // If fail to acquire the lock, we set the source unreadable and
193      // show a warning dialog, unless running without UI.
194      is_source_readable_ = false;
195      ShowWarningDialog();
196    }
197  }
198}
199
200void ImporterHost::CheckForLoadedModels(uint16 items) {
201  // BookmarkModel should be loaded before adding IE favorites. So we observe
202  // the BookmarkModel if needed, and start the task after it has been loaded.
203  if ((items & importer::FAVORITES) && !writer_->BookmarkModelIsLoaded()) {
204    profile_->GetBookmarkModel()->AddObserver(this);
205    waiting_for_bookmarkbar_model_ = true;
206    installed_bookmark_observer_ = true;
207  }
208
209  // Observes the TemplateURLModel if needed to import search engines from the
210  // other browser. We also check to see if we're importing bookmarks because
211  // we can import bookmark keywords from Firefox as search engines.
212  if ((items & importer::SEARCH_ENGINES) || (items & importer::FAVORITES)) {
213    if (!writer_->TemplateURLModelIsLoaded()) {
214      TemplateURLModel* model = profile_->GetTemplateURLModel();
215      registrar_.Add(this, NotificationType::TEMPLATE_URL_MODEL_LOADED,
216                     Source<TemplateURLModel>(model));
217      model->Load();
218    }
219  }
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::Loaded(BookmarkModel* model) {
230  DCHECK(model->IsLoaded());
231  model->RemoveObserver(this);
232  waiting_for_bookmarkbar_model_ = false;
233  installed_bookmark_observer_ = false;
234
235  importer_->set_import_to_bookmark_bar(!model->HasBookmarks());
236  InvokeTaskIfDone();
237}
238
239void ImporterHost::BookmarkModelBeingDeleted(BookmarkModel* model) {
240  installed_bookmark_observer_ = false;
241}
242
243void ImporterHost::BookmarkModelChanged() {
244}
245
246void ImporterHost::Observe(NotificationType type,
247                           const NotificationSource& source,
248                           const NotificationDetails& details) {
249  DCHECK(type == NotificationType::TEMPLATE_URL_MODEL_LOADED);
250  registrar_.RemoveAll();
251  InvokeTaskIfDone();
252}
253