chromium/chrome/browser/extensions/script_injection_tracker_browsertest.cc

// Copyright 2021 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/script_injection_tracker.h"

#include <string>
#include <string_view>
#include <vector>

#include "base/files/file_path.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/bind.h"
#include "build/buildflag.h"
#include "chrome/browser/apps/platform_apps/app_browsertest_util.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/extensions/permissions/permissions_test_util.h"
#include "chrome/browser/extensions/tab_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/version_info/channel.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/back_forward_cache_util.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/commit_message_delayer.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/browsertest_util.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/process_manager.h"
#include "extensions/browser/script_executor.h"
#include "extensions/browser/user_script_manager.h"
#include "extensions/common/features/feature_channel.h"
#include "extensions/common/manifest_handlers/permissions_parser.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/result_catcher.h"
#include "extensions/test/test_content_script_load_waiter.h"
#include "extensions/test/test_extension_dir.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/controllable_http_response.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

namespace extensions {

// Asks the |extension_id| to inject |content_script| into |web_contents|.
void ExecuteProgrammaticContentScriptNoWait(content::WebContents* web_contents,
                                            const ExtensionId& extension_id,
                                            const std::string& content_script,
                                            const char* message) {}

// Asks the |extension_id| to inject |content_script| into |web_contents| and
// waits until the script reports that it has finished executing.
void ExecuteProgrammaticContentScript(content::WebContents* web_contents,
                                      const ExtensionId& extension_id,
                                      const std::string& content_script) {}

// Executes a `script` as a user script associated with the given `extension_id`
// within the primary main frame of `web_contents`, waiting for the injection to
// complete.
void ExecuteUserScript(content::WebContents& web_contents,
                       const ExtensionId& extension_id,
                       const std::string& script) {}

// Test suite covering `extensions::ScriptInjectionTracker` from
// //extensions/browser/script_injection_tracker.h.
//
// See also ContentScriptMatchingBrowserTest in
// //extensions/browser/content_script_matching_browsertest.cc.
class ScriptInjectionTrackerBrowserTest : public ExtensionBrowserTest {};

// Helper class for executing a content script right before handling a DidCommit
// IPC.
class ContentScriptExecuterBeforeDidCommit {};

// Tests tracking of content scripts injected/declared via
// `chrome.scripting.executeScript` API.  See also:
// https://developer.chrome.com/docs/extensions/mv3/content_scripts/#programmatic
IN_PROC_BROWSER_TEST_F(ScriptInjectionTrackerBrowserTest,
                       ProgrammaticContentScript) {}

// Tests tracking of user scripts through the ScriptExecutor.
// The vast majority of implementation is the same for content script and user
// script tracking, so this is the main spot we explicitly test user script
// specific tracking.
IN_PROC_BROWSER_TEST_F(ScriptInjectionTrackerBrowserTest,
                       ProgrammaticUserScript) {}

// Tests what happens when the ExtensionMsg_ExecuteCode is sent *after* sending
// a Commit IPC to the renderer (i.e. after ReadyToCommit) but *before* a
// corresponding DidCommit IPC has been received by the browser process.  See
// also the "DocumentUserData race w/ Commit IPC" section in the
// document here:
// https://docs.google.com/document/d/1MFprp2ss2r9RNamJ7Jxva1bvRZvec3rzGceDGoJ6vW0/edit#heading=h.n2ppjzx4jpzt
// TODO(crbug.com/40615943): Remove the test after RenderDocument is shipped.
IN_PROC_BROWSER_TEST_F(ScriptInjectionTrackerBrowserTest,
                       ProgrammaticInjectionRacingWithDidCommit) {}

// Tests tracking of content scripts injected/declared via `content_scripts`
// entry in the extension manifest.  See also:
// https://developer.chrome.com/docs/extensions/mv3/content_scripts/#static-declarative
IN_PROC_BROWSER_TEST_F(ScriptInjectionTrackerBrowserTest,
                       ContentScriptDeclarationInExtensionManifest) {}

// Ensure ScriptInjectionTracker correctly tracks script injections in frames
// which undergo non-network (i.e. no ReadyToCommitNavigation notification)
// navigations after an extension is loaded.  For more details about the
// particular race condition covered by this test please see
// https://docs.google.com/document/d/1Z0-C3Bstva_-NK_bKhcyj4f2kdWjXv8pscuHre7UlSk/edit?usp=sharing
IN_PROC_BROWSER_TEST_F(
    ScriptInjectionTrackerBrowserTest,
    AboutBlankNavigationAfterLoadingExtensionMidwayThroughTest) {}

// Covers detecting content script injection into a 'data:...' URL.
IN_PROC_BROWSER_TEST_F(
    ScriptInjectionTrackerBrowserTest,
    ContentScriptDeclarationInExtensionManifest_DataUrlIframe) {}

// Covers detecting content script injection into 'about:blank'.
IN_PROC_BROWSER_TEST_F(
    ScriptInjectionTrackerBrowserTest,
    ContentScriptDeclarationInExtensionManifest_AboutBlankPopup) {}

// Covers detecting content script injection into an initial empty document.
//
// The code below exercises the test steps from "scenario #3" from the "Tracking
// injections in an initial empty document" section of a @chromium.org document
// here:
// https://docs.google.com/document/d/1MFprp2ss2r9RNamJ7Jxva1bvRZvec3rzGceDGoJ6vW0/edit?usp=sharing
IN_PROC_BROWSER_TEST_F(
    ScriptInjectionTrackerBrowserTest,
    ContentScriptDeclarationInExtensionManifest_SubframeWithInitialEmptyDoc) {}

// This is a regression test for https://crbug.com/1312125 - it simulates a race
// where an extension is loaded during or before a navigation, resulting in
// ScriptInjectionTracker::DidUpdateContentScriptsInRenderer getting called
// between ReadyToCommit and DidCommit of a navigation from a page where content
// scripts are not injected, to a page where content scripts are injected.
IN_PROC_BROWSER_TEST_F(
    ScriptInjectionTrackerBrowserTest,
    ContentScriptDeclarationInExtensionManifest_ScriptLoadRacesWithDidCommit) {}

// Tests tracking of content scripts injected/declared via
// `chrome.declarativeContent` API. See also:
// https://developer.chrome.com/docs/extensions/reference/declarativeContent/#type-RequestContentScript
IN_PROC_BROWSER_TEST_F(ScriptInjectionTrackerBrowserTest,
                       ContentScriptViaDeclarativeContentApi) {}

IN_PROC_BROWSER_TEST_F(ScriptInjectionTrackerBrowserTest, HistoryPushState) {}

class DynamicScriptsTrackerBrowserTest
    : public ScriptInjectionTrackerBrowserTest {};

// Tests tracking of content scripts dynamically injected/declared via
// `chrome.scripting` API.
IN_PROC_BROWSER_TEST_F(DynamicScriptsTrackerBrowserTest,
                       ContentScriptViaScriptingApi) {}

// Tests tracking of content scripts dynamically injected/declared via
// `chrome.scripting` API only when extension requests host permissions.
IN_PROC_BROWSER_TEST_F(DynamicScriptsTrackerBrowserTest,
                       ContentScriptViaScriptingApi_HostPermissions) {}

// Regression test for https://crbug.com/1439642.
IN_PROC_BROWSER_TEST_F(DynamicScriptsTrackerBrowserTest,
                       ContentScriptViaScriptingApiWhileIdle) {}

// Tests that ScriptInjectionTracker monitors extension permission changes and
// updates the renderer data accordingly.
IN_PROC_BROWSER_TEST_F(DynamicScriptsTrackerBrowserTest,
                       UpdateHostPermissions) {}

// Tests that ScriptInjectionTracker monitors extension permission changes
// between commit and load, and updates the renderer data accordingly.
// TODO(crbug.com/41495179): Flaky test.
#if BUILDFLAG(IS_LINUX)
#define MAYBE_UpdateHostPermissions_RaceCondition
#else
#define MAYBE_UpdateHostPermissions_RaceCondition
#endif
IN_PROC_BROWSER_TEST_F(DynamicScriptsTrackerBrowserTest,
                       MAYBE_UpdateHostPermissions_RaceCondition) {}

// Tests that ScriptInjectionTracker updates the renderer data when activeTab is
// granted.
IN_PROC_BROWSER_TEST_F(DynamicScriptsTrackerBrowserTest, ActiveTabGranted) {}

class UserScriptTrackerBrowserTest : public ScriptInjectionTrackerBrowserTest {};

// Tests tracking of user scripts dynamically injected/declared via
// `chrome.userScripts` API.
IN_PROC_BROWSER_TEST_F(UserScriptTrackerBrowserTest,
                       UserScriptViaUserScriptsApi) {}

// Tests tracking of user scripts dynamically injected/declared via
// `chrome.userScripts` API only when extension requests host permissions.
IN_PROC_BROWSER_TEST_F(UserScriptTrackerBrowserTest,
                       UserScriptViaUserScriptsApi_HostPermissions) {}

class ScriptInjectionTrackerAppBrowserTest : public PlatformAppBrowserTest {};

// Tests that ScriptInjectionTracker detects content scripts injected via
// <webview> (aka GuestView) APIs. This test covers a basic injection scenario.
IN_PROC_BROWSER_TEST_F(ScriptInjectionTrackerAppBrowserTest,
                       WebViewContentScript) {}

// Tests that ScriptInjectionTracker detects content scripts injected via
// <webview> (aka GuestView) APIs.  This test covers a scenario where the
// `addContentScripts` API is called in the middle of the test - after
// a matching guest content has already loaded (no content scripts there)
// but before a matching about:blank guest navigation happens (need to detect
// content scripts there).
IN_PROC_BROWSER_TEST_F(ScriptInjectionTrackerAppBrowserTest,
                       WebViewContentScriptForLateAboutBlank) {}

}  // namespace extensions