910e62b5创建于 1月15日历史提交
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "extensions/browser/message_tracker.h"

#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/with_feature_override.h"
#include "chrome/browser/extensions/browsertest_util.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "content/public/test/browser_test.h"
#include "extensions/browser/service_worker/service_worker_test_utils.h"
#include "extensions/common/extension_features.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/test_extension_dir.h"
#include "net/dns/mock_host_resolver.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace extensions {

class MessageTrackerMessagingTest : public ExtensionApiTest {
 public:
  MessageTrackerMessagingTest() = default;

  MessageTrackerMessagingTest(const MessageTrackerMessagingTest&) = delete;
  MessageTrackerMessagingTest& operator=(const MessageTrackerMessagingTest&) =
      delete;

  void SetUpOnMainThread() override {
    ExtensionApiTest::SetUpOnMainThread();
    host_resolver()->AddRule("*", "127.0.0.1");
    message_tracker_ = MessageTracker::Get(profile());
  }

  void TearDownOnMainThread() override {
    message_tracker_ = nullptr;
    ExtensionApiTest::TearDownOnMainThread();
  }

  MessageTracker* message_tracker() { return message_tracker_; }

 private:
  raw_ptr<MessageTracker> message_tracker_;
};

class MessageTrackerMessagingTestWithOptimizeServiceWorkerStart
    : public MessageTrackerMessagingTest,
      public base::test::WithFeatureOverride {
 public:
  MessageTrackerMessagingTestWithOptimizeServiceWorkerStart()
      : WithFeatureOverride(
            extensions_features::kOptimizeServiceWorkerStartRequests) {}
};

// Tests the tracking of messages when sent from a tab to a SW extension
// background context.
IN_PROC_BROWSER_TEST_P(
    MessageTrackerMessagingTestWithOptimizeServiceWorkerStart,
    SendMessageToWorker) {
  const bool wakeup_optimization_enabled = IsParamFeatureEnabled();
  const int kExpectedWakeUps = wakeup_optimization_enabled ? 0 : 1;

  ExtensionTestMessageListener worker_listener("WORKER_RUNNING");
  const Extension* extension = LoadExtension(test_data_dir_.AppendASCII(
      "service_worker/messaging/send_message_tab_to_worker"));
  ASSERT_TRUE(extension);
  EXPECT_TRUE(worker_listener.WaitUntilSatisfied());

  ExtensionTestMessageListener reply_listener("CONTENT_SCRIPT_RECEIVED_REPLY");
  reply_listener.set_failure_message("FAILURE");

  base::HistogramTester histogram_tester;

  {
    ASSERT_TRUE(StartEmbeddedTestServer());
    const GURL url =
        embedded_test_server()->GetURL("/extensions/test_file.html");
    content::WebContents* new_web_contents =
        browsertest_util::AddTab(browser(), url);
    EXPECT_TRUE(new_web_contents);
  }

  EXPECT_TRUE(reply_listener.WaitUntilSatisfied());

  // Overall channel open metrics expectations.
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelStatus.SendMessageChannel",
      /*expected_count=*/1);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelStatusWithActiveWorker",
      /*expected_count=*/1);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelStatusWithActiveWorker."
      "SendMessageChannel",
      /*expected_count=*/1);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelWorkerDispatchStatus."
      "SendMessageChannel",
      /*expected_count=*/1);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelWorkerWakeUpStatus."
      "SendMessageChannel",
      /*expected_count=*/kExpectedWakeUps);
  // Per connect IPC dispatch metrics expectations.
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForWorker",
      /*expected_count=*/1);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForWorker."
      "SendMessageChannel",
      /*expected_count=*/1);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForFrame",
      /*expected_count=*/0);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForFrame."
      "SendMessageChannel",
      /*expected_count=*/0);

  // Overall channel open metrics expectations.
  histogram_tester.ExpectBucketCount(
      "Extensions.MessagePipeline.OpenChannelStatus.SendMessageChannel",
      /*sample=*/MessageTracker::OpenChannelMessagePipelineResult::kOpened,
      /*expected_count=*/1);
  histogram_tester.ExpectBucketCount(
      "Extensions.MessagePipeline.OpenChannelStatusWithActiveWorker."
      "SendMessageChannel",
      /*sample=*/MessageTracker::OpenChannelMessagePipelineResult::kOpened,
      /*expected_count=*/1);
  histogram_tester.ExpectBucketCount(
      "Extensions.MessagePipeline.OpenChannelWorkerDispatchStatus."
      "SendMessageChannel",
      /*sample=*/MessageTracker::OpenChannelMessagePipelineResult::kOpened,
      /*expected_count=*/1);
  histogram_tester.ExpectBucketCount(
      "Extensions.MessagePipeline.OpenChannelWorkerWakeUpStatus."
      "SendMessageChannel",
      /*sample=*/
      MessageTracker::OpenChannelMessagePipelineResult::kWorkerStarted,
      /*expected_count=*/kExpectedWakeUps);
  // Per connect IPC dispatch metrics expectations.
  histogram_tester.ExpectBucketCount(
      "Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForWorker",
      /*sample=*/
      MessageTracker::OpenChannelMessagePipelineResult::kOpenChannelAcked,
      /*expected_count=*/1);
  histogram_tester.ExpectBucketCount(
      "Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForWorker."
      "SendMessageChannel",
      /*sample=*/
      MessageTracker::OpenChannelMessagePipelineResult::kOpenChannelAcked,
      /*expected_count=*/1);
}

