Commit 9ce5394b authored by imcheng's avatar imcheng Committed by Commit bot

[Presentation API] Convert screen availability API into Client style.

Introduced a PresentationServiceClient mojo interface implemented by
PresentationDispatcher. On connection to PresentationService,
PDispatcher will pass a proxy of itself to PSImpl.

Please note that screen availability updates are already throttled
from the browser's perspective.

When a screen availability update is available, it will be transmitted
back to PSDispatcher via the PresentationServiceClient interface.

In the future, I plan to use the same interface for other types of
observer-style APIs (e.g. default session start, session state change,
on message).

Also, simplified the ScreenAvailability API so it only operates on
the DPU (or 1-UA mode if absent). The current semantics to support
listening on arbitrarily presentation URLs + handling behavior for
DPU is very confusing. We should revisit the implementation when we
decide to support it.

This will also hopefully fix the bug where ScreenAvailability mojo
callbacks accumulate over time.

Also fix mojom indent.

BUG=485337

Review URL: https://codereview.chromium.org/1131053005

Cr-Commit-Position: refs/heads/master@{#329457}
parent e8b1380e
......@@ -138,58 +138,62 @@ void PresentationServiceImpl::OnConnectionError() {
delete this;
}
PresentationServiceImpl::ScreenAvailabilityContext*
PresentationServiceImpl::GetOrCreateAvailabilityContext(
const std::string& presentation_url) {
auto it = availability_contexts_.find(presentation_url);
if (it == availability_contexts_.end()) {
linked_ptr<ScreenAvailabilityContext> context(
new ScreenAvailabilityContext(presentation_url));
if (!delegate_->AddScreenAvailabilityListener(
render_process_id_, render_frame_id_, context.get())) {
DVLOG(1) << "AddScreenAvailabilityListener failed. Ignoring request.";
return nullptr;
}
it = availability_contexts_.insert(
std::make_pair(context->GetPresentationUrl(), context)).first;
}
return it->second.get();
void PresentationServiceImpl::SetClient(
presentation::PresentationServiceClientPtr client) {
DCHECK(!client_.get());
// TODO(imcheng): Set ErrorHandler to listen for errors.
client_ = client.Pass();
}
void PresentationServiceImpl::ListenForScreenAvailability(
const mojo::String& presentation_url,
const ScreenAvailabilityMojoCallback& callback) {
void PresentationServiceImpl::ListenForScreenAvailability() {
DVLOG(2) << "ListenForScreenAvailability";
if (!delegate_) {
callback.Run(presentation_url, false);
if (!delegate_)
return;
}
ScreenAvailabilityContext* context =
GetOrCreateAvailabilityContext(presentation_url.get());
if (!context) {
callback.Run(presentation_url, false);
if (screen_availability_listener_.get() &&
screen_availability_listener_->GetPresentationUrl() ==
default_presentation_url_) {
return;
}
context->CallbackReceived(callback);
ResetScreenAvailabilityListener(default_presentation_url_);
}
void PresentationServiceImpl::RemoveScreenAvailabilityListener(
const mojo::String& presentation_url) {
DVLOG(2) << "RemoveScreenAvailabilityListener";
if (!delegate_)
return;
void PresentationServiceImpl::ResetScreenAvailabilityListener(
const std::string& presentation_url) {
DCHECK(delegate_);
DCHECK(!screen_availability_listener_.get() ||
presentation_url != default_presentation_url_);
// (1) Unregister old listener with delegate
StopListeningForScreenAvailability();
const std::string& presentation_url_str = presentation_url.get();
auto it = availability_contexts_.find(presentation_url_str);
if (it == availability_contexts_.end())
// (2) Replace old listener with new listener
screen_availability_listener_.reset(new ScreenAvailabilityListenerImpl(
presentation_url, this));
// (3) Register new listener with delegate
if (!delegate_->AddScreenAvailabilityListener(
render_process_id_,
render_frame_id_,
screen_availability_listener_.get())) {
DVLOG(1) << "AddScreenAvailabilityListener failed. Ignoring request.";
screen_availability_listener_.reset();
}
}
void PresentationServiceImpl::StopListeningForScreenAvailability() {
DVLOG(2) << "StopListeningForScreenAvailability";
if (!delegate_)
return;
if (screen_availability_listener_.get()) {
delegate_->RemoveScreenAvailabilityListener(
render_process_id_, render_frame_id_, it->second.get());
// Resolve the context's pending callbacks before removing it.
it->second->OnScreenAvailabilityChanged(false);
availability_contexts_.erase(it);
render_process_id_,
render_frame_id_,
screen_availability_listener_.get());
screen_availability_listener_.reset();
}
}
void PresentationServiceImpl::ListenForDefaultSessionStart(
......@@ -322,19 +326,6 @@ void PresentationServiceImpl::RunAndEraseNewSessionMojoCallback(
pending_session_cbs_.erase(it);
}
void PresentationServiceImpl::DoSetDefaultPresentationUrl(
const std::string& default_presentation_url,
const std::string& default_presentation_id) {
DCHECK(delegate_);
delegate_->SetDefaultPresentationUrl(
render_process_id_,
render_frame_id_,
default_presentation_url,
default_presentation_id);
default_presentation_url_ = default_presentation_url;
default_presentation_id_ = default_presentation_id;
}
void PresentationServiceImpl::SetDefaultPresentationURL(
const mojo::String& default_presentation_url,
const mojo::String& default_presentation_id) {
......@@ -351,28 +342,19 @@ void PresentationServiceImpl::SetDefaultPresentationURL(
return;
}
auto old_it = availability_contexts_.find(old_default_url);
// Haven't started listening yet.
if (old_it == availability_contexts_.end()) {
DoSetDefaultPresentationUrl(new_default_url, default_presentation_id);
return;
if (old_default_url != new_default_url) {
// If DPU changed, replace screen availability listeners if any.
if (screen_availability_listener_.get())
ResetScreenAvailabilityListener(new_default_url);
}
// Have already started listening. Create a listener for the new URL and
// transfer the callbacks from the old listener, if any.
// This is done so that a listener added before default URL is changed
// will continue to work.
ScreenAvailabilityContext* context =
GetOrCreateAvailabilityContext(new_default_url);
old_it->second->PassPendingCallbacks(context);
// Remove listener for old default presentation URL.
delegate_->RemoveScreenAvailabilityListener(
delegate_->SetDefaultPresentationUrl(
render_process_id_,
render_frame_id_,
old_it->second.get());
availability_contexts_.erase(old_it);
DoSetDefaultPresentationUrl(new_default_url, default_presentation_id);
default_presentation_url,
default_presentation_id);
default_presentation_url_ = default_presentation_url;
default_presentation_id_ = default_presentation_id;
}
......@@ -507,7 +489,7 @@ void PresentationServiceImpl::Reset() {
default_presentation_url_.clear();
default_presentation_id_.clear();
availability_contexts_.clear();
screen_availability_listener_.reset();
queued_start_session_requests_.clear();
FlushNewSessionCallbacks();
default_session_start_context_.reset();
......@@ -545,65 +527,28 @@ void PresentationServiceImpl::OnDefaultPresentationStarted(
default_session_start_context_->set_session(session);
}
PresentationServiceImpl::ScreenAvailabilityContext::ScreenAvailabilityContext(
const std::string& presentation_url)
: presentation_url_(presentation_url) {
PresentationServiceImpl::ScreenAvailabilityListenerImpl
::ScreenAvailabilityListenerImpl(
const std::string& presentation_url,
PresentationServiceImpl* service)
: presentation_url_(presentation_url),
service_(service) {
DCHECK(service_);
DCHECK(service_->client_.get());
}
PresentationServiceImpl::ScreenAvailabilityContext::
~ScreenAvailabilityContext() {
// Ensure that pending callbacks are flushed.
OnScreenAvailabilityChanged(false);
PresentationServiceImpl::ScreenAvailabilityListenerImpl::
~ScreenAvailabilityListenerImpl() {
}
void PresentationServiceImpl::ScreenAvailabilityContext::CallbackReceived(
const ScreenAvailabilityMojoCallback& callback) {
// NOTE: This will overwrite previously registered callback if any.
if (!available_ptr_) {
// No results yet, store callback for later invocation.
callbacks_.push_back(new ScreenAvailabilityMojoCallback(callback));
} else {
// Run callback now, reset result.
// There shouldn't be any callbacks stored in this scenario.
DCHECK(!HasPendingCallbacks());
callback.Run(presentation_url_, *available_ptr_);
available_ptr_.reset();
}
}
std::string PresentationServiceImpl::ScreenAvailabilityContext
std::string PresentationServiceImpl::ScreenAvailabilityListenerImpl
::GetPresentationUrl() const {
return presentation_url_;
}
void PresentationServiceImpl::ScreenAvailabilityContext
void PresentationServiceImpl::ScreenAvailabilityListenerImpl
::OnScreenAvailabilityChanged(bool available) {
if (!HasPendingCallbacks()) {
// No callback, stash the result for now.
available_ptr_.reset(new bool(available));
} else {
// Invoke callbacks and erase them.
// There shouldn't be any result stored in this scenario.
DCHECK(!available_ptr_);
ScopedVector<ScreenAvailabilityMojoCallback> callbacks;
callbacks.swap(callbacks_);
for (const auto& callback : callbacks)
callback->Run(presentation_url_, available);
}
}
void PresentationServiceImpl::ScreenAvailabilityContext
::PassPendingCallbacks(
PresentationServiceImpl::ScreenAvailabilityContext* other) {
std::vector<ScreenAvailabilityMojoCallback*> callbacks;
callbacks_.release(&callbacks);
std::copy(callbacks.begin(), callbacks.end(),
std::back_inserter(other->callbacks_));
}
bool PresentationServiceImpl::ScreenAvailabilityContext
::HasPendingCallbacks() const {
return !callbacks_.empty();
service_->client_->OnScreenAvailabilityUpdated(available);
}
PresentationServiceImpl::StartSessionRequest::StartSessionRequest(
......
......@@ -55,8 +55,30 @@ class CONTENT_EXPORT PresentationServiceImpl
mojo::InterfaceRequest<presentation::PresentationService> request);
private:
using ScreenAvailabilityMojoCallback =
mojo::Callback<void(mojo::String, bool)>;
friend class PresentationServiceImplTest;
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, Reset);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, DidNavigateThisFrame);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest,
DidNavigateOtherFrame);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, ThisRenderFrameDeleted);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest,
OtherRenderFrameDeleted);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, DelegateFails);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest,
SetDefaultPresentationUrl);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest,
SetSameDefaultPresentationUrl);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest,
ClearDefaultPresentationUrl);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest,
ListenForDefaultSessionStart);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest,
ListenForDefaultSessionStartAfterSet);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest,
DefaultSessionStartReset);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest,
ReceiveSessionMessagesAfterReset);
using NewSessionMojoCallback =
mojo::Callback<void(presentation::PresentationSessionInfoPtr,
presentation::PresentationErrorPtr)>;
......@@ -69,48 +91,25 @@ class CONTENT_EXPORT PresentationServiceImpl
mojo::Callback<void(mojo::Array<presentation::SessionMessagePtr>)>;
using SendMessageMojoCallback = mojo::Callback<void(bool)>;
// A helper data class used by PresentationServiceImpl to do bookkeeping
// of currently registered screen availability listeners.
// An instance of this class is a simple state machine that waits for both
// the available bit and the Mojo callback to become available.
// Once this happens, the Mojo callback will be invoked with the available
// bit, and the state machine will reset.
// The available bit is obtained from the embedder's media router.
// The callback is obtained from the renderer via PresentationServiceImpl's
// ListenForScreenAvailability().
class CONTENT_EXPORT ScreenAvailabilityContext
// Listener implementation owned by PresentationServiceImpl. An instance of
// this is created when an |onavailablechange| handler is added.
// The instance receives screen availability results from the embedder and
// propagates results back to PresentationServiceImpl.
class CONTENT_EXPORT ScreenAvailabilityListenerImpl
: public PresentationScreenAvailabilityListener {
public:
explicit ScreenAvailabilityContext(
const std::string& presentation_url);
~ScreenAvailabilityContext() override;
// If available bit exists, |callback| will be invoked with the bit and
// this state machine will reset.
// Otherwise |callback| is saved for later use.
// |callback|: Callback to the client of PresentationService
// (i.e. the renderer) that screen availability has changed, via Mojo.
void CallbackReceived(const ScreenAvailabilityMojoCallback& callback);
ScreenAvailabilityListenerImpl(
const std::string& presentation_url,
PresentationServiceImpl* service);
~ScreenAvailabilityListenerImpl() override;
// PresentationScreenAvailabilityListener implementation.
std::string GetPresentationUrl() const override;
// If callback exists, it will be invoked with |available| and
// this state machine will reset.
// Otherwise |available| is saved for later use.
// |available|: New screen availability for the presentation URL.
void OnScreenAvailabilityChanged(bool available) override;
// Pass this context's queued callbacks to another context.
void PassPendingCallbacks(ScreenAvailabilityContext* other);
// Indicates if this context has any pending callbacks.
bool HasPendingCallbacks() const;
private:
std::string presentation_url_;
ScopedVector<ScreenAvailabilityMojoCallback> callbacks_;
scoped_ptr<bool> available_ptr_;
const std::string presentation_url_;
PresentationServiceImpl* const service_;
};
class CONTENT_EXPORT DefaultSessionStartContext {
......@@ -155,31 +154,6 @@ class CONTENT_EXPORT PresentationServiceImpl
NewSessionMojoCallback callback_;
};
friend class PresentationServiceImplTest;
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, Reset);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest,
DidNavigateThisFrame);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest,
DidNavigateNotThisFrame);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest,
ThisRenderFrameDeleted);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest,
NotThisRenderFrameDeleted);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest,
SetDefaultPresentationUrl);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest,
SetSameDefaultPresentationUrl);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest,
ClearDefaultPresentationUrl);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest,
ListenForDefaultSessionStart);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest,
ListenForDefaultSessionStartAfterSet);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest,
DefaultSessionStartReset);
FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest,
ReceiveSessionMessagesAfterReset);
// |render_frame_host|: The RFH this instance is associated with.
// |web_contents|: The WebContents to observe.
// |delegate|: Where Presentation API requests are delegated to. Not owned
......@@ -193,11 +167,9 @@ class CONTENT_EXPORT PresentationServiceImpl
void SetDefaultPresentationURL(
const mojo::String& presentation_url,
const mojo::String& presentation_id) override;
void ListenForScreenAvailability(
const mojo::String& presentation_url,
const ScreenAvailabilityMojoCallback& callback) override;
void RemoveScreenAvailabilityListener(
const mojo::String& presentation_url) override;
void SetClient(presentation::PresentationServiceClientPtr client) override;
void ListenForScreenAvailability() override;
void StopListeningForScreenAvailability() override;
void ListenForDefaultSessionStart(
const DefaultSessionMojoCallback& callback) override;
void StartSession(
......@@ -246,11 +218,9 @@ class CONTENT_EXPORT PresentationServiceImpl
presentation::PresentationSessionInfoPtr session,
presentation::PresentationErrorPtr error);
// Sets |default_presentation_url_| to |presentation_url| and informs the
// delegate of new default presentation URL and ID.
void DoSetDefaultPresentationUrl(
const std::string& presentation_url,
const std::string& presentation_id);
// Creates a new screen availability listener for |presentation_url| and
// registers it with |delegate_|. Replaces the existing listener if any.
void ResetScreenAvailabilityListener(const std::string& presentation_url);
// Removes all listeners and resets default presentation URL on this instance
// and informs the PresentationServiceDelegate of such.
......@@ -299,23 +269,22 @@ class CONTENT_EXPORT PresentationServiceImpl
static void InvokeNewSessionMojoCallbackWithError(
const NewSessionMojoCallback& callback);
// Gets the ScreenAvailabilityContext for |presentation_url|, or creates one
// if it does not exist.
ScreenAvailabilityContext* GetOrCreateAvailabilityContext(
const std::string& presentation_url);
// Returns true if this object is associated with |render_frame_host|.
bool FrameMatches(content::RenderFrameHost* render_frame_host) const;
// Embedder-specific delegate to forward Presentation requests to.
// May be null if embedder does not support Presentation API.
PresentationServiceDelegate* delegate_;
// Map from presentation URL to its ScreenAvailabilityContext state machine.
base::hash_map<std::string, linked_ptr<ScreenAvailabilityContext>>
availability_contexts_;
// Proxy to the PresentationServiceClient to send results (e.g., screen
// availability) to.
presentation::PresentationServiceClientPtr client_;
std::string default_presentation_url_;
std::string default_presentation_id_;
scoped_ptr<ScreenAvailabilityListenerImpl> screen_availability_listener_;
// We only allow one StartSession request to be processed at a time.
// StartSession requests are queued here. When a request has been processed,
// it is removed from head of the queue.
......
......@@ -46,19 +46,18 @@ interface PresentationService {
string default_presentation_url,
string? default_presentation_id);
// Returns the last screen availability state if it’s changed since the last
// time the method was called. The client has to call this method again when
// handling the result (provided via Mojo callback) to get the next update
// about the availability status.
// May start discovery of the presentation screens. The implementation might
// stop discovery once there are no active calls to
// ListenForScreenAvailability. |presentation_url| can be specified to help
// the implementation to filter out incompatible screens.
ListenForScreenAvailability(string? presentation_url) =>
(string? presentation_url, bool available);
// Called when the frame no longer listens to the |availablechange| event.
RemoveScreenAvailabilityListener(string? presentation_url);
// Sets the PresentationServiceClient.
SetClient(PresentationServiceClient client);
// Starts listening for screen availability for the current default
// presentation URL. Availability results will be returned to the client
// via PresentationServiceClient::OnScreenAvailabilityUpdated.
ListenForScreenAvailability();
// Stops listening for screen availability for the current default
// default presentation URL. The client will stop receiving availability
// updates.
StopListeningForScreenAvailability();
// Called when the renderer is ready to receive the browser initiated
// session. If the default session is started by the embedder before this
......@@ -110,3 +109,7 @@ interface PresentationService {
ListenForSessionMessages()
=> (array<SessionMessage>? messages);
};
interface PresentationServiceClient {
OnScreenAvailabilityUpdated(bool available);
};
......@@ -60,7 +60,8 @@ namespace content {
PresentationDispatcher::PresentationDispatcher(RenderFrame* render_frame)
: RenderFrameObserver(render_frame),
controller_(nullptr) {
controller_(nullptr),
binding_(this) {
}
PresentationDispatcher::~PresentationDispatcher() {
......@@ -80,21 +81,11 @@ void PresentationDispatcher::setController(
}
void PresentationDispatcher::updateAvailableChangeWatched(bool watched) {
GURL presentation_url(GetPresentationURLFromFrame(render_frame()));
DoUpdateAvailableChangeWatched(presentation_url.spec(), watched);
}
void PresentationDispatcher::DoUpdateAvailableChangeWatched(
const std::string& presentation_url, bool watched) {
ConnectToPresentationServiceIfNeeded();
if (watched) {
presentation_service_->ListenForScreenAvailability(
presentation_url,
base::Bind(&PresentationDispatcher::OnScreenAvailabilityChanged,
base::Unretained(this)));
} else {
presentation_service_->RemoveScreenAvailabilityListener(presentation_url);
}
if (watched)
presentation_service_->ListenForScreenAvailability();
else
presentation_service_->StopListeningForScreenAvailability();
}
void PresentationDispatcher::startSession(
......@@ -270,15 +261,8 @@ void PresentationDispatcher::DidCommitProvisionalLoad(
std::swap(message_request_queue_, empty);
}
void PresentationDispatcher::OnScreenAvailabilityChanged(
const std::string& presentation_url, bool available) {
if (!controller_)
return;
// Reset the callback to get the next event.
DoUpdateAvailableChangeWatched(presentation_url,
controller_->isAvailableChangeWatched());
void PresentationDispatcher::OnScreenAvailabilityUpdated(bool available) {
if (controller_)
controller_->didChangeAvailability(available);
}
......@@ -360,6 +344,10 @@ void PresentationDispatcher::ConnectToPresentationServiceIfNeeded() {
render_frame()->GetServiceRegistry()->ConnectToRemoteService(
&presentation_service_);
presentation::PresentationServiceClientPtr client_ptr;
binding_.Bind(GetProxy(&client_ptr));
presentation_service_->SetClient(client_ptr.Pass());
// TODO(imcheng): Uncomment these once they are implemented on the browser
// side. (crbug.com/459006)
/*
......
......@@ -22,7 +22,8 @@ namespace content {
// Blink. It forwards the calls to the Mojo PresentationService.
class CONTENT_EXPORT PresentationDispatcher
: public RenderFrameObserver,
public NON_EXPORTED_BASE(blink::WebPresentationClient) {
public NON_EXPORTED_BASE(blink::WebPresentationClient),
public NON_EXPORTED_BASE(presentation::PresentationServiceClient) {
public:
explicit PresentationDispatcher(RenderFrame* render_frame);
~PresentationDispatcher() override;
......@@ -59,9 +60,9 @@ class CONTENT_EXPORT PresentationDispatcher
bool is_new_navigation,
bool is_same_page_navigation) override;
void OnScreenAvailabilityChanged(
const std::string& presentation_url,
bool available);
// presentation::PresentationServiceClient
void OnScreenAvailabilityUpdated(bool available) override;
void OnSessionCreated(
blink::WebPresentationSessionClientCallbacks* callback,
presentation::PresentationSessionInfoPtr session_info,
......@@ -73,25 +74,23 @@ class CONTENT_EXPORT PresentationDispatcher
presentation::PresentationSessionState session_state);
void OnSessionMessagesReceived(
mojo::Array<presentation::SessionMessagePtr> messages);
void ConnectToPresentationServiceIfNeeded();
void DoUpdateAvailableChangeWatched(
const std::string& presentation_url,
bool watched);
void DoSendMessage(const presentation::SessionMessage& session_message);
void HandleSendMessageRequests(bool success);
void ConnectToPresentationServiceIfNeeded();
// Used as a weak reference. Can be null since lifetime is bound to the frame.
blink::WebPresentationController* controller_;
presentation::PresentationServicePtr presentation_service_;
mojo::Binding<presentation::PresentationServiceClient> binding_;
// Message requests are queued here and only one message at a time is sent
// over mojo channel.
using MessageRequestQueue =
std::queue<linked_ptr<presentation::SessionMessage>>;
MessageRequestQueue message_request_queue_;
DISALLOW_COPY_AND_ASSIGN(PresentationDispatcher);
};
} // namespace content
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment