// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/media_capabilities/media_capabilities.h"
#include "testing/libfuzzer/proto/lpm_interface.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_audio_configuration.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_key_system_track_configuration.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_media_capabilities_key_system_configuration.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_media_decoding_configuration.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_media_encoding_configuration.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/navigator.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
#include "third_party/blink/renderer/modules/media_capabilities/fuzzer_media_configuration.pb.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
#include "third_party/blink/renderer/platform/testing/task_environment.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
#include "third_party/protobuf/src/google/protobuf/repeated_field.h"
namespace blink {
String MediaKeysRequirementToString(
mc_fuzzer::MediaConfigProto_KeySystemConfig_MediaKeysRequirement
proto_requirement) {
switch (proto_requirement) {
case mc_fuzzer::
MediaConfigProto_KeySystemConfig_MediaKeysRequirement_REQUIRED:
return "required";
case mc_fuzzer::
MediaConfigProto_KeySystemConfig_MediaKeysRequirement_NOT_REQUIRED:
return "optional";
case mc_fuzzer::
MediaConfigProto_KeySystemConfig_MediaKeysRequirement_NOT_ALLOWED:
return "not-allowed";
}
return "";
}
Vector<String> MediaSessionTypeToVector(
const ::google::protobuf::RepeatedField<int>& proto_session_types) {
Vector<String> result;
for (auto& proto_session_type : proto_session_types) {
String session_type;
switch (proto_session_type) {
case mc_fuzzer::
MediaConfigProto_KeySystemConfig_MediaKeySessionType_TEMPORARY:
session_type = "temporary";
break;
case mc_fuzzer::
MediaConfigProto_KeySystemConfig_MediaKeySessionType_PERSISTENT_LICENSE:
session_type = "persistent-license";
break;
}
result.push_back(session_type);
}
return result;
}
template <class T>
T* MakeConfiguration(const mc_fuzzer::MediaConfigProto& proto) {
Persistent<T> config = T::Create();
if (proto.has_video()) {
config->setVideo(VideoConfiguration::Create());
config->video()->setContentType(proto.video().content_type().c_str());
config->video()->setWidth(proto.video().width());
config->video()->setHeight(proto.video().height());
config->video()->setBitrate(proto.video().bitrate());
config->video()->setFramerate(proto.video().framerate());
config->video()->setSpatialScalability(proto.video().spatial_scalability());
config->video()->setScalabilityMode(
proto.video().scalability_mode().c_str());
}
if (proto.has_audio()) {
config->setAudio(AudioConfiguration::Create());
config->audio()->setContentType(proto.audio().content_type().c_str());
config->audio()->setChannels(proto.audio().channels().c_str());
config->audio()->setBitrate(proto.audio().bitrate());
config->audio()->setSamplerate(proto.audio().samplerate());
}
switch (proto.type()) {
case mc_fuzzer::MediaConfigProto_MediaType_DECODING_FILE:
config->setType("file");
break;
case mc_fuzzer::MediaConfigProto_MediaType_DECODING_MEDIA_SOURCE:
config->setType("media-source");
break;
case mc_fuzzer::MediaConfigProto_MediaType_DECODING_WEBRTC:
case mc_fuzzer::MediaConfigProto_MediaType_ENCODING_WEBRTC:
config->setType("webrtc");
break;
}
return config;
}
void AddDecodingSpecificConfiguration(const mc_fuzzer::MediaConfigProto& proto,
MediaDecodingConfiguration* config) {
if (proto.has_key_system_config()) {
config->setKeySystemConfiguration(
MediaCapabilitiesKeySystemConfiguration::Create());
config->keySystemConfiguration()->setKeySystem(
String::FromUTF8(proto.key_system_config().key_system().c_str()));
config->keySystemConfiguration()->setInitDataType(
String::FromUTF8(proto.key_system_config().init_data_type().c_str()));
config->keySystemConfiguration()->setDistinctiveIdentifier(
MediaKeysRequirementToString(
proto.key_system_config().distinctive_identifier()));
config->keySystemConfiguration()->setPersistentState(
MediaKeysRequirementToString(
proto.key_system_config().persistent_state()));
config->keySystemConfiguration()->setSessionTypes(
MediaSessionTypeToVector(proto.key_system_config().session_types()));
if (proto.key_system_config().has_key_system_audio_config()) {
config->keySystemConfiguration()->setAudio(
KeySystemTrackConfiguration::Create());
config->keySystemConfiguration()->audio()->setRobustness(
String::FromUTF8(proto.key_system_config()
.key_system_audio_config()
.robustness()
.c_str()));
}
if (proto.key_system_config().has_key_system_video_config()) {
config->keySystemConfiguration()->setVideo(
KeySystemTrackConfiguration::Create());
config->keySystemConfiguration()->video()->setRobustness(
String::FromUTF8(proto.key_system_config()
.key_system_video_config()
.robustness()
.c_str()));
}
}
}
DEFINE_TEXT_PROTO_FUZZER(const mc_fuzzer::MediaConfigProto& proto) {
static BlinkFuzzerTestSupport test_support = BlinkFuzzerTestSupport();
test::TaskEnvironment task_environment;
auto page_holder = std::make_unique<DummyPageHolder>();
page_holder->GetFrame().GetSettings()->SetScriptEnabled(true);
ScriptState* script_state =
ToScriptStateForMainWorld(&page_holder->GetFrame());
ScriptState::Scope scope(script_state);
auto* media_capabilities = MediaCapabilities::mediaCapabilities(
*page_holder->GetFrame().DomWindow()->navigator());
switch (proto.type()) {
case mc_fuzzer::MediaConfigProto_MediaType_DECODING_FILE:
case mc_fuzzer::MediaConfigProto_MediaType_DECODING_MEDIA_SOURCE:
case mc_fuzzer::MediaConfigProto_MediaType_DECODING_WEBRTC: {
auto* config = MakeConfiguration<MediaDecodingConfiguration>(proto);
AddDecodingSpecificConfiguration(proto, config);
media_capabilities->decodingInfo(script_state, config,
IGNORE_EXCEPTION_FOR_TESTING);
} break;
case mc_fuzzer::MediaConfigProto_MediaType_ENCODING_WEBRTC: {
auto* config = MakeConfiguration<MediaEncodingConfiguration>(proto);
media_capabilities->encodingInfo(script_state, config,
IGNORE_EXCEPTION_FOR_TESTING);
} break;
}
// Request a V8 GC. Oilpan will be invoked by the GC epilogue.
//
// Multiple GCs may be required to ensure everything is collected (due to
// a chain of persistent handles), so some objects may not be collected until
// a subsequent iteration. This is slow enough as is, so we compromise on one
// major GC, as opposed to the 5 used in V8GCController for unit tests.
script_state->GetIsolate()->RequestGarbageCollectionForTesting(
v8::Isolate::kFullGarbageCollection);
}
} // namespace blink