// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/media/webrtc_browsertest_base.h" #include "base/lazy_instance.h" #include "base/strings/stringprintf.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/infobars/infobar.h" #include "chrome/browser/infobars/infobar_service.h" #include "chrome/browser/media/media_stream_infobar_delegate.h" #include "chrome/browser/media/webrtc_browsertest_common.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/test/base/ui_test_utils.h" #include "content/public/browser/notification_service.h" #include "content/public/test/browser_test_utils.h" const char WebRtcTestBase::kAudioVideoCallConstraints[] = "'{audio: true, video: true}'"; const char WebRtcTestBase::kAudioOnlyCallConstraints[] = "'{audio: true}'"; const char WebRtcTestBase::kVideoOnlyCallConstraints[] = "'{video: true}'"; const char WebRtcTestBase::kFailedWithPermissionDeniedError[] = "failed-with-error-PermissionDeniedError"; namespace { base::LazyInstance<bool> hit_javascript_errors_ = LAZY_INSTANCE_INITIALIZER; // Intercepts all log messages. We always attach this handler but only look at // the results if the test requests so. Note that this will only work if the // WebrtcTestBase-inheriting test cases do not run in parallel (if they did they // would race to look at the log, which is global to all tests). bool JavascriptErrorDetectingLogHandler(int severity, const char* file, int line, size_t message_start, const std::string& str) { if (file == NULL || std::string("CONSOLE") != file) return false; bool contains_uncaught = str.find("\"Uncaught ") != std::string::npos; if (severity == logging::LOG_ERROR || (severity == logging::LOG_INFO && contains_uncaught)) { hit_javascript_errors_.Get() = true; } return false; } } // namespace WebRtcTestBase::WebRtcTestBase(): detect_errors_in_javascript_(false) { // The handler gets set for each test method, but that's fine since this // set operation is idempotent. logging::SetLogMessageHandler(&JavascriptErrorDetectingLogHandler); hit_javascript_errors_.Get() = false; } WebRtcTestBase::~WebRtcTestBase() { if (detect_errors_in_javascript_) { EXPECT_FALSE(hit_javascript_errors_.Get()) << "Encountered javascript errors during test execution (Search " << "for Uncaught or ERROR:CONSOLE in the test output)."; } } void WebRtcTestBase::GetUserMediaAndAccept( content::WebContents* tab_contents) const { GetUserMediaWithSpecificConstraintsAndAccept(tab_contents, kAudioVideoCallConstraints); } void WebRtcTestBase::GetUserMediaWithSpecificConstraintsAndAccept( content::WebContents* tab_contents, const std::string& constraints) const { InfoBar* infobar = GetUserMediaAndWaitForInfoBar(tab_contents, constraints); infobar->delegate()->AsConfirmInfoBarDelegate()->Accept(); CloseInfoBarInTab(tab_contents, infobar); // Wait for WebRTC to call the success callback. const char kOkGotStream[] = "ok-got-stream"; EXPECT_TRUE(PollingWaitUntil("obtainGetUserMediaResult()", kOkGotStream, tab_contents)); } void WebRtcTestBase::GetUserMediaAndDeny(content::WebContents* tab_contents) { return GetUserMediaWithSpecificConstraintsAndDeny(tab_contents, kAudioVideoCallConstraints); } void WebRtcTestBase::GetUserMediaWithSpecificConstraintsAndDeny( content::WebContents* tab_contents, const std::string& constraints) const { InfoBar* infobar = GetUserMediaAndWaitForInfoBar(tab_contents, constraints); infobar->delegate()->AsConfirmInfoBarDelegate()->Cancel(); CloseInfoBarInTab(tab_contents, infobar); // Wait for WebRTC to call the fail callback. EXPECT_TRUE(PollingWaitUntil("obtainGetUserMediaResult()", kFailedWithPermissionDeniedError, tab_contents)); } void WebRtcTestBase::GetUserMediaAndDismiss( content::WebContents* tab_contents) const { InfoBar* infobar = GetUserMediaAndWaitForInfoBar(tab_contents, kAudioVideoCallConstraints); infobar->delegate()->InfoBarDismissed(); CloseInfoBarInTab(tab_contents, infobar); // A dismiss should be treated like a deny. EXPECT_TRUE(PollingWaitUntil("obtainGetUserMediaResult()", kFailedWithPermissionDeniedError, tab_contents)); } void WebRtcTestBase::GetUserMedia(content::WebContents* tab_contents, const std::string& constraints) const { // Request user media: this will launch the media stream info bar. std::string result; EXPECT_TRUE(content::ExecuteScriptAndExtractString( tab_contents, "doGetUserMedia(" + constraints + ");", &result)); EXPECT_EQ("ok-requested", result); } InfoBar* WebRtcTestBase::GetUserMediaAndWaitForInfoBar( content::WebContents* tab_contents, const std::string& constraints) const { content::WindowedNotificationObserver infobar_added( chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED, content::NotificationService::AllSources()); // Request user media: this will launch the media stream info bar. GetUserMedia(tab_contents, constraints); // Wait for the bar to pop up, then return it. infobar_added.Wait(); content::Details<InfoBar::AddedDetails> details(infobar_added.details()); EXPECT_TRUE(details->delegate()->AsMediaStreamInfoBarDelegate()); return details.ptr(); } content::WebContents* WebRtcTestBase::OpenPageAndGetUserMediaInNewTab( const GURL& url) const { chrome::AddTabAt(browser(), GURL(), -1, true); ui_test_utils::NavigateToURL(browser(), url); #if defined (OS_LINUX) // Load the page again on Linux to work around crbug.com/281268. ui_test_utils::NavigateToURL(browser(), url); #endif content::WebContents* new_tab = browser()->tab_strip_model()->GetActiveWebContents(); GetUserMediaAndAccept(new_tab); return new_tab; } content::WebContents* WebRtcTestBase::OpenPageAndAcceptUserMedia( const GURL& url) const { content::WindowedNotificationObserver infobar_added( chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED, content::NotificationService::AllSources()); ui_test_utils::NavigateToURL(browser(), url); infobar_added.Wait(); content::WebContents* tab_contents = browser()->tab_strip_model()->GetActiveWebContents(); content::Details<InfoBar::AddedDetails> details(infobar_added.details()); InfoBar* infobar = details.ptr(); EXPECT_TRUE(infobar); infobar->delegate()->AsMediaStreamInfoBarDelegate()->Accept(); CloseInfoBarInTab(tab_contents, infobar); return tab_contents; } void WebRtcTestBase::CloseInfoBarInTab( content::WebContents* tab_contents, InfoBar* infobar) const { content::WindowedNotificationObserver infobar_removed( chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED, content::NotificationService::AllSources()); InfoBarService* infobar_service = InfoBarService::FromWebContents(tab_contents); infobar_service->RemoveInfoBar(infobar); infobar_removed.Wait(); } // Convenience method which executes the provided javascript in the context // of the provided web contents and returns what it evaluated to. std::string WebRtcTestBase::ExecuteJavascript( const std::string& javascript, content::WebContents* tab_contents) const { std::string result; EXPECT_TRUE(content::ExecuteScriptAndExtractString( tab_contents, javascript, &result)); return result; } // The peer connection server lets our two tabs find each other and talk to // each other (e.g. it is the application-specific "signaling solution"). void WebRtcTestBase::ConnectToPeerConnectionServer( const std::string& peer_name, content::WebContents* tab_contents) const { std::string javascript = base::StringPrintf( "connect('http://localhost:%s', '%s');", PeerConnectionServerRunner::kDefaultPort, peer_name.c_str()); EXPECT_EQ("ok-connected", ExecuteJavascript(javascript, tab_contents)); } void WebRtcTestBase::DetectErrorsInJavaScript() { detect_errors_in_javascript_ = true; }