class MessageTrackerMessagingTestNonWorker
    : public ExtensionApiTest,
      public testing::WithParamInterface<const char*> {};

// Tests the tracking of messages when sent from a tab to a extension background
// page context.
IN_PROC_BROWSER_TEST_P(MessageTrackerMessagingTestNonWorker,
                       SendMessageToNonWorker) {
  ExtensionTestMessageListener background_listener("BACKGROUND_RUNNING");
  constexpr char kManifest[] =
      R"(
      {
        "name": "runtime.sendMessage from content script to extension",
        "version": "0.1",
        "manifest_version": 2,
        "description": "non service worker",
        "content_scripts": [{
          "matches": ["*://*/*"],
          "js": ["content.js"]
        }],
        "background": {
          "scripts": ["background.js"],
          "persistent": %s
        }
      }
    )";

  constexpr char kContentScript[] =
      R"(
      chrome.runtime.sendMessage('tab->background', response => {
        if (response != 'tab->background->tab') {
          chrome.test.sendMessage('FAILURE');
          return;
        }
        chrome.test.sendMessage('CONTENT_SCRIPT_RECEIVED_REPLY');
      });
    )";

  constexpr char kBackground[] =
      R"(
      chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
        if (msg != 'tab->background') {
          chrome.test.sendMessage('FAILURE');
          return;
        }
        sendResponse('tab->background->tab');
      });

      chrome.test.sendMessage('BACKGROUND_RUNNING');
    )";
  extensions::TestExtensionDir test_dir;
  test_dir.WriteManifest(
      base::StringPrintf(kManifest, /*persistent=*/GetParam()));
  test_dir.WriteFile(FILE_PATH_LITERAL("content.js"), kContentScript);
  test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackground);
  const Extension* extension = LoadExtension(test_dir.UnpackedPath());
  ASSERT_TRUE(extension);
  EXPECT_TRUE(background_listener.WaitUntilSatisfied());

  ExtensionTestMessageListener reply_listener("CONTENT_SCRIPT_RECEIVED_REPLY");
  reply_listener.set_failure_message("FAILURE");

  base::HistogramTester histogram_tester;

  {
    ASSERT_TRUE(StartEmbeddedTestServer());
    const GURL url =
        embedded_test_server()->GetURL("/extensions/test_file.html");
    content::WebContents* new_web_contents =
        browsertest_util::AddTab(browser(), url);
    EXPECT_TRUE(new_web_contents);
  }

  EXPECT_TRUE(reply_listener.WaitUntilSatisfied());

  // Overall channel open metrics expectations.
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelStatus.SendMessageChannel",
      /*expected_count=*/1);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelStatusWithNoWorker."
      "SendMessageChannel",
      /*expected_count=*/1);
  // No worker metrics should emitted.
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelStatusWithActiveWorker",
      /*expected_count=*/0);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelStatusWithIdleWorker",
      /*expected_count=*/0);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelStatusWithActiveWorker."
      "SendMessageChannel",
      /*expected_count=*/0);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelWorkerDispatchStatus."
      "SendMessageChannel",
      /*expected_count=*/0);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelWorkerWakeUpStatus."
      "SendMessageChannel",
      /*expected_count=*/0);
  // Per connect IPC dispatch metrics expectations.
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForWorker",
      /*expected_count=*/0);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForWorker."
      "SendMessageChannel",
      /*expected_count=*/0);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForFrame",
      /*expected_count=*/1);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForFrame."
      "SendMessageChannel",
      /*expected_count=*/1);

  // Overall channel open metrics expectations.
  histogram_tester.ExpectBucketCount(
      "Extensions.MessagePipeline.OpenChannelStatus.SendMessageChannel",
      /*sample=*/MessageTracker::OpenChannelMessagePipelineResult::kOpened,
      /*expected_count=*/1);
  histogram_tester.ExpectBucketCount(
      "Extensions.MessagePipeline.OpenChannelStatusWithNoWorker."
      "SendMessageChannel",
      /*sample=*/MessageTracker::OpenChannelMessagePipelineResult::kOpened,
      /*expected_count=*/1);
  // Per connect IPC dispatch metrics expectations.
  histogram_tester.ExpectBucketCount(
      "Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForFrame",
      /*sample=*/
      MessageTracker::OpenChannelMessagePipelineResult::kOpenChannelAcked,
      /*expected_count=*/1);
  histogram_tester.ExpectBucketCount(
      "Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForFrame."
      "SendMessageChannel",
      /*sample=*/
      MessageTracker::OpenChannelMessagePipelineResult::kOpenChannelAcked,
      /*expected_count=*/1);
}

