renderer_startup_helper_unittest.cc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "extensions/browser/renderer_startup_helper.h"
  5. #include "base/containers/contains.h"
  6. #include "base/memory/raw_ptr.h"
  7. #include "components/crx_file/id_util.h"
  8. #include "content/public/test/mock_render_process_host.h"
  9. #include "extensions/browser/extension_prefs.h"
  10. #include "extensions/browser/extension_registry.h"
  11. #include "extensions/browser/extension_registry_factory.h"
  12. #include "extensions/browser/extension_util.h"
  13. #include "extensions/browser/extensions_test.h"
  14. #include "extensions/browser/test_extensions_browser_client.h"
  15. #include "extensions/common/extension_builder.h"
  16. #include "extensions/common/extension_messages.h"
  17. #include "extensions/common/mojom/renderer.mojom.h"
  18. #include "extensions/common/permissions/permissions_data.h"
  19. #include "mojo/public/cpp/bindings/associated_receiver_set.h"
  20. namespace extensions {
  21. // Class that implements the binding of a new Renderer mojom interface and
  22. // can receive callbacks on it for testing validation.
  23. class RendererStartupHelperInterceptor : public RendererStartupHelper,
  24. public mojom::Renderer {
  25. public:
  26. explicit RendererStartupHelperInterceptor(
  27. content::BrowserContext* browser_context)
  28. : RendererStartupHelper(browser_context),
  29. browser_context_(browser_context) {}
  30. size_t num_activated_extensions() { return activated_extensions_.size(); }
  31. size_t num_loaded_extensions() { return num_loaded_extensions_; }
  32. size_t num_loaded_extensions_in_incognito() {
  33. return num_loaded_extensions_in_incognito_;
  34. }
  35. size_t num_unloaded_extensions() { return unloaded_extensions_.size(); }
  36. void clear_extensions() {
  37. activated_extensions_.clear();
  38. num_loaded_extensions_ = 0;
  39. num_loaded_extensions_in_incognito_ = 0;
  40. unloaded_extensions_.clear();
  41. }
  42. const URLPatternSet& default_policy_blocked_hosts() const {
  43. return default_blocked_hosts_;
  44. }
  45. const URLPatternSet& default_policy_allowed_hosts() const {
  46. return default_allowed_hosts_;
  47. }
  48. protected:
  49. mojo::PendingAssociatedRemote<mojom::Renderer> BindNewRendererRemote(
  50. content::RenderProcessHost* process) override {
  51. mojo::AssociatedRemote<mojom::Renderer> remote;
  52. receivers_.Add(this, remote.BindNewEndpointAndPassDedicatedReceiver());
  53. return remote.Unbind();
  54. }
  55. private:
  56. // mojom::Renderer implementation:
  57. void ActivateExtension(const std::string& extension_id) override {
  58. activated_extensions_.push_back(extension_id);
  59. }
  60. void SetActivityLoggingEnabled(bool enabled) override {}
  61. void LoadExtensions(
  62. std::vector<mojom::ExtensionLoadedParamsPtr> loaded_extensions) override {
  63. for (const auto& param : loaded_extensions) {
  64. const std::set<content::RenderProcessHost*>& process_set =
  65. extension_process_map_[param->id];
  66. for (content::RenderProcessHost* process : process_set) {
  67. // Count the invocation of the LoadExtensions method on the normal
  68. // renderer or the incognito renderer.
  69. if (process->GetBrowserContext() == browser_context_) {
  70. num_loaded_extensions_++;
  71. } else {
  72. // If RenderProcessHost's context isn't the same as |browser_context_|
  73. // , assume that the RenderProcessHost is the incognito renderer.
  74. num_loaded_extensions_in_incognito_++;
  75. }
  76. }
  77. }
  78. }
  79. void UnloadExtension(const std::string& extension_id) override {
  80. unloaded_extensions_.push_back(extension_id);
  81. }
  82. void SuspendExtension(
  83. const std::string& extension_id,
  84. mojom::Renderer::SuspendExtensionCallback callback) override {
  85. std::move(callback).Run();
  86. }
  87. void CancelSuspendExtension(const std::string& extension_id) override {}
  88. void SetDeveloperMode(bool current_developer_mode) override {}
  89. void SetSessionInfo(version_info::Channel channel,
  90. mojom::FeatureSessionType session,
  91. bool is_lock_screen_context) override {}
  92. void SetSystemFont(const std::string& font_family,
  93. const std::string& font_size) override {}
  94. void SetWebViewPartitionID(const std::string& partition_id) override {}
  95. void SetScriptingAllowlist(
  96. const std::vector<std::string>& extension_ids) override {}
  97. void ShouldSuspend(ShouldSuspendCallback callback) override {
  98. std::move(callback).Run();
  99. }
  100. void TransferBlobs(TransferBlobsCallback callback) override {
  101. std::move(callback).Run();
  102. }
  103. void UpdatePermissions(const std::string& extension_id,
  104. PermissionSet active_permissions,
  105. PermissionSet withheld_permissions,
  106. URLPatternSet policy_blocked_hosts,
  107. URLPatternSet policy_allowed_hosts,
  108. bool uses_default_policy_host_restrictions) override {}
  109. void UpdateDefaultPolicyHostRestrictions(
  110. URLPatternSet default_policy_blocked_hosts,
  111. URLPatternSet default_policy_allowed_hosts) override {
  112. default_blocked_hosts_.AddPatterns(default_policy_blocked_hosts);
  113. default_allowed_hosts_.AddPatterns(default_policy_allowed_hosts);
  114. }
  115. void UpdateUserHostRestrictions(URLPatternSet user_blocked_hosts,
  116. URLPatternSet user_allowed_hosts) override {}
  117. void UpdateTabSpecificPermissions(const std::string& extension_id,
  118. URLPatternSet new_hosts,
  119. int tab_id,
  120. bool update_origin_allowlist) override {}
  121. void UpdateUserScripts(base::ReadOnlySharedMemoryRegion shared_memory,
  122. mojom::HostIDPtr host_id) override {}
  123. void ClearTabSpecificPermissions(
  124. const std::vector<std::string>& extension_ids,
  125. int tab_id,
  126. bool update_origin_allowlist) override {}
  127. void WatchPages(const std::vector<std::string>& css_selectors) override {}
  128. URLPatternSet default_blocked_hosts_;
  129. URLPatternSet default_allowed_hosts_;
  130. std::vector<std::string> activated_extensions_;
  131. size_t num_loaded_extensions_;
  132. size_t num_loaded_extensions_in_incognito_;
  133. std::vector<std::string> unloaded_extensions_;
  134. raw_ptr<content::BrowserContext> browser_context_;
  135. mojo::AssociatedReceiverSet<mojom::Renderer> receivers_;
  136. };
  137. class RendererStartupHelperTest : public ExtensionsTest {
  138. public:
  139. RendererStartupHelperTest() {}
  140. RendererStartupHelperTest(const RendererStartupHelperTest&) = delete;
  141. RendererStartupHelperTest& operator=(const RendererStartupHelperTest&) =
  142. delete;
  143. ~RendererStartupHelperTest() override {}
  144. void SetUp() override {
  145. ExtensionsTest::SetUp();
  146. helper_ =
  147. std::make_unique<RendererStartupHelperInterceptor>(browser_context());
  148. registry_ =
  149. ExtensionRegistryFactory::GetForBrowserContext(browser_context());
  150. render_process_host_ =
  151. std::make_unique<content::MockRenderProcessHost>(browser_context());
  152. incognito_render_process_host_ =
  153. std::make_unique<content::MockRenderProcessHost>(incognito_context());
  154. extension_ = CreateExtension("ext_1");
  155. }
  156. void TearDown() override {
  157. render_process_host_.reset();
  158. incognito_render_process_host_.reset();
  159. helper_.reset();
  160. ExtensionsTest::TearDown();
  161. }
  162. protected:
  163. void SimulateRenderProcessCreated(content::RenderProcessHost* rph) {
  164. helper_->OnRenderProcessHostCreated(rph);
  165. }
  166. void SimulateRenderProcessTerminated(content::RenderProcessHost* rph) {
  167. helper_->RenderProcessHostDestroyed(rph);
  168. }
  169. scoped_refptr<const Extension> CreateExtension(const std::string& id_input) {
  170. std::unique_ptr<base::DictionaryValue> manifest =
  171. DictionaryBuilder()
  172. .Set("name", "extension")
  173. .Set("description", "an extension")
  174. .Set("manifest_version", 2)
  175. .Set("version", "0.1")
  176. .Build();
  177. return CreateExtension(id_input, std::move(manifest));
  178. }
  179. scoped_refptr<const Extension> CreateTheme(const std::string& id_input) {
  180. std::unique_ptr<base::DictionaryValue> manifest =
  181. DictionaryBuilder()
  182. .Set("name", "theme")
  183. .Set("description", "a theme")
  184. .Set("theme", DictionaryBuilder().Build())
  185. .Set("manifest_version", 2)
  186. .Set("version", "0.1")
  187. .Build();
  188. return CreateExtension(id_input, std::move(manifest));
  189. }
  190. scoped_refptr<const Extension> CreatePlatformApp(
  191. const std::string& id_input) {
  192. std::unique_ptr<base::Value> background =
  193. DictionaryBuilder()
  194. .Set("scripts", ListBuilder().Append("background.js").Build())
  195. .Build();
  196. std::unique_ptr<base::DictionaryValue> manifest =
  197. DictionaryBuilder()
  198. .Set("name", "platform_app")
  199. .Set("description", "a platform app")
  200. .Set("app", DictionaryBuilder()
  201. .Set("background", std::move(background))
  202. .Build())
  203. .Set("manifest_version", 2)
  204. .Set("version", "0.1")
  205. .Build();
  206. return CreateExtension(id_input, std::move(manifest));
  207. }
  208. void AddExtensionToRegistry(scoped_refptr<const Extension> extension) {
  209. registry_->AddEnabled(extension);
  210. }
  211. void RemoveExtensionFromRegistry(scoped_refptr<const Extension> extension) {
  212. registry_->RemoveEnabled(extension->id());
  213. }
  214. bool IsProcessInitialized(content::RenderProcessHost* rph) {
  215. return base::Contains(helper_->process_mojo_map_, rph);
  216. }
  217. bool IsExtensionLoaded(const Extension& extension) {
  218. return base::Contains(helper_->extension_process_map_, extension.id());
  219. }
  220. bool IsExtensionLoadedInProcess(const Extension& extension,
  221. content::RenderProcessHost* rph) {
  222. return IsExtensionLoaded(extension) &&
  223. base::Contains(helper_->extension_process_map_[extension.id()], rph);
  224. }
  225. bool IsExtensionPendingActivationInProcess(const Extension& extension,
  226. content::RenderProcessHost* rph) {
  227. return base::Contains(helper_->pending_active_extensions_, rph) &&
  228. base::Contains(helper_->pending_active_extensions_[rph],
  229. extension.id());
  230. }
  231. std::unique_ptr<RendererStartupHelperInterceptor> helper_;
  232. raw_ptr<ExtensionRegistry> registry_; // Weak.
  233. std::unique_ptr<content::MockRenderProcessHost> render_process_host_;
  234. std::unique_ptr<content::MockRenderProcessHost>
  235. incognito_render_process_host_;
  236. scoped_refptr<const Extension> extension_;
  237. private:
  238. scoped_refptr<const Extension> CreateExtension(
  239. const std::string& id_input,
  240. std::unique_ptr<base::DictionaryValue> manifest) {
  241. return ExtensionBuilder()
  242. .SetManifest(std::move(manifest))
  243. .SetID(crx_file::id_util::GenerateId(id_input))
  244. .Build();
  245. }
  246. };
  247. // Tests extension loading, unloading and activation and render process creation
  248. // and termination.
  249. TEST_F(RendererStartupHelperTest, NormalExtensionLifecycle) {
  250. // Initialize render process.
  251. EXPECT_FALSE(IsProcessInitialized(render_process_host_.get()));
  252. SimulateRenderProcessCreated(render_process_host_.get());
  253. base::RunLoop().RunUntilIdle();
  254. EXPECT_TRUE(IsProcessInitialized(render_process_host_.get()));
  255. // Enable extension.
  256. helper_->clear_extensions();
  257. EXPECT_FALSE(IsExtensionLoaded(*extension_));
  258. AddExtensionToRegistry(extension_);
  259. helper_->OnExtensionLoaded(*extension_);
  260. EXPECT_TRUE(
  261. IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
  262. EXPECT_FALSE(IsExtensionPendingActivationInProcess(
  263. *extension_, render_process_host_.get()));
  264. base::RunLoop().RunUntilIdle();
  265. ASSERT_EQ(1u, helper_->num_loaded_extensions());
  266. // Activate extension.
  267. helper_->ActivateExtensionInProcess(*extension_, render_process_host_.get());
  268. EXPECT_FALSE(IsExtensionPendingActivationInProcess(
  269. *extension_, render_process_host_.get()));
  270. base::RunLoop().RunUntilIdle();
  271. ASSERT_EQ(1u, helper_->num_activated_extensions());
  272. // Disable extension.
  273. helper_->clear_extensions();
  274. RemoveExtensionFromRegistry(extension_);
  275. helper_->OnExtensionUnloaded(*extension_);
  276. EXPECT_FALSE(IsExtensionLoaded(*extension_));
  277. base::RunLoop().RunUntilIdle();
  278. ASSERT_EQ(1u, helper_->num_unloaded_extensions());
  279. // Extension enabled again.
  280. helper_->clear_extensions();
  281. AddExtensionToRegistry(extension_);
  282. helper_->OnExtensionLoaded(*extension_);
  283. EXPECT_TRUE(
  284. IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
  285. EXPECT_FALSE(IsExtensionPendingActivationInProcess(
  286. *extension_, render_process_host_.get()));
  287. base::RunLoop().RunUntilIdle();
  288. ASSERT_EQ(1u, helper_->num_loaded_extensions());
  289. // Render Process terminated.
  290. SimulateRenderProcessTerminated(render_process_host_.get());
  291. EXPECT_FALSE(IsProcessInitialized(render_process_host_.get()));
  292. EXPECT_TRUE(IsExtensionLoaded(*extension_));
  293. EXPECT_FALSE(
  294. IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
  295. EXPECT_FALSE(IsExtensionPendingActivationInProcess(
  296. *extension_, render_process_host_.get()));
  297. }
  298. // Tests that activating an extension in an uninitialized render process works
  299. // fine.
  300. TEST_F(RendererStartupHelperTest, EnabledBeforeProcessInitialized) {
  301. EXPECT_FALSE(IsProcessInitialized(render_process_host_.get()));
  302. // Enable extension. The render process isn't initialized yet, so the
  303. // extension should be added to the list of extensions awaiting activation.
  304. helper_->clear_extensions();
  305. AddExtensionToRegistry(extension_);
  306. helper_->OnExtensionLoaded(*extension_);
  307. helper_->ActivateExtensionInProcess(*extension_, render_process_host_.get());
  308. ASSERT_EQ(0u, helper_->num_loaded_extensions());
  309. EXPECT_TRUE(IsExtensionLoaded(*extension_));
  310. EXPECT_FALSE(
  311. IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
  312. EXPECT_TRUE(IsExtensionPendingActivationInProcess(
  313. *extension_, render_process_host_.get()));
  314. // Initialize PermissionsData default policy hosts restrictions.
  315. // During the process initialization, UpdateDefaultPolicyHostRestrictions
  316. // will be called with the default policy values returned by PermissionsData.
  317. URLPatternSet default_blocked_hosts;
  318. URLPatternSet default_allowed_hosts;
  319. default_blocked_hosts.AddPattern(
  320. URLPattern(URLPattern::SCHEME_ALL, "*://*.example.com/*"));
  321. default_allowed_hosts.AddPattern(
  322. URLPattern(URLPattern::SCHEME_ALL, "*://test.example2.com/*"));
  323. PermissionsData::SetDefaultPolicyHostRestrictions(
  324. util::GetBrowserContextId(browser_context()), default_blocked_hosts,
  325. default_allowed_hosts);
  326. // Initialize the render process.
  327. SimulateRenderProcessCreated(render_process_host_.get());
  328. // The renderer would have been sent multiple initialization messages
  329. // including the loading and activation messages for the extension.
  330. base::RunLoop().RunUntilIdle();
  331. ASSERT_EQ(1u, helper_->num_loaded_extensions());
  332. // Method UpdateDefaultPolicyHostRestrictions() from mojom::Renderer should
  333. // have been called with the default policy for blocked/allowed hosts given by
  334. // PermissionsData, which was initialized above.
  335. base::RunLoop().RunUntilIdle();
  336. EXPECT_EQ(default_blocked_hosts, helper_->default_policy_blocked_hosts());
  337. EXPECT_EQ(default_allowed_hosts, helper_->default_policy_allowed_hosts());
  338. EXPECT_TRUE(IsProcessInitialized(render_process_host_.get()));
  339. EXPECT_TRUE(
  340. IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
  341. EXPECT_FALSE(IsExtensionPendingActivationInProcess(
  342. *extension_, render_process_host_.get()));
  343. }
  344. // Tests that themes aren't loaded in a renderer process.
  345. TEST_F(RendererStartupHelperTest, LoadTheme) {
  346. // Initialize render process.
  347. EXPECT_FALSE(IsProcessInitialized(render_process_host_.get()));
  348. SimulateRenderProcessCreated(render_process_host_.get());
  349. EXPECT_TRUE(IsProcessInitialized(render_process_host_.get()));
  350. scoped_refptr<const Extension> extension(CreateTheme("theme"));
  351. ASSERT_TRUE(extension->is_theme());
  352. IPC::TestSink& sink = render_process_host_->sink();
  353. // Enable the theme. Verify it isn't loaded and activated in the renderer.
  354. sink.ClearMessages();
  355. EXPECT_FALSE(IsExtensionLoaded(*extension));
  356. AddExtensionToRegistry(extension_);
  357. helper_->OnExtensionLoaded(*extension);
  358. EXPECT_EQ(0u, sink.message_count());
  359. EXPECT_TRUE(IsExtensionLoaded(*extension));
  360. EXPECT_FALSE(
  361. IsExtensionLoadedInProcess(*extension, render_process_host_.get()));
  362. helper_->ActivateExtensionInProcess(*extension, render_process_host_.get());
  363. EXPECT_EQ(0u, sink.message_count());
  364. EXPECT_FALSE(IsExtensionPendingActivationInProcess(
  365. *extension, render_process_host_.get()));
  366. helper_->OnExtensionUnloaded(*extension);
  367. EXPECT_EQ(0u, sink.message_count());
  368. EXPECT_FALSE(IsExtensionLoaded(*extension));
  369. }
  370. // Tests that only incognito-enabled extensions are loaded in an incognito
  371. // context.
  372. TEST_F(RendererStartupHelperTest, ExtensionInIncognitoRenderer) {
  373. // Initialize the incognito renderer.
  374. EXPECT_FALSE(IsProcessInitialized(incognito_render_process_host_.get()));
  375. SimulateRenderProcessCreated(incognito_render_process_host_.get());
  376. EXPECT_TRUE(IsProcessInitialized(incognito_render_process_host_.get()));
  377. // Enable the extension. It should not be loaded in the initialized incognito
  378. // renderer.
  379. helper_->clear_extensions();
  380. EXPECT_FALSE(util::IsIncognitoEnabled(extension_->id(), browser_context()));
  381. EXPECT_FALSE(IsExtensionLoaded(*extension_));
  382. AddExtensionToRegistry(extension_);
  383. helper_->OnExtensionLoaded(*extension_);
  384. EXPECT_EQ(0u, helper_->num_loaded_extensions());
  385. EXPECT_EQ(0u, helper_->num_loaded_extensions_in_incognito());
  386. EXPECT_TRUE(IsExtensionLoaded(*extension_));
  387. EXPECT_FALSE(IsExtensionLoadedInProcess(
  388. *extension_, incognito_render_process_host_.get()));
  389. EXPECT_FALSE(
  390. IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
  391. // Initialize the normal renderer. The extension should get loaded in it.
  392. helper_->clear_extensions();
  393. EXPECT_FALSE(IsProcessInitialized(render_process_host_.get()));
  394. SimulateRenderProcessCreated(render_process_host_.get());
  395. EXPECT_TRUE(IsProcessInitialized(render_process_host_.get()));
  396. EXPECT_TRUE(
  397. IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
  398. EXPECT_FALSE(IsExtensionLoadedInProcess(
  399. *extension_, incognito_render_process_host_.get()));
  400. // Multiple initialization messages including the extension load message
  401. // should be dispatched to the non-incognito renderer.
  402. base::RunLoop().RunUntilIdle();
  403. ASSERT_EQ(1u, helper_->num_loaded_extensions());
  404. ASSERT_EQ(0u, helper_->num_loaded_extensions_in_incognito());
  405. // Enable the extension in incognito mode. This will reload the extension.
  406. helper_->clear_extensions();
  407. ExtensionPrefs::Get(browser_context())
  408. ->SetIsIncognitoEnabled(extension_->id(), true);
  409. helper_->OnExtensionUnloaded(*extension_);
  410. helper_->OnExtensionLoaded(*extension_);
  411. EXPECT_TRUE(IsExtensionLoadedInProcess(*extension_,
  412. incognito_render_process_host_.get()));
  413. EXPECT_TRUE(
  414. IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
  415. // The extension would not have been unloaded from the incognito renderer
  416. // since it wasn't loaded.
  417. base::RunLoop().RunUntilIdle();
  418. // LoadExtensions are called twice because it's also called by
  419. // RendererStartupHelper::InitializeProcess as soon as a RenderProcessHost
  420. // instance is created.
  421. ASSERT_EQ(2u, helper_->num_loaded_extensions_in_incognito());
  422. // The extension would be first unloaded and then loaded from the normal
  423. // renderer.
  424. base::RunLoop().RunUntilIdle();
  425. ASSERT_EQ(1u, helper_->num_unloaded_extensions());
  426. ASSERT_EQ(2u, helper_->num_loaded_extensions());
  427. }
  428. // Tests that platform apps are always loaded in an incognito renderer.
  429. TEST_F(RendererStartupHelperTest, PlatformAppInIncognitoRenderer) {
  430. // Initialize the incognito renderer.
  431. EXPECT_FALSE(IsProcessInitialized(incognito_render_process_host_.get()));
  432. SimulateRenderProcessCreated(incognito_render_process_host_.get());
  433. base::RunLoop().RunUntilIdle();
  434. EXPECT_TRUE(IsProcessInitialized(incognito_render_process_host_.get()));
  435. scoped_refptr<const Extension> platform_app(
  436. CreatePlatformApp("platform_app"));
  437. ASSERT_TRUE(platform_app->is_platform_app());
  438. EXPECT_FALSE(util::IsIncognitoEnabled(platform_app->id(), browser_context()));
  439. EXPECT_FALSE(util::CanBeIncognitoEnabled(platform_app.get()));
  440. // Enable the app. It should get loaded in the incognito renderer even though
  441. // IsIncognitoEnabled returns false for it, since it can't be enabled for
  442. // incognito.
  443. helper_->clear_extensions();
  444. AddExtensionToRegistry(platform_app);
  445. helper_->OnExtensionLoaded(*platform_app);
  446. EXPECT_TRUE(IsExtensionLoadedInProcess(*platform_app,
  447. incognito_render_process_host_.get()));
  448. base::RunLoop().RunUntilIdle();
  449. ASSERT_EQ(1u, helper_->num_loaded_extensions_in_incognito());
  450. }
  451. } // namespace extensions