1 // Copyright 2015 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 "content/browser/presentation/presentation_service_impl.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 
10 #include <iterator>
11 #include <memory>
12 #include <string>
13 #include <utility>
14 #include <vector>
15 
16 #include "base/bind.h"
17 #include "base/bind_helpers.h"
18 #include "base/macros.h"
19 #include "base/optional.h"
20 #include "base/run_loop.h"
21 #include "content/browser/presentation/presentation_test_utils.h"
22 #include "content/public/browser/presentation_request.h"
23 #include "content/public/browser/presentation_service_delegate.h"
24 #include "content/public/test/mock_navigation_handle.h"
25 #include "content/test/test_render_frame_host.h"
26 #include "content/test/test_render_view_host.h"
27 #include "content/test/test_web_contents.h"
28 #include "mojo/public/cpp/bindings/interface_ptr.h"
29 #include "mojo/public/cpp/bindings/pending_receiver.h"
30 #include "mojo/public/cpp/bindings/pending_remote.h"
31 #include "mojo/public/cpp/bindings/receiver.h"
32 #include "mojo/public/cpp/bindings/remote.h"
33 #include "testing/gmock/include/gmock/gmock.h"
34 
35 
36 namespace content {
37 
38 namespace {
39 
40 const char kPresentationId[] = "presentationId";
41 const char kPresentationUrl1[] = "http://foo.com/index.html";
42 const char kPresentationUrl2[] = "http://example.com/index.html";
43 const char kPresentationUrl3[] = "http://example.net/index.html";
44 
45 }  // namespace
46 
47 class PresentationServiceImplTest : public RenderViewHostImplTestHarness {
48  public:
PresentationServiceImplTest()49   PresentationServiceImplTest()
50       : presentation_url1_(GURL(kPresentationUrl1)),
51         presentation_url2_(GURL(kPresentationUrl2)),
52         presentation_url3_(GURL(kPresentationUrl3)) {}
53 
SetUp()54   void SetUp() override {
55     RenderViewHostImplTestHarness::SetUp();
56     // This needed to keep the WebContentsObserverSanityChecker checks happy for
57     // when AppendChild is called.
58     NavigateAndCommit(GURL("about:blank"));
59 
60     EXPECT_CALL(mock_delegate_, AddObserver(_, _, _)).Times(1);
61     TestRenderFrameHost* render_frame_host = contents()->GetMainFrame();
62     render_frame_host->InitializeRenderFrameIfNeeded();
63     service_impl_.reset(new PresentationServiceImpl(
64         render_frame_host, contents(), &mock_delegate_, nullptr));
65 
66     mojo::PendingRemote<PresentationController> presentation_controller_remote;
67     controller_receiver_.emplace(
68         &mock_controller_,
69         presentation_controller_remote.InitWithNewPipeAndPassReceiver());
70     service_impl_->SetController(std::move(presentation_controller_remote));
71 
72     presentation_urls_.push_back(presentation_url1_);
73     presentation_urls_.push_back(presentation_url2_);
74 
75     expect_presentation_success_cb_ =
76         base::BindOnce(&PresentationServiceImplTest::ExpectPresentationSuccess,
77                        base::Unretained(this));
78     expect_presentation_error_cb_ =
79         base::BindOnce(&PresentationServiceImplTest::ExpectPresentationError,
80                        base::Unretained(this));
81   }
82 
TearDown()83   void TearDown() override {
84     if (service_impl_.get()) {
85       ExpectDelegateReset();
86       EXPECT_CALL(mock_delegate_, RemoveObserver(_, _)).Times(1);
87       service_impl_.reset();
88     }
89     RenderViewHostImplTestHarness::TearDown();
90   }
91 
Navigate(bool main_frame)92   void Navigate(bool main_frame) {
93     RenderFrameHost* rfh = main_rfh();
94     RenderFrameHostTester* rfh_tester = RenderFrameHostTester::For(rfh);
95     if (!main_frame)
96       rfh = rfh_tester->AppendChild("subframe");
97     MockNavigationHandle handle(GURL(), rfh);
98     handle.set_has_committed(true);
99     service_impl_->DidFinishNavigation(&handle);
100   }
101 
ListenForScreenAvailabilityAndWait(const GURL & url,bool delegate_success)102   void ListenForScreenAvailabilityAndWait(const GURL& url,
103                                           bool delegate_success) {
104     EXPECT_CALL(mock_delegate_, AddScreenAvailabilityListener())
105         .WillOnce(Return(delegate_success));
106     service_impl_->ListenForScreenAvailability(url);
107 
108     EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_delegate_));
109   }
110 
SimulateScreenAvailabilityChangeAndWait(const GURL & url,ScreenAvailability availability)111   void SimulateScreenAvailabilityChangeAndWait(
112       const GURL& url,
113       ScreenAvailability availability) {
114     auto listener_it = service_impl_->screen_availability_listeners_.find(url);
115     ASSERT_TRUE(listener_it->second);
116 
117     EXPECT_CALL(mock_controller_,
118                 OnScreenAvailabilityUpdated(url, availability));
119     listener_it->second->OnScreenAvailabilityChanged(availability);
120     base::RunLoop().RunUntilIdle();
121   }
122 
ExpectDelegateReset()123   void ExpectDelegateReset() {
124     EXPECT_CALL(mock_delegate_, Reset(_, _)).Times(1);
125   }
126 
ExpectCleanState()127   void ExpectCleanState() {
128     EXPECT_TRUE(service_impl_->default_presentation_urls_.empty());
129     EXPECT_EQ(
130         service_impl_->screen_availability_listeners_.find(presentation_url1_),
131         service_impl_->screen_availability_listeners_.end());
132   }
133 
ExpectPresentationSuccess(PresentationConnectionResultPtr result,PresentationErrorPtr error)134   void ExpectPresentationSuccess(PresentationConnectionResultPtr result,
135                                  PresentationErrorPtr error) {
136     EXPECT_TRUE(result);
137     EXPECT_FALSE(error);
138     presentation_cb_was_run_ = true;
139   }
140 
ExpectPresentationError(PresentationConnectionResultPtr result,PresentationErrorPtr error)141   void ExpectPresentationError(PresentationConnectionResultPtr result,
142                                PresentationErrorPtr error) {
143     EXPECT_FALSE(result);
144     EXPECT_TRUE(error);
145     presentation_cb_was_run_ = true;
146   }
147 
ExpectPresentationCallbackWasRun() const148   void ExpectPresentationCallbackWasRun() const {
149     EXPECT_TRUE(presentation_cb_was_run_)
150         << "ExpectPresentationSuccess or ExpectPresentationError was called";
151   }
152 
153   MockPresentationServiceDelegate mock_delegate_;
154   MockReceiverPresentationServiceDelegate mock_receiver_delegate_;
155 
156   std::unique_ptr<PresentationServiceImpl> service_impl_;
157 
158   MockPresentationController mock_controller_;
159   base::Optional<mojo::Receiver<PresentationController>> controller_receiver_;
160 
161   GURL presentation_url1_;
162   GURL presentation_url2_;
163   GURL presentation_url3_;
164   std::vector<GURL> presentation_urls_;
165 
166   NewPresentationCallback expect_presentation_success_cb_;
167   NewPresentationCallback expect_presentation_error_cb_;
168   bool presentation_cb_was_run_ = false;
169 };
170 
TEST_F(PresentationServiceImplTest,ListenForScreenAvailability)171 TEST_F(PresentationServiceImplTest, ListenForScreenAvailability) {
172   ListenForScreenAvailabilityAndWait(presentation_url1_, true);
173 
174   SimulateScreenAvailabilityChangeAndWait(presentation_url1_,
175                                           ScreenAvailability::AVAILABLE);
176   SimulateScreenAvailabilityChangeAndWait(presentation_url1_,
177                                           ScreenAvailability::UNAVAILABLE);
178   SimulateScreenAvailabilityChangeAndWait(presentation_url1_,
179                                           ScreenAvailability::AVAILABLE);
180 }
181 
TEST_F(PresentationServiceImplTest,ScreenAvailabilityNotSupported)182 TEST_F(PresentationServiceImplTest, ScreenAvailabilityNotSupported) {
183   mock_delegate_.set_screen_availability_listening_supported(false);
184   EXPECT_CALL(mock_controller_,
185               OnScreenAvailabilityUpdated(presentation_url1_,
186                                           ScreenAvailability::DISABLED));
187   ListenForScreenAvailabilityAndWait(presentation_url1_, false);
188   base::RunLoop().RunUntilIdle();
189 }
190 
TEST_F(PresentationServiceImplTest,OnDelegateDestroyed)191 TEST_F(PresentationServiceImplTest, OnDelegateDestroyed) {
192   ListenForScreenAvailabilityAndWait(presentation_url1_, true);
193 
194   service_impl_->OnDelegateDestroyed();
195 
196   // TearDown() expects |mock_delegate_| to have been notified when
197   // |service_impl_| is destroyed; this does not apply here since the delegate
198   // is destroyed first.
199   service_impl_.reset();
200 }
201 
TEST_F(PresentationServiceImplTest,DidNavigateThisFrame)202 TEST_F(PresentationServiceImplTest, DidNavigateThisFrame) {
203   ListenForScreenAvailabilityAndWait(presentation_url1_, true);
204 
205   ExpectDelegateReset();
206   Navigate(true);
207   ExpectCleanState();
208 }
209 
TEST_F(PresentationServiceImplTest,DidNavigateOtherFrame)210 TEST_F(PresentationServiceImplTest, DidNavigateOtherFrame) {
211   ListenForScreenAvailabilityAndWait(presentation_url1_, true);
212 
213   Navigate(false);
214 
215   // Availability is reported and callback is invoked since it was not
216   // removed.
217   SimulateScreenAvailabilityChangeAndWait(presentation_url1_,
218                                           ScreenAvailability::AVAILABLE);
219 }
220 
TEST_F(PresentationServiceImplTest,DelegateFails)221 TEST_F(PresentationServiceImplTest, DelegateFails) {
222   ListenForScreenAvailabilityAndWait(presentation_url1_, false);
223   ASSERT_EQ(
224       service_impl_->screen_availability_listeners_.end(),
225       service_impl_->screen_availability_listeners_.find(presentation_url1_));
226 }
227 
TEST_F(PresentationServiceImplTest,SetDefaultPresentationUrls)228 TEST_F(PresentationServiceImplTest, SetDefaultPresentationUrls) {
229   EXPECT_CALL(mock_delegate_, SetDefaultPresentationUrls(
230                                   PresentationUrlsAre(presentation_urls_), _))
231       .Times(1);
232 
233   service_impl_->SetDefaultPresentationUrls(presentation_urls_);
234 
235   // Sets different DPUs.
236   std::vector<GURL> more_urls = presentation_urls_;
237   more_urls.push_back(presentation_url3_);
238 
239   PresentationConnectionCallback callback;
240   EXPECT_CALL(mock_delegate_,
241               SetDefaultPresentationUrls(PresentationUrlsAre(more_urls), _))
242       .WillOnce(SaveArgByMove<1>(&callback));
243   service_impl_->SetDefaultPresentationUrls(more_urls);
244 
245   PresentationInfo presentation_info(presentation_url2_, kPresentationId);
246 
247   EXPECT_CALL(mock_controller_, OnDefaultPresentationStarted(_))
248       .WillOnce([&presentation_info](PresentationConnectionResultPtr result) {
249         EXPECT_THAT(*result->presentation_info, InfoEquals(presentation_info));
250       });
251   EXPECT_CALL(mock_delegate_, ListenForConnectionStateChange(_, _, _, _));
252 
253   mojo::PendingRemote<PresentationConnection> presentation_connection_remote;
254   mojo::Remote<PresentationConnection> controller_remote;
255   ignore_result(
256       presentation_connection_remote.InitWithNewPipeAndPassReceiver());
257   std::move(callback).Run(PresentationConnectionResult::New(
258       blink::mojom::PresentationInfo::New(presentation_url2_, kPresentationId),
259       std::move(presentation_connection_remote),
260       controller_remote.BindNewPipeAndPassReceiver()));
261   base::RunLoop().RunUntilIdle();
262 }
263 
TEST_F(PresentationServiceImplTest,SetDefaultPresentationUrlsNoopsOnNonMainFrame)264 TEST_F(PresentationServiceImplTest,
265        SetDefaultPresentationUrlsNoopsOnNonMainFrame) {
266   RenderFrameHost* rfh = main_rfh();
267   RenderFrameHostTester* rfh_tester = RenderFrameHostTester::For(rfh);
268   rfh = rfh_tester->AppendChild("subframe");
269 
270   EXPECT_CALL(mock_delegate_, RemoveObserver(_, _)).Times(1);
271   EXPECT_CALL(mock_delegate_, AddObserver(_, _, _)).Times(1);
272   service_impl_.reset(
273       new PresentationServiceImpl(rfh, contents(), &mock_delegate_, nullptr));
274 
275   EXPECT_CALL(mock_delegate_, SetDefaultPresentationUrls(_, _)).Times(0);
276   service_impl_->SetDefaultPresentationUrls(presentation_urls_);
277 }
278 
TEST_F(PresentationServiceImplTest,ListenForConnectionStateChange)279 TEST_F(PresentationServiceImplTest, ListenForConnectionStateChange) {
280   PresentationInfo connection(presentation_url1_, kPresentationId);
281   PresentationConnectionStateChangedCallback state_changed_cb;
282   // Trigger state change. It should be propagated back up to
283   // |mock_controller_|.
284   PresentationInfo presentation_connection(presentation_url1_, kPresentationId);
285 
286   EXPECT_CALL(mock_delegate_, ListenForConnectionStateChange(_, _, _, _))
287       .WillOnce(SaveArg<3>(&state_changed_cb));
288   service_impl_->ListenForConnectionStateChange(connection);
289 
290   EXPECT_CALL(mock_controller_, OnConnectionStateChanged(
291                                     InfoPtrEquals(presentation_connection),
292                                     PresentationConnectionState::TERMINATED));
293   state_changed_cb.Run(PresentationConnectionStateChangeInfo(
294       PresentationConnectionState::TERMINATED));
295   base::RunLoop().RunUntilIdle();
296 }
297 
TEST_F(PresentationServiceImplTest,ListenForConnectionClose)298 TEST_F(PresentationServiceImplTest, ListenForConnectionClose) {
299   PresentationInfo connection(presentation_url1_, kPresentationId);
300   PresentationConnectionStateChangedCallback state_changed_cb;
301   EXPECT_CALL(mock_delegate_, ListenForConnectionStateChange(_, _, _, _))
302       .WillOnce(SaveArg<3>(&state_changed_cb));
303   service_impl_->ListenForConnectionStateChange(connection);
304 
305   // Trigger connection close. It should be propagated back up to
306   // |mock_controller_|.
307   PresentationInfo presentation_connection(presentation_url1_, kPresentationId);
308   PresentationConnectionStateChangeInfo closed_info(
309       PresentationConnectionState::CLOSED);
310   closed_info.close_reason = PresentationConnectionCloseReason::WENT_AWAY;
311   closed_info.message = "Foo";
312 
313   EXPECT_CALL(
314       mock_controller_,
315       OnConnectionClosed(InfoPtrEquals(presentation_connection),
316                          PresentationConnectionCloseReason::WENT_AWAY, "Foo"));
317   state_changed_cb.Run(closed_info);
318   base::RunLoop().RunUntilIdle();
319 }
320 
TEST_F(PresentationServiceImplTest,SetSameDefaultPresentationUrls)321 TEST_F(PresentationServiceImplTest, SetSameDefaultPresentationUrls) {
322   EXPECT_CALL(mock_delegate_, SetDefaultPresentationUrls(_, _)).Times(1);
323   service_impl_->SetDefaultPresentationUrls(presentation_urls_);
324   EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_delegate_));
325 
326   // Same URLs as before; no-ops.
327   service_impl_->SetDefaultPresentationUrls(presentation_urls_);
328   EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_delegate_));
329 }
330 
TEST_F(PresentationServiceImplTest,StartPresentationSuccess)331 TEST_F(PresentationServiceImplTest, StartPresentationSuccess) {
332   PresentationConnectionCallback saved_success_cb;
333   EXPECT_CALL(mock_delegate_, StartPresentation(_, _, _))
334       .WillOnce([&saved_success_cb](const auto& request, auto success_cb,
335                                     auto error_cb) {
336         saved_success_cb = std::move(success_cb);
337       });
338   service_impl_->StartPresentation(presentation_urls_,
339                                    std::move(expect_presentation_success_cb_));
340   EXPECT_FALSE(saved_success_cb.is_null());
341   EXPECT_CALL(mock_delegate_, ListenForConnectionStateChange(_, _, _, _))
342       .Times(1);
343   std::move(saved_success_cb)
344       .Run(PresentationConnectionResult::New(
345           blink::mojom::PresentationInfo::New(presentation_url1_,
346                                               kPresentationId),
347           mojo::NullRemote(), mojo::NullReceiver()));
348   ExpectPresentationCallbackWasRun();
349 }
350 
TEST_F(PresentationServiceImplTest,StartPresentationError)351 TEST_F(PresentationServiceImplTest, StartPresentationError) {
352   base::OnceCallback<void(const PresentationError&)> saved_error_cb;
353   EXPECT_CALL(mock_delegate_, StartPresentation(_, _, _))
354       .WillOnce([&](const auto& request, auto success_cb, auto error_cb) {
355         saved_error_cb = std::move(error_cb);
356       });
357   service_impl_->StartPresentation(presentation_urls_,
358                                    std::move(expect_presentation_error_cb_));
359   EXPECT_FALSE(saved_error_cb.is_null());
360   std::move(saved_error_cb)
361       .Run(PresentationError(PresentationErrorType::UNKNOWN, "Error message"));
362   ExpectPresentationCallbackWasRun();
363 }
364 
TEST_F(PresentationServiceImplTest,StartPresentationInProgress)365 TEST_F(PresentationServiceImplTest, StartPresentationInProgress) {
366   EXPECT_CALL(mock_delegate_, StartPresentation(_, _, _)).Times(1);
367   // Uninvoked callbacks must outlive |service_impl_| since they get invoked
368   // at |service_impl_|'s destruction.
369   service_impl_->StartPresentation(presentation_urls_, base::DoNothing());
370 
371   // This request should fail immediately, since there is already a
372   // StartPresentation in progress.
373   service_impl_->StartPresentation(presentation_urls_,
374                                    std::move(expect_presentation_error_cb_));
375   ExpectPresentationCallbackWasRun();
376 }
377 
TEST_F(PresentationServiceImplTest,ReconnectPresentationSuccess)378 TEST_F(PresentationServiceImplTest, ReconnectPresentationSuccess) {
379   PresentationConnectionCallback saved_success_cb;
380   EXPECT_CALL(mock_delegate_, ReconnectPresentation(_, kPresentationId, _, _))
381       .WillOnce([&saved_success_cb](const auto& request, const auto& id,
382                                     auto success_cb, auto error_cb) {
383         saved_success_cb = std::move(success_cb);
384       });
385   service_impl_->ReconnectPresentation(
386       presentation_urls_, kPresentationId,
387       std::move(expect_presentation_success_cb_));
388   EXPECT_FALSE(saved_success_cb.is_null());
389   EXPECT_CALL(mock_delegate_, ListenForConnectionStateChange(_, _, _, _))
390       .Times(1);
391   std::move(saved_success_cb)
392       .Run(PresentationConnectionResult::New(
393           blink::mojom::PresentationInfo::New(presentation_url1_,
394                                               kPresentationId),
395           mojo::NullRemote(), mojo::NullReceiver()));
396   ExpectPresentationCallbackWasRun();
397 }
398 
TEST_F(PresentationServiceImplTest,ReconnectPresentationError)399 TEST_F(PresentationServiceImplTest, ReconnectPresentationError) {
400   base::OnceCallback<void(const PresentationError&)> saved_error_cb;
401   EXPECT_CALL(mock_delegate_, ReconnectPresentation(_, kPresentationId, _, _))
402       .WillOnce([&](const auto& request, const std::string& id, auto success_cb,
403                     auto error_cb) { saved_error_cb = std::move(error_cb); });
404   service_impl_->ReconnectPresentation(
405       presentation_urls_, kPresentationId,
406       std::move(expect_presentation_error_cb_));
407   EXPECT_FALSE(saved_error_cb.is_null());
408   std::move(saved_error_cb)
409       .Run(PresentationError(PresentationErrorType::UNKNOWN, "Error message"));
410   ExpectPresentationCallbackWasRun();
411 }
412 
TEST_F(PresentationServiceImplTest,MaxPendingReconnectPresentationRequests)413 TEST_F(PresentationServiceImplTest, MaxPendingReconnectPresentationRequests) {
414   const char* presentation_url = "http://fooUrl%d";
415   const char* presentation_id = "presentationId%d";
416   int num_requests = PresentationServiceImpl::kMaxQueuedRequests;
417   int i = 0;
418   EXPECT_CALL(mock_delegate_, ReconnectPresentation(_, _, _, _))
419       .Times(num_requests);
420   for (; i < num_requests; ++i) {
421     std::vector<GURL> urls = {GURL(base::StringPrintf(presentation_url, i))};
422     // Uninvoked callbacks must outlive |service_impl_| since they get invoked
423     // at |service_impl_|'s destruction.
424     service_impl_->ReconnectPresentation(
425         urls, base::StringPrintf(presentation_id, i), base::DoNothing());
426   }
427 
428   std::vector<GURL> urls = {GURL(base::StringPrintf(presentation_url, i))};
429   // Exceeded maximum queue size, should invoke mojo callback with error.
430   service_impl_->ReconnectPresentation(
431       urls, base::StringPrintf(presentation_id, i),
432       std::move(expect_presentation_error_cb_));
433   ExpectPresentationCallbackWasRun();
434 }
435 
TEST_F(PresentationServiceImplTest,CloseConnection)436 TEST_F(PresentationServiceImplTest, CloseConnection) {
437   EXPECT_CALL(mock_delegate_, CloseConnection(_, _, Eq(kPresentationId)));
438   service_impl_->CloseConnection(presentation_url1_, kPresentationId);
439 }
440 
TEST_F(PresentationServiceImplTest,Terminate)441 TEST_F(PresentationServiceImplTest, Terminate) {
442   EXPECT_CALL(mock_delegate_, Terminate(_, _, Eq(kPresentationId)));
443   service_impl_->Terminate(presentation_url1_, kPresentationId);
444 }
445 
TEST_F(PresentationServiceImplTest,ReceiverPresentationServiceDelegate)446 TEST_F(PresentationServiceImplTest, ReceiverPresentationServiceDelegate) {
447   EXPECT_CALL(mock_receiver_delegate_, AddObserver(_, _, _)).Times(1);
448 
449   PresentationServiceImpl service_impl(main_rfh(), contents(), nullptr,
450                                        &mock_receiver_delegate_);
451 
452   ReceiverConnectionAvailableCallback callback;
453   EXPECT_CALL(mock_receiver_delegate_,
454               RegisterReceiverConnectionAvailableCallback(_))
455       .WillOnce(SaveArg<0>(&callback));
456 
457   MockPresentationReceiver mock_receiver;
458   mojo::Receiver<blink::mojom::PresentationReceiver>
459       presentation_receiver_receiver(&mock_receiver);
460   service_impl.SetReceiver(
461       presentation_receiver_receiver.BindNewPipeAndPassRemote());
462   EXPECT_FALSE(callback.is_null());
463 
464   PresentationInfo expected(presentation_url1_, kPresentationId);
465 
466   // Client gets notified of receiver connections.
467   mojo::PendingRemote<PresentationConnection> controller_connection;
468   MockPresentationConnection mock_presentation_connection;
469   mojo::Receiver<PresentationConnection> connection_binding(
470       &mock_presentation_connection,
471       controller_connection.InitWithNewPipeAndPassReceiver());
472   mojo::Remote<PresentationConnection> receiver_connection;
473 
474   EXPECT_CALL(mock_receiver,
475               OnReceiverConnectionAvailable(InfoPtrEquals(expected), _, _))
476       .Times(1);
477   callback.Run(PresentationInfo::New(expected),
478                std::move(controller_connection),
479                receiver_connection.BindNewPipeAndPassReceiver());
480   base::RunLoop().RunUntilIdle();
481 
482   EXPECT_CALL(mock_receiver_delegate_, RemoveObserver(_, _)).Times(1);
483 }
484 
TEST_F(PresentationServiceImplTest,ReceiverDelegateOnSubFrame)485 TEST_F(PresentationServiceImplTest, ReceiverDelegateOnSubFrame) {
486   EXPECT_CALL(mock_receiver_delegate_, AddObserver(_, _, _)).Times(1);
487 
488   PresentationServiceImpl service_impl(main_rfh(), contents(), nullptr,
489                                        &mock_receiver_delegate_);
490   service_impl.is_main_frame_ = false;
491 
492   ReceiverConnectionAvailableCallback callback;
493   EXPECT_CALL(mock_receiver_delegate_,
494               RegisterReceiverConnectionAvailableCallback(_))
495       .Times(0);
496 
497   mojo::PendingRemote<PresentationController> presentation_controller_remote;
498   controller_receiver_.emplace(
499       &mock_controller_,
500       presentation_controller_remote.InitWithNewPipeAndPassReceiver());
501   service_impl.controller_delegate_ = nullptr;
502   service_impl.SetController(std::move(presentation_controller_remote));
503 
504   EXPECT_CALL(mock_receiver_delegate_, Reset(_, _)).Times(0);
505   service_impl.Reset();
506 
507   EXPECT_CALL(mock_receiver_delegate_, RemoveObserver(_, _)).Times(1);
508 }
509 
510 }  // namespace content
511