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