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_streams_registry.h"
11 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
12 #include "chrome/common/extensions/api/tabs.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/browser/render_process_host.h"
15 #include "content/public/browser/render_view_host.h"
16 #include "content/public/browser/web_contents.h"
17 #include "content/public/browser/web_contents_view.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.";
29 const char kNoTabIdError[] = "targetTab doesn't have id field set.";
30 const char kNoUrlError[] = "targetTab doesn't have URL field set.";
31 const char kInvalidTabIdError[] = "Invalid tab specified.";
32 const char kTabUrlChangedError[] = "URL for the specified tab has changed.";
34 DesktopCaptureChooseDesktopMediaFunction::PickerFactory* g_picker_factory =
40 void DesktopCaptureChooseDesktopMediaFunction::SetPickerFactoryForTests(
41 PickerFactory* factory) {
42 g_picker_factory = factory;
45 DesktopCaptureChooseDesktopMediaFunction::
46 DesktopCaptureChooseDesktopMediaFunction()
47 : render_process_id_(0),
51 DesktopCaptureChooseDesktopMediaFunction::
52 ~DesktopCaptureChooseDesktopMediaFunction() {
53 // RenderViewHost may be already destroyed.
54 if (render_view_host()) {
55 DesktopCaptureRequestsRegistry::GetInstance()->RemoveRequest(
56 render_view_host()->GetProcess()->GetID(), request_id_);
60 void DesktopCaptureChooseDesktopMediaFunction::Cancel() {
61 // Keep reference to |this| to ensure the object doesn't get destroyed before
63 scoped_refptr<DesktopCaptureChooseDesktopMediaFunction> self(this);
66 SetResult(new base::StringValue(std::string()));
71 bool DesktopCaptureChooseDesktopMediaFunction::RunImpl() {
72 EXTENSION_FUNCTION_VALIDATE(args_->GetSize() > 0);
74 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &request_id_));
75 args_->Remove(0, NULL);
77 scoped_ptr<api::desktop_capture::ChooseDesktopMedia::Params> params =
78 api::desktop_capture::ChooseDesktopMedia::Params::Create(*args_);
79 EXTENSION_FUNCTION_VALIDATE(params.get());
81 DesktopCaptureRequestsRegistry::GetInstance()->AddRequest(
82 render_view_host()->GetProcess()->GetID(), request_id_, this);
84 gfx::NativeWindow parent_window;
85 content::RenderViewHost* render_view;
86 if (params->target_tab) {
87 if (!params->target_tab->url) {
91 origin_ = GURL(*(params->target_tab->url)).GetOrigin();
93 if (!params->target_tab->id) {
94 error_ = kNoTabIdError;
98 content::WebContents* web_contents = NULL;
99 if (!ExtensionTabUtil::GetTabById(*(params->target_tab->id),
106 error_ = kInvalidTabIdError;
110 GURL current_origin_ =
111 web_contents->GetLastCommittedURL().GetOrigin();
112 if (current_origin_ != origin_) {
113 error_ = kTabUrlChangedError;
117 render_view = web_contents->GetRenderViewHost();
118 parent_window = web_contents->GetView()->GetTopLevelNativeWindow();
120 origin_ = GetExtension()->url();
121 render_view = render_view_host();
123 GetAssociatedWebContents()->GetView()->GetTopLevelNativeWindow();
125 render_process_id_ = render_view->GetProcess()->GetID();
126 render_view_id_ = render_view->GetRoutingID();
128 scoped_ptr<webrtc::ScreenCapturer> screen_capturer;
129 scoped_ptr<webrtc::WindowCapturer> window_capturer;
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:
140 // ScreenCapturerWin disables Aero by default.
141 screen_capturer.reset(
142 webrtc::ScreenCapturer::CreateWithDisableAero(false));
144 screen_capturer.reset(webrtc::ScreenCapturer::Create());
148 case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_WINDOW:
149 window_capturer.reset(webrtc::WindowCapturer::Create());
152 case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_TAB:
153 error_ = kTabCaptureNotSupportedError;
158 if (!screen_capturer && !window_capturer) {
159 error_ = kEmptySourcesListError;
163 scoped_ptr<DesktopMediaPickerModel> model;
164 if (g_picker_factory) {
165 model = g_picker_factory->CreateModel(
166 screen_capturer.Pass(), window_capturer.Pass());
167 picker_ = g_picker_factory->CreatePicker();
169 // DesktopMediaPicker is implemented only for Windows, OSX and
170 // Aura Linux builds.
171 #if (defined(TOOLKIT_VIEWS) && !defined(OS_CHROMEOS)) || defined(OS_MACOSX)
172 model.reset(new DesktopMediaPickerModelImpl(
173 screen_capturer.Pass(), window_capturer.Pass()));
174 picker_ = DesktopMediaPicker::Create();
176 const char kNotImplementedError[] =
177 "Desktop Capture API is not yet implemented for this platform.";
178 error_ = kNotImplementedError;
182 DesktopMediaPicker::DoneCallback callback = base::Bind(
183 &DesktopCaptureChooseDesktopMediaFunction::OnPickerDialogResults, this);
185 picker_->Show(parent_window, parent_window,
186 UTF8ToUTF16(GetExtension()->name()),
187 model.Pass(), callback);
191 void DesktopCaptureChooseDesktopMediaFunction::OnPickerDialogResults(
192 content::DesktopMediaID source) {
194 if (source.type != content::DesktopMediaID::TYPE_NONE) {
195 DesktopStreamsRegistry* registry =
196 MediaCaptureDevicesDispatcher::GetInstance()->
197 GetDesktopStreamsRegistry();
198 result = registry->RegisterStream(
199 render_process_id_, render_view_id_, origin_, source);
202 SetResult(new base::StringValue(result));
206 DesktopCaptureRequestsRegistry::RequestId::RequestId(int process_id,
208 : process_id(process_id),
209 request_id(request_id) {
212 bool DesktopCaptureRequestsRegistry::RequestId::operator<(
213 const RequestId& other) const {
214 if (process_id != other.process_id) {
215 return process_id < other.process_id;
217 return request_id < other.request_id;
221 DesktopCaptureCancelChooseDesktopMediaFunction::
222 DesktopCaptureCancelChooseDesktopMediaFunction() {}
224 DesktopCaptureCancelChooseDesktopMediaFunction::
225 ~DesktopCaptureCancelChooseDesktopMediaFunction() {}
227 bool DesktopCaptureCancelChooseDesktopMediaFunction::RunImpl() {
229 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &request_id));
231 DesktopCaptureRequestsRegistry::GetInstance()->CancelRequest(
232 render_view_host()->GetProcess()->GetID(), request_id);
236 DesktopCaptureRequestsRegistry::DesktopCaptureRequestsRegistry() {}
237 DesktopCaptureRequestsRegistry::~DesktopCaptureRequestsRegistry() {}
240 DesktopCaptureRequestsRegistry* DesktopCaptureRequestsRegistry::GetInstance() {
241 return Singleton<DesktopCaptureRequestsRegistry>::get();
244 void DesktopCaptureRequestsRegistry::AddRequest(
247 DesktopCaptureChooseDesktopMediaFunction* handler) {
249 RequestsMap::value_type(RequestId(process_id, request_id), handler));
252 void DesktopCaptureRequestsRegistry::RemoveRequest(int process_id,
254 requests_.erase(RequestId(process_id, request_id));
257 void DesktopCaptureRequestsRegistry::CancelRequest(int process_id,
259 RequestsMap::iterator it = requests_.find(RequestId(process_id, request_id));
260 if (it != requests_.end())
261 it->second->Cancel();
265 } // namespace extensions