910e62b5创建于 1月15日历史提交
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "fuchsia_web/webengine/browser/web_engine_config.h"

#include <string_view>
#include <vector>

#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/logging.h"
#include "base/metrics/field_trial.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/values.h"
#include "build/build_config.h"
#include "cc/base/switches.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/embedder_support/switches.h"
#include "components/network_session_configurator/common/network_switches.h"
#include "components/viz/common/features.h"
#include "components/viz/common/switches.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "fuchsia_web/webengine/switches.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "gpu/config/gpu_switches.h"
#include "media/base/media_switches.h"
#include "services/network/public/cpp/features.h"
#include "third_party/blink/public/common/switches.h"
#include "third_party/widevine/cdm/buildflags.h"
#include "ui/display/display_switches.h"
#include "ui/gl/gl_switches.h"
#include "ui/ozone/public/ozone_switches.h"
#include "url/gurl.h"

namespace {

// Returns true if protected memory is supported. Currently we assume that it is
// supported on ARM64, but not on x64.
//
// TODO(crbug.com/42050020): Detect if protected memory is supported.
bool IsProtectedMemorySupported() {
#if defined(ARCH_CPU_ARM64)
  return true;
#else
  return false;
#endif
}

// Appends `value` to the value of `switch_name` in the `command_line`.
// The switch is assumed to consist of comma-separated values. If `switch_name`
// is already set in `command_line` then a comma will be appended, followed by
// `value`, otherwise the switch will be set to `value`.
void AppendToSwitch(std::string_view switch_name,
                    std::string_view value,
                    base::CommandLine* command_line,
                    std::string_view separator = ",") {
  if (!command_line->HasSwitch(switch_name)) {
    command_line->AppendSwitchNative(switch_name, value);
    return;
  }

  std::string new_value = base::StrCat(
      {command_line->GetSwitchValueASCII(switch_name), separator, value});
  command_line->RemoveSwitch(switch_name);
  command_line->AppendSwitchNative(switch_name, new_value);
}

bool AddCommandLineArgsFromConfig(const base::Value::Dict& config,
                                  base::CommandLine* command_line) {
  const base::Value::Dict* args = config.FindDict("command-line-args");
  if (!args) {
    return true;
  }

  static const std::string_view kAllowedArgs[] = {
      blink::switches::kGpuRasterizationMSAASampleCount,
      blink::switches::kMinHeightForGpuRasterTile,
      blink::switches::kForceGpuMemAvailableMb,
      switches::kEnableClippedImageScaling,
      switches::kEnableGpuBenchmarking,
      embedder_support::kOriginTrialPublicKey,
      embedder_support::kOriginTrialDisabledFeatures,
      switches::kDisableFeatures,
      switches::kDisableGpuWatchdog,
      switches::kDisableQuic,
      switches::kDisableMipmapGeneration,
      // TODO(crbug.com/40131115): Remove this switch from the allow-list.
      switches::kEnableCastStreamingReceiver,
      switches::kEnableFeatures,
      switches::kEnableLowEndDeviceMode,
      switches::kForceDeviceScaleFactor,
      switches::kForceGpuMemDiscardableLimitMb,
      switches::kForceMaxTextureSize,
      switches::kGoogleApiKey,
      switches::kInProcessGPU,
      switches::kMaxDecodedImageSizeMb,
      switches::kMinVideoDecoderOutputBufferSize,
      switches::kOzonePlatform,
      switches::kRendererProcessLimit,
      switches::kUseCmdDecoder,
      switches::kV,
      switches::kVModule,
      switches::kVulkanHeapMemoryLimitMb,
      switches::kVulkanSyncCpuMemoryLimitMb,
      switches::kWebglAntialiasingMode,
      switches::kWebglMSAASampleCount,
      switches::kProtectedServiceWorkers,
  };

  for (const auto arg : *args) {
    if (!base::Contains(kAllowedArgs, arg.first)) {
      // TODO(crbug.com/40662865): Increase severity and return false
      // once we have a mechanism for soft transitions of supported arguments.
      LOG(WARNING) << "Unknown command-line arg: '" << arg.first
                   << "'. Config file and WebEngine version may not match.";
      continue;
    }

    if (arg.first == switches::kEnableFeatures ||
        arg.first == switches::kDisableFeatures) {
      if (!arg.second.is_string()) {
        LOG(ERROR) << "Config command-line arg must be a string: " << arg.first;
        return false;
      }
      // Merge the features.
      AppendToSwitch(arg.first, arg.second.GetString(), command_line);
      continue;
    }

    if (command_line->HasSwitch(arg.first)) {
      // Use the existing command line value rather than override it.
      continue;
    }

    if (arg.second.is_none()) {
      command_line->AppendSwitch(arg.first);
      continue;
    }

    if (arg.second.is_string()) {
      command_line->AppendSwitchNative(arg.first, arg.second.GetString());
      continue;
    }

    LOG(ERROR) << "Config command-line arg must be a string: " << arg.first;
    return false;
  }

  // Disable kWebRtcHWDecoding by default until config-data are updated.
  // TODO(b/326282208): Remove once config-data are updated to use the new
  // feature.
  AppendToSwitch(switches::kDisableFeatures, features::kWebRtcHWDecoding.name,
                 command_line);

  // Disable kLocalNetworkAccessChecks until fuchsia-dir:// is supported.
  // TODO(crbug.com/451700528): Re-enable kLocalNetworkAccessChecks.
  AppendToSwitch(switches::kDisableFeatures,
                 network::features::kLocalNetworkAccessChecks.name,
                 command_line);

  return true;
}

// Returns a list of ContentSettingsPattern from command line
// --protected-service-workers.
std::vector<ContentSettingsPattern> GetProtectedServiceWorkers() {
  const auto tokens = base::SplitString(
      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
          switches::kProtectedServiceWorkers),
      ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
  std::vector<ContentSettingsPattern> patterns;
  for (const auto& token : tokens) {
    patterns.push_back(ContentSettingsPattern::FromString(token));
  }
  return patterns;
}

}  // namespace

