1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/modules/remoteplayback/remote_playback.h"
6
7 #include "testing/gmock/include/gmock/gmock.h"
8 #include "testing/gtest/include/gtest/gtest.h"
9 #include "third_party/blink/renderer/bindings/core/v8/script_function.h"
10 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
11 #include "third_party/blink/renderer/bindings/modules/v8/v8_remote_playback_availability_callback.h"
12 #include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
13 #include "third_party/blink/renderer/core/frame/local_frame.h"
14 #include "third_party/blink/renderer/core/html/media/html_media_element.h"
15 #include "third_party/blink/renderer/core/html/media/html_video_element.h"
16 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
17 #include "third_party/blink/renderer/modules/presentation/presentation_controller.h"
18 #include "third_party/blink/renderer/modules/remoteplayback/html_media_element_remote_playback.h"
19 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
20 #include "third_party/blink/renderer/platform/heap/heap.h"
21 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
22 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
23
24 namespace blink {
25
26 namespace {
27
28 class MockFunction : public ScriptFunction {
29 public:
Create(ScriptState * script_state)30 static testing::StrictMock<MockFunction>* Create(ScriptState* script_state) {
31 return MakeGarbageCollected<testing::StrictMock<MockFunction>>(
32 script_state);
33 }
34
Bind()35 v8::Local<v8::Function> Bind() { return BindToV8Function(); }
36
37 MOCK_METHOD1(Call, ScriptValue(ScriptValue));
38
39 protected:
MockFunction(ScriptState * script_state)40 explicit MockFunction(ScriptState* script_state)
41 : ScriptFunction(script_state) {}
42 };
43
44 class MockEventListenerForRemotePlayback : public NativeEventListener {
45 public:
46 MOCK_METHOD2(Invoke, void(ExecutionContext* executionContext, Event*));
47 };
48
49 class MockPresentationController final : public PresentationController {
50 public:
MockPresentationController(LocalFrame & frame)51 explicit MockPresentationController(LocalFrame& frame)
52 : PresentationController(frame) {}
53 ~MockPresentationController() override = default;
54
55 MOCK_METHOD1(AddAvailabilityObserver,
56 void(PresentationAvailabilityObserver*));
57 MOCK_METHOD1(RemoveAvailabilityObserver,
58 void(PresentationAvailabilityObserver*));
59 };
60 } // namespace
61
62 class RemotePlaybackTest : public testing::Test,
63 private ScopedRemotePlaybackBackendForTest {
64 public:
RemotePlaybackTest()65 RemotePlaybackTest() : ScopedRemotePlaybackBackendForTest(true) {}
66
67 protected:
CancelPrompt(RemotePlayback & remote_playback)68 void CancelPrompt(RemotePlayback& remote_playback) {
69 remote_playback.PromptCancelled();
70 }
71
SetState(RemotePlayback & remote_playback,mojom::blink::PresentationConnectionState state)72 void SetState(RemotePlayback& remote_playback,
73 mojom::blink::PresentationConnectionState state) {
74 remote_playback.StateChanged(state);
75 }
76
IsListening(RemotePlayback & remote_playback)77 bool IsListening(RemotePlayback& remote_playback) {
78 return remote_playback.is_listening_;
79 }
80 };
81
TEST_F(RemotePlaybackTest,PromptCancelledRejectsWithNotAllowedError)82 TEST_F(RemotePlaybackTest, PromptCancelledRejectsWithNotAllowedError) {
83 V8TestingScope scope;
84
85 auto page_holder = std::make_unique<DummyPageHolder>();
86
87 auto* element =
88 MakeGarbageCollected<HTMLVideoElement>(page_holder->GetDocument());
89 RemotePlayback& remote_playback = RemotePlayback::From(*element);
90
91 auto* resolve = MockFunction::Create(scope.GetScriptState());
92 auto* reject = MockFunction::Create(scope.GetScriptState());
93
94 EXPECT_CALL(*resolve, Call(testing::_)).Times(0);
95 EXPECT_CALL(*reject, Call(testing::_)).Times(1);
96
97 LocalFrame::NotifyUserActivation(&page_holder->GetFrame());
98 remote_playback.prompt(scope.GetScriptState())
99 .Then(resolve->Bind(), reject->Bind());
100 CancelPrompt(remote_playback);
101
102 // Runs pending promises.
103 v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
104
105 // Verify mock expectations explicitly as the mock objects are garbage
106 // collected.
107 testing::Mock::VerifyAndClear(resolve);
108 testing::Mock::VerifyAndClear(reject);
109 }
110
TEST_F(RemotePlaybackTest,PromptConnectedRejectsWhenCancelled)111 TEST_F(RemotePlaybackTest, PromptConnectedRejectsWhenCancelled) {
112 V8TestingScope scope;
113
114 auto page_holder = std::make_unique<DummyPageHolder>();
115
116 auto* element =
117 MakeGarbageCollected<HTMLVideoElement>(page_holder->GetDocument());
118 RemotePlayback& remote_playback = RemotePlayback::From(*element);
119
120 auto* resolve = MockFunction::Create(scope.GetScriptState());
121 auto* reject = MockFunction::Create(scope.GetScriptState());
122
123 EXPECT_CALL(*resolve, Call(testing::_)).Times(0);
124 EXPECT_CALL(*reject, Call(testing::_)).Times(1);
125
126 SetState(remote_playback,
127 mojom::blink::PresentationConnectionState::CONNECTED);
128
129 LocalFrame::NotifyUserActivation(&page_holder->GetFrame());
130 remote_playback.prompt(scope.GetScriptState())
131 .Then(resolve->Bind(), reject->Bind());
132 CancelPrompt(remote_playback);
133
134 // Runs pending promises.
135 v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
136
137 // Verify mock expectations explicitly as the mock objects are garbage
138 // collected.
139 testing::Mock::VerifyAndClear(resolve);
140 testing::Mock::VerifyAndClear(reject);
141 }
142
TEST_F(RemotePlaybackTest,PromptConnectedResolvesWhenDisconnected)143 TEST_F(RemotePlaybackTest, PromptConnectedResolvesWhenDisconnected) {
144 V8TestingScope scope;
145
146 auto page_holder = std::make_unique<DummyPageHolder>();
147
148 auto* element =
149 MakeGarbageCollected<HTMLVideoElement>(page_holder->GetDocument());
150 RemotePlayback& remote_playback = RemotePlayback::From(*element);
151
152 auto* resolve = MockFunction::Create(scope.GetScriptState());
153 auto* reject = MockFunction::Create(scope.GetScriptState());
154
155 EXPECT_CALL(*resolve, Call(testing::_)).Times(1);
156 EXPECT_CALL(*reject, Call(testing::_)).Times(0);
157
158 SetState(remote_playback,
159 mojom::blink::PresentationConnectionState::CONNECTED);
160
161 LocalFrame::NotifyUserActivation(&page_holder->GetFrame());
162 remote_playback.prompt(scope.GetScriptState())
163 .Then(resolve->Bind(), reject->Bind());
164
165 SetState(remote_playback, mojom::blink::PresentationConnectionState::CLOSED);
166
167 // Runs pending promises.
168 v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
169
170 // Verify mock expectations explicitly as the mock objects are garbage
171 // collected.
172 testing::Mock::VerifyAndClear(resolve);
173 testing::Mock::VerifyAndClear(reject);
174 }
175
TEST_F(RemotePlaybackTest,StateChangeEvents)176 TEST_F(RemotePlaybackTest, StateChangeEvents) {
177 V8TestingScope scope;
178
179 auto page_holder = std::make_unique<DummyPageHolder>();
180
181 auto* element =
182 MakeGarbageCollected<HTMLVideoElement>(page_holder->GetDocument());
183 RemotePlayback& remote_playback = RemotePlayback::From(*element);
184
185 auto* connecting_handler = MakeGarbageCollected<
186 testing::StrictMock<MockEventListenerForRemotePlayback>>();
187 auto* connect_handler = MakeGarbageCollected<
188 testing::StrictMock<MockEventListenerForRemotePlayback>>();
189 auto* disconnect_handler = MakeGarbageCollected<
190 testing::StrictMock<MockEventListenerForRemotePlayback>>();
191
192 remote_playback.addEventListener(event_type_names::kConnecting,
193 connecting_handler);
194 remote_playback.addEventListener(event_type_names::kConnect, connect_handler);
195 remote_playback.addEventListener(event_type_names::kDisconnect,
196 disconnect_handler);
197
198 // Verify a state changes when a route is connected and closed.
199 EXPECT_CALL(*connecting_handler, Invoke(testing::_, testing::_)).Times(1);
200 EXPECT_CALL(*connect_handler, Invoke(testing::_, testing::_)).Times(1);
201 EXPECT_CALL(*disconnect_handler, Invoke(testing::_, testing::_)).Times(1);
202
203 SetState(remote_playback,
204 mojom::blink::PresentationConnectionState::CONNECTING);
205 SetState(remote_playback,
206 mojom::blink::PresentationConnectionState::CONNECTING);
207 SetState(remote_playback,
208 mojom::blink::PresentationConnectionState::CONNECTED);
209 SetState(remote_playback,
210 mojom::blink::PresentationConnectionState::CONNECTED);
211 SetState(remote_playback, mojom::blink::PresentationConnectionState::CLOSED);
212 SetState(remote_playback, mojom::blink::PresentationConnectionState::CLOSED);
213
214 // Verify mock expectations explicitly as the mock objects are garbage
215 // collected.
216 testing::Mock::VerifyAndClear(connecting_handler);
217 testing::Mock::VerifyAndClear(connect_handler);
218 testing::Mock::VerifyAndClear(disconnect_handler);
219
220 // Verify a state changes when a route is connected and terminated.
221 EXPECT_CALL(*connecting_handler, Invoke(testing::_, testing::_)).Times(1);
222 EXPECT_CALL(*connect_handler, Invoke(testing::_, testing::_)).Times(1);
223 EXPECT_CALL(*disconnect_handler, Invoke(testing::_, testing::_)).Times(1);
224
225 SetState(remote_playback,
226 mojom::blink::PresentationConnectionState::CONNECTING);
227 SetState(remote_playback,
228 mojom::blink::PresentationConnectionState::CONNECTED);
229 SetState(remote_playback,
230 mojom::blink::PresentationConnectionState::TERMINATED);
231
232 // Verify mock expectations explicitly as the mock objects are garbage
233 // collected.
234 testing::Mock::VerifyAndClear(connecting_handler);
235 testing::Mock::VerifyAndClear(connect_handler);
236 testing::Mock::VerifyAndClear(disconnect_handler);
237
238 // Verify we can connect after a route termination.
239 EXPECT_CALL(*connecting_handler, Invoke(testing::_, testing::_)).Times(1);
240 SetState(remote_playback,
241 mojom::blink::PresentationConnectionState::CONNECTING);
242 testing::Mock::VerifyAndClear(connecting_handler);
243 }
244
TEST_F(RemotePlaybackTest,DisableRemotePlaybackRejectsPromptWithInvalidStateError)245 TEST_F(RemotePlaybackTest,
246 DisableRemotePlaybackRejectsPromptWithInvalidStateError) {
247 V8TestingScope scope;
248
249 auto page_holder = std::make_unique<DummyPageHolder>();
250
251 auto* element =
252 MakeGarbageCollected<HTMLVideoElement>(page_holder->GetDocument());
253 RemotePlayback& remote_playback = RemotePlayback::From(*element);
254
255 MockFunction* resolve = MockFunction::Create(scope.GetScriptState());
256 MockFunction* reject = MockFunction::Create(scope.GetScriptState());
257
258 EXPECT_CALL(*resolve, Call(testing::_)).Times(0);
259 EXPECT_CALL(*reject, Call(testing::_)).Times(1);
260
261 LocalFrame::NotifyUserActivation(&page_holder->GetFrame());
262 remote_playback.prompt(scope.GetScriptState())
263 .Then(resolve->Bind(), reject->Bind());
264 HTMLMediaElementRemotePlayback::SetBooleanAttribute(
265 *element, html_names::kDisableremoteplaybackAttr, true);
266
267 // Runs pending promises.
268 v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
269
270 // Verify mock expectations explicitly as the mock objects are garbage
271 // collected.
272 testing::Mock::VerifyAndClear(resolve);
273 testing::Mock::VerifyAndClear(reject);
274 }
275
TEST_F(RemotePlaybackTest,DisableRemotePlaybackCancelsAvailabilityCallbacks)276 TEST_F(RemotePlaybackTest, DisableRemotePlaybackCancelsAvailabilityCallbacks) {
277 V8TestingScope scope;
278
279 auto page_holder = std::make_unique<DummyPageHolder>();
280
281 auto* element =
282 MakeGarbageCollected<HTMLVideoElement>(page_holder->GetDocument());
283 RemotePlayback& remote_playback = RemotePlayback::From(*element);
284
285 MockFunction* callback_function =
286 MockFunction::Create(scope.GetScriptState());
287 V8RemotePlaybackAvailabilityCallback* availability_callback =
288 V8RemotePlaybackAvailabilityCallback::Create(callback_function->Bind());
289
290 // The initial call upon registering will not happen as it's posted on the
291 // message loop.
292 EXPECT_CALL(*callback_function, Call(testing::_)).Times(0);
293
294 MockFunction* resolve = MockFunction::Create(scope.GetScriptState());
295 MockFunction* reject = MockFunction::Create(scope.GetScriptState());
296
297 EXPECT_CALL(*resolve, Call(testing::_)).Times(1);
298 EXPECT_CALL(*reject, Call(testing::_)).Times(0);
299
300 remote_playback
301 .watchAvailability(scope.GetScriptState(), availability_callback)
302 .Then(resolve->Bind(), reject->Bind());
303
304 HTMLMediaElementRemotePlayback::SetBooleanAttribute(
305 *element, html_names::kDisableremoteplaybackAttr, true);
306
307 // Runs pending promises.
308 v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
309
310 // Verify mock expectations explicitly as the mock objects are garbage
311 // collected.
312 testing::Mock::VerifyAndClear(resolve);
313 testing::Mock::VerifyAndClear(reject);
314 testing::Mock::VerifyAndClear(callback_function);
315 }
316
TEST_F(RemotePlaybackTest,PromptThrowsWhenBackendDisabled)317 TEST_F(RemotePlaybackTest, PromptThrowsWhenBackendDisabled) {
318 ScopedRemotePlaybackBackendForTest remote_playback_backend(false);
319 V8TestingScope scope;
320
321 auto page_holder = std::make_unique<DummyPageHolder>();
322
323 auto* element =
324 MakeGarbageCollected<HTMLVideoElement>(page_holder->GetDocument());
325 RemotePlayback& remote_playback = RemotePlayback::From(*element);
326
327 auto* resolve = MockFunction::Create(scope.GetScriptState());
328 auto* reject = MockFunction::Create(scope.GetScriptState());
329
330 EXPECT_CALL(*resolve, Call(testing::_)).Times(0);
331 EXPECT_CALL(*reject, Call(testing::_)).Times(1);
332
333 LocalFrame::NotifyUserActivation(&page_holder->GetFrame());
334 remote_playback.prompt(scope.GetScriptState())
335 .Then(resolve->Bind(), reject->Bind());
336
337 // Runs pending promises.
338 v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
339
340 // Verify mock expectations explicitly as the mock objects are garbage
341 // collected.
342 testing::Mock::VerifyAndClear(resolve);
343 testing::Mock::VerifyAndClear(reject);
344 }
345
TEST_F(RemotePlaybackTest,WatchAvailabilityWorksWhenBackendDisabled)346 TEST_F(RemotePlaybackTest, WatchAvailabilityWorksWhenBackendDisabled) {
347 ScopedRemotePlaybackBackendForTest remote_playback_backend(false);
348 V8TestingScope scope;
349
350 auto page_holder = std::make_unique<DummyPageHolder>();
351
352 auto* element =
353 MakeGarbageCollected<HTMLVideoElement>(page_holder->GetDocument());
354 RemotePlayback& remote_playback = RemotePlayback::From(*element);
355
356 MockFunction* callback_function =
357 MockFunction::Create(scope.GetScriptState());
358 V8RemotePlaybackAvailabilityCallback* availability_callback =
359 V8RemotePlaybackAvailabilityCallback::Create(callback_function->Bind());
360
361 // The initial call upon registering will not happen as it's posted on the
362 // message loop.
363 EXPECT_CALL(*callback_function, Call(testing::_)).Times(0);
364
365 MockFunction* resolve = MockFunction::Create(scope.GetScriptState());
366 MockFunction* reject = MockFunction::Create(scope.GetScriptState());
367
368 EXPECT_CALL(*resolve, Call(testing::_)).Times(1);
369 EXPECT_CALL(*reject, Call(testing::_)).Times(0);
370
371 remote_playback
372 .watchAvailability(scope.GetScriptState(), availability_callback)
373 .Then(resolve->Bind(), reject->Bind());
374
375 // Runs pending promises.
376 v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
377
378 // Verify mock expectations explicitly as the mock objects are garbage
379 // collected.
380 testing::Mock::VerifyAndClear(resolve);
381 testing::Mock::VerifyAndClear(reject);
382 testing::Mock::VerifyAndClear(callback_function);
383 }
384
TEST_F(RemotePlaybackTest,IsListening)385 TEST_F(RemotePlaybackTest, IsListening) {
386 V8TestingScope scope;
387
388 auto page_holder = std::make_unique<DummyPageHolder>();
389
390 auto* element =
391 MakeGarbageCollected<HTMLVideoElement>(page_holder->GetDocument());
392 RemotePlayback& remote_playback = RemotePlayback::From(*element);
393
394 LocalFrame& frame = page_holder->GetFrame();
395 MockPresentationController* mock_controller =
396 MakeGarbageCollected<MockPresentationController>(frame);
397 Supplement<LocalFrame>::ProvideTo(
398 frame, static_cast<PresentationController*>(mock_controller));
399
400 EXPECT_CALL(*mock_controller,
401 AddAvailabilityObserver(testing::Eq(&remote_playback)))
402 .Times(2);
403 EXPECT_CALL(*mock_controller,
404 RemoveAvailabilityObserver(testing::Eq(&remote_playback)))
405 .Times(2);
406
407 MockFunction* callback_function =
408 MockFunction::Create(scope.GetScriptState());
409 V8RemotePlaybackAvailabilityCallback* availability_callback =
410 V8RemotePlaybackAvailabilityCallback::Create(callback_function->Bind());
411
412 // The initial call upon registering will not happen as it's posted on the
413 // message loop.
414 EXPECT_CALL(*callback_function, Call(testing::_)).Times(2);
415
416 remote_playback.watchAvailability(scope.GetScriptState(),
417 availability_callback);
418
419 ASSERT_TRUE(remote_playback.Urls().IsEmpty());
420 ASSERT_FALSE(IsListening(remote_playback));
421
422 remote_playback.SourceChanged(WebURL(KURL("http://www.example.com")), true);
423 ASSERT_EQ((size_t)1, remote_playback.Urls().size());
424 ASSERT_TRUE(IsListening(remote_playback));
425 remote_playback.AvailabilityChanged(mojom::ScreenAvailability::AVAILABLE);
426
427 remote_playback.cancelWatchAvailability(scope.GetScriptState());
428 ASSERT_EQ((size_t)1, remote_playback.Urls().size());
429 ASSERT_FALSE(IsListening(remote_playback));
430
431 remote_playback.watchAvailability(scope.GetScriptState(),
432 availability_callback);
433 ASSERT_EQ((size_t)1, remote_playback.Urls().size());
434 ASSERT_TRUE(IsListening(remote_playback));
435 remote_playback.AvailabilityChanged(mojom::ScreenAvailability::AVAILABLE);
436
437 remote_playback.SourceChanged(WebURL(), false);
438 ASSERT_TRUE(remote_playback.Urls().IsEmpty());
439 ASSERT_FALSE(IsListening(remote_playback));
440
441 remote_playback.SourceChanged(WebURL(KURL("@$@#@#")), true);
442 ASSERT_TRUE(remote_playback.Urls().IsEmpty());
443 ASSERT_FALSE(IsListening(remote_playback));
444
445 // Runs pending promises.
446 v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
447
448 // Verify mock expectations explicitly as the mock objects are garbage
449 // collected.
450 testing::Mock::VerifyAndClear(callback_function);
451 testing::Mock::VerifyAndClear(mock_controller);
452 }
453
454 } // namespace blink
455