Home | History | Annotate | Download | only in desktop_capture
      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
     49 void DesktopCaptureChooseDesktopMediaFunction::SetPickerFactoryForTests(
     50     PickerFactory* factory) {
     51   g_picker_factory = factory;
     52 }
     53 
     54 DesktopCaptureChooseDesktopMediaFunction::
     55     DesktopCaptureChooseDesktopMediaFunction()
     56     : render_process_id_(0),
     57       render_view_id_(0) {
     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   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 
    230 void DesktopCaptureChooseDesktopMediaFunction::WebContentsDestroyed() {
    231   Cancel();
    232 }
    233 
    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 
    253 DesktopCaptureRequestsRegistry::RequestId::RequestId(int process_id,
    254                                                      int request_id)
    255     : process_id(process_id),
    256       request_id(request_id) {
    257 }
    258 
    259 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::
    269     DesktopCaptureCancelChooseDesktopMediaFunction() {}
    270 
    271 DesktopCaptureCancelChooseDesktopMediaFunction::
    272     ~DesktopCaptureCancelChooseDesktopMediaFunction() {}
    273 
    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 
    283 DesktopCaptureRequestsRegistry::DesktopCaptureRequestsRegistry() {}
    284 DesktopCaptureRequestsRegistry::~DesktopCaptureRequestsRegistry() {}
    285 
    286 // static
    287 DesktopCaptureRequestsRegistry* DesktopCaptureRequestsRegistry::GetInstance() {
    288   return Singleton<DesktopCaptureRequestsRegistry>::get();
    289 }
    290 
    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 
    299 void DesktopCaptureRequestsRegistry::RemoveRequest(int process_id,
    300                                                    int request_id) {
    301   requests_.erase(RequestId(process_id, request_id));
    302 }
    303 
    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