1 // Copyright 2013 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_api.h"
7 #include "base/compiler_specific.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/extensions/extension_tab_util.h"
10 #include "chrome/browser/media/desktop_media_list_ash.h"
11 #include "chrome/browser/media/desktop_streams_registry.h"
12 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
13 #include "chrome/browser/media/native_desktop_media_list.h"
14 #include "chrome/browser/ui/ash/ash_util.h"
15 #include "chrome/common/extensions/api/tabs.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/render_process_host.h"
18 #include "content/public/browser/render_view_host.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/browser/web_contents_view.h"
21 #include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h"
22 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
23 #include "third_party/webrtc/modules/desktop_capture/window_capturer.h"
25 namespace extensions
{
29 const char kInvalidSourceNameError
[] = "Invalid source type specified.";
30 const char kEmptySourcesListError
[] =
31 "At least one source type must be specified.";
32 const char kTabCaptureNotSupportedError
[] = "Tab capture is not supported yet.";
33 const char kNoTabIdError
[] = "targetTab doesn't have id field set.";
34 const char kNoUrlError
[] = "targetTab doesn't have URL field set.";
35 const char kInvalidTabIdError
[] = "Invalid tab specified.";
36 const char kTabUrlChangedError
[] = "URL for the specified tab has changed.";
38 DesktopCaptureChooseDesktopMediaFunction::PickerFactory
* g_picker_factory
=
44 void DesktopCaptureChooseDesktopMediaFunction::SetPickerFactoryForTests(
45 PickerFactory
* factory
) {
46 g_picker_factory
= factory
;
49 DesktopCaptureChooseDesktopMediaFunction::
50 DesktopCaptureChooseDesktopMediaFunction()
51 : render_process_id_(0),
55 DesktopCaptureChooseDesktopMediaFunction::
56 ~DesktopCaptureChooseDesktopMediaFunction() {
57 // RenderViewHost may be already destroyed.
58 if (render_view_host()) {
59 DesktopCaptureRequestsRegistry::GetInstance()->RemoveRequest(
60 render_view_host()->GetProcess()->GetID(), request_id_
);
64 void DesktopCaptureChooseDesktopMediaFunction::Cancel() {
65 // Keep reference to |this| to ensure the object doesn't get destroyed before
67 scoped_refptr
<DesktopCaptureChooseDesktopMediaFunction
> self(this);
70 SetResult(new base::StringValue(std::string()));
75 bool DesktopCaptureChooseDesktopMediaFunction::RunImpl() {
76 EXTENSION_FUNCTION_VALIDATE(args_
->GetSize() > 0);
78 EXTENSION_FUNCTION_VALIDATE(args_
->GetInteger(0, &request_id_
));
79 args_
->Remove(0, NULL
);
81 scoped_ptr
<api::desktop_capture::ChooseDesktopMedia::Params
> params
=
82 api::desktop_capture::ChooseDesktopMedia::Params::Create(*args_
);
83 EXTENSION_FUNCTION_VALIDATE(params
.get());
85 DesktopCaptureRequestsRegistry::GetInstance()->AddRequest(
86 render_view_host()->GetProcess()->GetID(), request_id_
, this);
88 gfx::NativeWindow parent_window
;
89 content::RenderViewHost
* render_view
;
90 if (params
->target_tab
) {
91 if (!params
->target_tab
->url
) {
95 origin_
= GURL(*(params
->target_tab
->url
)).GetOrigin();
97 if (!params
->target_tab
->id
) {
98 error_
= kNoTabIdError
;
102 content::WebContents
* web_contents
= NULL
;
103 if (!ExtensionTabUtil::GetTabById(*(params
->target_tab
->id
), GetProfile(),
104 true, NULL
, NULL
, &web_contents
, NULL
)) {
105 error_
= kInvalidTabIdError
;
109 GURL current_origin_
=
110 web_contents
->GetLastCommittedURL().GetOrigin();
111 if (current_origin_
!= origin_
) {
112 error_
= kTabUrlChangedError
;
116 render_view
= web_contents
->GetRenderViewHost();
117 parent_window
= web_contents
->GetView()->GetTopLevelNativeWindow();
119 origin_
= GetExtension()->url();
120 render_view
= render_view_host();
122 GetAssociatedWebContents()->GetView()->GetTopLevelNativeWindow();
124 render_process_id_
= render_view
->GetProcess()->GetID();
125 render_view_id_
= render_view
->GetRoutingID();
128 bool show_screens
= false;
129 bool show_windows
= false;
131 for (std::vector
<api::desktop_capture::DesktopCaptureSourceType
>::iterator
132 it
= params
->sources
.begin(); it
!= params
->sources
.end(); ++it
) {
134 case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_NONE
:
135 error_
= kInvalidSourceNameError
;
138 case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_SCREEN
:
142 case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_WINDOW
:
146 case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_TAB
:
147 error_
= kTabCaptureNotSupportedError
;
152 if (!show_screens
&& !show_windows
) {
153 error_
= kEmptySourcesListError
;
157 scoped_ptr
<DesktopMediaList
> media_list
;
158 if (g_picker_factory
) {
159 media_list
= g_picker_factory
->CreateModel(
160 show_screens
, show_windows
);
161 picker_
= g_picker_factory
->CreatePicker();
164 if (chrome::IsNativeWindowInAsh(parent_window
)) {
165 media_list
.reset(new DesktopMediaListAsh(
166 (show_screens
? DesktopMediaListAsh::SCREENS
: 0) |
167 (show_windows
? DesktopMediaListAsh::WINDOWS
: 0)));
171 webrtc::DesktopCaptureOptions options
=
172 webrtc::DesktopCaptureOptions::CreateDefault();
173 options
.set_disable_effects(false);
174 scoped_ptr
<webrtc::ScreenCapturer
> screen_capturer(
175 show_screens
? webrtc::ScreenCapturer::Create(options
) : NULL
);
176 scoped_ptr
<webrtc::WindowCapturer
> window_capturer(
177 show_windows
? webrtc::WindowCapturer::Create(options
) : NULL
);
179 media_list
.reset(new NativeDesktopMediaList(
180 screen_capturer
.Pass(), window_capturer
.Pass()));
183 // DesktopMediaPicker is implemented only for Windows, OSX and
184 // Aura Linux builds.
185 #if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX)
186 picker_
= DesktopMediaPicker::Create();
188 error_
= "Desktop Capture API is not yet implemented for this platform.";
192 DesktopMediaPicker::DoneCallback callback
= base::Bind(
193 &DesktopCaptureChooseDesktopMediaFunction::OnPickerDialogResults
, this);
195 picker_
->Show(parent_window
, parent_window
,
196 base::UTF8ToUTF16(GetExtension()->name()),
197 media_list
.Pass(), callback
);
201 void DesktopCaptureChooseDesktopMediaFunction::OnPickerDialogResults(
202 content::DesktopMediaID source
) {
204 if (source
.type
!= content::DesktopMediaID::TYPE_NONE
) {
205 DesktopStreamsRegistry
* registry
=
206 MediaCaptureDevicesDispatcher::GetInstance()->
207 GetDesktopStreamsRegistry();
208 result
= registry
->RegisterStream(
209 render_process_id_
, render_view_id_
, origin_
, source
);
212 SetResult(new base::StringValue(result
));
216 DesktopCaptureRequestsRegistry::RequestId::RequestId(int process_id
,
218 : process_id(process_id
),
219 request_id(request_id
) {
222 bool DesktopCaptureRequestsRegistry::RequestId::operator<(
223 const RequestId
& other
) const {
224 if (process_id
!= other
.process_id
) {
225 return process_id
< other
.process_id
;
227 return request_id
< other
.request_id
;
231 DesktopCaptureCancelChooseDesktopMediaFunction::
232 DesktopCaptureCancelChooseDesktopMediaFunction() {}
234 DesktopCaptureCancelChooseDesktopMediaFunction::
235 ~DesktopCaptureCancelChooseDesktopMediaFunction() {}
237 bool DesktopCaptureCancelChooseDesktopMediaFunction::RunImpl() {
239 EXTENSION_FUNCTION_VALIDATE(args_
->GetInteger(0, &request_id
));
241 DesktopCaptureRequestsRegistry::GetInstance()->CancelRequest(
242 render_view_host()->GetProcess()->GetID(), request_id
);
246 DesktopCaptureRequestsRegistry::DesktopCaptureRequestsRegistry() {}
247 DesktopCaptureRequestsRegistry::~DesktopCaptureRequestsRegistry() {}
250 DesktopCaptureRequestsRegistry
* DesktopCaptureRequestsRegistry::GetInstance() {
251 return Singleton
<DesktopCaptureRequestsRegistry
>::get();
254 void DesktopCaptureRequestsRegistry::AddRequest(
257 DesktopCaptureChooseDesktopMediaFunction
* handler
) {
259 RequestsMap::value_type(RequestId(process_id
, request_id
), handler
));
262 void DesktopCaptureRequestsRegistry::RemoveRequest(int process_id
,
264 requests_
.erase(RequestId(process_id
, request_id
));
267 void DesktopCaptureRequestsRegistry::CancelRequest(int process_id
,
269 RequestsMap::iterator it
= requests_
.find(RequestId(process_id
, request_id
));
270 if (it
!= requests_
.end())
271 it
->second
->Cancel();
275 } // namespace extensions