1 // Copyright 2013 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 #include "chrome/browser/media/webrtc_browsertest_base.h" 6 7 #include "base/lazy_instance.h" 8 #include "base/strings/string_util.h" 9 #include "base/strings/stringprintf.h" 10 #include "chrome/browser/chrome_notification_types.h" 11 #include "chrome/browser/infobars/infobar_service.h" 12 #include "chrome/browser/media/media_stream_infobar_delegate.h" 13 #include "chrome/browser/media/webrtc_browsertest_common.h" 14 #include "chrome/browser/ui/browser.h" 15 #include "chrome/browser/ui/browser_tabstrip.h" 16 #include "chrome/browser/ui/tabs/tab_strip_model.h" 17 #include "chrome/test/base/ui_test_utils.h" 18 #include "components/infobars/core/infobar.h" 19 #include "content/public/browser/notification_service.h" 20 #include "content/public/test/browser_test_utils.h" 21 #include "net/test/embedded_test_server/embedded_test_server.h" 22 23 #if defined(OS_WIN) 24 // For fine-grained suppression. 25 #include "base/win/windows_version.h" 26 #endif 27 28 const char WebRtcTestBase::kAudioVideoCallConstraints[] = 29 "{audio: true, video: true}"; 30 const char WebRtcTestBase::kAudioVideoCallConstraintsQVGA[] = 31 "{audio: true, video: {mandatory: {minWidth: 320, maxWidth: 320, " 32 " minHeight: 240, maxHeight: 240}}}"; 33 const char WebRtcTestBase::kAudioVideoCallConstraints360p[] = 34 "{audio: true, video: {mandatory: {minWidth: 640, maxWidth: 640, " 35 " minHeight: 360, maxHeight: 360}}}"; 36 const char WebRtcTestBase::kAudioVideoCallConstraintsVGA[] = 37 "{audio: true, video: {mandatory: {minWidth: 640, maxWidth: 640, " 38 " minHeight: 480, maxHeight: 480}}}"; 39 const char WebRtcTestBase::kAudioVideoCallConstraints720p[] = 40 "{audio: true, video: {mandatory: {minWidth: 1280, maxWidth: 1280, " 41 " minHeight: 720, maxHeight: 720}}}"; 42 const char WebRtcTestBase::kAudioVideoCallConstraints1080p[] = 43 "{audio: true, video: {mandatory: {minWidth: 1920, maxWidth: 1920, " 44 " minHeight: 1080, maxHeight: 1080}}}"; 45 const char WebRtcTestBase::kAudioOnlyCallConstraints[] = "{audio: true}"; 46 const char WebRtcTestBase::kVideoOnlyCallConstraints[] = "{video: true}"; 47 const char WebRtcTestBase::kFailedWithPermissionDeniedError[] = 48 "failed-with-error-PermissionDeniedError"; 49 const char WebRtcTestBase::kFailedWithPermissionDismissedError[] = 50 "failed-with-error-PermissionDismissedError"; 51 52 namespace { 53 54 base::LazyInstance<bool> hit_javascript_errors_ = 55 LAZY_INSTANCE_INITIALIZER; 56 57 // Intercepts all log messages. We always attach this handler but only look at 58 // the results if the test requests so. Note that this will only work if the 59 // WebrtcTestBase-inheriting test cases do not run in parallel (if they did they 60 // would race to look at the log, which is global to all tests). 61 bool JavascriptErrorDetectingLogHandler(int severity, 62 const char* file, 63 int line, 64 size_t message_start, 65 const std::string& str) { 66 if (file == NULL || std::string("CONSOLE") != file) 67 return false; 68 69 bool contains_uncaught = str.find("\"Uncaught ") != std::string::npos; 70 if (severity == logging::LOG_ERROR || 71 (severity == logging::LOG_INFO && contains_uncaught)) { 72 hit_javascript_errors_.Get() = true; 73 } 74 75 return false; 76 } 77 78 } // namespace 79 80 WebRtcTestBase::WebRtcTestBase(): detect_errors_in_javascript_(false) { 81 // The handler gets set for each test method, but that's fine since this 82 // set operation is idempotent. 83 logging::SetLogMessageHandler(&JavascriptErrorDetectingLogHandler); 84 hit_javascript_errors_.Get() = false; 85 86 EnablePixelOutput(); 87 } 88 89 WebRtcTestBase::~WebRtcTestBase() { 90 if (detect_errors_in_javascript_) { 91 EXPECT_FALSE(hit_javascript_errors_.Get()) 92 << "Encountered javascript errors during test execution (Search " 93 << "for Uncaught or ERROR:CONSOLE in the test output)."; 94 } 95 } 96 97 void WebRtcTestBase::GetUserMediaAndAccept( 98 content::WebContents* tab_contents) const { 99 GetUserMediaWithSpecificConstraintsAndAccept(tab_contents, 100 kAudioVideoCallConstraints); 101 } 102 103 void WebRtcTestBase::GetUserMediaWithSpecificConstraintsAndAccept( 104 content::WebContents* tab_contents, 105 const std::string& constraints) const { 106 infobars::InfoBar* infobar = 107 GetUserMediaAndWaitForInfoBar(tab_contents, constraints); 108 infobar->delegate()->AsConfirmInfoBarDelegate()->Accept(); 109 CloseInfoBarInTab(tab_contents, infobar); 110 111 // Wait for WebRTC to call the success callback. 112 const char kOkGotStream[] = "ok-got-stream"; 113 EXPECT_TRUE(test::PollingWaitUntil("obtainGetUserMediaResult()", kOkGotStream, 114 tab_contents)); 115 } 116 117 void WebRtcTestBase::GetUserMediaAndDeny(content::WebContents* tab_contents) { 118 return GetUserMediaWithSpecificConstraintsAndDeny(tab_contents, 119 kAudioVideoCallConstraints); 120 } 121 122 void WebRtcTestBase::GetUserMediaWithSpecificConstraintsAndDeny( 123 content::WebContents* tab_contents, 124 const std::string& constraints) const { 125 infobars::InfoBar* infobar = 126 GetUserMediaAndWaitForInfoBar(tab_contents, constraints); 127 infobar->delegate()->AsConfirmInfoBarDelegate()->Cancel(); 128 CloseInfoBarInTab(tab_contents, infobar); 129 130 // Wait for WebRTC to call the fail callback. 131 EXPECT_TRUE(test::PollingWaitUntil("obtainGetUserMediaResult()", 132 kFailedWithPermissionDeniedError, 133 tab_contents)); 134 } 135 136 void WebRtcTestBase::GetUserMediaAndDismiss( 137 content::WebContents* tab_contents) const { 138 infobars::InfoBar* infobar = 139 GetUserMediaAndWaitForInfoBar(tab_contents, kAudioVideoCallConstraints); 140 infobar->delegate()->InfoBarDismissed(); 141 CloseInfoBarInTab(tab_contents, infobar); 142 143 // A dismiss should be treated like a deny. 144 EXPECT_TRUE(test::PollingWaitUntil("obtainGetUserMediaResult()", 145 kFailedWithPermissionDismissedError, 146 tab_contents)); 147 } 148 149 void WebRtcTestBase::GetUserMedia(content::WebContents* tab_contents, 150 const std::string& constraints) const { 151 // Request user media: this will launch the media stream info bar. 152 std::string result; 153 EXPECT_TRUE(content::ExecuteScriptAndExtractString( 154 tab_contents, "doGetUserMedia(" + constraints + ");", &result)); 155 EXPECT_EQ("ok-requested", result); 156 } 157 158 infobars::InfoBar* WebRtcTestBase::GetUserMediaAndWaitForInfoBar( 159 content::WebContents* tab_contents, 160 const std::string& constraints) const { 161 content::WindowedNotificationObserver infobar_added( 162 chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED, 163 content::NotificationService::AllSources()); 164 165 // Request user media: this will launch the media stream info bar. 166 GetUserMedia(tab_contents, constraints); 167 168 // Wait for the bar to pop up, then return it. 169 infobar_added.Wait(); 170 content::Details<infobars::InfoBar::AddedDetails> details( 171 infobar_added.details()); 172 EXPECT_TRUE(details->delegate()->AsMediaStreamInfoBarDelegate()); 173 return details.ptr(); 174 } 175 176 content::WebContents* WebRtcTestBase::OpenPageAndGetUserMediaInNewTab( 177 const GURL& url) const { 178 return OpenPageAndGetUserMediaInNewTabWithConstraints( 179 url, kAudioVideoCallConstraints); 180 } 181 182 content::WebContents* 183 WebRtcTestBase::OpenPageAndGetUserMediaInNewTabWithConstraints( 184 const GURL& url, 185 const std::string& constraints) const { 186 chrome::AddTabAt(browser(), GURL(), -1, true); 187 ui_test_utils::NavigateToURL(browser(), url); 188 #if defined (OS_LINUX) 189 // Load the page again on Linux to work around crbug.com/281268. 190 ui_test_utils::NavigateToURL(browser(), url); 191 #endif 192 content::WebContents* new_tab = 193 browser()->tab_strip_model()->GetActiveWebContents(); 194 GetUserMediaWithSpecificConstraintsAndAccept(new_tab, constraints); 195 return new_tab; 196 } 197 198 content::WebContents* WebRtcTestBase::OpenTestPageAndGetUserMediaInNewTab( 199 const std::string& test_page) const { 200 return OpenPageAndGetUserMediaInNewTab( 201 embedded_test_server()->GetURL(test_page)); 202 } 203 204 content::WebContents* WebRtcTestBase::OpenPageAndAcceptUserMedia( 205 const GURL& url) const { 206 content::WindowedNotificationObserver infobar_added( 207 chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED, 208 content::NotificationService::AllSources()); 209 210 ui_test_utils::NavigateToURL(browser(), url); 211 212 infobar_added.Wait(); 213 214 content::WebContents* tab_contents = 215 browser()->tab_strip_model()->GetActiveWebContents(); 216 content::Details<infobars::InfoBar::AddedDetails> details( 217 infobar_added.details()); 218 infobars::InfoBar* infobar = details.ptr(); 219 EXPECT_TRUE(infobar); 220 infobar->delegate()->AsMediaStreamInfoBarDelegate()->Accept(); 221 222 CloseInfoBarInTab(tab_contents, infobar); 223 return tab_contents; 224 } 225 226 void WebRtcTestBase::CloseInfoBarInTab(content::WebContents* tab_contents, 227 infobars::InfoBar* infobar) const { 228 content::WindowedNotificationObserver infobar_removed( 229 chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED, 230 content::NotificationService::AllSources()); 231 232 InfoBarService* infobar_service = 233 InfoBarService::FromWebContents(tab_contents); 234 infobar_service->RemoveInfoBar(infobar); 235 236 infobar_removed.Wait(); 237 } 238 239 void WebRtcTestBase::CloseLastLocalStream( 240 content::WebContents* tab_contents) const { 241 EXPECT_EQ("ok-stopped", 242 ExecuteJavascript("stopLocalStream();", tab_contents)); 243 } 244 245 // Convenience method which executes the provided javascript in the context 246 // of the provided web contents and returns what it evaluated to. 247 std::string WebRtcTestBase::ExecuteJavascript( 248 const std::string& javascript, 249 content::WebContents* tab_contents) const { 250 std::string result; 251 EXPECT_TRUE(content::ExecuteScriptAndExtractString( 252 tab_contents, javascript, &result)); 253 return result; 254 } 255 256 void WebRtcTestBase::SetupPeerconnectionWithLocalStream( 257 content::WebContents* tab) const { 258 EXPECT_EQ("ok-peerconnection-created", 259 ExecuteJavascript("preparePeerConnection()", tab)); 260 EXPECT_EQ("ok-added", ExecuteJavascript("addLocalStream()", tab)); 261 } 262 263 std::string WebRtcTestBase::CreateLocalOffer( 264 content::WebContents* from_tab) const { 265 std::string response = ExecuteJavascript("createLocalOffer({})", from_tab); 266 EXPECT_EQ("ok-", response.substr(0, 3)) << "Failed to create local offer: " 267 << response; 268 269 std::string local_offer = response.substr(3); 270 return local_offer; 271 } 272 273 std::string WebRtcTestBase::CreateAnswer(std::string local_offer, 274 content::WebContents* to_tab) const { 275 std::string javascript = 276 base::StringPrintf("receiveOfferFromPeer('%s', {})", local_offer.c_str()); 277 std::string response = ExecuteJavascript(javascript, to_tab); 278 EXPECT_EQ("ok-", response.substr(0, 3)) 279 << "Receiving peer failed to receive offer and create answer: " 280 << response; 281 282 std::string answer = response.substr(3); 283 return answer; 284 } 285 286 void WebRtcTestBase::ReceiveAnswer(std::string answer, 287 content::WebContents* from_tab) const { 288 ASSERT_EQ( 289 "ok-accepted-answer", 290 ExecuteJavascript( 291 base::StringPrintf("receiveAnswerFromPeer('%s')", answer.c_str()), 292 from_tab)); 293 } 294 295 void WebRtcTestBase::GatherAndSendIceCandidates( 296 content::WebContents* from_tab, 297 content::WebContents* to_tab) const { 298 std::string ice_candidates = 299 ExecuteJavascript("getAllIceCandidates()", from_tab); 300 301 EXPECT_EQ("ok-received-candidates", ExecuteJavascript( 302 base::StringPrintf("receiveIceCandidates('%s')", ice_candidates.c_str()), 303 to_tab)); 304 } 305 306 void WebRtcTestBase::NegotiateCall(content::WebContents* from_tab, 307 content::WebContents* to_tab) const { 308 std::string local_offer = CreateLocalOffer(from_tab); 309 std::string answer = CreateAnswer(local_offer, to_tab); 310 ReceiveAnswer(answer, from_tab); 311 312 // Send all ICE candidates (wait for gathering to finish if necessary). 313 GatherAndSendIceCandidates(to_tab, from_tab); 314 GatherAndSendIceCandidates(from_tab, to_tab); 315 } 316 317 void WebRtcTestBase::HangUp(content::WebContents* from_tab) const { 318 EXPECT_EQ("ok-call-hung-up", ExecuteJavascript("hangUp()", from_tab)); 319 } 320 321 void WebRtcTestBase::DetectErrorsInJavaScript() { 322 detect_errors_in_javascript_ = true; 323 } 324 325 void WebRtcTestBase::StartDetectingVideo( 326 content::WebContents* tab_contents, 327 const std::string& video_element) const { 328 std::string javascript = base::StringPrintf( 329 "startDetection('%s', 320, 240)", video_element.c_str()); 330 EXPECT_EQ("ok-started", ExecuteJavascript(javascript, tab_contents)); 331 } 332 333 void WebRtcTestBase::WaitForVideoToPlay( 334 content::WebContents* tab_contents) const { 335 EXPECT_TRUE(test::PollingWaitUntil("isVideoPlaying()", "video-playing", 336 tab_contents)); 337 } 338 339 std::string WebRtcTestBase::GetStreamSize( 340 content::WebContents* tab_contents, 341 const std::string& video_element) const { 342 std::string javascript = 343 base::StringPrintf("getStreamSize('%s')", video_element.c_str()); 344 std::string result = ExecuteJavascript(javascript, tab_contents); 345 EXPECT_TRUE(StartsWithASCII(result, "ok-", true)); 346 return result.substr(3); 347 } 348 349 bool WebRtcTestBase::HasWebcamAvailableOnSystem( 350 content::WebContents* tab_contents) const { 351 std::string result = 352 ExecuteJavascript("hasVideoInputDeviceOnSystem();", tab_contents); 353 return result == "has-video-input-device"; 354 } 355 356 bool WebRtcTestBase::OnWinXp() const { 357 #if defined(OS_WIN) 358 return base::win::GetVersion() <= base::win::VERSION_XP; 359 #else 360 return false; 361 #endif 362 } 363 364 bool WebRtcTestBase::OnWin8() const { 365 #if defined(OS_WIN) 366 return base::win::GetVersion() > base::win::VERSION_WIN7; 367 #else 368 return false; 369 #endif 370 } 371