910e62b5创建于 1月15日历史提交
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "extensions/test/extension_test_notification_observer.h"

#include <memory>
#include <set>

#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/extension_util.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_id.h"

namespace extensions {
namespace {

bool HaveAllExtensionRenderFrameHostsFinishedLoading(ProcessManager* manager) {
  for (content::RenderFrameHost* host : manager->GetAllFrames()) {
    if (content::WebContents::FromRenderFrameHost(host)->IsLoading()) {
      return false;
    }
  }
  return true;
}

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// NotificationSet::ForwardingWebContentsObserver

class ExtensionTestNotificationObserver::NotificationSet::
    ForwardingWebContentsObserver : public content::WebContentsObserver {
 public:
  ForwardingWebContentsObserver(
      content::WebContents* contents,
      ExtensionTestNotificationObserver::NotificationSet* owner)
      : WebContentsObserver(contents), owner_(owner) {}

 private:
  // content::WebContentsObserver
  void DidStopLoading() override { owner_->DidStopLoading(web_contents()); }

  void WebContentsDestroyed() override {
    // Do not add code after this line, deletes `this`.
    owner_->WebContentsDestroyed(web_contents());
  }

  raw_ptr<ExtensionTestNotificationObserver::NotificationSet> owner_;
};

////////////////////////////////////////////////////////////////////////////////
// ExtensionTestNotificationObserver::NotificationSet

ExtensionTestNotificationObserver::NotificationSet::NotificationSet(
    ProcessManager* manager) {
  process_manager_observation_.Observe(manager);

  std::set<content::WebContents*> initial_extension_contents;
  for (content::RenderFrameHost* rfh : manager->GetAllFrames()) {
    initial_extension_contents.insert(
        content::WebContents::FromRenderFrameHost(rfh));
  }
  for (content::WebContents* web_contents : initial_extension_contents) {
    StartObservingWebContents(web_contents);
  }
}

ExtensionTestNotificationObserver::NotificationSet::~NotificationSet() =
    default;

void ExtensionTestNotificationObserver::NotificationSet::
    OnExtensionFrameUnregistered(const ExtensionId& extension_id,
                                 content::RenderFrameHost* render_frame_host) {
  closure_list_.Notify();
}

void ExtensionTestNotificationObserver::NotificationSet::OnWebContentsCreated(
    content::WebContents* web_contents) {
  StartObservingWebContents(web_contents);
}

void ExtensionTestNotificationObserver::NotificationSet::
    StartObservingWebContents(content::WebContents* web_contents) {
  CHECK(!base::Contains(web_contents_observers_, web_contents));
  web_contents_observers_[web_contents] =
      std::make_unique<ForwardingWebContentsObserver>(web_contents, this);
}

void ExtensionTestNotificationObserver::NotificationSet::DidStopLoading(
    content::WebContents* web_contents) {
  closure_list_.Notify();
}

void ExtensionTestNotificationObserver::NotificationSet::WebContentsDestroyed(
    content::WebContents* web_contents) {
  web_contents_observers_.erase(web_contents);
  closure_list_.Notify();
}

////////////////////////////////////////////////////////////////////////////////
// ExtensionTestNotificationObserver

ExtensionTestNotificationObserver::ExtensionTestNotificationObserver(
    content::BrowserContext* context)
    : context_(context) {}

ExtensionTestNotificationObserver::~ExtensionTestNotificationObserver() =
    default;

bool ExtensionTestNotificationObserver::WaitForExtensionViewsToLoad() {
  // Some views might not be created yet. This call may become insufficient if
  // e.g. implementation of ExtensionHostQueue changes.
  base::RunLoop().RunUntilIdle();

  ProcessManager* manager = ProcessManager::Get(context_);
  NotificationSet notification_set(manager);
  WaitForCondition(
      base::BindRepeating(&HaveAllExtensionRenderFrameHostsFinishedLoading,
                          manager),
      &notification_set);
  return true;
}

bool ExtensionTestNotificationObserver::WaitForExtensionIdle(
    const ExtensionId& extension_id) {
  ProcessManager* manager = ProcessManager::Get(context_);
  NotificationSet notification_set(manager);
  WaitForCondition(
      base::BindRepeating(&util::IsExtensionIdle, extension_id, context_),
      &notification_set);
  return true;
}

bool ExtensionTestNotificationObserver::WaitForExtensionNotIdle(
    const ExtensionId& extension_id) {
  ProcessManager* manager = ProcessManager::Get(context_);
  NotificationSet notification_set(manager);
  WaitForCondition(base::BindRepeating(
                       [](const ExtensionId& extension_id,
                          content::BrowserContext* context) -> bool {
                         return !util::IsExtensionIdle(extension_id, context);
                       },
                       extension_id, context_),
                   &notification_set);
  return true;
}

void ExtensionTestNotificationObserver::WaitForCondition(
    const ConditionCallback& condition,
    NotificationSet* notification_set) {
  if (condition.Run())
    return;
  condition_ = condition;

  base::RunLoop run_loop;
  quit_closure_ = run_loop.QuitClosure();

  base::CallbackListSubscription subscription;
  if (notification_set) {
    subscription = notification_set->closure_list().Add(base::BindRepeating(
        &ExtensionTestNotificationObserver::MaybeQuit, base::Unretained(this)));
  }
  run_loop.Run();

  condition_.Reset();
  quit_closure_.Reset();
}

void ExtensionTestNotificationObserver::MaybeQuit() {
  // We can be called synchronously from any of the events being observed,
  // so return immediately if the closure has already been run.
  if (quit_closure_.is_null())
    return;

  if (condition_.Run())
    std::move(quit_closure_).Run();
}

}  // namespace extensions