INSTANTIATE_TEST_SUITE_P(EventPage,
                         MessageTrackerMessagingTestNonWorker,
                         testing::Values("false"));
INSTANTIATE_TEST_SUITE_P(PersistentBackgroundPage,
                         MessageTrackerMessagingTestNonWorker,
                         testing::Values("true"));

// Tests the tracking of messages when sent from a tab content script to a
// extension background page context and an extension tab script.
IN_PROC_BROWSER_TEST_P(
    MessageTrackerMessagingTestWithOptimizeServiceWorkerStart,
    SendMessageToTabAndWorker) {
  const bool wakeup_optimization_enabled = IsParamFeatureEnabled();
  const int kExpectedWakeUps = wakeup_optimization_enabled ? 0 : 1;

  constexpr char kManifest[] =
      R"(
        {
          "name": "runtime.sendMessage from content script to SW extension",
          "version": "0.1",
          "manifest_version": 3,
          "description": "service worker",
          "content_scripts": [{
            "matches": ["*://*/*"],
            "js": ["content_script.js"]
          }],
          "background": {"service_worker": "background.js"}
        }
      )";

  constexpr char kContentScript[] =
      R"(
      chrome.runtime.sendMessage('tab->worker', response => {
        if (response != 'tab->worker->tab') {
          chrome.test.sendMessage('FAILURE');
          return;
        }
        chrome.test.sendMessage('CONTENT_SCRIPT_RECEIVED_REPLY');
      });
    )";

  constexpr char kTestExtTabHtml[] =
      R"(
      <!DOCTYPE html>
      <body>
        <script src="test_tab.js"></script>
      </body>
      </html>
      )";

  constexpr char kTestExtTab[] =
      R"(
      chrome.runtime.onMessage.addListener((details) => {
        chrome.test.sendMessage('TAB_LISTENER_RECEIVED_MESSAGE');
      });
    )";

  constexpr char kBackground[] =
      R"(
      chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
        if (msg != 'tab->worker') {
          chrome.test.sendMessage('FAILURE');
          return;
        }
        sendResponse('tab->worker->tab');
      });

      chrome.runtime.onInstalled.addListener((details) => {
        chrome.test.sendMessage('WORKER_RUNNING');
      });
    )";
  extensions::TestExtensionDir test_dir;
  test_dir.WriteManifest(kManifest);
  test_dir.WriteFile(FILE_PATH_LITERAL("content_script.js"), kContentScript);
  test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackground);
  test_dir.WriteFile(FILE_PATH_LITERAL("test_ext_tab.html"), kTestExtTabHtml);
  test_dir.WriteFile(FILE_PATH_LITERAL("test_tab.js"), kTestExtTab);
  ExtensionTestMessageListener background_listener("WORKER_RUNNING");
  const Extension* extension = LoadExtension(test_dir.UnpackedPath());
  ASSERT_TRUE(extension);
  EXPECT_TRUE(background_listener.WaitUntilSatisfied());

  ExtensionTestMessageListener reply_listener("CONTENT_SCRIPT_RECEIVED_REPLY");
  reply_listener.set_failure_message("REPLY_FAILURE");
  ExtensionTestMessageListener ext_tab_listener_fired(
      "TAB_LISTENER_RECEIVED_MESSAGE");
  ext_tab_listener_fired.set_failure_message("TAB_LISTENER_FAILURE");

  base::HistogramTester histogram_tester;

  {
    ASSERT_TRUE(StartEmbeddedTestServer());

    // Load the extension tab (and it's script).
    GURL ext_url = extension->GetResourceURL("test_ext_tab.html");
    content::WebContents* new_ext_tab_web_contents = GetActiveWebContents();
    ASSERT_TRUE(new_ext_tab_web_contents);
    ASSERT_TRUE(NavigateToURL(new_ext_tab_web_contents, GURL(ext_url)));

    // Must load the tab content script second since it's loading sends a
    // message to the extension tab.
    const GURL url =
        embedded_test_server()->GetURL("/extensions/test_file.html");
    content::WebContents* new_web_contents =
        browsertest_util::AddTab(browser(), url);
    ASSERT_TRUE(new_web_contents);
  }

  ASSERT_TRUE(reply_listener.WaitUntilSatisfied());
  ASSERT_TRUE(ext_tab_listener_fired.WaitUntilSatisfied());

  // Overall channel open metrics expectations.
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelStatus.SendMessageChannel",
      /*expected_count=*/1);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelStatusWithActiveWorker",
      /*expected_count=*/1);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelStatusWithActiveWorker."
      "SendMessageChannel",
      /*expected_count=*/1);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelWorkerDispatchStatus."
      "SendMessageChannel",
      /*expected_count=*/1);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelWorkerWakeUpStatus."
      "SendMessageChannel",
      /*expected_count=*/kExpectedWakeUps);
  // Per connect IPC dispatch metrics expectations.
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForWorker",
      /*expected_count=*/1);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForWorker."
      "SendMessageChannel",
      /*expected_count=*/1);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForFrame",
      /*expected_count=*/1);
  histogram_tester.ExpectTotalCount(
      "Extensions.MessagePipeline.OpenChannelDispatchOnConnectStatus.ForFrame."
      "SendMessageChannel",
      /*expected_count=*/1);

  // Overall channel open metrics expectations.
  histogram_tester.ExpectBucketCount(
      "Extensions.MessagePipeline.OpenChannelStatus.SendMessageChannel",
      /*sample=*/MessageTracker::OpenChannelMessagePipelineResult::kOpened,
      /*expected_count=*/1);
  histogram_tester.ExpectBucketCount(
      "Extensions.MessagePipeline.OpenChannelStatusWithActiveWorker."
      "SendMessageChannel",
      /*sample=*/MessageTracker::OpenChannelMessagePipelineResult::kOpened,
      /*expected_count=*/1);
  histogram_tester.ExpectBucketCount(
      "Extensions.MessagePipeline.OpenChannelWorkerDispatchStatus."
      "SendMessageChannel",
      /*sample=*/MessageTracker::OpenChannelMessagePipelineResult::kOpened,
      /*expected_count=*/1);
  histogram_tester.ExpectBucketCount(
      "Extensions.MessagePipeline.OpenChannelWorkerWakeUpStatus."
      "SendMessageChannel",
      /*sample=*/
      MessageTracker::OpenChannelMessagePipelineResult::kWorkerStarted,
      /*expected_count=*/kExpectedWakeUps);
  // Per connect IPC dispatch metrics expectations cannot be specified in this
  // test because the channel will be closed by the first port responder to the
  // IPC and that will can change the value emitted for the other port.
}

// TODO(crbug.com/371011217): Once we start tracking message dispatch metrics
// add a test case for a worker that never responds to the message.

// Toggle `extensions_features::OptimizeServiceWorkerStartRequests`.
INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(
    MessageTrackerMessagingTestWithOptimizeServiceWorkerStart);

}  // namespace extensions