Media Remoting: Get permission when requested to start.
Get permission to start remoting when harmony cast dialog is enabled.
Bug: 849020
Change-Id: I98e306c7ffaa6f53dc825a418f2c0b48ea7a9ee2
Reviewed-on: https://chromium-review.googlesource.com/1139173
Reviewed-by: Xiaohan Wang <xhwang@chromium.org>
Reviewed-by: Yuri Wiitala <miu@chromium.org>
Reviewed-by: Takumi Fujimoto <takumif@chromium.org>
Commit-Queue: Xiangjun Zhang <xjz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#579226}
diff --git a/chrome/browser/media/DEPS b/chrome/browser/media/DEPS
index 6c2e8e0..4ba0b3c8 100644
--- a/chrome/browser/media/DEPS
+++ b/chrome/browser/media/DEPS
@@ -11,3 +11,9 @@
"+services/data_decoder/public",
"+services/device/public/mojom",
]
+
+specific_include_rules = {
+ "cast_remoting_connector\.cc": [
+ "+chrome/browser/ui/views/media_router/media_remoting_dialog_view.h",
+ ],
+}
diff --git a/chrome/browser/media/cast_remoting_connector.cc b/chrome/browser/media/cast_remoting_connector.cc
index 70b6cb7..3682fdf0 100644
--- a/chrome/browser/media/cast_remoting_connector.cc
+++ b/chrome/browser/media/cast_remoting_connector.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/media/cast_remoting_connector.h"
#include <memory>
+#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
@@ -24,6 +25,11 @@
#include "content/public/browser/web_contents.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
+#if defined(TOOLKIT_VIEWS) && \
+ (!defined(OS_MACOSX) || defined(MAC_VIEWS_BROWSER))
+#include "chrome/browser/ui/views/media_router/media_remoting_dialog_view.h"
+#endif
+
using content::BrowserThread;
using media::mojom::RemotingStartFailReason;
using media::mojom::RemotingStopReason;
@@ -92,7 +98,7 @@
}
void Stop(RemotingStopReason reason) final {
if (connector_)
- connector_->StopRemoting(this, reason);
+ connector_->StopRemoting(this, reason, true);
}
void SendMessageToSink(const std::vector<uint8_t>& message) final {
if (connector_)
@@ -137,7 +143,30 @@
connector = new CastRemotingConnector(
media_router::MediaRouterFactory::GetApiForBrowserContext(
contents->GetBrowserContext()),
- tab_id);
+ tab_id,
+#if defined(TOOLKIT_VIEWS) && \
+ (!defined(OS_MACOSX) || defined(MAC_VIEWS_BROWSER))
+ base::BindRepeating(
+ [](content::WebContents* contents,
+ PermissionResultCallback result_callback) {
+ if (media_router::ShouldUseViewsDialog()) {
+ media_router::MediaRemotingDialogView::GetPermission(
+ contents, std::move(result_callback));
+ return base::BindOnce(
+ &media_router::MediaRemotingDialogView::HideDialog);
+ } else {
+ std::move(result_callback).Run(true);
+ return CancelPermissionRequestCallback();
+ }
+ },
+ contents)
+#else
+ base::BindRepeating([](PermissionResultCallback result_callback) {
+ std::move(result_callback).Run(true);
+ return CancelPermissionRequestCallback();
+ })
+#endif
+ );
contents->SetUserData(kUserDataKey, base::WrapUnique(connector));
}
return connector;
@@ -158,14 +187,18 @@
connector->CreateBridge(std::move(source), std::move(request));
}
-CastRemotingConnector::CastRemotingConnector(media_router::MediaRouter* router,
- SessionID tab_id)
+CastRemotingConnector::CastRemotingConnector(
+ media_router::MediaRouter* router,
+ SessionID tab_id,
+ PermissionRequestCallback permission_request_callback)
: media_router_(router),
tab_id_(tab_id),
+ permission_request_callback_(std::move(permission_request_callback)),
active_bridge_(nullptr),
binding_(this),
weak_factory_(this) {
VLOG(2) << "Register CastRemotingConnector for tab_id = " << tab_id_;
+ DCHECK(permission_request_callback_);
media_router_->RegisterRemotingSource(tab_id_, this);
}
@@ -174,7 +207,7 @@
// it's possible the owning WebContents will be destroyed before the Mojo
// message pipes to the RemotingBridges have been closed.
if (active_bridge_)
- StopRemoting(active_bridge_, RemotingStopReason::ROUTE_TERMINATED);
+ StopRemoting(active_bridge_, RemotingStopReason::ROUTE_TERMINATED, false);
for (RemotingBridge* notifyee : bridges_) {
notifyee->OnSinkGone();
notifyee->OnCastRemotingConnectorDestroyed();
@@ -213,7 +246,7 @@
sink_metadata_ = RemotingSinkMetadata();
if (active_bridge_)
- StopRemoting(active_bridge_, RemotingStopReason::SERVICE_GONE);
+ StopRemoting(active_bridge_, RemotingStopReason::SERVICE_GONE, false);
for (RemotingBridge* notifyee : bridges_)
notifyee->OnSinkGone();
}
@@ -239,9 +272,9 @@
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(bridges_.find(bridge) != bridges_.end());
- if (bridge == active_bridge_)
- StopRemoting(bridge, reason);
bridges_.erase(bridge);
+ if (bridge == active_bridge_)
+ StopRemoting(bridge, reason, true);
}
void CastRemotingConnector::StartRemoting(RemotingBridge* bridge) {
@@ -282,14 +315,15 @@
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!connector)
return;
+ connector->permission_request_cancel_callback_.Reset();
connector->remoting_allowed_ = is_allowed;
connector->StartRemotingIfPermitted();
},
weak_factory_.GetWeakPtr()));
- // TODO(http://crbug.com/849020): Show the remoting dialog to get user's
- // permission.
- std::move(dialog_result_callback).Run(true);
+ DCHECK(!permission_request_cancel_callback_);
+ permission_request_cancel_callback_ =
+ permission_request_callback_.Run(std::move(dialog_result_callback));
}
}
@@ -331,7 +365,7 @@
if ((!audio_pipe.is_valid() && !video_pipe.is_valid()) ||
(audio_pipe.is_valid() && !audio_sender_request.is_pending()) ||
(video_pipe.is_valid() && !video_sender_request.is_pending())) {
- StopRemoting(active_bridge_, RemotingStopReason::DATA_SEND_FAILED);
+ StopRemoting(active_bridge_, RemotingStopReason::DATA_SEND_FAILED, false);
return;
}
@@ -377,7 +411,8 @@
}
void CastRemotingConnector::StopRemoting(RemotingBridge* bridge,
- RemotingStopReason reason) {
+ RemotingStopReason reason,
+ bool is_initiated_by_source) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
VLOG(2) << __func__ << ": reason = " << reason;
@@ -389,6 +424,19 @@
// Cancel all outstanding callbacks related to the remoting session.
weak_factory_.InvalidateWeakPtrs();
+ if (permission_request_cancel_callback_) {
+ std::move(permission_request_cancel_callback_).Run();
+ if (is_initiated_by_source && remoter_) {
+ // The source requested remoting be stopped before the permission request
+ // was resolved. This means the |remoter_| was never started, and remains
+ // in the available state, still all ready to go. Thus, notify the sources
+ // that the sink is available once again.
+ for (RemotingBridge* notifyee : bridges_)
+ notifyee->OnSinkAvailable(sink_metadata_);
+ }
+ return; // Early returns since the |remoter_| was never started.
+ }
+
// Reset |sink_metadata_|. Remoting can only be started after
// OnSinkAvailable() is called again.
sink_metadata_ = RemotingSinkMetadata();
@@ -411,7 +459,7 @@
if (active_bridge_) {
// This call will reset |sink_metadata_| and notify the source that sink is
// gone.
- StopRemoting(active_bridge_, reason);
+ StopRemoting(active_bridge_, reason, false);
} else if (reason == RemotingStopReason::USER_DISABLED) {
// Notify all the sources that the sink is gone. Remoting can only be
// started after OnSinkAvailable() is called again.
@@ -480,7 +528,7 @@
VLOG(2) << __func__;
if (active_bridge_)
- StopRemoting(active_bridge_, RemotingStopReason::UNEXPECTED_FAILURE);
+ StopRemoting(active_bridge_, RemotingStopReason::UNEXPECTED_FAILURE, false);
}
void CastRemotingConnector::OnDataSendFailed() {
@@ -490,5 +538,5 @@
// A single data send failure is treated as fatal to an active remoting
// session.
if (active_bridge_)
- StopRemoting(active_bridge_, RemotingStopReason::DATA_SEND_FAILED);
+ StopRemoting(active_bridge_, RemotingStopReason::DATA_SEND_FAILED, false);
}
diff --git a/chrome/browser/media/cast_remoting_connector.h b/chrome/browser/media/cast_remoting_connector.h
index 397c2be..1878779 100644
--- a/chrome/browser/media/cast_remoting_connector.h
+++ b/chrome/browser/media/cast_remoting_connector.h
@@ -6,6 +6,7 @@
#define CHROME_BROWSER_MEDIA_CAST_REMOTING_CONNECTOR_H_
#include <set>
+#include <vector>
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
@@ -112,7 +113,15 @@
// Main constructor. |tab_id| refers to any remoted content managed
// by this instance (i.e., any remoted content from one tab/WebContents).
- CastRemotingConnector(media_router::MediaRouter* router, SessionID tab_id);
+ using CancelPermissionRequestCallback = base::OnceClosure;
+ // Called with true to mean "allowed", false to mean "not allowed".
+ using PermissionResultCallback = base::OnceCallback<void(bool)>;
+ using PermissionRequestCallback =
+ base::RepeatingCallback<CancelPermissionRequestCallback(
+ PermissionResultCallback)>;
+ CastRemotingConnector(media_router::MediaRouter* router,
+ SessionID tab_id,
+ PermissionRequestCallback request_callback);
// Creates a RemotingBridge that implements the requested Remoter service, and
// binds it to the interface |request|.
@@ -144,7 +153,8 @@
media::mojom::RemotingDataStreamSenderRequest audio_sender_request,
media::mojom::RemotingDataStreamSenderRequest video_sender_request);
void StopRemoting(RemotingBridge* bridge,
- media::mojom::RemotingStopReason reason);
+ media::mojom::RemotingStopReason reason,
+ bool is_initiated_by_source);
void SendMessageToSink(RemotingBridge* bridge,
const std::vector<uint8_t>& message);
void EstimateTransmissionCapacity(
@@ -174,6 +184,9 @@
const SessionID tab_id_;
+ // The callback to get permission.
+ const PermissionRequestCallback permission_request_callback_;
+
// Describes the remoting sink's metadata and its enabled features. The sink's
// metadata is updated by the mirror service calling OnSinkAvailable() and
// cleared when remoting stops.
@@ -195,6 +208,10 @@
// casting session.
base::Optional<bool> remoting_allowed_;
+ // This callback is non-null when a dialog is showing to get user's
+ // permission, and is reset when the dialog closes.
+ CancelPermissionRequestCallback permission_request_cancel_callback_;
+
// Produces weak pointers that are only valid for the current remoting
// session. This is used to cancel any outstanding callbacks when a remoting
// session is stopped.
diff --git a/chrome/browser/media/cast_remoting_connector_unittest.cc b/chrome/browser/media/cast_remoting_connector_unittest.cc
index 2e15e81..b129cc2 100644
--- a/chrome/browser/media/cast_remoting_connector_unittest.cc
+++ b/chrome/browser/media/cast_remoting_connector_unittest.cc
@@ -11,6 +11,7 @@
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
+#include "build/build_config.h"
#include "chrome/browser/media/router/test/mock_media_router.h"
#include "chrome/common/media_router/media_route.h"
#include "chrome/common/media_router/media_source.h"
@@ -164,9 +165,7 @@
class CastRemotingConnectorTest : public ::testing::Test {
public:
- CastRemotingConnectorTest() : connector_(&media_router_, kRemotingTabId) {
- EXPECT_EQ(kRemotingTabId, media_router_.tab_id());
- }
+ CastRemotingConnectorTest() { CreateConnector(true); }
void TearDown() final {
// Allow any pending Mojo operations to complete before destruction. For
@@ -180,8 +179,8 @@
RemotingSourcePtr source_ptr;
source->Bind(mojo::MakeRequest(&source_ptr));
RemoterPtr remoter_ptr;
- connector_.CreateBridge(std::move(source_ptr),
- mojo::MakeRequest(&remoter_ptr));
+ connector_->CreateBridge(std::move(source_ptr),
+ mojo::MakeRequest(&remoter_ptr));
return remoter_ptr;
}
@@ -190,14 +189,29 @@
}
void DisableRemoting() {
- connector_.OnStopped(RemotingStopReason::USER_DISABLED);
+ connector_->OnStopped(RemotingStopReason::USER_DISABLED);
+ }
+
+ void CreateConnector(bool remoting_allowed) {
+ connector_.reset(); // Call dtor first if there is one created.
+ connector_.reset(new CastRemotingConnector(
+ &media_router_, kRemotingTabId,
+ base::BindRepeating(
+ [](bool remoting_allowed,
+ CastRemotingConnector::PermissionResultCallback
+ result_callback) {
+ std::move(result_callback).Run(remoting_allowed);
+ return CastRemotingConnector::CancelPermissionRequestCallback();
+ },
+ remoting_allowed)));
+ EXPECT_EQ(kRemotingTabId, media_router_.tab_id());
}
FakeMediaRouter media_router_;
private:
content::TestBrowserThreadBundle browser_thread_bundle_;
- CastRemotingConnector connector_;
+ std::unique_ptr<CastRemotingConnector> connector_;
};
TEST_F(CastRemotingConnectorTest, NeverNotifiesThatSinkIsAvailable) {
@@ -309,6 +323,20 @@
RunUntilIdle();
}
+TEST_F(CastRemotingConnectorTest, NoPermissionToStart) {
+ CreateConnector(false);
+ MockRemotingSource source;
+ RemoterPtr remoter = CreateRemoter(&source);
+ std::unique_ptr<MockMediaRemoter> media_remoter =
+ std::make_unique<MockMediaRemoter>(&media_router_);
+
+ EXPECT_CALL(source, OnStartFailed(RemotingStartFailReason::ROUTE_TERMINATED))
+ .Times(1);
+ EXPECT_CALL(source, OnSinkGone()).Times(AtLeast(1));
+ remoter->Start();
+ RunUntilIdle();
+}
+
namespace {
// The possible ways a remoting session may be terminated in the "full
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 786ddc7..29a6274 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -3138,7 +3138,8 @@
void WebMediaPlayerImpl::SwitchToLocalRenderer(
MediaObserverClient::ReasonToSwitchToLocal reason) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
- DCHECK(disable_pipeline_auto_suspend_);
+ if (!disable_pipeline_auto_suspend_)
+ return; // Is currently with local renderer.
disable_pipeline_auto_suspend_ = false;
// Capabilities reporting may resume now that playback is local.