blob: 9cd24f25f06242ef0d70b9abd83c64bd403c495f [file] [log] [blame]
// Copyright 2013 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_api.h"
#include "base/compiler_specific.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/extensions/extension_tab_util.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/common/extensions/api/tabs.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.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.";
const char kTabCaptureNotSupportedError[] = "Tab capture is not supported yet.";
const char kNoTabIdError[] = "targetTab doesn't have id field set.";
const char kNoUrlError[] = "targetTab doesn't have URL field set.";
const char kInvalidTabIdError[] = "Invalid tab specified.";
const char kTabUrlChangedError[] = "URL for the specified tab has changed.";
DesktopCaptureChooseDesktopMediaFunction::PickerFactory* g_picker_factory =
NULL;
} // namespace
// static
void DesktopCaptureChooseDesktopMediaFunction::SetPickerFactoryForTests(
PickerFactory* factory) {
g_picker_factory = factory;
}
DesktopCaptureChooseDesktopMediaFunction::
DesktopCaptureChooseDesktopMediaFunction()
: render_process_id_(0),
render_view_id_(0) {
}
DesktopCaptureChooseDesktopMediaFunction::
~DesktopCaptureChooseDesktopMediaFunction() {
// RenderViewHost may be already destroyed.
if (render_view_host()) {
DesktopCaptureRequestsRegistry::GetInstance()->RemoveRequest(
render_view_host()->GetProcess()->GetID(), request_id_);
}
}
void DesktopCaptureChooseDesktopMediaFunction::Cancel() {
// Keep reference to |this| to ensure the object doesn't get destroyed before
// we return.
scoped_refptr<DesktopCaptureChooseDesktopMediaFunction> self(this);
if (picker_) {
picker_.reset();
SetResult(new base::StringValue(std::string()));
SendResponse(true);
}
}
bool DesktopCaptureChooseDesktopMediaFunction::RunImpl() {
EXTENSION_FUNCTION_VALIDATE(args_->GetSize() > 0);
EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &request_id_));
args_->Remove(0, NULL);
scoped_ptr<api::desktop_capture::ChooseDesktopMedia::Params> params =
api::desktop_capture::ChooseDesktopMedia::Params::Create(*args_);
EXTENSION_FUNCTION_VALIDATE(params.get());
DesktopCaptureRequestsRegistry::GetInstance()->AddRequest(
render_view_host()->GetProcess()->GetID(), request_id_, this);
gfx::NativeWindow parent_window;
content::RenderViewHost* render_view;
if (params->target_tab) {
if (!params->target_tab->url) {
error_ = kNoUrlError;
return false;
}
origin_ = GURL(*(params->target_tab->url)).GetOrigin();
if (!params->target_tab->id) {
error_ = kNoTabIdError;
return false;
}
content::WebContents* web_contents = NULL;
if (!ExtensionTabUtil::GetTabById(*(params->target_tab->id),
GetProfile(),
false,
NULL,
NULL,
&web_contents,
NULL)) {
error_ = kInvalidTabIdError;
return false;
}
GURL current_origin_ =
web_contents->GetLastCommittedURL().GetOrigin();
if (current_origin_ != origin_) {
error_ = kTabUrlChangedError;
return false;
}
render_view = web_contents->GetRenderViewHost();
parent_window = web_contents->GetView()->GetTopLevelNativeWindow();
} else {
origin_ = GetExtension()->url();
render_view = render_view_host();
parent_window =
GetAssociatedWebContents()->GetView()->GetTopLevelNativeWindow();
}
render_process_id_ = render_view->GetProcess()->GetID();
render_view_id_ = render_view->GetRoutingID();
bool show_screens = false;
bool show_windows = false;
for (std::vector<api::desktop_capture::DesktopCaptureSourceType>::iterator
it = params->sources.begin(); it != params->sources.end(); ++it) {
switch (*it) {
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:
error_ = kTabCaptureNotSupportedError;
return false;
}
}
if (!show_screens && !show_windows) {
error_ = kEmptySourcesListError;
return false;
}
scoped_ptr<DesktopMediaList> media_list;
if (g_picker_factory) {
media_list = g_picker_factory->CreateModel(
show_screens, show_windows);
picker_ = g_picker_factory->CreatePicker();
} else {
// DesktopMediaPicker is implemented only for Windows, OSX and
// Aura Linux builds.
#if (defined(TOOLKIT_VIEWS) && !defined(OS_CHROMEOS)) || defined(OS_MACOSX)
webrtc::DesktopCaptureOptions options =
webrtc::DesktopCaptureOptions::CreateDefault();
options.set_disable_effects(false);
scoped_ptr<webrtc::ScreenCapturer> screen_capturer(
show_screens ? webrtc::ScreenCapturer::Create(options) : NULL);
scoped_ptr<webrtc::WindowCapturer> window_capturer(
show_windows ? webrtc::WindowCapturer::Create(options) : NULL);
media_list.reset(new NativeDesktopMediaList(
screen_capturer.Pass(), window_capturer.Pass()));
picker_ = DesktopMediaPicker::Create();
#else
error_ = "Desktop Capture API is not yet implemented for this platform.";
return false;
#endif
}
DesktopMediaPicker::DoneCallback callback = base::Bind(
&DesktopCaptureChooseDesktopMediaFunction::OnPickerDialogResults, this);
picker_->Show(parent_window, parent_window,
UTF8ToUTF16(GetExtension()->name()),
media_list.Pass(), callback);
return true;
}
void DesktopCaptureChooseDesktopMediaFunction::OnPickerDialogResults(
content::DesktopMediaID source) {
std::string result;
if (source.type != content::DesktopMediaID::TYPE_NONE) {
DesktopStreamsRegistry* registry =
MediaCaptureDevicesDispatcher::GetInstance()->
GetDesktopStreamsRegistry();
result = registry->RegisterStream(
render_process_id_, render_view_id_, origin_, source);
}
SetResult(new 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 {
if (process_id != other.process_id) {
return process_id < other.process_id;
} else {
return request_id < other.request_id;
}
}
DesktopCaptureCancelChooseDesktopMediaFunction::
DesktopCaptureCancelChooseDesktopMediaFunction() {}
DesktopCaptureCancelChooseDesktopMediaFunction::
~DesktopCaptureCancelChooseDesktopMediaFunction() {}
bool DesktopCaptureCancelChooseDesktopMediaFunction::RunImpl() {
int request_id;
EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &request_id));
DesktopCaptureRequestsRegistry::GetInstance()->CancelRequest(
render_view_host()->GetProcess()->GetID(), request_id);
return true;
}
DesktopCaptureRequestsRegistry::DesktopCaptureRequestsRegistry() {}
DesktopCaptureRequestsRegistry::~DesktopCaptureRequestsRegistry() {}
// static
DesktopCaptureRequestsRegistry* DesktopCaptureRequestsRegistry::GetInstance() {
return Singleton<DesktopCaptureRequestsRegistry>::get();
}
void DesktopCaptureRequestsRegistry::AddRequest(
int process_id,
int request_id,
DesktopCaptureChooseDesktopMediaFunction* 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