OLD | NEW |
| (Empty) |
1 // Copyright 2013 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 "base/bind.h" | |
6 #include "base/file_util.h" | |
7 #include "base/files/file_enumerator.h" | |
8 #include "base/files/file_path.h" | |
9 #include "base/location.h" | |
10 #include "base/message_loop/message_loop_proxy.h" | |
11 #include "base/sequenced_task_runner.h" | |
12 #include "base/single_thread_task_runner.h" | |
13 #include "base/values.h" | |
14 #include "base/version.h" | |
15 // TODO(ddorwin): Find a better place for ReadManifest. | |
16 #include "chrome/browser/component_updater/component_unpacker.h" | |
17 #include "chrome/browser/component_updater/component_updater_configurator.h" | |
18 #include "chrome/browser/component_updater/default_component_installer.h" | |
19 | |
20 namespace component_updater { | |
21 | |
22 namespace { | |
23 // Version "0" corresponds to no installed version. By the server's conventions, | |
24 // we represent it as a dotted quad. | |
25 const char kNullVersion[] = "0.0.0.0"; | |
26 } // namespace | |
27 | |
28 ComponentInstallerTraits::~ComponentInstallerTraits() { | |
29 } | |
30 | |
31 DefaultComponentInstaller::DefaultComponentInstaller( | |
32 scoped_ptr<ComponentInstallerTraits> installer_traits) | |
33 : current_version_(kNullVersion), | |
34 main_task_runner_(base::MessageLoopProxy::current()) { | |
35 installer_traits_ = installer_traits.Pass(); | |
36 } | |
37 | |
38 DefaultComponentInstaller::~DefaultComponentInstaller() { | |
39 DCHECK(thread_checker_.CalledOnValidThread()); | |
40 } | |
41 | |
42 void DefaultComponentInstaller::Register(ComponentUpdateService* cus) { | |
43 DCHECK(thread_checker_.CalledOnValidThread()); | |
44 task_runner_ = cus->GetSequencedTaskRunner(); | |
45 | |
46 if (!installer_traits_) { | |
47 NOTREACHED() << "A DefaultComponentInstaller has been created but " | |
48 << "has no installer traits."; | |
49 return; | |
50 } | |
51 task_runner_->PostTask( | |
52 FROM_HERE, | |
53 base::Bind(&DefaultComponentInstaller::StartRegistration, | |
54 base::Unretained(this), | |
55 cus)); | |
56 } | |
57 | |
58 void DefaultComponentInstaller::OnUpdateError(int error) { | |
59 NOTREACHED() << "Component update error: " << error; | |
60 } | |
61 | |
62 bool DefaultComponentInstaller::InstallHelper( | |
63 const base::DictionaryValue& manifest, | |
64 const base::FilePath& unpack_path, | |
65 const base::FilePath& install_path) { | |
66 if (!base::Move(unpack_path, install_path)) | |
67 return false; | |
68 if (!installer_traits_->OnCustomInstall(manifest, install_path)) | |
69 return false; | |
70 if (!installer_traits_->VerifyInstallation(install_path)) | |
71 return false; | |
72 return true; | |
73 } | |
74 | |
75 bool DefaultComponentInstaller::Install(const base::DictionaryValue& manifest, | |
76 const base::FilePath& unpack_path) { | |
77 std::string manifest_version; | |
78 manifest.GetStringASCII("version", &manifest_version); | |
79 base::Version version(manifest_version.c_str()); | |
80 if (!version.IsValid()) | |
81 return false; | |
82 if (current_version_.CompareTo(version) > 0) | |
83 return false; | |
84 base::FilePath install_path = | |
85 installer_traits_->GetBaseDirectory().AppendASCII(version.GetString()); | |
86 if (base::PathExists(install_path)) { | |
87 if (!base::DeleteFile(install_path, true)) | |
88 return false; | |
89 } | |
90 if (!InstallHelper(manifest, unpack_path, install_path)) { | |
91 base::DeleteFile(install_path, true); | |
92 return false; | |
93 } | |
94 current_version_ = version; | |
95 // TODO(ddorwin): Change the parameter to scoped_ptr<base::DictionaryValue> | |
96 // so we can avoid this DeepCopy. | |
97 current_manifest_.reset(manifest.DeepCopy()); | |
98 scoped_ptr<base::DictionaryValue> manifest_copy( | |
99 current_manifest_->DeepCopy()); | |
100 main_task_runner_->PostTask( | |
101 FROM_HERE, | |
102 base::Bind(&ComponentInstallerTraits::ComponentReady, | |
103 base::Unretained(installer_traits_.get()), | |
104 current_version_, | |
105 GetInstallDirectory(), | |
106 base::Passed(&manifest_copy))); | |
107 return true; | |
108 } | |
109 | |
110 bool DefaultComponentInstaller::GetInstalledFile( | |
111 const std::string& file, | |
112 base::FilePath* installed_file) { | |
113 if (current_version_.Equals(base::Version(kNullVersion))) | |
114 return false; // No component has been installed yet. | |
115 | |
116 *installed_file = installer_traits_->GetBaseDirectory() | |
117 .AppendASCII(current_version_.GetString()) | |
118 .AppendASCII(file); | |
119 return true; | |
120 } | |
121 | |
122 void DefaultComponentInstaller::StartRegistration(ComponentUpdateService* cus) { | |
123 DCHECK(task_runner_); | |
124 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | |
125 base::FilePath base_dir = installer_traits_->GetBaseDirectory(); | |
126 if (!base::PathExists(base_dir) && !base::CreateDirectory(base_dir)) { | |
127 NOTREACHED() << "Could not create the base directory for " | |
128 << installer_traits_->GetName() << " (" | |
129 << base_dir.MaybeAsASCII() << ")."; | |
130 return; | |
131 } | |
132 | |
133 base::FilePath latest_dir; | |
134 base::Version latest_version(kNullVersion); | |
135 std::vector<base::FilePath> older_dirs; | |
136 bool found = false; | |
137 base::FileEnumerator file_enumerator( | |
138 base_dir, false, base::FileEnumerator::DIRECTORIES); | |
139 for (base::FilePath path = file_enumerator.Next(); | |
140 !path.value().empty(); | |
141 path = file_enumerator.Next()) { | |
142 base::Version version(path.BaseName().MaybeAsASCII()); | |
143 // Ignore folders that don't have valid version names. These folders are not | |
144 // managed by component installer so do not try to remove them. | |
145 if (!version.IsValid()) | |
146 continue; | |
147 if (!installer_traits_->VerifyInstallation(path)) { | |
148 older_dirs.push_back(path); | |
149 continue; | |
150 } | |
151 if (found) { | |
152 if (version.CompareTo(latest_version) > 0) { | |
153 older_dirs.push_back(latest_dir); | |
154 latest_dir = path; | |
155 latest_version = version; | |
156 } else { | |
157 older_dirs.push_back(path); | |
158 } | |
159 } else { | |
160 latest_dir = path; | |
161 latest_version = version; | |
162 found = true; | |
163 } | |
164 } | |
165 | |
166 if (found) { | |
167 current_manifest_ = ReadManifest(latest_dir); | |
168 if (current_manifest_) { | |
169 current_version_ = latest_version; | |
170 // TODO(ddorwin): Remove these members and pass them directly to | |
171 // FinishRegistration(). | |
172 base::ReadFileToString(latest_dir.AppendASCII("manifest.fingerprint"), | |
173 ¤t_fingerprint_); | |
174 } else { | |
175 // If the manifest can't be read, mark the directory for deletion and | |
176 // continue as if there were no versioned directories at all. | |
177 DLOG(ERROR) << "Failed to read manifest for " | |
178 << installer_traits_->GetName() << " (" | |
179 << base_dir.MaybeAsASCII() << ")."; | |
180 older_dirs.push_back(latest_dir); | |
181 } | |
182 } | |
183 | |
184 // Remove older versions of the component. None should be in use during | |
185 // browser startup. | |
186 for (std::vector<base::FilePath>::iterator iter = older_dirs.begin(); | |
187 iter != older_dirs.end(); | |
188 ++iter) { | |
189 base::DeleteFile(*iter, true); | |
190 } | |
191 | |
192 main_task_runner_->PostTask( | |
193 FROM_HERE, | |
194 base::Bind(&DefaultComponentInstaller::FinishRegistration, | |
195 base::Unretained(this), | |
196 cus)); | |
197 } | |
198 | |
199 base::FilePath DefaultComponentInstaller::GetInstallDirectory() { | |
200 return installer_traits_->GetBaseDirectory() | |
201 .AppendASCII(current_version_.GetString()); | |
202 } | |
203 | |
204 void DefaultComponentInstaller::FinishRegistration( | |
205 ComponentUpdateService* cus) { | |
206 DCHECK(thread_checker_.CalledOnValidThread()); | |
207 if (installer_traits_->CanAutoUpdate()) { | |
208 CrxComponent crx; | |
209 crx.name = installer_traits_->GetName(); | |
210 crx.installer = this; | |
211 crx.version = current_version_; | |
212 crx.fingerprint = current_fingerprint_; | |
213 installer_traits_->GetHash(&crx.pk_hash); | |
214 ComponentUpdateService::Status status = cus->RegisterComponent(crx); | |
215 if (status != ComponentUpdateService::kOk && | |
216 status != ComponentUpdateService::kReplaced) { | |
217 NOTREACHED() << "Component registration failed for " | |
218 << installer_traits_->GetName(); | |
219 return; | |
220 } | |
221 } | |
222 | |
223 if (current_version_.CompareTo(base::Version(kNullVersion)) > 0) { | |
224 scoped_ptr<base::DictionaryValue> manifest_copy( | |
225 current_manifest_->DeepCopy()); | |
226 installer_traits_->ComponentReady( | |
227 current_version_, GetInstallDirectory(), manifest_copy.Pass()); | |
228 } | |
229 } | |
230 | |
231 } // namespace component_updater | |
OLD | NEW |