Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / desktop_capture / desktop_capture_api.cc
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/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"
29
30 namespace extensions {
31
32 namespace {
33
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.";
44
45 DesktopCaptureChooseDesktopMediaFunction::PickerFactory* g_picker_factory =
46     NULL;
47
48 }  // namespace
49
50 // static
51 void DesktopCaptureChooseDesktopMediaFunction::SetPickerFactoryForTests(
52     PickerFactory* factory) {
53   g_picker_factory = factory;
54 }
55
56 DesktopCaptureChooseDesktopMediaFunction::
57     DesktopCaptureChooseDesktopMediaFunction() {
58 }
59
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_);
66   }
67 }
68
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
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   // |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) {
99       error_ = kNoUrlError;
100       return false;
101     }
102     origin_ = GURL(*(params->target_tab->url)).GetOrigin();
103
104     if (!origin_.is_valid()) {
105       error_ = kInvalidOriginError;
106       return false;
107     }
108
109     if (!CommandLine::ForCurrentProcess()->HasSwitch(
110             switches::kAllowHttpScreenCapture) &&
111         !origin_.SchemeIsSecure()) {
112       error_ = kTabUrlNotSecure;
113       return false;
114     }
115     target_name = base::UTF8ToUTF16(origin_.SchemeIsSecure() ?
116         net::GetHostAndOptionalPort(origin_) : origin_.spec());
117
118     if (!params->target_tab->id) {
119       error_ = kNoTabIdError;
120       return false;
121     }
122
123     if (!ExtensionTabUtil::GetTabById(*(params->target_tab->id), GetProfile(),
124                                       true, NULL, NULL, &web_contents, NULL)) {
125       error_ = kInvalidTabIdError;
126       return false;
127     }
128     DCHECK(web_contents);
129   } else {
130     origin_ = extension()->url();
131     target_name = base::UTF8ToUTF16(extension()->name());
132     web_contents = content::WebContents::FromRenderViewHost(render_view_host());
133     DCHECK(web_contents);
134   }
135
136   // Register to be notified when the tab is closed.
137   Observe(web_contents);
138
139   bool show_screens = false;
140   bool show_windows = false;
141
142   for (std::vector<api::desktop_capture::DesktopCaptureSourceType>::iterator
143        it = params->sources.begin(); it != params->sources.end(); ++it) {
144     switch (*it) {
145       case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_NONE:
146         error_ = kInvalidSourceNameError;
147         return false;
148
149       case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_SCREEN:
150         show_screens = true;
151         break;
152
153       case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_WINDOW:
154         show_windows = true;
155         break;
156
157       case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_TAB:
158         error_ = kTabCaptureNotSupportedError;
159         return false;
160     }
161   }
162
163   if (!show_screens && !show_windows) {
164     error_ = kEmptySourcesListError;
165     return false;
166   }
167
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();
175   } else {
176 #if defined(USE_ASH)
177     if (chrome::IsNativeWindowInAsh(parent_window)) {
178       media_list.reset(new DesktopMediaListAsh(
179           (show_screens ? DesktopMediaListAsh::SCREENS : 0) |
180           (show_windows ? DesktopMediaListAsh::WINDOWS : 0)));
181     } else
182 #endif
183     {
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);
191
192       media_list.reset(new NativeDesktopMediaList(
193           screen_capturer.Pass(), window_capturer.Pass()));
194     }
195
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();
200 #else
201     error_ = "Desktop Capture API is not yet implemented for this platform.";
202     return false;
203 #endif
204   }
205   DesktopMediaPicker::DoneCallback callback = base::Bind(
206       &DesktopCaptureChooseDesktopMediaFunction::OnPickerDialogResults, this);
207
208   picker_->Show(web_contents,
209                 parent_window,
210                 parent_window,
211                 base::UTF8ToUTF16(extension()->name()),
212                 target_name,
213                 media_list.Pass(),
214                 callback);
215   return true;
216 }
217
218 void DesktopCaptureChooseDesktopMediaFunction::WebContentsDestroyed() {
219   Cancel();
220 }
221
222 void DesktopCaptureChooseDesktopMediaFunction::OnPickerDialogResults(
223     content::DesktopMediaID source) {
224   std::string result;
225   if (source.type != content::DesktopMediaID::TYPE_NONE &&
226       web_contents()) {
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(),
238                                       origin_,
239                                       source,
240                                       extension()->name());
241   }
242
243   SetResult(new base::StringValue(result));
244   SendResponse(true);
245 }
246
247 DesktopCaptureRequestsRegistry::RequestId::RequestId(int process_id,
248                                                      int request_id)
249     : process_id(process_id),
250       request_id(request_id) {
251 }
252
253 bool DesktopCaptureRequestsRegistry::RequestId::operator<(
254     const RequestId& other) const {
255   if (process_id != other.process_id) {
256     return process_id < other.process_id;
257   } else {
258     return request_id < other.request_id;
259   }
260 }
261
262 DesktopCaptureCancelChooseDesktopMediaFunction::
263     DesktopCaptureCancelChooseDesktopMediaFunction() {}
264
265 DesktopCaptureCancelChooseDesktopMediaFunction::
266     ~DesktopCaptureCancelChooseDesktopMediaFunction() {}
267
268 bool DesktopCaptureCancelChooseDesktopMediaFunction::RunSync() {
269   int request_id;
270   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &request_id));
271
272   DesktopCaptureRequestsRegistry::GetInstance()->CancelRequest(
273       render_view_host()->GetProcess()->GetID(), request_id);
274   return true;
275 }
276
277 DesktopCaptureRequestsRegistry::DesktopCaptureRequestsRegistry() {}
278 DesktopCaptureRequestsRegistry::~DesktopCaptureRequestsRegistry() {}
279
280 // static
281 DesktopCaptureRequestsRegistry* DesktopCaptureRequestsRegistry::GetInstance() {
282   return Singleton<DesktopCaptureRequestsRegistry>::get();
283 }
284
285 void DesktopCaptureRequestsRegistry::AddRequest(
286     int process_id,
287     int request_id,
288     DesktopCaptureChooseDesktopMediaFunction* handler) {
289   requests_.insert(
290       RequestsMap::value_type(RequestId(process_id, request_id), handler));
291 }
292
293 void DesktopCaptureRequestsRegistry::RemoveRequest(int process_id,
294                                                    int request_id) {
295   requests_.erase(RequestId(process_id, request_id));
296 }
297
298 void DesktopCaptureRequestsRegistry::CancelRequest(int process_id,
299                                                    int request_id) {
300   RequestsMap::iterator it = requests_.find(RequestId(process_id, request_id));
301   if (it != requests_.end())
302     it->second->Cancel();
303 }
304
305
306 }  // namespace extensions