1 // 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.
5 #include "chrome/browser/extensions/api/desktop_capture/desktop_capture_base.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "chrome/browser/extensions/extension_tab_util.h"
9 #include "chrome/browser/media/desktop_media_list_ash.h"
10 #include "chrome/browser/media/desktop_streams_registry.h"
11 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
12 #include "chrome/browser/media/native_desktop_media_list.h"
13 #include "chrome/browser/ui/ash/ash_util.h"
14 #include "content/public/browser/render_frame_host.h"
15 #include "content/public/browser/render_process_host.h"
16 #include "content/public/browser/web_contents.h"
17 #include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h"
18 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
19 #include "third_party/webrtc/modules/desktop_capture/window_capturer.h"
21 namespace extensions
{
25 const char kInvalidSourceNameError
[] = "Invalid source type specified.";
26 const char kEmptySourcesListError
[] =
27 "At least one source type must be specified.";
28 const char kTabCaptureNotSupportedError
[] = "Tab capture is not supported yet.";
30 DesktopCaptureChooseDesktopMediaFunctionBase::PickerFactory
* g_picker_factory
=
36 void DesktopCaptureChooseDesktopMediaFunctionBase::SetPickerFactoryForTests(
37 PickerFactory
* factory
) {
38 g_picker_factory
= factory
;
41 DesktopCaptureChooseDesktopMediaFunctionBase::
42 DesktopCaptureChooseDesktopMediaFunctionBase() {
45 DesktopCaptureChooseDesktopMediaFunctionBase::
46 ~DesktopCaptureChooseDesktopMediaFunctionBase() {
47 // RenderViewHost may be already destroyed.
48 if (render_frame_host()) {
49 DesktopCaptureRequestsRegistry::GetInstance()->RemoveRequest(
50 render_frame_host()->GetProcess()->GetID(), request_id_
);
54 void DesktopCaptureChooseDesktopMediaFunctionBase::Cancel() {
55 // Keep reference to |this| to ensure the object doesn't get destroyed before
57 scoped_refptr
<DesktopCaptureChooseDesktopMediaFunctionBase
> self(this);
60 SetResult(new base::StringValue(std::string()));
65 bool DesktopCaptureChooseDesktopMediaFunctionBase::Execute(
66 const std::vector
<api::desktop_capture::DesktopCaptureSourceType
>& sources
,
67 content::WebContents
* web_contents
,
69 const base::string16 target_name
) {
70 // Register to be notified when the tab is closed.
71 Observe(web_contents
);
73 bool show_screens
= false;
74 bool show_windows
= false;
76 for (auto source_type
: sources
) {
77 switch (source_type
) {
78 case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_NONE
:
79 error_
= kInvalidSourceNameError
;
82 case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_SCREEN
:
86 case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_WINDOW
:
90 case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_TAB
:
91 error_
= kTabCaptureNotSupportedError
;
96 if (!show_screens
&& !show_windows
) {
97 error_
= kEmptySourcesListError
;
101 const gfx::NativeWindow parent_window
=
102 web_contents
->GetTopLevelNativeWindow();
103 scoped_ptr
<DesktopMediaList
> media_list
;
104 if (g_picker_factory
) {
105 media_list
= g_picker_factory
->CreateModel(
106 show_screens
, show_windows
);
107 picker_
= g_picker_factory
->CreatePicker();
110 if (chrome::IsNativeWindowInAsh(parent_window
)) {
111 media_list
.reset(new DesktopMediaListAsh(
112 (show_screens
? DesktopMediaListAsh::SCREENS
: 0) |
113 (show_windows
? DesktopMediaListAsh::WINDOWS
: 0)));
117 webrtc::DesktopCaptureOptions options
=
118 webrtc::DesktopCaptureOptions::CreateDefault();
119 options
.set_disable_effects(false);
120 scoped_ptr
<webrtc::ScreenCapturer
> screen_capturer(
121 show_screens
? webrtc::ScreenCapturer::Create(options
) : NULL
);
122 scoped_ptr
<webrtc::WindowCapturer
> window_capturer(
123 show_windows
? webrtc::WindowCapturer::Create(options
) : NULL
);
125 media_list
.reset(new NativeDesktopMediaList(
126 screen_capturer
.Pass(), window_capturer
.Pass()));
129 // DesktopMediaPicker is implemented only for Windows, OSX and
130 // Aura Linux builds.
131 #if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX)
132 picker_
= DesktopMediaPicker::Create();
134 error_
= "Desktop Capture API is not yet implemented for this platform.";
138 DesktopMediaPicker::DoneCallback callback
= base::Bind(
139 &DesktopCaptureChooseDesktopMediaFunctionBase::OnPickerDialogResults
,
142 picker_
->Show(web_contents
,
145 base::UTF8ToUTF16(extension()->name()),
153 void DesktopCaptureChooseDesktopMediaFunctionBase::WebContentsDestroyed() {
157 void DesktopCaptureChooseDesktopMediaFunctionBase::OnPickerDialogResults(
158 content::DesktopMediaID source
) {
160 if (source
.type
!= content::DesktopMediaID::TYPE_NONE
&&
162 DesktopStreamsRegistry
* registry
=
163 MediaCaptureDevicesDispatcher::GetInstance()->
164 GetDesktopStreamsRegistry();
165 // TODO(miu): Once render_frame_host() is being set, we should register the
166 // exact RenderFrame requesting the stream, not the main RenderFrame. With
167 // that change, also update
168 // MediaCaptureDevicesDispatcher::ProcessDesktopCaptureAccessRequest().
169 // http://crbug.com/304341
170 content::RenderFrameHost
* const main_frame
= web_contents()->GetMainFrame();
171 result
= registry
->RegisterStream(main_frame
->GetProcess()->GetID(),
172 main_frame
->GetRoutingID(),
175 extension()->name());
178 SetResult(new base::StringValue(result
));
182 DesktopCaptureRequestsRegistry::RequestId::RequestId(int process_id
,
184 : process_id(process_id
),
185 request_id(request_id
) {
188 bool DesktopCaptureRequestsRegistry::RequestId::operator<(
189 const RequestId
& other
) const {
190 if (process_id
!= other
.process_id
) {
191 return process_id
< other
.process_id
;
193 return request_id
< other
.request_id
;
197 DesktopCaptureCancelChooseDesktopMediaFunctionBase::
198 DesktopCaptureCancelChooseDesktopMediaFunctionBase() {}
200 DesktopCaptureCancelChooseDesktopMediaFunctionBase::
201 ~DesktopCaptureCancelChooseDesktopMediaFunctionBase() {}
203 bool DesktopCaptureCancelChooseDesktopMediaFunctionBase::RunSync() {
205 EXTENSION_FUNCTION_VALIDATE(args_
->GetInteger(0, &request_id
));
207 DesktopCaptureRequestsRegistry::GetInstance()->CancelRequest(
208 render_frame_host()->GetProcess()->GetID(), request_id
);
212 DesktopCaptureRequestsRegistry::DesktopCaptureRequestsRegistry() {}
213 DesktopCaptureRequestsRegistry::~DesktopCaptureRequestsRegistry() {}
216 DesktopCaptureRequestsRegistry
* DesktopCaptureRequestsRegistry::GetInstance() {
217 return base::Singleton
<DesktopCaptureRequestsRegistry
>::get();
220 void DesktopCaptureRequestsRegistry::AddRequest(
223 DesktopCaptureChooseDesktopMediaFunctionBase
* handler
) {
225 RequestsMap::value_type(RequestId(process_id
, request_id
), handler
));
228 void DesktopCaptureRequestsRegistry::RemoveRequest(int process_id
,
230 requests_
.erase(RequestId(process_id
, request_id
));
233 void DesktopCaptureRequestsRegistry::CancelRequest(int process_id
,
235 RequestsMap::iterator it
= requests_
.find(RequestId(process_id
, request_id
));
236 if (it
!= requests_
.end())
237 it
->second
->Cancel();
241 } // namespace extensions