text: // Copyright 2020 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "extensions/test/extension_background_page_waiter.h"
  
  #include "base/scoped_observer.h"
  #include "content/public/browser/browser_context.h"
  #include "extensions/browser/event_router.h"
  #include "extensions/browser/extension_host.h"
  #include "extensions/browser/extension_host_observer.h"
  #include "extensions/browser/process_manager.h"
  #include "extensions/browser/process_manager_observer.h"
  #include "extensions/common/manifest_handlers/background_info.h"
  #include "extensions/common/manifest_handlers/incognito_info.h"
  #include "testing/gtest/include/gtest/gtest.h"
  
  namespace extensions {
  
  ExtensionBackgroundPageWaiter::ExtensionBackgroundPageWaiter(
      content::BrowserContext* browser_context,
      const Extension& extension)
      : browser_context_(browser_context),
        extension_(base::WrapRefCounted(&extension)) {}
  ExtensionBackgroundPageWaiter::~ExtensionBackgroundPageWaiter() = default;
  
  void ExtensionBackgroundPageWaiter::Wait() {
    if (browser_context_->IsOffTheRecord() &&
        !IncognitoInfo::IsSplitMode(extension_.get())) {
      ADD_FAILURE() << "Trying to wait for an incognito background page from a "
                    << "spanning mode extension. Use the on-the-record context.";
    }
  
    if (!BackgroundInfo::HasBackgroundPage(extension_.get()))
      return;  // No background page to wait for!
  
    if (extension_->is_hosted_app()) {
      // Little known fact: hosted apps can have background pages. They are
      // handled separately in BackgroundContents[Service], and don't use the
      // same infrastructure. They're also deprecated. Don't worry about them.
      // (If we see flakiness in loading hosted apps, we could potentially
      // rejigger this to accommodate for them as well, but it's unclear if it's
      // a problem that needs solving.)
      return;
    }
  
    ProcessManager* process_manager = ProcessManager::Get(browser_context_);
    ExtensionHost* extension_host =
        process_manager->GetBackgroundHostForExtension(extension_->id());
  
    if (!extension_host) {
      // If the extension has a lazy background page, it's possible that it's
      // already been loaded and unloaded. As a proxy for this, check if there
      // are registered events.
      // This isn't a perfect solution, because
      // a) We might be waiting on a subsequent background page load, and
      // b) The extension might not register any events (which would normally be
      //    a bug in event page-based extensions, but not always).
      // But, it's a decent proxy for now.
      if (BackgroundInfo::HasLazyBackgroundPage(extension_.get()) &&
          EventRouter::Get(browser_context_)
              ->HasRegisteredEvents(extension_->id())) {
        return;
      }
  
      WaitForExtensionHostCreation();
      extension_host =
          process_manager->GetBackgroundHostForExtension(extension_->id());
    }
  
    ASSERT_TRUE(extension_host);
    if (extension_host->has_loaded_once()) {
      // The background host exists and has loaded; we're done.
      return;
    }
  
    WaitForExtensionHostReady(extension_host);
  }
  
  void ExtensionBackgroundPageWaiter::WaitForExtensionHostCreation() {
    process_manager_observer_.Add(ProcessManager::Get(browser_context_));
    host_created_run_loop_.Run();
  }
  
  void ExtensionBackgroundPageWaiter::WaitForExtensionHostReady(
      ExtensionHost* host) {
    extension_host_observer_.Add(host);
    host_ready_run_loop_.Run();
  }
  
  void ExtensionBackgroundPageWaiter::OnBackgroundHostCreated(
      ExtensionHost* host) {
    if (host->extension_id() != extension_->id() ||
        host->browser_context() != browser_context_) {
      return;
    }
  
    process_manager_observer_.RemoveAll();
    host_created_run_loop_.QuitWhenIdle();
  }
  
  void ExtensionBackgroundPageWaiter::OnExtensionHostDidStopFirstLoad(
      const ExtensionHost* host) {
    ASSERT_EQ(extension_->id(), host->extension_id());
    ASSERT_TRUE(host->has_loaded_once());
    extension_host_observer_.RemoveAll();
    host_ready_run_loop_.QuitWhenIdle();
  }
  
  void ExtensionBackgroundPageWaiter::OnExtensionHostDestroyed(
      ExtensionHost* host) {
    // This is only called while we're waiting for the host to be ready (since
    // we remove ourselves as an observer when it's done).
    DCHECK(host_ready_run_loop_.running());
    ASSERT_EQ(extension_->id(), host->extension_id());
    ADD_FAILURE() << "Extension host for " << extension_->name()
                  << "was destroyed before it finished loading.";
    ASSERT_TRUE(extension_host_observer_.IsObserving(host));
    extension_host_observer_.Remove(host);
  }
  
  }  // namespace extensions
  