// 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_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/ui/ash/ash_util.h" #include "chrome/common/extensions/api/tabs.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 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 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(), true, NULL, NULL, &web_contents, NULL)) { error_ = kInvalidTabIdError; return false; } GURL current_origin_ = web_contents->GetLastCommittedURL().GetOrigin(); if (current_origin_ != origin_) { error_ = kTabUrlChangedError; return false; } // Register to be notified when the tab is closed. Observe(web_contents); 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::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 media_list; if (g_picker_factory) { media_list = g_picker_factory->CreateModel( show_screens, show_windows); picker_ = g_picker_factory->CreatePicker(); } else { #if defined(USE_ASH) if (chrome::IsNativeWindowInAsh(parent_window)) { media_list.reset(new DesktopMediaListAsh( (show_screens ? DesktopMediaListAsh::SCREENS : 0) | (show_windows ? DesktopMediaListAsh::WINDOWS : 0))); } else #endif { webrtc::DesktopCaptureOptions options = webrtc::DesktopCaptureOptions::CreateDefault(); options.set_disable_effects(false); scoped_ptr screen_capturer( show_screens ? webrtc::ScreenCapturer::Create(options) : NULL); scoped_ptr window_capturer( show_windows ? webrtc::WindowCapturer::Create(options) : NULL); media_list.reset(new NativeDesktopMediaList( screen_capturer.Pass(), window_capturer.Pass())); } // 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( &DesktopCaptureChooseDesktopMediaFunction::OnPickerDialogResults, this); picker_->Show(parent_window, parent_window, base::UTF8ToUTF16(GetExtension()->name()), media_list.Pass(), callback); return true; } void DesktopCaptureChooseDesktopMediaFunction::WebContentsDestroyed( content::WebContents* web_contents) { Cancel(); } 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::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