// Copyright 2018 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 <memory>
#include <string>
#include <vector>

#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/metrics/subprocess_metrics_provider.h"
#include "chrome/browser/predictors/loading_predictor_config.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/download_test_observer.h"
#include "content/public/test/test_navigation_observer.h"
#include "net/base/filename_util.h"
#include "net/base/net_errors.h"
#include "net/http/http_response_info.h"
#include "net/test/embedded_test_server/controllable_http_response.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
#include "ui/base/window_open_disposition.h"
#include "url/gurl.h"

namespace content {
namespace {

// Used for the main frame, in subresource tests.
constexpr char kUninterestingMainFramePath[] =
    "/uninteresting/main_frame/path/";

// Used for the "interesting" request individual tests focus on.
constexpr char kInterestingPath[] = "/interesting/path/";

enum class RequestType {
  kMainFrame,
  kSubFrame,
  kImage,
  kScript,
};

enum class HeadersReceived {
  kHeadersReceived,
  kNoHeadersReceived,
};

enum class NetworkAccessed {
  kNetworkAccessed,
  kNoNetworkAccessed,
};

// Utility class to wait until the main resource load is complete. This is to
// make sure, in the cancel tests, the main resource is fully loaded before the
// navigation is cancelled, to ensure the main frame load histograms are in a
// consistent state, and can be checked at the end of each test. To avoid races,
// create this class before starting a navigation.
class WaitForMainFrameResourceObserver : public content::WebContentsObserver {
 public:
  explicit WaitForMainFrameResourceObserver(WebContents* web_contents)
      : content::WebContentsObserver(web_contents) {}
  ~WaitForMainFrameResourceObserver() override {}

  // content::WebContentsObserver implementation:
  void ResourceLoadComplete(
      RenderFrameHost* render_frame_host,
      const content::GlobalRequestID& request_id,
      const blink::mojom::ResourceLoadInfo& resource_load_info) override {
    EXPECT_EQ(network::mojom::RequestDestination::kDocument,
              resource_load_info.request_destination);
    EXPECT_EQ(net::OK, resource_load_info.net_error);
    run_loop_.Quit();
  }

  void Wait() { run_loop_.Run(); }

 private:
  base::RunLoop run_loop_;

