blob: 523af2eca0c2fb877079bb0eb0ef9befce3365eb [file] [log] [blame]
// Copyright 2016 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/presentation/presentation_request.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_presentation_source.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_union_presentationsource_usvstring.h"
#include "third_party/blink/renderer/core/execution_context/security_context.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
namespace blink {
namespace {
Member<V8UnionPresentationSourceOrUSVString> CreatePresentationSource(
const String& url) {
PresentationSource* source = PresentationSource::Create();
source->setType(V8PresentationSourceType::Enum::kUrl);
source->setUrl(url);
return MakeGarbageCollected<V8UnionPresentationSourceOrUSVString>(source);
}
Member<V8UnionPresentationSourceOrUSVString> CreateMirroringSource() {
PresentationSource* source = PresentationSource::Create();
source->setType(V8PresentationSourceType::Enum::kMirroring);
source->setAudioPlayback(V8AudioPlaybackDestination::Enum::kReceiver);
source->setLatencyHint(V8CaptureLatency::Enum::kDefault);
return MakeGarbageCollected<V8UnionPresentationSourceOrUSVString>(source);
}
HeapVector<Member<V8UnionPresentationSourceOrUSVString>> CreateUrlSources(
const WTF::Vector<String>& urls) {
HeapVector<Member<V8UnionPresentationSourceOrUSVString>> sources;
for (const String& url : urls) {
sources.push_back(
MakeGarbageCollected<V8UnionPresentationSourceOrUSVString>(url));
}
return sources;
}
TEST(PresentationRequestTest, TestSingleUrlConstructor) {
V8TestingScope scope;
PresentationRequest* request = PresentationRequest::Create(
scope.GetExecutionContext(), "https://example.com",
scope.GetExceptionState());
ASSERT_FALSE(scope.GetExceptionState().HadException());
WTF::Vector<KURL> request_urls = request->Urls();
EXPECT_EQ(static_cast<size_t>(1), request_urls.size());
EXPECT_TRUE(request_urls[0].IsValid());
EXPECT_EQ("https://example.com/", request_urls[0].GetString());
}
TEST(PresentationRequestTest, TestMultipleUrlConstructor) {
V8TestingScope scope;
HeapVector<Member<V8UnionPresentationSourceOrUSVString>> sources =
CreateUrlSources({"https://example.com", "cast://deadbeef?param=foo"});
PresentationRequest* request = PresentationRequest::Create(
scope.GetExecutionContext(), sources, scope.GetExceptionState());
ASSERT_FALSE(scope.GetExceptionState().HadException());
WTF::Vector<KURL> request_urls = request->Urls();
EXPECT_EQ(static_cast<size_t>(2), request_urls.size());
EXPECT_TRUE(request_urls[0].IsValid());
EXPECT_EQ("https://example.com/", request_urls[0].GetString());
EXPECT_TRUE(request_urls[1].IsValid());
EXPECT_EQ("cast://deadbeef?param=foo", request_urls[1].GetString());
}
TEST(PresentationRequestTest, TestMultipleUrlConstructorInvalidUrl) {
V8TestingScope scope;
HeapVector<Member<V8UnionPresentationSourceOrUSVString>> sources =
CreateUrlSources({"https://example.com", ""});
PresentationRequest::Create(scope.GetExecutionContext(), sources,
scope.GetExceptionState());
EXPECT_TRUE(scope.GetExceptionState().HadException());
EXPECT_EQ(DOMExceptionCode::kSyntaxError,
scope.GetExceptionState().CodeAs<DOMExceptionCode>());
}
TEST(PresentationRequestTest, TestMixedContentNotCheckedForNonHttpFamily) {
V8TestingScope scope(KURL("https://example.test"));
PresentationRequest* request = PresentationRequest::Create(
scope.GetExecutionContext(), "cast://deadbeef?param=foo",
scope.GetExceptionState());
ASSERT_FALSE(scope.GetExceptionState().HadException());
WTF::Vector<KURL> request_urls = request->Urls();
EXPECT_EQ(static_cast<size_t>(1), request_urls.size());
EXPECT_TRUE(request_urls[0].IsValid());
EXPECT_EQ("cast://deadbeef?param=foo", request_urls[0].GetString());
}
TEST(PresentationRequestTest, TestSingleUrlConstructorMixedContent) {
V8TestingScope scope(KURL("https://example.test"));
PresentationRequest::Create(scope.GetExecutionContext(), "http://example.com",
scope.GetExceptionState());
EXPECT_TRUE(scope.GetExceptionState().HadException());
EXPECT_EQ(DOMExceptionCode::kSecurityError,
scope.GetExceptionState().CodeAs<DOMExceptionCode>());
}
TEST(PresentationRequestTest, TestMultipleUrlConstructorMixedContent) {
V8TestingScope scope(KURL("https://example.test"));
HeapVector<Member<V8UnionPresentationSourceOrUSVString>> sources =
CreateUrlSources({"http://example.com", "https://example1.com"});
PresentationRequest::Create(scope.GetExecutionContext(), sources,
scope.GetExceptionState());
EXPECT_TRUE(scope.GetExceptionState().HadException());
EXPECT_EQ(DOMExceptionCode::kSecurityError,
scope.GetExceptionState().CodeAs<DOMExceptionCode>());
}
TEST(PresentationRequestTest, TestMultipleUrlConstructorEmptySequence) {
V8TestingScope scope;
HeapVector<Member<V8UnionPresentationSourceOrUSVString>> sources;
PresentationRequest::Create(scope.GetExecutionContext(), sources,
scope.GetExceptionState());
EXPECT_TRUE(scope.GetExceptionState().HadException());
EXPECT_EQ(DOMExceptionCode::kNotSupportedError,
scope.GetExceptionState().CodeAs<DOMExceptionCode>());
}
TEST(PresentationRequestTest, TestSingleUrlConstructorUnknownScheme) {
V8TestingScope scope;
PresentationRequest::Create(scope.GetExecutionContext(), "foobar:unknown",
scope.GetExceptionState());
EXPECT_TRUE(scope.GetExceptionState().HadException());
EXPECT_EQ(DOMExceptionCode::kNotSupportedError,
scope.GetExceptionState().CodeAs<DOMExceptionCode>());
}
TEST(PresentationRequestTest, TestMultipleUrlConstructorSomeUnknownSchemes) {
V8TestingScope scope;
HeapVector<Member<V8UnionPresentationSourceOrUSVString>> sources =
CreateUrlSources({"foobar:unknown", "https://example.com",
"cast://deadbeef?param=foo", "deadbeef:random"});
PresentationRequest* request = PresentationRequest::Create(
scope.GetExecutionContext(), sources, scope.GetExceptionState());
ASSERT_FALSE(scope.GetExceptionState().HadException());
WTF::Vector<KURL> request_urls = request->Urls();
EXPECT_EQ(static_cast<size_t>(2), request_urls.size());
EXPECT_TRUE(request_urls[0].IsValid());
EXPECT_EQ("https://example.com/", request_urls[0].GetString());
EXPECT_TRUE(request_urls[1].IsValid());
EXPECT_EQ("cast://deadbeef?param=foo", request_urls[1].GetString());
}
TEST(PresentationRequestTest, TestMultipleUrlConstructorAllUnknownSchemes) {
V8TestingScope scope;
HeapVector<Member<V8UnionPresentationSourceOrUSVString>> sources =
CreateUrlSources({"foobar:unknown", "deadbeef:random"});
PresentationRequest::Create(scope.GetExecutionContext(), sources,
scope.GetExceptionState());
EXPECT_TRUE(scope.GetExceptionState().HadException());
EXPECT_EQ(DOMExceptionCode::kNotSupportedError,
scope.GetExceptionState().CodeAs<DOMExceptionCode>());
}
// If the site-initiated mirroring feature is disabled, then we do not allow
// the PresentationSource specialization of V8UnionPresentationSourceOrUSVString
// to be used to create a PresentationRequest.
TEST(PresentationRequestTest, TestPresentationSourceNotAllowed) {
ScopedSiteInitiatedMirroringForTest site_initiated_mirroring_enabled{false};
V8TestingScope scope;
PresentationRequest::Create(scope.GetExecutionContext(),
{CreatePresentationSource("https://example.com")},
scope.GetExceptionState());
EXPECT_TRUE(scope.GetExceptionState().HadException());
EXPECT_EQ(DOMExceptionCode::kNotSupportedError,
scope.GetExceptionState().CodeAs<DOMExceptionCode>());
}
TEST(PresentationRequestTest, TestPresentationSourcesInConstructor) {
ScopedSiteInitiatedMirroringForTest site_initiated_mirroring_enabled{true};
V8TestingScope scope;
PresentationRequest* request = PresentationRequest::Create(
scope.GetExecutionContext(),
{CreatePresentationSource("https://example.com"),
CreateMirroringSource()},
scope.GetExceptionState());
CHECK(request);
ASSERT_FALSE(scope.GetExceptionState().HadException());
EXPECT_EQ(static_cast<size_t>(2), request->Urls().size());
EXPECT_TRUE(request->Urls()[0].IsValid());
EXPECT_EQ("https://example.com/", request->Urls()[0].GetString());
EXPECT_TRUE(request->Urls()[1].IsValid());
// TODO(crbug.com/1267372): This makes a lot of assumptions about the
// hardcoded URL in presentation_request.cc that should be removed.
EXPECT_EQ(
"cast:0F5096E8?streamingCaptureAudio=1&streamingTargetPlayoutDelayMillis="
"400",
request->Urls()[1].GetString());
}
TEST(PresentationRequestTest, TestInvalidPresentationSource) {
ScopedSiteInitiatedMirroringForTest site_initiated_mirroring_enabled{true};
V8TestingScope scope;
PresentationRequest::Create(scope.GetExecutionContext(),
{CreatePresentationSource("invalid_url")},
scope.GetExceptionState());
EXPECT_TRUE(scope.GetExceptionState().HadException());
EXPECT_EQ(DOMExceptionCode::kNotSupportedError,
scope.GetExceptionState().CodeAs<DOMExceptionCode>());
}
} // anonymous namespace
} // namespace blink