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