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/render_process_host.h"
17 #include "content/public/browser/render_view_host.h"
18 #include "content/public/browser/web_contents.h"
19 #include "content/public/browser/web_contents_view.h"
20 #include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h"
21 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
22 #include "third_party/webrtc/modules/desktop_capture/window_capturer.h"
24 namespace extensions {
28 const char kInvalidSourceNameError[] = "Invalid source type specified.";
29 const char kEmptySourcesListError[] =
30 "At least one source type must be specified.";
31 const char kTabCaptureNotSupportedError[] = "Tab capture is not supported yet.";
32 const char kNoTabIdError[] = "targetTab doesn't have id field set.";
33 const char kNoUrlError[] = "targetTab doesn't have URL field set.";
34 const char kInvalidTabIdError[] = "Invalid tab specified.";
35 const char kTabUrlChangedError[] = "URL for the specified tab has changed.";
37 DesktopCaptureChooseDesktopMediaFunction::PickerFactory* g_picker_factory =
43 void DesktopCaptureChooseDesktopMediaFunction::SetPickerFactoryForTests(
44 PickerFactory* factory) {
45 g_picker_factory = factory;
48 DesktopCaptureChooseDesktopMediaFunction::
49 DesktopCaptureChooseDesktopMediaFunction()
50 : render_process_id_(0),
54 DesktopCaptureChooseDesktopMediaFunction::
55 ~DesktopCaptureChooseDesktopMediaFunction() {
56 // RenderViewHost may be already destroyed.
57 if (render_view_host()) {
58 DesktopCaptureRequestsRegistry::GetInstance()->RemoveRequest(
59 render_view_host()->GetProcess()->GetID(), request_id_);
63 void DesktopCaptureChooseDesktopMediaFunction::Cancel() {
64 // Keep reference to |this| to ensure the object doesn't get destroyed before
66 scoped_refptr<DesktopCaptureChooseDesktopMediaFunction> self(this);
69 SetResult(new base::StringValue(std::string()));
74 bool DesktopCaptureChooseDesktopMediaFunction::RunImpl() {
75 EXTENSION_FUNCTION_VALIDATE(args_->GetSize() > 0);
77 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &request_id_));
78 args_->Remove(0, NULL);
80 scoped_ptr<api::desktop_capture::ChooseDesktopMedia::Params> params =
81 api::desktop_capture::ChooseDesktopMedia::Params::Create(*args_);
82 EXTENSION_FUNCTION_VALIDATE(params.get());
84 DesktopCaptureRequestsRegistry::GetInstance()->AddRequest(
85 render_view_host()->GetProcess()->GetID(), request_id_, this);
87 gfx::NativeWindow parent_window;
88 content::RenderViewHost* render_view;
89 if (params->target_tab) {
90 if (!params->target_tab->url) {
94 origin_ = GURL(*(params->target_tab->url)).GetOrigin();
96 if (!params->target_tab->id) {
97 error_ = kNoTabIdError;
101 content::WebContents* web_contents = NULL;
102 if (!ExtensionTabUtil::GetTabById(*(params->target_tab->id), GetProfile(),
103 true, NULL, NULL, &web_contents, NULL)) {
104 error_ = kInvalidTabIdError;
108 GURL current_origin_ =
109 web_contents->GetLastCommittedURL().GetOrigin();
110 if (current_origin_ != origin_) {
111 error_ = kTabUrlChangedError;
115 // Register to be notified when the tab is closed.
116 Observe(web_contents);
118 render_view = web_contents->GetRenderViewHost();
119 parent_window = web_contents->GetView()->GetTopLevelNativeWindow();
121 origin_ = GetExtension()->url();
122 render_view = render_view_host();
124 GetAssociatedWebContents()->GetView()->GetTopLevelNativeWindow();
126 render_process_id_ = render_view->GetProcess()->GetID();
127 render_view_id_ = render_view->GetRoutingID();
129 bool show_screens = false;
130 bool show_windows = false;
132 for (std::vector<api::desktop_capture::DesktopCaptureSourceType>::iterator
133 it = params->sources.begin(); it != params->sources.end(); ++it) {
135 case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_NONE:
136 error_ = kInvalidSourceNameError;
139 case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_SCREEN:
143 case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_WINDOW:
147 case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_TAB:
148 error_ = kTabCaptureNotSupportedError;
153 if (!show_screens && !show_windows) {
154 error_ = kEmptySourcesListError;
158 scoped_ptr<DesktopMediaList> media_list;
159 if (g_picker_factory) {
160 media_list = g_picker_factory->CreateModel(
161 show_screens, show_windows);
162 picker_ = g_picker_factory->CreatePicker();
165 if (chrome::IsNativeWindowInAsh(parent_window)) {
166 media_list.reset(new DesktopMediaListAsh(
167 (show_screens ? DesktopMediaListAsh::SCREENS : 0) |
168 (show_windows ? DesktopMediaListAsh::WINDOWS : 0)));
172 webrtc::DesktopCaptureOptions options =
173 webrtc::DesktopCaptureOptions::CreateDefault();
174 options.set_disable_effects(false);
175 scoped_ptr<webrtc::ScreenCapturer> screen_capturer(
176 show_screens ? webrtc::ScreenCapturer::Create(options) : NULL);
177 scoped_ptr<webrtc::WindowCapturer> window_capturer(
178 show_windows ? webrtc::WindowCapturer::Create(options) : NULL);
180 media_list.reset(new NativeDesktopMediaList(
181 screen_capturer.Pass(), window_capturer.Pass()));
184 // DesktopMediaPicker is implemented only for Windows, OSX and
185 // Aura Linux builds.
186 #if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX)
187 picker_ = DesktopMediaPicker::Create();
189 error_ = "Desktop Capture API is not yet implemented for this platform.";
193 DesktopMediaPicker::DoneCallback callback = base::Bind(
194 &DesktopCaptureChooseDesktopMediaFunction::OnPickerDialogResults, this);
196 picker_->Show(parent_window, parent_window,
197 base::UTF8ToUTF16(GetExtension()->name()),
198 media_list.Pass(), callback);
202 void DesktopCaptureChooseDesktopMediaFunction::WebContentsDestroyed(
203 content::WebContents* web_contents) {
207 void DesktopCaptureChooseDesktopMediaFunction::OnPickerDialogResults(
208 content::DesktopMediaID source) {
210 if (source.type != content::DesktopMediaID::TYPE_NONE) {
211 DesktopStreamsRegistry* registry =
212 MediaCaptureDevicesDispatcher::GetInstance()->
213 GetDesktopStreamsRegistry();
214 result = registry->RegisterStream(
215 render_process_id_, render_view_id_, origin_, source);
218 SetResult(new base::StringValue(result));
222 DesktopCaptureRequestsRegistry::RequestId::RequestId(int process_id,
224 : process_id(process_id),
225 request_id(request_id) {
228 bool DesktopCaptureRequestsRegistry::RequestId::operator<(
229 const RequestId& other) const {
230 if (process_id != other.process_id) {
231 return process_id < other.process_id;
233 return request_id < other.request_id;
237 DesktopCaptureCancelChooseDesktopMediaFunction::
238 DesktopCaptureCancelChooseDesktopMediaFunction() {}
240 DesktopCaptureCancelChooseDesktopMediaFunction::
241 ~DesktopCaptureCancelChooseDesktopMediaFunction() {}
243 bool DesktopCaptureCancelChooseDesktopMediaFunction::RunImpl() {
245 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &request_id));
247 DesktopCaptureRequestsRegistry::GetInstance()->CancelRequest(
248 render_view_host()->GetProcess()->GetID(), request_id);
252 DesktopCaptureRequestsRegistry::DesktopCaptureRequestsRegistry() {}
253 DesktopCaptureRequestsRegistry::~DesktopCaptureRequestsRegistry() {}
256 DesktopCaptureRequestsRegistry* DesktopCaptureRequestsRegistry::GetInstance() {
257 return Singleton<DesktopCaptureRequestsRegistry>::get();
260 void DesktopCaptureRequestsRegistry::AddRequest(
263 DesktopCaptureChooseDesktopMediaFunction* handler) {
265 RequestsMap::value_type(RequestId(process_id, request_id), handler));
268 void DesktopCaptureRequestsRegistry::RemoveRequest(int process_id,
270 requests_.erase(RequestId(process_id, request_id));
273 void DesktopCaptureRequestsRegistry::CancelRequest(int process_id,
275 RequestsMap::iterator it = requests_.find(RequestId(process_id, request_id));
276 if (it != requests_.end())
277 it->second->Cancel();
281 } // namespace extensions