diff options
Diffstat (limited to 'chromium/chrome/common/media_router')
18 files changed, 280 insertions, 98 deletions
diff --git a/chromium/chrome/common/media_router/discovery/media_sink_internal.cc b/chromium/chrome/common/media_router/discovery/media_sink_internal.cc index e39d0ebddbb..2c5c8ed5bc5 100644 --- a/chromium/chrome/common/media_router/discovery/media_sink_internal.cc +++ b/chromium/chrome/common/media_router/discovery/media_sink_internal.cc @@ -7,6 +7,7 @@ #include <new> #include "base/logging.h" +#include "base/notreached.h" #include "base/strings/string_util.h" namespace media_router { diff --git a/chromium/chrome/common/media_router/discovery/media_sink_service_base.cc b/chromium/chrome/common/media_router/discovery/media_sink_service_base.cc index a5a34a53330..e7eba6f2bb5 100644 --- a/chromium/chrome/common/media_router/discovery/media_sink_service_base.cc +++ b/chromium/chrome/common/media_router/discovery/media_sink_service_base.cc @@ -4,6 +4,7 @@ #include "chrome/common/media_router/discovery/media_sink_service_base.h" #include "base/bind.h" +#include "base/logging.h" #include "chrome/common/media_router/media_route.h" #include <vector> diff --git a/chromium/chrome/common/media_router/issue.h b/chromium/chrome/common/media_router/issue.h index 8b790b6cf72..840c3747645 100644 --- a/chromium/chrome/common/media_router/issue.h +++ b/chromium/chrome/common/media_router/issue.h @@ -8,7 +8,6 @@ #include <string> #include <vector> -#include "base/logging.h" #include "chrome/common/media_router/media_route.h" #include "chrome/common/media_router/media_sink.h" diff --git a/chromium/chrome/common/media_router/media_route.cc b/chromium/chrome/common/media_router/media_route.cc index eac0e63063e..82ba6ac02c2 100644 --- a/chromium/chrome/common/media_router/media_route.cc +++ b/chromium/chrome/common/media_router/media_route.cc @@ -33,9 +33,7 @@ MediaRoute::MediaRoute(const MediaRoute::Id& media_route_id, media_sink_id_(media_sink_id), description_(description), is_local_(is_local), - for_display_(for_display), - is_incognito_(false), - is_local_presentation_(false) {} + for_display_(for_display) {} MediaRoute::MediaRoute(const MediaRoute& other) = default; @@ -51,7 +49,7 @@ bool MediaRoute::operator==(const MediaRoute& other) const { description_ == other.description_ && is_local_ == other.is_local_ && controller_type_ == other.controller_type_ && for_display_ == other.for_display_ && - is_incognito_ == other.is_incognito_ && + is_off_the_record_ == other.is_off_the_record_ && is_local_presentation_ == other.is_local_presentation_; } diff --git a/chromium/chrome/common/media_router/media_route.h b/chromium/chrome/common/media_router/media_route.h index 1d8f946885f..16e19e7485c 100644 --- a/chromium/chrome/common/media_router/media_route.h +++ b/chromium/chrome/common/media_router/media_route.h @@ -8,7 +8,6 @@ #include <iosfwd> #include <string> -#include "base/logging.h" #include "base/values.h" #include "chrome/common/media_router/media_sink.h" #include "chrome/common/media_router/media_source.h" @@ -96,8 +95,10 @@ class MediaRoute { void set_for_display(bool for_display) { for_display_ = for_display; } bool for_display() const { return for_display_; } - void set_incognito(bool is_incognito) { is_incognito_ = is_incognito; } - bool is_incognito() const { return is_incognito_; } + void set_off_the_record(bool is_off_the_record) { + is_off_the_record_ = is_off_the_record; + } + bool is_off_the_record() const { return is_off_the_record_; } void set_local_presentation(bool is_local_presentation) { is_local_presentation_ = is_local_presentation; @@ -139,8 +140,8 @@ class MediaRoute { // |true| if the route can be displayed in the UI. bool for_display_ = false; - // |true| if the route was created by an incognito profile. - bool is_incognito_ = false; + // |true| if the route was created by an OffTheRecord profile. + bool is_off_the_record_ = false; // |true| if the presentation associated with this route is a local // presentation. diff --git a/chromium/chrome/common/media_router/media_route_unittest.cc b/chromium/chrome/common/media_router/media_route_unittest.cc index 01a94be6b1e..86a3ca8c758 100644 --- a/chromium/chrome/common/media_router/media_route_unittest.cc +++ b/chromium/chrome/common/media_router/media_route_unittest.cc @@ -47,10 +47,10 @@ TEST(MediaRouteTest, TestEquals) { false); EXPECT_FALSE(route1 == route5); - // Same as route1 with different incognito. + // Same as route1 with different off_the_record. MediaRoute route6(kRouteId1, media_source, "sinkId", "Description", true, false); - route6.set_incognito(true); + route6.set_off_the_record(true); EXPECT_FALSE(route1 == route6); } diff --git a/chromium/chrome/common/media_router/media_source.cc b/chromium/chrome/common/media_router/media_source.cc index 05a71cabcbd..1f7dd3f4d2c 100644 --- a/chromium/chrome/common/media_router/media_source.cc +++ b/chromium/chrome/common/media_router/media_source.cc @@ -21,13 +21,12 @@ namespace { // Prefixes used to format and detect various protocols' media source URNs. // See: https://www.ietf.org/rfc/rfc3406.txt +constexpr char kAnyTabMediaUrn[] = "urn:x-org.chromium.media:source:tab:*"; constexpr char kTabMediaUrnFormat[] = "urn:x-org.chromium.media:source:tab:%d"; constexpr base::StringPiece kDesktopMediaUrnPrefix = "urn:x-org.chromium.media:source:desktop:"; constexpr base::StringPiece kUnknownDesktopMediaUrn = "urn:x-org.chromium.media:source:desktop"; -constexpr char kTabRemotingUrnFormat[] = - "urn:x-org.chromium.media:source:tab_content_remoting:%d"; // List of non-http(s) schemes that are allowed in a Presentation URL. constexpr std::array<const char* const, 5> kAllowedSchemes{ @@ -70,17 +69,37 @@ MediaSource::MediaSource(const GURL& presentation_url) MediaSource::~MediaSource() = default; // static -MediaSource MediaSource::ForTab(int tab_id) { - return MediaSource(base::StringPrintf(kTabMediaUrnFormat, tab_id)); +MediaSource MediaSource::ForLocalFile() { + // TODO(crbug.com/1090878): Use something more sane here. Fixing this + // requires tracking down other places where tab ID 0 is used to indicate + // local file casting. + // + // This probably isn't a source of bugs in practice, because tab IDs are + // generated by SessionIdGenerator, which appears to only produce positive + // values, but that fact isn't clearly documentated, and other parts of + // Chromium don't seem to rely on it, using -1 as the canonical invalid tab + // ID. + return MediaSource(base::StringPrintf(kTabMediaUrnFormat, 0)); } // static -MediaSource MediaSource::ForTabContentRemoting(int tab_id) { - return MediaSource(base::StringPrintf(kTabRemotingUrnFormat, tab_id)); +MediaSource MediaSource::ForAnyTab() { + return MediaSource(std::string(kAnyTabMediaUrn)); +} + +// static +MediaSource MediaSource::ForTab(int tab_id) { + // Ideally we shouldn't allow -1 as a tab ID, but in unit tests, a tab ID of + // -1 can show up when this function is called from + // CastHandler::StartObservingForSinks() because SessionTabHelper::IdForTab + // can return -1. + DCHECK_GE(tab_id, -1); + return MediaSource(base::StringPrintf(kTabMediaUrnFormat, tab_id)); } // static MediaSource MediaSource::ForDesktop(const std::string& desktop_media_id) { + DCHECK(!desktop_media_id.empty()); return MediaSource(kDesktopMediaUrnPrefix.as_string() + desktop_media_id); } @@ -94,20 +113,19 @@ MediaSource MediaSource::ForPresentationUrl(const GURL& presentation_url) { return MediaSource(presentation_url); } +bool MediaSource::IsTabMirroringSource() const { + return id() == kAnyTabMediaUrn || TabId() > 0; +} + bool MediaSource::IsDesktopMirroringSource() const { return id() == kUnknownDesktopMediaUrn || base::StartsWith(id(), kDesktopMediaUrnPrefix, base::CompareCase::SENSITIVE); } -bool MediaSource::IsTabMirroringSource() const { - int tab_id; - return std::sscanf(id_.c_str(), kTabMediaUrnFormat, &tab_id) == 1 && - tab_id > 0; -} - -bool MediaSource::IsMirroringSource() const { - return IsDesktopMirroringSource() || IsTabMirroringSource(); +bool MediaSource::IsLocalFileSource() const { + // TODO(crbug.com/1090878): Keep this method is sync with ForLocalFile(). + return TabId() == 0; } bool MediaSource::IsCastPresentationUrl() const { @@ -116,13 +134,9 @@ bool MediaSource::IsCastPresentationUrl() const { } int MediaSource::TabId() const { - int tab_id; - if (sscanf(id_.c_str(), kTabMediaUrnFormat, &tab_id) == 1) - return tab_id; - else if (sscanf(id_.c_str(), kTabRemotingUrnFormat, &tab_id) == 1) - return tab_id; - else - return -1; + int tab_id = -1; + sscanf(id_.c_str(), kTabMediaUrnFormat, &tab_id); + return tab_id; } base::Optional<std::string> MediaSource::DesktopStreamId() const { @@ -133,11 +147,6 @@ base::Optional<std::string> MediaSource::DesktopStreamId() const { return base::nullopt; } -bool MediaSource::IsValid() const { - return TabId() > 0 || IsDesktopMirroringSource() || - IsValidPresentationUrl(GURL(id_)); -} - bool MediaSource::IsDialSource() const { return url_.SchemeIs(kCastDialPresentationUrlScheme); } @@ -146,4 +155,16 @@ std::string MediaSource::AppNameFromDialSource() const { return IsDialSource() ? url_.path() : ""; } +std::string MediaSource::TruncateForLogging(size_t max_length) const { + const std::string origin = url_.GetOrigin().spec(); + if (!origin.empty()) + return origin.substr(0, max_length); + // TODO(takumif): Keep the query string by redacting PII. The query string may + // contain info useful for debugging such as the required capabilities. + const size_t query_start_index = id_.find("?"); + const size_t length = + query_start_index == std::string::npos ? max_length : query_start_index; + return id_.substr(0, length); +} + } // namespace media_router diff --git a/chromium/chrome/common/media_router/media_source.h b/chromium/chrome/common/media_router/media_source.h index edf2111d694..6fdbea9350d 100644 --- a/chromium/chrome/common/media_router/media_source.h +++ b/chromium/chrome/common/media_router/media_source.h @@ -90,8 +90,9 @@ class MediaSource { // Protocol-specific media source object creation. // Returns MediaSource URI depending on the type of source. + static MediaSource ForLocalFile(); + static MediaSource ForAnyTab(); static MediaSource ForTab(int tab_id); - static MediaSource ForTabContentRemoting(int tab_id); static MediaSource ForPresentationUrl(const GURL& presentation_url); // Creates a media source for a specific desktop. @@ -108,16 +109,23 @@ class MediaSource { // extension-based Cast MRP is removed. static MediaSource ForDesktop(); - // Returns true if source outputs its content via mirroring. - bool IsDesktopMirroringSource() const; + // Returns true if source outputs its content via tab mirroring and isn't a + // local file. bool IsTabMirroringSource() const; - bool IsMirroringSource() const; + + // Returns true if source outputs its content via desktop mirroring. + bool IsDesktopMirroringSource() const; + + // Returns true if the source is a local file. + bool IsLocalFileSource() const; // Returns true if this is represents a Cast Presentation URL. bool IsCastPresentationUrl() const; // Parses the ID and returns the SessionTabHelper tab ID referencing a source - // tab. Returns a non-positive value on error. + // tab. Don't rely on this method returning something useful without first + // calling IsTabMirroringSource(); it will return 0 for for ForLocalFile() + // source and -1 for non-tab sources or the ForAnyTab() source. int TabId() const; // When this source was created by ForDesktop(string), returns a stream ID @@ -126,10 +134,6 @@ class MediaSource { // returns base::nullopt. base::Optional<std::string> DesktopStreamId() const; - // Checks that this is a parseable URN and is of a known type. - // Does not deeper protocol-level syntax checks. - bool IsValid() const; - // Returns true this source outputs its content via DIAL. // TODO(crbug.com/804419): Move this to in-browser DIAL/Cast MRP when we have // one. @@ -139,6 +143,9 @@ class MediaSource { // valid DIAL media source. std::string AppNameFromDialSource() const; + // Returns a shortened copy of the media source ID suitable for logging. + std::string TruncateForLogging(size_t max_length) const; + private: MediaSource::Id id_; GURL url_; diff --git a/chromium/chrome/common/media_router/media_source_unittest.cc b/chromium/chrome/common/media_router/media_source_unittest.cc index 11825581c17..c4609881dab 100644 --- a/chromium/chrome/common/media_router/media_source_unittest.cc +++ b/chromium/chrome/common/media_router/media_source_unittest.cc @@ -54,25 +54,34 @@ TEST(MediaSourceTest, ConstructorWithURLString) { EXPECT_EQ(test_url, source1.url()); } +TEST(MediaSourceTest, ForAnyTab) { + auto source = MediaSource::ForAnyTab(); + EXPECT_EQ("urn:x-org.chromium.media:source:tab:*", source.id()); + EXPECT_EQ(-1, source.TabId()); + EXPECT_FALSE(source.IsDesktopMirroringSource()); + EXPECT_TRUE(source.IsTabMirroringSource()); + EXPECT_FALSE(source.IsLocalFileSource()); + EXPECT_FALSE(source.IsCastPresentationUrl()); + EXPECT_FALSE(source.IsDialSource()); +} + TEST(MediaSourceTest, ForTab) { auto source = MediaSource::ForTab(123); EXPECT_EQ("urn:x-org.chromium.media:source:tab:123", source.id()); EXPECT_EQ(123, source.TabId()); - EXPECT_TRUE(source.IsValid()); EXPECT_FALSE(source.IsDesktopMirroringSource()); EXPECT_TRUE(source.IsTabMirroringSource()); - EXPECT_TRUE(source.IsMirroringSource()); + EXPECT_FALSE(source.IsLocalFileSource()); EXPECT_FALSE(source.IsCastPresentationUrl()); EXPECT_FALSE(source.IsDialSource()); } -TEST(MediaSourceTest, ForTabContentRemoting) { - auto source = MediaSource::ForTabContentRemoting(123); - EXPECT_EQ(123, source.TabId()); - EXPECT_TRUE(source.IsValid()); +TEST(MediaSourceTest, ForLocalFile) { + auto source = MediaSource::ForLocalFile(); + EXPECT_EQ("urn:x-org.chromium.media:source:tab:0", source.id()); EXPECT_FALSE(source.IsDesktopMirroringSource()); EXPECT_FALSE(source.IsTabMirroringSource()); - EXPECT_FALSE(source.IsMirroringSource()); + EXPECT_TRUE(source.IsLocalFileSource()); EXPECT_FALSE(source.IsCastPresentationUrl()); EXPECT_FALSE(source.IsDialSource()); } @@ -81,10 +90,9 @@ TEST(MediaSourceTest, ForDesktop) { std::string media_id = "fakeMediaId"; auto source = MediaSource::ForDesktop(media_id); EXPECT_EQ("urn:x-org.chromium.media:source:desktop:" + media_id, source.id()); - EXPECT_TRUE(source.IsValid()); EXPECT_TRUE(source.IsDesktopMirroringSource()); EXPECT_FALSE(source.IsTabMirroringSource()); - EXPECT_TRUE(source.IsMirroringSource()); + EXPECT_FALSE(source.IsLocalFileSource()); EXPECT_FALSE(source.IsCastPresentationUrl()); EXPECT_FALSE(source.IsDialSource()); } @@ -94,23 +102,13 @@ TEST(MediaSourceTest, ForPresentationUrl) { "https://www.example.com/presentation.html"; auto source = MediaSource::ForPresentationUrl(GURL(kPresentationUrl)); EXPECT_EQ(kPresentationUrl, source.id()); - EXPECT_TRUE(source.IsValid()); EXPECT_FALSE(source.IsDesktopMirroringSource()); EXPECT_FALSE(source.IsTabMirroringSource()); - EXPECT_FALSE(source.IsMirroringSource()); + EXPECT_FALSE(source.IsLocalFileSource()); EXPECT_FALSE(source.IsCastPresentationUrl()); EXPECT_FALSE(source.IsDialSource()); } -TEST(MediaSourceTest, IsValid) { - // Disallowed scheme - EXPECT_FALSE(MediaSource::ForPresentationUrl(GURL("file:///some/local/path")) - .IsValid()); - // Not a URL - EXPECT_FALSE( - MediaSource::ForPresentationUrl(GURL("totally not a url")).IsValid()); -} - TEST(MediaSourceTest, IsCastPresentationUrl) { EXPECT_TRUE(MediaSource(GURL("cast:233637DE")).IsCastPresentationUrl()); EXPECT_TRUE( diff --git a/chromium/chrome/common/media_router/mojom/BUILD.gn b/chromium/chrome/common/media_router/mojom/BUILD.gn index 7463f0776a5..b6cb091ba85 100644 --- a/chromium/chrome/common/media_router/mojom/BUILD.gn +++ b/chromium/chrome/common/media_router/mojom/BUILD.gn @@ -4,6 +4,11 @@ import("//mojo/public/tools/bindings/mojom.gni") +mojom("logger") { + sources = [ "logger.mojom" ] + public_deps = [ "//mojo/public/mojom/base" ] +} + mojom("media_controller") { sources = [ "media_controller.mojom", @@ -21,6 +26,7 @@ mojom("media_router") { sources = [ "media_router.mojom" ] public_deps = [ + ":logger", ":media_controller", "//components/mirroring/mojom:host", "//media/mojo/mojom:mirror_service_remoting", diff --git a/chromium/chrome/common/media_router/mojom/logger.mojom b/chromium/chrome/common/media_router/mojom/logger.mojom new file mode 100644 index 00000000000..340062e46ca --- /dev/null +++ b/chromium/chrome/common/media_router/mojom/logger.mojom @@ -0,0 +1,53 @@ +// Copyright 2020 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. + +module media_router.mojom; + +import "mojo/public/mojom/base/time.mojom"; + +enum LogCategory { + kDiscovery, + kRoute, + kMirroring, + kUi, +}; + +// Collects logs from Media Router components such as Media Route Providers. +// The implementation lives in the browser process. +interface Logger { + // Records a log entry of "info" severity. The severities are "info", + // "warning", and "error" in increasing order. + // |component|: This is usually the name of the class that is emitting the + // log, such as "CastSessionTracker". + // |message|: The log message. + // |sink_id|: ID of the media sink associated with this log. May be empty if + // the log is not associated with a sink. + // |media_source|: ID of the media source, which may be of the format + // "cast:ABCDEFGH?arg1=val1" (app ID and query params) for Cast sessions, + // or the receiver page URL in the case of presentations. May be empty. + // |session_id|: ID of the Cast or presentation session associated with this + // log. May be empty. + LogInfo(LogCategory category, + string component, + string message, + string sink_id, + string media_source, + string session_id); + + // Records a log entry of "warning" severity. + LogWarning(LogCategory category, + string component, + string message, + string sink_id, + string media_source, + string session_id); + + // Records a log entry of "error" severity. + LogError(LogCategory category, + string component, + string message, + string sink_id, + string media_source, + string session_id); +}; diff --git a/chromium/chrome/common/media_router/mojom/media_router.mojom b/chromium/chrome/common/media_router/mojom/media_router.mojom index 95e445dfa20..0219ea1b462 100644 --- a/chromium/chrome/common/media_router/mojom/media_router.mojom +++ b/chromium/chrome/common/media_router/mojom/media_router.mojom @@ -4,6 +4,7 @@ module media_router.mojom; +import "chrome/common/media_router/mojom/logger.mojom"; import "chrome/common/media_router/mojom/media_controller.mojom"; import "chrome/common/media_router/mojom/media_status.mojom"; import "components/mirroring/mojom/mirroring_service_host.mojom"; @@ -122,8 +123,8 @@ struct MediaRoute { RouteControllerType controller_type; // Set to true if this route should be displayed for |media_sink_id| in UI. bool for_display; - // Set to true if this route was created by an incognito profile. - bool is_incognito; + // Set to true if this route was created by an OffTheRecord profile. + bool is_off_the_record; // Set to true if this route corresponds to a local presentation. bool is_local_presentation; }; @@ -208,7 +209,7 @@ enum RouteRequestResultCode { ROUTE_NOT_FOUND, SINK_NOT_FOUND, INVALID_ORIGIN, - INCOGNITO_MISMATCH, + OFF_THE_RECORD_MISMATCH, NO_SUPPORTED_PROVIDER, CANCELLED, ROUTE_ALREADY_EXISTS, @@ -307,7 +308,8 @@ interface MediaRouteProvider { // If |timeout| is positive, it will be used in place of the default timeout // defined by Media Route Provider Manager. // - // If |incognito| is true, the request was made by an incognito profile. + // If |off_the_record| is true, the request was made by an OffTheRecord + // profile. // // If the operation was successful, |route| will be defined and |error_text| // will be null. If the operation failed, |route| will be null and @@ -323,7 +325,7 @@ interface MediaRouteProvider { url.mojom.Origin origin, int32 tab_id, mojo_base.mojom.TimeDelta timeout, - bool incognito) => + bool off_the_record) => (MediaRoute? route, RoutePresentationConnection? connection, string? error_text, @@ -338,8 +340,8 @@ interface MediaRouteProvider { // If |timeout| is positive, it will be used in place of the default timeout // defined by Media Route Provider Manager. // - // If the route request was created by an incognito profile, - // |incognito| must be true. + // If the route request was created by an OffTheRecord profile, + // |off_the_record| must be true. // // If the operation was successful, |route| will be defined and |error_text| // will be null. If the operation failed, |route| will be null and @@ -353,7 +355,7 @@ interface MediaRouteProvider { url.mojom.Origin origin, int32 tab_id, mojo_base.mojom.TimeDelta timeout, - bool incognito) => + bool off_the_record) => (MediaRoute? route, RoutePresentationConnection? connection, string? error_text, @@ -373,8 +375,8 @@ interface MediaRouteProvider { // defined by Media Route Provider Manager; see CreateRoute for additional // documentation. // - // If the route request was created by an incognito profile, - // |incognito| must be true. + // If the route request was created by an OffTheRecord profile, + // |off_the_record| must be true. // // If the operation was successful, |route| will be defined and // |error_text| will be null. If the operation failed, |route| will be null @@ -391,7 +393,7 @@ interface MediaRouteProvider { url.mojom.Origin origin, int32 tab_id, mojo_base.mojom.TimeDelta timeout, - bool incognito) => + bool off_the_record) => (MediaRoute? route, RoutePresentationConnection? connection, string? error_text, @@ -597,6 +599,15 @@ interface MediaRouter { // Returns current status of media sink service in JSON format. GetMediaSinkServiceStatus() => (string status); + // The logger can be used to add entries to logs returned by GetLogs(). + GetLogger(pending_receiver<Logger> receiver); + + // Returns a JSON array of logs collected by Media Router components. + // Serializing the logs requires allocating extra memory, so it should only be + // called under limited circumstances, such as when the user is submitting a + // feedback report. + GetLogsAsString() => (string logs); + // Called to get a mirroring.mojom.MirroringServiceHost. These APIs are used // by Media Router extension to start mirroring through the mirroring service. // TODO(http://crbug.com/809249): Remove these APIs when native Cast MRP with diff --git a/chromium/chrome/common/media_router/mojom/media_router_mojom_traits.cc b/chromium/chrome/common/media_router/mojom/media_router_mojom_traits.cc index ecb173bd65c..a48bece3a32 100644 --- a/chromium/chrome/common/media_router/mojom/media_router_mojom_traits.cc +++ b/chromium/chrome/common/media_router/mojom/media_router_mojom_traits.cc @@ -216,7 +216,7 @@ bool StructTraits<media_router::mojom::MediaRouteDataView, out->set_local(data.is_local()); out->set_for_display(data.for_display()); - out->set_incognito(data.is_incognito()); + out->set_off_the_record(data.is_off_the_record()); out->set_local_presentation(data.is_local_presentation()); return true; diff --git a/chromium/chrome/common/media_router/mojom/media_router_mojom_traits.h b/chromium/chrome/common/media_router/mojom/media_router_mojom_traits.h index cc7c383a797..76ab7593602 100644 --- a/chromium/chrome/common/media_router/mojom/media_router_mojom_traits.h +++ b/chromium/chrome/common/media_router/mojom/media_router_mojom_traits.h @@ -397,8 +397,8 @@ struct StructTraits<media_router::mojom::MediaRouteDataView, return route.for_display(); } - static bool is_incognito(const media_router::MediaRoute& route) { - return route.is_incognito(); + static bool is_off_the_record(const media_router::MediaRoute& route) { + return route.is_off_the_record(); } static bool is_local_presentation(const media_router::MediaRoute& route) { @@ -426,8 +426,9 @@ struct EnumTraits<media_router::mojom::RouteRequestResultCode, return media_router::mojom::RouteRequestResultCode::SINK_NOT_FOUND; case media_router::RouteRequestResult::INVALID_ORIGIN: return media_router::mojom::RouteRequestResultCode::INVALID_ORIGIN; - case media_router::RouteRequestResult::INCOGNITO_MISMATCH: - return media_router::mojom::RouteRequestResultCode::INCOGNITO_MISMATCH; + case media_router::RouteRequestResult::OFF_THE_RECORD_MISMATCH: + return media_router::mojom::RouteRequestResultCode:: + OFF_THE_RECORD_MISMATCH; case media_router::RouteRequestResult::NO_SUPPORTED_PROVIDER: return media_router::mojom::RouteRequestResultCode:: NO_SUPPORTED_PROVIDER; @@ -467,8 +468,8 @@ struct EnumTraits<media_router::mojom::RouteRequestResultCode, case media_router::mojom::RouteRequestResultCode::INVALID_ORIGIN: *output = media_router::RouteRequestResult::INVALID_ORIGIN; return true; - case media_router::mojom::RouteRequestResultCode::INCOGNITO_MISMATCH: - *output = media_router::RouteRequestResult::INCOGNITO_MISMATCH; + case media_router::mojom::RouteRequestResultCode::OFF_THE_RECORD_MISMATCH: + *output = media_router::RouteRequestResult::OFF_THE_RECORD_MISMATCH; return true; case media_router::mojom::RouteRequestResultCode::NO_SUPPORTED_PROVIDER: *output = media_router::RouteRequestResult::NO_SUPPORTED_PROVIDER; diff --git a/chromium/chrome/common/media_router/providers/cast/cast_media_source.cc b/chromium/chrome/common/media_router/providers/cast/cast_media_source.cc index 97002d0fd2a..c7fe6bf2e93 100644 --- a/chromium/chrome/common/media_router/providers/cast/cast_media_source.cc +++ b/chromium/chrome/common/media_router/providers/cast/cast_media_source.cc @@ -61,6 +61,7 @@ const EnumTable<CastDeviceCapability> EnumTable<CastDeviceCapability>::instance( template <> const EnumTable<ReceiverAppType> EnumTable<ReceiverAppType>::instance( { + {ReceiverAppType::kOther, "OTHER"}, {ReceiverAppType::kWeb, "WEB"}, {ReceiverAppType::kAndroidTv, "ANDROID_TV"}, }, @@ -70,6 +71,9 @@ const EnumTable<ReceiverAppType> EnumTable<ReceiverAppType>::instance( namespace media_router { +// The maximum length of presentation URL is 64KB. +constexpr int kMaxCastPresentationUrlLength = 64 * 1024; + namespace { // A nonmember version of base::Optional::value_or that works on pointers as @@ -188,8 +192,8 @@ std::unique_ptr<CastMediaSource> CastMediaSourceForTabMirroring( const MediaSource::Id& source_id) { return std::make_unique<CastMediaSource>( source_id, - std::vector<CastAppInfo>({CastAppInfo(kCastStreamingAppId), - CastAppInfo(kCastStreamingAudioAppId)})); + std::vector<CastAppInfo>({CastAppInfo::ForCastStreaming(), + CastAppInfo::ForCastStreamingAudio()})); } std::unique_ptr<CastMediaSource> CastMediaSourceForDesktopMirroring( @@ -197,7 +201,7 @@ std::unique_ptr<CastMediaSource> CastMediaSourceForDesktopMirroring( // TODO(https://crbug.com/849335): Add back audio-only devices for desktop // mirroring when proper support is implemented. return std::make_unique<CastMediaSource>( - source_id, std::vector<CastAppInfo>({CastAppInfo(kCastStreamingAppId)})); + source_id, std::vector<CastAppInfo>({CastAppInfo::ForCastStreaming()})); } // The logic shared by ParseCastUrl() and ParseLegacyCastUrl(). @@ -210,6 +214,8 @@ std::unique_ptr<CastMediaSource> CreateFromURLParams( const std::string& broadcast_namespace, const std::string& encoded_broadcast_message, const std::string& launch_timeout_str, + const std::string& target_playout_delay_millis_str, + const std::string& audio_capture_str, const std::vector<ReceiverAppType>& supported_app_types, const std::string& app_params) { if (app_infos.empty()) @@ -227,13 +233,24 @@ std::unique_ptr<CastMediaSource> CreateFromURLParams( broadcast_namespace, DecodeURLComponent(encoded_broadcast_message))); } - int launch_timeout_millis; + int launch_timeout_millis = 0; if (base::StringToInt(launch_timeout_str, &launch_timeout_millis) && launch_timeout_millis > 0) { cast_source->set_launch_timeout( base::TimeDelta::FromMilliseconds(launch_timeout_millis)); } + int target_playout_delay_millis = 0; + if (base::StringToInt(target_playout_delay_millis_str, + &target_playout_delay_millis) && + target_playout_delay_millis > 0) { + cast_source->set_target_playout_delay( + base::TimeDelta::FromMilliseconds(target_playout_delay_millis)); + } + + if (audio_capture_str == "0") + cast_source->set_allow_audio_capture(false); + if (!supported_app_types.empty()) cast_source->set_supported_app_types(supported_app_types); cast_source->set_app_params(app_params); @@ -259,6 +276,8 @@ std::unique_ptr<CastMediaSource> ParseCastUrl(const MediaSource::Id& source_id, FindValueOr(params, "broadcastNamespace", ""), FindValueOr(params, "broadcastMessage", ""), FindValueOr(params, "launchTimeout", ""), + FindValueOr(params, "streamingTargetPlayoutDelayMillis", ""), + FindValueOr(params, "streamingCaptureAudio", ""), SupportedAppTypesFromString(FindValueOr(params, "supportedAppTypes", "")), FindValueOr(params, "appParams", "")); } @@ -318,6 +337,8 @@ std::unique_ptr<CastMediaSource> ParseLegacyCastUrl( FindValueOr(params, "__castBroadcastNamespace__", ""), FindValueOr(params, "__castBroadcastMessage__", ""), FindValueOr(params, "__castLaunchTimeout__", ""), + /* target_playout_delay_millis_str */ "", + /* audio_capture */ "", /* supported_app_types */ {}, /* appParams */ ""); } @@ -339,27 +360,45 @@ bool IsAutoJoinAllowed(AutoJoinPolicy policy, } } +bool IsSiteInitiatedMirroringSource(const MediaSource::Id& source_id) { + return base::StartsWith(source_id, kMirroringAppUri, + base::CompareCase::SENSITIVE); +} + CastAppInfo::CastAppInfo( const std::string& app_id, BitwiseOr<cast_channel::CastDeviceCapability> required_capabilities) : app_id(app_id), required_capabilities(required_capabilities) {} + CastAppInfo::~CastAppInfo() = default; CastAppInfo::CastAppInfo(const CastAppInfo& other) = default; // static +CastAppInfo CastAppInfo::ForCastStreaming() { + return CastAppInfo(kCastStreamingAppId, {CastDeviceCapability::VIDEO_OUT, + CastDeviceCapability::AUDIO_OUT}); +} + +// static +CastAppInfo CastAppInfo::ForCastStreamingAudio() { + return CastAppInfo(kCastStreamingAudioAppId, + {CastDeviceCapability::AUDIO_OUT}); +} + +// static std::unique_ptr<CastMediaSource> CastMediaSource::FromMediaSource( const MediaSource& source) { - if (source.IsTabMirroringSource()) + if (source.IsTabMirroringSource() || source.IsLocalFileSource()) return CastMediaSourceForTabMirroring(source.id()); if (source.IsDesktopMirroringSource()) return CastMediaSourceForDesktopMirroring(source.id()); const GURL& url = source.url(); - if (!url.is_valid()) - return nullptr; + if (!url.is_valid() || url.spec().length() > kMaxCastPresentationUrlLength) + return nullptr; if (url.SchemeIs(kCastPresentationUrlScheme)) { return ParseCastUrl(source.id(), url); } else if (IsLegacyCastPresentationUrl(url)) { diff --git a/chromium/chrome/common/media_router/providers/cast/cast_media_source.h b/chromium/chrome/common/media_router/providers/cast/cast_media_source.h index 09c04496b2f..ce8f7b9ef98 100644 --- a/chromium/chrome/common/media_router/providers/cast/cast_media_source.h +++ b/chromium/chrome/common/media_router/providers/cast/cast_media_source.h @@ -11,7 +11,7 @@ #include <type_traits> #include <vector> -#include "base/logging.h" +#include "base/check.h" #include "base/optional.h" #include "chrome/common/media_router/media_source.h" #include "components/cast_channel/cast_message_util.h" @@ -28,6 +28,13 @@ static constexpr char kCastStreamingAudioAppId[] = "85CDB22F"; // message. static constexpr char kMultizoneLeaderAppId[] = "MultizoneLeader"; +static const constexpr char* const kMultizoneMemberAppIds[] = { + kMultizoneLeaderAppId, + "531A4F84", // MultizoneLeader + "MultizoneFollower", + "705D30C6" // MultizoneFollower +}; + static constexpr base::TimeDelta kDefaultLaunchTimeout = base::TimeDelta::FromSeconds(60); @@ -44,12 +51,20 @@ class BitwiseOr { for (E e : values) Add(e); } + static constexpr BitwiseOr FromBits(T bits) { return BitwiseOr(bits); } bool empty() const { return bits_ == 0; } + T bits() const { return bits_; } void Add(E value) { bits_ |= Mask(value); } + bool Has(E value) const { return (bits_ & Mask(value)) != 0; } + bool HasAll(const BitwiseOr& other) const { + return (bits_ & other.bits_) == other.bits_; + } bool operator==(const BitwiseOr& other) const { return bits_ == other.bits_; } bool operator!=(const BitwiseOr& other) const { return *this != other; } private: + explicit constexpr BitwiseOr(T bits) : bits_(bits) {} + static T Mask(E value) { const T result = static_cast<T>(value); DCHECK(static_cast<E>(result) == value); @@ -60,12 +75,16 @@ class BitwiseOr { // Represents a Cast app and its capabilitity requirements. struct CastAppInfo { - explicit CastAppInfo(const std::string& app_id, - BitwiseOr<cast_channel::CastDeviceCapability> = {}); + explicit CastAppInfo( + const std::string& app_id, + BitwiseOr<cast_channel::CastDeviceCapability> required_capabilities); ~CastAppInfo(); CastAppInfo(const CastAppInfo& other); + static CastAppInfo ForCastStreaming(); + static CastAppInfo ForCastStreamingAudio(); + std::string app_id; // A bitset of capabilities required by the app. @@ -114,6 +133,9 @@ bool IsAutoJoinAllowed(AutoJoinPolicy policy, const url::Origin& origin2, int tab_id2); +// Returns true if |source_id| is a valid origin for site-initiated mirroring. +bool IsSiteInitiatedMirroringSource(const MediaSource::Id& source_id); + // Represents a MediaSource parsed into structured, Cast specific data. The // following MediaSources can be parsed into CastMediaSource: // - Cast Presentation URLs @@ -165,6 +187,17 @@ class CastMediaSource { DefaultActionPolicy default_action_policy() const { return default_action_policy_; } + base::Optional<base::TimeDelta> target_playout_delay() const { + return target_playout_delay_; + } + void set_target_playout_delay( + const base::Optional<base::TimeDelta>& target_playout_delay) { + target_playout_delay_ = target_playout_delay; + } + bool allow_audio_capture() const { return allow_audio_capture_; } + void set_allow_audio_capture(bool allow_audio_capture) { + allow_audio_capture_ = allow_audio_capture; + } const std::string& app_params() const { return app_params_; } void set_app_params(const std::string& app_params) { app_params_ = app_params; @@ -180,9 +213,11 @@ class CastMediaSource { AutoJoinPolicy auto_join_policy_; DefaultActionPolicy default_action_policy_; base::TimeDelta launch_timeout_ = kDefaultLaunchTimeout; - // Empty if not set. + // Optional parameters. std::string client_id_; base::Optional<cast_channel::BroadcastRequest> broadcast_request_; + base::Optional<base::TimeDelta> target_playout_delay_; + bool allow_audio_capture_ = true; std::vector<ReceiverAppType> supported_app_types_ = {ReceiverAppType::kWeb}; std::string app_params_; }; diff --git a/chromium/chrome/common/media_router/providers/cast/cast_media_source_unittest.cc b/chromium/chrome/common/media_router/providers/cast/cast_media_source_unittest.cc index 42c5c660f7a..03ebc375c99 100644 --- a/chromium/chrome/common/media_router/providers/cast/cast_media_source_unittest.cc +++ b/chromium/chrome/common/media_router/providers/cast/cast_media_source_unittest.cc @@ -30,6 +30,8 @@ TEST(CastMediaSourceTest, FromCastURLWithDefaults) { EXPECT_EQ(DefaultActionPolicy::kCreateSession, source->default_action_policy()); EXPECT_EQ(ReceiverAppType::kWeb, source->supported_app_types()[0]); + EXPECT_EQ(base::nullopt, source->target_playout_delay()); + EXPECT_EQ(true, source->allow_audio_capture()); } TEST(CastMediaSourceTest, FromCastURL) { @@ -42,7 +44,9 @@ TEST(CastMediaSourceTest, FromCastURL) { "&autoJoinPolicy=tab_and_origin_scoped" "&defaultActionPolicy=cast_this_tab" "&appParams=appParams" - "&supportedAppTypes=ANDROID_TV,WEB"); + "&supportedAppTypes=ANDROID_TV,WEB" + "&streamingTargetPlayoutDelayMillis=42" + "&streamingCaptureAudio=0"); std::unique_ptr<CastMediaSource> source = CastMediaSource::FromMediaSourceId(source_id); ASSERT_TRUE(source); @@ -64,6 +68,9 @@ TEST(CastMediaSourceTest, FromCastURL) { EXPECT_EQ(ReceiverAppType::kAndroidTv, source->supported_app_types()[0]); EXPECT_EQ(ReceiverAppType::kWeb, source->supported_app_types()[1]); EXPECT_EQ("appParams", source->app_params()); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(42), + source->target_playout_delay()); + EXPECT_EQ(false, source->allow_audio_capture()); } TEST(CastMediaSourceTest, FromLegacyCastURL) { @@ -159,6 +166,10 @@ TEST(CastMediaSourceTest, FromInvalidSource) { EXPECT_FALSE(CastMediaSource::FromMediaSourceId("cast:?param=foo")); EXPECT_FALSE(CastMediaSource::FromMediaSourceId( "https://google.com/cast#__castAppId__=/param=foo")); + // URL spec exceeds maximum size limit 64KB. + int length = 64 * 1024 + 1; + std::string invalidURL(length, 'a'); + EXPECT_FALSE(CastMediaSource::FromMediaSourceId("cast:appid?" + invalidURL)); } } // namespace media_router diff --git a/chromium/chrome/common/media_router/route_request_result.h b/chromium/chrome/common/media_router/route_request_result.h index 855dfa8e1f0..65761d726a2 100644 --- a/chromium/chrome/common/media_router/route_request_result.h +++ b/chromium/chrome/common/media_router/route_request_result.h @@ -42,7 +42,7 @@ class RouteRequestResult { ROUTE_NOT_FOUND = 3, SINK_NOT_FOUND = 4, INVALID_ORIGIN = 5, - INCOGNITO_MISMATCH = 6, + OFF_THE_RECORD_MISMATCH = 6, NO_SUPPORTED_PROVIDER = 7, CANCELLED = 8, ROUTE_ALREADY_EXISTS = 9, |