chromium/chrome/browser/extensions/lazy_background_page_apitest.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stddef.h>

#include <memory>

#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/scoped_observation.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "chrome/browser/bookmarks/bookmark_model_factory.h"
#include "chrome/browser/browser_features.h"
#include "chrome/browser/chrome_browser_main_extra_parts_nacl_deprecation.h"
#include "chrome/browser/devtools/chrome_devtools_manager_delegate.h"
#include "chrome/browser/devtools/devtools_window_testing.h"
#include "chrome/browser/extensions/api/developer_private/developer_private_api.h"
#include "chrome/browser/extensions/extension_action_test_util.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/extensions/extension_action_test_helper.h"
#include "chrome/browser/ui/location_bar/location_bar.h"
#include "chrome/browser/ui/tabs/tab_enums.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/api/tabs.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/browser/bookmark_utils.h"
#include "components/bookmarks/test/bookmark_test_helpers.h"
#include "components/javascript_dialogs/app_modal_dialog_controller.h"
#include "components/nacl/common/buildflags.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.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/test_utils.h"
#include "extensions/browser/api_test_utils.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_action_manager.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/extension_host_test_helper.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_registry_observer.h"
#include "extensions/browser/process_manager.h"
#include "extensions/common/extension.h"
#include "extensions/common/manifest_handlers/background_info.h"
#include "extensions/common/mojom/view_type.mojom.h"
#include "extensions/common/switches.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/result_catcher.h"
#include "net/dns/mock_host_resolver.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"

BookmarkModel;
BookmarkNode;

namespace extensions {

namespace {

// This unfortunate bit of silliness is necessary when loading an extension in
// incognito. The goal is to load the extension, enable incognito, then wait
// for both background pages to load and close. The problem is that enabling
// incognito involves reloading the extension - and the background pages may
// have already loaded once before then. So we wait until the extension is
// unloaded before listening to the background page notifications.
class LoadedIncognitoObserver : public ExtensionRegistryObserver {};

}  // namespace

class LazyBackgroundPageApiTest : public ExtensionApiTest {};

IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, BrowserActionCreateTab) {}

IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest,
                       BrowserActionCreateTabAfterCallback) {}

IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, BroadcastEvent) {}

IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, Filters) {}

// Tests that the lazy background page receives the onInstalled event and shuts
// down.
IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, OnInstalled) {}

// Tests that a JavaScript alert keeps the lazy background page alive.
IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, WaitForDialog) {}

// Tests that DevToolsWindowCreationObserver observes creation of the lazy
// background page.
IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest,
                       DevToolsWindowCreationObserver) {}

// Tests that the lazy background page stays alive until all visible views are
// closed.
IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, WaitForView) {}

// Flaky. https://crbug.com/1006634
// Tests that the lazy background page stays alive until all network requests
// are complete.
IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, DISABLED_WaitForRequest) {}

// Tests that the lazy background page stays alive while a NaCl module exists in
// its DOM.
#if BUILDFLAG(ENABLE_NACL)

IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, NaClInBackgroundPage) {
  {
    base::FilePath extdir;
    base::ScopedAllowBlockingForTesting allow_blocking;
    ASSERT_TRUE(base::PathService::Get(chrome::DIR_GEN_TEST_DATA, &extdir));
    extdir = extdir.AppendASCII("ppapi/tests/extensions/load_unload/newlib");
    ExtensionHostTestHelper host_helper(profile());
    host_helper.RestrictToType(mojom::ViewType::kExtensionBackgroundPage);
    ASSERT_TRUE(LoadExtension(extdir));
    // Wait for the background page to cycle.
    host_helper.WaitForDocumentElementAvailable();
    host_helper.WaitForHostDestroyed();
  }

  // The NaCl module is loaded, and the Lazy Background Page stays alive.
  {
    ExtensionTestMessageListener nacl_module_loaded("nacl_module_loaded");
    ExtensionActionTestHelper::Create(browser())->Press(
        last_loaded_extension_id());
    EXPECT_TRUE(nacl_module_loaded.WaitUntilSatisfied());
    content::RunAllTasksUntilIdle();
    EXPECT_TRUE(IsBackgroundPageAlive(last_loaded_extension_id()));
  }

  // The NaCl module is detached from DOM, and the Lazy Background Page shuts
  // down.
  {
    ExtensionHostTestHelper host_helper(profile());
    host_helper.RestrictToType(mojom::ViewType::kExtensionBackgroundPage);
    ExtensionActionTestHelper::Create(browser())->Press(
        last_loaded_extension_id());
    host_helper.WaitForHostDestroyed();
  }

  // The Lazy Background Page has been shut down.
  EXPECT_FALSE(IsBackgroundPageAlive(last_loaded_extension_id()));
}

