blob: 52b55dcd502d63930f828d9cddf85ecf17cba45a [file] [log] [blame]
// 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/string_util.h"
#include "base/strings/stringprintf.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/browser/ui/website_settings/permission_bubble_manager.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/test/browser_test_utils.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#if defined(OS_WIN)
// For fine-grained suppression.
#include "base/win/windows_version.h"
#endif
const char WebRtcTestBase::kAudioVideoCallConstraints[] =
"{audio: true, video: true}";
const char WebRtcTestBase::kVideoCallConstraintsQVGA[] =
"{video: {mandatory: {minWidth: 320, maxWidth: 320, "
" minHeight: 240, maxHeight: 240}}}";
const char WebRtcTestBase::kVideoCallConstraints360p[] =
"{video: {mandatory: {minWidth: 640, maxWidth: 640, "
" minHeight: 360, maxHeight: 360}}}";
const char WebRtcTestBase::kVideoCallConstraintsVGA[] =
"{video: {mandatory: {minWidth: 640, maxWidth: 640, "
" minHeight: 480, maxHeight: 480}}}";
const char WebRtcTestBase::kVideoCallConstraints720p[] =
"{video: {mandatory: {minWidth: 1280, maxWidth: 1280, "
" minHeight: 720, maxHeight: 720}}}";
const char WebRtcTestBase::kVideoCallConstraints1080p[] =
"{video: {mandatory: {minWidth: 1920, maxWidth: 1920, "
" minHeight: 1080, maxHeight: 1080}}}";
const char WebRtcTestBase::kAudioOnlyCallConstraints[] = "{audio: true}";
const char WebRtcTestBase::kVideoOnlyCallConstraints[] = "{video: true}";
const char WebRtcTestBase::kOkGotStream[] = "ok-got-stream";
const char WebRtcTestBase::kFailedWithPermissionDeniedError[] =
"failed-with-error-PermissionDeniedError";
const char WebRtcTestBase::kFailedWithPermissionDismissedError[] =
"failed-with-error-PermissionDismissedError";
const char WebRtcTestBase::kAudioVideoCallConstraints360p[] =
"{audio: true, video: {mandatory: {minWidth: 640, maxWidth: 640, "
" minHeight: 360, maxHeight: 360}}}";
const char WebRtcTestBase::kAudioVideoCallConstraints720p[] =
"{audio: true, video: {mandatory: {minWidth: 1280, maxWidth: 1280, "
" minHeight: 720, maxHeight: 720}}}";
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;
}
// PermissionRequestObserver ---------------------------------------------------
// Used to observe the creation of permission prompt without responding.
class PermissionRequestObserver : public PermissionBubbleManager::Observer {
public:
explicit PermissionRequestObserver(content::WebContents* web_contents)
: bubble_manager_(PermissionBubbleManager::FromWebContents(web_contents)),
request_shown_(false),
message_loop_runner_(new content::MessageLoopRunner) {
bubble_manager_->AddObserver(this);
}
~PermissionRequestObserver() override {
// Safe to remove twice if it happens.
bubble_manager_->RemoveObserver(this);
}
void Wait() { message_loop_runner_->Run(); }
bool request_shown() { return request_shown_; }
private:
// PermissionBubbleManager::Observer
void OnBubbleAdded() override {
request_shown_ = true;
bubble_manager_->RemoveObserver(this);
message_loop_runner_->Quit();
}
PermissionBubbleManager* bubble_manager_;
bool request_shown_;
scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
DISALLOW_COPY_AND_ASSIGN(PermissionRequestObserver);
};
} // 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;
EnablePixelOutput();
}
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).";
}
}
bool WebRtcTestBase::GetUserMediaAndAccept(
content::WebContents* tab_contents) const {
return GetUserMediaWithSpecificConstraintsAndAccept(
tab_contents, kAudioVideoCallConstraints);
}
bool WebRtcTestBase::GetUserMediaWithSpecificConstraintsAndAccept(
content::WebContents* tab_contents,
const std::string& constraints) const {
std::string result;
PermissionBubbleManager::FromWebContents(tab_contents)
->set_auto_response_for_test(PermissionBubbleManager::ACCEPT_ALL);
PermissionRequestObserver permissionRequestObserver(tab_contents);
GetUserMedia(tab_contents, constraints);
EXPECT_TRUE(permissionRequestObserver.request_shown());
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
tab_contents->GetMainFrame(), "obtainGetUserMediaResult();", &result));
return kOkGotStream == result;
}
void WebRtcTestBase::GetUserMediaAndDeny(content::WebContents* tab_contents) {
return GetUserMediaWithSpecificConstraintsAndDeny(tab_contents,
kAudioVideoCallConstraints);
}
void WebRtcTestBase::GetUserMediaWithSpecificConstraintsAndDeny(
content::WebContents* tab_contents,
const std::string& constraints) const {
std::string result;
PermissionBubbleManager::FromWebContents(tab_contents)
->set_auto_response_for_test(PermissionBubbleManager::DENY_ALL);
PermissionRequestObserver permissionRequestObserver(tab_contents);
GetUserMedia(tab_contents, constraints);
EXPECT_TRUE(permissionRequestObserver.request_shown());
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
tab_contents->GetMainFrame(), "obtainGetUserMediaResult();", &result));
EXPECT_EQ(kFailedWithPermissionDeniedError, result);
}
void WebRtcTestBase::GetUserMediaAndDismiss(
content::WebContents* tab_contents) const {
std::string result;
PermissionBubbleManager::FromWebContents(tab_contents)
->set_auto_response_for_test(PermissionBubbleManager::DISMISS);
PermissionRequestObserver permissionRequestObserver(tab_contents);
GetUserMedia(tab_contents, kAudioVideoCallConstraints);
EXPECT_TRUE(permissionRequestObserver.request_shown());
// A dismiss should be treated like a deny.
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
tab_contents->GetMainFrame(), "obtainGetUserMediaResult();", &result));
EXPECT_EQ(kFailedWithPermissionDismissedError, result);
}
void WebRtcTestBase::GetUserMediaAndExpectAutoAcceptWithoutPrompt(
content::WebContents* tab_contents) const {
std::string result;
// We issue a GetUserMedia() request. We expect that the origin already has a
// sticky "accept" permission (e.g. because the caller previously called
// GetUserMediaAndAccept()), and therefore the GetUserMedia() request
// automatically succeeds without a prompt.
// If the caller made a mistake, a prompt may show up instead. For this case,
// we set an auto-response to avoid leaving the prompt hanging. The choice of
// DENY_ALL makes sure that the response to the prompt doesn't accidentally
// result in a newly granted media stream permission.
PermissionBubbleManager::FromWebContents(tab_contents)
->set_auto_response_for_test(PermissionBubbleManager::DENY_ALL);
PermissionRequestObserver permissionRequestObserver(tab_contents);
GetUserMedia(tab_contents, kAudioVideoCallConstraints);
EXPECT_FALSE(permissionRequestObserver.request_shown());
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
tab_contents->GetMainFrame(), "obtainGetUserMediaResult();", &result));
EXPECT_EQ(kOkGotStream, result);
}
void WebRtcTestBase::GetUserMediaAndExpectAutoDenyWithoutPrompt(
content::WebContents* tab_contents) const {
std::string result;
// We issue a GetUserMedia() request. We expect that the origin already has a
// sticky "deny" permission (e.g. because the caller previously called
// GetUserMediaAndDeny()), and therefore the GetUserMedia() request
// automatically succeeds without a prompt.
// If the caller made a mistake, a prompt may show up instead. For this case,
// we set an auto-response to avoid leaving the prompt hanging. The choice of
// ACCEPT_ALL makes sure that the response to the prompt doesn't accidentally
// result in a newly granted media stream permission.
PermissionBubbleManager::FromWebContents(tab_contents)
->set_auto_response_for_test(PermissionBubbleManager::ACCEPT_ALL);
PermissionRequestObserver permissionRequestObserver(tab_contents);
GetUserMedia(tab_contents, kAudioVideoCallConstraints);
EXPECT_FALSE(permissionRequestObserver.request_shown());
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
tab_contents->GetMainFrame(), "obtainGetUserMediaResult();", &result));
EXPECT_EQ(kFailedWithPermissionDeniedError, result);
}
void WebRtcTestBase::GetUserMedia(content::WebContents* tab_contents,
const std::string& constraints) const {
// Request user media: this will launch the media stream info bar or bubble.
std::string result;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
tab_contents, "doGetUserMedia(" + constraints + ");", &result));
EXPECT_TRUE(result == "request-callback-denied" ||
result == "request-callback-granted");
}
content::WebContents* WebRtcTestBase::OpenPageAndGetUserMediaInNewTab(
const GURL& url) const {
return OpenPageAndGetUserMediaInNewTabWithConstraints(
url, kAudioVideoCallConstraints);
}
content::WebContents*
WebRtcTestBase::OpenPageAndGetUserMediaInNewTabWithConstraints(
const GURL& url,
const std::string& constraints) const {
chrome::AddTabAt(browser(), GURL(), -1, true);
ui_test_utils::NavigateToURL(browser(), url);
content::WebContents* new_tab =
browser()->tab_strip_model()->GetActiveWebContents();
// Accept if necessary, but don't expect a prompt (because auto-accept is also
// okay).
PermissionBubbleManager::FromWebContents(new_tab)
->set_auto_response_for_test(PermissionBubbleManager::ACCEPT_ALL);
GetUserMedia(new_tab, constraints);
std::string result;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
new_tab->GetMainFrame(), "obtainGetUserMediaResult();", &result));
EXPECT_EQ(kOkGotStream, result);
return new_tab;
}
content::WebContents* WebRtcTestBase::OpenTestPageAndGetUserMediaInNewTab(
const std::string& test_page) const {
return OpenPageAndGetUserMediaInNewTab(
embedded_test_server()->GetURL(test_page));
}
void WebRtcTestBase::CloseLastLocalStream(
content::WebContents* tab_contents) const {
EXPECT_EQ("ok-stopped",
ExecuteJavascript("stopLocalStream();", tab_contents));
}
// 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;
}
void WebRtcTestBase::SetupPeerconnectionWithLocalStream(
content::WebContents* tab) const {
SetupPeerconnectionWithoutLocalStream(tab);
EXPECT_EQ("ok-added", ExecuteJavascript("addLocalStream()", tab));
}
void WebRtcTestBase::SetupPeerconnectionWithoutLocalStream(
content::WebContents* tab) const {
EXPECT_EQ("ok-peerconnection-created",
ExecuteJavascript("preparePeerConnection()", tab));
}
std::string WebRtcTestBase::CreateLocalOffer(
content::WebContents* from_tab) const {
std::string response = ExecuteJavascript("createLocalOffer({})", from_tab);
EXPECT_EQ("ok-", response.substr(0, 3)) << "Failed to create local offer: "
<< response;
std::string local_offer = response.substr(3);
return local_offer;
}
std::string WebRtcTestBase::CreateAnswer(std::string local_offer,
content::WebContents* to_tab) const {
std::string javascript =
base::StringPrintf("receiveOfferFromPeer('%s', {})", local_offer.c_str());
std::string response = ExecuteJavascript(javascript, to_tab);
EXPECT_EQ("ok-", response.substr(0, 3))
<< "Receiving peer failed to receive offer and create answer: "
<< response;
std::string answer = response.substr(3);
return answer;
}
void WebRtcTestBase::ReceiveAnswer(std::string answer,
content::WebContents* from_tab) const {
ASSERT_EQ(
"ok-accepted-answer",
ExecuteJavascript(
base::StringPrintf("receiveAnswerFromPeer('%s')", answer.c_str()),
from_tab));
}
void WebRtcTestBase::GatherAndSendIceCandidates(
content::WebContents* from_tab,
content::WebContents* to_tab) const {
std::string ice_candidates =
ExecuteJavascript("getAllIceCandidates()", from_tab);
EXPECT_EQ("ok-received-candidates", ExecuteJavascript(
base::StringPrintf("receiveIceCandidates('%s')", ice_candidates.c_str()),
to_tab));
}
void WebRtcTestBase::NegotiateCall(content::WebContents* from_tab,
content::WebContents* to_tab) const {
std::string local_offer = CreateLocalOffer(from_tab);
std::string answer = CreateAnswer(local_offer, to_tab);
ReceiveAnswer(answer, from_tab);
// Send all ICE candidates (wait for gathering to finish if necessary).
GatherAndSendIceCandidates(to_tab, from_tab);
GatherAndSendIceCandidates(from_tab, to_tab);
}
void WebRtcTestBase::HangUp(content::WebContents* from_tab) const {
EXPECT_EQ("ok-call-hung-up", ExecuteJavascript("hangUp()", from_tab));
}
void WebRtcTestBase::DetectErrorsInJavaScript() {
detect_errors_in_javascript_ = true;
}
void WebRtcTestBase::StartDetectingVideo(
content::WebContents* tab_contents,
const std::string& video_element) const {
std::string javascript = base::StringPrintf(
"startDetection('%s', 320, 240)", video_element.c_str());
EXPECT_EQ("ok-started", ExecuteJavascript(javascript, tab_contents));
}
bool WebRtcTestBase::WaitForVideoToPlay(
content::WebContents* tab_contents) const {
bool is_video_playing = test::PollingWaitUntil(
"isVideoPlaying()", "video-playing", tab_contents);
EXPECT_TRUE(is_video_playing);
return is_video_playing;
}
std::string WebRtcTestBase::GetStreamSize(
content::WebContents* tab_contents,
const std::string& video_element) const {
std::string javascript =
base::StringPrintf("getStreamSize('%s')", video_element.c_str());
std::string result = ExecuteJavascript(javascript, tab_contents);
EXPECT_TRUE(base::StartsWith(result, "ok-", base::CompareCase::SENSITIVE));
return result.substr(3);
}
bool WebRtcTestBase::HasWebcamAvailableOnSystem(
content::WebContents* tab_contents) const {
std::string result =
ExecuteJavascript("hasVideoInputDeviceOnSystem();", tab_contents);
return result == "has-video-input-device";
}
bool WebRtcTestBase::OnWinXp() const {
#if defined(OS_WIN)
return base::win::GetVersion() <= base::win::VERSION_XP;
#else
return false;
#endif
}
bool WebRtcTestBase::OnWin8() const {
#if defined(OS_WIN)
return base::win::GetVersion() > base::win::VERSION_WIN7;
#else
return false;
#endif
}