1// Copyright (c) 2012 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/external_process_importer_host.h"
6
7#include "base/bind.h"
8#include "chrome/browser/bookmarks/bookmark_model_factory.h"
9#include "chrome/browser/chrome_notification_types.h"
10#include "chrome/browser/importer/external_process_importer_client.h"
11#include "chrome/browser/importer/firefox_profile_lock.h"
12#include "chrome/browser/importer/importer_lock_dialog.h"
13#include "chrome/browser/importer/importer_progress_observer.h"
14#include "chrome/browser/importer/in_process_importer_bridge.h"
15#include "chrome/browser/search_engines/template_url_service_factory.h"
16#include "components/bookmarks/browser/bookmark_model.h"
17#include "components/search_engines/template_url_service.h"
18#include "content/public/browser/browser_thread.h"
19
20using content::BrowserThread;
21
22ExternalProcessImporterHost::ExternalProcessImporterHost()
23    : headless_(false),
24      parent_window_(NULL),
25      observer_(NULL),
26      profile_(NULL),
27      waiting_for_bookmarkbar_model_(false),
28      installed_bookmark_observer_(false),
29      is_source_readable_(true),
30      client_(NULL),
31      items_(0),
32      cancelled_(false),
33      weak_ptr_factory_(this) {
34}
35
36void ExternalProcessImporterHost::Cancel() {
37  cancelled_ = true;
38  // There is only a |client_| if the import was started.
39  if (client_)
40    client_->Cancel();
41  NotifyImportEnded();  // Tells the observer that we're done, and deletes us.
42}
43
44void ExternalProcessImporterHost::StartImportSettings(
45    const importer::SourceProfile& source_profile,
46    Profile* target_profile,
47    uint16 items,
48    ProfileWriter* writer) {
49  // We really only support importing from one host at a time.
50  DCHECK(!profile_);
51  DCHECK(target_profile);
52
53  profile_ = target_profile;
54  writer_ = writer;
55  source_profile_ = source_profile;
56  items_ = items;
57
58  if (!CheckForFirefoxLock(source_profile)) {
59    Cancel();
60    return;
61  }
62
63  CheckForLoadedModels(items);
64
65  LaunchImportIfReady();
66}
67
68void ExternalProcessImporterHost::NotifyImportStarted() {
69  if (observer_)
70    observer_->ImportStarted();
71}
72
73void ExternalProcessImporterHost::NotifyImportItemStarted(
74    importer::ImportItem item) {
75  if (observer_)
76    observer_->ImportItemStarted(item);
77}
78
79void ExternalProcessImporterHost::NotifyImportItemEnded(
80    importer::ImportItem item) {
81  if (observer_)
82    observer_->ImportItemEnded(item);
83}
84
85void ExternalProcessImporterHost::NotifyImportEnded() {
86  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
87  firefox_lock_.reset();
88  if (observer_)
89    observer_->ImportEnded();
90  delete this;
91}
92
93ExternalProcessImporterHost::~ExternalProcessImporterHost() {
94  if (installed_bookmark_observer_) {
95    DCHECK(profile_);
96    BookmarkModelFactory::GetForProfile(profile_)->RemoveObserver(this);
97  }
98}
99
100void ExternalProcessImporterHost::LaunchImportIfReady() {
101  if (waiting_for_bookmarkbar_model_ || template_service_subscription_.get() ||
102      !is_source_readable_ || cancelled_)
103    return;
104
105  // This is the in-process half of the bridge, which catches data from the IPC
106  // pipe and feeds it to the ProfileWriter. The external process half of the
107  // bridge lives in the external process (see ProfileImportThread).
108  // The ExternalProcessImporterClient created in the next line owns the bridge,
109  // and will delete it.
110  InProcessImporterBridge* bridge =
111      new InProcessImporterBridge(writer_.get(),
112                                  weak_ptr_factory_.GetWeakPtr());
113  client_ = new ExternalProcessImporterClient(
114      weak_ptr_factory_.GetWeakPtr(), source_profile_, items_, bridge);
115  client_->Start();
116}
117
118void ExternalProcessImporterHost::BookmarkModelLoaded(BookmarkModel* model,
119                                                      bool ids_reassigned) {
120  DCHECK(model->loaded());
121  model->RemoveObserver(this);
122  waiting_for_bookmarkbar_model_ = false;
123  installed_bookmark_observer_ = false;
124
125  LaunchImportIfReady();
126}
127
128void ExternalProcessImporterHost::BookmarkModelBeingDeleted(
129    BookmarkModel* model) {
130  installed_bookmark_observer_ = false;
131}
132
133void ExternalProcessImporterHost::BookmarkModelChanged() {
134}
135
136void ExternalProcessImporterHost::OnTemplateURLServiceLoaded() {
137  template_service_subscription_.reset();
138  LaunchImportIfReady();
139}
140
141void ExternalProcessImporterHost::ShowWarningDialog() {
142  DCHECK(!headless_);
143  importer::ShowImportLockDialog(
144      parent_window_,
145      base::Bind(&ExternalProcessImporterHost::OnImportLockDialogEnd,
146                 weak_ptr_factory_.GetWeakPtr()));
147}
148
149void ExternalProcessImporterHost::OnImportLockDialogEnd(bool is_continue) {
150  if (is_continue) {
151    // User chose to continue, then we check the lock again to make
152    // sure that Firefox has been closed. Try to import the settings
153    // if successful. Otherwise, show a warning dialog.
154    firefox_lock_->Lock();
155    if (firefox_lock_->HasAcquired()) {
156      is_source_readable_ = true;
157      LaunchImportIfReady();
158    } else {
159      ShowWarningDialog();
160    }
161  } else {
162    NotifyImportEnded();
163  }
164}
165
166bool ExternalProcessImporterHost::CheckForFirefoxLock(
167    const importer::SourceProfile& source_profile) {
168  if (source_profile.importer_type != importer::TYPE_FIREFOX)
169    return true;
170
171  DCHECK(!firefox_lock_.get());
172  firefox_lock_.reset(new FirefoxProfileLock(source_profile.source_path));
173  if (firefox_lock_->HasAcquired())
174    return true;
175
176  // If fail to acquire the lock, we set the source unreadable and
177  // show a warning dialog, unless running without UI (in which case the import
178  // must be aborted).
179  is_source_readable_ = false;
180  if (headless_)
181    return false;
182
183  ShowWarningDialog();
184  return true;
185}
186
187void ExternalProcessImporterHost::CheckForLoadedModels(uint16 items) {
188  // A target profile must be loaded by StartImportSettings().
189  DCHECK(profile_);
190
191  // BookmarkModel should be loaded before adding IE favorites. So we observe
192  // the BookmarkModel if needed, and start the task after it has been loaded.
193  if ((items & importer::FAVORITES) && !writer_->BookmarkModelIsLoaded()) {
194    BookmarkModelFactory::GetForProfile(profile_)->AddObserver(this);
195    waiting_for_bookmarkbar_model_ = true;
196    installed_bookmark_observer_ = true;
197  }
198
199  // Observes the TemplateURLService if needed to import search engines from the
200  // other browser. We also check to see if we're importing bookmarks because
201  // we can import bookmark keywords from Firefox as search engines.
202  if ((items & importer::SEARCH_ENGINES) || (items & importer::FAVORITES)) {
203    if (!writer_->TemplateURLServiceIsLoaded()) {
204      TemplateURLService* model =
205          TemplateURLServiceFactory::GetForProfile(profile_);
206      template_service_subscription_ = model->RegisterOnLoadedCallback(
207          base::Bind(&ExternalProcessImporterHost::OnTemplateURLServiceLoaded,
208                     weak_ptr_factory_.GetWeakPtr()));
209      model->Load();
210    }
211  }
212}
213