blob: 277f257c3a4d69d747fab9e68e9504995d25e519 [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/public/browser/web_contents_media_capture_id.h"
#include <string_view>
#include "base/compiler_specific.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
namespace {
constexpr char kDisableLocalEchoFlag[] = "local_echo=false";
constexpr char kOptionStart = '?';
constexpr char kOptionSeparator = '&';
bool ExtractTabCaptureTarget(const std::string& device_id_param,
int* render_process_id,
int* main_render_frame_id) {
const std::string device_scheme = content::kWebContentsCaptureScheme;
if (!base::StartsWith(device_id_param, device_scheme,
base::CompareCase::SENSITIVE))
return false;
const std::string device_id = device_id_param.substr(device_scheme.size());
const size_t sep_pos = device_id.find(':');
if (sep_pos == std::string::npos)
return false;
const std::string_view component1(device_id.data(), sep_pos);
size_t end_pos = device_id.find('?');
if (end_pos == std::string::npos)
end_pos = device_id.length();
const std::string_view component2(UNSAFE_TODO(device_id.data() + sep_pos + 1),
end_pos - sep_pos - 1);
return (base::StringToInt(component1, render_process_id) &&
base::StringToInt(component2, main_render_frame_id));
}
bool ExtractOptions(const std::string& device_id,
bool* disable_local_echo) {
DCHECK(disable_local_echo);
*disable_local_echo = false;
// Find the option part of the string and just do a naive string compare since
// there are no other options in the |device_id| to account for (at the time
// of this writing).
size_t option_pos = device_id.find(kOptionStart);
if (option_pos == std::string::npos)
return true;
size_t option_pos_end;
while (option_pos < device_id.length()) {
option_pos_end = device_id.find(kOptionSeparator, option_pos + 1);
if (option_pos_end == std::string::npos)
option_pos_end = device_id.length();
const std::string_view component(
UNSAFE_TODO(device_id.data() + option_pos + 1),
option_pos_end - option_pos - 1);
if (component.compare(kDisableLocalEchoFlag) == 0)
*disable_local_echo = true;
else // Some unknown parameter is specified, and thus this ID is invalid.
return false;
option_pos = option_pos_end;
}
return true;
}
} // namespace
namespace content {
const char kWebContentsCaptureScheme[] = "web-contents-media-stream://";
bool WebContentsMediaCaptureId::is_null() const {
return (render_process_id < 0) || (main_render_frame_id < 0);
}
std::string WebContentsMediaCaptureId::ToString() const {
std::string s = kWebContentsCaptureScheme;
s.append(base::NumberToString(render_process_id));
s.append(":");
s.append(base::NumberToString(main_render_frame_id));
if (disable_local_echo) {
s += kOptionStart;
s.append(kDisableLocalEchoFlag);
}
return s;
}
// static
bool WebContentsMediaCaptureId::Parse(const std::string& str,
WebContentsMediaCaptureId* output_id) {
int render_process_id;
int main_render_frame_id;
if (!ExtractTabCaptureTarget(str, &render_process_id, &main_render_frame_id))
return false;
bool disable_local_echo;
if (!ExtractOptions(str, &disable_local_echo))
return false;
if (output_id) {
output_id->render_process_id = render_process_id;
output_id->main_render_frame_id = main_render_frame_id;
output_id->disable_local_echo = disable_local_echo;
}
return true;
}
} // namespace content