text: // Copyright 2016 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 "third_party/blink/renderer/modules/webaudio/audio_context.h"
  
  #include <memory>
  
  #include "build/build_config.h"
  #include "testing/gtest/include/gtest/gtest.h"
  #include "third_party/blink/public/platform/platform.h"
  #include "third_party/blink/public/platform/web_audio_device.h"
  #include "third_party/blink/public/platform/web_audio_latency_hint.h"
  #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
  #include "third_party/blink/renderer/bindings/modules/v8/v8_audio_context_options.h"
  #include "third_party/blink/renderer/core/dom/document.h"
  #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
  #include "third_party/blink/renderer/core/frame/frame_types.h"
  #include "third_party/blink/renderer/core/frame/local_frame.h"
  #include "third_party/blink/renderer/core/frame/settings.h"
  #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
  #include "third_party/blink/renderer/core/html/media/autoplay_policy.h"
  #include "third_party/blink/renderer/core/loader/empty_clients.h"
  #include "third_party/blink/renderer/platform/testing/histogram_tester.h"
  #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
  #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
  
  namespace blink {
  
  namespace {
  
  const char* const kAutoplayMetric = "WebAudio.Autoplay";
  const char* const kAutoplayCrossOriginMetric = "WebAudio.Autoplay.CrossOrigin";
  
  class MockWebAudioDeviceForAutoplayTest : public WebAudioDevice {
   public:
    explicit MockWebAudioDeviceForAutoplayTest(double sample_rate,
                                               int frames_per_buffer)
        : sample_rate_(sample_rate), frames_per_buffer_(frames_per_buffer) {}
    ~MockWebAudioDeviceForAutoplayTest() override = default;
  
    void Start() override {}
    void Stop() override {}
    void Pause() override {}
    void Resume() override {}
    double SampleRate() override { return sample_rate_; }
    int FramesPerBuffer() override { return frames_per_buffer_; }
  
   private:
    double sample_rate_;
    int frames_per_buffer_;
  };
  
  class AudioContextAutoplayTestPlatform : public TestingPlatformSupport {
   public:
    std::unique_ptr<WebAudioDevice> CreateAudioDevice(
        unsigned number_of_input_channels,
        unsigned number_of_channels,
        const WebAudioLatencyHint& latency_hint,
        WebAudioDevice::RenderCallback*,
        const WebString& device_id) override {
      return std::make_unique<MockWebAudioDeviceForAutoplayTest>(
          AudioHardwareSampleRate(), AudioHardwareBufferSize());
    }
  
    double AudioHardwareSampleRate() override { return 44100; }
    size_t AudioHardwareBufferSize() override { return 128; }
  };
  
  }  // anonymous namespace
  
  class AudioContextAutoplayTest
      : public testing::TestWithParam<AutoplayPolicy::Type> {
   protected:
    using AutoplayStatus = AudioContext::AutoplayStatus;
  
    void SetUp() override {
      helper_.Initialize();
      frame_test_helpers::LoadHTMLString(helper_.LocalMainFrame(),
                                         "<iframe></iframe>",
                                         WebURL(KURL("https://example.com")));
      frame_test_helpers::LoadHTMLString(
          To<WebLocalFrameImpl>(helper_.LocalMainFrame()->FirstChild()), "",
          WebURL(KURL("https://cross-origin.com")));
      GetDocument().GetSettings()->SetAutoplayPolicy(GetParam());
      ChildDocument().GetSettings()->SetAutoplayPolicy(GetParam());
  
      histogram_tester_ = std::make_unique<HistogramTester>();
    }
  
    Document& GetDocument() {
      return *helper_.LocalMainFrame()->GetFrame()->GetDocument();
    }
  
    Document& ChildDocument() {
      return *To<WebLocalFrameImpl>(helper_.LocalMainFrame()->FirstChild())
                  ->GetFrame()
                  ->GetDocument();
    }
  
    ScriptState* GetScriptStateFrom(const Document& document) {
      return ToScriptStateForMainWorld(document.GetFrame());
    }
  
    void RejectPendingResolvers(AudioContext* audio_context) {
      audio_context->RejectPendingResolvers();
    }
  
    void RecordAutoplayStatus(AudioContext* audio_context) {
      audio_context->RecordAutoplayMetrics();
    }
  
    HistogramTester* GetHistogramTester() {
      return histogram_tester_.get();
    }
  
   private:
    ScopedTestingPlatformSupport<AudioContextAutoplayTestPlatform> platform_;
    frame_test_helpers::WebViewHelper helper_;
    std::unique_ptr<HistogramTester> histogram_tester_;
  };
  
  // Creates an AudioContext without a gesture inside a x-origin child frame.
  TEST_P(AudioContextAutoplayTest, AutoplayMetrics_CreateNoGesture_Child) {
    AudioContext* audio_context = AudioContext::Create(
        ChildDocument(), AudioContextOptions::Create(), ASSERT_NO_EXCEPTION);
    RecordAutoplayStatus(audio_context);
  
    switch (GetParam()) {
      case AutoplayPolicy::Type::kNoUserGestureRequired:
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 0);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
      case AutoplayPolicy::Type::kUserGestureRequired:
      case AutoplayPolicy::Type::kDocumentUserActivationRequired:
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayMetric, static_cast<int>(AutoplayStatus::kFailed), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 1);
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayCrossOriginMetric, static_cast<int>(AutoplayStatus::kFailed),
            1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 1);
        break;
    }
  }
  
  // Creates an AudioContext without a gesture inside a main frame.
  TEST_P(AudioContextAutoplayTest, AutoplayMetrics_CreateNoGesture_Main) {
    AudioContext* audio_context = AudioContext::Create(
        GetDocument(), AudioContextOptions::Create(), ASSERT_NO_EXCEPTION);
    RecordAutoplayStatus(audio_context);
  
    switch (GetParam()) {
      case AutoplayPolicy::Type::kNoUserGestureRequired:
      case AutoplayPolicy::Type::kUserGestureRequired:
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 0);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
      case AutoplayPolicy::Type::kDocumentUserActivationRequired:
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayMetric, static_cast<int>(AutoplayStatus::kFailed), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
    }
  }
  
  // Creates an AudioContext then call resume without a gesture in a x-origin
  // child frame.
  TEST_P(AudioContextAutoplayTest,
         AutoplayMetrics_CallResumeNoGesture_Child) {
    ScriptState::Scope scope(GetScriptStateFrom(ChildDocument()));
  
    AudioContext* audio_context = AudioContext::Create(
        ChildDocument(), AudioContextOptions::Create(), ASSERT_NO_EXCEPTION);
    audio_context->resumeContext(GetScriptStateFrom(ChildDocument()),
                                 ASSERT_NO_EXCEPTION);
    RejectPendingResolvers(audio_context);
    RecordAutoplayStatus(audio_context);
  
    switch (GetParam()) {
      case AutoplayPolicy::Type::kNoUserGestureRequired:
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 0);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
      case AutoplayPolicy::Type::kUserGestureRequired:
      case AutoplayPolicy::Type::kDocumentUserActivationRequired:
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayMetric, static_cast<int>(AutoplayStatus::kFailed), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 1);
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayCrossOriginMetric, static_cast<int>(AutoplayStatus::kFailed),
            1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 1);
        break;
    }
  }
  
  // Creates an AudioContext then call resume without a gesture in a main frame.
  TEST_P(AudioContextAutoplayTest, AutoplayMetrics_CallResumeNoGesture_Main) {
    ScriptState::Scope scope(GetScriptStateFrom(GetDocument()));
  
    AudioContext* audio_context = AudioContext::Create(
        GetDocument(), AudioContextOptions::Create(), ASSERT_NO_EXCEPTION);
    audio_context->resumeContext(GetScriptStateFrom(ChildDocument()),
                                 ASSERT_NO_EXCEPTION);
    RejectPendingResolvers(audio_context);
    RecordAutoplayStatus(audio_context);
  
    switch (GetParam()) {
      case AutoplayPolicy::Type::kNoUserGestureRequired:
      case AutoplayPolicy::Type::kUserGestureRequired:
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 0);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
      case AutoplayPolicy::Type::kDocumentUserActivationRequired:
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayMetric, static_cast<int>(AutoplayStatus::kFailed), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
    }
  }
  
  // Creates an AudioContext with a user gesture inside a x-origin child frame.
  TEST_P(AudioContextAutoplayTest, AutoplayMetrics_CreateGesture_Child) {
    LocalFrame::NotifyUserActivation(
        ChildDocument().GetFrame(), mojom::UserActivationNotificationType::kTest);
  
    AudioContext* audio_context = AudioContext::Create(
        ChildDocument(), AudioContextOptions::Create(), ASSERT_NO_EXCEPTION);
    RecordAutoplayStatus(audio_context);
  
    switch (GetParam()) {
      case AutoplayPolicy::Type::kNoUserGestureRequired:
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 0);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
      case AutoplayPolicy::Type::kUserGestureRequired:
      case AutoplayPolicy::Type::kDocumentUserActivationRequired:
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayMetric, static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 1);
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayCrossOriginMetric,
            static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 1);
        break;
    }
  }
  
  // Creates an AudioContext with a user gesture inside a main frame.
  TEST_P(AudioContextAutoplayTest, AutoplayMetrics_CreateGesture_Main) {
    LocalFrame::NotifyUserActivation(
        GetDocument().GetFrame(), mojom::UserActivationNotificationType::kTest);
  
    AudioContext* audio_context = AudioContext::Create(
        GetDocument(), AudioContextOptions::Create(), ASSERT_NO_EXCEPTION);
    RecordAutoplayStatus(audio_context);
  
    switch (GetParam()) {
      case AutoplayPolicy::Type::kNoUserGestureRequired:
      case AutoplayPolicy::Type::kUserGestureRequired:
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 0);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
      case AutoplayPolicy::Type::kDocumentUserActivationRequired:
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayMetric, static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
    }
  }
  
  // Creates an AudioContext then calls resume with a user gesture inside a
  // x-origin child frame.
  TEST_P(AudioContextAutoplayTest, AutoplayMetrics_CallResumeGesture_Child) {
    ScriptState::Scope scope(GetScriptStateFrom(ChildDocument()));
  
    AudioContext* audio_context = AudioContext::Create(
        ChildDocument(), AudioContextOptions::Create(), ASSERT_NO_EXCEPTION);
  
    LocalFrame::NotifyUserActivation(
        ChildDocument().GetFrame(), mojom::UserActivationNotificationType::kTest);
  
    audio_context->resumeContext(GetScriptStateFrom(ChildDocument()),
                                 ASSERT_NO_EXCEPTION);
    RejectPendingResolvers(audio_context);
    RecordAutoplayStatus(audio_context);
  
    switch (GetParam()) {
      case AutoplayPolicy::Type::kNoUserGestureRequired:
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 0);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
      case AutoplayPolicy::Type::kUserGestureRequired:
      case AutoplayPolicy::Type::kDocumentUserActivationRequired:
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayMetric, static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 1);
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayCrossOriginMetric,
            static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 1);
        break;
    }
  }
  
  // Creates an AudioContext then calls resume with a user gesture inside a main
  // frame.
  TEST_P(AudioContextAutoplayTest, AutoplayMetrics_CallResumeGesture_Main) {
    ScriptState::Scope scope(GetScriptStateFrom(GetDocument()));
  
    AudioContext* audio_context = AudioContext::Create(
        GetDocument(), AudioContextOptions::Create(), ASSERT_NO_EXCEPTION);
  
    LocalFrame::NotifyUserActivation(
        GetDocument().GetFrame(), mojom::UserActivationNotificationType::kTest);
  
    audio_context->resumeContext(GetScriptStateFrom(GetDocument()),
                                 ASSERT_NO_EXCEPTION);
    RejectPendingResolvers(audio_context);
    RecordAutoplayStatus(audio_context);
  
    switch (GetParam()) {
      case AutoplayPolicy::Type::kNoUserGestureRequired:
      case AutoplayPolicy::Type::kUserGestureRequired:
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 0);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
      case AutoplayPolicy::Type::kDocumentUserActivationRequired:
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayMetric, static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
    }
  }
  
  // Creates an AudioContext then calls start on a node without a gesture inside a
  // x-origin child frame.
  TEST_P(AudioContextAutoplayTest, AutoplayMetrics_NodeStartNoGesture_Child) {
    AudioContext* audio_context = AudioContext::Create(
        ChildDocument(), AudioContextOptions::Create(), ASSERT_NO_EXCEPTION);
    audio_context->NotifySourceNodeStart();
    RecordAutoplayStatus(audio_context);
  
    switch (GetParam()) {
      case AutoplayPolicy::Type::kNoUserGestureRequired:
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 0);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
      case AutoplayPolicy::Type::kUserGestureRequired:
      case AutoplayPolicy::Type::kDocumentUserActivationRequired:
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayMetric, static_cast<int>(AutoplayStatus::kFailed), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 1);
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayCrossOriginMetric, static_cast<int>(AutoplayStatus::kFailed),
            1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 1);
        break;
    }
  }
  
  // Creates an AudioContext then calls start on a node without a gesture inside a
  // main frame.
  TEST_P(AudioContextAutoplayTest, AutoplayMetrics_NodeStartNoGesture_Main) {
    AudioContext* audio_context = AudioContext::Create(
        GetDocument(), AudioContextOptions::Create(), ASSERT_NO_EXCEPTION);
    audio_context->NotifySourceNodeStart();
    RecordAutoplayStatus(audio_context);
  
    switch (GetParam()) {
      case AutoplayPolicy::Type::kNoUserGestureRequired:
      case AutoplayPolicy::Type::kUserGestureRequired:
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 0);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
      case AutoplayPolicy::Type::kDocumentUserActivationRequired:
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayMetric, static_cast<int>(AutoplayStatus::kFailed), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
    }
  }
  
  // Creates an AudioContext then calls start on a node with a gesture inside a
  // x-origin child frame.
  TEST_P(AudioContextAutoplayTest, AutoplayMetrics_NodeStartGesture_Child) {
    AudioContext* audio_context = AudioContext::Create(
        ChildDocument(), AudioContextOptions::Create(), ASSERT_NO_EXCEPTION);
  
    LocalFrame::NotifyUserActivation(
        ChildDocument().GetFrame(), mojom::UserActivationNotificationType::kTest);
    audio_context->NotifySourceNodeStart();
    RecordAutoplayStatus(audio_context);
  
    switch (GetParam()) {
      case AutoplayPolicy::Type::kNoUserGestureRequired:
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 0);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
      case AutoplayPolicy::Type::kUserGestureRequired:
      case AutoplayPolicy::Type::kDocumentUserActivationRequired:
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayMetric, static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 1);
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayCrossOriginMetric,
            static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 1);
        break;
    }
  }
  
  // Creates an AudioContext then calls start on a node with a gesture inside a
  // main frame.
  TEST_P(AudioContextAutoplayTest, AutoplayMetrics_NodeStartGesture_Main) {
    AudioContext* audio_context = AudioContext::Create(
        GetDocument(), AudioContextOptions::Create(), ASSERT_NO_EXCEPTION);
  
    LocalFrame::NotifyUserActivation(
        GetDocument().GetFrame(), mojom::UserActivationNotificationType::kTest);
    audio_context->NotifySourceNodeStart();
    RecordAutoplayStatus(audio_context);
  
    switch (GetParam()) {
      case AutoplayPolicy::Type::kNoUserGestureRequired:
      case AutoplayPolicy::Type::kUserGestureRequired:
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 0);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
      case AutoplayPolicy::Type::kDocumentUserActivationRequired:
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayMetric, static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
    }
  }
  
  // Creates an AudioContext then calls start on a node without a gesture and
  // finally allows the AudioContext to produce sound inside x-origin child frame.
  TEST_P(AudioContextAutoplayTest,
         AutoplayMetrics_NodeStartNoGestureThenSuccess_Child) {
    ScriptState::Scope scope(GetScriptStateFrom(ChildDocument()));
  
    AudioContext* audio_context = AudioContext::Create(
        ChildDocument(), AudioContextOptions::Create(), ASSERT_NO_EXCEPTION);
    audio_context->NotifySourceNodeStart();
  
    LocalFrame::NotifyUserActivation(
        ChildDocument().GetFrame(), mojom::UserActivationNotificationType::kTest);
    audio_context->resumeContext(GetScriptStateFrom(ChildDocument()),
                                 ASSERT_NO_EXCEPTION);
    RejectPendingResolvers(audio_context);
    RecordAutoplayStatus(audio_context);
  
    switch (GetParam()) {
      case AutoplayPolicy::Type::kNoUserGestureRequired:
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 0);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
      case AutoplayPolicy::Type::kUserGestureRequired:
      case AutoplayPolicy::Type::kDocumentUserActivationRequired:
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayMetric, static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 1);
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayCrossOriginMetric,
            static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 1);
        break;
    }
  }
  
  // Creates an AudioContext then calls start on a node without a gesture and
  // finally allows the AudioContext to produce sound inside a main frame.
  TEST_P(AudioContextAutoplayTest,
         AutoplayMetrics_NodeStartNoGestureThenSuccess_Main) {
    ScriptState::Scope scope(GetScriptStateFrom(GetDocument()));
  
    AudioContext* audio_context = AudioContext::Create(
        GetDocument(), AudioContextOptions::Create(), ASSERT_NO_EXCEPTION);
    audio_context->NotifySourceNodeStart();
  
    LocalFrame::NotifyUserActivation(
        GetDocument().GetFrame(), mojom::UserActivationNotificationType::kTest);
    audio_context->resumeContext(GetScriptStateFrom(GetDocument()),
                                 ASSERT_NO_EXCEPTION);
    RejectPendingResolvers(audio_context);
    RecordAutoplayStatus(audio_context);
  
    switch (GetParam()) {
      case AutoplayPolicy::Type::kNoUserGestureRequired:
      case AutoplayPolicy::Type::kUserGestureRequired:
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 0);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
      case AutoplayPolicy::Type::kDocumentUserActivationRequired:
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayMetric, static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
    }
  }
  
  // Creates an AudioContext then calls start on a node with a gesture and
  // finally allows the AudioContext to produce sound inside x-origin child frame.
  TEST_P(AudioContextAutoplayTest,
         AutoplayMetrics_NodeStartGestureThenSucces_Child) {
    ScriptState::Scope scope(GetScriptStateFrom(ChildDocument()));
  
    AudioContext* audio_context = AudioContext::Create(
        ChildDocument(), AudioContextOptions::Create(), ASSERT_NO_EXCEPTION);
  
    LocalFrame::NotifyUserActivation(
        ChildDocument().GetFrame(), mojom::UserActivationNotificationType::kTest);
    audio_context->NotifySourceNodeStart();
    audio_context->resumeContext(GetScriptStateFrom(ChildDocument()),
                                 ASSERT_NO_EXCEPTION);
    RejectPendingResolvers(audio_context);
    RecordAutoplayStatus(audio_context);
  
    switch (GetParam()) {
      case AutoplayPolicy::Type::kNoUserGestureRequired:
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 0);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
      case AutoplayPolicy::Type::kUserGestureRequired:
      case AutoplayPolicy::Type::kDocumentUserActivationRequired:
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayMetric, static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 1);
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayCrossOriginMetric,
            static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 1);
        break;
    }
  }
  
  // Creates an AudioContext then calls start on a node with a gesture and
  // finally allows the AudioContext to produce sound inside a main frame.
  TEST_P(AudioContextAutoplayTest,
         AutoplayMetrics_NodeStartGestureThenSucces_Main) {
    ScriptState::Scope scope(GetScriptStateFrom(GetDocument()));
  
    AudioContext* audio_context = AudioContext::Create(
        GetDocument(), AudioContextOptions::Create(), ASSERT_NO_EXCEPTION);
  
    LocalFrame::NotifyUserActivation(
        GetDocument().GetFrame(), mojom::UserActivationNotificationType::kTest);
    audio_context->NotifySourceNodeStart();
    audio_context->resumeContext(GetScriptStateFrom(GetDocument()),
                                 ASSERT_NO_EXCEPTION);
    RejectPendingResolvers(audio_context);
    RecordAutoplayStatus(audio_context);
  
    switch (GetParam()) {
      case AutoplayPolicy::Type::kNoUserGestureRequired:
      case AutoplayPolicy::Type::kUserGestureRequired:
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 0);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
      case AutoplayPolicy::Type::kDocumentUserActivationRequired:
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayMetric, static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
    }
  }
  
  // Attempts to autoplay an AudioContext in a x-origin child frame when the
  // document previous received a user gesture.
  TEST_P(AudioContextAutoplayTest,
         AutoplayMetrics_DocumentReceivedGesture_Child) {
    LocalFrame::NotifyUserActivation(
        ChildDocument().GetFrame(), mojom::UserActivationNotificationType::kTest);
  
    AudioContext* audio_context = AudioContext::Create(
        ChildDocument(), AudioContextOptions::Create(), ASSERT_NO_EXCEPTION);
    RecordAutoplayStatus(audio_context);
  
    switch (GetParam()) {
      case AutoplayPolicy::Type::kNoUserGestureRequired:
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 0);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
      case AutoplayPolicy::Type::kUserGestureRequired:
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayMetric, static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 1);
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayCrossOriginMetric,
            static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 1);
        break;
      case AutoplayPolicy::Type::kDocumentUserActivationRequired:
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayMetric, static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 1);
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayCrossOriginMetric,
            static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 1);
        break;
    }
  }
  
  // Attempts to autoplay an AudioContext in a main child frame when the
  // document previous received a user gesture.
  TEST_P(AudioContextAutoplayTest,
         AutoplayMetrics_DocumentReceivedGesture_Main) {
    LocalFrame::NotifyUserActivation(
        ChildDocument().GetFrame(), mojom::UserActivationNotificationType::kTest);
  
    AudioContext* audio_context = AudioContext::Create(
        GetDocument(), AudioContextOptions::Create(), ASSERT_NO_EXCEPTION);
    RecordAutoplayStatus(audio_context);
  
    switch (GetParam()) {
      case AutoplayPolicy::Type::kNoUserGestureRequired:
      case AutoplayPolicy::Type::kUserGestureRequired:
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 0);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
      case AutoplayPolicy::Type::kDocumentUserActivationRequired:
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayMetric, static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
    }
  }
  
  // Attempts to autoplay an AudioContext in a main child frame when the
  // document received a user gesture before navigation.
  TEST_P(AudioContextAutoplayTest,
         AutoplayMetrics_DocumentReceivedGesture_BeforeNavigation) {
    GetDocument().GetFrame()->SetHadStickyUserActivationBeforeNavigation(true);
  
    AudioContext* audio_context = AudioContext::Create(
        GetDocument(), AudioContextOptions::Create(), ASSERT_NO_EXCEPTION);
    RecordAutoplayStatus(audio_context);
  
    switch (GetParam()) {
      case AutoplayPolicy::Type::kNoUserGestureRequired:
      case AutoplayPolicy::Type::kUserGestureRequired:
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 0);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
      case AutoplayPolicy::Type::kDocumentUserActivationRequired:
        GetHistogramTester()->ExpectBucketCount(
            kAutoplayMetric, static_cast<int>(AutoplayStatus::kSucceeded), 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayMetric, 1);
        GetHistogramTester()->ExpectTotalCount(kAutoplayCrossOriginMetric, 0);
        break;
    }
  }
  
  INSTANTIATE_TEST_SUITE_P(
      AudioContextAutoplayTest,
      AudioContextAutoplayTest,
      testing::Values(AutoplayPolicy::Type::kNoUserGestureRequired,
                      AutoplayPolicy::Type::kUserGestureRequired,
                      AutoplayPolicy::Type::kDocumentUserActivationRequired));
  
  }  // namespace blink
  