// Tests that the lazy background page shuts down when all visible views with
// NaCl modules are closed.
IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, NaClInView) {
  // The extension is loaded and should've opened a new tab to an extension
  // page, and the Lazy Background Page stays alive.
  {
    base::FilePath extdir;
    base::ScopedAllowBlockingForTesting allow_blocking;
    ASSERT_TRUE(base::PathService::Get(chrome::DIR_GEN_TEST_DATA, &extdir));
    extdir = extdir.AppendASCII("ppapi/tests/extensions/popup/newlib");
    ResultCatcher catcher;
    const Extension* extension = LoadExtension(extdir);
    ASSERT_TRUE(extension);
    EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
    EXPECT_EQ(extension->GetResourceURL("popup.html"),
              browser()
                  ->tab_strip_model()
                  ->GetActiveWebContents()
                  ->GetLastCommittedURL());
    EXPECT_TRUE(IsBackgroundPageAlive(last_loaded_extension_id()));
  }

  // Close the new tab.
  {
    ExtensionHostTestHelper host_helper(profile(), last_loaded_extension_id());
    host_helper.RestrictToType(mojom::ViewType::kExtensionBackgroundPage);
    browser()->tab_strip_model()->CloseWebContentsAt(
        browser()->tab_strip_model()->active_index(),
        TabCloseTypes::CLOSE_NONE);
    host_helper.WaitForHostDestroyed();
  }

  // The Lazy Background Page has been shut down.
  EXPECT_FALSE(IsBackgroundPageAlive(last_loaded_extension_id()));
}
#endif

// Tests that the lazy background page stays alive until all visible views are
// closed.
// http://crbug.com/175778; test fails frequently on OS X
#if BUILDFLAG(IS_MAC)
#define MAYBE_WaitForNTP
#else
#define MAYBE_WaitForNTP
#endif
IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, MAYBE_WaitForNTP) {}

// Tests that an incognito split mode extension gets 2 lazy background pages,
// and they each load and unload at the proper times.
// See crbug.com/248437
IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, DISABLED_IncognitoSplitMode) {}

enum class BackForwardCacheParam {};

class LazyBackgroundPageApiWithBFCacheParamTest
    : public LazyBackgroundPageApiTest,
      public testing::WithParamInterface<BackForwardCacheParam> {};

INSTANTIATE_TEST_SUITE_P();

// Tests that messages from the content script activate the lazy background
// page, and keep it alive until all channels are closed.
// http://crbug.com/1179524; test fails occasionally on OS X 10.15
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
#define MAYBE_Messaging
#else
#define MAYBE_Messaging
#endif
IN_PROC_BROWSER_TEST_P(LazyBackgroundPageApiWithBFCacheParamTest,
                       MAYBE_Messaging) {}

// Tests that the lazy background page receives the unload event when we
// close it, and that it can execute simple API calls that don't require an
// asynchronous response.
IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, OnUnload) {}

// Tests that both a regular page and an event page will receive events when
// the event page is not loaded.
IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, EventDispatchToTab) {}

// Tests that the lazy background page will be unloaded if the onSuspend event
// handler calls an API function such as chrome.storage.local.set().
// See: http://crbug.com/296834
IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, OnSuspendUseStorageApi) {}

// TODO: background page with timer.
// TODO: background page that interacts with popup.

// Ensure that the events page of an extension is properly torn down and the
// process does not linger around.
// See https://crbug.com/612668.
IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, EventProcessCleanup) {}

// Tests that lazy listeners persist when the event page is torn down, but
// the listeners associated with the process do not.
IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, EventListenerCleanup) {}

// Tests that an extension can fetch a file scheme URL from the lazy background
// page, if it has file access.
// TODO(crbug.com/40813949): Deflake test.
IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest,
                       DISABLED_FetchFileSchemeURLWithFileAccess) {}

// Tests that an extension can not fetch a file scheme URL from the lazy
// background page, if it does not have file access.
// Flaky on various builders: crbug.com/1284362.
IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest,
                       DISABLED_FetchFileSchemeURLWithNoFileAccess) {}

class PictureInPictureLazyBackgroundPageApiTest
    : public LazyBackgroundPageApiTest {};

// Tests that the lazy background page stays alive while a video is playing in
// Picture-in-Picture mode.
IN_PROC_BROWSER_TEST_F(PictureInPictureLazyBackgroundPageApiTest,
                       PictureInPictureInBackgroundPage) {}

}  // namespace extensions