123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 |
- // 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 "extensions/browser/renderer_startup_helper.h"
- #include "base/containers/contains.h"
- #include "base/memory/raw_ptr.h"
- #include "components/crx_file/id_util.h"
- #include "content/public/test/mock_render_process_host.h"
- #include "extensions/browser/extension_prefs.h"
- #include "extensions/browser/extension_registry.h"
- #include "extensions/browser/extension_registry_factory.h"
- #include "extensions/browser/extension_util.h"
- #include "extensions/browser/extensions_test.h"
- #include "extensions/browser/test_extensions_browser_client.h"
- #include "extensions/common/extension_builder.h"
- #include "extensions/common/extension_messages.h"
- #include "extensions/common/mojom/renderer.mojom.h"
- #include "extensions/common/permissions/permissions_data.h"
- #include "mojo/public/cpp/bindings/associated_receiver_set.h"
- namespace extensions {
- // Class that implements the binding of a new Renderer mojom interface and
- // can receive callbacks on it for testing validation.
- class RendererStartupHelperInterceptor : public RendererStartupHelper,
- public mojom::Renderer {
- public:
- explicit RendererStartupHelperInterceptor(
- content::BrowserContext* browser_context)
- : RendererStartupHelper(browser_context),
- browser_context_(browser_context) {}
- size_t num_activated_extensions() { return activated_extensions_.size(); }
- size_t num_loaded_extensions() { return num_loaded_extensions_; }
- size_t num_loaded_extensions_in_incognito() {
- return num_loaded_extensions_in_incognito_;
- }
- size_t num_unloaded_extensions() { return unloaded_extensions_.size(); }
- void clear_extensions() {
- activated_extensions_.clear();
- num_loaded_extensions_ = 0;
- num_loaded_extensions_in_incognito_ = 0;
- unloaded_extensions_.clear();
- }
- const URLPatternSet& default_policy_blocked_hosts() const {
- return default_blocked_hosts_;
- }
- const URLPatternSet& default_policy_allowed_hosts() const {
- return default_allowed_hosts_;
- }
- protected:
- mojo::PendingAssociatedRemote<mojom::Renderer> BindNewRendererRemote(
- content::RenderProcessHost* process) override {
- mojo::AssociatedRemote<mojom::Renderer> remote;
- receivers_.Add(this, remote.BindNewEndpointAndPassDedicatedReceiver());
- return remote.Unbind();
- }
- private:
- // mojom::Renderer implementation:
- void ActivateExtension(const std::string& extension_id) override {
- activated_extensions_.push_back(extension_id);
- }
- void SetActivityLoggingEnabled(bool enabled) override {}
- void LoadExtensions(
- std::vector<mojom::ExtensionLoadedParamsPtr> loaded_extensions) override {
- for (const auto& param : loaded_extensions) {
- const std::set<content::RenderProcessHost*>& process_set =
- extension_process_map_[param->id];
- for (content::RenderProcessHost* process : process_set) {
- // Count the invocation of the LoadExtensions method on the normal
- // renderer or the incognito renderer.
- if (process->GetBrowserContext() == browser_context_) {
- num_loaded_extensions_++;
- } else {
- // If RenderProcessHost's context isn't the same as |browser_context_|
- // , assume that the RenderProcessHost is the incognito renderer.
- num_loaded_extensions_in_incognito_++;
- }
- }
- }
- }
- void UnloadExtension(const std::string& extension_id) override {
- unloaded_extensions_.push_back(extension_id);
- }
- void SuspendExtension(
- const std::string& extension_id,
- mojom::Renderer::SuspendExtensionCallback callback) override {
- std::move(callback).Run();
- }
- void CancelSuspendExtension(const std::string& extension_id) override {}
- void SetDeveloperMode(bool current_developer_mode) override {}
- void SetSessionInfo(version_info::Channel channel,
- mojom::FeatureSessionType session,
- bool is_lock_screen_context) override {}
- void SetSystemFont(const std::string& font_family,
- const std::string& font_size) override {}
- void SetWebViewPartitionID(const std::string& partition_id) override {}
- void SetScriptingAllowlist(
- const std::vector<std::string>& extension_ids) override {}
- void ShouldSuspend(ShouldSuspendCallback callback) override {
- std::move(callback).Run();
- }
- void TransferBlobs(TransferBlobsCallback callback) override {
- std::move(callback).Run();
- }
- void UpdatePermissions(const std::string& extension_id,
- PermissionSet active_permissions,
- PermissionSet withheld_permissions,
- URLPatternSet policy_blocked_hosts,
- URLPatternSet policy_allowed_hosts,
- bool uses_default_policy_host_restrictions) override {}
- void UpdateDefaultPolicyHostRestrictions(
- URLPatternSet default_policy_blocked_hosts,
- URLPatternSet default_policy_allowed_hosts) override {
- default_blocked_hosts_.AddPatterns(default_policy_blocked_hosts);
- default_allowed_hosts_.AddPatterns(default_policy_allowed_hosts);
- }
- void UpdateUserHostRestrictions(URLPatternSet user_blocked_hosts,
- URLPatternSet user_allowed_hosts) override {}
- void UpdateTabSpecificPermissions(const std::string& extension_id,
- URLPatternSet new_hosts,
- int tab_id,
- bool update_origin_allowlist) override {}
- void UpdateUserScripts(base::ReadOnlySharedMemoryRegion shared_memory,
- mojom::HostIDPtr host_id) override {}
- void ClearTabSpecificPermissions(
- const std::vector<std::string>& extension_ids,
- int tab_id,
- bool update_origin_allowlist) override {}
- void WatchPages(const std::vector<std::string>& css_selectors) override {}
- URLPatternSet default_blocked_hosts_;
- URLPatternSet default_allowed_hosts_;
- std::vector<std::string> activated_extensions_;
- size_t num_loaded_extensions_;
- size_t num_loaded_extensions_in_incognito_;
- std::vector<std::string> unloaded_extensions_;
- raw_ptr<content::BrowserContext> browser_context_;
- mojo::AssociatedReceiverSet<mojom::Renderer> receivers_;
- };
- class RendererStartupHelperTest : public ExtensionsTest {
- public:
- RendererStartupHelperTest() {}
- RendererStartupHelperTest(const RendererStartupHelperTest&) = delete;
- RendererStartupHelperTest& operator=(const RendererStartupHelperTest&) =
- delete;
- ~RendererStartupHelperTest() override {}
- void SetUp() override {
- ExtensionsTest::SetUp();
- helper_ =
- std::make_unique<RendererStartupHelperInterceptor>(browser_context());
- registry_ =
- ExtensionRegistryFactory::GetForBrowserContext(browser_context());
- render_process_host_ =
- std::make_unique<content::MockRenderProcessHost>(browser_context());
- incognito_render_process_host_ =
- std::make_unique<content::MockRenderProcessHost>(incognito_context());
- extension_ = CreateExtension("ext_1");
- }
- void TearDown() override {
- render_process_host_.reset();
- incognito_render_process_host_.reset();
- helper_.reset();
- ExtensionsTest::TearDown();
- }
- protected:
- void SimulateRenderProcessCreated(content::RenderProcessHost* rph) {
- helper_->OnRenderProcessHostCreated(rph);
- }
- void SimulateRenderProcessTerminated(content::RenderProcessHost* rph) {
- helper_->RenderProcessHostDestroyed(rph);
- }
- scoped_refptr<const Extension> CreateExtension(const std::string& id_input) {
- std::unique_ptr<base::DictionaryValue> manifest =
- DictionaryBuilder()
- .Set("name", "extension")
- .Set("description", "an extension")
- .Set("manifest_version", 2)
- .Set("version", "0.1")
- .Build();
- return CreateExtension(id_input, std::move(manifest));
- }
- scoped_refptr<const Extension> CreateTheme(const std::string& id_input) {
- std::unique_ptr<base::DictionaryValue> manifest =
- DictionaryBuilder()
- .Set("name", "theme")
- .Set("description", "a theme")
- .Set("theme", DictionaryBuilder().Build())
- .Set("manifest_version", 2)
- .Set("version", "0.1")
- .Build();
- return CreateExtension(id_input, std::move(manifest));
- }
- scoped_refptr<const Extension> CreatePlatformApp(
- const std::string& id_input) {
- std::unique_ptr<base::Value> background =
- DictionaryBuilder()
- .Set("scripts", ListBuilder().Append("background.js").Build())
- .Build();
- std::unique_ptr<base::DictionaryValue> manifest =
- DictionaryBuilder()
- .Set("name", "platform_app")
- .Set("description", "a platform app")
- .Set("app", DictionaryBuilder()
- .Set("background", std::move(background))
- .Build())
- .Set("manifest_version", 2)
- .Set("version", "0.1")
- .Build();
- return CreateExtension(id_input, std::move(manifest));
- }
- void AddExtensionToRegistry(scoped_refptr<const Extension> extension) {
- registry_->AddEnabled(extension);
- }
- void RemoveExtensionFromRegistry(scoped_refptr<const Extension> extension) {
- registry_->RemoveEnabled(extension->id());
- }
- bool IsProcessInitialized(content::RenderProcessHost* rph) {
- return base::Contains(helper_->process_mojo_map_, rph);
- }
- bool IsExtensionLoaded(const Extension& extension) {
- return base::Contains(helper_->extension_process_map_, extension.id());
- }
- bool IsExtensionLoadedInProcess(const Extension& extension,
- content::RenderProcessHost* rph) {
- return IsExtensionLoaded(extension) &&
- base::Contains(helper_->extension_process_map_[extension.id()], rph);
- }
- bool IsExtensionPendingActivationInProcess(const Extension& extension,
- content::RenderProcessHost* rph) {
- return base::Contains(helper_->pending_active_extensions_, rph) &&
- base::Contains(helper_->pending_active_extensions_[rph],
- extension.id());
- }
- std::unique_ptr<RendererStartupHelperInterceptor> helper_;
- raw_ptr<ExtensionRegistry> registry_; // Weak.
- std::unique_ptr<content::MockRenderProcessHost> render_process_host_;
- std::unique_ptr<content::MockRenderProcessHost>
- incognito_render_process_host_;
- scoped_refptr<const Extension> extension_;
- private:
- scoped_refptr<const Extension> CreateExtension(
- const std::string& id_input,
- std::unique_ptr<base::DictionaryValue> manifest) {
- return ExtensionBuilder()
- .SetManifest(std::move(manifest))
- .SetID(crx_file::id_util::GenerateId(id_input))
- .Build();
- }
- };
- // Tests extension loading, unloading and activation and render process creation
- // and termination.
- TEST_F(RendererStartupHelperTest, NormalExtensionLifecycle) {
- // Initialize render process.
- EXPECT_FALSE(IsProcessInitialized(render_process_host_.get()));
- SimulateRenderProcessCreated(render_process_host_.get());
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(IsProcessInitialized(render_process_host_.get()));
- // Enable extension.
- helper_->clear_extensions();
- EXPECT_FALSE(IsExtensionLoaded(*extension_));
- AddExtensionToRegistry(extension_);
- helper_->OnExtensionLoaded(*extension_);
- EXPECT_TRUE(
- IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
- EXPECT_FALSE(IsExtensionPendingActivationInProcess(
- *extension_, render_process_host_.get()));
- base::RunLoop().RunUntilIdle();
- ASSERT_EQ(1u, helper_->num_loaded_extensions());
- // Activate extension.
- helper_->ActivateExtensionInProcess(*extension_, render_process_host_.get());
- EXPECT_FALSE(IsExtensionPendingActivationInProcess(
- *extension_, render_process_host_.get()));
- base::RunLoop().RunUntilIdle();
- ASSERT_EQ(1u, helper_->num_activated_extensions());
- // Disable extension.
- helper_->clear_extensions();
- RemoveExtensionFromRegistry(extension_);
- helper_->OnExtensionUnloaded(*extension_);
- EXPECT_FALSE(IsExtensionLoaded(*extension_));
- base::RunLoop().RunUntilIdle();
- ASSERT_EQ(1u, helper_->num_unloaded_extensions());
- // Extension enabled again.
- helper_->clear_extensions();
- AddExtensionToRegistry(extension_);
- helper_->OnExtensionLoaded(*extension_);
- EXPECT_TRUE(
- IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
- EXPECT_FALSE(IsExtensionPendingActivationInProcess(
- *extension_, render_process_host_.get()));
- base::RunLoop().RunUntilIdle();
- ASSERT_EQ(1u, helper_->num_loaded_extensions());
- // Render Process terminated.
- SimulateRenderProcessTerminated(render_process_host_.get());
- EXPECT_FALSE(IsProcessInitialized(render_process_host_.get()));
- EXPECT_TRUE(IsExtensionLoaded(*extension_));
- EXPECT_FALSE(
- IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
- EXPECT_FALSE(IsExtensionPendingActivationInProcess(
- *extension_, render_process_host_.get()));
- }
- // Tests that activating an extension in an uninitialized render process works
- // fine.
- TEST_F(RendererStartupHelperTest, EnabledBeforeProcessInitialized) {
- EXPECT_FALSE(IsProcessInitialized(render_process_host_.get()));
- // Enable extension. The render process isn't initialized yet, so the
- // extension should be added to the list of extensions awaiting activation.
- helper_->clear_extensions();
- AddExtensionToRegistry(extension_);
- helper_->OnExtensionLoaded(*extension_);
- helper_->ActivateExtensionInProcess(*extension_, render_process_host_.get());
- ASSERT_EQ(0u, helper_->num_loaded_extensions());
- EXPECT_TRUE(IsExtensionLoaded(*extension_));
- EXPECT_FALSE(
- IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
- EXPECT_TRUE(IsExtensionPendingActivationInProcess(
- *extension_, render_process_host_.get()));
- // Initialize PermissionsData default policy hosts restrictions.
- // During the process initialization, UpdateDefaultPolicyHostRestrictions
- // will be called with the default policy values returned by PermissionsData.
- URLPatternSet default_blocked_hosts;
- URLPatternSet default_allowed_hosts;
- default_blocked_hosts.AddPattern(
- URLPattern(URLPattern::SCHEME_ALL, "*://*.example.com/*"));
- default_allowed_hosts.AddPattern(
- URLPattern(URLPattern::SCHEME_ALL, "*://test.example2.com/*"));
- PermissionsData::SetDefaultPolicyHostRestrictions(
- util::GetBrowserContextId(browser_context()), default_blocked_hosts,
- default_allowed_hosts);
- // Initialize the render process.
- SimulateRenderProcessCreated(render_process_host_.get());
- // The renderer would have been sent multiple initialization messages
- // including the loading and activation messages for the extension.
- base::RunLoop().RunUntilIdle();
- ASSERT_EQ(1u, helper_->num_loaded_extensions());
- // Method UpdateDefaultPolicyHostRestrictions() from mojom::Renderer should
- // have been called with the default policy for blocked/allowed hosts given by
- // PermissionsData, which was initialized above.
- base::RunLoop().RunUntilIdle();
- EXPECT_EQ(default_blocked_hosts, helper_->default_policy_blocked_hosts());
- EXPECT_EQ(default_allowed_hosts, helper_->default_policy_allowed_hosts());
- EXPECT_TRUE(IsProcessInitialized(render_process_host_.get()));
- EXPECT_TRUE(
- IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
- EXPECT_FALSE(IsExtensionPendingActivationInProcess(
- *extension_, render_process_host_.get()));
- }
- // Tests that themes aren't loaded in a renderer process.
- TEST_F(RendererStartupHelperTest, LoadTheme) {
- // Initialize render process.
- EXPECT_FALSE(IsProcessInitialized(render_process_host_.get()));
- SimulateRenderProcessCreated(render_process_host_.get());
- EXPECT_TRUE(IsProcessInitialized(render_process_host_.get()));
- scoped_refptr<const Extension> extension(CreateTheme("theme"));
- ASSERT_TRUE(extension->is_theme());
- IPC::TestSink& sink = render_process_host_->sink();
- // Enable the theme. Verify it isn't loaded and activated in the renderer.
- sink.ClearMessages();
- EXPECT_FALSE(IsExtensionLoaded(*extension));
- AddExtensionToRegistry(extension_);
- helper_->OnExtensionLoaded(*extension);
- EXPECT_EQ(0u, sink.message_count());
- EXPECT_TRUE(IsExtensionLoaded(*extension));
- EXPECT_FALSE(
- IsExtensionLoadedInProcess(*extension, render_process_host_.get()));
- helper_->ActivateExtensionInProcess(*extension, render_process_host_.get());
- EXPECT_EQ(0u, sink.message_count());
- EXPECT_FALSE(IsExtensionPendingActivationInProcess(
- *extension, render_process_host_.get()));
- helper_->OnExtensionUnloaded(*extension);
- EXPECT_EQ(0u, sink.message_count());
- EXPECT_FALSE(IsExtensionLoaded(*extension));
- }
- // Tests that only incognito-enabled extensions are loaded in an incognito
- // context.
- TEST_F(RendererStartupHelperTest, ExtensionInIncognitoRenderer) {
- // Initialize the incognito renderer.
- EXPECT_FALSE(IsProcessInitialized(incognito_render_process_host_.get()));
- SimulateRenderProcessCreated(incognito_render_process_host_.get());
- EXPECT_TRUE(IsProcessInitialized(incognito_render_process_host_.get()));
- // Enable the extension. It should not be loaded in the initialized incognito
- // renderer.
- helper_->clear_extensions();
- EXPECT_FALSE(util::IsIncognitoEnabled(extension_->id(), browser_context()));
- EXPECT_FALSE(IsExtensionLoaded(*extension_));
- AddExtensionToRegistry(extension_);
- helper_->OnExtensionLoaded(*extension_);
- EXPECT_EQ(0u, helper_->num_loaded_extensions());
- EXPECT_EQ(0u, helper_->num_loaded_extensions_in_incognito());
- EXPECT_TRUE(IsExtensionLoaded(*extension_));
- EXPECT_FALSE(IsExtensionLoadedInProcess(
- *extension_, incognito_render_process_host_.get()));
- EXPECT_FALSE(
- IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
- // Initialize the normal renderer. The extension should get loaded in it.
- helper_->clear_extensions();
- EXPECT_FALSE(IsProcessInitialized(render_process_host_.get()));
- SimulateRenderProcessCreated(render_process_host_.get());
- EXPECT_TRUE(IsProcessInitialized(render_process_host_.get()));
- EXPECT_TRUE(
- IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
- EXPECT_FALSE(IsExtensionLoadedInProcess(
- *extension_, incognito_render_process_host_.get()));
- // Multiple initialization messages including the extension load message
- // should be dispatched to the non-incognito renderer.
- base::RunLoop().RunUntilIdle();
- ASSERT_EQ(1u, helper_->num_loaded_extensions());
- ASSERT_EQ(0u, helper_->num_loaded_extensions_in_incognito());
- // Enable the extension in incognito mode. This will reload the extension.
- helper_->clear_extensions();
- ExtensionPrefs::Get(browser_context())
- ->SetIsIncognitoEnabled(extension_->id(), true);
- helper_->OnExtensionUnloaded(*extension_);
- helper_->OnExtensionLoaded(*extension_);
- EXPECT_TRUE(IsExtensionLoadedInProcess(*extension_,
- incognito_render_process_host_.get()));
- EXPECT_TRUE(
- IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
- // The extension would not have been unloaded from the incognito renderer
- // since it wasn't loaded.
- base::RunLoop().RunUntilIdle();
- // LoadExtensions are called twice because it's also called by
- // RendererStartupHelper::InitializeProcess as soon as a RenderProcessHost
- // instance is created.
- ASSERT_EQ(2u, helper_->num_loaded_extensions_in_incognito());
- // The extension would be first unloaded and then loaded from the normal
- // renderer.
- base::RunLoop().RunUntilIdle();
- ASSERT_EQ(1u, helper_->num_unloaded_extensions());
- ASSERT_EQ(2u, helper_->num_loaded_extensions());
- }
- // Tests that platform apps are always loaded in an incognito renderer.
- TEST_F(RendererStartupHelperTest, PlatformAppInIncognitoRenderer) {
- // Initialize the incognito renderer.
- EXPECT_FALSE(IsProcessInitialized(incognito_render_process_host_.get()));
- SimulateRenderProcessCreated(incognito_render_process_host_.get());
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(IsProcessInitialized(incognito_render_process_host_.get()));
- scoped_refptr<const Extension> platform_app(
- CreatePlatformApp("platform_app"));
- ASSERT_TRUE(platform_app->is_platform_app());
- EXPECT_FALSE(util::IsIncognitoEnabled(platform_app->id(), browser_context()));
- EXPECT_FALSE(util::CanBeIncognitoEnabled(platform_app.get()));
- // Enable the app. It should get loaded in the incognito renderer even though
- // IsIncognitoEnabled returns false for it, since it can't be enabled for
- // incognito.
- helper_->clear_extensions();
- AddExtensionToRegistry(platform_app);
- helper_->OnExtensionLoaded(*platform_app);
- EXPECT_TRUE(IsExtensionLoadedInProcess(*platform_app,
- incognito_render_process_host_.get()));
- base::RunLoop().RunUntilIdle();
- ASSERT_EQ(1u, helper_->num_loaded_extensions_in_incognito());
- }
- } // namespace extensions
|