// Copyright (c) 2012 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. // Implements the Chrome Extensions Tab Capture API. #include "chrome/browser/extensions/api/tab_capture/tab_capture_api.h" #include "base/command_line.h" #include "base/strings/stringprintf.h" #include "base/values.h" #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h" #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry_factory.h" #include "chrome/browser/extensions/browser_event_router.h" #include "chrome/browser/extensions/event_names.h" #include "chrome/browser/extensions/extension_renderer_state.h" #include "chrome/browser/extensions/tab_helper.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sessions/session_tab_helper.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/feature_switch.h" #include "chrome/common/extensions/features/base_feature_provider.h" #include "chrome/common/extensions/features/simple_feature.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" using extensions::api::tab_capture::MediaStreamConstraint; namespace TabCapture = extensions::api::tab_capture; namespace GetCapturedTabs = TabCapture::GetCapturedTabs; namespace extensions { namespace { const char kCapturingSameTab[] = "Cannot capture a tab with an active stream."; const char kFindingTabError[] = "Error finding tab to capture."; const char kNoAudioOrVideo[] = "Capture failed. No audio or video requested."; const char kPermissionError[] = "Tab Capture API flag is not enabled."; const char kGrantError[] = "Extension has not been invoked for the current page (see activeTab " "permission). Chrome pages cannot be captured."; // Keys/values for media stream constraints. const char kMediaStreamSource[] = "chromeMediaSource"; const char kMediaStreamSourceId[] = "chromeMediaSourceId"; const char kMediaStreamSourceTab[] = "tab"; } // namespace bool TabCaptureCaptureFunction::RunImpl() { if (!FeatureSwitch::tab_capture()->IsEnabled()) { error_ = kPermissionError; return false; } scoped_ptr params = TabCapture::Capture::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(params.get()); // Figure out the active WebContents and retrieve the needed ids. Browser* target_browser = chrome::FindAnyBrowser(profile(), include_incognito(), chrome::GetActiveDesktop()); if (!target_browser) { error_ = kFindingTabError; return false; } content::WebContents* target_contents = target_browser->tab_strip_model()->GetActiveWebContents(); if (!target_contents) { error_ = kFindingTabError; return false; } const Extension* extension = GetExtension(); const std::string& extension_id = extension->id(); // Make sure either we have been granted permission to capture through an // extension icon click or our extension is whitelisted. if (!TabHelper::FromWebContents(target_contents)-> active_tab_permission_granter()->IsGranted(extension) && CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kWhitelistedExtensionID) != extension_id && !BaseFeatureProvider::GetPermissionFeatures()->GetFeature("tabCapture")-> IsIdInWhitelist(extension_id)) { error_ = kGrantError; return false; } content::RenderViewHost* const rvh = target_contents->GetRenderViewHost(); int render_process_id = rvh->GetProcess()->GetID(); int routing_id = rvh->GetRoutingID(); int tab_id = SessionTabHelper::FromWebContents(target_contents)-> session_id().id(); // Create a constraints vector. We will modify all the constraints in this // vector to append our chrome specific constraints. std::vector constraints; bool has_audio = params->options.audio.get() && *params->options.audio.get(); bool has_video = params->options.video.get() && *params->options.video.get(); if (!has_audio && !has_video) { error_ = kNoAudioOrVideo; return false; } if (has_audio) { if (!params->options.audio_constraints.get()) params->options.audio_constraints.reset(new MediaStreamConstraint); constraints.push_back(params->options.audio_constraints.get()); } if (has_video) { if (!params->options.video_constraints.get()) params->options.video_constraints.reset(new MediaStreamConstraint); constraints.push_back(params->options.video_constraints.get()); } // Device id we use for Tab Capture. std::string device_id = base::StringPrintf("%i:%i", render_process_id, routing_id); // Append chrome specific tab constraints. for (std::vector::iterator it = constraints.begin(); it != constraints.end(); ++it) { base::DictionaryValue* constraint = &(*it)->mandatory.additional_properties; constraint->SetString(kMediaStreamSource, kMediaStreamSourceTab); constraint->SetString(kMediaStreamSourceId, device_id); } extensions::TabCaptureRegistry* registry = extensions::TabCaptureRegistryFactory::GetForProfile(profile()); if (!registry->AddRequest(render_process_id, routing_id, extension_id, tab_id, tab_capture::TAB_CAPTURE_STATE_NONE)) { error_ = kCapturingSameTab; return false; } // Copy the result from our modified input parameters. This will be // intercepted by custom bindings which will build and send the special // WebRTC user media request. base::DictionaryValue* result = new base::DictionaryValue(); result->MergeDictionary(params->options.ToValue().get()); SetResult(result); return true; } bool TabCaptureGetCapturedTabsFunction::RunImpl() { if (!FeatureSwitch::tab_capture()->IsEnabled()) { error_ = kPermissionError; return false; } extensions::TabCaptureRegistry* registry = extensions::TabCaptureRegistryFactory::GetForProfile(profile()); const TabCaptureRegistry::RegistryCaptureInfo& captured_tabs = registry->GetCapturedTabs(GetExtension()->id()); base::ListValue *list = new base::ListValue(); for (TabCaptureRegistry::RegistryCaptureInfo::const_iterator it = captured_tabs.begin(); it != captured_tabs.end(); ++it) { scoped_ptr info(new tab_capture::CaptureInfo()); info->tab_id = it->first; info->status = it->second; list->Append(info->ToValue().release()); } SetResult(list); return true; } } // namespace extensions