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"
8 #include "base/command_line.h"
9 #include "base/compiler_specific.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/extensions/extension_tab_util.h"
12 #include "chrome/browser/media/desktop_media_list_ash.h"
13 #include "chrome/browser/media/desktop_streams_registry.h"
14 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
15 #include "chrome/browser/media/native_desktop_media_list.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/ui/ash/ash_util.h"
18 #include "chrome/browser/ui/host_desktop.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/common/extensions/api/tabs.h"
21 #include "content/public/browser/render_frame_host.h"
22 #include "content/public/browser/render_process_host.h"
23 #include "content/public/browser/render_view_host.h"
24 #include "content/public/browser/web_contents.h"
25 #include "net/base/net_util.h"
26 #include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h"
27 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
28 #include "third_party/webrtc/modules/desktop_capture/window_capturer.h"
30 namespace extensions {
34 const char kInvalidSourceNameError[] = "Invalid source type specified.";
35 const char kEmptySourcesListError[] =
36 "At least one source type must be specified.";
37 const char kTabCaptureNotSupportedError[] = "Tab capture is not supported yet.";
38 const char kNoTabIdError[] = "targetTab doesn't have id field set.";
39 const char kNoUrlError[] = "targetTab doesn't have URL field set.";
40 const char kInvalidOriginError[] = "targetTab.url is not a valid URL.";
41 const char kInvalidTabIdError[] = "Invalid tab specified.";
42 const char kTabUrlNotSecure[] =
43 "URL scheme for the specified tab is not secure.";
45 DesktopCaptureChooseDesktopMediaFunction::PickerFactory* g_picker_factory =
51 void DesktopCaptureChooseDesktopMediaFunction::SetPickerFactoryForTests(
52 PickerFactory* factory) {
53 g_picker_factory = factory;
56 DesktopCaptureChooseDesktopMediaFunction::
57 DesktopCaptureChooseDesktopMediaFunction() {
60 DesktopCaptureChooseDesktopMediaFunction::
61 ~DesktopCaptureChooseDesktopMediaFunction() {
62 // RenderViewHost may be already destroyed.
63 if (render_view_host()) {
64 DesktopCaptureRequestsRegistry::GetInstance()->RemoveRequest(
65 render_view_host()->GetProcess()->GetID(), request_id_);
69 void DesktopCaptureChooseDesktopMediaFunction::Cancel() {
70 // Keep reference to |this| to ensure the object doesn't get destroyed before
72 scoped_refptr<DesktopCaptureChooseDesktopMediaFunction> self(this);
75 SetResult(new base::StringValue(std::string()));
80 bool DesktopCaptureChooseDesktopMediaFunction::RunAsync() {
81 EXTENSION_FUNCTION_VALIDATE(args_->GetSize() > 0);
83 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &request_id_));
84 args_->Remove(0, NULL);
86 scoped_ptr<api::desktop_capture::ChooseDesktopMedia::Params> params =
87 api::desktop_capture::ChooseDesktopMedia::Params::Create(*args_);
88 EXTENSION_FUNCTION_VALIDATE(params.get());
90 DesktopCaptureRequestsRegistry::GetInstance()->AddRequest(
91 render_view_host()->GetProcess()->GetID(), request_id_, this);
93 // |web_contents| is the WebContents for which the stream is created, and will
94 // also be used to determine where to show the picker's UI.
95 content::WebContents* web_contents = NULL;
96 base::string16 target_name;
97 if (params->target_tab) {
98 if (!params->target_tab->url) {
102 origin_ = GURL(*(params->target_tab->url)).GetOrigin();
104 if (!origin_.is_valid()) {
105 error_ = kInvalidOriginError;
109 if (!CommandLine::ForCurrentProcess()->HasSwitch(
110 switches::kAllowHttpScreenCapture) &&
111 !origin_.SchemeIsSecure()) {
112 error_ = kTabUrlNotSecure;
115 target_name = base::UTF8ToUTF16(origin_.SchemeIsSecure() ?
116 net::GetHostAndOptionalPort(origin_) : origin_.spec());
118 if (!params->target_tab->id) {
119 error_ = kNoTabIdError;
123 if (!ExtensionTabUtil::GetTabById(*(params->target_tab->id), GetProfile(),
124 true, NULL, NULL, &web_contents, NULL)) {
125 error_ = kInvalidTabIdError;
128 DCHECK(web_contents);
130 origin_ = extension()->url();
131 target_name = base::UTF8ToUTF16(extension()->name());
132 web_contents = content::WebContents::FromRenderViewHost(render_view_host());
133 DCHECK(web_contents);
136 // Register to be notified when the tab is closed.
137 Observe(web_contents);
139 bool show_screens = false;
140 bool show_windows = false;
142 for (std::vector<api::desktop_capture::DesktopCaptureSourceType>::iterator
143 it = params->sources.begin(); it != params->sources.end(); ++it) {
145 case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_NONE:
146 error_ = kInvalidSourceNameError;
149 case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_SCREEN:
153 case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_WINDOW:
157 case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_TAB:
158 error_ = kTabCaptureNotSupportedError;
163 if (!show_screens && !show_windows) {
164 error_ = kEmptySourcesListError;
168 const gfx::NativeWindow parent_window =
169 web_contents->GetTopLevelNativeWindow();
170 scoped_ptr<DesktopMediaList> media_list;
171 if (g_picker_factory) {
172 media_list = g_picker_factory->CreateModel(
173 show_screens, show_windows);
174 picker_ = g_picker_factory->CreatePicker();
177 if (chrome::IsNativeWindowInAsh(parent_window)) {
178 media_list.reset(new DesktopMediaListAsh(
179 (show_screens ? DesktopMediaListAsh::SCREENS : 0) |
180 (show_windows ? DesktopMediaListAsh::WINDOWS : 0)));
184 webrtc::DesktopCaptureOptions options =
185 webrtc::DesktopCaptureOptions::CreateDefault();
186 options.set_disable_effects(false);
187 scoped_ptr<webrtc::ScreenCapturer> screen_capturer(
188 show_screens ? webrtc::ScreenCapturer::Create(options) : NULL);
189 scoped_ptr<webrtc::WindowCapturer> window_capturer(
190 show_windows ? webrtc::WindowCapturer::Create(options) : NULL);
192 media_list.reset(new NativeDesktopMediaList(
193 screen_capturer.Pass(), window_capturer.Pass()));
196 // DesktopMediaPicker is implemented only for Windows, OSX and
197 // Aura Linux builds.
198 #if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX)
199 picker_ = DesktopMediaPicker::Create();
201 error_ = "Desktop Capture API is not yet implemented for this platform.";
205 DesktopMediaPicker::DoneCallback callback = base::Bind(
206 &DesktopCaptureChooseDesktopMediaFunction::OnPickerDialogResults, this);
208 picker_->Show(web_contents,
211 base::UTF8ToUTF16(extension()->name()),
218 void DesktopCaptureChooseDesktopMediaFunction::WebContentsDestroyed() {
222 void DesktopCaptureChooseDesktopMediaFunction::OnPickerDialogResults(
223 content::DesktopMediaID source) {
225 if (source.type != content::DesktopMediaID::TYPE_NONE &&
227 DesktopStreamsRegistry* registry =
228 MediaCaptureDevicesDispatcher::GetInstance()->
229 GetDesktopStreamsRegistry();
230 // TODO(miu): Once render_frame_host() is being set, we should register the
231 // exact RenderFrame requesting the stream, not the main RenderFrame. With
232 // that change, also update
233 // MediaCaptureDevicesDispatcher::ProcessDesktopCaptureAccessRequest().
234 // http://crbug.com/304341
235 content::RenderFrameHost* const main_frame = web_contents()->GetMainFrame();
236 result = registry->RegisterStream(main_frame->GetProcess()->GetID(),
237 main_frame->GetRoutingID(),
240 extension()->name());
243 SetResult(new base::StringValue(result));
247 DesktopCaptureRequestsRegistry::RequestId::RequestId(int process_id,
249 : process_id(process_id),
250 request_id(request_id) {
253 bool DesktopCaptureRequestsRegistry::RequestId::operator<(
254 const RequestId& other) const {
255 if (process_id != other.process_id) {
256 return process_id < other.process_id;
258 return request_id < other.request_id;
262 DesktopCaptureCancelChooseDesktopMediaFunction::
263 DesktopCaptureCancelChooseDesktopMediaFunction() {}
265 DesktopCaptureCancelChooseDesktopMediaFunction::
266 ~DesktopCaptureCancelChooseDesktopMediaFunction() {}
268 bool DesktopCaptureCancelChooseDesktopMediaFunction::RunSync() {
270 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &request_id));
272 DesktopCaptureRequestsRegistry::GetInstance()->CancelRequest(
273 render_view_host()->GetProcess()->GetID(), request_id);
277 DesktopCaptureRequestsRegistry::DesktopCaptureRequestsRegistry() {}
278 DesktopCaptureRequestsRegistry::~DesktopCaptureRequestsRegistry() {}
281 DesktopCaptureRequestsRegistry* DesktopCaptureRequestsRegistry::GetInstance() {
282 return Singleton<DesktopCaptureRequestsRegistry>::get();
285 void DesktopCaptureRequestsRegistry::AddRequest(
288 DesktopCaptureChooseDesktopMediaFunction* handler) {
290 RequestsMap::value_type(RequestId(process_id, request_id), handler));
293 void DesktopCaptureRequestsRegistry::RemoveRequest(int process_id,
295 requests_.erase(RequestId(process_id, request_id));
298 void DesktopCaptureRequestsRegistry::CancelRequest(int process_id,
300 RequestsMap::iterator it = requests_.find(RequestId(process_id, request_id));
301 if (it != requests_.end())
302 it->second->Cancel();
306 } // namespace extensions