1 // Copyright 2018 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 <memory>
6 #include <string>
7 #include <vector>
8
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/macros.h"
13 #include "base/run_loop.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/test/metrics/histogram_tester.h"
16 #include "base/test/scoped_feature_list.h"
17 #include "base/threading/sequenced_task_runner_handle.h"
18 #include "base/threading/thread_restrictions.h"
19 #include "chrome/browser/download/download_prefs.h"
20 #include "chrome/browser/metrics/subprocess_metrics_provider.h"
21 #include "chrome/browser/predictors/loading_predictor_config.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/ui/browser.h"
24 #include "chrome/browser/ui/tabs/tab_strip_model.h"
25 #include "chrome/common/chrome_features.h"
26 #include "chrome/common/pref_names.h"
27 #include "chrome/test/base/in_process_browser_test.h"
28 #include "chrome/test/base/ui_test_utils.h"
29 #include "components/prefs/pref_service.h"
30 #include "content/public/browser/web_contents_observer.h"
31 #include "content/public/test/download_test_observer.h"
32 #include "content/public/test/test_navigation_observer.h"
33 #include "net/base/filename_util.h"
34 #include "net/base/net_errors.h"
35 #include "net/http/http_response_info.h"
36 #include "net/test/embedded_test_server/controllable_http_response.h"
37 #include "net/test/embedded_test_server/embedded_test_server.h"
38 #include "testing/gtest/include/gtest/gtest.h"
39 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
40 #include "ui/base/window_open_disposition.h"
41 #include "url/gurl.h"
42
43 namespace content {
44 namespace {
45
46 // Used for the main frame, in subresource tests.
47 constexpr char kUninterestingMainFramePath[] =
48 "/uninteresting/main_frame/path/";
49
50 // Used for the "interesting" request individual tests focus on.
51 constexpr char kInterestingPath[] = "/interesting/path/";
52
53 enum class RequestType {
54 kMainFrame,
55 kSubFrame,
56 kImage,
57 kScript,
58 };
59
60 enum class HeadersReceived {
61 kHeadersReceived,
62 kNoHeadersReceived,
63 };
64
65 enum class NetworkAccessed {
66 kNetworkAccessed,
67 kNoNetworkAccessed,
68 };
69
70 // Utility class to wait until the main resource load is complete. This is to
71 // make sure, in the cancel tests, the main resource is fully loaded before the
72 // navigation is cancelled, to ensure the main frame load histograms are in a
73 // consistent state, and can be checked at the end of each test. To avoid races,
74 // create this class before starting a navigation.
75 class WaitForMainFrameResourceObserver : public content::WebContentsObserver {
76 public:
WaitForMainFrameResourceObserver(WebContents * web_contents)77 explicit WaitForMainFrameResourceObserver(WebContents* web_contents)
78 : content::WebContentsObserver(web_contents) {}
~WaitForMainFrameResourceObserver()79 ~WaitForMainFrameResourceObserver() override {}
80
81 // content::WebContentsObserver implementation:
ResourceLoadComplete(RenderFrameHost * render_frame_host,const content::GlobalRequestID & request_id,const blink::mojom::ResourceLoadInfo & resource_load_info)82 void ResourceLoadComplete(
83 RenderFrameHost* render_frame_host,
84 const content::GlobalRequestID& request_id,
85 const blink::mojom::ResourceLoadInfo& resource_load_info) override {
86 EXPECT_EQ(network::mojom::RequestDestination::kDocument,
87 resource_load_info.request_destination);
88 EXPECT_EQ(net::OK, resource_load_info.net_error);
89 run_loop_.Quit();
90 }
91
Wait()92 void Wait() { run_loop_.Run(); }
93
94 private:
95 base::RunLoop run_loop_;
96
97 DISALLOW_COPY_AND_ASSIGN(WaitForMainFrameResourceObserver);
98 };
99
100 // This test fixture tests code in content/. The fixture itself is in chrome/
101 // because SubprocessMetricsProvider is a chrome-only test class.
102 class NetworkRequestMetricsBrowserTest
103 : public InProcessBrowserTest,
104 public testing::WithParamInterface<RequestType> {
105 public:
NetworkRequestMetricsBrowserTest()106 NetworkRequestMetricsBrowserTest() {
107 scoped_feature_list_.InitAndDisableFeature(
108 predictors::kSpeculativePreconnectFeature);
109 }
~NetworkRequestMetricsBrowserTest()110 ~NetworkRequestMetricsBrowserTest() override {}
111
112 // ContentBrowserTest implementation:
SetUpOnMainThread()113 void SetUpOnMainThread() override {
114 uninteresting_main_frame_response_ =
115 std::make_unique<net::test_server::ControllableHttpResponse>(
116 embedded_test_server(), kUninterestingMainFramePath);
117 interesting_http_response_ =
118 std::make_unique<net::test_server::ControllableHttpResponse>(
119 embedded_test_server(), kInterestingPath);
120 ASSERT_TRUE(embedded_test_server()->Start());
121
122 // Need to make this after test setup, to make sure the initial about:blank
123 // load is not counted.
124 histograms_ = std::make_unique<base::HistogramTester>();
125 }
126
127 // For non-RequestType::kMainFrame tests, returns the contents of the main
128 // frame, based on the RequestType.
GetMainFrameContents(const std::string subresource_path)129 std::string GetMainFrameContents(const std::string subresource_path) {
130 switch (GetParam()) {
131 case RequestType::kSubFrame:
132 return base::StringPrintf("<iframe src='%s'></iframe>",
133 subresource_path.c_str());
134 case RequestType::kImage:
135 return base::StringPrintf("<img src='%s'>", subresource_path.c_str());
136 break;
137 case RequestType::kScript:
138 return base::StringPrintf("<script src='%s'></script>",
139 subresource_path.c_str());
140 break;
141 case RequestType::kMainFrame:
142 NOTREACHED();
143 }
144 return std::string();
145 }
146
StartNavigatingAndWaitForRequest()147 void StartNavigatingAndWaitForRequest() {
148 GURL interesting_url = embedded_test_server()->GetURL(kInterestingPath);
149 if (GetParam() == RequestType::kMainFrame) {
150 ui_test_utils::NavigateToURLWithDisposition(
151 browser(), interesting_url, WindowOpenDisposition::CURRENT_TAB,
152 ui_test_utils::BROWSER_TEST_NONE);
153 } else {
154 WaitForMainFrameResourceObserver wait_for_main_frame_resource_observer(
155 active_web_contents());
156 ui_test_utils::NavigateToURLWithDisposition(
157 browser(),
158 embedded_test_server()->GetURL(kUninterestingMainFramePath),
159 WindowOpenDisposition::CURRENT_TAB, ui_test_utils::BROWSER_TEST_NONE);
160 uninteresting_main_frame_response_->WaitForRequest();
161 uninteresting_main_frame_response_->Send(
162 "HTTP/1.1 200 Peachy\r\n"
163 "Content-Type: text/html\r\n"
164 "\r\n");
165 uninteresting_main_frame_response_->Send(
166 GetMainFrameContents(interesting_url.spec()));
167 uninteresting_main_frame_response_->Done();
168 wait_for_main_frame_resource_observer.Wait();
169 }
170
171 interesting_http_response_->WaitForRequest();
172 }
173
interesting_http_response()174 net::test_server::ControllableHttpResponse* interesting_http_response() {
175 return interesting_http_response_.get();
176 }
177
178 // Checks all relevant histograms. |expected_net_error| is the expected result
179 // of the RequestType specified by the test parameter.
CheckHistograms(int expected_net_error,HeadersReceived headers_received,NetworkAccessed network_accessed)180 void CheckHistograms(int expected_net_error,
181 HeadersReceived headers_received,
182 NetworkAccessed network_accessed) {
183 // Some metrics may come from the renderer. This call ensures that those
184 // metrics are available.
185 SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
186
187 if (GetParam() == RequestType::kMainFrame) {
188 histograms_->ExpectTotalCount("Net.ErrorCodesForImages2", 0);
189
190 histograms_->ExpectUniqueSample("Net.ErrorCodesForMainFrame4",
191 -expected_net_error, 1);
192
193 if (headers_received == HeadersReceived::kHeadersReceived) {
194 histograms_->ExpectUniqueSample(
195 "Net.ConnectionInfo.MainFrame",
196 net::HttpResponseInfo::CONNECTION_INFO_HTTP1_1, 1);
197 } else {
198 histograms_->ExpectTotalCount("Net.ConnectionInfo.MainFrame", 0);
199 }
200
201 // Favicon may or may not have been loaded.
202 EXPECT_GE(
203 1u,
204 histograms_->GetAllSamples("Net.ErrorCodesForSubresources3").size());
205 EXPECT_GE(
206 1u,
207 histograms_->GetAllSamples("Net.ConnectionInfo.SubResource").size());
208
209 return;
210 }
211
212 // If not testing the main frame, there should also be just one result for
213 // the main frame.
214 histograms_->ExpectUniqueSample("Net.ErrorCodesForMainFrame4", -net::OK, 1);
215
216 // Some fuzziness here because of the favicon. It should typically succeed,
217 // but allow it to have been aborted, too, since the test server won't
218 // return a valid icon.
219 std::vector<base::Bucket> buckets =
220 histograms_->GetAllSamples("Net.ErrorCodesForSubresources3");
221 bool found_expected_load = false;
222 bool found_favicon_load = false;
223 for (auto& bucket : buckets) {
224 if (!found_expected_load && bucket.min == -expected_net_error) {
225 found_expected_load = true;
226 bucket.count--;
227 }
228 if (!found_favicon_load && bucket.count > 0 &&
229 (bucket.min == -net::OK || bucket.min == -net::ERR_ABORTED)) {
230 found_favicon_load = true;
231 bucket.count--;
232 }
233 EXPECT_EQ(0, bucket.count)
234 << "Found unexpected load with result: " << bucket.min;
235 }
236 EXPECT_TRUE(found_expected_load);
237
238 if (GetParam() != RequestType::kImage) {
239 histograms_->ExpectTotalCount("Net.ErrorCodesForImages2", 0);
240 } else {
241 histograms_->ExpectUniqueSample("Net.ErrorCodesForImages2",
242 -expected_net_error, 1);
243 }
244
245 // A subresource load requires a main frame load, which is only logged for
246 // network URLs.
247 if (network_accessed == NetworkAccessed::kNetworkAccessed) {
248 histograms_->ExpectUniqueSample(
249 "Net.ConnectionInfo.MainFrame",
250 net::HttpResponseInfo::CONNECTION_INFO_HTTP1_1, 1);
251 if (headers_received == HeadersReceived::kHeadersReceived) {
252 // Favicon request may or may not have received a response.
253 size_t subresources =
254 histograms_->GetAllSamples("Net.ConnectionInfo.SubResource").size();
255 EXPECT_LE(1u, subresources);
256 EXPECT_GE(2u, subresources);
257 } else {
258 histograms_->ExpectTotalCount("Net.ConnectionInfo.SubResource", 0);
259 }
260 } else {
261 histograms_->ExpectTotalCount("Net.ConnectionInfo.MainFrame", 0);
262 histograms_->ExpectTotalCount("Net.ConnectionInfo.SubResource", 0);
263 }
264 }
265
266 // Checks all relevant histograms in the case a new main frame navigation
267 // interrupted the first one. The request identified by GetParam() is expected
268 // to fail with net::ERR_ABORTED.
CheckHistogramsAfterMainFrameInterruption()269 void CheckHistogramsAfterMainFrameInterruption() {
270 // Some metrics may come from the renderer. This call ensures that those
271 // metrics are available.
272 SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
273
274 if (GetParam() == RequestType::kMainFrame) {
275 // Can't check Net.ErrorCodesForSubresources3, due to the favicon, which
276 // Chrome may or may not have attempted to load.
277 histograms_->ExpectTotalCount("Net.ErrorCodesForImages2", 0);
278
279 histograms_->ExpectTotalCount("Net.ErrorCodesForMainFrame4", 2);
280 EXPECT_EQ(1, histograms_->GetBucketCount("Net.ErrorCodesForMainFrame4",
281 -net::ERR_ABORTED));
282 EXPECT_EQ(1, histograms_->GetBucketCount("Net.ErrorCodesForMainFrame4",
283 -net::OK));
284 return;
285 }
286
287 histograms_->ExpectUniqueSample("Net.ErrorCodesForMainFrame4", -net::OK, 2);
288
289 // Some fuzziness here because of the favicon. It should typically succeed,
290 // but allow it to have been aborted, too, since the test server won't
291 // return a valid icon.
292 std::vector<base::Bucket> buckets =
293 histograms_->GetAllSamples("Net.ErrorCodesForSubresources3");
294 bool found_expected_load = false;
295 int found_favicon_loads = 0;
296 for (auto& bucket : buckets) {
297 if (!found_expected_load && bucket.min == -net::ERR_ABORTED) {
298 found_expected_load = true;
299 bucket.count--;
300 }
301 // Allow up to two favicon loads, one for the original page load, that was
302 // interrupted by a new load, and one for the new page load.
303 if (found_favicon_loads < 2 && bucket.count > 0 &&
304 (bucket.min == -net::OK || bucket.min == -net::ERR_ABORTED)) {
305 found_favicon_loads++;
306 bucket.count--;
307 }
308 EXPECT_EQ(0, bucket.count)
309 << "Found unexpected load with result: " << bucket.min;
310 }
311 EXPECT_TRUE(found_expected_load);
312
313 if (GetParam() != RequestType::kImage) {
314 histograms_->ExpectTotalCount("Net.ErrorCodesForImages2", 0);
315 } else {
316 histograms_->ExpectUniqueSample("Net.ErrorCodesForImages2",
317 -net::ERR_ABORTED, 1);
318 }
319 }
320
321 // Send headers and a partial body to |interesting_http_response_|. Doesn't
322 // terminate the response, so the socket can either be closed, or the request
323 // aborted.
SendHeadersPartialBody()324 void SendHeadersPartialBody() {
325 // Sending a body that's too short will result in an error after all the
326 // bytes are read.
327 interesting_http_response_->Send("HTTP/1.1 200 Peachy\r\n");
328 // Send a MIME type to avoid MIME sniffing (Shouldn't matter, but it's one
329 // less thing to worry about).
330 if (GetParam() == RequestType::kImage) {
331 interesting_http_response_->Send("Content-Type: image/png\r\n");
332 } else if (GetParam() == RequestType::kScript) {
333 interesting_http_response_->Send("Content-Type: text/css\r\n");
334 } else {
335 interesting_http_response_->Send("Content-Type: text/html\r\n");
336 }
337 interesting_http_response_->Send("Content-Length: 50\r\n\r\n");
338 // This is the first byte of the PNG header, to avoid any chance of the
339 // request being cancelled for not looking like a PNG.
340 interesting_http_response_->Send("\x89");
341 }
342
active_web_contents()343 content::WebContents* active_web_contents() {
344 return browser()->tab_strip_model()->GetActiveWebContents();
345 }
346
histograms()347 base::HistogramTester* histograms() { return histograms_.get(); }
348
349 private:
350 base::test::ScopedFeatureList scoped_feature_list_;
351 std::unique_ptr<net::test_server::ControllableHttpResponse>
352 uninteresting_main_frame_response_;
353 std::unique_ptr<net::test_server::ControllableHttpResponse>
354 interesting_http_response_;
355 std::unique_ptr<base::HistogramTester> histograms_;
356 };
357
358 // Testing before headers / during body is most interesting in the frame case,
359 // as it checks the before and after commit case, which follow very different
360 // paths.
IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest,NetErrorBeforeHeaders)361 IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest,
362 NetErrorBeforeHeaders) {
363 TestNavigationObserver navigation_observer(active_web_contents(), 1);
364 StartNavigatingAndWaitForRequest();
365 // Not sending any body will result in failing with ERR_EMPTY_RESPONSE,
366 // without receiving any headers so the load won't be committed until the
367 // error page is seen.
368 interesting_http_response()->Done();
369 navigation_observer.Wait();
370
371 CheckHistograms(net::ERR_EMPTY_RESPONSE, HeadersReceived::kNoHeadersReceived,
372 NetworkAccessed::kNetworkAccessed);
373 }
374
IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest,NetErrorDuringBody)375 IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, NetErrorDuringBody) {
376 TestNavigationObserver navigation_observer(active_web_contents(), 1);
377 StartNavigatingAndWaitForRequest();
378 SendHeadersPartialBody();
379 interesting_http_response()->Done();
380 navigation_observer.Wait();
381
382 CheckHistograms(net::ERR_CONTENT_LENGTH_MISMATCH,
383 HeadersReceived::kHeadersReceived,
384 NetworkAccessed::kNetworkAccessed);
385 }
386
IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest,CancelBeforeHeaders)387 IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, CancelBeforeHeaders) {
388 TestNavigationObserver navigation_observer(active_web_contents(), 1);
389 StartNavigatingAndWaitForRequest();
390 active_web_contents()->Stop();
391 navigation_observer.Wait();
392
393 CheckHistograms(net::ERR_ABORTED, HeadersReceived::kNoHeadersReceived,
394 NetworkAccessed::kNetworkAccessed);
395 }
396
IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest,CancelDuringBody)397 IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, CancelDuringBody) {
398 TestNavigationObserver navigation_observer(active_web_contents(), 1);
399 StartNavigatingAndWaitForRequest();
400 SendHeadersPartialBody();
401
402 // Unfortunately, there's no way to ensure that the body has partially been
403 // received, so can only wait and hope. If the partial body hasn't been
404 // recieved by the time Stop() is called, the test should still pass, however.
405 base::RunLoop run_loop;
406 base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
407 FROM_HERE, run_loop.QuitClosure(), base::TimeDelta::FromSeconds(1));
408 run_loop.Run();
409
410 active_web_contents()->Stop();
411 navigation_observer.Wait();
412
413 CheckHistograms(net::ERR_ABORTED, HeadersReceived::kHeadersReceived,
414 NetworkAccessed::kNetworkAccessed);
415 }
416
IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest,InterruptedBeforeHeaders)417 IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest,
418 InterruptedBeforeHeaders) {
419 StartNavigatingAndWaitForRequest();
420
421 TestNavigationObserver navigation_observer(active_web_contents(), 1);
422 // Can't use ui_test_utils::NavigateToURLWithDisposition(), as it will wait
423 // for the current load to stop, rather than interrupting it.
424 browser()->OpenURL(OpenURLParams(
425 embedded_test_server()->GetURL("/echo"), Referrer(),
426 WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
427 false /* is_renderer_initiated */));
428 navigation_observer.Wait();
429
430 CheckHistogramsAfterMainFrameInterruption();
431 }
432
IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest,InterruptedCancelDuringBody)433 IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest,
434 InterruptedCancelDuringBody) {
435 StartNavigatingAndWaitForRequest();
436 SendHeadersPartialBody();
437
438 // Unfortunately, there's no way to ensure that the body has partially been
439 // received, so can only wait and hope. If the partial body hasn't been
440 // recieved by the time Stop() is called, the test should still pass, however.
441 base::RunLoop run_loop;
442 base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
443 FROM_HERE, run_loop.QuitClosure(), base::TimeDelta::FromSeconds(1));
444 run_loop.Run();
445
446 TestNavigationObserver navigation_observer(active_web_contents(), 1);
447 // Can't use ui_test_utils::NavigateToURLWithDisposition(), as it will wait
448 // for the current load to stop, rather than interrupting it.
449 browser()->OpenURL(OpenURLParams(
450 embedded_test_server()->GetURL("/echo"), Referrer(),
451 WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
452 false /* is_renderer_initiated */));
453 navigation_observer.Wait();
454
455 CheckHistogramsAfterMainFrameInterruption();
456 }
457
IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest,SuccessWithBody)458 IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, SuccessWithBody) {
459 TestNavigationObserver navigation_observer(active_web_contents(), 1);
460 StartNavigatingAndWaitForRequest();
461 interesting_http_response()->Send("HTTP/1.1 200 Peachy\r\n\r\n");
462 interesting_http_response()->Send("Grapefruit");
463 interesting_http_response()->Done();
464 navigation_observer.Wait();
465
466 CheckHistograms(net::OK, HeadersReceived::kHeadersReceived,
467 NetworkAccessed::kNetworkAccessed);
468 }
469
IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest,SuccessWithEmptyBody)470 IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, SuccessWithEmptyBody) {
471 TestNavigationObserver navigation_observer(active_web_contents(), 1);
472 StartNavigatingAndWaitForRequest();
473 interesting_http_response()->Send("HTTP/1.1 200 Peachy\r\n");
474 interesting_http_response()->Send("Content-Length: 0\r\n\r\n");
475 interesting_http_response()->Done();
476 navigation_observer.Wait();
477
478 CheckHistograms(net::OK, HeadersReceived::kHeadersReceived,
479 NetworkAccessed::kNetworkAccessed);
480 }
481
482 // Downloads should not be logged (Either as successes or failures).
IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest,Download)483 IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, Download) {
484 // Only frames can be converted to downloads.
485 if (GetParam() != RequestType::kMainFrame &&
486 GetParam() != RequestType::kSubFrame) {
487 return;
488 }
489
490 browser()->profile()->GetPrefs()->SetInteger(
491 prefs::kDownloadRestrictions,
492 static_cast<int>(DownloadPrefs::DownloadRestriction::ALL_FILES));
493 browser()->profile()->GetPrefs()->SetBoolean(prefs::kPromptForDownload,
494 false);
495
496 // Need this to wait for the download to be fully cancelled to avoid a
497 // confirmation prompt on quit.
498 DownloadTestObserverTerminal download_test_observer_terminal(
499 BrowserContext::GetDownloadManager(browser()->profile()), 1,
500 DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_IGNORE);
501
502 TestNavigationObserver navigation_observer(active_web_contents(), 1);
503 StartNavigatingAndWaitForRequest();
504 interesting_http_response()->Send("HTTP/1.1 200 Peachy\r\n");
505 interesting_http_response()->Send(
506 "Content-Type: binary/octet-stream\r\n\r\n");
507 interesting_http_response()->Send("\x01");
508 interesting_http_response()->Done();
509 navigation_observer.Wait();
510
511 download_test_observer_terminal.WaitForFinished();
512
513 // Some metrics may come from the renderer. This call ensures that those
514 // metrics are available.
515 SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
516
517 if (GetParam() == RequestType::kMainFrame) {
518 histograms()->ExpectTotalCount("Net.ErrorCodesForImages2", 0);
519 histograms()->ExpectTotalCount("Net.ErrorCodesForMainFrame4", 0);
520 histograms()->ExpectTotalCount("Net.ConnectionInfo.MainFrame", 0);
521 // Favicon may or may not have been loaded.
522 EXPECT_GE(
523 1u,
524 histograms()->GetAllSamples("Net.ErrorCodesForSubresources3").size());
525 EXPECT_GE(
526 1u,
527 histograms()->GetAllSamples("Net.ConnectionInfo.SubResource").size());
528
529 return;
530 }
531
532 // If not testing the main frame, there should also be just one result for
533 // the main frame.
534 histograms()->ExpectUniqueSample("Net.ErrorCodesForMainFrame4", -net::OK, 1);
535
536 // Some fuzziness here because of the favicon. It should typically succeed,
537 // but allow it to have been aborted, too, since the test server won't
538 // return a valid icon.
539 std::vector<base::Bucket> buckets =
540 histograms()->GetAllSamples("Net.ErrorCodesForSubresources3");
541 bool found_favicon_load = false;
542 for (auto& bucket : buckets) {
543 if (!found_favicon_load && bucket.count > 0 &&
544 (bucket.min == -net::OK || bucket.min == -net::ERR_ABORTED)) {
545 found_favicon_load = true;
546 bucket.count--;
547 }
548 EXPECT_EQ(0, bucket.count)
549 << "Found unexpected load with result: " << bucket.min;
550 }
551
552 histograms()->ExpectUniqueSample(
553 "Net.ConnectionInfo.MainFrame",
554 net::HttpResponseInfo::CONNECTION_INFO_HTTP1_1, 1);
555 // Favicon request may or may not have received a response.
556 size_t subresources =
557 histograms()->GetAllSamples("Net.ConnectionInfo.SubResource").size();
558 EXPECT_LE(0u, subresources);
559 EXPECT_GE(1u, subresources);
560 }
561
562 // A few tests for file:// URLs, so that URLs not handled by the network service
563 // itself have some coverage.
564
IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest,FileURLError)565 IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, FileURLError) {
566 base::ScopedAllowBlockingForTesting allow_blocking;
567 base::ScopedTempDir temp_dir_;
568 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
569
570 base::FilePath main_frame_path = temp_dir_.GetPath().AppendASCII("main.html");
571 if (GetParam() != RequestType::kMainFrame) {
572 std::string main_frame_data = GetMainFrameContents("subresource");
573 ASSERT_EQ(static_cast<int>(main_frame_data.length()),
574 base::WriteFile(main_frame_path, main_frame_data.c_str(),
575 main_frame_data.length()));
576 }
577
578 ui_test_utils::NavigateToURL(browser(),
579 net::FilePathToFileURL(main_frame_path));
580 CheckHistograms(net::ERR_FILE_NOT_FOUND, HeadersReceived::kNoHeadersReceived,
581 NetworkAccessed::kNoNetworkAccessed);
582 }
583
IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest,FileURLSuccess)584 IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, FileURLSuccess) {
585 const char kSubresourcePath[] = "subresource";
586
587 base::ScopedAllowBlockingForTesting allow_blocking;
588 base::ScopedTempDir temp_dir_;
589 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
590
591 base::FilePath main_frame_path = temp_dir_.GetPath().AppendASCII("main.html");
592 std::string main_frame_data = "foo";
593 if (GetParam() != RequestType::kMainFrame)
594 main_frame_data = GetMainFrameContents(kSubresourcePath);
595 ASSERT_EQ(static_cast<int>(main_frame_data.length()),
596 base::WriteFile(main_frame_path, main_frame_data.c_str(),
597 main_frame_data.length()));
598 if (GetParam() != RequestType::kMainFrame) {
599 std::string subresource_data = "foo";
600 ASSERT_EQ(
601 static_cast<int>(subresource_data.length()),
602 base::WriteFile(temp_dir_.GetPath().AppendASCII(kSubresourcePath),
603 subresource_data.c_str(), subresource_data.length()));
604 }
605
606 ui_test_utils::NavigateToURL(browser(),
607 net::FilePathToFileURL(main_frame_path));
608 CheckHistograms(net::OK, HeadersReceived::kNoHeadersReceived,
609 NetworkAccessed::kNoNetworkAccessed);
610 }
611
612 INSTANTIATE_TEST_SUITE_P(All,
613 NetworkRequestMetricsBrowserTest,
614 testing::Values(RequestType::kMainFrame,
615 RequestType::kSubFrame,
616 RequestType::kImage,
617 RequestType::kScript));
618
619 } // namespace
620 } // namespace content
621