11e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
51e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "extensions/browser/lazy_background_task_queue.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/callback.h"
81e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "content/public/browser/browser_context.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_service.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_process_host.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_view_host.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/site_instance.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_contents.h"
1423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#include "extensions/browser/extension_host.h"
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_registry.h"
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_system.h"
171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "extensions/browser/extensions_browser_client.h"
185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "extensions/browser/notification_types.h"
19f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/browser/process_manager.h"
20f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/browser/process_map.h"
21f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/common/extension.h"
22f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/common/manifest_handlers/background_info.h"
23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "extensions/common/view_type.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace extensions {
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
271e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)LazyBackgroundTaskQueue::LazyBackgroundTaskQueue(
281e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    content::BrowserContext* browser_context)
29116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    : browser_context_(browser_context), extension_registry_observer_(this) {
305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  registrar_.Add(this,
315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                 extensions::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING,
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::NotificationService::AllBrowserContextsAndSources());
335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  registrar_.Add(this,
345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                 extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED,
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::NotificationService::AllBrowserContextsAndSources());
36116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
37116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context));
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)LazyBackgroundTaskQueue::~LazyBackgroundTaskQueue() {
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool LazyBackgroundTaskQueue::ShouldEnqueueTask(
441e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    content::BrowserContext* browser_context,
451e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    const Extension* extension) {
465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Note: browser_context may not be the same as browser_context_ for incognito
475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // extension tasks.
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(extension);
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (BackgroundInfo::HasBackgroundPage(extension)) {
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    ProcessManager* pm = ExtensionSystem::Get(
511e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        browser_context)->process_manager();
521e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    DCHECK(pm);
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ExtensionHost* background_host =
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        pm->GetBackgroundHostForExtension(extension->id());
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!background_host || !background_host->did_stop_loading())
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (pm->IsBackgroundHostClosing(extension->id()))
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pm->CancelSuspend(extension);
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void LazyBackgroundTaskQueue::AddPendingTask(
651e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    content::BrowserContext* browser_context,
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& extension_id,
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const PendingTask& task) {
681e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (ExtensionsBrowserClient::Get()->IsShuttingDown()) {
6958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    task.Run(NULL);
7058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return;
7158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PendingTasksList* tasks_list = NULL;
731e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  PendingTasksKey key(browser_context, extension_id);
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PendingTasksMap::iterator it = pending_tasks_.find(key);
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (it == pending_tasks_.end()) {
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tasks_list = new PendingTasksList();
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    pending_tasks_[key] = linked_ptr<PendingTasksList>(tasks_list);
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension =
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        ExtensionRegistry::Get(browser_context)->enabled_extensions().GetByID(
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            extension_id);
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (extension && BackgroundInfo::HasLazyBackgroundPage(extension)) {
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If this is the first enqueued task, and we're not waiting for the
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // background page to unload, ensure the background page is loaded.
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      ProcessManager* pm = ExtensionSystem::Get(
861e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          browser_context)->process_manager();
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pm->IncrementLazyKeepaliveCount(extension);
884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      // Creating the background host may fail, e.g. if |profile| is incognito
894e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      // but the extension isn't enabled in incognito mode.
904e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      if (!pm->CreateBackgroundHost(
914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            extension, BackgroundInfo::GetBackgroundURL(extension))) {
924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        task.Run(NULL);
934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        return;
944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      }
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tasks_list = it->second.get();
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tasks_list->push_back(task);
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void LazyBackgroundTaskQueue::ProcessPendingTasks(
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ExtensionHost* host,
1051e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    content::BrowserContext* browser_context,
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension) {
1071e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (!ExtensionsBrowserClient::Get()->IsSameContext(browser_context,
1081e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                                                     browser_context_))
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1111e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  PendingTasksKey key(browser_context, extension->id());
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PendingTasksMap::iterator map_it = pending_tasks_.find(key);
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (map_it == pending_tasks_.end()) {
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (BackgroundInfo::HasLazyBackgroundPage(extension))
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CHECK(!host);  // lazy page should not load without any pending tasks
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Swap the pending tasks to a temporary, to avoid problems if the task
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // list is modified during processing.
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PendingTasksList tasks;
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tasks.swap(*map_it->second);
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (PendingTasksList::const_iterator it = tasks.begin();
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       it != tasks.end(); ++it) {
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    it->Run(host);
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pending_tasks_.erase(key);
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Balance the keepalive in AddPendingTask. Note we don't do this on a
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // failure to load, because the keepalive count is reset in that case.
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (host && BackgroundInfo::HasLazyBackgroundPage(extension)) {
1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    ExtensionSystem::Get(browser_context)->process_manager()->
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DecrementLazyKeepaliveCount(extension);
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void LazyBackgroundTaskQueue::Observe(
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int type,
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationSource& source,
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationDetails& details) {
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (type) {
1435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    case extensions::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING: {
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If an on-demand background page finished loading, dispatch queued up
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // events for it.
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ExtensionHost* host =
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          content::Details<ExtensionHost>(details).ptr();
148c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (host->extension_host_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        CHECK(host->did_stop_loading());
1501e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        ProcessPendingTasks(host, host->browser_context(), host->extension());
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    case extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED: {
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Notify consumers about the load failure when the background host dies.
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // This can happen if the extension crashes. This is not strictly
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // necessary, since we also unload the extension in that case (which
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // dispatches the tasks below), but is a good extra precaution.
1591e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      content::BrowserContext* browser_context =
1601e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          content::Source<content::BrowserContext>(source).ptr();
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ExtensionHost* host =
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           content::Details<ExtensionHost>(details).ptr();
163c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (host->extension_host_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
1641e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        ProcessPendingTasks(NULL, browser_context, host->extension());
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
174116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid LazyBackgroundTaskQueue::OnExtensionUnloaded(
175116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    content::BrowserContext* browser_context,
176116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    const Extension* extension,
177116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    UnloadedExtensionInfo::Reason reason) {
178116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Notify consumers that the page failed to load.
179116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  ProcessPendingTasks(NULL, browser_context, extension);
180116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // If this extension is also running in an off-the-record context, notify that
181116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // task queue as well.
182116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  ExtensionsBrowserClient* browser_client = ExtensionsBrowserClient::Get();
183116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (browser_client->HasOffTheRecordContext(browser_context)) {
184116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    ProcessPendingTasks(NULL,
185116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                        browser_client->GetOffTheRecordContext(browser_context),
186116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                        extension);
187116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
188116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
189116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace extensions
191