| // Copyright 2017 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/vr/assets_loader.h" | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/files/file_util.h" | 
 | #include "base/memory/singleton.h" | 
 | #include "base/single_thread_task_runner.h" | 
 | #include "base/task/task_traits.h" | 
 | #include "base/task/thread_pool.h" | 
 | #include "base/threading/thread_task_runner_handle.h" | 
 | #include "base/values.h" | 
 | #include "chrome/browser/vr/model/assets.h" | 
 | #include "chrome/browser/vr/vr_buildflags.h" | 
 | #include "content/public/browser/browser_task_traits.h" | 
 | #include "content/public/browser/browser_thread.h" | 
 | #include "media/audio/wav_audio_handler.h" | 
 | #include "third_party/skia/include/core/SkBitmap.h" | 
 | #include "ui/gfx/codec/jpeg_codec.h" | 
 | #include "ui/gfx/codec/png_codec.h" | 
 |  | 
 | namespace vr { | 
 |  | 
 | namespace { | 
 |  | 
 | constexpr char kMinVersionWithGradients[] = "1.1"; | 
 | constexpr char kMinVersionWithSounds[] = "2.0"; | 
 | constexpr char kMinVersionWithInactiveButtonClickSound[] = "2.2"; | 
 |  | 
 | static const base::FilePath::CharType kBackgroundBaseFilename[] = | 
 |     FILE_PATH_LITERAL("background"); | 
 | static const base::FilePath::CharType kNormalGradientBaseFilename[] = | 
 |     FILE_PATH_LITERAL("normal_gradient"); | 
 | static const base::FilePath::CharType kIncognitoGradientBaseFilename[] = | 
 |     FILE_PATH_LITERAL("incognito_gradient"); | 
 | static const base::FilePath::CharType kFullscreenGradientBaseFilename[] = | 
 |     FILE_PATH_LITERAL("fullscreen_gradient"); | 
 | static const base::FilePath::CharType kPngExtension[] = | 
 |     FILE_PATH_LITERAL("png"); | 
 | static const base::FilePath::CharType kJpegExtension[] = | 
 |     FILE_PATH_LITERAL("jpeg"); | 
 |  | 
 | static const base::FilePath::CharType kButtonHoverSoundFilename[] = | 
 |     FILE_PATH_LITERAL("button_hover.wav"); | 
 | static const base::FilePath::CharType kButtonClickSoundFilename[] = | 
 |     FILE_PATH_LITERAL("button_click.wav"); | 
 | static const base::FilePath::CharType kBackButtonClickSoundFilename[] = | 
 |     FILE_PATH_LITERAL("back_button_click.wav"); | 
 | static const base::FilePath::CharType kInactiveButtonClickSoundFilename[] = | 
 |     FILE_PATH_LITERAL("inactive_button_click.wav"); | 
 |  | 
 | }  // namespace | 
 |  | 
 | struct AssetsLoaderSingletonTrait | 
 |     : public base::DefaultSingletonTraits<AssetsLoader> { | 
 |   static AssetsLoader* New() { return new AssetsLoader(); } | 
 |   static void Delete(AssetsLoader* assets) { delete assets; } | 
 | }; | 
 |  | 
 | // static | 
 | AssetsLoader* AssetsLoader::GetInstance() { | 
 |   return base::Singleton<AssetsLoader, AssetsLoaderSingletonTrait>::get(); | 
 | } | 
 |  | 
 | // static | 
 | base::Version AssetsLoader::MinVersionWithGradients() { | 
 |   return base::Version(kMinVersionWithGradients); | 
 | } | 
 |  | 
 | // static | 
 | bool AssetsLoader::AssetsSupported() { | 
 | #if BUILDFLAG(USE_VR_ASSETS_COMPONENT) | 
 |   return true; | 
 | #else   // BUILDFLAG(USE_VR_ASSETS_COMPONENT) | 
 |   return false; | 
 | #endif  // BUILDFLAG(USE_VR_ASSETS_COMPONENT) | 
 | } | 
 |  | 
 | void AssetsLoader::OnComponentReady( | 
 |     const base::Version& version, | 
 |     const base::FilePath& install_dir, | 
 |     std::unique_ptr<base::DictionaryValue> manifest) { | 
 |   main_thread_task_runner_->PostTask( | 
 |       FROM_HERE, | 
 |       base::BindOnce(&AssetsLoader::OnComponentReadyInternal, | 
 |                      weak_ptr_factory_.GetWeakPtr(), version, install_dir)); | 
 | } | 
 |  | 
 | void AssetsLoader::Load(OnAssetsLoadedCallback on_loaded) { | 
 |   main_thread_task_runner_->PostTask( | 
 |       FROM_HERE, base::BindOnce(&AssetsLoader::LoadInternal, | 
 |                                 weak_ptr_factory_.GetWeakPtr(), | 
 |                                 base::ThreadTaskRunnerHandle::Get(), | 
 |                                 std::move(on_loaded))); | 
 | } | 
 |  | 
 | bool AssetsLoader::ComponentReady() { | 
 |   DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); | 
 |   return component_ready_; | 
 | } | 
 |  | 
 | void AssetsLoader::SetOnComponentReadyCallback( | 
 |     const base::RepeatingCallback<void()>& on_component_ready) { | 
 |   DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); | 
 |   on_component_ready_callback_ = on_component_ready; | 
 | } | 
 |  | 
 | AssetsLoadStatus LoadImage(const base::FilePath& component_install_dir, | 
 |                            const base::FilePath::CharType* base_file, | 
 |                            std::unique_ptr<SkBitmap>* out_image) { | 
 |   bool is_png = false; | 
 |   std::string encoded_file_content; | 
 |  | 
 |   base::FilePath file_path = component_install_dir.Append(base_file); | 
 |  | 
 |   if (base::PathExists(file_path.AddExtension(kPngExtension))) { | 
 |     file_path = file_path.AddExtension(kPngExtension); | 
 |     is_png = true; | 
 |   } else if (base::PathExists(file_path.AddExtension(kJpegExtension))) { | 
 |     file_path = file_path.AddExtension(kJpegExtension); | 
 |   } else { | 
 |     return AssetsLoadStatus::kNotFound; | 
 |   } | 
 |  | 
 |   if (!base::ReadFileToString(file_path, &encoded_file_content)) { | 
 |     return AssetsLoadStatus::kParseFailure; | 
 |   } | 
 |  | 
 |   if (is_png) { | 
 |     (*out_image) = std::make_unique<SkBitmap>(); | 
 |     if (!gfx::PNGCodec::Decode( | 
 |             reinterpret_cast<const unsigned char*>(encoded_file_content.data()), | 
 |             encoded_file_content.size(), out_image->get())) { | 
 |       out_image->reset(); | 
 |     } | 
 |   } else { | 
 |     (*out_image) = gfx::JPEGCodec::Decode( | 
 |         reinterpret_cast<const unsigned char*>(encoded_file_content.data()), | 
 |         encoded_file_content.size()); | 
 |   } | 
 |  | 
 |   if (!out_image->get()) { | 
 |     return AssetsLoadStatus::kInvalidContent; | 
 |   } | 
 |  | 
 |   return AssetsLoadStatus::kSuccess; | 
 | } | 
 |  | 
 | AssetsLoadStatus LoadSound(const base::FilePath& component_install_dir, | 
 |                            const base::FilePath::CharType* file_name, | 
 |                            std::unique_ptr<std::string>* out_buffer) { | 
 |   base::FilePath file_path = component_install_dir.Append(file_name); | 
 |   if (!base::PathExists(file_path)) { | 
 |     return AssetsLoadStatus::kNotFound; | 
 |   } | 
 |  | 
 |   auto buffer = std::make_unique<std::string>(); | 
 |   if (!base::ReadFileToString(file_path, buffer.get())) { | 
 |     return AssetsLoadStatus::kParseFailure; | 
 |   } | 
 |  | 
 |   if (!media::WavAudioHandler::Create(*buffer)) { | 
 |     return AssetsLoadStatus::kInvalidContent; | 
 |   } | 
 |  | 
 |   *out_buffer = std::move(buffer); | 
 |   return AssetsLoadStatus::kSuccess; | 
 | } | 
 |  | 
 | // static | 
 | void AssetsLoader::LoadAssetsTask( | 
 |     scoped_refptr<base::SingleThreadTaskRunner> task_runner, | 
 |     const base::Version& component_version, | 
 |     const base::FilePath& component_install_dir, | 
 |     OnAssetsLoadedCallback on_loaded) { | 
 |   auto assets = std::make_unique<Assets>(); | 
 |   AssetsLoadStatus status = AssetsLoadStatus::kSuccess; | 
 |  | 
 |   status = LoadImage(component_install_dir, kBackgroundBaseFilename, | 
 |                      &assets->background); | 
 |  | 
 |   if (component_version >= AssetsLoader::MinVersionWithGradients()) { | 
 |     if (status == AssetsLoadStatus::kSuccess) { | 
 |       status = LoadImage(component_install_dir, kNormalGradientBaseFilename, | 
 |                          &assets->normal_gradient); | 
 |     } | 
 |     if (status == AssetsLoadStatus::kSuccess) { | 
 |       status = LoadImage(component_install_dir, kIncognitoGradientBaseFilename, | 
 |                          &assets->incognito_gradient); | 
 |     } | 
 |     if (status == AssetsLoadStatus::kSuccess) { | 
 |       status = LoadImage(component_install_dir, kFullscreenGradientBaseFilename, | 
 |                          &assets->fullscreen_gradient); | 
 |     } | 
 |   } | 
 |  | 
 |   std::vector<std::tuple<const char*, const base::FilePath::CharType*, | 
 |                          std::unique_ptr<std::string>*>> | 
 |       sounds = {{kMinVersionWithSounds, kButtonHoverSoundFilename, | 
 |                  &assets->button_hover_sound}, | 
 |                 {kMinVersionWithSounds, kButtonClickSoundFilename, | 
 |                  &assets->button_click_sound}, | 
 |                 {kMinVersionWithSounds, kBackButtonClickSoundFilename, | 
 |                  &assets->back_button_click_sound}, | 
 |                 {kMinVersionWithInactiveButtonClickSound, | 
 |                  kInactiveButtonClickSoundFilename, | 
 |                  &assets->inactive_button_click_sound}}; | 
 |  | 
 |   auto sounds_iter = sounds.begin(); | 
 |   while (status == AssetsLoadStatus::kSuccess && sounds_iter != sounds.end()) { | 
 |     const char* min_version; | 
 |     const base::FilePath::CharType* file_name; | 
 |     std::unique_ptr<std::string>* data; | 
 |     std::tie(min_version, file_name, data) = *sounds_iter; | 
 |     if (component_version >= base::Version(min_version)) { | 
 |       status = LoadSound(component_install_dir, file_name, data); | 
 |     } | 
 |     sounds_iter++; | 
 |   } | 
 |  | 
 |   if (status != AssetsLoadStatus::kSuccess) { | 
 |     assets.reset(); | 
 |   } | 
 |  | 
 |   task_runner->PostTask( | 
 |       FROM_HERE, base::BindOnce(std::move(on_loaded), status, std::move(assets), | 
 |                                 component_version)); | 
 | } | 
 |  | 
 | AssetsLoader::AssetsLoader() | 
 |     : main_thread_task_runner_(content::GetUIThreadTaskRunner({})) { | 
 |   DCHECK(main_thread_task_runner_.get()); | 
 | } | 
 |  | 
 | AssetsLoader::~AssetsLoader() = default; | 
 |  | 
 | void AssetsLoader::OnComponentReadyInternal(const base::Version& version, | 
 |                                             const base::FilePath& install_dir) { | 
 |   DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); | 
 |   component_version_ = version; | 
 |   component_install_dir_ = install_dir; | 
 |   component_ready_ = true; | 
 |   if (on_component_ready_callback_) { | 
 |     on_component_ready_callback_.Run(); | 
 |   } | 
 | } | 
 |  | 
 | void AssetsLoader::LoadInternal( | 
 |     scoped_refptr<base::SingleThreadTaskRunner> task_runner, | 
 |     OnAssetsLoadedCallback on_loaded) { | 
 |   DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); | 
 |   DCHECK(component_ready_); | 
 |   base::ThreadPool::PostTask( | 
 |       FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()}, | 
 |       base::BindOnce(&AssetsLoader::LoadAssetsTask, task_runner, | 
 |                      component_version_, component_install_dir_, | 
 |                      std::move(on_loaded))); | 
 | } | 
 |  | 
 | }  // namespace vr |