bool UpdateCommandLineFromConfigFile(const base::Value::Dict& config,
                                     base::CommandLine* command_line) {
  // The FieldTrialList should be initialized only after config is loaded.
  CHECK(!base::FieldTrialList::GetInstance());

  if (!AddCommandLineArgsFromConfig(config, command_line)) {
    return false;
  }

  // The following two args are set by calling component. They are used to set
  // other flags below.
  const bool playready_enabled =
      command_line->HasSwitch(switches::kPlayreadyKeySystem);
  const bool widevine_enabled =
      command_line->HasSwitch(switches::kEnableWidevine);

  // Ignore "force-protected-video-buffers" if protected memory is not
  // supported. This is necessary to workaround https://fxbug.dev/126639.
  const bool force_protected_video_buffers =
      IsProtectedMemorySupported() &&
      config.FindBool("force-protected-video-buffers").value_or(false);

  const bool enable_protected_graphics =
      playready_enabled || widevine_enabled || force_protected_video_buffers;

  if (enable_protected_graphics) {
    command_line->AppendSwitch(switches::kEnableVulkanProtectedMemory);
    command_line->AppendSwitch(switches::kEnableProtectedVideoBuffers);
  }

  if (force_protected_video_buffers) {
    command_line->AppendSwitch(switches::kForceProtectedVideoOutputBuffers);
  }

  // TODO(crbug.com/40269624): Remove this switch once fixed.
  command_line->AppendSwitchASCII(switches::kEnableHardwareOverlays,
                                  "underlay");

  std::optional<int> max_old_space =
      config.FindInt("js-heap-max-old-space-size");
  if (max_old_space) {
    AppendToSwitch(
        blink::switches::kJavaScriptFlags,
        "--max_old_space_size=" + base::NumberToString(max_old_space.value()),
        command_line, " ");
  }

  std::optional<int> max_semi_space =
      config.FindInt("js-heap-max-semi-space-size");
  if (max_semi_space) {
    AppendToSwitch(
        blink::switches::kJavaScriptFlags,
        "--max_semi_space_size=" + base::NumberToString(max_semi_space.value()),
        command_line, " ");
  }

  return true;
}

bool IsProtectedServiceWorker(const GURL& scope) {
  static const auto protected_service_workers = GetProtectedServiceWorkers();
  for (const auto& pattern : protected_service_workers) {
    if (pattern.Matches(scope)) {
      return true;
    }
  }
  return false;
}

bool AllowNotifications(const GURL& origin) {
  // The hardware spec of smart displays is fairly low and can easily trigger
  // the teardown of service workers. Without being protected, allowing the
  // service worker using longliving functionalities like PushMessagingAPI or
  // Notification permission as the dependency of PushMessagingAPI doesn't make
  // sense.
  return IsProtectedServiceWorker(origin);
}