Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // Implements the Chrome Extensions Tab Capture API. | 5 // Implements the Chrome Extensions Tab Capture API. |
| 6 | 6 |
| 7 #include "chrome/browser/extensions/api/tab_capture/tab_capture_api.h" | 7 #include "chrome/browser/extensions/api/tab_capture/tab_capture_api.h" |
| 8 | 8 |
| 9 #include <algorithm> | |
| 9 #include <set> | 10 #include <set> |
| 10 #include <string> | 11 #include <string> |
| 11 #include <vector> | 12 #include <vector> |
| 12 | 13 |
| 13 #include "base/command_line.h" | 14 #include "base/command_line.h" |
| 14 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" |
| 15 #include "base/values.h" | 16 #include "base/values.h" |
| 17 #include "chrome/browser/extensions/api/tab_capture/offscreen_presentation.h" | |
| 16 #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h" | 18 #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h" |
| 17 #include "chrome/browser/extensions/extension_renderer_state.h" | 19 #include "chrome/browser/extensions/extension_renderer_state.h" |
| 18 #include "chrome/browser/profiles/profile.h" | 20 #include "chrome/browser/profiles/profile.h" |
| 19 #include "chrome/browser/sessions/session_tab_helper.h" | 21 #include "chrome/browser/sessions/session_tab_helper.h" |
| 20 #include "chrome/browser/ui/browser.h" | 22 #include "chrome/browser/ui/browser.h" |
| 21 #include "chrome/browser/ui/browser_finder.h" | 23 #include "chrome/browser/ui/browser_finder.h" |
| 22 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 24 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| 25 #include "chrome/common/chrome_switches.h" | |
| 23 #include "content/public/browser/render_frame_host.h" | 26 #include "content/public/browser/render_frame_host.h" |
| 24 #include "content/public/browser/render_process_host.h" | 27 #include "content/public/browser/render_process_host.h" |
| 28 #include "content/public/browser/web_contents.h" | |
| 25 #include "extensions/common/features/feature.h" | 29 #include "extensions/common/features/feature.h" |
| 26 #include "extensions/common/features/feature_provider.h" | 30 #include "extensions/common/features/feature_provider.h" |
| 27 #include "extensions/common/features/simple_feature.h" | 31 #include "extensions/common/features/simple_feature.h" |
| 28 #include "extensions/common/permissions/permissions_data.h" | 32 #include "extensions/common/permissions/permissions_data.h" |
| 29 #include "extensions/common/switches.h" | 33 #include "extensions/common/switches.h" |
| 30 | 34 |
| 31 using extensions::api::tab_capture::MediaStreamConstraint; | 35 using extensions::api::tab_capture::MediaStreamConstraint; |
| 32 | 36 |
| 33 namespace TabCapture = extensions::api::tab_capture; | 37 namespace TabCapture = extensions::api::tab_capture; |
| 34 namespace GetCapturedTabs = TabCapture::GetCapturedTabs; | 38 namespace GetCapturedTabs = TabCapture::GetCapturedTabs; |
| 35 | 39 |
| 36 namespace extensions { | 40 namespace extensions { |
| 37 namespace { | 41 namespace { |
| 38 | 42 |
| 39 const char kCapturingSameTab[] = "Cannot capture a tab with an active stream."; | 43 const char kCapturingSameTab[] = "Cannot capture a tab with an active stream."; |
| 40 const char kFindingTabError[] = "Error finding tab to capture."; | 44 const char kFindingTabError[] = "Error finding tab to capture."; |
| 41 const char kNoAudioOrVideo[] = "Capture failed. No audio or video requested."; | 45 const char kNoAudioOrVideo[] = "Capture failed. No audio or video requested."; |
| 42 const char kGrantError[] = | 46 const char kGrantError[] = |
| 43 "Extension has not been invoked for the current page (see activeTab " | 47 "Extension has not been invoked for the current page (see activeTab " |
| 44 "permission). Chrome pages cannot be captured."; | 48 "permission). Chrome pages cannot be captured."; |
| 45 | 49 |
| 46 // Keys/values for media stream constraints. | 50 const char kNotWhitelistedForOffscreenTabApi[] = |
| 51 "Extension is not whitelisted for use of the unstable, in-development " | |
| 52 "chrome.tabCapture.captureOffscreenTab API."; | |
| 53 const char kInvalidStartUrl[] = | |
| 54 "Invalid/Missing/Malformatted starting URL for off-screen tab."; | |
| 55 const char kMissingExtensionPage[] = | |
| 56 "tabCapture API was not invoked from an extension background page."; | |
| 57 const char kTooManyOffscreenTabs[] = | |
| 58 "Extension has already started too many off-screen tabs."; | |
| 59 const char kCapturingSameOffscreenTab[] = | |
| 60 "Cannot capture the same off-screen tab more than once."; | |
| 61 | |
| 62 const char kOffscreenTabNotFound[] = | |
| 63 "ID does not reference any currently-alive off-screen tab."; | |
| 64 | |
| 65 // Keys/values passed to renderer-side JS bindings. | |
| 47 const char kMediaStreamSource[] = "chromeMediaSource"; | 66 const char kMediaStreamSource[] = "chromeMediaSource"; |
| 48 const char kMediaStreamSourceId[] = "chromeMediaSourceId"; | 67 const char kMediaStreamSourceId[] = "chromeMediaSourceId"; |
| 49 const char kMediaStreamSourceTab[] = "tab"; | 68 const char kMediaStreamSourceTab[] = "tab"; |
| 69 const char kOffscreenTabIdKey[] = "offscreenTabId"; | |
| 50 | 70 |
| 51 // Tab Capture-specific video constraint to enable automatic resolution/rate | 71 // Tab Capture-specific video constraint to enable automatic resolution/rate |
| 52 // throttling mode in the capture pipeline. | 72 // throttling mode in the capture pipeline. |
| 53 const char kEnableAutoThrottlingKey[] = "enableAutoThrottling"; | 73 const char kEnableAutoThrottlingKey[] = "enableAutoThrottling"; |
| 54 | 74 |
| 75 bool OptionsSpecifyAudioOrVideo(const TabCapture::CaptureOptions& options) { | |
| 76 return (options.audio && *options.audio) || (options.video && *options.video); | |
| 77 } | |
| 78 | |
| 79 bool IsAcceptableOffscreenTabUrl(const GURL& url) { | |
| 80 return url.is_valid() && (url.SchemeIsHTTPOrHTTPS() || url.SchemeIs("data")); | |
| 81 } | |
| 82 | |
| 83 // Add Chrome-specific source identifiers to the MediaStreamConstraints objects | |
| 84 // in |options| to provide references to the |target_contents| to be captured. | |
| 85 void AddMediaStreamSourceConstraints(content::WebContents* target_contents, | |
| 86 TabCapture::CaptureOptions* options) { | |
| 87 DCHECK(options); | |
| 88 DCHECK(target_contents); | |
| 89 | |
| 90 MediaStreamConstraint* constraints_to_modify[2] = { nullptr, nullptr }; | |
| 91 | |
| 92 if (options->audio && *options->audio) { | |
| 93 if (!options->audio_constraints) | |
| 94 options->audio_constraints.reset(new MediaStreamConstraint); | |
| 95 constraints_to_modify[0] = options->audio_constraints.get(); | |
| 96 } | |
| 97 | |
| 98 bool enable_auto_throttling = false; | |
| 99 if (options->video && *options->video) { | |
| 100 if (options->video_constraints) { | |
| 101 // Check for the Tab Capture-specific video constraint for enabling | |
| 102 // automatic resolution/rate throttling mode in the capture pipeline. See | |
| 103 // implementation comments for content::WebContentsVideoCaptureDevice. | |
| 104 base::DictionaryValue& props = | |
| 105 options->video_constraints->mandatory.additional_properties; | |
| 106 if (!props.GetBooleanWithoutPathExpansion( | |
| 107 kEnableAutoThrottlingKey, &enable_auto_throttling)) { | |
| 108 enable_auto_throttling = false; | |
| 109 } | |
| 110 // Remove the key from the properties to avoid an "unrecognized | |
| 111 // constraint" error in the renderer. | |
| 112 props.RemoveWithoutPathExpansion(kEnableAutoThrottlingKey, nullptr); | |
| 113 } else { | |
| 114 options->video_constraints.reset(new MediaStreamConstraint); | |
| 115 } | |
| 116 constraints_to_modify[1] = options->video_constraints.get(); | |
| 117 } | |
| 118 | |
| 119 // Format the device ID that references the target tab. | |
| 120 content::RenderFrameHost* const main_frame = target_contents->GetMainFrame(); | |
| 121 // TODO(miu): We should instead use a "randomly generated device ID" scheme, | |
| 122 // like that employed by the desktop capture API. http://crbug.com/163100 | |
| 123 const std::string device_id = base::StringPrintf( | |
| 124 "web-contents-media-stream://%i:%i%s", | |
| 125 main_frame->GetProcess()->GetID(), | |
| 126 main_frame->GetRoutingID(), | |
| 127 enable_auto_throttling ? "?throttling=auto" : ""); | |
| 128 | |
| 129 // Append chrome specific tab constraints. | |
| 130 for (MediaStreamConstraint* msc : constraints_to_modify) { | |
| 131 if (!msc) | |
| 132 continue; | |
| 133 base::DictionaryValue* constraint = &msc->mandatory.additional_properties; | |
| 134 constraint->SetString(kMediaStreamSource, kMediaStreamSourceTab); | |
| 135 constraint->SetString(kMediaStreamSourceId, device_id); | |
| 136 } | |
| 137 } | |
| 138 | |
| 55 } // namespace | 139 } // namespace |
| 56 | 140 |
| 57 // Whitelisted extensions that do not check for a browser action grant because | 141 // Whitelisted extensions that do not check for a browser action grant because |
| 58 // they provide API's. If there are additional extension ids that need | 142 // they provide API's. If there are additional extension ids that need |
| 59 // whitelisting and are *not* the Chromecast extension, add them to a new | 143 // whitelisting and are *not* the Chromecast extension, add them to a new |
| 60 // kWhitelist array. | 144 // kWhitelist array. |
| 61 // | 145 // |
| 62 // This list is also used by CastConfigDelegateChromeos to find official Cast | 146 // This list is also used by CastConfigDelegateChromeos to find official Cast |
| 63 // extensions. | 147 // extensions. |
| 64 const char* const kChromecastExtensionIds[] = { | 148 const char* const kChromecastExtensionIds[] = { |
| 65 "enhhojjnijigcajfphajepfemndkmdlo", // Dev | 149 "enhhojjnijigcajfphajepfemndkmdlo", // Dev |
| 66 "fmfcbgogabcbclcofgocippekhfcmgfj", // Staging | 150 "fmfcbgogabcbclcofgocippekhfcmgfj", // Staging |
| 67 "hfaagokkkhdbgiakmmlclaapfelnkoah", // Canary | 151 "hfaagokkkhdbgiakmmlclaapfelnkoah", // Canary |
| 68 "dliochdbjfkdbacpmhlcpmleaejidimm", // Google Cast Beta | 152 "dliochdbjfkdbacpmhlcpmleaejidimm", // Google Cast Beta |
| 69 "boadgeojelhgndaghljhdicfkmllpafd", // Google Cast Stable | 153 "boadgeojelhgndaghljhdicfkmllpafd", // Google Cast Stable |
| 70 "hlgmmjhlnlapooncikdpiiokdjcdpjme", // Test cast extension | 154 "hlgmmjhlnlapooncikdpiiokdjcdpjme", // Test cast extension |
| 71 }; | 155 }; |
| 72 | 156 |
| 73 const char* const kMediaRouterExtensionIds[] = { | 157 const char* const kMediaRouterExtensionIds[] = { |
| 74 "fjhoaacokmgbjemoflkofnenfaiekifl", // Stable | 158 "fjhoaacokmgbjemoflkofnenfaiekifl", // Stable |
| 75 "ekpaaapppgpmolpcldedioblbkmijaca", // Beta | 159 "ekpaaapppgpmolpcldedioblbkmijaca", // Beta |
| 76 }; | 160 }; |
| 77 | 161 |
| 78 bool TabCaptureCaptureFunction::RunSync() { | 162 bool TabCaptureCaptureFunction::RunSync() { |
| 79 scoped_ptr<api::tab_capture::Capture::Params> params = | 163 scoped_ptr<api::tab_capture::Capture::Params> params = |
| 80 TabCapture::Capture::Params::Create(*args_); | 164 TabCapture::Capture::Params::Create(*args_); |
| 81 EXTENSION_FUNCTION_VALIDATE(params.get()); | 165 EXTENSION_FUNCTION_VALIDATE(params); |
| 82 | 166 |
| 83 // Figure out the active WebContents and retrieve the needed ids. | 167 // Figure out the active WebContents and retrieve the needed ids. |
| 84 Browser* target_browser = chrome::FindAnyBrowser( | 168 Browser* target_browser = chrome::FindAnyBrowser( |
| 85 GetProfile(), include_incognito(), chrome::GetActiveDesktop()); | 169 GetProfile(), include_incognito(), chrome::GetActiveDesktop()); |
| 86 if (!target_browser) { | 170 if (!target_browser) { |
| 87 error_ = kFindingTabError; | 171 error_ = kFindingTabError; |
| 88 return false; | 172 return false; |
| 89 } | 173 } |
| 90 | 174 |
| 91 content::WebContents* target_contents = | 175 content::WebContents* target_contents = |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 105 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | 189 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
| 106 switches::kWhitelistedExtensionID) != extension_id && | 190 switches::kWhitelistedExtensionID) != extension_id && |
| 107 !SimpleFeature::IsIdInArray(extension_id, kChromecastExtensionIds, | 191 !SimpleFeature::IsIdInArray(extension_id, kChromecastExtensionIds, |
| 108 arraysize(kChromecastExtensionIds)) && | 192 arraysize(kChromecastExtensionIds)) && |
| 109 !SimpleFeature::IsIdInArray(extension_id, kMediaRouterExtensionIds, | 193 !SimpleFeature::IsIdInArray(extension_id, kMediaRouterExtensionIds, |
| 110 arraysize(kMediaRouterExtensionIds))) { | 194 arraysize(kMediaRouterExtensionIds))) { |
| 111 error_ = kGrantError; | 195 error_ = kGrantError; |
| 112 return false; | 196 return false; |
| 113 } | 197 } |
| 114 | 198 |
| 115 // Create a constraints vector. We will modify all the constraints in this | 199 if (!OptionsSpecifyAudioOrVideo(params->options)) { |
| 116 // vector to append our chrome specific constraints. | |
| 117 std::vector<MediaStreamConstraint*> constraints; | |
| 118 bool has_audio = params->options.audio.get() && *params->options.audio.get(); | |
| 119 bool has_video = params->options.video.get() && *params->options.video.get(); | |
| 120 | |
| 121 if (!has_audio && !has_video) { | |
| 122 error_ = kNoAudioOrVideo; | 200 error_ = kNoAudioOrVideo; |
| 123 return false; | 201 return false; |
| 124 } | 202 } |
| 125 | 203 |
| 126 if (has_audio) { | |
| 127 if (!params->options.audio_constraints.get()) | |
| 128 params->options.audio_constraints.reset(new MediaStreamConstraint); | |
| 129 | |
| 130 constraints.push_back(params->options.audio_constraints.get()); | |
| 131 } | |
| 132 | |
| 133 bool enable_auto_throttling = false; | |
| 134 if (has_video) { | |
| 135 if (params->options.video_constraints.get()) { | |
| 136 // Check for the Tab Capture-specific video constraint for enabling | |
| 137 // automatic resolution/rate throttling mode in the capture pipeline. See | |
| 138 // implementation comments for content::WebContentsVideoCaptureDevice. | |
| 139 base::DictionaryValue& props = | |
| 140 params->options.video_constraints->mandatory.additional_properties; | |
| 141 if (!props.GetBooleanWithoutPathExpansion( | |
| 142 kEnableAutoThrottlingKey, &enable_auto_throttling)) { | |
| 143 enable_auto_throttling = false; | |
| 144 } | |
| 145 // Remove the key from the properties to avoid an "unrecognized | |
| 146 // constraint" error in the renderer. | |
| 147 props.RemoveWithoutPathExpansion(kEnableAutoThrottlingKey, nullptr); | |
| 148 } else { | |
| 149 params->options.video_constraints.reset(new MediaStreamConstraint); | |
| 150 } | |
| 151 | |
| 152 constraints.push_back(params->options.video_constraints.get()); | |
| 153 } | |
| 154 | |
| 155 // Device id we use for Tab Capture. | |
| 156 content::RenderFrameHost* const main_frame = target_contents->GetMainFrame(); | |
| 157 // TODO(miu): We should instead use a "randomly generated device ID" scheme, | |
| 158 // like that employed by the desktop capture API. http://crbug.com/163100 | |
| 159 const std::string device_id = base::StringPrintf( | |
| 160 "web-contents-media-stream://%i:%i%s", | |
| 161 main_frame->GetProcess()->GetID(), | |
| 162 main_frame->GetRoutingID(), | |
| 163 enable_auto_throttling ? "?throttling=auto" : ""); | |
| 164 | |
| 165 // Append chrome specific tab constraints. | |
| 166 for (std::vector<MediaStreamConstraint*>::iterator it = constraints.begin(); | |
| 167 it != constraints.end(); ++it) { | |
| 168 base::DictionaryValue* constraint = &(*it)->mandatory.additional_properties; | |
| 169 constraint->SetString(kMediaStreamSource, kMediaStreamSourceTab); | |
| 170 constraint->SetString(kMediaStreamSourceId, device_id); | |
| 171 } | |
| 172 | |
| 173 TabCaptureRegistry* registry = TabCaptureRegistry::Get(GetProfile()); | 204 TabCaptureRegistry* registry = TabCaptureRegistry::Get(GetProfile()); |
| 174 if (!registry->AddRequest(target_contents, extension_id)) { | 205 if (!registry->AddRequest(target_contents, extension_id, false)) { |
| 206 // TODO(miu): Allow multiple consumers of single tab capture. | |
| 207 // http://crbug.com/535336 | |
| 175 error_ = kCapturingSameTab; | 208 error_ = kCapturingSameTab; |
| 176 return false; | 209 return false; |
| 177 } | 210 } |
| 211 AddMediaStreamSourceConstraints(target_contents, ¶ms->options); | |
| 178 | 212 |
| 179 // Copy the result from our modified input parameters. This will be | 213 // At this point, everything is set up in the browser process. It's now up to |
| 180 // intercepted by custom bindings which will build and send the special | 214 // the custom JS bindings in the extension's render process to request a |
| 181 // WebRTC user media request. | 215 // MediaStream using navigator.webkitGetUserMedia(). The result dictionary, |
| 216 // passed to SetResult() here, contains the extra "hidden options" that will | |
| 217 // allow the Chrome platform implementation for getUserMedia() to start the | |
| 218 // virtual audio/video capture devices and set up all the data flows. The | |
| 219 // custom JS bindings can be found here: | |
| 220 // chrome/renderer/resources/extensions/tab_capture_custom_bindings.js | |
| 182 base::DictionaryValue* result = new base::DictionaryValue(); | 221 base::DictionaryValue* result = new base::DictionaryValue(); |
| 183 result->MergeDictionary(params->options.ToValue().get()); | 222 result->MergeDictionary(params->options.ToValue().get()); |
| 184 | |
| 185 SetResult(result); | 223 SetResult(result); |
| 186 return true; | 224 return true; |
| 187 } | 225 } |
| 188 | 226 |
| 189 bool TabCaptureGetCapturedTabsFunction::RunSync() { | 227 bool TabCaptureGetCapturedTabsFunction::RunSync() { |
| 190 TabCaptureRegistry* registry = TabCaptureRegistry::Get(GetProfile()); | 228 TabCaptureRegistry* registry = TabCaptureRegistry::Get(GetProfile()); |
| 191 base::ListValue* const list = new base::ListValue(); | 229 base::ListValue* const list = new base::ListValue(); |
| 192 if (registry) | 230 if (registry) |
| 193 registry->GetCapturedTabs(extension()->id(), list); | 231 registry->GetCapturedTabs(extension()->id(), list); |
| 194 SetResult(list); | 232 SetResult(list); |
| 195 return true; | 233 return true; |
| 196 } | 234 } |
| 197 | 235 |
| 236 bool TabCaptureCaptureOffscreenTabFunction::RunSync() { | |
| 237 scoped_ptr<TabCapture::CaptureOffscreenTab::Params> params = | |
| 238 TabCapture::CaptureOffscreenTab::Params::Create(*args_); | |
| 239 EXTENSION_FUNCTION_VALIDATE(params); | |
| 240 | |
| 241 // Make sure the extension is whitelisted for using this API, or this is a | |
| 242 // Canary/Dev-channel build of Chrome. | |
| 243 // | |
| 244 // TODO(miu): Use _api_features.json and extensions::Feature library instead. | |
| 245 // http://crbug.com/537732 | |
| 246 const bool is_whitelisted_extension = | |
| 247 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
| 248 switches::kWhitelistedExtensionID) == extension()->id() || | |
| 249 SimpleFeature::IsIdInArray(extension()->id(), kChromecastExtensionIds, | |
| 250 arraysize(kChromecastExtensionIds)) || | |
| 251 SimpleFeature::IsIdInArray(extension()->id(), kMediaRouterExtensionIds, | |
| 252 arraysize(kMediaRouterExtensionIds)); | |
| 253 if (!is_whitelisted_extension) { | |
| 254 error_ = kNotWhitelistedForOffscreenTabApi; | |
| 255 return false; | |
| 256 } | |
| 257 | |
| 258 const GURL start_url(params->start_url); | |
| 259 if (!IsAcceptableOffscreenTabUrl(start_url)) { | |
| 260 SetError(kInvalidStartUrl); | |
| 261 return false; | |
| 262 } | |
| 263 | |
| 264 if (!OptionsSpecifyAudioOrVideo(params->options)) { | |
| 265 SetError(kNoAudioOrVideo); | |
| 266 return false; | |
| 267 } | |
| 268 | |
| 269 content::WebContents* const extension_web_contents = GetSenderWebContents(); | |
| 270 if (!extension_web_contents) { | |
| 271 SetError(kMissingExtensionPage); | |
| 272 return false; | |
| 273 } | |
| 274 | |
| 275 OffscreenPresentation* const offscreen_tab = | |
| 276 OffscreenPresentationsOwner::Get(extension_web_contents) | |
| 277 ->StartPresentation(start_url, | |
| 278 DetermineInitialSize(params->options)); | |
| 279 if (!offscreen_tab) { | |
| 280 SetError(kTooManyOffscreenTabs); | |
| 281 return false; | |
| 282 } | |
| 283 | |
| 284 if (!TabCaptureRegistry::Get(browser_context())->AddRequest( | |
| 285 offscreen_tab->web_contents(), extension()->id(), true)) { | |
| 286 // TODO(miu): Allow multiple consumers of single tab capture. | |
| 287 // http://crbug.com/535336 | |
| 288 SetError(kCapturingSameOffscreenTab); | |
| 289 return false; | |
| 290 } | |
| 291 AddMediaStreamSourceConstraints(offscreen_tab->web_contents(), | |
| 292 ¶ms->options); | |
| 293 | |
| 294 // At this point, everything is set up in the browser process. It's now up to | |
| 295 // the custom JS bindings in the extension's render process to complete the | |
| 296 // request. See the comment at end of TabCaptureCaptureFunction::RunSync() | |
| 297 // for more details. | |
| 298 base::DictionaryValue* const result = new base::DictionaryValue(); | |
| 299 result->MergeDictionary(params->options.ToValue().get()); | |
| 300 result->SetStringWithoutPathExpansion(kOffscreenTabIdKey, | |
| 301 offscreen_tab->id()); | |
| 302 SetResult(result); | |
| 303 return true; | |
| 304 } | |
| 305 | |
| 306 // static | |
| 307 gfx::Size TabCaptureCaptureOffscreenTabFunction::DetermineInitialSize( | |
| 308 const TabCapture::CaptureOptions& options) { | |
| 309 static const int kDefaultWidth = 1280; | |
| 310 static const int kDefaultHeight = 720; | |
| 311 | |
| 312 if (!options.video_constraints) | |
| 313 return gfx::Size(kDefaultWidth, kDefaultHeight); | |
| 314 | |
| 315 gfx::Size min_size; | |
| 316 int width = -1; | |
| 317 int height = -1; | |
| 318 const base::DictionaryValue& mandatory_properties = | |
| 319 options.video_constraints->mandatory.additional_properties; | |
| 320 if (mandatory_properties.GetInteger("maxWidth", &width) && width >= 0 && | |
| 321 mandatory_properties.GetInteger("maxHeight", &height) && height >= 0) { | |
| 322 return gfx::Size(width, height); | |
| 323 } | |
| 324 if (mandatory_properties.GetInteger("minWidth", &width) && width >= 0 && | |
| 325 mandatory_properties.GetInteger("minHeight", &height) && height >= 0) { | |
| 326 min_size.SetSize(width, height); | |
| 327 } | |
| 328 | |
| 329 // Use optional size constraints if no mandatory ones were provided. | |
| 330 if (options.video_constraints->optional) { | |
| 331 const base::DictionaryValue& optional_properties = | |
| 332 options.video_constraints->optional->additional_properties; | |
| 333 if (optional_properties.GetInteger("maxWidth", &width) && width >= 0 && | |
| 334 optional_properties.GetInteger("maxHeight", &height) && height >= 0) { | |
| 335 if (min_size.IsEmpty()) { | |
| 336 return gfx::Size(width, height); | |
| 337 } else { | |
| 338 return gfx::Size(std::max(width, min_size.width()), | |
| 339 std::max(height, min_size.height())); | |
| 340 } | |
| 341 } | |
| 342 if (min_size.IsEmpty() && | |
| 343 optional_properties.GetInteger("minWidth", &width) && width >= 0 && | |
| 344 optional_properties.GetInteger("minHeight", &height) && height >= 0) { | |
| 345 min_size.SetSize(width, height); | |
| 346 } | |
| 347 } | |
| 348 | |
| 349 // No maximum size was provided, so just return the default size bounded by | |
| 350 // the minimum size. | |
| 351 return gfx::Size(std::max(kDefaultWidth, min_size.width()), | |
| 352 std::max(kDefaultHeight, min_size.height())); | |
| 353 } | |
| 354 | |
| 355 bool TabCapturePrivateRegisterOffscreenTabAsPresentationFunction::RunSync() { | |
| 356 namespace TabCapturePrivate = extensions::api::tab_capture_private; | |
|
not at google - send to devlin
2015/10/02 01:17:26
already in extensions namespace, no extensions::
miu
2015/10/02 18:58:21
N/A, since reverting mostly back to PS6.
| |
| 357 using Params = TabCapturePrivate::RegisterOffscreenTabAsPresentation::Params; | |
| 358 scoped_ptr<Params> params = Params::Create(*args_); | |
| 359 EXTENSION_FUNCTION_VALIDATE(params); | |
| 360 | |
| 361 // Make sure the extension is whitelisted for using this API. | |
| 362 // | |
| 363 // TODO(miu): Use _api_features.json and extensions::Feature library instead. | |
| 364 // http://crbug.com/537732 | |
| 365 const bool is_whitelisted_extension = | |
| 366 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
| 367 switches::kWhitelistedExtensionID) == extension()->id() || | |
| 368 SimpleFeature::IsIdInArray(extension()->id(), kChromecastExtensionIds, | |
| 369 arraysize(kChromecastExtensionIds)) || | |
| 370 SimpleFeature::IsIdInArray(extension()->id(), kMediaRouterExtensionIds, | |
| 371 arraysize(kMediaRouterExtensionIds)); | |
| 372 if (!is_whitelisted_extension) { | |
| 373 error_ = kNotWhitelistedForOffscreenTabApi; | |
| 374 return false; | |
| 375 } | |
| 376 | |
| 377 content::WebContents* const extension_web_contents = GetSenderWebContents(); | |
|
not at google - send to devlin
2015/10/02 01:17:26
This API can only be called from extension pages,
miu
2015/10/02 18:58:21
Done in TabCaptureCaptureOffscreenTabFunction::Run
| |
| 378 if (!extension_web_contents) { | |
| 379 SetError(kMissingExtensionPage); | |
| 380 return false; | |
| 381 } | |
| 382 | |
| 383 OffscreenPresentation* const offscreen_tab = | |
| 384 OffscreenPresentationsOwner::Get(extension_web_contents) | |
| 385 ->FindByOffscreenTabId(params->offscreen_tab_id); | |
| 386 if (!offscreen_tab) { | |
| 387 SetError(kOffscreenTabNotFound); | |
| 388 return false; | |
| 389 } | |
| 390 | |
| 391 // TODO(imcheng): Register offscreen_tab->web_contents() with the | |
| 392 // PresentationRouter. http://crbug.com/513859 | |
| 393 LOG(ERROR) << "NOT IMPLEMENTED: Register with PresentationRouter, id=" | |
|
not at google - send to devlin
2015/10/02 01:17:26
NOT_IMPLEMENTED()?
miu
2015/10/02 18:58:21
Done. (I had it as LOG(ERROR) to confirm the plum
| |
| 394 << params->presentation_id; | |
| 395 | |
| 396 return true; | |
| 397 } | |
| 398 | |
| 198 } // namespace extensions | 399 } // namespace extensions |
| OLD | NEW |