Extensions: Only load incognito-enabled extensions in an incognito renderer.
Currently, extensions which are disabled in incognito mode are also loaded in an
incognito renderer. This is redundant and can cause non-required bindings to be
generated in the renderer. Also, it has potential security implications.
Prevent this by only loading incognito-enabled extensions in an incognito
renderer. However extensions which can't be enabled in the incognito mode (e.g.
platform apps etc.) still need to be loaded in incognito renderers, so that
incognito tabs can connect with them (issue 305394).
Also, add tests for the same.
BUG=527548
Review-Url: https://codereview.chromium.org/2766263003
Cr-Commit-Position: refs/heads/master@{#462707}
diff --git a/extensions/browser/renderer_startup_helper_unittest.cc b/extensions/browser/renderer_startup_helper_unittest.cc
index 613ed84..2e92234 100644
--- a/extensions/browser/renderer_startup_helper_unittest.cc
+++ b/extensions/browser/renderer_startup_helper_unittest.cc
@@ -6,12 +6,16 @@
#include "base/memory/ptr_util.h"
#include "base/stl_util.h"
+#include "components/crx_file/id_util.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.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"
@@ -29,11 +33,14 @@
ExtensionRegistryFactory::GetForBrowserContext(browser_context());
render_process_host_ =
base::MakeUnique<content::MockRenderProcessHost>(browser_context());
+ incognito_render_process_host_ =
+ base::MakeUnique<content::MockRenderProcessHost>(incognito_context());
extension_ = CreateExtension("ext_1");
}
void TearDown() override {
render_process_host_.reset();
+ incognito_render_process_host_.reset();
helper_.reset();
ExtensionsTest::TearDown();
}
@@ -53,7 +60,7 @@
content::NotificationService::NoDetails());
}
- scoped_refptr<Extension> CreateExtension(const std::string& extension_id) {
+ scoped_refptr<Extension> CreateExtension(const std::string& id_input) {
std::unique_ptr<base::DictionaryValue> manifest =
DictionaryBuilder()
.Set("name", "extension")
@@ -61,13 +68,10 @@
.Set("manifest_version", 2)
.Set("version", "0.1")
.Build();
- return ExtensionBuilder()
- .SetManifest(std::move(manifest))
- .SetID(extension_id)
- .Build();
+ return CreateExtension(id_input, std::move(manifest));
}
- scoped_refptr<Extension> CreateTheme(const std::string& extension_id) {
+ scoped_refptr<Extension> CreateTheme(const std::string& id_input) {
std::unique_ptr<base::DictionaryValue> manifest =
DictionaryBuilder()
.Set("name", "theme")
@@ -76,10 +80,25 @@
.Set("manifest_version", 2)
.Set("version", "0.1")
.Build();
- return ExtensionBuilder()
- .SetManifest(std::move(manifest))
- .SetID(extension_id)
- .Build();
+ return CreateExtension(id_input, std::move(manifest));
+ }
+
+ scoped_refptr<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<Extension> extension) {
@@ -115,9 +134,20 @@
std::unique_ptr<RendererStartupHelper> helper_;
ExtensionRegistry* registry_; // Weak.
std::unique_ptr<content::MockRenderProcessHost> render_process_host_;
+ std::unique_ptr<content::MockRenderProcessHost>
+ incognito_render_process_host_;
scoped_refptr<Extension> extension_;
private:
+ scoped_refptr<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();
+ }
+
DISALLOW_COPY_AND_ASSIGN(RendererStartupHelperTest);
};
@@ -243,4 +273,94 @@
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()));
+
+ IPC::TestSink& sink = render_process_host_->sink();
+ IPC::TestSink& incognito_sink = incognito_render_process_host_->sink();
+
+ // Enable the extension. It should not be loaded in the initialized incognito
+ // renderer.
+ sink.ClearMessages();
+ incognito_sink.ClearMessages();
+ EXPECT_FALSE(util::IsIncognitoEnabled(extension_->id(), browser_context()));
+ EXPECT_FALSE(IsExtensionLoaded(*extension_));
+ AddExtensionToRegistry(extension_);
+ helper_->OnExtensionLoaded(*extension_);
+ EXPECT_EQ(0u, sink.message_count());
+ EXPECT_EQ(0u, incognito_sink.message_count());
+ 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.
+ sink.ClearMessages();
+ incognito_sink.ClearMessages();
+ 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.
+ EXPECT_LE(1u, sink.message_count());
+ EXPECT_EQ(0u, incognito_sink.message_count());
+
+ // Enable the extension in incognito mode. This will reload the extension.
+ sink.ClearMessages();
+ incognito_sink.ClearMessages();
+ 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.
+ ASSERT_EQ(1u, incognito_sink.message_count());
+ EXPECT_EQ(ExtensionMsg_Loaded::ID, incognito_sink.GetMessageAt(0)->type());
+ // The extension would be first unloaded and then loaded from the normal
+ // renderer.
+ ASSERT_EQ(2u, sink.message_count());
+ EXPECT_EQ(ExtensionMsg_Unloaded::ID, sink.GetMessageAt(0)->type());
+ EXPECT_EQ(ExtensionMsg_Loaded::ID, sink.GetMessageAt(1)->type());
+}
+
+// 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());
+ EXPECT_TRUE(IsProcessInitialized(incognito_render_process_host_.get()));
+
+ IPC::TestSink& incognito_sink = incognito_render_process_host_->sink();
+
+ scoped_refptr<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.
+ incognito_sink.ClearMessages();
+ AddExtensionToRegistry(platform_app);
+ helper_->OnExtensionLoaded(*platform_app);
+ EXPECT_TRUE(IsExtensionLoadedInProcess(*platform_app,
+ incognito_render_process_host_.get()));
+ ASSERT_EQ(1u, incognito_sink.message_count());
+ EXPECT_EQ(ExtensionMsg_Loaded::ID, incognito_sink.GetMessageAt(0)->type());
+}
+
} // namespace extensions