1 // Copyright 2020 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
5 #include "extensions/test/extension_background_page_waiter.h"
6
7 #include "base/scoped_observer.h"
8 #include "content/public/browser/browser_context.h"
9 #include "extensions/browser/event_router.h"
10 #include "extensions/browser/extension_host.h"
11 #include "extensions/browser/extension_host_observer.h"
12 #include "extensions/browser/process_manager.h"
13 #include "extensions/browser/process_manager_observer.h"
14 #include "extensions/common/manifest_handlers/background_info.h"
15 #include "extensions/common/manifest_handlers/incognito_info.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 namespace extensions {
19
ExtensionBackgroundPageWaiter(content::BrowserContext * browser_context,const Extension & extension)20 ExtensionBackgroundPageWaiter::ExtensionBackgroundPageWaiter(
21 content::BrowserContext* browser_context,
22 const Extension& extension)
23 : browser_context_(browser_context),
24 extension_(base::WrapRefCounted(&extension)) {}
25 ExtensionBackgroundPageWaiter::~ExtensionBackgroundPageWaiter() = default;
26
Wait()27 void ExtensionBackgroundPageWaiter::Wait() {
28 if (browser_context_->IsOffTheRecord() &&
29 !IncognitoInfo::IsSplitMode(extension_.get())) {
30 ADD_FAILURE() << "Trying to wait for an incognito background page from a "
31 << "spanning mode extension. Use the on-the-record context.";
32 }
33
34 if (!BackgroundInfo::HasBackgroundPage(extension_.get()))
35 return; // No background page to wait for!
36
37 if (extension_->is_hosted_app()) {
38 // Little known fact: hosted apps can have background pages. They are
39 // handled separately in BackgroundContents[Service], and don't use the
40 // same infrastructure. They're also deprecated. Don't worry about them.
41 // (If we see flakiness in loading hosted apps, we could potentially
42 // rejigger this to accommodate for them as well, but it's unclear if it's
43 // a problem that needs solving.)
44 return;
45 }
46
47 ProcessManager* process_manager = ProcessManager::Get(browser_context_);
48 ExtensionHost* extension_host =
49 process_manager->GetBackgroundHostForExtension(extension_->id());
50
51 if (!extension_host) {
52 // If the extension has a lazy background page, it's possible that it's
53 // already been loaded and unloaded. As a proxy for this, check if there
54 // are registered events.
55 // This isn't a perfect solution, because
56 // a) We might be waiting on a subsequent background page load, and
57 // b) The extension might not register any events (which would normally be
58 // a bug in event page-based extensions, but not always).
59 // But, it's a decent proxy for now.
60 if (BackgroundInfo::HasLazyBackgroundPage(extension_.get()) &&
61 EventRouter::Get(browser_context_)
62 ->HasRegisteredEvents(extension_->id())) {
63 return;
64 }
65
66 WaitForExtensionHostCreation();
67 extension_host =
68 process_manager->GetBackgroundHostForExtension(extension_->id());
69 }
70
71 ASSERT_TRUE(extension_host);
72 if (extension_host->has_loaded_once()) {
73 // The background host exists and has loaded; we're done.
74 return;
75 }
76
77 WaitForExtensionHostReady(extension_host);
78 }
79
WaitForExtensionHostCreation()80 void ExtensionBackgroundPageWaiter::WaitForExtensionHostCreation() {
81 process_manager_observer_.Add(ProcessManager::Get(browser_context_));
82 host_created_run_loop_.Run();
83 }
84
WaitForExtensionHostReady(ExtensionHost * host)85 void ExtensionBackgroundPageWaiter::WaitForExtensionHostReady(
86 ExtensionHost* host) {
87 extension_host_observer_.Add(host);
88 host_ready_run_loop_.Run();
89 }
90
OnBackgroundHostCreated(ExtensionHost * host)91 void ExtensionBackgroundPageWaiter::OnBackgroundHostCreated(
92 ExtensionHost* host) {
93 if (host->extension_id() != extension_->id() ||
94 host->browser_context() != browser_context_) {
95 return;
96 }
97
98 process_manager_observer_.RemoveAll();
99 host_created_run_loop_.QuitWhenIdle();
100 }
101
OnExtensionHostDidStopFirstLoad(const ExtensionHost * host)102 void ExtensionBackgroundPageWaiter::OnExtensionHostDidStopFirstLoad(
103 const ExtensionHost* host) {
104 ASSERT_EQ(extension_->id(), host->extension_id());
105 ASSERT_TRUE(host->has_loaded_once());
106 extension_host_observer_.RemoveAll();
107 host_ready_run_loop_.QuitWhenIdle();
108 }
109
OnExtensionHostDestroyed(ExtensionHost * host)110 void ExtensionBackgroundPageWaiter::OnExtensionHostDestroyed(
111 ExtensionHost* host) {
112 // This is only called while we're waiting for the host to be ready (since
113 // we remove ourselves as an observer when it's done).
114 DCHECK(host_ready_run_loop_.running());
115 ASSERT_EQ(extension_->id(), host->extension_id());
116 ADD_FAILURE() << "Extension host for " << extension_->name()
117 << "was destroyed before it finished loading.";
118 ASSERT_TRUE(extension_host_observer_.IsObserving(host));
119 extension_host_observer_.Remove(host);
120 }
121
122 } // namespace extensions
123