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).
JavascriptErrorDetectingLogHandler(int severity,const char * file,int line,size_t message_start,const std::string & str)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
WebRtcTestBase()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
~WebRtcTestBase()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
GetUserMediaAndAccept(content::WebContents * tab_contents) const97 void WebRtcTestBase::GetUserMediaAndAccept(
98 content::WebContents* tab_contents) const {
99 GetUserMediaWithSpecificConstraintsAndAccept(tab_contents,
100 kAudioVideoCallConstraints);
101 }
102
GetUserMediaWithSpecificConstraintsAndAccept(content::WebContents * tab_contents,const std::string & constraints) const103 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
GetUserMediaAndDeny(content::WebContents * tab_contents)117 void WebRtcTestBase::GetUserMediaAndDeny(content::WebContents* tab_contents) {
118 return GetUserMediaWithSpecificConstraintsAndDeny(tab_contents,
119 kAudioVideoCallConstraints);
120 }
121
GetUserMediaWithSpecificConstraintsAndDeny(content::WebContents * tab_contents,const std::string & constraints) const122 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
GetUserMediaAndDismiss(content::WebContents * tab_contents) const136 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
GetUserMedia(content::WebContents * tab_contents,const std::string & constraints) const149 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
GetUserMediaAndWaitForInfoBar(content::WebContents * tab_contents,const std::string & constraints) const158 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
OpenPageAndGetUserMediaInNewTab(const GURL & url) const176 content::WebContents* WebRtcTestBase::OpenPageAndGetUserMediaInNewTab(
177 const GURL& url) const {
178 return OpenPageAndGetUserMediaInNewTabWithConstraints(
179 url, kAudioVideoCallConstraints);
180 }
181
182 content::WebContents*
OpenPageAndGetUserMediaInNewTabWithConstraints(const GURL & url,const std::string & constraints) const183 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
OpenTestPageAndGetUserMediaInNewTab(const std::string & test_page) const198 content::WebContents* WebRtcTestBase::OpenTestPageAndGetUserMediaInNewTab(
199 const std::string& test_page) const {
200 return OpenPageAndGetUserMediaInNewTab(
201 embedded_test_server()->GetURL(test_page));
202 }
203
OpenPageAndAcceptUserMedia(const GURL & url) const204 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
CloseInfoBarInTab(content::WebContents * tab_contents,infobars::InfoBar * infobar) const226 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
CloseLastLocalStream(content::WebContents * tab_contents) const239 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.
ExecuteJavascript(const std::string & javascript,content::WebContents * tab_contents) const247 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
SetupPeerconnectionWithLocalStream(content::WebContents * tab) const256 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
CreateLocalOffer(content::WebContents * from_tab) const263 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
CreateAnswer(std::string local_offer,content::WebContents * to_tab) const273 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
ReceiveAnswer(std::string answer,content::WebContents * from_tab) const286 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
GatherAndSendIceCandidates(content::WebContents * from_tab,content::WebContents * to_tab) const295 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
NegotiateCall(content::WebContents * from_tab,content::WebContents * to_tab) const306 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
HangUp(content::WebContents * from_tab) const317 void WebRtcTestBase::HangUp(content::WebContents* from_tab) const {
318 EXPECT_EQ("ok-call-hung-up", ExecuteJavascript("hangUp()", from_tab));
319 }
320
DetectErrorsInJavaScript()321 void WebRtcTestBase::DetectErrorsInJavaScript() {
322 detect_errors_in_javascript_ = true;
323 }
324
StartDetectingVideo(content::WebContents * tab_contents,const std::string & video_element) const325 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
WaitForVideoToPlay(content::WebContents * tab_contents) const333 void WebRtcTestBase::WaitForVideoToPlay(
334 content::WebContents* tab_contents) const {
335 EXPECT_TRUE(test::PollingWaitUntil("isVideoPlaying()", "video-playing",
336 tab_contents));
337 }
338
GetStreamSize(content::WebContents * tab_contents,const std::string & video_element) const339 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
HasWebcamAvailableOnSystem(content::WebContents * tab_contents) const349 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
OnWinXp() const356 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
OnWin8() const364 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