1 // Copyright 2017 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/vr/assets_loader.h"
6
7 #include "base/bind.h"
8 #include "base/files/file_util.h"
9 #include "base/memory/singleton.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/task/task_traits.h"
12 #include "base/task/thread_pool.h"
13 #include "base/threading/thread_task_runner_handle.h"
14 #include "base/values.h"
15 #include "chrome/browser/vr/model/assets.h"
16 #include "chrome/browser/vr/vr_buildflags.h"
17 #include "content/public/browser/browser_task_traits.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "media/audio/wav_audio_handler.h"
20 #include "third_party/skia/include/core/SkBitmap.h"
21 #include "ui/gfx/codec/jpeg_codec.h"
22 #include "ui/gfx/codec/png_codec.h"
23
24 namespace vr {
25
26 namespace {
27
28 constexpr char kMinVersionWithGradients[] = "1.1";
29 constexpr char kMinVersionWithSounds[] = "2.0";
30 constexpr char kMinVersionWithInactiveButtonClickSound[] = "2.2";
31
32 static const base::FilePath::CharType kBackgroundBaseFilename[] =
33 FILE_PATH_LITERAL("background");
34 static const base::FilePath::CharType kNormalGradientBaseFilename[] =
35 FILE_PATH_LITERAL("normal_gradient");
36 static const base::FilePath::CharType kIncognitoGradientBaseFilename[] =
37 FILE_PATH_LITERAL("incognito_gradient");
38 static const base::FilePath::CharType kFullscreenGradientBaseFilename[] =
39 FILE_PATH_LITERAL("fullscreen_gradient");
40 static const base::FilePath::CharType kPngExtension[] =
41 FILE_PATH_LITERAL("png");
42 static const base::FilePath::CharType kJpegExtension[] =
43 FILE_PATH_LITERAL("jpeg");
44
45 static const base::FilePath::CharType kButtonHoverSoundFilename[] =
46 FILE_PATH_LITERAL("button_hover.wav");
47 static const base::FilePath::CharType kButtonClickSoundFilename[] =
48 FILE_PATH_LITERAL("button_click.wav");
49 static const base::FilePath::CharType kBackButtonClickSoundFilename[] =
50 FILE_PATH_LITERAL("back_button_click.wav");
51 static const base::FilePath::CharType kInactiveButtonClickSoundFilename[] =
52 FILE_PATH_LITERAL("inactive_button_click.wav");
53
54 } // namespace
55
56 struct AssetsLoaderSingletonTrait
57 : public base::DefaultSingletonTraits<AssetsLoader> {
Newvr::AssetsLoaderSingletonTrait58 static AssetsLoader* New() { return new AssetsLoader(); }
Deletevr::AssetsLoaderSingletonTrait59 static void Delete(AssetsLoader* assets) { delete assets; }
60 };
61
62 // static
GetInstance()63 AssetsLoader* AssetsLoader::GetInstance() {
64 return base::Singleton<AssetsLoader, AssetsLoaderSingletonTrait>::get();
65 }
66
67 // static
MinVersionWithGradients()68 base::Version AssetsLoader::MinVersionWithGradients() {
69 return base::Version(kMinVersionWithGradients);
70 }
71
72 // static
AssetsSupported()73 bool AssetsLoader::AssetsSupported() {
74 #if BUILDFLAG(USE_VR_ASSETS_COMPONENT)
75 return true;
76 #else // BUILDFLAG(USE_VR_ASSETS_COMPONENT)
77 return false;
78 #endif // BUILDFLAG(USE_VR_ASSETS_COMPONENT)
79 }
80
OnComponentReady(const base::Version & version,const base::FilePath & install_dir,std::unique_ptr<base::DictionaryValue> manifest)81 void AssetsLoader::OnComponentReady(
82 const base::Version& version,
83 const base::FilePath& install_dir,
84 std::unique_ptr<base::DictionaryValue> manifest) {
85 main_thread_task_runner_->PostTask(
86 FROM_HERE,
87 base::BindOnce(&AssetsLoader::OnComponentReadyInternal,
88 weak_ptr_factory_.GetWeakPtr(), version, install_dir));
89 }
90
Load(OnAssetsLoadedCallback on_loaded)91 void AssetsLoader::Load(OnAssetsLoadedCallback on_loaded) {
92 main_thread_task_runner_->PostTask(
93 FROM_HERE, base::BindOnce(&AssetsLoader::LoadInternal,
94 weak_ptr_factory_.GetWeakPtr(),
95 base::ThreadTaskRunnerHandle::Get(),
96 std::move(on_loaded)));
97 }
98
ComponentReady()99 bool AssetsLoader::ComponentReady() {
100 DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
101 return component_ready_;
102 }
103
SetOnComponentReadyCallback(const base::RepeatingCallback<void ()> & on_component_ready)104 void AssetsLoader::SetOnComponentReadyCallback(
105 const base::RepeatingCallback<void()>& on_component_ready) {
106 DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
107 on_component_ready_callback_ = on_component_ready;
108 }
109
LoadImage(const base::FilePath & component_install_dir,const base::FilePath::CharType * base_file,std::unique_ptr<SkBitmap> * out_image)110 AssetsLoadStatus LoadImage(const base::FilePath& component_install_dir,
111 const base::FilePath::CharType* base_file,
112 std::unique_ptr<SkBitmap>* out_image) {
113 bool is_png = false;
114 std::string encoded_file_content;
115
116 base::FilePath file_path = component_install_dir.Append(base_file);
117
118 if (base::PathExists(file_path.AddExtension(kPngExtension))) {
119 file_path = file_path.AddExtension(kPngExtension);
120 is_png = true;
121 } else if (base::PathExists(file_path.AddExtension(kJpegExtension))) {
122 file_path = file_path.AddExtension(kJpegExtension);
123 } else {
124 return AssetsLoadStatus::kNotFound;
125 }
126
127 if (!base::ReadFileToString(file_path, &encoded_file_content)) {
128 return AssetsLoadStatus::kParseFailure;
129 }
130
131 if (is_png) {
132 (*out_image) = std::make_unique<SkBitmap>();
133 if (!gfx::PNGCodec::Decode(
134 reinterpret_cast<const unsigned char*>(encoded_file_content.data()),
135 encoded_file_content.size(), out_image->get())) {
136 out_image->reset();
137 }
138 } else {
139 (*out_image) = gfx::JPEGCodec::Decode(
140 reinterpret_cast<const unsigned char*>(encoded_file_content.data()),
141 encoded_file_content.size());
142 }
143
144 if (!out_image->get()) {
145 return AssetsLoadStatus::kInvalidContent;
146 }
147
148 return AssetsLoadStatus::kSuccess;
149 }
150
LoadSound(const base::FilePath & component_install_dir,const base::FilePath::CharType * file_name,std::unique_ptr<std::string> * out_buffer)151 AssetsLoadStatus LoadSound(const base::FilePath& component_install_dir,
152 const base::FilePath::CharType* file_name,
153 std::unique_ptr<std::string>* out_buffer) {
154 base::FilePath file_path = component_install_dir.Append(file_name);
155 if (!base::PathExists(file_path)) {
156 return AssetsLoadStatus::kNotFound;
157 }
158
159 auto buffer = std::make_unique<std::string>();
160 if (!base::ReadFileToString(file_path, buffer.get())) {
161 return AssetsLoadStatus::kParseFailure;
162 }
163
164 if (!media::WavAudioHandler::Create(*buffer)) {
165 return AssetsLoadStatus::kInvalidContent;
166 }
167
168 *out_buffer = std::move(buffer);
169 return AssetsLoadStatus::kSuccess;
170 }
171
172 // static
LoadAssetsTask(scoped_refptr<base::SingleThreadTaskRunner> task_runner,const base::Version & component_version,const base::FilePath & component_install_dir,OnAssetsLoadedCallback on_loaded)173 void AssetsLoader::LoadAssetsTask(
174 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
175 const base::Version& component_version,
176 const base::FilePath& component_install_dir,
177 OnAssetsLoadedCallback on_loaded) {
178 auto assets = std::make_unique<Assets>();
179 AssetsLoadStatus status = AssetsLoadStatus::kSuccess;
180
181 status = LoadImage(component_install_dir, kBackgroundBaseFilename,
182 &assets->background);
183
184 if (component_version >= AssetsLoader::MinVersionWithGradients()) {
185 if (status == AssetsLoadStatus::kSuccess) {
186 status = LoadImage(component_install_dir, kNormalGradientBaseFilename,
187 &assets->normal_gradient);
188 }
189 if (status == AssetsLoadStatus::kSuccess) {
190 status = LoadImage(component_install_dir, kIncognitoGradientBaseFilename,
191 &assets->incognito_gradient);
192 }
193 if (status == AssetsLoadStatus::kSuccess) {
194 status = LoadImage(component_install_dir, kFullscreenGradientBaseFilename,
195 &assets->fullscreen_gradient);
196 }
197 }
198
199 std::vector<std::tuple<const char*, const base::FilePath::CharType*,
200 std::unique_ptr<std::string>*>>
201 sounds = {{kMinVersionWithSounds, kButtonHoverSoundFilename,
202 &assets->button_hover_sound},
203 {kMinVersionWithSounds, kButtonClickSoundFilename,
204 &assets->button_click_sound},
205 {kMinVersionWithSounds, kBackButtonClickSoundFilename,
206 &assets->back_button_click_sound},
207 {kMinVersionWithInactiveButtonClickSound,
208 kInactiveButtonClickSoundFilename,
209 &assets->inactive_button_click_sound}};
210
211 auto sounds_iter = sounds.begin();
212 while (status == AssetsLoadStatus::kSuccess && sounds_iter != sounds.end()) {
213 const char* min_version;
214 const base::FilePath::CharType* file_name;
215 std::unique_ptr<std::string>* data;
216 std::tie(min_version, file_name, data) = *sounds_iter;
217 if (component_version >= base::Version(min_version)) {
218 status = LoadSound(component_install_dir, file_name, data);
219 }
220 sounds_iter++;
221 }
222
223 if (status != AssetsLoadStatus::kSuccess) {
224 assets.reset();
225 }
226
227 task_runner->PostTask(
228 FROM_HERE, base::BindOnce(std::move(on_loaded), status, std::move(assets),
229 component_version));
230 }
231
AssetsLoader()232 AssetsLoader::AssetsLoader()
233 : main_thread_task_runner_(content::GetUIThreadTaskRunner({})) {
234 DCHECK(main_thread_task_runner_.get());
235 }
236
237 AssetsLoader::~AssetsLoader() = default;
238
OnComponentReadyInternal(const base::Version & version,const base::FilePath & install_dir)239 void AssetsLoader::OnComponentReadyInternal(const base::Version& version,
240 const base::FilePath& install_dir) {
241 DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
242 component_version_ = version;
243 component_install_dir_ = install_dir;
244 component_ready_ = true;
245 if (on_component_ready_callback_) {
246 on_component_ready_callback_.Run();
247 }
248 }
249
LoadInternal(scoped_refptr<base::SingleThreadTaskRunner> task_runner,OnAssetsLoadedCallback on_loaded)250 void AssetsLoader::LoadInternal(
251 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
252 OnAssetsLoadedCallback on_loaded) {
253 DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
254 DCHECK(component_ready_);
255 base::ThreadPool::PostTask(
256 FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
257 base::BindOnce(&AssetsLoader::LoadAssetsTask, task_runner,
258 component_version_, component_install_dir_,
259 std::move(on_loaded)));
260 }
261
262 } // namespace vr
263