  DISALLOW_COPY_AND_ASSIGN(WaitForMainFrameResourceObserver);
};

// This test fixture tests code in content/. The fixture itself is in chrome/
// because SubprocessMetricsProvider is a chrome-only test class.
class NetworkRequestMetricsBrowserTest
    : public InProcessBrowserTest,
      public testing::WithParamInterface<RequestType> {
 public:
  NetworkRequestMetricsBrowserTest() {
    scoped_feature_list_.InitAndDisableFeature(
        predictors::kSpeculativePreconnectFeature);
  }
  ~NetworkRequestMetricsBrowserTest() override {}

  // ContentBrowserTest implementation:
  void SetUpOnMainThread() override {
    uninteresting_main_frame_response_ =
        std::make_unique<net::test_server::ControllableHttpResponse>(
            embedded_test_server(), kUninterestingMainFramePath);
    interesting_http_response_ =
        std::make_unique<net::test_server::ControllableHttpResponse>(
            embedded_test_server(), kInterestingPath);
    ASSERT_TRUE(embedded_test_server()->Start());

    // Need to make this after test setup, to make sure the initial about:blank
    // load is not counted.
    histograms_ = std::make_unique<base::HistogramTester>();
  }

  // For non-RequestType::kMainFrame tests, returns the contents of the main
  // frame, based on the RequestType.
  std::string GetMainFrameContents(const std::string subresource_path) {
    switch (GetParam()) {
      case RequestType::kSubFrame:
        return base::StringPrintf("<iframe src='%s'></iframe>",
                                  subresource_path.c_str());
      case RequestType::kImage:
        return base::StringPrintf("<img src='%s'>", subresource_path.c_str());
        break;
      case RequestType::kScript:
        return base::StringPrintf("<script src='%s'></script>",
                                  subresource_path.c_str());
        break;
      case RequestType::kMainFrame:
        NOTREACHED();
    }
    return std::string();
  }

  void StartNavigatingAndWaitForRequest() {
    GURL interesting_url = embedded_test_server()->GetURL(kInterestingPath);
    if (GetParam() == RequestType::kMainFrame) {
      ui_test_utils::NavigateToURLWithDisposition(
          browser(), interesting_url, WindowOpenDisposition::CURRENT_TAB,
          ui_test_utils::BROWSER_TEST_NONE);
    } else {
      WaitForMainFrameResourceObserver wait_for_main_frame_resource_observer(
          active_web_contents());
      ui_test_utils::NavigateToURLWithDisposition(
          browser(),
          embedded_test_server()->GetURL(kUninterestingMainFramePath),
          WindowOpenDisposition::CURRENT_TAB, ui_test_utils::BROWSER_TEST_NONE);
      uninteresting_main_frame_response_->WaitForRequest();
      uninteresting_main_frame_response_->Send(
          "HTTP/1.1 200 Peachy\r\n"
          "Content-Type: text/html\r\n"
          "\r\n");
      uninteresting_main_frame_response_->Send(
          GetMainFrameContents(interesting_url.spec()));
      uninteresting_main_frame_response_->Done();
      wait_for_main_frame_resource_observer.Wait();
    }

    interesting_http_response_->WaitForRequest();
  }

  net::test_server::ControllableHttpResponse* interesting_http_response() {
    return interesting_http_response_.get();
  }

  // Checks all relevant histograms. |expected_net_error| is the expected result
  // of the RequestType specified by the test parameter.
  void CheckHistograms(int expected_net_error,
                       HeadersReceived headers_received,
                       NetworkAccessed network_accessed) {
    // Some metrics may come from the renderer. This call ensures that those
    // metrics are available.
    SubprocessMetricsProvider::MergeHistogramDeltasForTesting();

    if (GetParam() == RequestType::kMainFrame) {
      histograms_->ExpectTotalCount("Net.ErrorCodesForImages2", 0);

      histograms_->ExpectUniqueSample("Net.ErrorCodesForMainFrame4",
                                      -expected_net_error, 1);

      if (headers_received == HeadersReceived::kHeadersReceived) {
        histograms_->ExpectUniqueSample(
            "Net.ConnectionInfo.MainFrame",
            net::HttpResponseInfo::CONNECTION_INFO_HTTP1_1, 1);
      } else {
        histograms_->ExpectTotalCount("Net.ConnectionInfo.MainFrame", 0);
      }

      // Favicon may or may not have been loaded.
      EXPECT_GE(
          1u,
          histograms_->GetAllSamples("Net.ErrorCodesForSubresources3").size());
      EXPECT_GE(
          1u,
          histograms_->GetAllSamples("Net.ConnectionInfo.SubResource").size());

      return;
    }

    // If not testing the main frame, there should also be just one result for
    // the main frame.
    histograms_->ExpectUniqueSample("Net.ErrorCodesForMainFrame4", -net::OK, 1);

    // Some fuzziness here because of the favicon. It should typically succeed,
    // but allow it to have been aborted, too, since the test server won't
    // return a valid icon.
    std::vector<base::Bucket> buckets =
        histograms_->GetAllSamples("Net.ErrorCodesForSubresources3");
    bool found_expected_load = false;
    bool found_favicon_load = false;
    for (auto& bucket : buckets) {
      if (!found_expected_load && bucket.min == -expected_net_error) {
        found_expected_load = true;
        bucket.count--;
      }
      if (!found_favicon_load && bucket.count > 0 &&
          (bucket.min == -net::OK || bucket.min == -net::ERR_ABORTED)) {
        found_favicon_load = true;
        bucket.count--;
      }
      EXPECT_EQ(0, bucket.count)
          << "Found unexpected load with result: " << bucket.min;
    }
    EXPECT_TRUE(found_expected_load);

    if (GetParam() != RequestType::kImage) {
      histograms_->ExpectTotalCount("Net.ErrorCodesForImages2", 0);
    } else {
      histograms_->ExpectUniqueSample("Net.ErrorCodesForImages2",
                                      -expected_net_error, 1);
    }

    // A subresource load requires a main frame load, which is only logged for
    // network URLs.
    if (network_accessed == NetworkAccessed::kNetworkAccessed) {
      histograms_->ExpectUniqueSample(
          "Net.ConnectionInfo.MainFrame",
          net::HttpResponseInfo::CONNECTION_INFO_HTTP1_1, 1);
      if (headers_received == HeadersReceived::kHeadersReceived) {
        // Favicon request may or may not have received a response.
        size_t subresources =
            histograms_->GetAllSamples("Net.ConnectionInfo.SubResource").size();
        EXPECT_LE(1u, subresources);
        EXPECT_GE(2u, subresources);
      } else {
        histograms_->ExpectTotalCount("Net.ConnectionInfo.SubResource", 0);
      }
    } else {
      histograms_->ExpectTotalCount("Net.ConnectionInfo.MainFrame", 0);
      histograms_->ExpectTotalCount("Net.ConnectionInfo.SubResource", 0);
    }
  }

  // Checks all relevant histograms in the case a new main frame navigation
  // interrupted the first one. The request identified by GetParam() is expected
  // to fail with net::ERR_ABORTED.
  void CheckHistogramsAfterMainFrameInterruption() {
    // Some metrics may come from the renderer. This call ensures that those
    // metrics are available.
    SubprocessMetricsProvider::MergeHistogramDeltasForTesting();

    if (GetParam() == RequestType::kMainFrame) {
      // Can't check Net.ErrorCodesForSubresources3, due to the favicon, which
      // Chrome may or may not have attempted to load.
      histograms_->ExpectTotalCount("Net.ErrorCodesForImages2", 0);

      histograms_->ExpectTotalCount("Net.ErrorCodesForMainFrame4", 2);
      EXPECT_EQ(1, histograms_->GetBucketCount("Net.ErrorCodesForMainFrame4",
                                               -net::ERR_ABORTED));
      EXPECT_EQ(1, histograms_->GetBucketCount("Net.ErrorCodesForMainFrame4",
                                               -net::OK));
      return;
    }

    histograms_->ExpectUniqueSample("Net.ErrorCodesForMainFrame4", -net::OK, 2);

    // Some fuzziness here because of the favicon. It should typically succeed,
    // but allow it to have been aborted, too, since the test server won't
    // return a valid icon.
    std::vector<base::Bucket> buckets =
        histograms_->GetAllSamples("Net.ErrorCodesForSubresources3");
    bool found_expected_load = false;
    int found_favicon_loads = 0;
    for (auto& bucket : buckets) {
      if (!found_expected_load && bucket.min == -net::ERR_ABORTED) {
        found_expected_load = true;
        bucket.count--;
      }
      // Allow up to two favicon loads, one for the original page load, that was
      // interrupted by a new load, and one for the new page load.
      if (found_favicon_loads < 2 && bucket.count > 0 &&
          (bucket.min == -net::OK || bucket.min == -net::ERR_ABORTED)) {
        found_favicon_loads++;
        bucket.count--;
      }
      EXPECT_EQ(0, bucket.count)
          << "Found unexpected load with result: " << bucket.min;
    }
    EXPECT_TRUE(found_expected_load);

    if (GetParam() != RequestType::kImage) {
      histograms_->ExpectTotalCount("Net.ErrorCodesForImages2", 0);
    } else {
      histograms_->ExpectUniqueSample("Net.ErrorCodesForImages2",
                                      -net::ERR_ABORTED, 1);
    }
  }

  // Send headers and a partial body to |interesting_http_response_|. Doesn't
  // terminate the response, so the socket can either be closed, or the request
  // aborted.
  void SendHeadersPartialBody() {
    // Sending a body that's too short will result in an error after all the
    // bytes are read.
    interesting_http_response_->Send("HTTP/1.1 200 Peachy\r\n");
    // Send a MIME type to avoid MIME sniffing (Shouldn't matter, but it's one
    // less thing to worry about).
    if (GetParam() == RequestType::kImage) {
      interesting_http_response_->Send("Content-Type: image/png\r\n");
    } else if (GetParam() == RequestType::kScript) {
      interesting_http_response_->Send("Content-Type: text/css\r\n");
    } else {
      interesting_http_response_->Send("Content-Type: text/html\r\n");
    }
    interesting_http_response_->Send("Content-Length: 50\r\n\r\n");
    // This is the first byte of the PNG header, to avoid any chance of the
    // request being cancelled for not looking like a PNG.
    interesting_http_response_->Send("\x89");
  }

  content::WebContents* active_web_contents() {
    return browser()->tab_strip_model()->GetActiveWebContents();
  }

  base::HistogramTester* histograms() { return histograms_.get(); }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
  std::unique_ptr<net::test_server::ControllableHttpResponse>
      uninteresting_main_frame_response_;
  std::unique_ptr<net::test_server::ControllableHttpResponse>
      interesting_http_response_;
  std::unique_ptr<base::HistogramTester> histograms_;
};

// Testing before headers / during body is most interesting in the frame case,
// as it checks the before and after commit case, which follow very different
// paths.
IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest,
                       NetErrorBeforeHeaders) {
  TestNavigationObserver navigation_observer(active_web_contents(), 1);
  StartNavigatingAndWaitForRequest();
  // Not sending any body will result in failing with ERR_EMPTY_RESPONSE,
  // without receiving any headers so the load won't be committed until the
  // error page is seen.
  interesting_http_response()->Done();
  navigation_observer.Wait();

  CheckHistograms(net::ERR_EMPTY_RESPONSE, HeadersReceived::kNoHeadersReceived,
                  NetworkAccessed::kNetworkAccessed);
}

IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, NetErrorDuringBody) {
  TestNavigationObserver navigation_observer(active_web_contents(), 1);
  StartNavigatingAndWaitForRequest();
  SendHeadersPartialBody();
  interesting_http_response()->Done();
  navigation_observer.Wait();

  CheckHistograms(net::ERR_CONTENT_LENGTH_MISMATCH,
                  HeadersReceived::kHeadersReceived,
                  NetworkAccessed::kNetworkAccessed);
}

IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, CancelBeforeHeaders) {
  TestNavigationObserver navigation_observer(active_web_contents(), 1);
  StartNavigatingAndWaitForRequest();
  active_web_contents()->Stop();
  navigation_observer.Wait();

  CheckHistograms(net::ERR_ABORTED, HeadersReceived::kNoHeadersReceived,
                  NetworkAccessed::kNetworkAccessed);
}

IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, CancelDuringBody) {
  TestNavigationObserver navigation_observer(active_web_contents(), 1);
  StartNavigatingAndWaitForRequest();
  SendHeadersPartialBody();

  // Unfortunately, there's no way to ensure that the body has partially been
  // received, so can only wait and hope. If the partial body hasn't been
  // recieved by the time Stop() is called, the test should still pass, however.
  base::RunLoop run_loop;
  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
      FROM_HERE, run_loop.QuitClosure(), base::TimeDelta::FromSeconds(1));
  run_loop.Run();

  active_web_contents()->Stop();
  navigation_observer.Wait();

  CheckHistograms(net::ERR_ABORTED, HeadersReceived::kHeadersReceived,
                  NetworkAccessed::kNetworkAccessed);
}

IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest,
                       InterruptedBeforeHeaders) {
  StartNavigatingAndWaitForRequest();

  TestNavigationObserver navigation_observer(active_web_contents(), 1);
  // Can't use ui_test_utils::NavigateToURLWithDisposition(), as it will wait
  // for the current load to stop, rather than interrupting it.
  browser()->OpenURL(OpenURLParams(
      embedded_test_server()->GetURL("/echo"), Referrer(),
      WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
      false /* is_renderer_initiated */));
  navigation_observer.Wait();

  CheckHistogramsAfterMainFrameInterruption();
}

IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest,
                       InterruptedCancelDuringBody) {
  StartNavigatingAndWaitForRequest();
  SendHeadersPartialBody();

  // Unfortunately, there's no way to ensure that the body has partially been
  // received, so can only wait and hope. If the partial body hasn't been
  // recieved by the time Stop() is called, the test should still pass, however.
  base::RunLoop run_loop;
  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
      FROM_HERE, run_loop.QuitClosure(), base::TimeDelta::FromSeconds(1));
  run_loop.Run();

  TestNavigationObserver navigation_observer(active_web_contents(), 1);
  // Can't use ui_test_utils::NavigateToURLWithDisposition(), as it will wait
  // for the current load to stop, rather than interrupting it.
  browser()->OpenURL(OpenURLParams(
      embedded_test_server()->GetURL("/echo"), Referrer(),
      WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
      false /* is_renderer_initiated */));
  navigation_observer.Wait();

  CheckHistogramsAfterMainFrameInterruption();
}

IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, SuccessWithBody) {
  TestNavigationObserver navigation_observer(active_web_contents(), 1);
  StartNavigatingAndWaitForRequest();
  interesting_http_response()->Send("HTTP/1.1 200 Peachy\r\n\r\n");
  interesting_http_response()->Send("Grapefruit");
  interesting_http_response()->Done();
  navigation_observer.Wait();

  CheckHistograms(net::OK, HeadersReceived::kHeadersReceived,
                  NetworkAccessed::kNetworkAccessed);
}

IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, SuccessWithEmptyBody) {
  TestNavigationObserver navigation_observer(active_web_contents(), 1);
  StartNavigatingAndWaitForRequest();
  interesting_http_response()->Send("HTTP/1.1 200 Peachy\r\n");
  interesting_http_response()->Send("Content-Length: 0\r\n\r\n");
  interesting_http_response()->Done();
  navigation_observer.Wait();

  CheckHistograms(net::OK, HeadersReceived::kHeadersReceived,
                  NetworkAccessed::kNetworkAccessed);
}

// Downloads should not be logged (Either as successes or failures).
IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, Download) {
  // Only frames can be converted to downloads.
  if (GetParam() != RequestType::kMainFrame &&
      GetParam() != RequestType::kSubFrame) {
    return;
  }

  browser()->profile()->GetPrefs()->SetInteger(
      prefs::kDownloadRestrictions,
      static_cast<int>(DownloadPrefs::DownloadRestriction::ALL_FILES));
  browser()->profile()->GetPrefs()->SetBoolean(prefs::kPromptForDownload,
                                               false);

  // Need this to wait for the download to be fully cancelled to avoid a
  // confirmation prompt on quit.
  DownloadTestObserverTerminal download_test_observer_terminal(
      BrowserContext::GetDownloadManager(browser()->profile()), 1,
      DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_IGNORE);

  TestNavigationObserver navigation_observer(active_web_contents(), 1);
  StartNavigatingAndWaitForRequest();
  interesting_http_response()->Send("HTTP/1.1 200 Peachy\r\n");
  interesting_http_response()->Send(
      "Content-Type: binary/octet-stream\r\n\r\n");
  interesting_http_response()->Send("\x01");
  interesting_http_response()->Done();
  navigation_observer.Wait();

  download_test_observer_terminal.WaitForFinished();

  // Some metrics may come from the renderer. This call ensures that those
  // metrics are available.
  SubprocessMetricsProvider::MergeHistogramDeltasForTesting();

  if (GetParam() == RequestType::kMainFrame) {
    histograms()->ExpectTotalCount("Net.ErrorCodesForImages2", 0);
    histograms()->ExpectTotalCount("Net.ErrorCodesForMainFrame4", 0);
    histograms()->ExpectTotalCount("Net.ConnectionInfo.MainFrame", 0);
    // Favicon may or may not have been loaded.
    EXPECT_GE(
        1u,
        histograms()->GetAllSamples("Net.ErrorCodesForSubresources3").size());
    EXPECT_GE(
        1u,
        histograms()->GetAllSamples("Net.ConnectionInfo.SubResource").size());

    return;
  }

  // If not testing the main frame, there should also be just one result for
  // the main frame.
  histograms()->ExpectUniqueSample("Net.ErrorCodesForMainFrame4", -net::OK, 1);

  // Some fuzziness here because of the favicon. It should typically succeed,
  // but allow it to have been aborted, too, since the test server won't
  // return a valid icon.
  std::vector<base::Bucket> buckets =
      histograms()->GetAllSamples("Net.ErrorCodesForSubresources3");
  bool found_favicon_load = false;
  for (auto& bucket : buckets) {
    if (!found_favicon_load && bucket.count > 0 &&
        (bucket.min == -net::OK || bucket.min == -net::ERR_ABORTED)) {
      found_favicon_load = true;
      bucket.count--;
    }
    EXPECT_EQ(0, bucket.count)
        << "Found unexpected load with result: " << bucket.min;
  }

  histograms()->ExpectUniqueSample(
      "Net.ConnectionInfo.MainFrame",
      net::HttpResponseInfo::CONNECTION_INFO_HTTP1_1, 1);
  // Favicon request may or may not have received a response.
  size_t subresources =
      histograms()->GetAllSamples("Net.ConnectionInfo.SubResource").size();
  EXPECT_LE(0u, subresources);
  EXPECT_GE(1u, subresources);
}

