tab_capture_api.cc revision 4311e82a78ceafbe0585f51d4c8a86df9f21aa0d
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Implements the Chrome Extensions Tab Capture API. 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/api/tab_capture/tab_capture_api.h" 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 9c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/command_line.h" 10868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/stringprintf.h" 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/api/tab_capture/tab_capture_registry_factory.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/browser_event_router.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/event_names.h" 162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/extension_renderer_state.h" 17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/extensions/tab_helper.h" 18c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/profiles/profile.h" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sessions/session_tab_helper.h" 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser.h" 212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_finder.h" 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h" 23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/common/chrome_switches.h" 244311e82a78ceafbe0585f51d4c8a86df9f21aa0dBen Murdoch#include "chrome/common/extensions/features/feature.h" 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_process_host.h" 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_view_host.h" 274311e82a78ceafbe0585f51d4c8a86df9f21aa0dBen Murdoch#include "extensions/common/features/feature_provider.h" 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using extensions::api::tab_capture::MediaStreamConstraint; 302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace TabCapture = extensions::api::tab_capture; 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace GetCapturedTabs = TabCapture::GetCapturedTabs; 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace extensions { 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kCapturingSameTab[] = "Cannot capture a tab with an active stream."; 392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const char kFindingTabError[] = "Error finding tab to capture."; 402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const char kNoAudioOrVideo[] = "Capture failed. No audio or video requested."; 412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const char kPermissionError[] = "Tab Capture API flag is not enabled."; 42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const char kGrantError[] = 43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) "Extension has not been invoked for the current page (see activeTab " 44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) "permission). Chrome pages cannot be captured."; 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Keys/values for media stream constraints. 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kMediaStreamSource[] = "chromeMediaSource"; 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kMediaStreamSourceId[] = "chromeMediaSourceId"; 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kMediaStreamSourceTab[] = "tab"; 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabCaptureCaptureFunction::RunImpl() { 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_ptr<api::tab_capture::Capture::Params> params = 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TabCapture::Capture::Params::Create(*args_); 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) EXTENSION_FUNCTION_VALIDATE(params.get()); 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Figure out the active WebContents and retrieve the needed ids. 592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Browser* target_browser = chrome::FindAnyBrowser(profile(), 602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) include_incognito(), 612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) chrome::GetActiveDesktop()); 622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!target_browser) { 632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) error_ = kFindingTabError; 642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) content::WebContents* target_contents = 682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) target_browser->tab_strip_model()->GetActiveWebContents(); 692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!target_contents) { 702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) error_ = kFindingTabError; 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const Extension* extension = GetExtension(); 75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const std::string& extension_id = extension->id(); 76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Make sure either we have been granted permission to capture through an 78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // extension icon click or our extension is whitelisted. 79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (!TabHelper::FromWebContents(target_contents)-> 80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) active_tab_permission_granter()->IsGranted(extension) && 81c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) switches::kWhitelistedExtensionID) != extension_id && 834311e82a78ceafbe0585f51d4c8a86df9f21aa0dBen Murdoch !FeatureProvider::GetByName("permission")->GetFeature("tabCapture")-> 84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) IsIdInWhitelist(extension_id)) { 85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) error_ = kGrantError; 86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return false; 87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) content::RenderViewHost* const rvh = target_contents->GetRenderViewHost(); 902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) int render_process_id = rvh->GetProcess()->GetID(); 912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) int routing_id = rvh->GetRoutingID(); 922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) int tab_id = SessionTabHelper::FromWebContents(target_contents)-> 932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) session_id().id(); 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Create a constraints vector. We will modify all the constraints in this 962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // vector to append our chrome specific constraints. 972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) std::vector<MediaStreamConstraint*> constraints; 982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool has_audio = params->options.audio.get() && *params->options.audio.get(); 992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool has_video = params->options.video.get() && *params->options.video.get(); 1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!has_audio && !has_video) { 1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) error_ = kNoAudioOrVideo; 1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (has_audio) { 1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!params->options.audio_constraints.get()) 1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) params->options.audio_constraints.reset(new MediaStreamConstraint); 1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) constraints.push_back(params->options.audio_constraints.get()); 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (has_video) { 1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!params->options.video_constraints.get()) 1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) params->options.video_constraints.reset(new MediaStreamConstraint); 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) constraints.push_back(params->options.video_constraints.get()); 1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Device id we use for Tab Capture. 1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) std::string device_id = 1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::StringPrintf("%i:%i", render_process_id, routing_id); 1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Append chrome specific tab constraints. 1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) for (std::vector<MediaStreamConstraint*>::iterator it = constraints.begin(); 1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) it != constraints.end(); ++it) { 1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::DictionaryValue* constraint = &(*it)->mandatory.additional_properties; 1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) constraint->SetString(kMediaStreamSource, kMediaStreamSourceTab); 1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) constraint->SetString(kMediaStreamSourceId, device_id); 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) extensions::TabCaptureRegistry* registry = 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) extensions::TabCaptureRegistryFactory::GetForProfile(profile()); 1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!registry->AddRequest(render_process_id, 1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) routing_id, 135c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) extension_id, 1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) tab_id, 1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) tab_capture::TAB_CAPTURE_STATE_NONE)) { 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) error_ = kCapturingSameTab; 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Copy the result from our modified input parameters. This will be 1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // intercepted by custom bindings which will build and send the special 1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // WebRTC user media request. 1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::DictionaryValue* result = new base::DictionaryValue(); 1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) result->MergeDictionary(params->options.ToValue().get()); 1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetResult(result); 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabCaptureGetCapturedTabsFunction::RunImpl() { 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) extensions::TabCaptureRegistry* registry = 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) extensions::TabCaptureRegistryFactory::GetForProfile(profile()); 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const TabCaptureRegistry::RegistryCaptureInfo& captured_tabs = 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) registry->GetCapturedTabs(GetExtension()->id()); 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::ListValue *list = new base::ListValue(); 1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) for (TabCaptureRegistry::RegistryCaptureInfo::const_iterator it = 1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) captured_tabs.begin(); it != captured_tabs.end(); ++it) { 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_ptr<tab_capture::CaptureInfo> info(new tab_capture::CaptureInfo()); 1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) info->tab_id = it->first; 1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) info->status = it->second; 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) list->Append(info->ToValue().release()); 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetResult(list); 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace extensions 173