• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/extensions/api/desktop_capture/desktop_capture_api.h"
6 
7 #include "ash/shell.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/ui/ash/ash_util.h"
17 #include "chrome/browser/ui/host_desktop.h"
18 #include "chrome/common/chrome_switches.h"
19 #include "chrome/common/extensions/api/tabs.h"
20 #include "content/public/browser/render_process_host.h"
21 #include "content/public/browser/render_view_host.h"
22 #include "content/public/browser/web_contents.h"
23 #include "net/base/net_util.h"
24 #include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h"
25 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
26 #include "third_party/webrtc/modules/desktop_capture/window_capturer.h"
27 
28 namespace extensions {
29 
30 namespace {
31 
32 const char kInvalidSourceNameError[] = "Invalid source type specified.";
33 const char kEmptySourcesListError[] =
34     "At least one source type must be specified.";
35 const char kTabCaptureNotSupportedError[] = "Tab capture is not supported yet.";
36 const char kNoTabIdError[] = "targetTab doesn't have id field set.";
37 const char kNoUrlError[] = "targetTab doesn't have URL field set.";
38 const char kInvalidTabIdError[] = "Invalid tab specified.";
39 const char kTabUrlChangedError[] = "URL for the specified tab has changed.";
40 const char kTabUrlNotSecure[] =
41     "URL scheme for the specified tab is not secure.";
42 
43 DesktopCaptureChooseDesktopMediaFunction::PickerFactory* g_picker_factory =
44     NULL;
45 
46 }  // namespace
47 
48 // static
SetPickerFactoryForTests(PickerFactory * factory)49 void DesktopCaptureChooseDesktopMediaFunction::SetPickerFactoryForTests(
50     PickerFactory* factory) {
51   g_picker_factory = factory;
52 }
53 
54 DesktopCaptureChooseDesktopMediaFunction::
DesktopCaptureChooseDesktopMediaFunction()55     DesktopCaptureChooseDesktopMediaFunction()
56     : render_process_id_(0),
57       render_view_id_(0) {
58 }
59 
60 DesktopCaptureChooseDesktopMediaFunction::
~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_);
66   }
67 }
68 
Cancel()69 void DesktopCaptureChooseDesktopMediaFunction::Cancel() {
70   // Keep reference to |this| to ensure the object doesn't get destroyed before
71   // we return.
72   scoped_refptr<DesktopCaptureChooseDesktopMediaFunction> self(this);
73   if (picker_) {
74     picker_.reset();
75     SetResult(new base::StringValue(std::string()));
76     SendResponse(true);
77   }
78 }
79 
RunAsync()80 bool DesktopCaptureChooseDesktopMediaFunction::RunAsync() {
81   EXTENSION_FUNCTION_VALIDATE(args_->GetSize() > 0);
82 
83   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &request_id_));
84   args_->Remove(0, NULL);
85 
86   scoped_ptr<api::desktop_capture::ChooseDesktopMedia::Params> params =
87       api::desktop_capture::ChooseDesktopMedia::Params::Create(*args_);
88   EXTENSION_FUNCTION_VALIDATE(params.get());
89 
90   DesktopCaptureRequestsRegistry::GetInstance()->AddRequest(
91       render_view_host()->GetProcess()->GetID(), request_id_, this);
92 
93   gfx::NativeWindow parent_window = NULL;
94   content::RenderViewHost* render_view = NULL;
95   content::WebContents* web_contents = NULL;
96   base::string16 target_name;
97   if (params->target_tab) {
98     if (!params->target_tab->url) {
99       error_ = kNoUrlError;
100       return false;
101     }
102     origin_ = GURL(*(params->target_tab->url)).GetOrigin();
103 
104     if (!CommandLine::ForCurrentProcess()->HasSwitch(
105             switches::kAllowHttpScreenCapture) &&
106         !origin_.SchemeIsSecure()) {
107       error_ = kTabUrlNotSecure;
108       return false;
109     }
110     target_name = base::UTF8ToUTF16(origin_.SchemeIsSecure() ?
111         net::GetHostAndOptionalPort(origin_) : origin_.spec());
112 
113     if (!params->target_tab->id) {
114       error_ = kNoTabIdError;
115       return false;
116     }
117 
118     if (!ExtensionTabUtil::GetTabById(*(params->target_tab->id), GetProfile(),
119                                       true, NULL, NULL, &web_contents, NULL)) {
120       error_ = kInvalidTabIdError;
121       return false;
122     }
123 
124     GURL current_origin_ =
125         web_contents->GetLastCommittedURL().GetOrigin();
126     if (current_origin_ != origin_) {
127       error_ = kTabUrlChangedError;
128       return false;
129     }
130 
131     // Register to be notified when the tab is closed.
132     Observe(web_contents);
133 
134     render_view = web_contents->GetRenderViewHost();
135     parent_window = web_contents->GetTopLevelNativeWindow();
136   } else {
137     origin_ = GetExtension()->url();
138     target_name = base::UTF8ToUTF16(GetExtension()->name());
139     render_view = render_view_host();
140 
141     web_contents = GetAssociatedWebContents();
142     if (web_contents) {
143       parent_window = web_contents->GetTopLevelNativeWindow();
144     } else {
145 #if defined(USE_ASH)
146       if (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH)
147         parent_window = ash::Shell::GetPrimaryRootWindow();
148 #endif
149     }
150   }
151 
152   render_process_id_ = render_view->GetProcess()->GetID();
153   render_view_id_ = render_view->GetRoutingID();
154 
155   bool show_screens = false;
156   bool show_windows = false;
157 
158   for (std::vector<api::desktop_capture::DesktopCaptureSourceType>::iterator
159        it = params->sources.begin(); it != params->sources.end(); ++it) {
160     switch (*it) {
161       case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_NONE:
162         error_ = kInvalidSourceNameError;
163         return false;
164 
165       case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_SCREEN:
166         show_screens = true;
167         break;
168 
169       case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_WINDOW:
170         show_windows = true;
171         break;
172 
173       case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_TAB:
174         error_ = kTabCaptureNotSupportedError;
175         return false;
176     }
177   }
178 
179   if (!show_screens && !show_windows) {
180     error_ = kEmptySourcesListError;
181     return false;
182   }
183 
184   scoped_ptr<DesktopMediaList> media_list;
185   if (g_picker_factory) {
186     media_list = g_picker_factory->CreateModel(
187         show_screens, show_windows);
188     picker_ = g_picker_factory->CreatePicker();
189   } else {
190 #if defined(USE_ASH)
191     if (chrome::IsNativeWindowInAsh(parent_window)) {
192       media_list.reset(new DesktopMediaListAsh(
193           (show_screens ? DesktopMediaListAsh::SCREENS : 0) |
194           (show_windows ? DesktopMediaListAsh::WINDOWS : 0)));
195     } else
196 #endif
197     {
198       webrtc::DesktopCaptureOptions options =
199           webrtc::DesktopCaptureOptions::CreateDefault();
200       options.set_disable_effects(false);
201       scoped_ptr<webrtc::ScreenCapturer> screen_capturer(
202           show_screens ? webrtc::ScreenCapturer::Create(options) : NULL);
203       scoped_ptr<webrtc::WindowCapturer> window_capturer(
204           show_windows ? webrtc::WindowCapturer::Create(options) : NULL);
205 
206       media_list.reset(new NativeDesktopMediaList(
207           screen_capturer.Pass(), window_capturer.Pass()));
208     }
209 
210     // DesktopMediaPicker is implemented only for Windows, OSX and
211     // Aura Linux builds.
212 #if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX)
213     picker_ = DesktopMediaPicker::Create();
214 #else
215     error_ = "Desktop Capture API is not yet implemented for this platform.";
216     return false;
217 #endif
218   }
219   DesktopMediaPicker::DoneCallback callback = base::Bind(
220       &DesktopCaptureChooseDesktopMediaFunction::OnPickerDialogResults, this);
221 
222   picker_->Show(web_contents,
223                 parent_window, parent_window,
224                 base::UTF8ToUTF16(GetExtension()->name()),
225                 target_name,
226                 media_list.Pass(), callback);
227   return true;
228 }
229 
WebContentsDestroyed()230 void DesktopCaptureChooseDesktopMediaFunction::WebContentsDestroyed() {
231   Cancel();
232 }
233 
OnPickerDialogResults(content::DesktopMediaID source)234 void DesktopCaptureChooseDesktopMediaFunction::OnPickerDialogResults(
235     content::DesktopMediaID source) {
236   std::string result;
237   if (source.type != content::DesktopMediaID::TYPE_NONE) {
238     DesktopStreamsRegistry* registry =
239         MediaCaptureDevicesDispatcher::GetInstance()->
240         GetDesktopStreamsRegistry();
241     result = registry->RegisterStream(
242         render_process_id_,
243         render_view_id_,
244         origin_,
245         source,
246         GetExtension()->name());
247   }
248 
249   SetResult(new base::StringValue(result));
250   SendResponse(true);
251 }
252 
RequestId(int process_id,int request_id)253 DesktopCaptureRequestsRegistry::RequestId::RequestId(int process_id,
254                                                      int request_id)
255     : process_id(process_id),
256       request_id(request_id) {
257 }
258 
operator <(const RequestId & other) const259 bool DesktopCaptureRequestsRegistry::RequestId::operator<(
260     const RequestId& other) const {
261   if (process_id != other.process_id) {
262     return process_id < other.process_id;
263   } else {
264     return request_id < other.request_id;
265   }
266 }
267 
268 DesktopCaptureCancelChooseDesktopMediaFunction::
DesktopCaptureCancelChooseDesktopMediaFunction()269     DesktopCaptureCancelChooseDesktopMediaFunction() {}
270 
271 DesktopCaptureCancelChooseDesktopMediaFunction::
~DesktopCaptureCancelChooseDesktopMediaFunction()272     ~DesktopCaptureCancelChooseDesktopMediaFunction() {}
273 
RunSync()274 bool DesktopCaptureCancelChooseDesktopMediaFunction::RunSync() {
275   int request_id;
276   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &request_id));
277 
278   DesktopCaptureRequestsRegistry::GetInstance()->CancelRequest(
279       render_view_host()->GetProcess()->GetID(), request_id);
280   return true;
281 }
282 
DesktopCaptureRequestsRegistry()283 DesktopCaptureRequestsRegistry::DesktopCaptureRequestsRegistry() {}
~DesktopCaptureRequestsRegistry()284 DesktopCaptureRequestsRegistry::~DesktopCaptureRequestsRegistry() {}
285 
286 // static
GetInstance()287 DesktopCaptureRequestsRegistry* DesktopCaptureRequestsRegistry::GetInstance() {
288   return Singleton<DesktopCaptureRequestsRegistry>::get();
289 }
290 
AddRequest(int process_id,int request_id,DesktopCaptureChooseDesktopMediaFunction * handler)291 void DesktopCaptureRequestsRegistry::AddRequest(
292     int process_id,
293     int request_id,
294     DesktopCaptureChooseDesktopMediaFunction* handler) {
295   requests_.insert(
296       RequestsMap::value_type(RequestId(process_id, request_id), handler));
297 }
298 
RemoveRequest(int process_id,int request_id)299 void DesktopCaptureRequestsRegistry::RemoveRequest(int process_id,
300                                                    int request_id) {
301   requests_.erase(RequestId(process_id, request_id));
302 }
303 
CancelRequest(int process_id,int request_id)304 void DesktopCaptureRequestsRegistry::CancelRequest(int process_id,
305                                                    int request_id) {
306   RequestsMap::iterator it = requests_.find(RequestId(process_id, request_id));
307   if (it != requests_.end())
308     it->second->Cancel();
309 }
310 
311 
312 }  // namespace extensions
313