Commit 2ed5f820 authored by imcheng's avatar imcheng Committed by Commit bot

Reland of: [Presentation API] Implement ondefaultsessionstart in PSImpl.

To fix PresentationServiceImplTest.DefaultSessionStartReset timeout
in  Android Tests (dbg).

TEST=git cl try + linux_android_dbg_ng
----

Added DefaultSessionStartContext for coordinating sending default
session back to PresentationDispatcher.

When ListenForDefaultSessionStart is called, DefaultSessionStartContext
will be installed on PresentationServiceImpl. When both the default
session and PresentationDispatcher's callback are available, the
callback will be invoked with the session.

On Reset(), if a callback is available, it will be invoked with null.
Changed PresentationDispatcher to not update Blink in that case.

Also, PSImpl now keeps track of the corresponding RFH's ID instead of
RFH* since most of the time we only need to use the ID.

Changed PresentationServiceDelegate's Add/RemoveObserver interface,
since the PresentationServiceDelegate need to be able to correlate
an Observer with a RFH. (at most 1 per RFH, as it stands today).

Added OnDefaultPresentationStarted to
PresentationServiceDelegate::Observer interface and implemented it
in PresentationServiceImpl.

Added tests in PresentationServiceImpl.

BUG=459001

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

Cr-Commit-Position: refs/heads/master@{#326859}
parent 5f663d8a
......@@ -23,23 +23,24 @@ PresentationServiceImpl::PresentationServiceImpl(
WebContents* web_contents,
PresentationServiceDelegate* delegate)
: WebContentsObserver(web_contents),
render_frame_host_(render_frame_host),
delegate_(delegate),
is_start_session_pending_(false),
next_request_session_id_(0),
weak_factory_(this) {
DCHECK(render_frame_host_);
DCHECK(render_frame_host);
DCHECK(web_contents);
render_process_id_ = render_frame_host->GetProcess()->GetID();
render_frame_id_ = render_frame_host->GetRoutingID();
DVLOG(2) << "PresentationServiceImpl: "
<< render_frame_host_->GetProcess()->GetID() << ", "
<< render_frame_host_->GetRoutingID();
<< render_process_id_ << ", " << render_frame_id_;
if (delegate_)
delegate_->AddObserver(this);
delegate_->AddObserver(render_process_id_, render_frame_id_, this);
}
PresentationServiceImpl::~PresentationServiceImpl() {
if (delegate_)
delegate_->RemoveObserver(this);
delegate_->RemoveObserver(render_process_id_, render_frame_id_);
FlushNewSessionCallbacks();
}
......@@ -83,9 +84,7 @@ PresentationServiceImpl::GetOrCreateAvailabilityContext(
linked_ptr<ScreenAvailabilityContext> context(
new ScreenAvailabilityContext(presentation_url));
if (!delegate_->AddScreenAvailabilityListener(
render_frame_host_->GetProcess()->GetID(),
render_frame_host_->GetRoutingID(),
context.get())) {
render_process_id_, render_frame_id_, context.get())) {
DVLOG(1) << "AddScreenAvailabilityListener failed. Ignoring request.";
return nullptr;
}
......@@ -125,9 +124,7 @@ void PresentationServiceImpl::RemoveScreenAvailabilityListener(
return;
delegate_->RemoveScreenAvailabilityListener(
render_frame_host_->GetProcess()->GetID(),
render_frame_host_->GetRoutingID(),
it->second.get());
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);
......@@ -135,7 +132,9 @@ void PresentationServiceImpl::RemoveScreenAvailabilityListener(
void PresentationServiceImpl::ListenForDefaultSessionStart(
const DefaultSessionMojoCallback& callback) {
NOTIMPLEMENTED();
if (!default_session_start_context_.get())
default_session_start_context_.reset(new DefaultSessionStartContext);
default_session_start_context_->AddCallback(callback);
}
void PresentationServiceImpl::StartSession(
......@@ -169,8 +168,8 @@ void PresentationServiceImpl::JoinSession(
int request_session_id = RegisterNewSessionCallback(callback);
delegate_->JoinSession(
render_frame_host_->GetProcess()->GetID(),
render_frame_host_->GetRoutingID(),
render_process_id_,
render_frame_id_,
presentation_url,
presentation_id,
base::Bind(&PresentationServiceImpl::OnStartOrJoinSessionSucceeded,
......@@ -214,8 +213,8 @@ void PresentationServiceImpl::DoStartSession(
int request_session_id = RegisterNewSessionCallback(callback);
is_start_session_pending_ = true;
delegate_->StartSession(
render_frame_host_->GetProcess()->GetID(),
render_frame_host_->GetRoutingID(),
render_process_id_,
render_frame_id_,
presentation_url,
presentation_id,
base::Bind(&PresentationServiceImpl::OnStartOrJoinSessionSucceeded,
......@@ -266,8 +265,8 @@ void PresentationServiceImpl::DoSetDefaultPresentationUrl(
const std::string& default_presentation_id) {
DCHECK(delegate_);
delegate_->SetDefaultPresentationUrl(
render_frame_host_->GetProcess()->GetID(),
render_frame_host_->GetRoutingID(),
render_process_id_,
render_frame_id_,
default_presentation_url,
default_presentation_id);
default_presentation_url_ = default_presentation_url;
......@@ -307,8 +306,8 @@ void PresentationServiceImpl::SetDefaultPresentationURL(
// Remove listener for old default presentation URL.
delegate_->RemoveScreenAvailabilityListener(
render_frame_host_->GetProcess()->GetID(),
render_frame_host_->GetRoutingID(),
render_process_id_,
render_frame_id_,
old_it->second.get());
availability_contexts_.erase(old_it);
DoSetDefaultPresentationUrl(new_default_url, default_presentation_id);
......@@ -325,12 +324,21 @@ void PresentationServiceImpl::ListenForSessionStateChange(
NOTIMPLEMENTED();
}
bool PresentationServiceImpl::FrameMatches(
content::RenderFrameHost* render_frame_host) const {
if (!render_frame_host)
return false;
return render_frame_host->GetProcess()->GetID() == render_process_id_ &&
render_frame_host->GetRoutingID() == render_frame_id_;
}
void PresentationServiceImpl::DidNavigateAnyFrame(
content::RenderFrameHost* render_frame_host,
const content::LoadCommittedDetails& details,
const content::FrameNavigateParams& params) {
DVLOG(2) << "PresentationServiceImpl::DidNavigateAnyFrame";
if (render_frame_host_ != render_frame_host)
if (!FrameMatches(render_frame_host))
return;
std::string prev_url_host = details.previous_url.host();
......@@ -355,29 +363,26 @@ void PresentationServiceImpl::DidNavigateAnyFrame(
void PresentationServiceImpl::RenderFrameDeleted(
content::RenderFrameHost* render_frame_host) {
DVLOG(2) << "PresentationServiceImpl::RenderFrameDeleted";
if (render_frame_host_ != render_frame_host)
if (!FrameMatches(render_frame_host))
return;
// RenderFrameDeleted means |render_frame_host_| is going to be deleted soon.
// RenderFrameDeleted means the associated RFH is going to be deleted soon.
// This object should also be deleted.
Reset();
render_frame_host_ = nullptr;
delete this;
}
void PresentationServiceImpl::Reset() {
DVLOG(2) << "PresentationServiceImpl::Reset";
if (delegate_) {
delegate_->Reset(
render_frame_host_->GetProcess()->GetID(),
render_frame_host_->GetRoutingID());
}
if (delegate_)
delegate_->Reset(render_process_id_, render_frame_id_);
default_presentation_url_.clear();
default_presentation_id_.clear();
availability_contexts_.clear();
queued_start_session_requests_.clear();
FlushNewSessionCallbacks();
default_session_start_context_.reset();
}
// static
......@@ -395,6 +400,12 @@ void PresentationServiceImpl::OnDelegateDestroyed() {
Reset();
}
void PresentationServiceImpl::OnDefaultPresentationStarted(
const PresentationSessionInfo& session) {
if (default_session_start_context_.get())
default_session_start_context_->set_session(session);
}
PresentationServiceImpl::ScreenAvailabilityContext::ScreenAvailabilityContext(
const std::string& presentation_url)
: presentation_url_(presentation_url) {
......@@ -478,5 +489,46 @@ PresentationServiceImpl::StartSessionRequest::PassCallback() {
return callback;
}
PresentationServiceImpl::DefaultSessionStartContext
::DefaultSessionStartContext() {
}
PresentationServiceImpl::DefaultSessionStartContext
::~DefaultSessionStartContext() {
Reset();
}
void PresentationServiceImpl::DefaultSessionStartContext::AddCallback(
const DefaultSessionMojoCallback& callback) {
if (session_.get()) {
DCHECK(callbacks_.empty());
callback.Run(presentation::PresentationSessionInfo::From(*session_));
session_.reset();
} else {
callbacks_.push_back(new DefaultSessionMojoCallback(callback));
}
}
void PresentationServiceImpl::DefaultSessionStartContext::set_session(
const PresentationSessionInfo& session) {
if (callbacks_.empty()) {
session_.reset(new PresentationSessionInfo(session));
} else {
DCHECK(!session_.get());
ScopedVector<DefaultSessionMojoCallback> callbacks;
callbacks.swap(callbacks_);
for (const auto& callback : callbacks)
callback->Run(presentation::PresentationSessionInfo::From(session));
}
}
void PresentationServiceImpl::DefaultSessionStartContext::Reset() {
ScopedVector<DefaultSessionMojoCallback> callbacks;
callbacks.swap(callbacks_);
for (const auto& callback : callbacks)
callback->Run(presentation::PresentationSessionInfoPtr());
session_.reset();
}
} // namespace content
......@@ -109,6 +109,27 @@ class CONTENT_EXPORT PresentationServiceImpl
scoped_ptr<bool> available_ptr_;
};
class CONTENT_EXPORT DefaultSessionStartContext {
public:
DefaultSessionStartContext();
~DefaultSessionStartContext();
// Adds a callback. May invoke the callback immediately if |session| using
// default presentation URL was already started.
void AddCallback(const DefaultSessionMojoCallback& callback);
// Sets the session info. Maybe invoke callbacks queued with AddCallback().
void set_session(const PresentationSessionInfo& session);
private:
// Flush all queued callbacks by invoking them with null
// PresentationSessionInfoPtr.
void Reset();
ScopedVector<DefaultSessionMojoCallback> callbacks_;
scoped_ptr<PresentationSessionInfo> session_;
};
// Context for a StartSession request.
class CONTENT_EXPORT StartSessionRequest {
public:
......@@ -146,6 +167,12 @@ class CONTENT_EXPORT PresentationServiceImpl
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);
// |render_frame_host|: The RFH this instance is associated with.
// |web_contents|: The WebContents to observe.
......@@ -197,6 +224,8 @@ class CONTENT_EXPORT PresentationServiceImpl
// PresentationServiceDelegate::Observer
void OnDelegateDestroyed() override;
void OnDefaultPresentationStarted(const PresentationSessionInfo& session)
override;
// Finds the callback from |pending_session_cbs_| using |request_session_id|.
// If it exists, invoke it with |session| and |error|, then erase it from
......@@ -256,7 +285,9 @@ class CONTENT_EXPORT PresentationServiceImpl
ScreenAvailabilityContext* GetOrCreateAvailabilityContext(
const std::string& presentation_url);
RenderFrameHost* render_frame_host_;
// Returns true if this object is associated with |render_frame_host|.
bool FrameMatches(content::RenderFrameHost* render_frame_host) const;
PresentationServiceDelegate* delegate_;
// Map from presentation URL to its ScreenAvailabilityContext state machine.
......@@ -277,10 +308,16 @@ class CONTENT_EXPORT PresentationServiceImpl
int next_request_session_id_;
base::hash_map<int, linked_ptr<NewSessionMojoCallback>> pending_session_cbs_;
scoped_ptr<DefaultSessionStartContext> default_session_start_context_;
// RAII binding of |this| to an Presentation interface request.
// The binding is removed when binding_ is cleared or goes out of scope.
scoped_ptr<mojo::Binding<presentation::PresentationService>> binding_;
// ID of the RenderFrameHost this object is associated with.
int render_process_id_;
int render_frame_id_;
// NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<PresentationServiceImpl> weak_factory_;
......
......@@ -5,6 +5,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/test/test_timeouts.h"
#include "content/browser/presentation/presentation_service_impl.h"
#include "content/public/browser/presentation_service_delegate.h"
#include "content/public/browser/presentation_session.h"
......@@ -23,12 +24,23 @@ using ::testing::SaveArg;
namespace content {
namespace {
bool ArePresentationSessionsEqual(
const presentation::PresentationSessionInfo& expected,
const presentation::PresentationSessionInfo& actual) {
return expected.url == actual.url && expected.id == actual.id;
}
} // namespace
class MockPresentationServiceDelegate : public PresentationServiceDelegate {
public:
MOCK_METHOD1(AddObserver,
void(PresentationServiceDelegate::Observer* observer));
MOCK_METHOD1(RemoveObserver,
void(PresentationServiceDelegate::Observer* observer));
MOCK_METHOD3(AddObserver,
void(int render_process_id,
int render_frame_id,
PresentationServiceDelegate::Observer* observer));
MOCK_METHOD2(RemoveObserver,
void(int render_process_id, int render_frame_id));
MOCK_METHOD3(AddScreenAvailabilityListener,
bool(
int render_process_id,
......@@ -69,13 +81,15 @@ class MockPresentationServiceDelegate : public PresentationServiceDelegate {
class PresentationServiceImplTest : public RenderViewHostImplTestHarness {
public:
PresentationServiceImplTest() : callback_count_(0) {}
PresentationServiceImplTest()
: callback_count_(0), default_session_started_count_(0) {}
void SetUp() override {
RenderViewHostImplTestHarness::SetUp();
auto request = mojo::GetProxy(&service_ptr_);
EXPECT_CALL(mock_delegate_, AddObserver(_)).Times(1);
EXPECT_CALL(mock_delegate_, AddObserver(_, _, _)).Times(1);
service_impl_.reset(new PresentationServiceImpl(
contents()->GetMainFrame(), contents(), &mock_delegate_));
service_impl_->Bind(request.Pass());
......@@ -84,11 +98,9 @@ class PresentationServiceImplTest : public RenderViewHostImplTestHarness {
void TearDown() override {
service_ptr_.reset();
if (service_impl_.get()) {
EXPECT_CALL(mock_delegate_, RemoveObserver(Eq(service_impl_.get())))
.Times(1);
EXPECT_CALL(mock_delegate_, RemoveObserver(_, _)).Times(1);
service_impl_.reset();
}
RenderViewHostImplTestHarness::TearDown();
}
......@@ -155,8 +167,7 @@ class PresentationServiceImplTest : public RenderViewHostImplTestHarness {
}
void ExpectReset() {
EXPECT_CALL(mock_delegate_, Reset(_, _))
.Times(1);
EXPECT_CALL(mock_delegate_, Reset(_, _)).Times(1);
}
void ExpectCleanState() {
......@@ -164,6 +175,7 @@ class PresentationServiceImplTest : public RenderViewHostImplTestHarness {
EXPECT_TRUE(service_impl_->default_presentation_url_.empty());
EXPECT_TRUE(service_impl_->default_presentation_id_.empty());
EXPECT_TRUE(service_impl_->queued_start_session_requests_.empty());
EXPECT_FALSE(service_impl_->default_session_start_context_.get());
}
void ExpectNewSessionMojoCallbackSuccess(
......@@ -184,11 +196,31 @@ class PresentationServiceImplTest : public RenderViewHostImplTestHarness {
run_loop_quit_closure_.Run();
}
void ExpectDefaultSessionStarted(
const presentation::PresentationSessionInfo& expected_session,
presentation::PresentationSessionInfoPtr actual_session) {
ASSERT_TRUE(!actual_session.is_null());
EXPECT_TRUE(ArePresentationSessionsEqual(
expected_session, *actual_session));
++default_session_started_count_;
if (!run_loop_quit_closure_.is_null())
run_loop_quit_closure_.Run();
}
void ExpectDefaultSessionNull(
presentation::PresentationSessionInfoPtr actual_session) {
EXPECT_TRUE(actual_session.is_null());
++default_session_started_count_;
if (!run_loop_quit_closure_.is_null())
run_loop_quit_closure_.Run();
}
MockPresentationServiceDelegate mock_delegate_;
scoped_ptr<PresentationServiceImpl> service_impl_;
mojo::InterfacePtr<presentation::PresentationService> service_ptr_;
base::Closure run_loop_quit_closure_;
int callback_count_;
int default_session_started_count_;
};
TEST_F(PresentationServiceImplTest, ListenForScreenAvailability) {
......@@ -294,7 +326,7 @@ TEST_F(PresentationServiceImplTest, ThisRenderFrameDeleted) {
// Since the frame matched the service, |service_impl_| will be deleted.
PresentationServiceImpl* service = service_impl_.release();
EXPECT_CALL(mock_delegate_, RemoveObserver(Eq(service))).Times(1);
EXPECT_CALL(mock_delegate_, RemoveObserver(_, _)).Times(1);
service->RenderFrameDeleted(contents()->GetMainFrame());
}
......@@ -581,4 +613,59 @@ TEST_F(PresentationServiceImplTest, StartSessionInProgress) {
SaveQuitClosureAndRunLoop();
}
TEST_F(PresentationServiceImplTest, ListenForDefaultSessionStart) {
std::string presentation_url1("http://fooUrl1");
std::string presentation_id1("presentationId1");
presentation::PresentationSessionInfo expected_session;
expected_session.url = presentation_url1;
expected_session.id = presentation_id1;
service_ptr_->ListenForDefaultSessionStart(
base::Bind(&PresentationServiceImplTest::ExpectDefaultSessionStarted,
base::Unretained(this),
expected_session));
RunLoopFor(base::TimeDelta::FromMilliseconds(50));
service_impl_->OnDefaultPresentationStarted(
content::PresentationSessionInfo(presentation_url1, presentation_id1));
SaveQuitClosureAndRunLoop();
EXPECT_EQ(1, default_session_started_count_);
}
TEST_F(PresentationServiceImplTest, ListenForDefaultSessionStartAfterSet) {
// Note that the callback will only pick up presentation_url2/id2 since
// ListenForDefaultSessionStart wasn't called yet when the DPU was still
// presentation_url1.
std::string presentation_url1("http://fooUrl1");
std::string presentation_id1("presentationId1");
std::string presentation_url2("http://fooUrl2");
std::string presentation_id2("presentationId2");
service_impl_->OnDefaultPresentationStarted(
content::PresentationSessionInfo(presentation_url1, presentation_id1));
presentation::PresentationSessionInfo expected_session;
expected_session.url = presentation_url2;
expected_session.id = presentation_id2;
service_ptr_->ListenForDefaultSessionStart(
base::Bind(&PresentationServiceImplTest::ExpectDefaultSessionStarted,
base::Unretained(this),
expected_session));
RunLoopFor(base::TimeDelta::FromMilliseconds(50));
service_impl_->OnDefaultPresentationStarted(
content::PresentationSessionInfo(presentation_url2, presentation_id2));
SaveQuitClosureAndRunLoop();
EXPECT_EQ(1, default_session_started_count_);
}
TEST_F(PresentationServiceImplTest, DefaultSessionStartReset) {
service_ptr_->ListenForDefaultSessionStart(
base::Bind(&PresentationServiceImplTest::ExpectDefaultSessionNull,
base::Unretained(this)));
RunLoopFor(TestTimeouts::tiny_timeout());
ExpectReset();
service_impl_->Reset();
ExpectCleanState();
SaveQuitClosureAndRunLoop();
EXPECT_EQ(1, default_session_started_count_);
}
} // namespace content
......@@ -52,7 +52,7 @@ interface PresentationService {
// call, the embedder may queue it and run the callback when the call is
// performed.
ListenForDefaultSessionStart()
=> (PresentationSessionInfo defaultSessionInfo);
=> (PresentationSessionInfo? defaultSessionInfo);
// Called when startSession() is called by the frame. The result callback
// will return a non-null and valid PresentationSessionInfo if starting the
......
......@@ -23,6 +23,13 @@ class CONTENT_EXPORT PresentationServiceDelegate {
// Called when the PresentationServiceDelegate is being destroyed.
virtual void OnDelegateDestroyed() = 0;
// Called when the default presentation has been started outside of a
// Presentation API context (e.g., browser action). This will not be called
// if the session was created as a result of Presentation API's
// StartSession()/JoinSession().
virtual void OnDefaultPresentationStarted(
const PresentationSessionInfo& session) = 0;
protected:
virtual ~Observer() {}
};
......@@ -34,12 +41,19 @@ class CONTENT_EXPORT PresentationServiceDelegate {
virtual ~PresentationServiceDelegate() {}
// Registers an observer with this class to listen for updates to this class.
// Registers an observer associated with frame with |render_process_id|
// and |render_frame_id| with this class to listen for updates.
// This class does not own the observer.
// It is an error to add an observer if it has already been added before.
virtual void AddObserver(Observer* observer) = 0;
// Unregisters an observer with this class.
virtual void RemoveObserver(Observer* observer) = 0;
// It is an error to add an observer if there is already an observer for that
// frame.
virtual void AddObserver(int render_process_id,
int render_frame_id,
Observer* observer) = 0;
// Unregisters the observer associated with the frame with |render_process_id|
// and |render_frame_id|.
// The observer will no longer receive updates.
virtual void RemoveObserver(int render_process_id, int render_frame_id) = 0;
// Registers |listener| to continuously listen for
// availability updates for a presentation URL, originated from the frame
......
......@@ -172,9 +172,10 @@ void PresentationDispatcher::OnDefaultSessionStarted(
&PresentationDispatcher::OnDefaultSessionStarted,
base::Unretained(this)));
DCHECK(!session_info.is_null());
controller_->didStartDefaultSession(
new PresentationSessionClient(session_info.Pass()));
if (!session_info.is_null()) {
controller_->didStartDefaultSession(
new PresentationSessionClient(session_info.Pass()));
}
}
void PresentationDispatcher::OnSessionCreated(
......
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