feat: session.setDisplayMediaRequestHandler (#30702)
This commit is contained in:
parent
0c04be502c
commit
221bb51326
14 changed files with 711 additions and 11 deletions
|
@ -52,6 +52,7 @@
|
|||
#include "shell/browser/api/electron_api_net_log.h"
|
||||
#include "shell/browser/api/electron_api_protocol.h"
|
||||
#include "shell/browser/api/electron_api_service_worker_context.h"
|
||||
#include "shell/browser/api/electron_api_web_frame_main.h"
|
||||
#include "shell/browser/api/electron_api_web_request.h"
|
||||
#include "shell/browser/browser.h"
|
||||
#include "shell/browser/electron_browser_context.h"
|
||||
|
@ -65,6 +66,7 @@
|
|||
#include "shell/common/gin_converters/content_converter.h"
|
||||
#include "shell/common/gin_converters/file_path_converter.h"
|
||||
#include "shell/common/gin_converters/gurl_converter.h"
|
||||
#include "shell/common/gin_converters/media_converter.h"
|
||||
#include "shell/common/gin_converters/net_converter.h"
|
||||
#include "shell/common/gin_converters/value_converter.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
|
@ -73,6 +75,7 @@
|
|||
#include "shell/common/options_switches.h"
|
||||
#include "shell/common/process_util.h"
|
||||
#include "third_party/blink/public/common/storage_key/storage_key.h"
|
||||
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
|
@ -643,6 +646,22 @@ void Session::SetPermissionCheckHandler(v8::Local<v8::Value> val,
|
|||
permission_manager->SetPermissionCheckHandler(handler);
|
||||
}
|
||||
|
||||
void Session::SetDisplayMediaRequestHandler(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val) {
|
||||
if (val->IsNull()) {
|
||||
browser_context_->SetDisplayMediaRequestHandler(
|
||||
DisplayMediaRequestHandler());
|
||||
return;
|
||||
}
|
||||
DisplayMediaRequestHandler handler;
|
||||
if (!gin::ConvertFromV8(isolate, val, &handler)) {
|
||||
gin_helper::ErrorThrower(isolate).ThrowTypeError(
|
||||
"Display media request handler must be null or a function");
|
||||
return;
|
||||
}
|
||||
browser_context_->SetDisplayMediaRequestHandler(handler);
|
||||
}
|
||||
|
||||
void Session::SetDevicePermissionHandler(v8::Local<v8::Value> val,
|
||||
gin::Arguments* args) {
|
||||
ElectronPermissionManager::DeviceCheckHandler handler;
|
||||
|
@ -1198,6 +1217,8 @@ gin::ObjectTemplateBuilder Session::GetObjectTemplateBuilder(
|
|||
&Session::SetPermissionRequestHandler)
|
||||
.SetMethod("setPermissionCheckHandler",
|
||||
&Session::SetPermissionCheckHandler)
|
||||
.SetMethod("setDisplayMediaRequestHandler",
|
||||
&Session::SetDisplayMediaRequestHandler)
|
||||
.SetMethod("setDevicePermissionHandler",
|
||||
&Session::SetDevicePermissionHandler)
|
||||
.SetMethod("clearHostResolverCache", &Session::ClearHostResolverCache)
|
||||
|
|
|
@ -179,6 +179,9 @@ class Session : public gin::Wrappable<Session>,
|
|||
#endif
|
||||
|
||||
private:
|
||||
void SetDisplayMediaRequestHandler(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val);
|
||||
|
||||
// Cached gin_helper::Wrappable objects.
|
||||
v8::Global<v8::Value> cookies_;
|
||||
v8::Global<v8::Value> protocol_;
|
||||
|
|
|
@ -31,8 +31,11 @@
|
|||
#include "content/browser/blob_storage/chrome_blob_storage_context.h" // nogncheck
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/cors_origin_pattern_setter.h"
|
||||
#include "content/public/browser/render_process_host.h"
|
||||
#include "content/public/browser/shared_cors_origin_access_list.h"
|
||||
#include "content/public/browser/storage_partition.h"
|
||||
#include "content/public/browser/web_contents_media_capture_id.h"
|
||||
#include "media/audio/audio_device_description.h"
|
||||
#include "services/network/public/cpp/features.h"
|
||||
#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
|
||||
#include "services/network/public/mojom/network_context.mojom.h"
|
||||
|
@ -51,7 +54,10 @@
|
|||
#include "shell/browser/zoom_level_delegate.h"
|
||||
#include "shell/common/application_info.h"
|
||||
#include "shell/common/electron_paths.h"
|
||||
#include "shell/common/gin_converters/frame_converter.h"
|
||||
#include "shell/common/gin_helper/error_thrower.h"
|
||||
#include "shell/common/options_switches.h"
|
||||
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
#include "extensions/browser/browser_context_keyed_service_factories.h"
|
||||
|
@ -412,6 +418,131 @@ void ElectronBrowserContext::SetSSLConfigClient(
|
|||
ssl_config_client_ = std::move(client);
|
||||
}
|
||||
|
||||
void ElectronBrowserContext::SetDisplayMediaRequestHandler(
|
||||
DisplayMediaRequestHandler handler) {
|
||||
display_media_request_handler_ = handler;
|
||||
}
|
||||
|
||||
void ElectronBrowserContext::DisplayMediaDeviceChosen(
|
||||
const content::MediaStreamRequest& request,
|
||||
content::MediaResponseCallback callback,
|
||||
gin::Arguments* args) {
|
||||
blink::mojom::StreamDevicesSetPtr stream_devices_set =
|
||||
blink::mojom::StreamDevicesSet::New();
|
||||
v8::Local<v8::Value> result;
|
||||
if (!args->GetNext(&result) || result->IsNullOrUndefined()) {
|
||||
std::move(callback).Run(
|
||||
blink::mojom::StreamDevicesSet(),
|
||||
blink::mojom::MediaStreamRequestResult::CAPTURE_FAILURE, nullptr);
|
||||
return;
|
||||
}
|
||||
gin_helper::Dictionary result_dict;
|
||||
if (!gin::ConvertFromV8(args->isolate(), result, &result_dict)) {
|
||||
gin_helper::ErrorThrower(args->isolate())
|
||||
.ThrowTypeError(
|
||||
"Display Media Request streams callback must be called with null "
|
||||
"or a valid object");
|
||||
std::move(callback).Run(
|
||||
blink::mojom::StreamDevicesSet(),
|
||||
blink::mojom::MediaStreamRequestResult::CAPTURE_FAILURE, nullptr);
|
||||
return;
|
||||
}
|
||||
stream_devices_set->stream_devices.emplace_back(
|
||||
blink::mojom::StreamDevices::New());
|
||||
blink::mojom::StreamDevices& devices = *stream_devices_set->stream_devices[0];
|
||||
bool video_requested =
|
||||
request.video_type != blink::mojom::MediaStreamType::NO_SERVICE;
|
||||
bool audio_requested =
|
||||
request.audio_type != blink::mojom::MediaStreamType::NO_SERVICE;
|
||||
bool has_video = false;
|
||||
if (video_requested && result_dict.Has("video")) {
|
||||
gin_helper::Dictionary video_dict;
|
||||
std::string id;
|
||||
std::string name;
|
||||
content::RenderFrameHost* rfh;
|
||||
if (result_dict.Get("video", &video_dict) && video_dict.Get("id", &id) &&
|
||||
video_dict.Get("name", &name)) {
|
||||
devices.video_device =
|
||||
blink::MediaStreamDevice(request.video_type, id, name);
|
||||
} else if (result_dict.Get("video", &rfh)) {
|
||||
devices.video_device = blink::MediaStreamDevice(
|
||||
request.video_type,
|
||||
content::WebContentsMediaCaptureId(rfh->GetProcess()->GetID(),
|
||||
rfh->GetRoutingID())
|
||||
.ToString(),
|
||||
base::UTF16ToUTF8(
|
||||
content::WebContents::FromRenderFrameHost(rfh)->GetTitle()));
|
||||
} else {
|
||||
gin_helper::ErrorThrower(args->isolate())
|
||||
.ThrowTypeError(
|
||||
"video must be a WebFrameMain or DesktopCapturerSource");
|
||||
std::move(callback).Run(
|
||||
blink::mojom::StreamDevicesSet(),
|
||||
blink::mojom::MediaStreamRequestResult::CAPTURE_FAILURE, nullptr);
|
||||
return;
|
||||
}
|
||||
has_video = true;
|
||||
}
|
||||
if (audio_requested && result_dict.Has("audio")) {
|
||||
gin_helper::Dictionary audio_dict;
|
||||
std::string id;
|
||||
std::string name;
|
||||
content::RenderFrameHost* rfh;
|
||||
// NB. this is not permitted by the documentation, but is left here as an
|
||||
// "escape hatch" for providing an arbitrary name/id if needed in the
|
||||
// future.
|
||||
if (result_dict.Get("audio", &audio_dict) && audio_dict.Get("id", &id) &&
|
||||
audio_dict.Get("name", &name)) {
|
||||
devices.audio_device =
|
||||
blink::MediaStreamDevice(request.audio_type, id, name);
|
||||
} else if (result_dict.Get("audio", &rfh)) {
|
||||
devices.audio_device = blink::MediaStreamDevice(
|
||||
request.audio_type,
|
||||
content::WebContentsMediaCaptureId(rfh->GetProcess()->GetID(),
|
||||
rfh->GetRoutingID(),
|
||||
/* disable_local_echo= */ true)
|
||||
.ToString(),
|
||||
"Tab audio");
|
||||
} else if (result_dict.Get("audio", &id)) {
|
||||
devices.audio_device =
|
||||
blink::MediaStreamDevice(request.audio_type, id, "System audio");
|
||||
} else {
|
||||
gin_helper::ErrorThrower(args->isolate())
|
||||
.ThrowTypeError(
|
||||
"audio must be a WebFrameMain, \"loopback\" or "
|
||||
"\"loopbackWithMute\"");
|
||||
std::move(callback).Run(
|
||||
blink::mojom::StreamDevicesSet(),
|
||||
blink::mojom::MediaStreamRequestResult::CAPTURE_FAILURE, nullptr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((video_requested && !has_video)) {
|
||||
gin_helper::ErrorThrower(args->isolate())
|
||||
.ThrowTypeError(
|
||||
"Video was requested, but no video stream was provided");
|
||||
std::move(callback).Run(
|
||||
blink::mojom::StreamDevicesSet(),
|
||||
blink::mojom::MediaStreamRequestResult::CAPTURE_FAILURE, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
std::move(callback).Run(*stream_devices_set,
|
||||
blink::mojom::MediaStreamRequestResult::OK, nullptr);
|
||||
}
|
||||
|
||||
bool ElectronBrowserContext::ChooseDisplayMediaDevice(
|
||||
const content::MediaStreamRequest& request,
|
||||
content::MediaResponseCallback callback) {
|
||||
if (!display_media_request_handler_)
|
||||
return false;
|
||||
DisplayMediaResponseCallbackJs callbackJs =
|
||||
base::BindOnce(&DisplayMediaDeviceChosen, request, std::move(callback));
|
||||
display_media_request_handler_.Run(request, std::move(callbackJs));
|
||||
return true;
|
||||
}
|
||||
|
||||
void ElectronBrowserContext::GrantDevicePermission(
|
||||
const url::Origin& origin,
|
||||
const base::Value& device,
|
||||
|
|
|
@ -13,8 +13,10 @@
|
|||
#include "base/memory/weak_ptr.h"
|
||||
#include "chrome/browser/predictors/preconnect_manager.h"
|
||||
#include "content/public/browser/browser_context.h"
|
||||
#include "content/public/browser/media_stream_request.h"
|
||||
#include "content/public/browser/resource_context.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
#include "gin/arguments.h"
|
||||
#include "mojo/public/cpp/bindings/remote.h"
|
||||
#include "services/network/public/mojom/network_context.mojom.h"
|
||||
#include "services/network/public/mojom/url_loader_factory.mojom.h"
|
||||
|
@ -38,6 +40,13 @@ class ElectronExtensionSystem;
|
|||
}
|
||||
#endif
|
||||
|
||||
namespace v8 {
|
||||
template <typename T>
|
||||
class Local;
|
||||
class Isolate;
|
||||
class Value;
|
||||
} // namespace v8
|
||||
|
||||
namespace electron {
|
||||
|
||||
using DevicePermissionMap =
|
||||
|
@ -51,6 +60,12 @@ class ResolveProxyHelper;
|
|||
class WebViewManager;
|
||||
class ProtocolRegistry;
|
||||
|
||||
using DisplayMediaResponseCallbackJs =
|
||||
base::OnceCallback<void(gin::Arguments* args)>;
|
||||
using DisplayMediaRequestHandler =
|
||||
base::RepeatingCallback<void(const content::MediaStreamRequest&,
|
||||
DisplayMediaResponseCallbackJs)>;
|
||||
|
||||
class ElectronBrowserContext : public content::BrowserContext {
|
||||
public:
|
||||
// disable copy
|
||||
|
@ -150,6 +165,10 @@ class ElectronBrowserContext : public content::BrowserContext {
|
|||
network::mojom::SSLConfigPtr GetSSLConfig();
|
||||
void SetSSLConfigClient(mojo::Remote<network::mojom::SSLConfigClient> client);
|
||||
|
||||
bool ChooseDisplayMediaDevice(const content::MediaStreamRequest& request,
|
||||
content::MediaResponseCallback callback);
|
||||
void SetDisplayMediaRequestHandler(DisplayMediaRequestHandler handler);
|
||||
|
||||
~ElectronBrowserContext() override;
|
||||
|
||||
// Grants |origin| access to |device|.
|
||||
|
@ -176,6 +195,11 @@ class ElectronBrowserContext : public content::BrowserContext {
|
|||
bool in_memory,
|
||||
base::Value::Dict options);
|
||||
|
||||
static void DisplayMediaDeviceChosen(
|
||||
const content::MediaStreamRequest& request,
|
||||
content::MediaResponseCallback callback,
|
||||
gin::Arguments* args);
|
||||
|
||||
// Initialize pref registry.
|
||||
void InitPrefs();
|
||||
|
||||
|
@ -214,6 +238,8 @@ class ElectronBrowserContext : public content::BrowserContext {
|
|||
network::mojom::SSLConfigPtr ssl_config_;
|
||||
mojo::Remote<network::mojom::SSLConfigClient> ssl_config_client_;
|
||||
|
||||
DisplayMediaRequestHandler display_media_request_handler_;
|
||||
|
||||
// In-memory cache that holds objects that have been granted permissions.
|
||||
DevicePermissionMap granted_devices_;
|
||||
|
||||
|
|
|
@ -111,19 +111,43 @@ void MediaAccessAllowed(const content::MediaStreamRequest& request,
|
|||
request.video_type ==
|
||||
blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE ||
|
||||
request.audio_type ==
|
||||
blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE)
|
||||
blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE) {
|
||||
HandleUserMediaRequest(request, std::move(callback));
|
||||
else if (request.video_type ==
|
||||
blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE ||
|
||||
request.audio_type ==
|
||||
blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE)
|
||||
} else if (request.video_type ==
|
||||
blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE ||
|
||||
request.audio_type ==
|
||||
blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) {
|
||||
webrtc::MediaStreamDevicesController::RequestPermissions(
|
||||
request, MediaCaptureDevicesDispatcher::GetInstance(),
|
||||
base::BindOnce(&OnMediaStreamRequestResponse, std::move(callback)));
|
||||
else
|
||||
} else if (request.video_type ==
|
||||
blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE ||
|
||||
request.video_type == blink::mojom::MediaStreamType::
|
||||
DISPLAY_VIDEO_CAPTURE_THIS_TAB ||
|
||||
request.video_type ==
|
||||
blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET ||
|
||||
request.audio_type ==
|
||||
blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE) {
|
||||
content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
|
||||
request.render_process_id, request.render_frame_id);
|
||||
if (!rfh)
|
||||
return;
|
||||
|
||||
content::BrowserContext* browser_context = rfh->GetBrowserContext();
|
||||
ElectronBrowserContext* electron_browser_context =
|
||||
static_cast<ElectronBrowserContext*>(browser_context);
|
||||
auto split_callback = base::SplitOnceCallback(std::move(callback));
|
||||
if (electron_browser_context->ChooseDisplayMediaDevice(
|
||||
request, std::move(split_callback.second)))
|
||||
return;
|
||||
std::move(split_callback.first)
|
||||
.Run(blink::mojom::StreamDevicesSet(),
|
||||
blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED, nullptr);
|
||||
} else {
|
||||
std::move(callback).Run(
|
||||
blink::mojom::StreamDevicesSet(),
|
||||
blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED, nullptr);
|
||||
}
|
||||
} else {
|
||||
std::move(callback).Run(
|
||||
blink::mojom::StreamDevicesSet(),
|
||||
|
|
|
@ -26,6 +26,19 @@ v8::Local<v8::Value> Converter<content::RenderFrameHost*>::ToV8(
|
|||
return electron::api::WebFrameMain::From(isolate, val).ToV8();
|
||||
}
|
||||
|
||||
// static
|
||||
bool Converter<content::RenderFrameHost*>::FromV8(
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
content::RenderFrameHost** out) {
|
||||
electron::api::WebFrameMain* web_frame_main = nullptr;
|
||||
if (!ConvertFromV8(isolate, val, &web_frame_main))
|
||||
return false;
|
||||
*out = web_frame_main->render_frame_host();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
v8::Local<v8::Value>
|
||||
Converter<gin_helper::AccessorValue<content::RenderFrameHost*>>::ToV8(
|
||||
|
|
|
@ -18,6 +18,9 @@ template <>
|
|||
struct Converter<content::RenderFrameHost*> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
content::RenderFrameHost* val);
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
content::RenderFrameHost** out);
|
||||
};
|
||||
|
||||
template <>
|
||||
|
|
36
shell/common/gin_converters/media_converter.cc
Normal file
36
shell/common/gin_converters/media_converter.cc
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) 2021 Slack Technologies, LLC.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/common/gin_converters/media_converter.h"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "content/public/browser/media_stream_request.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "gin/data_object_builder.h"
|
||||
#include "shell/common/gin_converters/frame_converter.h"
|
||||
#include "shell/common/gin_converters/gurl_converter.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
|
||||
|
||||
namespace gin {
|
||||
|
||||
v8::Local<v8::Value> Converter<content::MediaStreamRequest>::ToV8(
|
||||
v8::Isolate* isolate,
|
||||
const content::MediaStreamRequest& request) {
|
||||
content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
|
||||
request.render_process_id, request.render_frame_id);
|
||||
return gin::DataObjectBuilder(isolate)
|
||||
.Set("frame", rfh)
|
||||
.Set("securityOrigin", request.security_origin)
|
||||
.Set("userGesture", request.user_gesture)
|
||||
.Set("videoRequested",
|
||||
request.video_type != blink::mojom::MediaStreamType::NO_SERVICE)
|
||||
.Set("audioRequested",
|
||||
request.audio_type != blink::mojom::MediaStreamType::NO_SERVICE)
|
||||
.Build();
|
||||
}
|
||||
|
||||
} // namespace gin
|
26
shell/common/gin_converters/media_converter.h
Normal file
26
shell/common/gin_converters/media_converter.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) 2021 Slack Technologies, LLC.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ELECTRON_SHELL_COMMON_GIN_CONVERTERS_MEDIA_CONVERTER_H_
|
||||
#define ELECTRON_SHELL_COMMON_GIN_CONVERTERS_MEDIA_CONVERTER_H_
|
||||
|
||||
#include "gin/converter.h"
|
||||
#include "third_party/blink/public/common/mediastream/media_stream_request.h"
|
||||
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-forward.h"
|
||||
|
||||
namespace content {
|
||||
struct MediaStreamRequest;
|
||||
}
|
||||
|
||||
namespace gin {
|
||||
|
||||
template <>
|
||||
struct Converter<content::MediaStreamRequest> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const content::MediaStreamRequest& request);
|
||||
};
|
||||
|
||||
} // namespace gin
|
||||
|
||||
#endif // ELECTRON_SHELL_COMMON_GIN_CONVERTERS_MEDIA_CONVERTER_H_
|
Loading…
Add table
Add a link
Reference in a new issue