[email protected] | 9fe4204 | 2013-10-29 21:13:33 | [diff] [blame] | 1 | // Copyright 2013 The Chromium Authors. All rights reserved. |
[email protected] | b6536df | 2012-03-16 18:55:23 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | 9fe4204 | 2013-10-29 21:13:33 | [diff] [blame] | 5 | #include "extensions/browser/lazy_background_task_queue.h" |
[email protected] | b6536df | 2012-03-16 18:55:23 | [diff] [blame] | 6 | |
| 7 | #include "base/callback.h" |
Karan Bhatia | 019bc70 | 2017-11-10 21:30:23 | [diff] [blame] | 8 | #include "base/logging.h" |
[email protected] | 9fe4204 | 2013-10-29 21:13:33 | [diff] [blame] | 9 | #include "content/public/browser/browser_context.h" |
[email protected] | b6536df | 2012-03-16 18:55:23 | [diff] [blame] | 10 | #include "content/public/browser/notification_service.h" |
| 11 | #include "content/public/browser/render_process_host.h" |
| 12 | #include "content/public/browser/render_view_host.h" |
| 13 | #include "content/public/browser/site_instance.h" |
| 14 | #include "content/public/browser/web_contents.h" |
[email protected] | 22401dc | 2014-03-21 01:38:57 | [diff] [blame] | 15 | #include "extensions/browser/extension_host.h" |
[email protected] | 9fe4204 | 2013-10-29 21:13:33 | [diff] [blame] | 16 | #include "extensions/browser/extensions_browser_client.h" |
juncai | 07ea5387 | 2015-05-20 22:20:13 | [diff] [blame] | 17 | #include "extensions/browser/lazy_background_task_queue_factory.h" |
lazyboy | 63b994a | 2017-06-30 21:20:23 | [diff] [blame] | 18 | #include "extensions/browser/lazy_context_id.h" |
[email protected] | adf5a10 | 2014-07-31 12:44:06 | [diff] [blame] | 19 | #include "extensions/browser/notification_types.h" |
[email protected] | 98b6d94 | 2013-11-10 00:34:07 | [diff] [blame] | 20 | #include "extensions/browser/process_manager.h" |
[email protected] | 50de9aa2 | 2013-11-14 06:30:34 | [diff] [blame] | 21 | #include "extensions/browser/process_map.h" |
[email protected] | e4452d3 | 2013-11-15 23:07:41 | [diff] [blame] | 22 | #include "extensions/common/extension.h" |
[email protected] | 558878cc8 | 2013-11-09 01:25:51 | [diff] [blame] | 23 | #include "extensions/common/manifest_handlers/background_info.h" |
[email protected] | cb2edf2 | 2013-04-01 20:25:23 | [diff] [blame] | 24 | #include "extensions/common/view_type.h" |
[email protected] | b6536df | 2012-03-16 18:55:23 | [diff] [blame] | 25 | |
[email protected] | 83055ea7 | 2012-04-05 18:56:36 | [diff] [blame] | 26 | namespace extensions { |
| 27 | |
lazyboy | 63b994a | 2017-06-30 21:20:23 | [diff] [blame] | 28 | namespace { |
| 29 | |
Derek Cheng | 86c3f0e | 2018-05-21 18:14:32 | [diff] [blame] | 30 | // Attempts to create a background host for a lazy background page. Returns true |
| 31 | // if the background host is created. |
| 32 | bool CreateLazyBackgroundHost(ProcessManager* pm, const Extension* extension) { |
David Bertoni | 3e1e9fa | 2018-08-29 20:39:30 | [diff] [blame] | 33 | pm->IncrementLazyKeepaliveCount(extension, Activity::LIFECYCLE_MANAGEMENT, |
| 34 | Activity::kCreatePage); |
Derek Cheng | 86c3f0e | 2018-05-21 18:14:32 | [diff] [blame] | 35 | // Creating the background host may fail, e.g. if the extension isn't enabled |
| 36 | // in incognito mode. |
| 37 | return pm->CreateBackgroundHost(extension, |
| 38 | BackgroundInfo::GetBackgroundURL(extension)); |
| 39 | } |
| 40 | |
lazyboy | 63b994a | 2017-06-30 21:20:23 | [diff] [blame] | 41 | } // namespace |
| 42 | |
[email protected] | 9fe4204 | 2013-10-29 21:13:33 | [diff] [blame] | 43 | LazyBackgroundTaskQueue::LazyBackgroundTaskQueue( |
| 44 | content::BrowserContext* browser_context) |
Evan Stade | 922f3f1f | 2019-09-04 21:05:13 | [diff] [blame] | 45 | : browser_context_(browser_context) { |
[email protected] | adf5a10 | 2014-07-31 12:44:06 | [diff] [blame] | 46 | registrar_.Add(this, |
kalman | fd474fa | 2015-03-16 22:30:57 | [diff] [blame] | 47 | extensions::NOTIFICATION_EXTENSION_HOST_DID_STOP_FIRST_LOAD, |
[email protected] | b6536df | 2012-03-16 18:55:23 | [diff] [blame] | 48 | content::NotificationService::AllBrowserContextsAndSources()); |
[email protected] | adf5a10 | 2014-07-31 12:44:06 | [diff] [blame] | 49 | registrar_.Add(this, |
| 50 | extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED, |
[email protected] | 76a383e | 2012-04-13 18:46:01 | [diff] [blame] | 51 | content::NotificationService::AllBrowserContextsAndSources()); |
[email protected] | c3bb718 | 2014-07-16 21:15:18 | [diff] [blame] | 52 | |
| 53 | extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context)); |
[email protected] | b6536df | 2012-03-16 18:55:23 | [diff] [blame] | 54 | } |
| 55 | |
| 56 | LazyBackgroundTaskQueue::~LazyBackgroundTaskQueue() { |
| 57 | } |
| 58 | |
juncai | 07ea5387 | 2015-05-20 22:20:13 | [diff] [blame] | 59 | // static |
| 60 | LazyBackgroundTaskQueue* LazyBackgroundTaskQueue::Get( |
| 61 | content::BrowserContext* browser_context) { |
| 62 | return LazyBackgroundTaskQueueFactory::GetForBrowserContext(browser_context); |
| 63 | } |
| 64 | |
[email protected] | d79e3ab | 2012-04-03 18:28:36 | [diff] [blame] | 65 | bool LazyBackgroundTaskQueue::ShouldEnqueueTask( |
[email protected] | 9fe4204 | 2013-10-29 21:13:33 | [diff] [blame] | 66 | content::BrowserContext* browser_context, |
| 67 | const Extension* extension) { |
[email protected] | 9fc5bdc8 | 2014-08-03 23:49:26 | [diff] [blame] | 68 | // Note: browser_context may not be the same as browser_context_ for incognito |
| 69 | // extension tasks. |
[email protected] | d79e3ab | 2012-04-03 18:28:36 | [diff] [blame] | 70 | DCHECK(extension); |
[email protected] | 9367eabc | 2013-03-01 01:29:29 | [diff] [blame] | 71 | if (BackgroundInfo::HasBackgroundPage(extension)) { |
reillyg | 0ea3fa90 | 2014-10-28 15:30:23 | [diff] [blame] | 72 | ProcessManager* pm = ProcessManager::Get(browser_context); |
[email protected] | d79e3ab | 2012-04-03 18:28:36 | [diff] [blame] | 73 | ExtensionHost* background_host = |
| 74 | pm->GetBackgroundHostForExtension(extension->id()); |
kalman | fd474fa | 2015-03-16 22:30:57 | [diff] [blame] | 75 | if (!background_host || !background_host->has_loaded_once()) |
[email protected] | d79e3ab | 2012-04-03 18:28:36 | [diff] [blame] | 76 | return true; |
[email protected] | 0d475e07 | 2012-07-26 02:30:42 | [diff] [blame] | 77 | if (pm->IsBackgroundHostClosing(extension->id())) |
| 78 | pm->CancelSuspend(extension); |
[email protected] | d79e3ab | 2012-04-03 18:28:36 | [diff] [blame] | 79 | } |
| 80 | |
| 81 | return false; |
| 82 | } |
| 83 | |
David Bertoni | 8269a09 | 2018-12-19 15:55:42 | [diff] [blame] | 84 | void LazyBackgroundTaskQueue::AddPendingTask(const LazyContextId& context_id, |
| 85 | PendingTask task) { |
[email protected] | 9fe4204 | 2013-10-29 21:13:33 | [diff] [blame] | 86 | if (ExtensionsBrowserClient::Get()->IsShuttingDown()) { |
Istiaque Ahmed | b7f0e2fd9 | 2017-10-11 17:37:42 | [diff] [blame] | 87 | std::move(task).Run(nullptr); |
[email protected] | 0951077 | 2013-09-05 00:08:47 | [diff] [blame] | 88 | return; |
| 89 | } |
David Bertoni | 8269a09 | 2018-12-19 15:55:42 | [diff] [blame] | 90 | const ExtensionId& extension_id = context_id.extension_id(); |
| 91 | content::BrowserContext* const browser_context = context_id.browser_context(); |
Istiaque Ahmed | b7f0e2fd9 | 2017-10-11 17:37:42 | [diff] [blame] | 92 | PendingTasksList* tasks_list = nullptr; |
David Bertoni | 6478643 | 2018-12-21 23:10:12 | [diff] [blame] | 93 | auto it = pending_tasks_.find(context_id); |
[email protected] | b6536df | 2012-03-16 18:55:23 | [diff] [blame] | 94 | if (it == pending_tasks_.end()) { |
Derek Cheng | 86c3f0e | 2018-05-21 18:14:32 | [diff] [blame] | 95 | const Extension* extension = ExtensionRegistry::Get(browser_context) |
| 96 | ->enabled_extensions() |
| 97 | .GetByID(extension_id); |
[email protected] | 9367eabc | 2013-03-01 01:29:29 | [diff] [blame] | 98 | if (extension && BackgroundInfo::HasLazyBackgroundPage(extension)) { |
[email protected] | 546fc9d | 2012-08-18 04:10:06 | [diff] [blame] | 99 | // If this is the first enqueued task, and we're not waiting for the |
| 100 | // background page to unload, ensure the background page is loaded. |
Derek Cheng | 86c3f0e | 2018-05-21 18:14:32 | [diff] [blame] | 101 | if (!CreateLazyBackgroundHost(ProcessManager::Get(browser_context), |
| 102 | extension)) { |
Istiaque Ahmed | b7f0e2fd9 | 2017-10-11 17:37:42 | [diff] [blame] | 103 | std::move(task).Run(nullptr); |
[email protected] | 61aa8c6 | 2013-10-01 00:43:07 | [diff] [blame] | 104 | return; |
| 105 | } |
[email protected] | 546fc9d | 2012-08-18 04:10:06 | [diff] [blame] | 106 | } |
Derek Cheng | 86c3f0e | 2018-05-21 18:14:32 | [diff] [blame] | 107 | auto tasks_list_tmp = std::make_unique<PendingTasksList>(); |
| 108 | tasks_list = tasks_list_tmp.get(); |
David Bertoni | 6478643 | 2018-12-21 23:10:12 | [diff] [blame] | 109 | pending_tasks_[context_id] = std::move(tasks_list_tmp); |
[email protected] | b6536df | 2012-03-16 18:55:23 | [diff] [blame] | 110 | } else { |
| 111 | tasks_list = it->second.get(); |
| 112 | } |
| 113 | |
Istiaque Ahmed | b7f0e2fd9 | 2017-10-11 17:37:42 | [diff] [blame] | 114 | tasks_list->push_back(std::move(task)); |
[email protected] | b6536df | 2012-03-16 18:55:23 | [diff] [blame] | 115 | } |
| 116 | |
[email protected] | 1ad12ef | 2012-04-16 19:26:22 | [diff] [blame] | 117 | void LazyBackgroundTaskQueue::ProcessPendingTasks( |
| 118 | ExtensionHost* host, |
[email protected] | 9fe4204 | 2013-10-29 21:13:33 | [diff] [blame] | 119 | content::BrowserContext* browser_context, |
[email protected] | 1ad12ef | 2012-04-16 19:26:22 | [diff] [blame] | 120 | const Extension* extension) { |
Karan Bhatia | 019bc70 | 2017-11-10 21:30:23 | [diff] [blame] | 121 | DCHECK(extension); |
| 122 | |
[email protected] | 9fe4204 | 2013-10-29 21:13:33 | [diff] [blame] | 123 | if (!ExtensionsBrowserClient::Get()->IsSameContext(browser_context, |
| 124 | browser_context_)) |
[email protected] | 1ad12ef | 2012-04-16 19:26:22 | [diff] [blame] | 125 | return; |
| 126 | |
[email protected] | 9fe4204 | 2013-10-29 21:13:33 | [diff] [blame] | 127 | PendingTasksKey key(browser_context, extension->id()); |
jdoerrie | a1e1598b | 2018-10-10 09:10:37 | [diff] [blame] | 128 | auto map_it = pending_tasks_.find(key); |
[email protected] | b6536df | 2012-03-16 18:55:23 | [diff] [blame] | 129 | if (map_it == pending_tasks_.end()) { |
[email protected] | 9367eabc | 2013-03-01 01:29:29 | [diff] [blame] | 130 | if (BackgroundInfo::HasLazyBackgroundPage(extension)) |
[email protected] | 546fc9d | 2012-08-18 04:10:06 | [diff] [blame] | 131 | CHECK(!host); // lazy page should not load without any pending tasks |
[email protected] | b6536df | 2012-03-16 18:55:23 | [diff] [blame] | 132 | return; |
| 133 | } |
| 134 | |
[email protected] | b49fd0bdb | 2012-07-25 20:30:56 | [diff] [blame] | 135 | // Swap the pending tasks to a temporary, to avoid problems if the task |
| 136 | // list is modified during processing. |
| 137 | PendingTasksList tasks; |
| 138 | tasks.swap(*map_it->second); |
Istiaque Ahmed | b7f0e2fd9 | 2017-10-11 17:37:42 | [diff] [blame] | 139 | for (auto& task : tasks) |
David Bertoni | 643f6a94 | 2018-12-17 16:50:07 | [diff] [blame] | 140 | std::move(task).Run(host ? std::make_unique<ContextInfo>(host) : nullptr); |
[email protected] | b6536df | 2012-03-16 18:55:23 | [diff] [blame] | 141 | |
[email protected] | b49fd0bdb | 2012-07-25 20:30:56 | [diff] [blame] | 142 | pending_tasks_.erase(key); |
[email protected] | b6536df | 2012-03-16 18:55:23 | [diff] [blame] | 143 | |
Derek Cheng | 86c3f0e | 2018-05-21 18:14:32 | [diff] [blame] | 144 | // Balance the keepalive in CreateLazyBackgroundHost. Note we don't do this on |
| 145 | // a failure to load, because the keepalive count is reset in that case. |
[email protected] | 9367eabc | 2013-03-01 01:29:29 | [diff] [blame] | 146 | if (host && BackgroundInfo::HasLazyBackgroundPage(extension)) { |
reillyg | 0ea3fa90 | 2014-10-28 15:30:23 | [diff] [blame] | 147 | ProcessManager::Get(browser_context) |
David Bertoni | 3e1e9fa | 2018-08-29 20:39:30 | [diff] [blame] | 148 | ->DecrementLazyKeepaliveCount(extension, Activity::LIFECYCLE_MANAGEMENT, |
| 149 | Activity::kCreatePage); |
[email protected] | 1ad12ef | 2012-04-16 19:26:22 | [diff] [blame] | 150 | } |
[email protected] | b6536df | 2012-03-16 18:55:23 | [diff] [blame] | 151 | } |
| 152 | |
Derek Cheng | 86c3f0e | 2018-05-21 18:14:32 | [diff] [blame] | 153 | void LazyBackgroundTaskQueue::NotifyTasksExtensionFailedToLoad( |
| 154 | content::BrowserContext* browser_context, |
| 155 | const Extension* extension) { |
| 156 | ProcessPendingTasks(nullptr, browser_context, extension); |
| 157 | // If this extension is also running in an off-the-record context, notify that |
| 158 | // task queue as well. |
| 159 | ExtensionsBrowserClient* browser_client = ExtensionsBrowserClient::Get(); |
| 160 | if (browser_client->HasOffTheRecordContext(browser_context)) { |
| 161 | ProcessPendingTasks(nullptr, |
| 162 | browser_client->GetOffTheRecordContext(browser_context), |
| 163 | extension); |
| 164 | } |
| 165 | } |
| 166 | |
[email protected] | b6536df | 2012-03-16 18:55:23 | [diff] [blame] | 167 | void LazyBackgroundTaskQueue::Observe( |
| 168 | int type, |
| 169 | const content::NotificationSource& source, |
| 170 | const content::NotificationDetails& details) { |
| 171 | switch (type) { |
kalman | fd474fa | 2015-03-16 22:30:57 | [diff] [blame] | 172 | case extensions::NOTIFICATION_EXTENSION_HOST_DID_STOP_FIRST_LOAD: { |
[email protected] | b6536df | 2012-03-16 18:55:23 | [diff] [blame] | 173 | // If an on-demand background page finished loading, dispatch queued up |
| 174 | // events for it. |
[email protected] | 3a1dc57 | 2012-07-31 22:25:13 | [diff] [blame] | 175 | ExtensionHost* host = |
| 176 | content::Details<ExtensionHost>(details).ptr(); |
[email protected] | cb2edf2 | 2013-04-01 20:25:23 | [diff] [blame] | 177 | if (host->extension_host_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) { |
kalman | fd474fa | 2015-03-16 22:30:57 | [diff] [blame] | 178 | CHECK(host->has_loaded_once()); |
[email protected] | 9fe4204 | 2013-10-29 21:13:33 | [diff] [blame] | 179 | ProcessPendingTasks(host, host->browser_context(), host->extension()); |
[email protected] | b6536df | 2012-03-16 18:55:23 | [diff] [blame] | 180 | } |
| 181 | break; |
| 182 | } |
[email protected] | adf5a10 | 2014-07-31 12:44:06 | [diff] [blame] | 183 | case extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED: { |
[email protected] | ed78d3fd | 2012-08-09 03:59:57 | [diff] [blame] | 184 | // Notify consumers about the load failure when the background host dies. |
| 185 | // This can happen if the extension crashes. This is not strictly |
| 186 | // necessary, since we also unload the extension in that case (which |
| 187 | // dispatches the tasks below), but is a good extra precaution. |
[email protected] | 9fe4204 | 2013-10-29 21:13:33 | [diff] [blame] | 188 | content::BrowserContext* browser_context = |
| 189 | content::Source<content::BrowserContext>(source).ptr(); |
[email protected] | 3a1dc57 | 2012-07-31 22:25:13 | [diff] [blame] | 190 | ExtensionHost* host = |
| 191 | content::Details<ExtensionHost>(details).ptr(); |
Karan Bhatia | 019bc70 | 2017-11-10 21:30:23 | [diff] [blame] | 192 | if (host->extension() && |
| 193 | host->extension_host_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) { |
[email protected] | 9fe4204 | 2013-10-29 21:13:33 | [diff] [blame] | 194 | ProcessPendingTasks(NULL, browser_context, host->extension()); |
[email protected] | 76a383e | 2012-04-13 18:46:01 | [diff] [blame] | 195 | } |
| 196 | break; |
| 197 | } |
[email protected] | b6536df | 2012-03-16 18:55:23 | [diff] [blame] | 198 | default: |
| 199 | NOTREACHED(); |
| 200 | break; |
| 201 | } |
| 202 | } |
[email protected] | 83055ea7 | 2012-04-05 18:56:36 | [diff] [blame] | 203 | |
Derek Cheng | 86c3f0e | 2018-05-21 18:14:32 | [diff] [blame] | 204 | void LazyBackgroundTaskQueue::OnExtensionLoaded( |
| 205 | content::BrowserContext* browser_context, |
| 206 | const Extension* extension) { |
| 207 | // If there are pending tasks for a lazy background page, and its background |
| 208 | // host has not been created yet, then create it. This can happen if a pending |
| 209 | // task was added while the extension is not yet enabled (e.g., component |
| 210 | // extension crashed and waiting to reload, https://crbug.com/835017). |
| 211 | if (!BackgroundInfo::HasLazyBackgroundPage(extension)) |
| 212 | return; |
| 213 | |
| 214 | CreateLazyBackgroundHostOnExtensionLoaded(browser_context, extension); |
| 215 | |
| 216 | // Also try to create the background host for the off-the-record context. |
| 217 | ExtensionsBrowserClient* browser_client = ExtensionsBrowserClient::Get(); |
| 218 | if (browser_client->HasOffTheRecordContext(browser_context)) { |
| 219 | CreateLazyBackgroundHostOnExtensionLoaded( |
| 220 | browser_client->GetOffTheRecordContext(browser_context), extension); |
| 221 | } |
| 222 | } |
| 223 | |
[email protected] | c3bb718 | 2014-07-16 21:15:18 | [diff] [blame] | 224 | void LazyBackgroundTaskQueue::OnExtensionUnloaded( |
| 225 | content::BrowserContext* browser_context, |
| 226 | const Extension* extension, |
limasdf | 0deef204 | 2017-05-03 19:17:17 | [diff] [blame] | 227 | UnloadedExtensionReason reason) { |
Derek Cheng | 86c3f0e | 2018-05-21 18:14:32 | [diff] [blame] | 228 | NotifyTasksExtensionFailedToLoad(browser_context, extension); |
| 229 | } |
| 230 | |
| 231 | void LazyBackgroundTaskQueue::CreateLazyBackgroundHostOnExtensionLoaded( |
| 232 | content::BrowserContext* browser_context, |
| 233 | const Extension* extension) { |
| 234 | PendingTasksKey key(browser_context, extension->id()); |
Jan Wilken Dörrie | 0fd53a2 | 2019-06-07 09:55:46 | [diff] [blame] | 235 | if (!base::Contains(pending_tasks_, key)) |
Derek Cheng | 86c3f0e | 2018-05-21 18:14:32 | [diff] [blame] | 236 | return; |
| 237 | |
| 238 | ProcessManager* pm = ProcessManager::Get(browser_context); |
| 239 | |
| 240 | // Background host already created, just wait for it to finish loading. |
| 241 | if (pm->GetBackgroundHostForExtension(extension->id())) |
| 242 | return; |
| 243 | |
| 244 | if (!CreateLazyBackgroundHost(pm, extension)) |
| 245 | ProcessPendingTasks(nullptr, browser_context, extension); |
[email protected] | c3bb718 | 2014-07-16 21:15:18 | [diff] [blame] | 246 | } |
| 247 | |
[email protected] | 83055ea7 | 2012-04-05 18:56:36 | [diff] [blame] | 248 | } // namespace extensions |