Home | History | Annotate | Download | only in tab_capture
      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