// A few tests for file:// URLs, so that URLs not handled by the network service
// itself have some coverage.

IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, FileURLError) {
  base::ScopedAllowBlockingForTesting allow_blocking;
  base::ScopedTempDir temp_dir_;
  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());

  base::FilePath main_frame_path = temp_dir_.GetPath().AppendASCII("main.html");
  if (GetParam() != RequestType::kMainFrame) {
    std::string main_frame_data = GetMainFrameContents("subresource");
    ASSERT_EQ(static_cast<int>(main_frame_data.length()),
              base::WriteFile(main_frame_path, main_frame_data.c_str(),
                              main_frame_data.length()));
  }

  ui_test_utils::NavigateToURL(browser(),
                               net::FilePathToFileURL(main_frame_path));
  CheckHistograms(net::ERR_FILE_NOT_FOUND, HeadersReceived::kNoHeadersReceived,
                  NetworkAccessed::kNoNetworkAccessed);
}

IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, FileURLSuccess) {
  const char kSubresourcePath[] = "subresource";

  base::ScopedAllowBlockingForTesting allow_blocking;
  base::ScopedTempDir temp_dir_;
  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());

  base::FilePath main_frame_path = temp_dir_.GetPath().AppendASCII("main.html");
  std::string main_frame_data = "foo";
  if (GetParam() != RequestType::kMainFrame)
    main_frame_data = GetMainFrameContents(kSubresourcePath);
  ASSERT_EQ(static_cast<int>(main_frame_data.length()),
            base::WriteFile(main_frame_path, main_frame_data.c_str(),
                            main_frame_data.length()));
  if (GetParam() != RequestType::kMainFrame) {
    std::string subresource_data = "foo";
    ASSERT_EQ(
        static_cast<int>(subresource_data.length()),
        base::WriteFile(temp_dir_.GetPath().AppendASCII(kSubresourcePath),
                        subresource_data.c_str(), subresource_data.length()));
  }

  ui_test_utils::NavigateToURL(browser(),
                               net::FilePathToFileURL(main_frame_path));
  CheckHistograms(net::OK, HeadersReceived::kNoHeadersReceived,
                  NetworkAccessed::kNoNetworkAccessed);
}

INSTANTIATE_TEST_SUITE_P(All,
                         NetworkRequestMetricsBrowserTest,
                         testing::Values(RequestType::kMainFrame,
                                         RequestType::kSubFrame,
                                         RequestType::kImage,
                                         RequestType::kScript));

}  //  namespace
}  // namespace content
