1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Implements the Chrome Extensions Tab Capture API. 6 7 #include "chrome/browser/extensions/api/tab_capture/tab_capture_api.h" 8 9 #include "base/command_line.h" 10 #include "base/strings/stringprintf.h" 11 #include "base/values.h" 12 #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h" 13 #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry_factory.h" 14 #include "chrome/browser/extensions/browser_event_router.h" 15 #include "chrome/browser/extensions/event_names.h" 16 #include "chrome/browser/extensions/extension_renderer_state.h" 17 #include "chrome/browser/extensions/tab_helper.h" 18 #include "chrome/browser/profiles/profile.h" 19 #include "chrome/browser/sessions/session_tab_helper.h" 20 #include "chrome/browser/ui/browser.h" 21 #include "chrome/browser/ui/browser_finder.h" 22 #include "chrome/browser/ui/tabs/tab_strip_model.h" 23 #include "chrome/common/chrome_switches.h" 24 #include "chrome/common/extensions/features/feature.h" 25 #include "content/public/browser/render_process_host.h" 26 #include "content/public/browser/render_view_host.h" 27 #include "extensions/common/features/feature_provider.h" 28 29 using extensions::api::tab_capture::MediaStreamConstraint; 30 31 namespace TabCapture = extensions::api::tab_capture; 32 namespace GetCapturedTabs = TabCapture::GetCapturedTabs; 33 34 namespace extensions { 35 36 namespace { 37 38 const char kCapturingSameTab[] = "Cannot capture a tab with an active stream."; 39 const char kFindingTabError[] = "Error finding tab to capture."; 40 const char kNoAudioOrVideo[] = "Capture failed. No audio or video requested."; 41 const char kPermissionError[] = "Tab Capture API flag is not enabled."; 42 const char kGrantError[] = 43 "Extension has not been invoked for the current page (see activeTab " 44 "permission). Chrome pages cannot be captured."; 45 46 // Keys/values for media stream constraints. 47 const char kMediaStreamSource[] = "chromeMediaSource"; 48 const char kMediaStreamSourceId[] = "chromeMediaSourceId"; 49 const char kMediaStreamSourceTab[] = "tab"; 50 51 } // namespace 52 53 bool TabCaptureCaptureFunction::RunImpl() { 54 scoped_ptr<api::tab_capture::Capture::Params> params = 55 TabCapture::Capture::Params::Create(*args_); 56 EXTENSION_FUNCTION_VALIDATE(params.get()); 57 58 // Figure out the active WebContents and retrieve the needed ids. 59 Browser* target_browser = chrome::FindAnyBrowser(profile(), 60 include_incognito(), 61 chrome::GetActiveDesktop()); 62 if (!target_browser) { 63 error_ = kFindingTabError; 64 return false; 65 } 66 67 content::WebContents* target_contents = 68 target_browser->tab_strip_model()->GetActiveWebContents(); 69 if (!target_contents) { 70 error_ = kFindingTabError; 71 return false; 72 } 73 74 const Extension* extension = GetExtension(); 75 const std::string& extension_id = extension->id(); 76 77 // Make sure either we have been granted permission to capture through an 78 // extension icon click or our extension is whitelisted. 79 if (!TabHelper::FromWebContents(target_contents)-> 80 active_tab_permission_granter()->IsGranted(extension) && 81 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 82 switches::kWhitelistedExtensionID) != extension_id && 83 !FeatureProvider::GetByName("permission")->GetFeature("tabCapture")-> 84 IsIdInWhitelist(extension_id)) { 85 error_ = kGrantError; 86 return false; 87 } 88 89 content::RenderViewHost* const rvh = target_contents->GetRenderViewHost(); 90 int render_process_id = rvh->GetProcess()->GetID(); 91 int routing_id = rvh->GetRoutingID(); 92 int tab_id = SessionTabHelper::FromWebContents(target_contents)-> 93 session_id().id(); 94 95 // Create a constraints vector. We will modify all the constraints in this 96 // vector to append our chrome specific constraints. 97 std::vector<MediaStreamConstraint*> constraints; 98 bool has_audio = params->options.audio.get() && *params->options.audio.get(); 99 bool has_video = params->options.video.get() && *params->options.video.get(); 100 101 if (!has_audio && !has_video) { 102 error_ = kNoAudioOrVideo; 103 return false; 104 } 105 106 if (has_audio) { 107 if (!params->options.audio_constraints.get()) 108 params->options.audio_constraints.reset(new MediaStreamConstraint); 109 110 constraints.push_back(params->options.audio_constraints.get()); 111 } 112 if (has_video) { 113 if (!params->options.video_constraints.get()) 114 params->options.video_constraints.reset(new MediaStreamConstraint); 115 116 constraints.push_back(params->options.video_constraints.get()); 117 } 118 119 // Device id we use for Tab Capture. 120 std::string device_id = 121 base::StringPrintf("%i:%i", render_process_id, routing_id); 122 123 // Append chrome specific tab constraints. 124 for (std::vector<MediaStreamConstraint*>::iterator it = constraints.begin(); 125 it != constraints.end(); ++it) { 126 base::DictionaryValue* constraint = &(*it)->mandatory.additional_properties; 127 constraint->SetString(kMediaStreamSource, kMediaStreamSourceTab); 128 constraint->SetString(kMediaStreamSourceId, device_id); 129 } 130 131 extensions::TabCaptureRegistry* registry = 132 extensions::TabCaptureRegistryFactory::GetForProfile(profile()); 133 if (!registry->AddRequest(render_process_id, 134 routing_id, 135 extension_id, 136 tab_id, 137 tab_capture::TAB_CAPTURE_STATE_NONE)) { 138 error_ = kCapturingSameTab; 139 return false; 140 } 141 142 // Copy the result from our modified input parameters. This will be 143 // intercepted by custom bindings which will build and send the special 144 // WebRTC user media request. 145 base::DictionaryValue* result = new base::DictionaryValue(); 146 result->MergeDictionary(params->options.ToValue().get()); 147 148 SetResult(result); 149 return true; 150 } 151 152 bool TabCaptureGetCapturedTabsFunction::RunImpl() { 153 extensions::TabCaptureRegistry* registry = 154 extensions::TabCaptureRegistryFactory::GetForProfile(profile()); 155 156 const TabCaptureRegistry::RegistryCaptureInfo& captured_tabs = 157 registry->GetCapturedTabs(GetExtension()->id()); 158 159 base::ListValue *list = new base::ListValue(); 160 for (TabCaptureRegistry::RegistryCaptureInfo::const_iterator it = 161 captured_tabs.begin(); it != captured_tabs.end(); ++it) { 162 scoped_ptr<tab_capture::CaptureInfo> info(new tab_capture::CaptureInfo()); 163 info->tab_id = it->first; 164 info->status = it->second; 165 list->Append(info->ToValue().release()); 166 } 167 168 SetResult(list); 169 return true; 170 } 171 172 } // namespace extensions 173