text: // Copyright 2015 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "chrome/browser/extensions/api/desktop_capture/desktop_capture_base.h"
  
  #include <tuple>
  #include <utility>
  
  #include "base/command_line.h"
  #include "base/memory/ptr_util.h"
  #include "base/strings/utf_string_conversions.h"
  #include "build/build_config.h"
  #include "chrome/browser/extensions/extension_tab_util.h"
  #include "chrome/browser/media/desktop_media_list_ash.h"
  #include "chrome/browser/media/desktop_streams_registry.h"
  #include "chrome/browser/media/media_capture_devices_dispatcher.h"
  #include "chrome/browser/media/native_desktop_media_list.h"
  #include "chrome/browser/media/tab_desktop_media_list.h"
  #include "chrome/common/channel_info.h"
  #include "components/version_info/version_info.h"
  #include "content/public/browser/render_frame_host.h"
  #include "content/public/browser/render_process_host.h"
  #include "content/public/browser/web_contents.h"
  #include "extensions/common/switches.h"
  #include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h"
  #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
  #include "third_party/webrtc/modules/desktop_capture/window_capturer.h"
  
  namespace extensions {
  
  namespace {
  
  const char kInvalidSourceNameError[] = "Invalid source type specified.";
  const char kEmptySourcesListError[] =
      "At least one source type must be specified.";
  
  DesktopCaptureChooseDesktopMediaFunctionBase::PickerFactory* g_picker_factory =
      NULL;
  
  }  // namespace
  
  // static
  void DesktopCaptureChooseDesktopMediaFunctionBase::SetPickerFactoryForTests(
      PickerFactory* factory) {
    g_picker_factory = factory;
  }
  
  DesktopCaptureChooseDesktopMediaFunctionBase::
      DesktopCaptureChooseDesktopMediaFunctionBase() {
  }
  
  DesktopCaptureChooseDesktopMediaFunctionBase::
      ~DesktopCaptureChooseDesktopMediaFunctionBase() {
    // RenderViewHost may be already destroyed.
    if (render_frame_host()) {
      DesktopCaptureRequestsRegistry::GetInstance()->RemoveRequest(
          render_frame_host()->GetProcess()->GetID(), request_id_);
    }
  }
  
  void DesktopCaptureChooseDesktopMediaFunctionBase::Cancel() {
    // Keep reference to |this| to ensure the object doesn't get destroyed before
    // we return.
    scoped_refptr<DesktopCaptureChooseDesktopMediaFunctionBase> self(this);
    if (picker_) {
      picker_.reset();
      SetResult(base::MakeUnique<base::StringValue>(std::string()));
      SendResponse(true);
    }
  }
  
  bool DesktopCaptureChooseDesktopMediaFunctionBase::Execute(
      const std::vector<api::desktop_capture::DesktopCaptureSourceType>& sources,
      content::WebContents* web_contents,
      const GURL& origin,
      const base::string16 target_name) {
    // Register to be notified when the tab is closed.
    Observe(web_contents);
  
    bool show_screens = false;
    bool show_windows = false;
    bool show_tabs = false;
    bool request_audio = false;
  
    for (auto source_type : sources) {
      switch (source_type) {
        case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_NONE:
          error_ = kInvalidSourceNameError;
          return false;
  
        case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_SCREEN:
          show_screens = true;
          break;
  
        case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_WINDOW:
          show_windows = true;
          break;
  
        case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_TAB:
          if (base::CommandLine::ForCurrentProcess()->HasSwitch(
                  extensions::switches::kEnableTabForDesktopShare)) {
            show_tabs = true;
          } else if (base::CommandLine::ForCurrentProcess()->HasSwitch(
              extensions::switches::kDisableTabForDesktopShare)) {
            show_tabs = false;
          } else {
            const version_info::Channel channel = chrome::GetChannel();
            if ((channel == version_info::Channel::STABLE) ||
                (channel == version_info::Channel::BETA)) {
              show_tabs = false;
            } else {
              show_tabs = true;
            }
          }
          break;
  
        case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_AUDIO:
          bool has_flag = base::CommandLine::ForCurrentProcess()->HasSwitch(
              extensions::switches::kDisableDesktopCaptureAudio);
          request_audio = !has_flag;
          break;
      }
    }
  
    if (!show_screens && !show_windows && !show_tabs) {
      error_ = kEmptySourcesListError;
      return false;
    }
  
    const gfx::NativeWindow parent_window =
        web_contents->GetTopLevelNativeWindow();
    std::unique_ptr<DesktopMediaList> screen_list;
    std::unique_ptr<DesktopMediaList> window_list;
    std::unique_ptr<DesktopMediaList> tab_list;
    if (g_picker_factory) {
      PickerFactory::MediaListArray media_lists =
          g_picker_factory->CreateModel(show_screens, show_windows, show_tabs,
                                        request_audio);
      screen_list = std::move(media_lists[0]);
      window_list = std::move(media_lists[1]);
      tab_list = std::move(media_lists[2]);
      picker_ = g_picker_factory->CreatePicker();
    } else {
      // Create a screens list.
      if (show_screens) {
  #if defined(USE_ASH)
        screen_list = base::WrapUnique(
            new DesktopMediaListAsh(DesktopMediaListAsh::SCREENS));
  #endif
        if (!screen_list) {
          webrtc::DesktopCaptureOptions options =
              webrtc::DesktopCaptureOptions::CreateDefault();
          options.set_disable_effects(false);
          std::unique_ptr<webrtc::ScreenCapturer> screen_capturer(
              webrtc::ScreenCapturer::Create(options));
  
          screen_list = base::WrapUnique(
              new NativeDesktopMediaList(std::move(screen_capturer), nullptr));
        }
      }
  
      // Create a windows list.
      if (show_windows) {
  #if defined(USE_ASH)
        window_list = base::WrapUnique(
            new DesktopMediaListAsh(DesktopMediaListAsh::WINDOWS));
  #endif
        if (!window_list) {
          webrtc::DesktopCaptureOptions options =
              webrtc::DesktopCaptureOptions::CreateDefault();
          options.set_disable_effects(false);
          std::unique_ptr<webrtc::WindowCapturer> window_capturer(
              webrtc::WindowCapturer::Create(options));
  
          window_list = base::WrapUnique(
              new NativeDesktopMediaList(nullptr, std::move(window_capturer)));
        }
      }
  
      if (show_tabs)
        tab_list = base::WrapUnique(new TabDesktopMediaList());
  
      DCHECK(screen_list || window_list || tab_list);
  
      // DesktopMediaPicker is implemented only for Windows, OSX and
      // Aura Linux builds.
  #if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX)
      picker_ = DesktopMediaPicker::Create();
  #else
      error_ = "Desktop Capture API is not yet implemented for this platform.";
      return false;
  #endif
    }
  
    DesktopMediaPicker::DoneCallback callback = base::Bind(
        &DesktopCaptureChooseDesktopMediaFunctionBase::OnPickerDialogResults,
        this);
  
    picker_->Show(web_contents, parent_window, parent_window,
                  base::UTF8ToUTF16(extension()->name()), target_name,
                  std::move(screen_list), std::move(window_list),
                  std::move(tab_list), request_audio, callback);
    origin_ = origin;
    return true;
  }
  
  void DesktopCaptureChooseDesktopMediaFunctionBase::WebContentsDestroyed() {
    Cancel();
  }
  
  void DesktopCaptureChooseDesktopMediaFunctionBase::OnPickerDialogResults(
      content::DesktopMediaID source) {
    std::string result;
    if (source.type != content::DesktopMediaID::TYPE_NONE &&
        web_contents()) {
      DesktopStreamsRegistry* registry =
          MediaCaptureDevicesDispatcher::GetInstance()->
          GetDesktopStreamsRegistry();
      // TODO(miu): Once render_frame_host() is being set, we should register the
      // exact RenderFrame requesting the stream, not the main RenderFrame.  With
      // that change, also update
      // MediaCaptureDevicesDispatcher::ProcessDesktopCaptureAccessRequest().
      // http://crbug.com/304341
      content::RenderFrameHost* const main_frame = web_contents()->GetMainFrame();
      result = registry->RegisterStream(main_frame->GetProcess()->GetID(),
                                        main_frame->GetRoutingID(),
                                        origin_,
                                        source,
                                        extension()->name());
    }
  
    SetResult(base::MakeUnique<base::StringValue>(result));
    SendResponse(true);
  }
  
  DesktopCaptureRequestsRegistry::RequestId::RequestId(int process_id,
                                                       int request_id)
      : process_id(process_id),
        request_id(request_id) {
  }
  
  bool DesktopCaptureRequestsRegistry::RequestId::operator<(
      const RequestId& other) const {
    return std::tie(process_id, request_id) <
           std::tie(other.process_id, other.request_id);
  }
  
  DesktopCaptureCancelChooseDesktopMediaFunctionBase::
      DesktopCaptureCancelChooseDesktopMediaFunctionBase() {}
  
  DesktopCaptureCancelChooseDesktopMediaFunctionBase::
      ~DesktopCaptureCancelChooseDesktopMediaFunctionBase() {}
  
  bool DesktopCaptureCancelChooseDesktopMediaFunctionBase::RunSync() {
    int request_id;
    EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &request_id));
  
    DesktopCaptureRequestsRegistry::GetInstance()->CancelRequest(
        render_frame_host()->GetProcess()->GetID(), request_id);
    return true;
  }
  
  DesktopCaptureRequestsRegistry::DesktopCaptureRequestsRegistry() {}
  DesktopCaptureRequestsRegistry::~DesktopCaptureRequestsRegistry() {}
  
  // static
  DesktopCaptureRequestsRegistry* DesktopCaptureRequestsRegistry::GetInstance() {
    return base::Singleton<DesktopCaptureRequestsRegistry>::get();
  }
  
  void DesktopCaptureRequestsRegistry::AddRequest(
      int process_id,
      int request_id,
      DesktopCaptureChooseDesktopMediaFunctionBase* handler) {
    requests_.insert(
        RequestsMap::value_type(RequestId(process_id, request_id), handler));
  }
  
  void DesktopCaptureRequestsRegistry::RemoveRequest(int process_id,
                                                     int request_id) {
    requests_.erase(RequestId(process_id, request_id));
  }
  
  void DesktopCaptureRequestsRegistry::CancelRequest(int process_id,
                                                     int request_id) {
    RequestsMap::iterator it = requests_.find(RequestId(process_id, request_id));
    if (it != requests_.end())
      it->second->Cancel();
  }
  
  
  }  // namespace extensions
  