chromium/chrome/browser/ash/system_web_apps/apps/media_app/media_app_guest_ui_config.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/system_web_apps/apps/media_app/media_app_guest_ui_config.h"

#include <memory>

#include "ash/constants/ash_features.h"
#include "ash/webui/media_app_ui/url_constants.h"
#include "base/version.h"
#include "chrome/browser/accessibility/media_app/ax_media_app_handler_factory.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/ash/app_list/arc/arc_app_utils.h"
#include "chrome/browser/ash/mahi/media_app/mahi_media_app_handler_factory.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/webui_url_constants.h"
#include "chromeos/components/mahi/public/cpp/mahi_manager.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/search_engines/template_url_service.h"
#include "components/services/app_service/public/cpp/app_registry_cache.h"
#include "components/services/app_service/public/cpp/types_util.h"
#include "components/version_info/channel.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/browser/web_ui_data_source.h"

namespace {

bool PhotosIntegrationSupported(const apps::AppUpdate& update) {
  constexpr char kMinPhotosVersion[] = "6.12";
  if (!apps_util::IsInstalled(update.Readiness())) {
    return false;
  }

  auto photos_version = base::Version(update.Version());
  if (!photos_version.IsValid()) {
    // There are two reasons why a version string from Photos will be "invalid",
    // i.e. cannot be parsed into a concatenation of numbers and dots:
    // 1. The version string is "DEVELOPMENT", which is used by Photos engineers
    // for unreleased builds, or
    // 2. The version is suffixed with e.g. "-dogfood" or "-release", which is
    // the new standard for Photos version strings.
    // Version suffixes (2) were introduced a few months after version 6.12 was
    // released, so we can conclude "invalid" strings are post-6.12 and
    // therefore are supported for the Media App <-> Photos integration. It is
    // safer to assume "invalid" versions are supported rather than implementing
    // special handling of them to e.g. strip suffixes since there are no
    // guarantees the version strings won't change again in the future.
    return true;
  }

  return photos_version >= base::Version(kMinPhotosVersion);
}

bool IsLensInGalleryEnabled(Profile* profile, PrefService* pref_service) {
  if (!base::FeatureList::IsEnabled(ash::features::kMediaAppLens)) {
    return false;
  }
  if (!pref_service->GetBoolean(prefs::kMediaAppLensEnabled)) {
    return false;
  }

  const TemplateURLService* service =
      TemplateURLServiceFactory::GetForProfile(profile);
  const TemplateURL* default_url = service->GetDefaultSearchProvider();
  return default_url &&
         default_url->url_ref().HasGoogleBaseURLs(service->search_terms_data());
}

}  // namespace

ChromeMediaAppGuestUIDelegate::ChromeMediaAppGuestUIDelegate() = default;

void ChromeMediaAppGuestUIDelegate::RegisterProfilePrefs(
    PrefRegistrySimple* registry) {
  registry->RegisterBooleanPref(prefs::kMediaAppLensEnabled, true);
}

void ChromeMediaAppGuestUIDelegate::PopulateLoadTimeData(
    content::WebUI* web_ui,
    content::WebUIDataSource* source) {
  Profile* profile = Profile::FromWebUI(web_ui);
  PrefService* pref_service = profile->GetPrefs();
  apps::AppRegistryCache& app_registry_cache =
      apps::AppServiceProxyFactory::GetForProfile(profile)->AppRegistryCache();

  bool photos_integration_supported = false;
  app_registry_cache.ForOneApp(
      arc::kGooglePhotosAppId,
      [&photos_integration_supported](const apps::AppUpdate& update) {
        photos_integration_supported = PhotosIntegrationSupported(update);
      });

  source->AddString("appLocale", g_browser_process->GetApplicationLocale());
  source->AddBoolean("lensInGallery",
                     IsLensInGalleryEnabled(profile, pref_service));
  source->AddBoolean("pdfReadonly",
                     !pref_service->GetBoolean(prefs::kPdfAnnotationsEnabled));
  version_info::Channel channel = chrome::GetChannel();
  source->AddBoolean("colorThemes", true);
  source->AddBoolean("photosAvailableForImage", photos_integration_supported);
  source->AddBoolean("photosAvailableForVideo", photos_integration_supported);

  // TODO(b:356518781): rename the boolean to e.g. pdfContextMenu.
  // If true, show a context menu on right click on PDF surface. And if Mahi
  // message pipe is connected (see `CreateAndBindMahiHandler` below), also show
  // the Mahi card for the user to use HelpMeRead feature for the PDF.
  source->AddBoolean(
      "pdfMahi", base::FeatureList::IsEnabled(ash::features::kMediaAppPdfMahi));

  source->AddBoolean("flagsMenu", channel != version_info::Channel::BETA &&
                                      channel != version_info::Channel::STABLE);
  source->AddBoolean("isDevChannel", channel == version_info::Channel::DEV);
}

void ChromeMediaAppGuestUIDelegate::CreateAndBindOcrHandler(
    content::BrowserContext& context,
    gfx::NativeWindow native_window,
    mojo::PendingReceiver<ash::media_app_ui::mojom::OcrUntrustedPageHandler>
        receiver,
    mojo::PendingRemote<ash::media_app_ui::mojom::OcrUntrustedPage> page) {
  ash::AXMediaAppHandlerFactory::GetInstance()
      ->CreateAXMediaAppUntrustedHandler(context, native_window,
                                         std::move(receiver), std::move(page));
}

void ChromeMediaAppGuestUIDelegate::CreateAndBindMahiHandler(
    mojo::PendingReceiver<ash::media_app_ui::mojom::MahiUntrustedPageHandler>
        receiver,
    mojo::PendingRemote<ash::media_app_ui::mojom::MahiUntrustedPage> page,
    const std::string& file_name,
    aura::Window* window) {
  if (chromeos::MahiManager::Get() &&
      chromeos::MahiManager::Get()->IsEnabled()) {
    ash::MahiMediaAppHandlerFactory::GetInstance()
        ->CreateMahiMediaAppUntrustedHandler(
            std::move(receiver), std::move(page), file_name, window);
  }
}

MediaAppGuestUIConfig::MediaAppGuestUIConfig()
    : WebUIConfig(content::kChromeUIUntrustedScheme,
                  ash::kChromeUIMediaAppHost) {}

MediaAppGuestUIConfig::~MediaAppGuestUIConfig() = default;

std::unique_ptr<content::WebUIController>
MediaAppGuestUIConfig::CreateWebUIController(content::WebUI* web_ui,
                                             const GURL& url) {
  auto delegate = std::make_unique<ChromeMediaAppGuestUIDelegate>();
  return std::make_unique<ash::MediaAppGuestUI>(web_ui, std::move(delegate));
}