Add MRUIBase and MRViewsUI
- Factor out parts of MediaRouterUI that will be shared with the Views implementation of the Cast dialog into MRUIBase - Add a stub MRViewsUI class, which (in a future patch) will be responsible for notifying the Views dialog of sink/route updates - Have MRUI and MRViewsUI subclass MRUIBase Bug: 826091 Change-Id: Id162dd1ab7b0722489f9a61f680e4f5e4de86a36 Reviewed-on: https://chromium-review.googlesource.com/994275 Commit-Queue: Takumi Fujimoto <takumif@chromium.org> Reviewed-by: mark a. foltz <mfoltz@chromium.org> Cr-Commit-Position: refs/heads/master@{#554980}
This commit is contained in:

committed by
Commit Bot

parent
129345b66a
commit
72d48c21cd
chrome
browser
ui
BUILD.gn
media_router
media_router_ui_base.ccmedia_router_ui_base.hmedia_router_ui_helper.ccmedia_router_ui_helper.hmedia_router_ui_helper_unittest.cc
views
media_router
webui
test
@@ -1365,6 +1365,10 @@ split_static_library("ui") {
|
||||
"media_router/media_router_dialog_controller_impl_base.h",
|
||||
"media_router/media_router_file_dialog.cc",
|
||||
"media_router/media_router_file_dialog.h",
|
||||
"media_router/media_router_ui_base.cc",
|
||||
"media_router/media_router_ui_base.h",
|
||||
"media_router/media_router_ui_helper.cc",
|
||||
"media_router/media_router_ui_helper.h",
|
||||
"media_router/media_sink_with_cast_modes.cc",
|
||||
"media_router/media_sink_with_cast_modes.h",
|
||||
"media_router/presentation_receiver_window.h",
|
||||
@@ -3152,6 +3156,8 @@ split_static_library("ui") {
|
||||
"views/media_router/cast_dialog_sink_button.h",
|
||||
"views/media_router/media_router_dialog_controller_views.cc",
|
||||
"views/media_router/media_router_dialog_controller_views.h",
|
||||
"views/media_router/media_router_views_ui.cc",
|
||||
"views/media_router/media_router_views_ui.h",
|
||||
"views/media_router/presentation_receiver_window_factory.cc",
|
||||
"views/media_router/presentation_receiver_window_frame.cc",
|
||||
"views/media_router/presentation_receiver_window_frame.h",
|
||||
|
453
chrome/browser/ui/media_router/media_router_ui_base.cc
Normal file
453
chrome/browser/ui/media_router/media_router_ui_base.cc
Normal file
@@ -0,0 +1,453 @@
|
||||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/ui/media_router/media_router_ui_base.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "build/build_config.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "chrome/browser/media/router/media_router.h"
|
||||
#include "chrome/browser/media/router/media_router_factory.h"
|
||||
#include "chrome/browser/media/router/media_router_feature.h"
|
||||
#include "chrome/browser/media/router/media_router_metrics.h"
|
||||
#include "chrome/browser/media/router/media_routes_observer.h"
|
||||
#include "chrome/browser/media/router/presentation/presentation_service_delegate_impl.h"
|
||||
#include "chrome/browser/profiles/profile.h"
|
||||
#include "chrome/browser/sessions/session_tab_helper.h"
|
||||
#include "chrome/common/media_router/media_route.h"
|
||||
#include "chrome/common/media_router/media_sink.h"
|
||||
#include "chrome/common/media_router/media_source.h"
|
||||
#include "chrome/common/media_router/media_source_helper.h"
|
||||
#include "chrome/common/media_router/route_request_result.h"
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
#include "extensions/common/constants.h"
|
||||
#include "third_party/icu/source/i18n/unicode/coll.h"
|
||||
#include "url/origin.h"
|
||||
|
||||
#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
|
||||
#include "chrome/browser/media/router/providers/wired_display/wired_display_media_route_provider.h"
|
||||
#include "ui/display/display.h"
|
||||
#endif
|
||||
|
||||
namespace media_router {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string TruncateHost(const std::string& host) {
|
||||
const std::string truncated =
|
||||
net::registry_controlled_domains::GetDomainAndRegistry(
|
||||
host, net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
|
||||
// The truncation will be empty in some scenarios (e.g. host is
|
||||
// simply an IP address). Fail gracefully.
|
||||
return truncated.empty() ? host : truncated;
|
||||
}
|
||||
|
||||
// Returns the first source in |sources| that can be connected to, or an empty
|
||||
// source if there is none. This is used by the Media Router to find such a
|
||||
// matching route if it exists.
|
||||
MediaSource GetSourceForRouteObserver(const std::vector<MediaSource>& sources) {
|
||||
auto source_it =
|
||||
std::find_if(sources.begin(), sources.end(), IsCastPresentationUrl);
|
||||
return source_it != sources.end() ? *source_it : MediaSource("");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MediaRouterUIBase::UIMediaRoutesObserver::UIMediaRoutesObserver(
|
||||
MediaRouter* router,
|
||||
const MediaSource::Id& source_id,
|
||||
const RoutesUpdatedCallback& callback)
|
||||
: MediaRoutesObserver(router, source_id), callback_(callback) {
|
||||
DCHECK(!callback_.is_null());
|
||||
}
|
||||
|
||||
MediaRouterUIBase::UIMediaRoutesObserver::~UIMediaRoutesObserver() {}
|
||||
|
||||
void MediaRouterUIBase::UIMediaRoutesObserver::OnRoutesUpdated(
|
||||
const std::vector<MediaRoute>& routes,
|
||||
const std::vector<MediaRoute::Id>& joinable_route_ids) {
|
||||
callback_.Run(routes, joinable_route_ids);
|
||||
}
|
||||
|
||||
MediaRouterUIBase::MediaRouterUIBase()
|
||||
: current_route_request_id_(-1),
|
||||
route_request_counter_(0),
|
||||
initiator_(nullptr),
|
||||
weak_factory_(this) {}
|
||||
|
||||
MediaRouterUIBase::~MediaRouterUIBase() {
|
||||
if (query_result_manager_.get())
|
||||
query_result_manager_->RemoveObserver(this);
|
||||
if (presentation_service_delegate_.get())
|
||||
presentation_service_delegate_->RemoveDefaultPresentationRequestObserver(
|
||||
this);
|
||||
// If |start_presentation_context_| still exists, then it means presentation
|
||||
// route request was never attempted.
|
||||
if (start_presentation_context_) {
|
||||
bool presentation_sinks_available = std::any_of(
|
||||
sinks_.begin(), sinks_.end(), [](const MediaSinkWithCastModes& sink) {
|
||||
return base::ContainsKey(sink.cast_modes,
|
||||
MediaCastMode::PRESENTATION);
|
||||
});
|
||||
if (presentation_sinks_available) {
|
||||
start_presentation_context_->InvokeErrorCallback(
|
||||
blink::mojom::PresentationError(blink::mojom::PresentationErrorType::
|
||||
PRESENTATION_REQUEST_CANCELLED,
|
||||
"Dialog closed."));
|
||||
} else {
|
||||
start_presentation_context_->InvokeErrorCallback(
|
||||
blink::mojom::PresentationError(
|
||||
blink::mojom::PresentationErrorType::NO_AVAILABLE_SCREENS,
|
||||
"No screens found."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MediaRouterUIBase::InitWithDefaultMediaSource(
|
||||
content::WebContents* initiator,
|
||||
PresentationServiceDelegateImpl* delegate) {
|
||||
DCHECK(initiator);
|
||||
DCHECK(!presentation_service_delegate_);
|
||||
DCHECK(!query_result_manager_);
|
||||
|
||||
InitCommon(initiator);
|
||||
if (delegate) {
|
||||
presentation_service_delegate_ = delegate->GetWeakPtr();
|
||||
presentation_service_delegate_->AddDefaultPresentationRequestObserver(this);
|
||||
}
|
||||
|
||||
if (delegate && delegate->HasDefaultPresentationRequest()) {
|
||||
OnDefaultPresentationChanged(delegate->GetDefaultPresentationRequest());
|
||||
} else {
|
||||
// Register for MediaRoute updates without a media source.
|
||||
routes_observer_ = std::make_unique<UIMediaRoutesObserver>(
|
||||
GetMediaRouter(), MediaSource::Id(),
|
||||
base::BindRepeating(&MediaRouterUIBase::OnRoutesUpdated,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
}
|
||||
|
||||
void MediaRouterUIBase::InitWithStartPresentationContext(
|
||||
content::WebContents* initiator,
|
||||
PresentationServiceDelegateImpl* delegate,
|
||||
std::unique_ptr<StartPresentationContext> context) {
|
||||
DCHECK(initiator);
|
||||
DCHECK(delegate);
|
||||
DCHECK(context);
|
||||
DCHECK(!start_presentation_context_);
|
||||
DCHECK(!query_result_manager_);
|
||||
|
||||
start_presentation_context_ = std::move(context);
|
||||
presentation_service_delegate_ = delegate->GetWeakPtr();
|
||||
|
||||
InitCommon(initiator);
|
||||
OnDefaultPresentationChanged(
|
||||
start_presentation_context_->presentation_request());
|
||||
}
|
||||
|
||||
std::vector<MediaSource> MediaRouterUIBase::GetSourcesForCastMode(
|
||||
MediaCastMode cast_mode) const {
|
||||
return query_result_manager_->GetSourcesForCastMode(cast_mode);
|
||||
}
|
||||
|
||||
void MediaRouterUIBase::HandleCreateSessionRequestRouteResponse(
|
||||
const RouteRequestResult&) {}
|
||||
|
||||
void MediaRouterUIBase::InitCommon(content::WebContents* initiator) {
|
||||
DCHECK(initiator);
|
||||
initiator_ = initiator;
|
||||
|
||||
GetMediaRouter()->OnUserGesture();
|
||||
|
||||
// Create |collator_| before |query_result_manager_| so that |collator_| is
|
||||
// already set up when we get a callback from |query_result_manager_|.
|
||||
UErrorCode error = U_ZERO_ERROR;
|
||||
const std::string& locale = g_browser_process->GetApplicationLocale();
|
||||
collator_.reset(
|
||||
icu::Collator::createInstance(icu::Locale(locale.c_str()), error));
|
||||
if (U_FAILURE(error)) {
|
||||
DLOG(ERROR) << "Failed to create collator for locale " << locale;
|
||||
collator_.reset();
|
||||
}
|
||||
|
||||
query_result_manager_ =
|
||||
std::make_unique<QueryResultManager>(GetMediaRouter());
|
||||
query_result_manager_->AddObserver(this);
|
||||
|
||||
// Use a placeholder URL as origin for mirroring.
|
||||
url::Origin origin = url::Origin::Create(GURL());
|
||||
|
||||
// Desktop mirror mode is always available.
|
||||
query_result_manager_->SetSourcesForCastMode(
|
||||
MediaCastMode::DESKTOP_MIRROR, {MediaSourceForDesktop()}, origin);
|
||||
|
||||
// For now, file mirroring is always availible if enabled.
|
||||
if (CastLocalMediaEnabled()) {
|
||||
query_result_manager_->SetSourcesForCastMode(
|
||||
MediaCastMode::LOCAL_FILE, {MediaSourceForTab(0)}, origin);
|
||||
}
|
||||
|
||||
SessionID::id_type tab_id = SessionTabHelper::IdForTab(initiator).id();
|
||||
if (tab_id != -1) {
|
||||
MediaSource mirroring_source(MediaSourceForTab(tab_id));
|
||||
query_result_manager_->SetSourcesForCastMode(MediaCastMode::TAB_MIRROR,
|
||||
{mirroring_source}, origin);
|
||||
}
|
||||
|
||||
// Get the current list of media routes, so that the WebUI will have routes
|
||||
// information at initialization.
|
||||
OnRoutesUpdated(GetMediaRouter()->GetCurrentRoutes(),
|
||||
std::vector<MediaRoute::Id>());
|
||||
#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
|
||||
display_observer_ = WebContentsDisplayObserver::Create(
|
||||
initiator_, base::BindRepeating(&MediaRouterUIBase::UpdateSinks,
|
||||
base::Unretained(this)));
|
||||
#endif
|
||||
}
|
||||
|
||||
void MediaRouterUIBase::OnDefaultPresentationChanged(
|
||||
const content::PresentationRequest& presentation_request) {
|
||||
std::vector<MediaSource> sources =
|
||||
MediaSourcesForPresentationUrls(presentation_request.presentation_urls);
|
||||
presentation_request_ = presentation_request;
|
||||
query_result_manager_->SetSourcesForCastMode(
|
||||
MediaCastMode::PRESENTATION, sources,
|
||||
presentation_request_->frame_origin);
|
||||
// Register for MediaRoute updates. NOTE(mfoltz): If there are multiple
|
||||
// sources that can be connected to via the dialog, this will break. We will
|
||||
// need to observe multiple sources (keyed by sinks) in that case. As this is
|
||||
// Cast-specific for the forseeable future, it may be simpler to plumb a new
|
||||
// observer API for this case.
|
||||
const MediaSource source_for_route_observer =
|
||||
GetSourceForRouteObserver(sources);
|
||||
routes_observer_ = std::make_unique<UIMediaRoutesObserver>(
|
||||
GetMediaRouter(), source_for_route_observer.id(),
|
||||
base::BindRepeating(&MediaRouterUIBase::OnRoutesUpdated,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
|
||||
void MediaRouterUIBase::OnDefaultPresentationRemoved() {
|
||||
presentation_request_.reset();
|
||||
query_result_manager_->RemoveSourcesForCastMode(MediaCastMode::PRESENTATION);
|
||||
|
||||
// Register for MediaRoute updates without a media source.
|
||||
routes_observer_ = std::make_unique<UIMediaRoutesObserver>(
|
||||
GetMediaRouter(), MediaSource::Id(),
|
||||
base::BindRepeating(&MediaRouterUIBase::OnRoutesUpdated,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
|
||||
base::Optional<RouteParameters> MediaRouterUIBase::GetRouteParameters(
|
||||
const MediaSink::Id& sink_id,
|
||||
MediaCastMode cast_mode) {
|
||||
DCHECK(query_result_manager_);
|
||||
DCHECK(initiator_);
|
||||
|
||||
RouteParameters params;
|
||||
|
||||
// Note that there is a rarely-encountered bug, where the MediaCastMode to
|
||||
// MediaSource mapping could have been updated, between when the user clicked
|
||||
// on the UI to start a create route request, and when this function is
|
||||
// called. However, since the user does not have visibility into the
|
||||
// MediaSource, and that it occurs very rarely in practice, we leave it as-is
|
||||
// for now.
|
||||
std::unique_ptr<MediaSource> source =
|
||||
query_result_manager_->GetSourceForCastModeAndSink(cast_mode, sink_id);
|
||||
|
||||
if (!source) {
|
||||
LOG(ERROR) << "No corresponding MediaSource for cast mode "
|
||||
<< static_cast<int>(cast_mode) << " and sink " << sink_id;
|
||||
return base::nullopt;
|
||||
}
|
||||
params.source_id = source->id();
|
||||
|
||||
bool for_presentation_source = cast_mode == MediaCastMode::PRESENTATION;
|
||||
if (for_presentation_source && !presentation_request_) {
|
||||
DLOG(ERROR) << "Requested to create a route for presentation, but "
|
||||
<< "presentation request is missing.";
|
||||
return base::nullopt;
|
||||
}
|
||||
|
||||
current_route_request_id_ = ++route_request_counter_;
|
||||
params.origin = for_presentation_source ? presentation_request_->frame_origin
|
||||
: url::Origin::Create(GURL());
|
||||
DVLOG(1) << "DoCreateRoute: origin: " << params.origin;
|
||||
|
||||
// There are 3 cases. In cases (1) and (3) the MediaRouterUIBase will need to
|
||||
// be notified. In case (2) the dialog will be closed.
|
||||
// (1) Non-presentation route request (e.g., mirroring). No additional
|
||||
// notification necessary.
|
||||
// (2) Presentation route request for a PresentationRequest.start() call.
|
||||
// The StartPresentationContext will need to be answered with the route
|
||||
// response.
|
||||
// (3) Browser-initiated presentation route request. If successful,
|
||||
// PresentationServiceDelegateImpl will have to be notified. Note that we
|
||||
// treat subsequent route requests from a Presentation API-initiated
|
||||
// dialogs as browser-initiated.
|
||||
if (!for_presentation_source || !start_presentation_context_) {
|
||||
params.route_response_callbacks.push_back(base::BindOnce(
|
||||
&MediaRouterUIBase::OnRouteResponseReceived, weak_factory_.GetWeakPtr(),
|
||||
current_route_request_id_, sink_id, cast_mode,
|
||||
base::UTF8ToUTF16(GetTruncatedPresentationRequestSourceName())));
|
||||
}
|
||||
if (for_presentation_source) {
|
||||
if (start_presentation_context_) {
|
||||
// |start_presentation_context_| will be nullptr after this call, as the
|
||||
// object will be transferred to the callback.
|
||||
params.route_response_callbacks.push_back(
|
||||
base::BindOnce(&StartPresentationContext::HandleRouteResponse,
|
||||
std::move(start_presentation_context_)));
|
||||
params.route_response_callbacks.push_back(base::BindOnce(
|
||||
&MediaRouterUIBase::HandleCreateSessionRequestRouteResponse,
|
||||
weak_factory_.GetWeakPtr()));
|
||||
} else if (presentation_service_delegate_) {
|
||||
params.route_response_callbacks.push_back(base::BindOnce(
|
||||
&PresentationServiceDelegateImpl::OnRouteResponse,
|
||||
presentation_service_delegate_, *presentation_request_));
|
||||
}
|
||||
}
|
||||
|
||||
params.route_response_callbacks.push_back(
|
||||
base::BindOnce(&MediaRouterUIBase::MaybeReportCastingSource,
|
||||
weak_factory_.GetWeakPtr(), cast_mode));
|
||||
|
||||
params.timeout = GetRouteRequestTimeout(cast_mode);
|
||||
CHECK(initiator());
|
||||
params.incognito = initiator()->GetBrowserContext()->IsOffTheRecord();
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
bool MediaRouterUIBase::CreateRoute(const MediaSink::Id& sink_id,
|
||||
MediaCastMode cast_mode) {
|
||||
base::Optional<RouteParameters> params =
|
||||
GetRouteParameters(sink_id, cast_mode);
|
||||
if (!params)
|
||||
return false;
|
||||
|
||||
GetMediaRouter()->CreateRoute(params->source_id, sink_id, params->origin,
|
||||
initiator_,
|
||||
std::move(params->route_response_callbacks),
|
||||
params->timeout, params->incognito);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MediaRouterUIBase::CloseRoute(const MediaRoute::Id& route_id) {
|
||||
GetMediaRouter()->TerminateRoute(route_id);
|
||||
}
|
||||
|
||||
void MediaRouterUIBase::OnResultsUpdated(
|
||||
const std::vector<MediaSinkWithCastModes>& sinks) {
|
||||
sinks_ = sinks;
|
||||
|
||||
const icu::Collator* collator_ptr = collator_.get();
|
||||
std::sort(sinks_.begin(), sinks_.end(),
|
||||
[collator_ptr](const MediaSinkWithCastModes& sink1,
|
||||
const MediaSinkWithCastModes& sink2) {
|
||||
return sink1.sink.CompareUsingCollator(sink2.sink, collator_ptr);
|
||||
});
|
||||
UpdateSinks();
|
||||
}
|
||||
|
||||
void MediaRouterUIBase::OnRoutesUpdated(
|
||||
const std::vector<MediaRoute>& routes,
|
||||
const std::vector<MediaRoute::Id>& joinable_route_ids) {
|
||||
routes_.clear();
|
||||
|
||||
for (const MediaRoute& route : routes) {
|
||||
if (route.for_display()) {
|
||||
#ifndef NDEBUG
|
||||
for (const MediaRoute& existing_route : routes_) {
|
||||
if (existing_route.media_sink_id() == route.media_sink_id()) {
|
||||
DVLOG(2) << "Received another route for display with the same sink"
|
||||
<< " id as an existing route. " << route.media_route_id()
|
||||
<< " has the same sink id as "
|
||||
<< existing_route.media_sink_id() << ".";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
routes_.push_back(route);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MediaRouterUIBase::OnRouteResponseReceived(
|
||||
int route_request_id,
|
||||
const MediaSink::Id& sink_id,
|
||||
MediaCastMode cast_mode,
|
||||
const base::string16& presentation_request_source_name,
|
||||
const RouteRequestResult& result) {
|
||||
DVLOG(1) << "OnRouteResponseReceived";
|
||||
// If we receive a new route that we aren't expecting, do nothing.
|
||||
if (route_request_id != current_route_request_id_)
|
||||
return;
|
||||
|
||||
const MediaRoute* route = result.route();
|
||||
if (!route) {
|
||||
// The provider will handle sending an issue for a failed route request.
|
||||
DVLOG(1) << "MediaRouteResponse returned error: " << result.error();
|
||||
}
|
||||
|
||||
current_route_request_id_ = -1;
|
||||
}
|
||||
|
||||
void MediaRouterUIBase::MaybeReportCastingSource(
|
||||
MediaCastMode cast_mode,
|
||||
const RouteRequestResult& result) {
|
||||
if (result.result_code() == RouteRequestResult::OK)
|
||||
MediaRouterMetrics::RecordMediaRouterCastingSource(cast_mode);
|
||||
}
|
||||
|
||||
GURL MediaRouterUIBase::GetFrameURL() const {
|
||||
return presentation_request_ ? presentation_request_->frame_origin.GetURL()
|
||||
: GURL();
|
||||
}
|
||||
|
||||
std::vector<MediaSinkWithCastModes> MediaRouterUIBase::GetEnabledSinks() const {
|
||||
#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
|
||||
if (!display_observer_)
|
||||
return sinks_;
|
||||
|
||||
// Filter out the wired display sink for the display that the dialog is on.
|
||||
// This is not the best place to do this because MRUI should not perform a
|
||||
// provider-specific behavior, but we currently do not have a way to
|
||||
// communicate dialog-specific information to/from the
|
||||
// WiredDisplayMediaRouteProvider.
|
||||
std::vector<MediaSinkWithCastModes> enabled_sinks;
|
||||
const std::string display_sink_id =
|
||||
WiredDisplayMediaRouteProvider::GetSinkIdForDisplay(
|
||||
display_observer_->GetCurrentDisplay());
|
||||
for (const MediaSinkWithCastModes& sink : sinks_) {
|
||||
if (sink.sink.id() != display_sink_id)
|
||||
enabled_sinks.push_back(sink);
|
||||
}
|
||||
return enabled_sinks;
|
||||
#else
|
||||
return sinks_;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string MediaRouterUIBase::GetTruncatedPresentationRequestSourceName()
|
||||
const {
|
||||
GURL gurl = GetFrameURL();
|
||||
CHECK(initiator());
|
||||
return gurl.SchemeIs(extensions::kExtensionScheme)
|
||||
? GetExtensionName(gurl, extensions::ExtensionRegistry::Get(
|
||||
initiator()->GetBrowserContext()))
|
||||
: TruncateHost(GetHostFromURL(gurl));
|
||||
}
|
||||
|
||||
MediaRouter* MediaRouterUIBase::GetMediaRouter() const {
|
||||
CHECK(initiator());
|
||||
return MediaRouterFactory::GetApiForBrowserContext(
|
||||
initiator()->GetBrowserContext());
|
||||
}
|
||||
|
||||
} // namespace media_router
|
276
chrome/browser/ui/media_router/media_router_ui_base.h
Normal file
276
chrome/browser/ui/media_router/media_router_ui_base.h
Normal file
@@ -0,0 +1,276 @@
|
||||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_UI_MEDIA_ROUTER_MEDIA_ROUTER_UI_BASE_H_
|
||||
#define CHROME_BROWSER_UI_MEDIA_ROUTER_MEDIA_ROUTER_UI_BASE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "build/build_config.h"
|
||||
#include "chrome/browser/media/router/media_router_dialog_controller.h"
|
||||
#include "chrome/browser/media/router/presentation/presentation_service_delegate_impl.h"
|
||||
#include "chrome/browser/ui/media_router/media_cast_mode.h"
|
||||
#include "chrome/browser/ui/media_router/media_router_ui_helper.h"
|
||||
#include "chrome/browser/ui/media_router/media_sink_with_cast_modes.h"
|
||||
#include "chrome/browser/ui/media_router/query_result_manager.h"
|
||||
#include "chrome/common/media_router/issue.h"
|
||||
#include "chrome/common/media_router/media_source.h"
|
||||
#include "ui/base/ui_features.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
|
||||
#include "chrome/browser/ui/webui/media_router/web_contents_display_observer.h"
|
||||
#endif
|
||||
|
||||
namespace content {
|
||||
struct PresentationRequest;
|
||||
class WebContents;
|
||||
} // namespace content
|
||||
|
||||
namespace U_ICU_NAMESPACE {
|
||||
class Collator;
|
||||
}
|
||||
|
||||
namespace media_router {
|
||||
|
||||
class MediaRoute;
|
||||
class MediaRouter;
|
||||
class MediaRoutesObserver;
|
||||
class MediaSink;
|
||||
class RouteRequestResult;
|
||||
|
||||
// Abstract base class for Views and WebUI implementations. Responsible for
|
||||
// observing and organizing route, sink, and presentation request information,
|
||||
// and executing and keeping track of route requests from the dialog to Media
|
||||
// Router.
|
||||
class MediaRouterUIBase : public QueryResultManager::Observer,
|
||||
public PresentationServiceDelegateImpl::
|
||||
DefaultPresentationRequestObserver {
|
||||
public:
|
||||
MediaRouterUIBase();
|
||||
~MediaRouterUIBase() override;
|
||||
|
||||
// Initializes internal state (e.g. starts listening for MediaSinks) for
|
||||
// targeting the default MediaSource (if any) of the initiator tab that owns
|
||||
// |delegate|, as well as mirroring sources of that tab.
|
||||
// The contents of the UI will change as the default MediaSource changes.
|
||||
// If there is a default MediaSource, then PRESENTATION MediaCastMode will be
|
||||
// added to |cast_modes_|.
|
||||
// Init* methods can only be called once.
|
||||
// |initiator|: Reference to the WebContents that initiated the dialog.
|
||||
// Must not be null.
|
||||
// |delegate|: PresentationServiceDelegateImpl of the initiator tab.
|
||||
// Must not be null.
|
||||
// TODO(imcheng): Replace use of impl with an intermediate abstract
|
||||
// interface.
|
||||
void InitWithDefaultMediaSource(content::WebContents* initiator,
|
||||
PresentationServiceDelegateImpl* delegate);
|
||||
|
||||
// Initializes internal state targeting the presentation specified in
|
||||
// |context|. Also sets up mirroring sources based on |initiator|.
|
||||
// This is different from InitWithDefaultMediaSource() in that it does not
|
||||
// listen for default media source changes, as the UI is fixed to the source
|
||||
// in |request|.
|
||||
// Init* methods can only be called once.
|
||||
// |initiator|: Reference to the WebContents that initiated the dialog.
|
||||
// Must not be null.
|
||||
// |delegate|: PresentationServiceDelegateImpl of the initiator tab.
|
||||
// Must not be null.
|
||||
// |context|: Context object for the PresentationRequest. This instance will
|
||||
// take ownership of it. Must not be null.
|
||||
void InitWithStartPresentationContext(
|
||||
content::WebContents* initiator,
|
||||
PresentationServiceDelegateImpl* delegate,
|
||||
std::unique_ptr<StartPresentationContext> context);
|
||||
|
||||
// Requests a route be created from the source mapped to
|
||||
// |cast_mode|, to the sink given by |sink_id|.
|
||||
// Returns true if a route request is successfully submitted.
|
||||
// |OnRouteResponseReceived()| will be invoked when the route request
|
||||
// completes.
|
||||
virtual bool CreateRoute(const MediaSink::Id& sink_id,
|
||||
MediaCastMode cast_mode);
|
||||
|
||||
// Calls MediaRouter to close the given route.
|
||||
void CloseRoute(const MediaRoute::Id& route_id);
|
||||
|
||||
// Logs a UMA stat for the source that was cast if the result is successful.
|
||||
void MaybeReportCastingSource(MediaCastMode cast_mode,
|
||||
const RouteRequestResult& result);
|
||||
|
||||
// Returns a subset of |sinks_| that should be listed in the dialog.
|
||||
std::vector<MediaSinkWithCastModes> GetEnabledSinks() const;
|
||||
|
||||
// Returns a source name that can be shown in the dialog.
|
||||
std::string GetTruncatedPresentationRequestSourceName() const;
|
||||
|
||||
const std::vector<MediaRoute>& routes() const { return routes_; }
|
||||
content::WebContents* initiator() const { return initiator_; }
|
||||
|
||||
#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
|
||||
// Used in tests for wired display presentations.
|
||||
void set_display_observer_for_test(
|
||||
std::unique_ptr<WebContentsDisplayObserver> display_observer) {
|
||||
display_observer_ = std::move(display_observer);
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
std::vector<MediaSource> GetSourcesForCastMode(MediaCastMode cast_mode) const;
|
||||
|
||||
// QueryResultManager::Observer:
|
||||
void OnResultsUpdated(
|
||||
const std::vector<MediaSinkWithCastModes>& sinks) override;
|
||||
|
||||
// Called by |routes_observer_| when the set of active routes has changed.
|
||||
virtual void OnRoutesUpdated(
|
||||
const std::vector<MediaRoute>& routes,
|
||||
const std::vector<MediaRoute::Id>& joinable_route_ids);
|
||||
|
||||
// Callback passed to MediaRouter to receive response to route creation
|
||||
// requests.
|
||||
virtual void OnRouteResponseReceived(
|
||||
int route_request_id,
|
||||
const MediaSink::Id& sink_id,
|
||||
MediaCastMode cast_mode,
|
||||
const base::string16& presentation_request_source_name,
|
||||
const RouteRequestResult& result);
|
||||
|
||||
// Closes the dialog after receiving a route response when using
|
||||
// |start_presentation_context_|. This prevents the dialog from trying to use
|
||||
// the same presentation request again.
|
||||
virtual void HandleCreateSessionRequestRouteResponse(
|
||||
const RouteRequestResult&);
|
||||
|
||||
// Initializes the dialog with mirroring sources derived from |initiator|.
|
||||
virtual void InitCommon(content::WebContents* initiator);
|
||||
|
||||
// PresentationServiceDelegateImpl::DefaultPresentationObserver
|
||||
void OnDefaultPresentationChanged(
|
||||
const content::PresentationRequest& presentation_request) override;
|
||||
void OnDefaultPresentationRemoved() override;
|
||||
|
||||
// Called to update the dialog with the current list of of enabled sinks.
|
||||
virtual void UpdateSinks() = 0;
|
||||
|
||||
// Populates common route-related parameters for calls to MediaRouter.
|
||||
base::Optional<RouteParameters> GetRouteParameters(
|
||||
const MediaSink::Id& sink_id,
|
||||
MediaCastMode cast_mode);
|
||||
|
||||
// Returns the default PresentationRequest's frame URL if there is one.
|
||||
// Otherwise returns an empty GURL.
|
||||
GURL GetFrameURL() const;
|
||||
|
||||
int current_route_request_id() const { return current_route_request_id_; }
|
||||
|
||||
StartPresentationContext* start_presentation_context() const {
|
||||
return start_presentation_context_.get();
|
||||
}
|
||||
|
||||
void set_start_presentation_context_for_test(
|
||||
std::unique_ptr<StartPresentationContext> start_presentation_context) {
|
||||
start_presentation_context_ = std::move(start_presentation_context);
|
||||
}
|
||||
|
||||
QueryResultManager* query_result_manager() const {
|
||||
return query_result_manager_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest,
|
||||
UIMediaRoutesObserverAssignsCurrentCastModes);
|
||||
FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest,
|
||||
UIMediaRoutesObserverSkipsUnavailableCastModes);
|
||||
|
||||
class UIMediaRoutesObserver : public MediaRoutesObserver {
|
||||
public:
|
||||
using RoutesUpdatedCallback =
|
||||
base::RepeatingCallback<void(const std::vector<MediaRoute>&,
|
||||
const std::vector<MediaRoute::Id>&)>;
|
||||
UIMediaRoutesObserver(MediaRouter* router,
|
||||
const MediaSource::Id& source_id,
|
||||
const RoutesUpdatedCallback& callback);
|
||||
~UIMediaRoutesObserver() override;
|
||||
|
||||
// MediaRoutesObserver
|
||||
void OnRoutesUpdated(
|
||||
const std::vector<MediaRoute>& routes,
|
||||
const std::vector<MediaRoute::Id>& joinable_route_ids) override;
|
||||
|
||||
private:
|
||||
// Callback to the owning MediaRouterUIBase instance.
|
||||
RoutesUpdatedCallback callback_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(UIMediaRoutesObserver);
|
||||
};
|
||||
|
||||
// Returns the MediaRouter for this instance's BrowserContext.
|
||||
virtual MediaRouter* GetMediaRouter() const;
|
||||
|
||||
// This is non-null while this instance is registered to receive
|
||||
// updates from them.
|
||||
std::unique_ptr<MediaRoutesObserver> routes_observer_;
|
||||
|
||||
// Set to -1 if not tracking a pending route request.
|
||||
int current_route_request_id_;
|
||||
|
||||
// Sequential counter for route requests. Used to update
|
||||
// |current_route_request_id_| when there is a new route request.
|
||||
int route_request_counter_;
|
||||
|
||||
// Used for locale-aware sorting of sinks by name. Set during |InitCommon()|
|
||||
// using the current locale.
|
||||
std::unique_ptr<icu::Collator> collator_;
|
||||
|
||||
std::vector<MediaSinkWithCastModes> sinks_;
|
||||
std::vector<MediaRoute> routes_;
|
||||
|
||||
// Monitors and reports sink availability.
|
||||
std::unique_ptr<QueryResultManager> query_result_manager_;
|
||||
|
||||
// If set, then the result of the next presentation route request will
|
||||
// be handled by this object.
|
||||
std::unique_ptr<StartPresentationContext> start_presentation_context_;
|
||||
|
||||
// Set to the presentation request corresponding to the presentation cast
|
||||
// mode, if supported. Otherwise set to nullopt.
|
||||
base::Optional<content::PresentationRequest> presentation_request_;
|
||||
|
||||
// It's possible for PresentationServiceDelegateImpl to be destroyed before
|
||||
// this class.
|
||||
// (e.g. if a tab with the UI open is closed, then the tab WebContents will
|
||||
// be destroyed first momentarily before the UI WebContents).
|
||||
// Holding a WeakPtr to PresentationServiceDelegateImpl is the cleanest way to
|
||||
// handle this.
|
||||
// TODO(imcheng): hold a weak ptr to an abstract type instead.
|
||||
base::WeakPtr<PresentationServiceDelegateImpl> presentation_service_delegate_;
|
||||
|
||||
// WebContents for the tab for which the Cast dialog is shown.
|
||||
content::WebContents* initiator_;
|
||||
|
||||
#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
|
||||
// Keeps track of which display the initiator WebContents is on. This is used
|
||||
// to make sure we don't show a wired display presentation over the
|
||||
// controlling window.
|
||||
std::unique_ptr<WebContentsDisplayObserver> display_observer_;
|
||||
#endif
|
||||
|
||||
// NOTE: Weak pointers must be invalidated before all other member variables.
|
||||
// Therefore |weak_factory_| must be placed at the end.
|
||||
base::WeakPtrFactory<MediaRouterUIBase> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MediaRouterUIBase);
|
||||
};
|
||||
|
||||
} // namespace media_router
|
||||
|
||||
#endif // CHROME_BROWSER_UI_MEDIA_ROUTER_MEDIA_ROUTER_UI_BASE_H_
|
68
chrome/browser/ui/media_router/media_router_ui_helper.cc
Normal file
68
chrome/browser/ui/media_router/media_router_ui_helper.cc
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/ui/media_router/media_router_ui_helper.h"
|
||||
|
||||
#include "base/time/time.h"
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
namespace media_router {
|
||||
|
||||
namespace {
|
||||
|
||||
// The amount of time to wait for a response when creating a new route.
|
||||
const int kCreateRouteTimeoutSeconds = 20;
|
||||
const int kCreateRouteTimeoutSecondsForTab = 60;
|
||||
const int kCreateRouteTimeoutSecondsForLocalFile = 60;
|
||||
const int kCreateRouteTimeoutSecondsForDesktop = 120;
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string GetExtensionName(const GURL& gurl,
|
||||
extensions::ExtensionRegistry* registry) {
|
||||
if (gurl.is_empty() || !registry)
|
||||
return std::string();
|
||||
|
||||
const extensions::Extension* extension =
|
||||
registry->enabled_extensions().GetExtensionOrAppByURL(gurl);
|
||||
|
||||
return extension ? extension->name() : std::string();
|
||||
}
|
||||
|
||||
std::string GetHostFromURL(const GURL& gurl) {
|
||||
if (gurl.is_empty())
|
||||
return std::string();
|
||||
std::string host = gurl.host();
|
||||
if (base::StartsWith(host, "www.", base::CompareCase::INSENSITIVE_ASCII))
|
||||
host = host.substr(4);
|
||||
return host;
|
||||
}
|
||||
|
||||
base::TimeDelta GetRouteRequestTimeout(MediaCastMode cast_mode) {
|
||||
switch (cast_mode) {
|
||||
case PRESENTATION:
|
||||
return base::TimeDelta::FromSeconds(kCreateRouteTimeoutSeconds);
|
||||
case TAB_MIRROR:
|
||||
return base::TimeDelta::FromSeconds(kCreateRouteTimeoutSecondsForTab);
|
||||
case DESKTOP_MIRROR:
|
||||
return base::TimeDelta::FromSeconds(kCreateRouteTimeoutSecondsForDesktop);
|
||||
case LOCAL_FILE:
|
||||
return base::TimeDelta::FromSeconds(
|
||||
kCreateRouteTimeoutSecondsForLocalFile);
|
||||
default:
|
||||
NOTREACHED();
|
||||
return base::TimeDelta();
|
||||
}
|
||||
}
|
||||
|
||||
RouteParameters::RouteParameters() = default;
|
||||
|
||||
RouteParameters::RouteParameters(RouteParameters&& other) = default;
|
||||
|
||||
RouteParameters::~RouteParameters() = default;
|
||||
|
||||
RouteParameters& RouteParameters::operator=(RouteParameters&& other) = default;
|
||||
|
||||
} // namespace media_router
|
53
chrome/browser/ui/media_router/media_router_ui_helper.h
Normal file
53
chrome/browser/ui/media_router/media_router_ui_helper.h
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_UI_MEDIA_ROUTER_MEDIA_ROUTER_UI_HELPER_H_
|
||||
#define CHROME_BROWSER_UI_MEDIA_ROUTER_MEDIA_ROUTER_UI_HELPER_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/time/time.h"
|
||||
#include "chrome/browser/media/router/media_router.h"
|
||||
#include "chrome/browser/ui/media_router/media_cast_mode.h"
|
||||
#include "chrome/common/media_router/media_source.h"
|
||||
#include "url/origin.h"
|
||||
|
||||
namespace extensions {
|
||||
class ExtensionRegistry;
|
||||
}
|
||||
|
||||
class GURL;
|
||||
|
||||
namespace media_router {
|
||||
|
||||
// Returns the extension name for |url|, so that it can be displayed for
|
||||
// extension-initiated presentations.
|
||||
std::string GetExtensionName(const GURL& url,
|
||||
extensions::ExtensionRegistry* registry);
|
||||
|
||||
std::string GetHostFromURL(const GURL& gurl);
|
||||
|
||||
// Returns the duration to wait for route creation result before we time out.
|
||||
base::TimeDelta GetRouteRequestTimeout(MediaCastMode cast_mode);
|
||||
|
||||
// Contains common parameters for route requests to MediaRouter.
|
||||
struct RouteParameters {
|
||||
public:
|
||||
RouteParameters();
|
||||
RouteParameters(RouteParameters&& other);
|
||||
~RouteParameters();
|
||||
|
||||
RouteParameters& operator=(RouteParameters&& other);
|
||||
|
||||
MediaSource::Id source_id;
|
||||
url::Origin origin;
|
||||
std::vector<MediaRouteResponseCallback> route_response_callbacks;
|
||||
base::TimeDelta timeout;
|
||||
bool incognito;
|
||||
};
|
||||
|
||||
} // namespace media_router
|
||||
|
||||
#endif // CHROME_BROWSER_UI_MEDIA_ROUTER_MEDIA_ROUTER_UI_HELPER_H_
|
@@ -0,0 +1,45 @@
|
||||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/ui/media_router/media_router_ui_helper.h"
|
||||
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
#include "extensions/common/extension_builder.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace media_router {
|
||||
|
||||
TEST(MediaRouterUIHelperTest, GetExtensionNameExtensionPresent) {
|
||||
std::string id = "extensionid";
|
||||
GURL url = GURL("chrome-extension://" + id);
|
||||
std::unique_ptr<extensions::ExtensionRegistry> registry =
|
||||
std::make_unique<extensions::ExtensionRegistry>(nullptr);
|
||||
scoped_refptr<extensions::Extension> app =
|
||||
extensions::ExtensionBuilder(
|
||||
"test app name", extensions::ExtensionBuilder::Type::PLATFORM_APP)
|
||||
.SetID(id)
|
||||
.Build();
|
||||
|
||||
ASSERT_TRUE(registry->AddEnabled(app));
|
||||
EXPECT_EQ("test app name", GetExtensionName(url, registry.get()));
|
||||
}
|
||||
|
||||
TEST(MediaRouterUIHelperTest, GetExtensionNameEmptyWhenNotInstalled) {
|
||||
std::string id = "extensionid";
|
||||
GURL url = GURL("chrome-extension://" + id);
|
||||
std::unique_ptr<extensions::ExtensionRegistry> registry =
|
||||
std::make_unique<extensions::ExtensionRegistry>(nullptr);
|
||||
|
||||
EXPECT_EQ("", GetExtensionName(url, registry.get()));
|
||||
}
|
||||
|
||||
TEST(MediaRouterUIHelperTest, GetExtensionNameEmptyWhenNotExtensionURL) {
|
||||
GURL url = GURL("https://www.google.com");
|
||||
std::unique_ptr<extensions::ExtensionRegistry> registry =
|
||||
std::make_unique<extensions::ExtensionRegistry>(nullptr);
|
||||
|
||||
EXPECT_EQ("", GetExtensionName(url, registry.get()));
|
||||
}
|
||||
|
||||
} // namespace media_router
|
@@ -0,0 +1,17 @@
|
||||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/ui/views/media_router/media_router_views_ui.h"
|
||||
|
||||
namespace media_router {
|
||||
|
||||
MediaRouterViewsUI::MediaRouterViewsUI() = default;
|
||||
|
||||
MediaRouterViewsUI::~MediaRouterViewsUI() = default;
|
||||
|
||||
void MediaRouterViewsUI::UpdateSinks() {
|
||||
// TODO(crbug.com/826091): Implement this method.
|
||||
}
|
||||
|
||||
} // namespace media_router
|
28
chrome/browser/ui/views/media_router/media_router_views_ui.h
Normal file
28
chrome/browser/ui/views/media_router/media_router_views_ui.h
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_UI_VIEWS_MEDIA_ROUTER_MEDIA_ROUTER_VIEWS_UI_H_
|
||||
#define CHROME_BROWSER_UI_VIEWS_MEDIA_ROUTER_MEDIA_ROUTER_VIEWS_UI_H_
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "chrome/browser/ui/media_router/media_router_ui_base.h"
|
||||
|
||||
namespace media_router {
|
||||
|
||||
// Functions as an intermediary between MediaRouter and Views Cast dialog.
|
||||
class MediaRouterViewsUI : public MediaRouterUIBase {
|
||||
public:
|
||||
MediaRouterViewsUI();
|
||||
~MediaRouterViewsUI() override;
|
||||
|
||||
private:
|
||||
// MediaRouterUIBase:
|
||||
void UpdateSinks() override;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MediaRouterViewsUI);
|
||||
};
|
||||
|
||||
} // namespace media_router
|
||||
|
||||
#endif // CHROME_BROWSER_UI_VIEWS_MEDIA_ROUTER_MEDIA_ROUTER_VIEWS_UI_H_
|
@@ -15,22 +15,19 @@
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/trace_event/trace_event.h"
|
||||
#include "build/build_config.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "chrome/browser/media/router/issue_manager.h"
|
||||
#include "chrome/browser/media/router/issues_observer.h"
|
||||
#include "chrome/browser/media/router/media_router.h"
|
||||
#include "chrome/browser/media/router/media_router_factory.h"
|
||||
#include "chrome/browser/media/router/media_router_feature.h"
|
||||
#include "chrome/browser/media/router/media_router_metrics.h"
|
||||
#include "chrome/browser/media/router/media_routes_observer.h"
|
||||
#include "chrome/browser/media/router/media_sinks_observer.h"
|
||||
#include "chrome/browser/media/router/presentation/presentation_service_delegate_impl.h"
|
||||
#include "chrome/browser/profiles/profile.h"
|
||||
#include "chrome/browser/sessions/session_tab_helper.h"
|
||||
#include "chrome/browser/ui/browser_finder.h"
|
||||
#include "chrome/browser/ui/browser_navigator.h"
|
||||
#include "chrome/browser/ui/browser_navigator_params.h"
|
||||
#include "chrome/browser/ui/browser_tabstrip.h"
|
||||
#include "chrome/browser/ui/media_router/media_router_ui_helper.h"
|
||||
#include "chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.h"
|
||||
#include "chrome/browser/ui/webui/media_router/media_router_resources_provider.h"
|
||||
#include "chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h"
|
||||
@@ -40,7 +37,6 @@
|
||||
#include "chrome/common/media_router/media_sink.h"
|
||||
#include "chrome/common/media_router/media_source.h"
|
||||
#include "chrome/common/media_router/media_source_helper.h"
|
||||
#include "chrome/common/media_router/route_request_result.h"
|
||||
#include "chrome/common/pref_names.h"
|
||||
#include "chrome/common/url_constants.h"
|
||||
#include "chrome/grit/generated_resources.h"
|
||||
@@ -56,7 +52,6 @@
|
||||
#include "extensions/common/constants.h"
|
||||
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
|
||||
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
|
||||
#include "third_party/icu/source/i18n/unicode/coll.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/web_dialogs/web_dialog_delegate.h"
|
||||
#include "url/origin.h"
|
||||
@@ -68,85 +63,19 @@
|
||||
|
||||
namespace media_router {
|
||||
|
||||
namespace {
|
||||
|
||||
// The amount of time to wait for a response when creating a new route.
|
||||
const int kCreateRouteTimeoutSeconds = 20;
|
||||
const int kCreateRouteTimeoutSecondsForTab = 60;
|
||||
const int kCreateRouteTimeoutSecondsForLocalFile = 60;
|
||||
const int kCreateRouteTimeoutSecondsForDesktop = 120;
|
||||
|
||||
std::string GetHostFromURL(const GURL& gurl) {
|
||||
if (gurl.is_empty())
|
||||
return std::string();
|
||||
std::string host = gurl.host();
|
||||
if (base::StartsWith(host, "www.", base::CompareCase::INSENSITIVE_ASCII))
|
||||
host = host.substr(4);
|
||||
return host;
|
||||
}
|
||||
|
||||
std::string TruncateHost(const std::string& host) {
|
||||
const std::string truncated =
|
||||
net::registry_controlled_domains::GetDomainAndRegistry(
|
||||
host, net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
|
||||
// The truncation will be empty in some scenarios (e.g. host is
|
||||
// simply an IP address). Fail gracefully.
|
||||
return truncated.empty() ? host : truncated;
|
||||
}
|
||||
|
||||
base::TimeDelta GetRouteRequestTimeout(MediaCastMode cast_mode) {
|
||||
switch (cast_mode) {
|
||||
case PRESENTATION:
|
||||
return base::TimeDelta::FromSeconds(kCreateRouteTimeoutSeconds);
|
||||
case TAB_MIRROR:
|
||||
return base::TimeDelta::FromSeconds(kCreateRouteTimeoutSecondsForTab);
|
||||
case DESKTOP_MIRROR:
|
||||
return base::TimeDelta::FromSeconds(kCreateRouteTimeoutSecondsForDesktop);
|
||||
case LOCAL_FILE:
|
||||
return base::TimeDelta::FromSeconds(
|
||||
kCreateRouteTimeoutSecondsForLocalFile);
|
||||
default:
|
||||
NOTREACHED();
|
||||
return base::TimeDelta();
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the first source in |sources| that can be connected to by using the
|
||||
// "Cast" button in the dialog, or an empty source if there is none. This is
|
||||
// used by the Media Router to find such a matching route if it exists.
|
||||
MediaSource GetSourceForRouteObserver(const std::vector<MediaSource>& sources) {
|
||||
auto source_it =
|
||||
std::find_if(sources.begin(), sources.end(), IsCastPresentationUrl);
|
||||
return source_it != sources.end() ? *source_it : MediaSource("");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
std::string MediaRouterUI::GetExtensionName(
|
||||
const GURL& gurl,
|
||||
extensions::ExtensionRegistry* registry) {
|
||||
if (gurl.is_empty() || !registry)
|
||||
return std::string();
|
||||
|
||||
const extensions::Extension* extension =
|
||||
registry->enabled_extensions().GetExtensionOrAppByURL(gurl);
|
||||
|
||||
return extension ? extension->name() : std::string();
|
||||
}
|
||||
|
||||
Browser* MediaRouterUI::GetBrowser() {
|
||||
CHECK(initiator());
|
||||
return chrome::FindBrowserWithWebContents(initiator());
|
||||
}
|
||||
|
||||
content::WebContents* MediaRouterUI::OpenTabWithUrl(const GURL url) {
|
||||
// Check if the current page is a new tab. If so open file in current page.
|
||||
// If not then open a new page.
|
||||
if (initiator_->GetVisibleURL() == chrome::kChromeUINewTabURL) {
|
||||
if (initiator()->GetVisibleURL() == chrome::kChromeUINewTabURL) {
|
||||
content::NavigationController::LoadURLParams load_params(url);
|
||||
load_params.transition_type = ui::PAGE_TRANSITION_GENERATED;
|
||||
initiator_->GetController().LoadURLWithParams(load_params);
|
||||
return initiator_;
|
||||
initiator()->GetController().LoadURLWithParams(load_params);
|
||||
return initiator();
|
||||
} else {
|
||||
return chrome::AddSelectedTabWithURL(GetBrowser(), url,
|
||||
ui::PAGE_TRANSITION_LINK);
|
||||
@@ -291,22 +220,6 @@ class MediaRouterUI::WebContentsFullscreenOnLoadedObserver final
|
||||
}
|
||||
};
|
||||
|
||||
MediaRouterUI::UIMediaRoutesObserver::UIMediaRoutesObserver(
|
||||
MediaRouter* router,
|
||||
const MediaSource::Id& source_id,
|
||||
const RoutesUpdatedCallback& callback)
|
||||
: MediaRoutesObserver(router, source_id), callback_(callback) {
|
||||
DCHECK(!callback_.is_null());
|
||||
}
|
||||
|
||||
MediaRouterUI::UIMediaRoutesObserver::~UIMediaRoutesObserver() {}
|
||||
|
||||
void MediaRouterUI::UIMediaRoutesObserver::OnRoutesUpdated(
|
||||
const std::vector<MediaRoute>& routes,
|
||||
const std::vector<MediaRoute::Id>& joinable_route_ids) {
|
||||
callback_.Run(routes, joinable_route_ids);
|
||||
}
|
||||
|
||||
MediaRouterUI::UIMediaRouteControllerObserver::UIMediaRouteControllerObserver(
|
||||
MediaRouterUI* ui,
|
||||
scoped_refptr<MediaRouteController> controller)
|
||||
@@ -330,10 +243,6 @@ void MediaRouterUI::UIMediaRouteControllerObserver::OnControllerInvalidated() {
|
||||
MediaRouterUI::MediaRouterUI(content::WebUI* web_ui)
|
||||
: ConstrainedWebDialogUI(web_ui),
|
||||
ui_initialized_(false),
|
||||
current_route_request_id_(-1),
|
||||
route_request_counter_(0),
|
||||
initiator_(nullptr),
|
||||
router_(nullptr),
|
||||
weak_factory_(this) {
|
||||
auto handler = std::make_unique<MediaRouterWebUIMessageHandler>(this);
|
||||
handler_ = handler.get();
|
||||
@@ -352,138 +261,15 @@ MediaRouterUI::MediaRouterUI(content::WebUI* web_ui)
|
||||
web_ui->AddMessageHandler(std::move(handler));
|
||||
}
|
||||
|
||||
MediaRouterUI::~MediaRouterUI() {
|
||||
if (query_result_manager_.get())
|
||||
query_result_manager_->RemoveObserver(this);
|
||||
if (presentation_service_delegate_.get())
|
||||
presentation_service_delegate_->RemoveDefaultPresentationRequestObserver(
|
||||
this);
|
||||
// If |start_presentation_context_| still exists, then it means presentation
|
||||
// route request was never attempted.
|
||||
if (start_presentation_context_) {
|
||||
bool presentation_sinks_available = std::any_of(
|
||||
sinks_.begin(), sinks_.end(), [](const MediaSinkWithCastModes& sink) {
|
||||
return base::ContainsKey(sink.cast_modes,
|
||||
MediaCastMode::PRESENTATION);
|
||||
});
|
||||
if (presentation_sinks_available) {
|
||||
start_presentation_context_->InvokeErrorCallback(
|
||||
blink::mojom::PresentationError(blink::mojom::PresentationErrorType::
|
||||
PRESENTATION_REQUEST_CANCELLED,
|
||||
"Dialog closed."));
|
||||
} else {
|
||||
start_presentation_context_->InvokeErrorCallback(
|
||||
blink::mojom::PresentationError(
|
||||
blink::mojom::PresentationErrorType::NO_AVAILABLE_SCREENS,
|
||||
"No screens found."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MediaRouterUI::InitWithDefaultMediaSource(
|
||||
content::WebContents* initiator,
|
||||
PresentationServiceDelegateImpl* delegate) {
|
||||
DCHECK(initiator);
|
||||
DCHECK(!presentation_service_delegate_);
|
||||
DCHECK(!query_result_manager_.get());
|
||||
|
||||
InitCommon(initiator);
|
||||
if (delegate) {
|
||||
presentation_service_delegate_ = delegate->GetWeakPtr();
|
||||
presentation_service_delegate_->AddDefaultPresentationRequestObserver(this);
|
||||
}
|
||||
|
||||
if (delegate && delegate->HasDefaultPresentationRequest()) {
|
||||
OnDefaultPresentationChanged(delegate->GetDefaultPresentationRequest());
|
||||
} else {
|
||||
// Register for MediaRoute updates without a media source.
|
||||
routes_observer_ = std::make_unique<UIMediaRoutesObserver>(
|
||||
router_, MediaSource::Id(),
|
||||
base::BindRepeating(&MediaRouterUI::OnRoutesUpdated,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
}
|
||||
|
||||
void MediaRouterUI::InitWithStartPresentationContext(
|
||||
content::WebContents* initiator,
|
||||
PresentationServiceDelegateImpl* delegate,
|
||||
std::unique_ptr<StartPresentationContext> context) {
|
||||
DCHECK(initiator);
|
||||
DCHECK(delegate);
|
||||
DCHECK(context);
|
||||
DCHECK(!start_presentation_context_);
|
||||
DCHECK(!query_result_manager_);
|
||||
|
||||
start_presentation_context_ = std::move(context);
|
||||
presentation_service_delegate_ = delegate->GetWeakPtr();
|
||||
|
||||
InitCommon(initiator);
|
||||
OnDefaultPresentationChanged(
|
||||
start_presentation_context_->presentation_request());
|
||||
}
|
||||
MediaRouterUI::~MediaRouterUI() = default;
|
||||
|
||||
void MediaRouterUI::InitCommon(content::WebContents* initiator) {
|
||||
DCHECK(initiator);
|
||||
|
||||
TRACE_EVENT_NESTABLE_ASYNC_INSTANT1("media_router", "UI", initiator,
|
||||
"MediaRouterUI::InitCommon", this);
|
||||
|
||||
router_ = GetMediaRouter();
|
||||
DCHECK(router_);
|
||||
|
||||
MediaRouterUIBase::InitCommon(initiator);
|
||||
UpdateCastModes();
|
||||
// Presentation requests from content must show the origin requesting
|
||||
// presentation: crbug.com/704964
|
||||
if (start_presentation_context_)
|
||||
if (start_presentation_context())
|
||||
forced_cast_mode_ = MediaCastMode::PRESENTATION;
|
||||
|
||||
router_->OnUserGesture();
|
||||
|
||||
// Create |collator_| before |query_result_manager_| so that |collator_| is
|
||||
// already set up when we get a callback from |query_result_manager_|.
|
||||
UErrorCode error = U_ZERO_ERROR;
|
||||
const std::string& locale = g_browser_process->GetApplicationLocale();
|
||||
collator_.reset(
|
||||
icu::Collator::createInstance(icu::Locale(locale.c_str()), error));
|
||||
if (U_FAILURE(error)) {
|
||||
DLOG(ERROR) << "Failed to create collator for locale " << locale;
|
||||
collator_.reset();
|
||||
}
|
||||
|
||||
query_result_manager_ = std::make_unique<QueryResultManager>(router_);
|
||||
query_result_manager_->AddObserver(this);
|
||||
|
||||
// Use a placeholder URL as origin for mirroring.
|
||||
url::Origin origin =
|
||||
url::Origin::Create(GURL(chrome::kChromeUIMediaRouterURL));
|
||||
|
||||
// Desktop mirror mode is always available.
|
||||
query_result_manager_->SetSourcesForCastMode(
|
||||
MediaCastMode::DESKTOP_MIRROR, {MediaSourceForDesktop()}, origin);
|
||||
|
||||
// For now, file mirroring is always availible if enabled.
|
||||
if (CastLocalMediaEnabled()) {
|
||||
query_result_manager_->SetSourcesForCastMode(
|
||||
MediaCastMode::LOCAL_FILE, {MediaSourceForTab(0)}, origin);
|
||||
}
|
||||
|
||||
initiator_ = initiator;
|
||||
SessionID tab_id = SessionTabHelper::IdForTab(initiator);
|
||||
if (tab_id.is_valid()) {
|
||||
MediaSource mirroring_source(MediaSourceForTab(tab_id.id()));
|
||||
query_result_manager_->SetSourcesForCastMode(MediaCastMode::TAB_MIRROR,
|
||||
{mirroring_source}, origin);
|
||||
}
|
||||
|
||||
UpdateCastModes();
|
||||
|
||||
// Get the current list of media routes, so that the WebUI will have routes
|
||||
// information at initialization.
|
||||
OnRoutesUpdated(router_->GetCurrentRoutes(), std::vector<MediaRoute::Id>());
|
||||
#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
|
||||
display_observer_ = WebContentsDisplayObserver::Create(
|
||||
initiator_,
|
||||
base::BindRepeating(&MediaRouterUI::UpdateSinks, base::Unretained(this)));
|
||||
#endif
|
||||
}
|
||||
|
||||
void MediaRouterUI::InitForTest(
|
||||
@@ -493,12 +279,12 @@ void MediaRouterUI::InitForTest(
|
||||
std::unique_ptr<StartPresentationContext> context,
|
||||
std::unique_ptr<MediaRouterFileDialog> file_dialog) {
|
||||
handler_ = handler;
|
||||
start_presentation_context_ = std::move(context);
|
||||
set_start_presentation_context_for_test(std::move(context));
|
||||
InitForTest(std::move(file_dialog));
|
||||
InitCommon(initiator);
|
||||
if (start_presentation_context_) {
|
||||
if (start_presentation_context()) {
|
||||
OnDefaultPresentationChanged(
|
||||
start_presentation_context_->presentation_request());
|
||||
start_presentation_context()->presentation_request());
|
||||
}
|
||||
|
||||
UIInitialized();
|
||||
@@ -511,64 +297,39 @@ void MediaRouterUI::InitForTest(
|
||||
|
||||
void MediaRouterUI::OnDefaultPresentationChanged(
|
||||
const content::PresentationRequest& presentation_request) {
|
||||
std::vector<MediaSource> sources =
|
||||
MediaSourcesForPresentationUrls(presentation_request.presentation_urls);
|
||||
presentation_request_ = presentation_request;
|
||||
query_result_manager_->SetSourcesForCastMode(
|
||||
MediaCastMode::PRESENTATION, sources,
|
||||
presentation_request_->frame_origin);
|
||||
// Register for MediaRoute updates. NOTE(mfoltz): If there are multiple
|
||||
// sources that can be connected to via the dialog, this will break. We will
|
||||
// need to observe multiple sources (keyed by sinks) in that case. As this is
|
||||
// Cast-specific for the forseeable future, it may be simpler to plumb a new
|
||||
// observer API for this case.
|
||||
const MediaSource source_for_route_observer =
|
||||
GetSourceForRouteObserver(sources);
|
||||
routes_observer_ = std::make_unique<UIMediaRoutesObserver>(
|
||||
router_, source_for_route_observer.id(),
|
||||
base::BindRepeating(&MediaRouterUI::OnRoutesUpdated,
|
||||
base::Unretained(this)));
|
||||
|
||||
MediaRouterUIBase::OnDefaultPresentationChanged(presentation_request);
|
||||
UpdateCastModes();
|
||||
}
|
||||
|
||||
void MediaRouterUI::OnDefaultPresentationRemoved() {
|
||||
presentation_request_.reset();
|
||||
query_result_manager_->RemoveSourcesForCastMode(MediaCastMode::PRESENTATION);
|
||||
MediaRouterUIBase::OnDefaultPresentationRemoved();
|
||||
|
||||
// This should not be set if the dialog was initiated with a default
|
||||
// presentation request from the top level frame. However, clear it just to
|
||||
// be safe.
|
||||
forced_cast_mode_ = base::nullopt;
|
||||
|
||||
// Register for MediaRoute updates without a media source.
|
||||
routes_observer_ = std::make_unique<UIMediaRoutesObserver>(
|
||||
router_, MediaSource::Id(),
|
||||
base::BindRepeating(&MediaRouterUI::OnRoutesUpdated,
|
||||
base::Unretained(this)));
|
||||
UpdateCastModes();
|
||||
}
|
||||
|
||||
void MediaRouterUI::UpdateCastModes() {
|
||||
// Gets updated cast modes from |query_result_manager_| and forwards it to UI.
|
||||
cast_modes_ = query_result_manager_->GetSupportedCastModes();
|
||||
// Gets updated cast modes from |query_result_manager()| and forwards it to
|
||||
// UI.
|
||||
cast_modes_ = query_result_manager()->GetSupportedCastModes();
|
||||
if (ui_initialized_) {
|
||||
handler_->UpdateCastModes(cast_modes_, GetPresentationRequestSourceName(),
|
||||
handler_->UpdateCastModes(cast_modes(), GetPresentationRequestSourceName(),
|
||||
forced_cast_mode());
|
||||
}
|
||||
}
|
||||
|
||||
void MediaRouterUI::UpdateRoutesToCastModesMapping() {
|
||||
std::unordered_map<MediaSource::Id, MediaCastMode> available_source_map;
|
||||
for (const auto& cast_mode : cast_modes_) {
|
||||
for (const auto& source :
|
||||
query_result_manager_->GetSourcesForCastMode(cast_mode)) {
|
||||
for (const auto& cast_mode : cast_modes()) {
|
||||
for (const auto& source : GetSourcesForCastMode(cast_mode))
|
||||
available_source_map.insert(std::make_pair(source.id(), cast_mode));
|
||||
}
|
||||
}
|
||||
|
||||
routes_and_cast_modes_.clear();
|
||||
for (const auto& route : routes_) {
|
||||
for (const auto& route : routes()) {
|
||||
auto source_entry = available_source_map.find(route.media_source().id());
|
||||
if (source_entry != available_source_map.end()) {
|
||||
routes_and_cast_modes_.insert(
|
||||
@@ -586,202 +347,96 @@ void MediaRouterUI::Close() {
|
||||
}
|
||||
|
||||
void MediaRouterUI::UIInitialized() {
|
||||
TRACE_EVENT_NESTABLE_ASYNC_END0("media_router", "UI", initiator_);
|
||||
ui_initialized_ = true;
|
||||
TRACE_EVENT_NESTABLE_ASYNC_END0("media_router", "UI", initiator());
|
||||
|
||||
// Workaround for MediaRouterElementsBrowserTest, in which MediaRouterUI is
|
||||
// created without calling one of the |Init*()| methods.
|
||||
// TODO(imcheng): We should be able to instantiate |issue_observer_| during
|
||||
// InitCommon by storing an initial Issue in this class.
|
||||
if (!router_)
|
||||
router_ = GetMediaRouter();
|
||||
|
||||
// Register for Issue updates.
|
||||
issues_observer_ =
|
||||
std::make_unique<UIIssuesObserver>(GetIssueManager(), this);
|
||||
issues_observer_->Init();
|
||||
|
||||
ui_initialized_ = true;
|
||||
}
|
||||
|
||||
bool MediaRouterUI::CreateRoute(const MediaSink::Id& sink_id,
|
||||
MediaCastMode cast_mode) {
|
||||
MediaSource::Id source_id;
|
||||
url::Origin origin;
|
||||
std::vector<MediaRouteResponseCallback> route_response_callbacks;
|
||||
base::TimeDelta timeout;
|
||||
bool incognito;
|
||||
|
||||
// Default the tab casting the content to the initiator, and change if
|
||||
// necessary.
|
||||
content::WebContents* tab_contents = initiator_;
|
||||
content::WebContents* tab_contents = initiator();
|
||||
|
||||
base::Optional<RouteParameters> params;
|
||||
if (cast_mode == MediaCastMode::LOCAL_FILE) {
|
||||
GURL url = media_router_file_dialog_->GetLastSelectedFileUrl();
|
||||
tab_contents = OpenTabWithUrl(url);
|
||||
|
||||
SessionID tab_id = SessionTabHelper::IdForTab(tab_contents);
|
||||
source_id = MediaSourceForTab(tab_id.id()).id();
|
||||
|
||||
SetLocalFileRouteParameters(sink_id, &origin, url, tab_contents,
|
||||
&route_response_callbacks, &timeout,
|
||||
&incognito);
|
||||
} else if (!SetRouteParameters(sink_id, cast_mode, &source_id, &origin,
|
||||
&route_response_callbacks, &timeout,
|
||||
&incognito)) {
|
||||
params = GetLocalFileRouteParameters(sink_id, url, tab_contents);
|
||||
} else {
|
||||
params = GetRouteParameters(sink_id, cast_mode);
|
||||
}
|
||||
if (!params) {
|
||||
SendIssueForUnableToCast(cast_mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
GetIssueManager()->ClearNonBlockingIssues();
|
||||
router_->CreateRoute(source_id, sink_id, origin, tab_contents,
|
||||
std::move(route_response_callbacks), timeout, incognito);
|
||||
GetMediaRouter()->CreateRoute(params->source_id, sink_id, params->origin,
|
||||
tab_contents,
|
||||
std::move(params->route_response_callbacks),
|
||||
params->timeout, params->incognito);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MediaRouterUI::SetRouteParameters(
|
||||
base::Optional<RouteParameters> MediaRouterUI::GetLocalFileRouteParameters(
|
||||
const MediaSink::Id& sink_id,
|
||||
MediaCastMode cast_mode,
|
||||
MediaSource::Id* source_id,
|
||||
url::Origin* origin,
|
||||
std::vector<MediaRouteResponseCallback>* route_response_callbacks,
|
||||
base::TimeDelta* timeout,
|
||||
bool* incognito) {
|
||||
DCHECK(query_result_manager_.get());
|
||||
DCHECK(initiator_);
|
||||
|
||||
// Note that there is a rarely-encountered bug, where the MediaCastMode to
|
||||
// MediaSource mapping could have been updated, between when the user clicked
|
||||
// on the UI to start a create route request, and when this function is
|
||||
// called. However, since the user does not have visibility into the
|
||||
// MediaSource, and that it occurs very rarely in practice, we leave it as-is
|
||||
// for now.
|
||||
|
||||
std::unique_ptr<MediaSource> source =
|
||||
query_result_manager_->GetSourceForCastModeAndSink(cast_mode, sink_id);
|
||||
|
||||
if (!source) {
|
||||
LOG(ERROR) << "No corresponding MediaSource for cast mode "
|
||||
<< static_cast<int>(cast_mode) << " and sink " << sink_id;
|
||||
return false;
|
||||
}
|
||||
*source_id = source->id();
|
||||
|
||||
bool for_presentation_source = cast_mode == MediaCastMode::PRESENTATION;
|
||||
if (for_presentation_source && !presentation_request_) {
|
||||
DLOG(ERROR) << "Requested to create a route for presentation, but "
|
||||
<< "presentation request is missing.";
|
||||
return false;
|
||||
}
|
||||
|
||||
current_route_request_id_ = ++route_request_counter_;
|
||||
*origin = for_presentation_source
|
||||
? presentation_request_->frame_origin
|
||||
: url::Origin::Create(GURL(chrome::kChromeUIMediaRouterURL));
|
||||
DVLOG(1) << "DoCreateRoute: origin: " << *origin;
|
||||
|
||||
// There are 3 cases. In cases (1) and (3) the MediaRouterUI will need to be
|
||||
// notified. In case (2) the dialog will be closed.
|
||||
// (1) Non-presentation route request (e.g., mirroring). No additional
|
||||
// notification necessary.
|
||||
// (2) Presentation route request for a PresentationRequest.start() call.
|
||||
// The StartPresentationContext will need to be answered with the route
|
||||
// response. (3) Browser-initiated presentation route request. If successful,
|
||||
// PresentationServiceDelegateImpl will have to be notified. Note that we
|
||||
// treat subsequent route requests from a Presentation API-initiated dialogs
|
||||
// as browser-initiated.
|
||||
if (!for_presentation_source || !start_presentation_context_) {
|
||||
route_response_callbacks->push_back(base::BindOnce(
|
||||
&MediaRouterUI::OnRouteResponseReceived, weak_factory_.GetWeakPtr(),
|
||||
current_route_request_id_, sink_id, cast_mode,
|
||||
base::UTF8ToUTF16(GetTruncatedPresentationRequestSourceName())));
|
||||
}
|
||||
if (for_presentation_source) {
|
||||
if (start_presentation_context_) {
|
||||
// |start_presentation_context_| will be nullptr after this call, as the
|
||||
// object will be transferred to the callback.
|
||||
route_response_callbacks->push_back(
|
||||
base::BindOnce(&StartPresentationContext::HandleRouteResponse,
|
||||
std::move(start_presentation_context_)));
|
||||
route_response_callbacks->push_back(base::BindOnce(
|
||||
&MediaRouterUI::HandleCreateSessionRequestRouteResponse,
|
||||
weak_factory_.GetWeakPtr()));
|
||||
} else if (presentation_service_delegate_) {
|
||||
route_response_callbacks->push_back(base::BindOnce(
|
||||
&PresentationServiceDelegateImpl::OnRouteResponse,
|
||||
presentation_service_delegate_, *presentation_request_));
|
||||
}
|
||||
}
|
||||
|
||||
route_response_callbacks->push_back(
|
||||
base::BindOnce(&MediaRouterUI::MaybeReportCastingSource,
|
||||
weak_factory_.GetWeakPtr(), cast_mode));
|
||||
|
||||
*timeout = GetRouteRequestTimeout(cast_mode);
|
||||
*incognito = Profile::FromWebUI(web_ui())->IsOffTheRecord();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(crbug.com/751317): This function and the above function are messy, this
|
||||
// code would be much neater if the route params were combined in a single
|
||||
// struct, which will require mojo changes as well.
|
||||
bool MediaRouterUI::SetLocalFileRouteParameters(
|
||||
const MediaSink::Id& sink_id,
|
||||
url::Origin* origin,
|
||||
const GURL& file_url,
|
||||
content::WebContents* tab_contents,
|
||||
std::vector<MediaRouteResponseCallback>* route_response_callbacks,
|
||||
base::TimeDelta* timeout,
|
||||
bool* incognito) {
|
||||
content::WebContents* tab_contents) {
|
||||
RouteParameters params;
|
||||
SessionID::id_type tab_id = SessionTabHelper::IdForTab(tab_contents).id();
|
||||
params.source_id = MediaSourceForTab(tab_id).id();
|
||||
|
||||
// Use a placeholder URL as origin for local file casting, which is
|
||||
// essentially mirroring.
|
||||
*origin = url::Origin::Create(GURL(chrome::kChromeUIMediaRouterURL));
|
||||
params.origin = url::Origin::Create(GURL(chrome::kChromeUIMediaRouterURL));
|
||||
|
||||
route_response_callbacks->push_back(base::BindOnce(
|
||||
params.route_response_callbacks.push_back(base::BindOnce(
|
||||
&MediaRouterUI::OnRouteResponseReceived, weak_factory_.GetWeakPtr(),
|
||||
current_route_request_id_, sink_id, MediaCastMode::LOCAL_FILE,
|
||||
current_route_request_id(), sink_id, MediaCastMode::LOCAL_FILE,
|
||||
base::UTF8ToUTF16(GetTruncatedPresentationRequestSourceName())));
|
||||
|
||||
route_response_callbacks->push_back(
|
||||
base::BindOnce(&MediaRouterUI::MaybeReportCastingSource,
|
||||
params.route_response_callbacks.push_back(
|
||||
base::BindOnce(&MediaRouterUIBase::MaybeReportCastingSource,
|
||||
weak_factory_.GetWeakPtr(), MediaCastMode::LOCAL_FILE));
|
||||
|
||||
route_response_callbacks->push_back(base::BindOnce(
|
||||
params.route_response_callbacks.push_back(base::BindOnce(
|
||||
&MediaRouterUI::MaybeReportFileInformation, weak_factory_.GetWeakPtr()));
|
||||
|
||||
route_response_callbacks->push_back(
|
||||
params.route_response_callbacks.push_back(
|
||||
base::BindOnce(&MediaRouterUI::FullScreenFirstVideoElement,
|
||||
weak_factory_.GetWeakPtr(), file_url, tab_contents));
|
||||
|
||||
*timeout = GetRouteRequestTimeout(MediaCastMode::LOCAL_FILE);
|
||||
*incognito = Profile::FromWebUI(web_ui())->IsOffTheRecord();
|
||||
params.timeout = GetRouteRequestTimeout(MediaCastMode::LOCAL_FILE);
|
||||
CHECK(initiator());
|
||||
params.incognito = initiator()->GetBrowserContext()->IsOffTheRecord();
|
||||
|
||||
return true;
|
||||
return params;
|
||||
}
|
||||
|
||||
bool MediaRouterUI::ConnectRoute(const MediaSink::Id& sink_id,
|
||||
const MediaRoute::Id& route_id) {
|
||||
MediaSource::Id source_id;
|
||||
url::Origin origin;
|
||||
std::vector<MediaRouteResponseCallback> route_response_callbacks;
|
||||
base::TimeDelta timeout;
|
||||
bool incognito;
|
||||
if (!SetRouteParameters(sink_id, MediaCastMode::PRESENTATION, &source_id,
|
||||
&origin, &route_response_callbacks, &timeout,
|
||||
&incognito)) {
|
||||
base::Optional<RouteParameters> params =
|
||||
GetRouteParameters(sink_id, MediaCastMode::PRESENTATION);
|
||||
if (!params) {
|
||||
SendIssueForUnableToCast(MediaCastMode::PRESENTATION);
|
||||
return false;
|
||||
}
|
||||
GetIssueManager()->ClearNonBlockingIssues();
|
||||
router_->ConnectRouteByRouteId(source_id, route_id, origin, initiator_,
|
||||
std::move(route_response_callbacks), timeout,
|
||||
incognito);
|
||||
GetMediaRouter()->ConnectRouteByRouteId(
|
||||
params->source_id, route_id, params->origin, initiator(),
|
||||
std::move(params->route_response_callbacks), params->timeout,
|
||||
params->incognito);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MediaRouterUI::CloseRoute(const MediaRoute::Id& route_id) {
|
||||
router_->TerminateRoute(route_id);
|
||||
}
|
||||
|
||||
void MediaRouterUI::AddIssue(const IssueInfo& issue) {
|
||||
GetIssueManager()->AddIssue(issue);
|
||||
}
|
||||
@@ -804,12 +459,12 @@ void MediaRouterUI::SearchSinksAndCreateRoute(
|
||||
const std::string& domain,
|
||||
MediaCastMode cast_mode) {
|
||||
std::unique_ptr<MediaSource> source =
|
||||
query_result_manager_->GetSourceForCastModeAndSink(cast_mode, sink_id);
|
||||
query_result_manager()->GetSourceForCastModeAndSink(cast_mode, sink_id);
|
||||
const std::string source_id = source ? source->id() : "";
|
||||
|
||||
// The CreateRoute() part of the function is accomplished in the callback
|
||||
// OnSearchSinkResponseReceived().
|
||||
router_->SearchSinks(
|
||||
GetMediaRouter()->SearchSinks(
|
||||
sink_id, source_id, search_criteria, domain,
|
||||
base::BindRepeating(&MediaRouterUI::OnSearchSinkResponseReceived,
|
||||
weak_factory_.GetWeakPtr(), cast_mode));
|
||||
@@ -848,21 +503,6 @@ void MediaRouterUI::RecordCastModeSelection(MediaCastMode cast_mode) {
|
||||
}
|
||||
}
|
||||
|
||||
void MediaRouterUI::OnResultsUpdated(
|
||||
const std::vector<MediaSinkWithCastModes>& sinks) {
|
||||
sinks_ = sinks;
|
||||
|
||||
const icu::Collator* collator_ptr = collator_.get();
|
||||
std::sort(sinks_.begin(), sinks_.end(),
|
||||
[collator_ptr](const MediaSinkWithCastModes& sink1,
|
||||
const MediaSinkWithCastModes& sink2) {
|
||||
return sink1.sink.CompareUsingCollator(sink2.sink, collator_ptr);
|
||||
});
|
||||
|
||||
if (ui_initialized_)
|
||||
UpdateSinks();
|
||||
}
|
||||
|
||||
void MediaRouterUI::SetIssue(const Issue& issue) {
|
||||
if (ui_initialized_)
|
||||
handler_->UpdateIssue(issue);
|
||||
@@ -876,33 +516,21 @@ void MediaRouterUI::ClearIssue() {
|
||||
void MediaRouterUI::OnRoutesUpdated(
|
||||
const std::vector<MediaRoute>& routes,
|
||||
const std::vector<MediaRoute::Id>& joinable_route_ids) {
|
||||
routes_.clear();
|
||||
MediaRouterUIBase::OnRoutesUpdated(routes, joinable_route_ids);
|
||||
joinable_route_ids_.clear();
|
||||
|
||||
for (const MediaRoute& route : routes) {
|
||||
if (route.for_display()) {
|
||||
#ifndef NDEBUG
|
||||
for (const MediaRoute& existing_route : routes_) {
|
||||
if (existing_route.media_sink_id() == route.media_sink_id()) {
|
||||
DVLOG(2) << "Received another route for display with the same sink"
|
||||
<< " id as an existing route. " << route.media_route_id()
|
||||
<< " has the same sink id as "
|
||||
<< existing_route.media_sink_id() << ".";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (base::ContainsValue(joinable_route_ids, route.media_route_id())) {
|
||||
joinable_route_ids_.push_back(route.media_route_id());
|
||||
}
|
||||
|
||||
routes_.push_back(route);
|
||||
if (route.for_display() &&
|
||||
base::ContainsValue(joinable_route_ids, route.media_route_id())) {
|
||||
joinable_route_ids_.push_back(route.media_route_id());
|
||||
}
|
||||
}
|
||||
UpdateRoutesToCastModesMapping();
|
||||
|
||||
if (ui_initialized_)
|
||||
handler_->UpdateRoutes(routes_, joinable_route_ids_,
|
||||
routes_and_cast_modes_);
|
||||
if (ui_initialized_) {
|
||||
handler_->UpdateRoutes(MediaRouterUIBase::routes(), joinable_route_ids_,
|
||||
routes_and_cast_modes());
|
||||
}
|
||||
UpdateRoutesToCastModesMapping();
|
||||
}
|
||||
|
||||
void MediaRouterUI::OnRouteResponseReceived(
|
||||
@@ -911,30 +539,14 @@ void MediaRouterUI::OnRouteResponseReceived(
|
||||
MediaCastMode cast_mode,
|
||||
const base::string16& presentation_request_source_name,
|
||||
const RouteRequestResult& result) {
|
||||
DVLOG(1) << "OnRouteResponseReceived";
|
||||
// If we receive a new route that we aren't expecting, do nothing.
|
||||
if (route_request_id != current_route_request_id_)
|
||||
return;
|
||||
|
||||
const MediaRoute* route = result.route();
|
||||
if (!route) {
|
||||
// The provider will handle sending an issue for a failed route request.
|
||||
DVLOG(1) << "MediaRouteResponse returned error: " << result.error();
|
||||
}
|
||||
|
||||
handler_->OnCreateRouteResponseReceived(sink_id, route);
|
||||
current_route_request_id_ = -1;
|
||||
|
||||
MediaRouterUIBase::OnRouteResponseReceived(
|
||||
route_request_id, sink_id, cast_mode, presentation_request_source_name,
|
||||
result);
|
||||
handler_->OnCreateRouteResponseReceived(sink_id, result.route());
|
||||
if (result.result_code() == RouteRequestResult::TIMED_OUT)
|
||||
SendIssueForRouteTimeout(cast_mode, presentation_request_source_name);
|
||||
}
|
||||
|
||||
void MediaRouterUI::MaybeReportCastingSource(MediaCastMode cast_mode,
|
||||
const RouteRequestResult& result) {
|
||||
if (result.result_code() == RouteRequestResult::OK)
|
||||
MediaRouterMetrics::RecordMediaRouterCastingSource(cast_mode);
|
||||
}
|
||||
|
||||
// TODO(crbug.com/792547): Refactor these next two methods into a local media
|
||||
// casting specific location instead of here in the main ui.
|
||||
void MediaRouterUI::FullScreenFirstVideoElement(
|
||||
@@ -1007,35 +619,6 @@ void MediaRouterUI::SendIssueForUnableToCast(MediaCastMode cast_mode) {
|
||||
IssueInfo::Severity::WARNING));
|
||||
}
|
||||
|
||||
GURL MediaRouterUI::GetFrameURL() const {
|
||||
return presentation_request_ ? presentation_request_->frame_origin.GetURL()
|
||||
: GURL();
|
||||
}
|
||||
|
||||
std::vector<MediaSinkWithCastModes> MediaRouterUI::GetEnabledSinks() const {
|
||||
#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
|
||||
if (!display_observer_)
|
||||
return sinks_;
|
||||
|
||||
// Filter out the wired display sink for the display that the dialog is on.
|
||||
// This is not the best place to do this because MRUI should not perform a
|
||||
// provider-specific behavior, but we currently do not have a way to
|
||||
// communicate dialog-specific information to/from the
|
||||
// WiredDisplayMediaRouteProvider.
|
||||
std::vector<MediaSinkWithCastModes> enabled_sinks;
|
||||
const std::string display_sink_id =
|
||||
WiredDisplayMediaRouteProvider::GetSinkIdForDisplay(
|
||||
display_observer_->GetCurrentDisplay());
|
||||
for (const MediaSinkWithCastModes& sink : sinks_) {
|
||||
if (sink.sink.id() != display_sink_id)
|
||||
enabled_sinks.push_back(sink);
|
||||
}
|
||||
return enabled_sinks;
|
||||
#else
|
||||
return sinks_;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string MediaRouterUI::GetPresentationRequestSourceName() const {
|
||||
GURL gurl = GetFrameURL();
|
||||
return gurl.SchemeIs(extensions::kExtensionScheme)
|
||||
@@ -1044,14 +627,6 @@ std::string MediaRouterUI::GetPresentationRequestSourceName() const {
|
||||
: GetHostFromURL(gurl);
|
||||
}
|
||||
|
||||
std::string MediaRouterUI::GetTruncatedPresentationRequestSourceName() const {
|
||||
GURL gurl = GetFrameURL();
|
||||
return gurl.SchemeIs(extensions::kExtensionScheme)
|
||||
? GetExtensionName(gurl, extensions::ExtensionRegistry::Get(
|
||||
Profile::FromWebUI(web_ui())))
|
||||
: TruncateHost(GetHostFromURL(gurl));
|
||||
}
|
||||
|
||||
const std::set<MediaCastMode>& MediaRouterUI::cast_modes() const {
|
||||
return cast_modes_;
|
||||
}
|
||||
@@ -1091,7 +666,7 @@ MediaRouteController* MediaRouterUI::GetMediaRouteController() const {
|
||||
void MediaRouterUI::OnMediaControllerUIAvailable(
|
||||
const MediaRoute::Id& route_id) {
|
||||
scoped_refptr<MediaRouteController> controller =
|
||||
router_->GetRouteController(route_id);
|
||||
GetMediaRouter()->GetRouteController(route_id);
|
||||
if (!controller) {
|
||||
DVLOG(1) << "Requested a route controller with an invalid route ID.";
|
||||
return;
|
||||
@@ -1110,28 +685,29 @@ void MediaRouterUI::OnRouteControllerInvalidated() {
|
||||
route_controller_observer_.reset();
|
||||
handler_->OnRouteControllerInvalidated();
|
||||
}
|
||||
|
||||
void MediaRouterUI::UpdateMediaRouteStatus(const MediaStatus& status) {
|
||||
handler_->UpdateMediaRouteStatus(status);
|
||||
}
|
||||
|
||||
std::string MediaRouterUI::GetSerializedInitiatorOrigin() const {
|
||||
url::Origin origin =
|
||||
initiator_ ? url::Origin::Create(initiator_->GetLastCommittedURL())
|
||||
: url::Origin();
|
||||
initiator() ? url::Origin::Create(initiator()->GetLastCommittedURL())
|
||||
: url::Origin();
|
||||
return origin.Serialize();
|
||||
}
|
||||
|
||||
IssueManager* MediaRouterUI::GetIssueManager() {
|
||||
return router_->GetIssueManager();
|
||||
return GetMediaRouter()->GetIssueManager();
|
||||
}
|
||||
|
||||
void MediaRouterUI::UpdateSinks() {
|
||||
handler_->UpdateSinks(GetEnabledSinks());
|
||||
if (ui_initialized_)
|
||||
handler_->UpdateSinks(GetEnabledSinks());
|
||||
}
|
||||
|
||||
MediaRouter* MediaRouterUI::GetMediaRouter() {
|
||||
MediaRouter* MediaRouterUI::GetMediaRouter() const {
|
||||
return MediaRouterFactory::GetApiForBrowserContext(
|
||||
web_ui()->GetWebContents()->GetBrowserContext());
|
||||
}
|
||||
|
||||
} // namespace media_router
|
||||
|
@@ -5,129 +5,53 @@
|
||||
#ifndef CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_UI_H_
|
||||
#define CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_UI_H_
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/gtest_prod_util.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/timer/timer.h"
|
||||
#include "build/build_config.h"
|
||||
#include "chrome/browser/media/router/media_router_dialog_controller.h"
|
||||
#include "base/strings/string16.h"
|
||||
#include "chrome/browser/media/router/mojo/media_route_controller.h"
|
||||
#include "chrome/browser/media/router/presentation/presentation_service_delegate_impl.h"
|
||||
#include "chrome/browser/ui/media_router/media_cast_mode.h"
|
||||
#include "chrome/browser/ui/media_router/media_router_file_dialog.h"
|
||||
#include "chrome/browser/ui/media_router/media_sink_with_cast_modes.h"
|
||||
#include "chrome/browser/ui/media_router/query_result_manager.h"
|
||||
#include "chrome/browser/ui/media_router/media_router_ui_base.h"
|
||||
#include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
|
||||
#include "chrome/common/media_router/issue.h"
|
||||
#include "chrome/common/media_router/media_source.h"
|
||||
#include "content/public/browser/web_ui_data_source.h"
|
||||
#include "third_party/icu/source/common/unicode/uversion.h"
|
||||
#include "ui/base/ui_features.h"
|
||||
#include "url/gurl.h"
|
||||
#include "chrome/common/media_router/media_route.h"
|
||||
#include "chrome/common/media_router/media_sink.h"
|
||||
|
||||
#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
|
||||
#include "chrome/browser/ui/webui/media_router/web_contents_display_observer.h"
|
||||
#endif
|
||||
|
||||
namespace content {
|
||||
struct PresentationRequest;
|
||||
class WebContents;
|
||||
namespace ui {
|
||||
struct SelectedFileInfo;
|
||||
}
|
||||
|
||||
namespace extensions {
|
||||
class ExtensionRegistry;
|
||||
}
|
||||
|
||||
namespace U_ICU_NAMESPACE {
|
||||
class Collator;
|
||||
}
|
||||
|
||||
class Browser;
|
||||
|
||||
namespace media_router {
|
||||
|
||||
struct MediaStatus;
|
||||
class Issue;
|
||||
class IssueManager;
|
||||
class IssuesObserver;
|
||||
class MediaRoute;
|
||||
class MediaRouter;
|
||||
class MediaRoutesObserver;
|
||||
class MediaRouterWebUIMessageHandler;
|
||||
class MediaSink;
|
||||
class RouteRequestResult;
|
||||
|
||||
// Implements the chrome://media-router user interface.
|
||||
// Functions as an intermediary between MediaRouter and WebUI Cast dialog.
|
||||
class MediaRouterUI
|
||||
: public ConstrainedWebDialogUI,
|
||||
public QueryResultManager::Observer,
|
||||
public PresentationServiceDelegateImpl::
|
||||
DefaultPresentationRequestObserver,
|
||||
: public MediaRouterUIBase,
|
||||
public ConstrainedWebDialogUI,
|
||||
public MediaRouterFileDialog::MediaRouterFileDialogDelegate {
|
||||
public:
|
||||
// |web_ui| owns this object and is used to initialize the base class.
|
||||
explicit MediaRouterUI(content::WebUI* web_ui);
|
||||
~MediaRouterUI() override;
|
||||
|
||||
// Initializes internal state (e.g. starts listening for MediaSinks) for
|
||||
// targeting the default MediaSource (if any) of the initiator tab that owns
|
||||
// |initiator|: Reference to the WebContents that initiated the dialog.
|
||||
// Must not be null.
|
||||
// |delegate|, as well as mirroring sources of that tab.
|
||||
// The contents of the UI will change as the default MediaSource changes.
|
||||
// If there is a default MediaSource, then PRESENTATION MediaCastMode will be
|
||||
// added to |cast_modes_|.
|
||||
// Init* methods can only be called once.
|
||||
// |delegate|: PresentationServiceDelegateImpl of the initiator tab.
|
||||
// Must not be null.
|
||||
// TODO(imcheng): Replace use of impl with an intermediate abstract
|
||||
// interface.
|
||||
void InitWithDefaultMediaSource(content::WebContents* initiator,
|
||||
PresentationServiceDelegateImpl* delegate);
|
||||
|
||||
// Initializes internal state targeting the presentation specified in
|
||||
// |context|. Also sets up mirroring sources based on |initiator|.
|
||||
// This is different from |InitWithDefaultMediaSource| in that it does not
|
||||
// listen for default media source changes, as the UI is fixed to the source
|
||||
// in |request|.
|
||||
// Init* methods can only be called once.
|
||||
// |initiator|: Reference to the WebContents that initiated the dialog.
|
||||
// Must not be null.
|
||||
// |delegate|: PresentationServiceDelegateImpl of the initiator tab.
|
||||
// Must not be null.
|
||||
// |context|: Context object for the PresentationRequest. This instance will
|
||||
// take
|
||||
// ownership of it. Must not be null.
|
||||
void InitWithStartPresentationContext(
|
||||
content::WebContents* initiator,
|
||||
PresentationServiceDelegateImpl* delegate,
|
||||
std::unique_ptr<StartPresentationContext> context);
|
||||
|
||||
// Closes the media router UI.
|
||||
void Close();
|
||||
|
||||
// Notifies this instance that the UI has been initialized.
|
||||
virtual void UIInitialized();
|
||||
|
||||
// Requests a route be created from the source mapped to
|
||||
// |cast_mode|, to the sink given by |sink_id|.
|
||||
// Returns true if a route request is successfully submitted.
|
||||
// |OnRouteResponseReceived()| will be invoked when the route request
|
||||
// completes.
|
||||
bool CreateRoute(const MediaSink::Id& sink_id, MediaCastMode cast_mode);
|
||||
// MediaRouterUIBase:
|
||||
bool CreateRoute(const MediaSink::Id& sink_id,
|
||||
MediaCastMode cast_mode) override;
|
||||
|
||||
// Calls MediaRouter to join the given route.
|
||||
bool ConnectRoute(const MediaSink::Id& sink_id,
|
||||
const MediaRoute::Id& route_id);
|
||||
|
||||
// Calls MediaRouter to close the given route.
|
||||
void CloseRoute(const MediaRoute::Id& route_id);
|
||||
|
||||
// Calls MediaRouter to add the given issue.
|
||||
void AddIssue(const IssueInfo& issue);
|
||||
|
||||
@@ -153,16 +77,11 @@ class MediaRouterUI
|
||||
// mode is MediaCastMode::DESKTOP_MIRROR.
|
||||
virtual void RecordCastModeSelection(MediaCastMode cast_mode);
|
||||
|
||||
// Returns a subset of |sinks_| that should be listed in the dialog.
|
||||
std::vector<MediaSinkWithCastModes> GetEnabledSinks() const;
|
||||
|
||||
// Returns the hostname of the PresentationRequest's parent frame URL.
|
||||
std::string GetPresentationRequestSourceName() const;
|
||||
std::string GetTruncatedPresentationRequestSourceName() const;
|
||||
virtual std::string GetPresentationRequestSourceName() const;
|
||||
bool HasPendingRouteRequest() const {
|
||||
return current_route_request_id_ != -1;
|
||||
return current_route_request_id() != -1;
|
||||
}
|
||||
const std::vector<MediaRoute>& routes() const { return routes_; }
|
||||
const std::vector<MediaRoute::Id>& joinable_route_ids() const {
|
||||
return joinable_route_ids_;
|
||||
}
|
||||
@@ -171,7 +90,6 @@ class MediaRouterUI
|
||||
routes_and_cast_modes() const {
|
||||
return routes_and_cast_modes_;
|
||||
}
|
||||
const content::WebContents* initiator() const { return initiator_; }
|
||||
const base::Optional<MediaCastMode>& forced_cast_mode() const {
|
||||
return forced_cast_mode_;
|
||||
}
|
||||
@@ -203,13 +121,6 @@ class MediaRouterUI
|
||||
|
||||
void InitForTest(std::unique_ptr<MediaRouterFileDialog> file_dialog);
|
||||
|
||||
#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
|
||||
void set_display_observer_for_test(
|
||||
std::unique_ptr<WebContentsDisplayObserver> display_observer) {
|
||||
display_observer_ = std::move(display_observer);
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
friend class MediaRouterUITest;
|
||||
|
||||
@@ -228,7 +139,8 @@ class MediaRouterUI
|
||||
GetExtensionNameEmptyWhenNotExtensionURL);
|
||||
FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest,
|
||||
RouteCreationTimeoutForPresentation);
|
||||
FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest, RouteRequestFromIncognito);
|
||||
FRIEND_TEST_ALL_PREFIXES(MediaRouterUIIncognitoTest,
|
||||
RouteRequestFromIncognito);
|
||||
FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest, OpenAndCloseUIDetailsView);
|
||||
FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest, SendMediaCommands);
|
||||
FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest, SendMediaStatusUpdate);
|
||||
@@ -239,28 +151,6 @@ class MediaRouterUI
|
||||
class UIIssuesObserver;
|
||||
class WebContentsFullscreenOnLoadedObserver;
|
||||
|
||||
class UIMediaRoutesObserver : public MediaRoutesObserver {
|
||||
public:
|
||||
using RoutesUpdatedCallback =
|
||||
base::RepeatingCallback<void(const std::vector<MediaRoute>&,
|
||||
const std::vector<MediaRoute::Id>&)>;
|
||||
UIMediaRoutesObserver(MediaRouter* router,
|
||||
const MediaSource::Id& source_id,
|
||||
const RoutesUpdatedCallback& callback);
|
||||
~UIMediaRoutesObserver() override;
|
||||
|
||||
// MediaRoutesObserver
|
||||
void OnRoutesUpdated(
|
||||
const std::vector<MediaRoute>& routes,
|
||||
const std::vector<MediaRoute::Id>& joinable_route_ids) override;
|
||||
|
||||
private:
|
||||
// Callback to the owning MediaRouterUI instance.
|
||||
RoutesUpdatedCallback callback_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(UIMediaRoutesObserver);
|
||||
};
|
||||
|
||||
class UIMediaRouteControllerObserver : public MediaRouteController::Observer {
|
||||
public:
|
||||
explicit UIMediaRouteControllerObserver(
|
||||
@@ -268,7 +158,7 @@ class MediaRouterUI
|
||||
scoped_refptr<MediaRouteController> controller);
|
||||
~UIMediaRouteControllerObserver() override;
|
||||
|
||||
// MediaRouteController::Observer
|
||||
// MediaRouteController::Observer:
|
||||
void OnMediaStatusUpdated(const MediaStatus& status) override;
|
||||
void OnControllerInvalidated() override;
|
||||
|
||||
@@ -278,53 +168,39 @@ class MediaRouterUI
|
||||
DISALLOW_COPY_AND_ASSIGN(UIMediaRouteControllerObserver);
|
||||
};
|
||||
|
||||
static std::string GetExtensionName(const GURL& url,
|
||||
extensions::ExtensionRegistry* registry);
|
||||
|
||||
// Retrieves the browser associated with this UI.
|
||||
Browser* GetBrowser();
|
||||
|
||||
// Opens the URL in a tab, returns the tab it was opened in.
|
||||
content::WebContents* OpenTabWithUrl(const GURL url);
|
||||
|
||||
// Methods for MediaRouterFileDialogDelegate
|
||||
// MediaRouterFileDialogDelegate:
|
||||
void FileDialogFileSelected(const ui::SelectedFileInfo& file_info) override;
|
||||
void FileDialogSelectionFailed(const IssueInfo& issue) override;
|
||||
|
||||
// QueryResultManager::Observer
|
||||
void OnResultsUpdated(
|
||||
const std::vector<MediaSinkWithCastModes>& sinks) override;
|
||||
|
||||
// Called by |issues_observer_| when the top issue has changed.
|
||||
// If the UI is already initialized, notifies |handler_| to update the UI.
|
||||
// Ignored if the UI is not yet initialized.
|
||||
void SetIssue(const Issue& issue);
|
||||
void ClearIssue();
|
||||
|
||||
// Called by |routes_observer_| when the set of active routes has changed.
|
||||
void OnRoutesUpdated(const std::vector<MediaRoute>& routes,
|
||||
const std::vector<MediaRoute::Id>& joinable_route_ids);
|
||||
void OnRoutesUpdated(
|
||||
const std::vector<MediaRoute>& routes,
|
||||
const std::vector<MediaRoute::Id>& joinable_route_ids) override;
|
||||
|
||||
// Callback passed to MediaRouter to receive response to route creation
|
||||
// requests.
|
||||
void OnRouteResponseReceived(
|
||||
int route_request_id,
|
||||
const MediaSink::Id& sink_id,
|
||||
MediaCastMode cast_mode,
|
||||
const base::string16& presentation_request_source_name,
|
||||
const RouteRequestResult& result);
|
||||
const RouteRequestResult& result) override;
|
||||
|
||||
// Logs a UMA stat for the source that was cast if the result is successful.
|
||||
void MaybeReportCastingSource(MediaCastMode cast_mode,
|
||||
const RouteRequestResult& result);
|
||||
// Sends a request to the file dialog to log UMA stats for the file that was
|
||||
// cast if the result is successful.
|
||||
void MaybeReportFileInformation(const RouteRequestResult& result);
|
||||
|
||||
// Closes the dialog after receiving a route response when using
|
||||
// |start_presentation_context_|. This prevents the dialog from trying to use
|
||||
// the same presentation request again.
|
||||
void HandleCreateSessionRequestRouteResponse(const RouteRequestResult&);
|
||||
void HandleCreateSessionRequestRouteResponse(
|
||||
const RouteRequestResult&) override;
|
||||
|
||||
// Callback passed to MediaRouter to receive the sink ID of the sink found by
|
||||
// SearchSinksAndCreateRoute().
|
||||
@@ -339,35 +215,19 @@ class MediaRouterUI
|
||||
// Creates and sends an issue if casting fails for any other reason.
|
||||
void SendIssueForUnableToCast(MediaCastMode cast_mode);
|
||||
|
||||
// Initializes the dialog with mirroring sources derived from |initiator|.
|
||||
void InitCommon(content::WebContents* initiator);
|
||||
void InitCommon(content::WebContents* initiator) override;
|
||||
|
||||
// PresentationServiceDelegateImpl::DefaultPresentationObserver
|
||||
// PresentationServiceDelegateImpl::DefaultPresentationObserver:
|
||||
void OnDefaultPresentationChanged(
|
||||
const content::PresentationRequest& presentation_request) override;
|
||||
void OnDefaultPresentationRemoved() override;
|
||||
|
||||
// Populates common route-related parameters for CreateRoute(),
|
||||
// ConnectRoute(), and SearchSinksAndCreateRoute().
|
||||
bool SetRouteParameters(
|
||||
const MediaSink::Id& sink_id,
|
||||
MediaCastMode cast_mode,
|
||||
MediaSource::Id* source_id,
|
||||
url::Origin* origin,
|
||||
std::vector<MediaRouteResponseCallback>* route_response_callbacks,
|
||||
base::TimeDelta* timeout,
|
||||
bool* incognito);
|
||||
|
||||
// Populates route-related parameters for CreateRoute() when doing file
|
||||
// casting.
|
||||
bool SetLocalFileRouteParameters(
|
||||
base::Optional<RouteParameters> GetLocalFileRouteParameters(
|
||||
const MediaSink::Id& sink_id,
|
||||
url::Origin* origin,
|
||||
const GURL& file_url,
|
||||
content::WebContents* tab_contents,
|
||||
std::vector<MediaRouteResponseCallback>* route_response_callbacks,
|
||||
base::TimeDelta* timeout,
|
||||
bool* incognito);
|
||||
content::WebContents* tab_contents);
|
||||
|
||||
void FullScreenFirstVideoElement(const GURL& file_url,
|
||||
content::WebContents* web_contents,
|
||||
@@ -381,10 +241,6 @@ class MediaRouterUI
|
||||
// match the value of |routes_|.
|
||||
void UpdateRoutesToCastModesMapping();
|
||||
|
||||
// Returns the default PresentationRequest's frame URL if there is one.
|
||||
// Otherwise returns an empty GURL.
|
||||
GURL GetFrameURL() const;
|
||||
|
||||
// Returns the serialized origin for |initiator_|, or the serialization of an
|
||||
// opaque origin ("null") if |initiator_| is not set.
|
||||
std::string GetSerializedInitiatorOrigin() const;
|
||||
@@ -400,65 +256,23 @@ class MediaRouterUI
|
||||
// Returns the IssueManager associated with |router_|.
|
||||
IssueManager* GetIssueManager();
|
||||
|
||||
// Sends the current list of enabled sinks to |handler_|.
|
||||
void UpdateSinks();
|
||||
void UpdateSinks() override;
|
||||
|
||||
// Overridden by tests.
|
||||
virtual MediaRouter* GetMediaRouter();
|
||||
MediaRouter* GetMediaRouter() const override;
|
||||
|
||||
// Owned by the |web_ui| passed in the ctor, and guaranteed to be deleted
|
||||
// only after it has deleted |this|.
|
||||
MediaRouterWebUIMessageHandler* handler_ = nullptr;
|
||||
|
||||
// These are non-null while this instance is registered to receive
|
||||
// updates from them.
|
||||
std::unique_ptr<IssuesObserver> issues_observer_;
|
||||
std::unique_ptr<MediaRoutesObserver> routes_observer_;
|
||||
|
||||
// Set to true by |handler_| when the UI has been initialized.
|
||||
bool ui_initialized_;
|
||||
|
||||
// Set to -1 if not tracking a pending route request.
|
||||
int current_route_request_id_;
|
||||
|
||||
// Sequential counter for route requests. Used to update
|
||||
// |current_route_request_id_| when there is a new route request.
|
||||
int route_request_counter_;
|
||||
|
||||
// Used for locale-aware sorting of sinks by name. Set during |InitCommon()|
|
||||
// using the current locale. Set to null
|
||||
std::unique_ptr<icu::Collator> collator_;
|
||||
|
||||
std::vector<MediaSinkWithCastModes> sinks_;
|
||||
std::vector<MediaRoute> routes_;
|
||||
std::vector<MediaRoute::Id> joinable_route_ids_;
|
||||
CastModeSet cast_modes_;
|
||||
std::unordered_map<MediaRoute::Id, MediaCastMode> routes_and_cast_modes_;
|
||||
|
||||
std::unique_ptr<QueryResultManager> query_result_manager_;
|
||||
|
||||
// If set, then the result of the next presentation route request will
|
||||
// be handled by this object.
|
||||
std::unique_ptr<StartPresentationContext> start_presentation_context_;
|
||||
|
||||
// Set to the presentation request corresponding to the presentation cast
|
||||
// mode, if supported. Otherwise set to nullptr.
|
||||
base::Optional<content::PresentationRequest> presentation_request_;
|
||||
|
||||
// It's possible for PresentationServiceDelegateImpl to be destroyed before
|
||||
// this class.
|
||||
// (e.g. if a tab with the UI open is closed, then the tab WebContents will
|
||||
// be destroyed first momentarily before the UI WebContents).
|
||||
// Holding a WeakPtr to PresentationServiceDelegateImpl is the cleanest way to
|
||||
// handle this.
|
||||
// TODO(imcheng): hold a weak ptr to an abstract type instead.
|
||||
base::WeakPtr<PresentationServiceDelegateImpl> presentation_service_delegate_;
|
||||
|
||||
content::WebContents* initiator_;
|
||||
|
||||
// Pointer to the MediaRouter for this instance's BrowserContext.
|
||||
MediaRouter* router_;
|
||||
|
||||
// The start time for UI initialization metrics timer. When a dialog has been
|
||||
// been painted and initialized with initial data, this should be cleared.
|
||||
base::Time start_time_;
|
||||
@@ -474,13 +288,6 @@ class MediaRouterUI
|
||||
// If set, a cast mode that is required to be shown first.
|
||||
base::Optional<MediaCastMode> forced_cast_mode_;
|
||||
|
||||
#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
|
||||
// Keeps track of which display the dialog WebContents is on.
|
||||
std::unique_ptr<WebContentsDisplayObserver> display_observer_;
|
||||
#endif
|
||||
|
||||
// NOTE: Weak pointers must be invalidated before all other member variables.
|
||||
// Therefore |weak_factory_| must be placed at the end.
|
||||
base::WeakPtrFactory<MediaRouterUI> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MediaRouterUI);
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include "chrome/browser/media/router/test/mock_media_router.h"
|
||||
#include "chrome/browser/media/router/test/test_helper.h"
|
||||
#include "chrome/browser/sessions/session_tab_helper.h"
|
||||
#include "chrome/browser/ui/media_router/media_router_ui_helper.h"
|
||||
#include "chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h"
|
||||
#include "chrome/common/media_router/media_route.h"
|
||||
#include "chrome/common/media_router/media_source_helper.h"
|
||||
@@ -123,9 +124,9 @@ class TestMediaRouterUI : public MediaRouterUI {
|
||||
: MediaRouterUI(web_ui), router_(router) {}
|
||||
~TestMediaRouterUI() override = default;
|
||||
|
||||
MediaRouter* GetMediaRouter() override { return router_; }
|
||||
|
||||
private:
|
||||
MediaRouter* GetMediaRouter() const override { return router_; }
|
||||
|
||||
MediaRouter* router_;
|
||||
DISALLOW_COPY_AND_ASSIGN(TestMediaRouterUI);
|
||||
};
|
||||
@@ -233,6 +234,14 @@ class MediaRouterUITest : public ChromeRenderViewHostTestHarness {
|
||||
base::test::ScopedFeatureList scoped_feature_list_;
|
||||
};
|
||||
|
||||
class MediaRouterUIIncognitoTest : public MediaRouterUITest {
|
||||
protected:
|
||||
content::BrowserContext* GetBrowserContext() override {
|
||||
return static_cast<Profile*>(MediaRouterUITest::GetBrowserContext())
|
||||
->GetOffTheRecordProfile();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(MediaRouterUITest, RouteCreationTimeoutForTab) {
|
||||
CreateMediaRouterUI(profile());
|
||||
std::vector<MediaRouteResponseCallback> callbacks;
|
||||
@@ -336,8 +345,8 @@ TEST_F(MediaRouterUITest, RouteCreationParametersCantBeCreated) {
|
||||
std::move(sink_callback).Run("foundSinkId");
|
||||
}
|
||||
|
||||
TEST_F(MediaRouterUITest, RouteRequestFromIncognito) {
|
||||
CreateMediaRouterUI(profile()->GetOffTheRecordProfile());
|
||||
TEST_F(MediaRouterUIIncognitoTest, RouteRequestFromIncognito) {
|
||||
CreateMediaRouterUI(profile());
|
||||
media_router_ui_->OnDefaultPresentationChanged(presentation_request_);
|
||||
|
||||
EXPECT_CALL(mock_router_,
|
||||
@@ -370,7 +379,7 @@ TEST_F(MediaRouterUITest, SortedSinks) {
|
||||
|
||||
// Sorted order is 2, 3, 1.
|
||||
media_router_ui_->OnResultsUpdated(unsorted_sinks);
|
||||
const auto& sorted_sinks = media_router_ui_->sinks_;
|
||||
const auto& sorted_sinks = media_router_ui_->GetEnabledSinks();
|
||||
EXPECT_EQ(sink_name2, sorted_sinks[0].sink.name());
|
||||
EXPECT_EQ(sink_id3, sorted_sinks[1].sink.id());
|
||||
EXPECT_EQ(sink_id1, sorted_sinks[2].sink.id());
|
||||
@@ -399,7 +408,7 @@ TEST_F(MediaRouterUITest, SortSinksByIconType) {
|
||||
// Sorted order is CAST, CAST_AUDIO_GROUP "A", CAST_AUDIO_GROUP "B",
|
||||
// CAST_AUDIO, HANGOUT, GENERIC.
|
||||
media_router_ui_->OnResultsUpdated(unsorted_sinks);
|
||||
const auto& sorted_sinks = media_router_ui_->sinks_;
|
||||
const auto& sorted_sinks = media_router_ui_->GetEnabledSinks();
|
||||
EXPECT_EQ(sink6.sink.id(), sorted_sinks[0].sink.id());
|
||||
EXPECT_EQ(sink4.sink.id(), sorted_sinks[1].sink.id());
|
||||
EXPECT_EQ(sink2.sink.id(), sorted_sinks[2].sink.id());
|
||||
@@ -424,11 +433,11 @@ TEST_F(MediaRouterUITest, FilterNonDisplayRoutes) {
|
||||
routes.push_back(display_route_2);
|
||||
|
||||
media_router_ui_->OnRoutesUpdated(routes, std::vector<MediaRoute::Id>());
|
||||
ASSERT_EQ(2u, media_router_ui_->routes_.size());
|
||||
EXPECT_TRUE(display_route_1.Equals(media_router_ui_->routes_[0]));
|
||||
EXPECT_TRUE(media_router_ui_->routes_[0].for_display());
|
||||
EXPECT_TRUE(display_route_2.Equals(media_router_ui_->routes_[1]));
|
||||
EXPECT_TRUE(media_router_ui_->routes_[1].for_display());
|
||||
ASSERT_EQ(2u, media_router_ui_->routes().size());
|
||||
EXPECT_TRUE(display_route_1.Equals(media_router_ui_->routes()[0]));
|
||||
EXPECT_TRUE(media_router_ui_->routes()[0].for_display());
|
||||
EXPECT_TRUE(display_route_2.Equals(media_router_ui_->routes()[1]));
|
||||
EXPECT_TRUE(media_router_ui_->routes()[1].for_display());
|
||||
}
|
||||
|
||||
TEST_F(MediaRouterUITest, FilterNonDisplayJoinableRoutes) {
|
||||
@@ -452,11 +461,11 @@ TEST_F(MediaRouterUITest, FilterNonDisplayJoinableRoutes) {
|
||||
joinable_route_ids.push_back("routeId3");
|
||||
|
||||
media_router_ui_->OnRoutesUpdated(routes, joinable_route_ids);
|
||||
ASSERT_EQ(2u, media_router_ui_->joinable_route_ids_.size());
|
||||
ASSERT_EQ(2u, media_router_ui_->joinable_route_ids().size());
|
||||
EXPECT_EQ(display_route_1.media_route_id(),
|
||||
media_router_ui_->joinable_route_ids_[0]);
|
||||
media_router_ui_->joinable_route_ids()[0]);
|
||||
EXPECT_EQ(display_route_2.media_route_id(),
|
||||
media_router_ui_->joinable_route_ids_[1]);
|
||||
media_router_ui_->joinable_route_ids()[1]);
|
||||
}
|
||||
|
||||
TEST_F(MediaRouterUITest, UIMediaRoutesObserverAssignsCurrentCastModes) {
|
||||
@@ -556,39 +565,6 @@ TEST_F(MediaRouterUITest, UIMediaRoutesObserverSkipsUnavailableCastModes) {
|
||||
observer.reset();
|
||||
}
|
||||
|
||||
TEST_F(MediaRouterUITest, GetExtensionNameExtensionPresent) {
|
||||
std::string id = "extensionid";
|
||||
GURL url = GURL("chrome-extension://" + id);
|
||||
std::unique_ptr<extensions::ExtensionRegistry> registry =
|
||||
std::make_unique<extensions::ExtensionRegistry>(nullptr);
|
||||
scoped_refptr<extensions::Extension> app =
|
||||
extensions::ExtensionBuilder(
|
||||
"test app name", extensions::ExtensionBuilder::Type::PLATFORM_APP)
|
||||
.SetID(id)
|
||||
.Build();
|
||||
|
||||
ASSERT_TRUE(registry->AddEnabled(app));
|
||||
EXPECT_EQ("test app name",
|
||||
MediaRouterUI::GetExtensionName(url, registry.get()));
|
||||
}
|
||||
|
||||
TEST_F(MediaRouterUITest, GetExtensionNameEmptyWhenNotInstalled) {
|
||||
std::string id = "extensionid";
|
||||
GURL url = GURL("chrome-extension://" + id);
|
||||
std::unique_ptr<extensions::ExtensionRegistry> registry =
|
||||
std::make_unique<extensions::ExtensionRegistry>(nullptr);
|
||||
|
||||
EXPECT_EQ("", MediaRouterUI::GetExtensionName(url, registry.get()));
|
||||
}
|
||||
|
||||
TEST_F(MediaRouterUITest, GetExtensionNameEmptyWhenNotExtensionURL) {
|
||||
GURL url = GURL("https://www.google.com");
|
||||
std::unique_ptr<extensions::ExtensionRegistry> registry =
|
||||
std::make_unique<extensions::ExtensionRegistry>(nullptr);
|
||||
|
||||
EXPECT_EQ("", MediaRouterUI::GetExtensionName(url, registry.get()));
|
||||
}
|
||||
|
||||
TEST_F(MediaRouterUITest, NotFoundErrorOnCloseWithNoSinks) {
|
||||
blink::mojom::PresentationError expected_error(
|
||||
blink::mojom::PresentationErrorType::NO_AVAILABLE_SCREENS,
|
||||
|
@@ -94,6 +94,7 @@ class MockMediaRouterUI : public MediaRouterUI {
|
||||
MOCK_METHOD0(UIInitialized, void());
|
||||
MOCK_CONST_METHOD0(UserSelectedTabMirroringForCurrentOrigin, bool());
|
||||
MOCK_METHOD1(RecordCastModeSelection, void(MediaCastMode cast_mode));
|
||||
MOCK_CONST_METHOD0(GetPresentationRequestSourceName, std::string());
|
||||
MOCK_CONST_METHOD0(cast_modes, const std::set<MediaCastMode>&());
|
||||
MOCK_METHOD1(OnMediaControllerUIAvailable,
|
||||
void(const MediaRoute::Id& route_id));
|
||||
@@ -555,6 +556,8 @@ TEST_F(MediaRouterWebUIMessageHandlerTest, RetrieveCastModeSelection) {
|
||||
EXPECT_CALL(*mock_media_router_ui_, cast_modes())
|
||||
.WillRepeatedly(ReturnRef(cast_modes));
|
||||
|
||||
EXPECT_CALL(*mock_media_router_ui_, GetPresentationRequestSourceName())
|
||||
.WillRepeatedly(Return("source"));
|
||||
EXPECT_CALL(*mock_media_router_ui_,
|
||||
UserSelectedTabMirroringForCurrentOrigin())
|
||||
.WillOnce(Return(true));
|
||||
|
@@ -3137,6 +3137,7 @@ test("unit_tests") {
|
||||
"../browser/ui/media_router/cast_modes_with_media_sources_unittest.cc",
|
||||
"../browser/ui/media_router/media_cast_mode_unittest.cc",
|
||||
"../browser/ui/media_router/media_router_file_dialog_unittest.cc",
|
||||
"../browser/ui/media_router/media_router_ui_helper_unittest.cc",
|
||||
"../browser/ui/media_router/query_result_manager_unittest.cc",
|
||||
"../browser/ui/passwords/manage_passwords_ui_controller_unittest.cc",
|
||||
"../browser/ui/toolbar/media_router_action_controller_unittest.cc",
|
||||
|
Reference in New Issue
Block a user