// Copyright 2017 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 "base/files/scoped_temp_dir.h"
#include "base/test/scoped_feature_list.h"
#include "components/ukm/content/source_url_recorder.h"
#include "components/ukm/test_ukm_recorder.h"
#include "components/ukm/ukm_source.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/navigation_handle_observer.h"
#include "content/shell/browser/shell.h"
#include "content/shell/browser/shell_browser_context.h"
#include "content/shell/browser/shell_download_manager_delegate.h"
#include "services/metrics/public/cpp/ukm_builders.h"

class SourceUrlRecorderWebContentsObserverBrowserTest
    : public content::ContentBrowserTest {
 protected:
  SourceUrlRecorderWebContentsObserverBrowserTest() {
    scoped_feature_list_.InitAndEnableFeature(ukm::kUkmFeature);
  }

  ~SourceUrlRecorderWebContentsObserverBrowserTest() override {}

  void SetUpOnMainThread() override {
    content::ContentBrowserTest::SetUpOnMainThread();

    ASSERT_TRUE(embedded_test_server()->Start());

    test_ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
    ukm::InitializeSourceUrlRecorderForWebContents(shell()->web_contents());
  }

  const ukm::UkmSource* GetSourceForNavigationId(int64_t navigation_id) {
    CHECK_GT(navigation_id, 0);
    const ukm::SourceId source_id =
        ukm::ConvertToSourceId(navigation_id, ukm::SourceIdType::NAVIGATION_ID);
    return test_ukm_recorder_->GetSourceForSourceId(source_id);
  }

  GURL GetAssociatedURLForWebContentsDocument() {
    const ukm::UkmSource* src = test_ukm_recorder_->GetSourceForSourceId(
        ukm::GetSourceIdForWebContentsDocument(shell()->web_contents()));
    return src ? src->url() : GURL();
  }

  const ukm::TestAutoSetUkmRecorder& test_ukm_recorder() const {
    return *test_ukm_recorder_;
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
  std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder_;

  DISALLOW_COPY_AND_ASSIGN(SourceUrlRecorderWebContentsObserverBrowserTest);
};

class SourceUrlRecorderWebContentsObserverDownloadBrowserTest
    : public SourceUrlRecorderWebContentsObserverBrowserTest {
 protected:
  void SetUpOnMainThread() override {
    SourceUrlRecorderWebContentsObserverBrowserTest::SetUpOnMainThread();

    // Set up a test download directory, in order to prevent prompting for
    // handling downloads.
    ASSERT_TRUE(downloads_directory_.CreateUniqueTempDir());
    content::ShellDownloadManagerDelegate* delegate =
        static_cast<content::ShellDownloadManagerDelegate*>(
            shell()
                ->web_contents()
                ->GetBrowserContext()
                ->GetDownloadManagerDelegate());
    delegate->SetDownloadBehaviorForTesting(downloads_directory_.GetPath());
  }

 private:
  base::ScopedTempDir downloads_directory_;
};

IN_PROC_BROWSER_TEST_F(SourceUrlRecorderWebContentsObserverBrowserTest, Basic) {
  using Entry = ukm::builders::DocumentCreated;

  GURL url = embedded_test_server()->GetURL("/title1.html");
  content::NavigationHandleObserver observer(shell()->web_contents(), url);
  content::NavigateToURL(shell(), url);
  EXPECT_TRUE(observer.has_committed());
  const ukm::UkmSource* source =
      GetSourceForNavigationId(observer.navigation_id());
  EXPECT_NE(nullptr, source);
  EXPECT_EQ(url, source->url());
  EXPECT_TRUE(source->initial_url().is_empty());

  EXPECT_EQ(url, GetAssociatedURLForWebContentsDocument());

  // Check we have created a DocumentCreated event.
  auto ukm_entries = test_ukm_recorder().GetEntriesByName(Entry::kEntryName);
  EXPECT_EQ(1u, ukm_entries.size());
  EXPECT_EQ(source->id(), *test_ukm_recorder().GetEntryMetric(
                              ukm_entries[0], Entry::kNavigationSourceIdName));
  EXPECT_EQ(1, *test_ukm_recorder().GetEntryMetric(ukm_entries[0],
                                                   Entry::kIsMainFrameName));
  EXPECT_NE(source->id(), ukm_entries[0]->source_id);
}

IN_PROC_BROWSER_TEST_F(SourceUrlRecorderWebContentsObserverBrowserTest,
                       IgnoreUrlInSubframe) {
  using Entry = ukm::builders::DocumentCreated;

  GURL main_url = embedded_test_server()->GetURL("/page_with_iframe.html");
  GURL subframe_url = embedded_test_server()->GetURL("/title1.html");

  content::NavigationHandleObserver main_observer(shell()->web_contents(),
                                                  main_url);
  content::NavigationHandleObserver subframe_observer(shell()->web_contents(),
                                                      subframe_url);
  content::NavigateToURL(shell(), main_url);
  EXPECT_TRUE(main_observer.has_committed());
  EXPECT_TRUE(main_observer.is_main_frame());
  EXPECT_TRUE(subframe_observer.has_committed());
  EXPECT_FALSE(subframe_observer.is_main_frame());

  const ukm::UkmSource* source =
      GetSourceForNavigationId(main_observer.navigation_id());
  EXPECT_NE(nullptr, source);
  EXPECT_EQ(main_url, source->url());
  EXPECT_EQ(nullptr,
            GetSourceForNavigationId(subframe_observer.navigation_id()));

  EXPECT_EQ(main_url, GetAssociatedURLForWebContentsDocument());

  // Check we have created a DocumentCreated event for both frames.
  auto ukm_entries = test_ukm_recorder().GetEntriesByName(Entry::kEntryName);
  EXPECT_EQ(2u, ukm_entries.size());
  EXPECT_EQ(source->id(), *test_ukm_recorder().GetEntryMetric(
                              ukm_entries[0], Entry::kNavigationSourceIdName));
  EXPECT_EQ(0, *test_ukm_recorder().GetEntryMetric(ukm_entries[0],
                                                   Entry::kIsMainFrameName));
  EXPECT_EQ(source->id(), *test_ukm_recorder().GetEntryMetric(
                              ukm_entries[1], Entry::kNavigationSourceIdName));
  EXPECT_EQ(1, *test_ukm_recorder().GetEntryMetric(ukm_entries[1],
                                                   Entry::kIsMainFrameName));
  EXPECT_NE(ukm_entries[0]->source_id, ukm_entries[1]->source_id);
  EXPECT_NE(source->id(), ukm_entries[0]->source_id);
  EXPECT_NE(source->id(), ukm_entries[1]->source_id);
}

IN_PROC_BROWSER_TEST_F(SourceUrlRecorderWebContentsObserverDownloadBrowserTest,
                       IgnoreDownload) {
  GURL url(embedded_test_server()->GetURL("/download-test1.lib"));
  content::NavigationHandleObserver observer(shell()->web_contents(), url);
  content::NavigateToURL(shell(), url);
  EXPECT_FALSE(observer.has_committed());
  EXPECT_TRUE(observer.is_download());
  EXPECT_EQ(nullptr, GetSourceForNavigationId(observer.navigation_id()));
  EXPECT_EQ(GURL(), GetAssociatedURLForWebContentsDocument());
}
