blob: 4d9c56af23d8419c094eed0c5bdcded496a6d252 [file] [log] [blame]
kmarshalld2f3bea2015-03-11 23:42:221// Copyright 2015 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Evan Stade92b69ca2020-08-21 23:55:585#include "components/media_router/common/media_source.h"
kmarshalld2f3bea2015-03-11 23:42:226
John Williamsd48d028c2019-05-23 23:59:127#include <algorithm>
8#include <array>
9#include <cstdio>
John Williamse955a24812019-01-19 01:35:0010#include <ostream>
kmarshalld2f3bea2015-03-11 23:42:2211#include <string>
12
Peter Kasting01167522021-04-28 19:32:0813#include "base/strings/string_piece.h"
John Williamsd48d028c2019-05-23 23:59:1214#include "base/strings/string_util.h"
15#include "base/strings/stringprintf.h"
Evan Stade92b69ca2020-08-21 23:55:5816#include "components/media_router/common/media_source.h"
John Williamsd48d028c2019-05-23 23:59:1217#include "url/gurl.h"
zhaobine1ed84732016-12-20 23:16:3418
kmarshalld2f3bea2015-03-11 23:42:2219namespace media_router {
20
John Williamsd48d028c2019-05-23 23:59:1221namespace {
22
23// Prefixes used to format and detect various protocols' media source URNs.
24// See: https://www.ietf.org/rfc/rfc3406.txt
Jordan Bayles89346462020-05-26 21:36:1225constexpr char kAnyTabMediaUrn[] = "urn:x-org.chromium.media:source:tab:*";
John Williamsd48d028c2019-05-23 23:59:1226constexpr char kTabMediaUrnFormat[] = "urn:x-org.chromium.media:source:tab:%d";
John Williams4c353442019-11-13 23:37:0027constexpr base::StringPiece kDesktopMediaUrnPrefix =
28 "urn:x-org.chromium.media:source:desktop:";
Yuri Wiitala85245382020-08-25 03:09:3929// WARNING: If more desktop URN parameters are added in the future, the parsing
30// code will have to be smarter!
31constexpr base::StringPiece kDesktopMediaUrnAudioParam = "?with_audio=true";
32constexpr base::StringPiece kUnchosenDesktopMediaUrn =
John Williams4c353442019-11-13 23:37:0033 "urn:x-org.chromium.media:source:desktop";
John Williamsd48d028c2019-05-23 23:59:1234
35// List of non-http(s) schemes that are allowed in a Presentation URL.
36constexpr std::array<const char* const, 5> kAllowedSchemes{
37 {kCastPresentationUrlScheme, kCastDialPresentationUrlScheme,
38 kDialPresentationUrlScheme, kRemotePlaybackPresentationUrlScheme, "test"}};
39
40bool IsSchemeAllowed(const GURL& url) {
41 return url.SchemeIsHTTPOrHTTPS() ||
42 std::any_of(
43 kAllowedSchemes.begin(), kAllowedSchemes.end(),
44 [&url](const char* const scheme) { return url.SchemeIs(scheme); });
45}
46
47} // namespace
48
49bool IsLegacyCastPresentationUrl(const GURL& url) {
50 return base::StartsWith(url.spec(), kLegacyCastPresentationUrlPrefix,
51 base::CompareCase::INSENSITIVE_ASCII);
52}
53
54bool IsValidPresentationUrl(const GURL& url) {
55 return url.is_valid() && IsSchemeAllowed(url);
56}
57
Clifford Cheng5d6c5172020-11-25 02:05:0958bool IsValidStandardPresentationSource(const std::string& media_source) {
59 const GURL source_url(media_source);
60 return source_url.is_valid() && source_url.SchemeIsHTTPOrHTTPS() &&
61 !base::StartsWith(source_url.spec(), kLegacyCastPresentationUrlPrefix,
62 base::CompareCase::INSENSITIVE_ASCII);
63}
64
John Williamsd48d028c2019-05-23 23:59:1265bool IsAutoJoinPresentationId(const std::string& presentation_id) {
66 return presentation_id == kAutoJoinPresentationId;
67}
68
69MediaSource::MediaSource() = default;
70
kmarshall5fa0263d2015-06-03 16:07:0871MediaSource::MediaSource(const MediaSource::Id& source_id) : id_(source_id) {
zhaobine1ed84732016-12-20 23:16:3472 GURL url(source_id);
73 if (IsValidPresentationUrl(url))
74 url_ = url;
imcheng2ae6fd32015-04-10 17:59:1875}
76
mfoltz7a2c823b2016-10-08 01:35:2477MediaSource::MediaSource(const GURL& presentation_url)
zhaobin81cb26bf2016-12-07 19:42:5578 : id_(presentation_url.spec()), url_(presentation_url) {}
mfoltz7a2c823b2016-10-08 01:35:2479
John Williamsd48d028c2019-05-23 23:59:1280MediaSource::~MediaSource() = default;
kmarshalld2f3bea2015-03-11 23:42:2281
John Williamsd48d028c2019-05-23 23:59:1282// static
John Williams479d9902020-06-04 17:09:3483MediaSource MediaSource::ForLocalFile() {
84 // TODO(crbug.com/1090878): Use something more sane here. Fixing this
85 // requires tracking down other places where tab ID 0 is used to indicate
86 // local file casting.
87 //
88 // This probably isn't a source of bugs in practice, because tab IDs are
89 // generated by SessionIdGenerator, which appears to only produce positive
90 // values, but that fact isn't clearly documentated, and other parts of
91 // Chromium don't seem to rely on it, using -1 as the canonical invalid tab
92 // ID.
93 return MediaSource(base::StringPrintf(kTabMediaUrnFormat, 0));
94}
95
96// static
Jordan Bayles89346462020-05-26 21:36:1297MediaSource MediaSource::ForAnyTab() {
98 return MediaSource(std::string(kAnyTabMediaUrn));
99}
100
101// static
John Williamsd48d028c2019-05-23 23:59:12102MediaSource MediaSource::ForTab(int tab_id) {
John Williams479d9902020-06-04 17:09:34103 // Ideally we shouldn't allow -1 as a tab ID, but in unit tests, a tab ID of
104 // -1 can show up when this function is called from
105 // CastHandler::StartObservingForSinks() because SessionTabHelper::IdForTab
106 // can return -1.
107 DCHECK_GE(tab_id, -1);
John Williamsd48d028c2019-05-23 23:59:12108 return MediaSource(base::StringPrintf(kTabMediaUrnFormat, tab_id));
kmarshalld2f3bea2015-03-11 23:42:22109}
110
John Williamsd48d028c2019-05-23 23:59:12111// static
Yuri Wiitala85245382020-08-25 03:09:39112MediaSource MediaSource::ForDesktop(
113 const std::string& registered_desktop_stream_id,
114 bool with_audio) {
115 DCHECK(!registered_desktop_stream_id.empty());
116 std::string id =
Peter Kasting01167522021-04-28 19:32:08117 std::string(kDesktopMediaUrnPrefix) + registered_desktop_stream_id;
Yuri Wiitala85245382020-08-25 03:09:39118 if (with_audio) {
Peter Kasting01167522021-04-28 19:32:08119 id += std::string(kDesktopMediaUrnAudioParam);
Yuri Wiitala85245382020-08-25 03:09:39120 }
121 return MediaSource(id);
John Williams4c353442019-11-13 23:37:00122}
123
124// static
Yuri Wiitala85245382020-08-25 03:09:39125MediaSource MediaSource::ForUnchosenDesktop() {
Peter Kasting01167522021-04-28 19:32:08126 return MediaSource(std::string(kUnchosenDesktopMediaUrn));
imcheng2ae6fd32015-04-10 17:59:18127}
128
John Williamsd48d028c2019-05-23 23:59:12129// static
130MediaSource MediaSource::ForPresentationUrl(const GURL& presentation_url) {
131 return MediaSource(presentation_url);
Bin Zhao3e0c55d2018-03-16 23:40:51132}
133
Jordan Bayles89346462020-05-26 21:36:12134bool MediaSource::IsTabMirroringSource() const {
John Williams479d9902020-06-04 17:09:34135 return id() == kAnyTabMediaUrn || TabId() > 0;
Jordan Bayles89346462020-05-26 21:36:12136}
137
John Williamsd48d028c2019-05-23 23:59:12138bool MediaSource::IsDesktopMirroringSource() const {
Yuri Wiitala85245382020-08-25 03:09:39139 return id() == kUnchosenDesktopMediaUrn ||
John Williams4c353442019-11-13 23:37:00140 base::StartsWith(id(), kDesktopMediaUrnPrefix,
141 base::CompareCase::SENSITIVE);
John Williamsd48d028c2019-05-23 23:59:12142}
143
John Williams479d9902020-06-04 17:09:34144bool MediaSource::IsLocalFileSource() const {
145 // TODO(crbug.com/1090878): Keep this method is sync with ForLocalFile().
146 return TabId() == 0;
John Williamsd48d028c2019-05-23 23:59:12147}
148
149bool MediaSource::IsCastPresentationUrl() const {
150 return url_.SchemeIs(kCastPresentationUrlScheme) ||
151 IsLegacyCastPresentationUrl(url_);
152}
153
154int MediaSource::TabId() const {
John Williams479d9902020-06-04 17:09:34155 int tab_id = -1;
156 sscanf(id_.c_str(), kTabMediaUrnFormat, &tab_id);
157 return tab_id;
John Williamsd48d028c2019-05-23 23:59:12158}
159
Anton Bikineev1156b5f2021-05-15 22:35:36160absl::optional<std::string> MediaSource::DesktopStreamId() const {
John Williams4c353442019-11-13 23:37:00161 if (base::StartsWith(id_, kDesktopMediaUrnPrefix,
162 base::CompareCase::SENSITIVE)) {
Yuri Wiitala85245382020-08-25 03:09:39163 const auto begin = id_.begin() + kDesktopMediaUrnPrefix.size();
164 auto end = id_.end();
165 if (base::EndsWith(id_, kDesktopMediaUrnAudioParam,
166 base::CompareCase::SENSITIVE)) {
167 end -= kDesktopMediaUrnAudioParam.size();
168 }
169 return std::string(begin, end);
John Williams4c353442019-11-13 23:37:00170 }
Anton Bikineev1156b5f2021-05-15 22:35:36171 return absl::nullopt;
John Williams4c353442019-11-13 23:37:00172}
173
Yuri Wiitala85245382020-08-25 03:09:39174bool MediaSource::IsDesktopSourceWithAudio() const {
175 return base::StartsWith(id_, kDesktopMediaUrnPrefix,
176 base::CompareCase::SENSITIVE) &&
177 base::EndsWith(id_, kDesktopMediaUrnAudioParam,
178 base::CompareCase::SENSITIVE);
179}
180
John Williamsd48d028c2019-05-23 23:59:12181bool MediaSource::IsDialSource() const {
182 return url_.SchemeIs(kCastDialPresentationUrlScheme);
183}
184
185std::string MediaSource::AppNameFromDialSource() const {
186 return IsDialSource() ? url_.path() : "";
187}
imchengab1bf6352017-02-16 03:30:07188
Takumi Fujimoto979451572020-06-08 23:04:58189std::string MediaSource::TruncateForLogging(size_t max_length) const {
190 const std::string origin = url_.GetOrigin().spec();
191 if (!origin.empty())
192 return origin.substr(0, max_length);
193 // TODO(takumif): Keep the query string by redacting PII. The query string may
194 // contain info useful for debugging such as the required capabilities.
195 const size_t query_start_index = id_.find("?");
196 const size_t length =
197 query_start_index == std::string::npos ? max_length : query_start_index;
198 return id_.substr(0, length);
199}
200
kmarshalld2f3bea2015-03-11 23:42:22201} // namespace media_router