| Index: third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.cpp | 
| diff --git a/third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.cpp b/third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.cpp | 
| index 38f780fb1cb3d2a01f024ac5e298c19e10dfcc35..c5088ac65ddc6ff824ab155e63b293491da98891 100644 | 
| --- a/third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.cpp | 
| +++ b/third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.cpp | 
| @@ -24,6 +24,62 @@ namespace blink { | 
|  | 
| namespace { | 
|  | 
| +constexpr const char* kApplicationMimeTypePrefix = "application/"; | 
| +constexpr const char* kAudioMimeTypePrefix = "audio/"; | 
| +constexpr const char* kVideoMimeTypePrefix = "video/"; | 
| +constexpr const char* kCodecsMimeTypeParam = "codecs"; | 
| + | 
| +bool IsValidMimeType(const String& content_type, const String& prefix) { | 
| +  ParsedContentType parsed_content_type(content_type, | 
| +                                        ParsedContentType::Mode::kStrict); | 
| +  if (!parsed_content_type.IsValid()) | 
| +    return false; | 
| + | 
| +  if (!parsed_content_type.MimeType().StartsWith(prefix) && | 
| +      !parsed_content_type.MimeType().StartsWith(kApplicationMimeTypePrefix)) { | 
| +    return false; | 
| +  } | 
| + | 
| +  if (parsed_content_type.ParameterCount() > 1) | 
| +    return false; | 
| + | 
| +  if (parsed_content_type.ParameterCount() == 1 && | 
| +      parsed_content_type.ParameterValueForName(kCodecsMimeTypeParam) | 
| +          .IsNull()) { | 
| +    return false; | 
| +  } | 
| + | 
| +  return true; | 
| +} | 
| + | 
| +bool IsValidMediaConfiguration(const MediaConfiguration& configuration) { | 
| +  return configuration.hasAudio() || configuration.hasVideo(); | 
| +} | 
| + | 
| +bool IsValidVideoConfiguration(const VideoConfiguration& configuration) { | 
| +  DCHECK(configuration.hasContentType()); | 
| + | 
| +  if (!IsValidMimeType(configuration.contentType(), kVideoMimeTypePrefix)) | 
| +    return false; | 
| + | 
| +  DCHECK(configuration.hasFramerate()); | 
| +  if (!std::isfinite(configuration.framerate()) || | 
| +      configuration.framerate() <= 0) { | 
| +    return false; | 
| +  } | 
| + | 
| +  return true; | 
| +} | 
| + | 
| +bool IsValidAudioConfiguration(const AudioConfiguration& configuration) { | 
| +  DCHECK(configuration.hasContentType()); | 
| + | 
| +  if (!IsValidMimeType(configuration.contentType(), kAudioMimeTypePrefix)) | 
| +    return false; | 
| + | 
| +  return true; | 
| +} | 
| + | 
| WebAudioConfiguration ToWebAudioConfiguration( | 
| const AudioConfiguration& configuration) { | 
| WebAudioConfiguration web_configuration; | 
| @@ -32,9 +88,7 @@ WebAudioConfiguration ToWebAudioConfiguration( | 
| DCHECK(configuration.hasContentType()); | 
| ParsedContentType parsed_content_type(configuration.contentType(), | 
| ParsedContentType::Mode::kStrict); | 
| - | 
| -  // TODO(chcunningham): Throw TypeError for invalid input. | 
| -  // DCHECK(parsed_content_type.IsValid()); | 
| +  DCHECK(parsed_content_type.IsValid()); | 
|  | 
| DEFINE_STATIC_LOCAL(const String, codecs, ("codecs")); | 
| web_configuration.mime_type = parsed_content_type.MimeType().LowerASCII(); | 
| @@ -62,9 +116,7 @@ WebVideoConfiguration ToWebVideoConfiguration( | 
| DCHECK(configuration.hasContentType()); | 
| ParsedContentType parsed_content_type(configuration.contentType(), | 
| ParsedContentType::Mode::kStrict); | 
| - | 
| -  // TODO(chcunningham): Throw TypeError for invalid input. | 
| -  // DCHECK(parsed_content_type.IsValid()); | 
| +  DCHECK(parsed_content_type.IsValid()); | 
|  | 
| DEFINE_STATIC_LOCAL(const String, codecs, ("codecs")); | 
| web_configuration.mime_type = parsed_content_type.MimeType().LowerASCII(); | 
| @@ -86,9 +138,37 @@ WebVideoConfiguration ToWebVideoConfiguration( | 
| } | 
|  | 
| WebMediaConfiguration ToWebMediaConfiguration( | 
| -    const MediaConfiguration& configuration) { | 
| +    const MediaDecodingConfiguration& configuration) { | 
| +  WebMediaConfiguration web_configuration; | 
| + | 
| +  // |type| is mandatory. | 
| +  DCHECK(configuration.hasType()); | 
| +  if (configuration.type() == "file") | 
| +    web_configuration.type = MediaConfigurationType::kFile; | 
| +  else if (configuration.type() == "media-source") | 
| +    web_configuration.type = MediaConfigurationType::kMediaSource; | 
| +  else | 
| +    NOTREACHED(); | 
| + | 
| +  if (configuration.hasAudio()) { | 
| +    web_configuration.audio_configuration = | 
| +        ToWebAudioConfiguration(configuration.audio()); | 
| +  } | 
| + | 
| +  if (configuration.hasVideo()) { | 
| +    web_configuration.video_configuration = | 
| +        ToWebVideoConfiguration(configuration.video()); | 
| +  } | 
| + | 
| +  return web_configuration; | 
| +} | 
| + | 
| +WebMediaConfiguration ToWebMediaConfiguration( | 
| +    const MediaEncodingConfiguration& configuration) { | 
| WebMediaConfiguration web_configuration; | 
|  | 
| +  // TODO(mcasas): parse and set the type for encoding. | 
| + | 
| if (configuration.hasAudio()) { | 
| web_configuration.audio_configuration = | 
| ToWebAudioConfiguration(configuration.audio()); | 
| @@ -112,8 +192,29 @@ ScriptPromise MediaCapabilities::decodingInfo( | 
| ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); | 
| ScriptPromise promise = resolver->Promise(); | 
|  | 
| -  // |type| is mandatory. | 
| -  DCHECK(configuration.hasType()); | 
| +  if (!IsValidMediaConfiguration(configuration)) { | 
| +    resolver->Reject(V8ThrowException::CreateTypeError( | 
| +        script_state->GetIsolate(), | 
| +        "The configuration dictionary has neither |video| nor |audio| " | 
| +        "specified and needs at least one of them.")); | 
| +    return promise; | 
| +  } | 
| + | 
| +  if (configuration.hasVideo() && | 
| +      !IsValidVideoConfiguration(configuration.video())) { | 
| +    resolver->Reject(V8ThrowException::CreateTypeError( | 
| +        script_state->GetIsolate(), | 
| +        "The video configuration dictionary is not valid.")); | 
| +    return promise; | 
| +  } | 
| + | 
| +  if (configuration.hasAudio() && | 
| +      !IsValidAudioConfiguration(configuration.audio())) { | 
| +    resolver->Reject(V8ThrowException::CreateTypeError( | 
| +        script_state->GetIsolate(), | 
| +        "The audio configuration dictionary is not valid.")); | 
| +    return promise; | 
| +  } | 
|  | 
| Platform::Current()->MediaCapabilitiesClient()->DecodingInfo( | 
| ToWebMediaConfiguration(configuration), | 
| @@ -129,14 +230,30 @@ ScriptPromise MediaCapabilities::encodingInfo( | 
| ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); | 
| ScriptPromise promise = resolver->Promise(); | 
|  | 
| -  if (!configuration.hasVideo() && !configuration.hasAudio()) { | 
| -    resolver->Reject(DOMException::Create( | 
| -        kSyntaxError, | 
| +  if (!IsValidMediaConfiguration(configuration)) { | 
| +    resolver->Reject(V8ThrowException::CreateTypeError( | 
| +        script_state->GetIsolate(), | 
| "The configuration dictionary has neither |video| nor |audio| " | 
| "specified and needs at least one of them.")); | 
| return promise; | 
| } | 
|  | 
| +  if (configuration.hasVideo() && | 
| +      !IsValidVideoConfiguration(configuration.video())) { | 
| +    resolver->Reject(V8ThrowException::CreateTypeError( | 
| +        script_state->GetIsolate(), | 
| +        "The video configuration dictionary is not valid.")); | 
| +    return promise; | 
| +  } | 
| + | 
| +  if (configuration.hasAudio() && | 
| +      !IsValidAudioConfiguration(configuration.audio())) { | 
| +    resolver->Reject(V8ThrowException::CreateTypeError( | 
| +        script_state->GetIsolate(), | 
| +        "The audio configuration dictionary is not valid.")); | 
| +    return promise; | 
| +  } | 
| + | 
| std::unique_ptr<WebMediaRecorderHandler> handler = | 
| Platform::Current()->CreateMediaRecorderHandler(); | 
| if (!handler) { | 
|  |