| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "content/test/fuzzer/controller_presentation_service_delegate_for_fuzzing.h" |
| |
| #include "base/notreached.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "third_party/blink/public/mojom/presentation/presentation.mojom-mojolpm.h" |
| #include "url/mojom/url.mojom-mojolpm.h" |
| |
| // Code copy of a limit of the same name |
| // This allows preventing the fake making bad calls |
| // See "content/browser/presentation/presentation_service_impl.cc" |
| static constexpr size_t kMaxPresentationIdLength = 256; |
| |
| ControllerPresentationServiceDelegateForFuzzing:: |
| ControllerPresentationServiceDelegateForFuzzing() { |
| DETACH_FROM_SEQUENCE(sequence_checker_); |
| } |
| |
| ControllerPresentationServiceDelegateForFuzzing:: |
| ~ControllerPresentationServiceDelegateForFuzzing() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| for (auto& observer_pair : observers_) |
| observer_pair.second->OnDelegateDestroyed(); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing::AddObserver( |
| int render_process_id, |
| int render_frame_id, |
| ControllerPresentationServiceDelegate::Observer* observer) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| observers_[content::GlobalRenderFrameHostId(render_process_id, |
| render_frame_id)] = observer; |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing::RemoveObserver( |
| int render_process_id, |
| int render_frame_id) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| observers_.erase( |
| content::GlobalRenderFrameHostId(render_process_id, render_frame_id)); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing::Reset( |
| int render_process_id, |
| int render_frame_id) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| // Intentionally, we do not reset all callbacks, in order to mimic |
| // `ControllerPresentationServiceDelegateImpl` as closely as possible. |
| listeners_.clear(); |
| set_default_presentation_urls_callback_.Reset(); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing:: |
| SetDefaultPresentationUrls( |
| const content::PresentationRequest& request, |
| content::DefaultPresentationConnectionCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| set_default_presentation_urls_callback_ = std::move(callback); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing::StartPresentation( |
| const content::PresentationRequest& request, |
| content::PresentationConnectionCallback success_cb, |
| content::PresentationConnectionErrorCallback error_cb) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| start_presentation_success_cb_ = std::move(success_cb); |
| start_presentation_error_cb_ = std::move(error_cb); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing::ReconnectPresentation( |
| const content::PresentationRequest& request, |
| const std::string& presentation_id, |
| content::PresentationConnectionCallback success_cb, |
| content::PresentationConnectionErrorCallback error_cb) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| reconnect_presentation_success_cb_ = std::move(success_cb); |
| reconnect_presentation_error_cb_ = std::move(error_cb); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing::CloseConnection( |
| int render_process_id, |
| int render_frame_id, |
| const std::string& presentation_id) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| start_presentation_success_cb_.Reset(); |
| start_presentation_error_cb_.Reset(); |
| reconnect_presentation_success_cb_.Reset(); |
| reconnect_presentation_error_cb_.Reset(); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing::Terminate( |
| int render_process_id, |
| int render_frame_id, |
| const std::string& presentation_id) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| start_presentation_success_cb_.Reset(); |
| start_presentation_error_cb_.Reset(); |
| reconnect_presentation_success_cb_.Reset(); |
| reconnect_presentation_error_cb_.Reset(); |
| } |
| |
| std::unique_ptr<media::FlingingController> |
| ControllerPresentationServiceDelegateForFuzzing::GetFlingingController( |
| int render_process_id, |
| int render_frame_id, |
| const std::string& presentation_id) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| // This should not fail. As the MojoLPM fuzzer this fake was designed to work |
| // with has no way to call this. |
| NOTIMPLEMENTED(); |
| return nullptr; |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing:: |
| ListenForConnectionStateChange( |
| int render_process_id, |
| int render_frame_id, |
| const blink::mojom::PresentationInfo& connection, |
| const content::PresentationConnectionStateChangedCallback& |
| state_changed_cb) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| listen_for_connection_state_change_state_changed_cb_ = |
| std::move(state_changed_cb); |
| } |
| |
| bool ControllerPresentationServiceDelegateForFuzzing:: |
| AddScreenAvailabilityListener( |
| int render_process_id, |
| int render_frame_id, |
| content::PresentationScreenAvailabilityListener* listener) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| listeners_[listener->GetAvailabilityUrl()] = listener; |
| return true; |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing:: |
| RemoveScreenAvailabilityListener( |
| int render_process_id, |
| int render_frame_id, |
| content::PresentationScreenAvailabilityListener* listener) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| listeners_.erase(listener->GetAvailabilityUrl()); |
| } |
| void ControllerPresentationServiceDelegateForFuzzing:: |
| HandleListenersGetAvailabilityUrl( |
| const mojolpm::url::mojom::Url& proto_url) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| GURL url; |
| if (!mojolpm::FromProto(proto_url, url)) |
| return; |
| content::GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&ControllerPresentationServiceDelegateForFuzzing:: |
| CallListenersGetAvailabilityUrl, |
| GetWeakPtr(), std::move(url))); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing:: |
| CallListenersGetAvailabilityUrl(GURL url) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (!listeners_.count(url)) |
| return; |
| listeners_[url]->GetAvailabilityUrl(); |
| } |
| void ControllerPresentationServiceDelegateForFuzzing:: |
| HandleListenersOnScreenAvailabilityChanged( |
| const mojolpm::url::mojom::Url& proto_url, |
| const mojolpm::blink::mojom::ScreenAvailability& |
| proto_screen_availability) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| GURL url; |
| if (!mojolpm::FromProto(proto_url, url)) |
| return; |
| blink::mojom::ScreenAvailability screen_availability; |
| if (!mojolpm::FromProto(proto_screen_availability, screen_availability)) |
| return; |
| content::GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&ControllerPresentationServiceDelegateForFuzzing:: |
| CallListenersOnScreenAvailabilityChanged, |
| GetWeakPtr(), std::move(url), |
| std::move(screen_availability))); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing:: |
| CallListenersOnScreenAvailabilityChanged( |
| GURL url, |
| blink::mojom::ScreenAvailability screen_availability) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (!listeners_.count(url)) |
| return; |
| listeners_[url]->OnScreenAvailabilityChanged(std::move(screen_availability)); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing:: |
| HandleSetDefaultPresentationUrls( |
| const mojolpm::blink::mojom::PresentationConnectionResult& |
| proto_result) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| blink::mojom::PresentationConnectionResultPtr result; |
| if (!mojolpm::FromProto(proto_result, result)) |
| return; |
| content::GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&ControllerPresentationServiceDelegateForFuzzing:: |
| CallSetDefaultPresentationUrls, |
| GetWeakPtr(), std::move(result))); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing:: |
| CallSetDefaultPresentationUrls( |
| blink::mojom::PresentationConnectionResultPtr result_ptr) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (set_default_presentation_urls_callback_.is_null()) |
| return; |
| set_default_presentation_urls_callback_.Run(std::move(result_ptr)); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing:: |
| HandleStartPresentationSuccess( |
| const mojolpm::blink::mojom::PresentationConnectionResult& |
| proto_result) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| blink::mojom::PresentationConnectionResultPtr result; |
| if (!mojolpm::FromProto(proto_result, result)) |
| return; |
| content::GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&ControllerPresentationServiceDelegateForFuzzing:: |
| CallStartPresentationSuccess, |
| GetWeakPtr(), std::move(result))); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing:: |
| CallStartPresentationSuccess( |
| blink::mojom::PresentationConnectionResultPtr result_ptr) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (result_ptr->presentation_info->id.length() > kMaxPresentationIdLength) |
| return; |
| if (start_presentation_success_cb_.is_null()) |
| return; |
| std::move(start_presentation_success_cb_).Run(std::move(result_ptr)); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing:: |
| HandleStartPresentationError( |
| const mojolpm::blink::mojom::PresentationError& proto_error) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| blink::mojom::PresentationErrorPtr error; |
| if (!mojolpm::FromProto(proto_error, error)) |
| return; |
| content::GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&ControllerPresentationServiceDelegateForFuzzing:: |
| CallStartPresentationError, |
| GetWeakPtr(), std::move(error))); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing:: |
| CallStartPresentationError(blink::mojom::PresentationErrorPtr error_ptr) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (start_presentation_error_cb_.is_null()) |
| return; |
| std::move(start_presentation_error_cb_).Run(*std::move(error_ptr)); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing:: |
| HandleReconnectPresentationSuccess( |
| const mojolpm::blink::mojom::PresentationConnectionResult& |
| proto_result) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| blink::mojom::PresentationConnectionResultPtr result; |
| if (!mojolpm::FromProto(proto_result, result)) |
| return; |
| content::GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&ControllerPresentationServiceDelegateForFuzzing:: |
| CallReconnectPresentationSuccess, |
| GetWeakPtr(), std::move(result))); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing:: |
| CallReconnectPresentationSuccess( |
| blink::mojom::PresentationConnectionResultPtr result_ptr) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (reconnect_presentation_success_cb_.is_null()) |
| return; |
| std::move(reconnect_presentation_success_cb_).Run(std::move(result_ptr)); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing:: |
| HandleReconnectPresentationError( |
| const mojolpm::blink::mojom::PresentationError& proto_error) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| blink::mojom::PresentationErrorPtr error; |
| if (!mojolpm::FromProto(proto_error, error)) |
| return; |
| content::GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&ControllerPresentationServiceDelegateForFuzzing:: |
| CallReconnectPresentationError, |
| GetWeakPtr(), std::move(error))); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing:: |
| CallReconnectPresentationError( |
| blink::mojom::PresentationErrorPtr error_ptr) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (reconnect_presentation_error_cb_.is_null()) |
| return; |
| std::move(reconnect_presentation_error_cb_).Run(*std::move(error_ptr)); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing:: |
| HandleListenForConnectionStateChangeStateChanged( |
| const mojolpm::blink::mojom::PresentationConnectionState& |
| proto_connection_state) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| blink::mojom::PresentationConnectionState connection_state; |
| if (!mojolpm::FromProto(proto_connection_state, connection_state)) |
| return; |
| content::GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&ControllerPresentationServiceDelegateForFuzzing:: |
| CallListenForConnectionStateChangeStateChanged, |
| GetWeakPtr(), std::move(connection_state))); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing:: |
| CallListenForConnectionStateChangeStateChanged( |
| blink::mojom::PresentationConnectionState connection_state) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (listen_for_connection_state_change_state_changed_cb_.is_null()) |
| return; |
| content::PresentationConnectionStateChangeInfo state_change_info{ |
| connection_state}; |
| std::move(listen_for_connection_state_change_state_changed_cb_) |
| .Run(state_change_info); |
| } |
| |
| void ControllerPresentationServiceDelegateForFuzzing::NextAction( |
| const Action& action) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| switch (action.action_case()) { |
| case Action::kListenerGetAvailabilityUrl: { |
| HandleListenersGetAvailabilityUrl( |
| action.listener_get_availability_url().listener_availability_url()); |
| } break; |
| |
| case Action::kListenerOnScreenAvailabilityChanged: { |
| HandleListenersOnScreenAvailabilityChanged( |
| action.listener_on_screen_availability_changed() |
| .listener_availability_url(), |
| action.listener_on_screen_availability_changed() |
| .screen_availability()); |
| } break; |
| |
| case Action::kSetDefaultPresentationUrls: { |
| HandleSetDefaultPresentationUrls(action.set_default_presentation_urls() |
| .presentation_connection_result()); |
| } break; |
| |
| case Action::kStartPresentationSuccess: { |
| HandleStartPresentationSuccess( |
| action.start_presentation_success().presentation_connection_result()); |
| } break; |
| |
| case Action::kStartPresentationError: { |
| HandleStartPresentationError( |
| action.start_presentation_error().presentation_error()); |
| } break; |
| |
| case Action::kReconnectPresentationSuccess: { |
| HandleReconnectPresentationSuccess(action.reconnect_presentation_success() |
| .presentation_connection_result()); |
| } break; |
| |
| case Action::kReconnectPresentationError: { |
| HandleReconnectPresentationError( |
| action.reconnect_presentation_error().presentation_error()); |
| } break; |
| |
| case Action::kListenForConnectionStateChangeStateChanged: { |
| HandleListenForConnectionStateChangeStateChanged( |
| action.listen_for_connection_state_change_state_changed() |
| .presentation_connection_state()); |
| } break; |
| |
| case Action::ACTION_NOT_SET: |
| break; |
| } |
| } |