// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/guest_view/browser/guest_view_manager.h"

#include <tuple>
#include <utility>

#include "base/memory/ptr_util.h"
#include "build/build_config.h"
#include "components/crash/core/common/crash_key.h"
#include "components/guest_view/browser/bad_message.h"
#include "components/guest_view/browser/guest_view_base.h"
#include "components/guest_view/browser/guest_view_manager_delegate.h"
#include "components/guest_view/browser/guest_view_manager_factory.h"
#include "components/guest_view/common/guest_view_constants.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/child_process_host.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_process_host_observer.h"
#include "content/public/browser/security_principal.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/content_features.h"

using content::BrowserContext;
using content::RenderProcessHost;
using content::SiteInstance;
using content::WebContents;

namespace guest_view {

namespace {

// Static factory instance (always NULL for non-test).
GuestViewManagerFactory* g_factory;

}  // namespace

// This observer observes the RenderProcessHosts of GuestView embedders, and
// notifies the GuestViewManager when they are destroyed.
class GuestViewManager::EmbedderRenderProcessHostObserver
    : public content::RenderProcessHostObserver {
 public:
  EmbedderRenderProcessHostObserver(
      base::WeakPtr<GuestViewManager> guest_view_manager,
      RenderProcessHost* host)
      : guest_view_manager_(guest_view_manager) {
    DCHECK(host);
    host->AddObserver(this);
  }

  void RenderProcessExited(
      RenderProcessHost* host,
      const content::ChildProcessTerminationInfo& info) override {
    if (guest_view_manager_)
      guest_view_manager_->EmbedderProcessDestroyed(host->GetID());
  }

  void RenderProcessHostDestroyed(RenderProcessHost* host) override {
    host->RemoveObserver(this);
    delete this;
  }

 private:
  base::WeakPtr<GuestViewManager> guest_view_manager_;
};

GuestViewManager::GuestViewManager(
    content::BrowserContext* context,
    std::unique_ptr<GuestViewManagerDelegate> delegate)
    : context_(context), delegate_(std::move(delegate)) {}

GuestViewManager::~GuestViewManager() {
  // It seems that ChromeOS OTR profiles may still have RenderProcessHosts at
  // this point. See https://crbug.com/828479
#if !BUILDFLAG(IS_CHROMEOS)
  DCHECK(view_destruction_callback_map_.empty());
#endif
}

// static
GuestViewManager* GuestViewManager::CreateWithDelegate(
    BrowserContext* context,
    std::unique_ptr<GuestViewManagerDelegate> delegate) {
  GuestViewManager* guest_manager = FromBrowserContext(context);
  if (!guest_manager) {
    std::unique_ptr<GuestViewManager> new_manager =
        g_factory
            ? g_factory->CreateGuestViewManager(context, std::move(delegate))
            : std::make_unique<GuestViewManager>(context, std::move(delegate));
    guest_manager = new_manager.get();
    context->SetUserData(kGuestViewManagerKeyName, std::move(new_manager));
  }
  return guest_manager;
}

// static
GuestViewManager* GuestViewManager::FromBrowserContext(
    BrowserContext* context) {
  return static_cast<GuestViewManager*>(context->GetUserData(
      kGuestViewManagerKeyName));
}

// static
void GuestViewManager::set_factory_for_testing(
    GuestViewManagerFactory* factory) {
  g_factory = factory;
}

GuestViewBase* GuestViewManager::GetGuestByInstanceIDSafely(
    int guest_instance_id,
    content::ChildProcessId embedder_render_process_id) {
  if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id,
                                            guest_instance_id)) {
    return nullptr;
  }
  return GetGuestByInstanceID(guest_instance_id);
}

GuestViewBase* GuestViewManager::GetGuestByInstanceIDSafely(
    int guest_instance_id,
    int embedder_render_process_id) {
  return GetGuestByInstanceIDSafely(
      guest_instance_id, content::ChildProcessId(embedder_render_process_id));
}

void GuestViewManager::AttachGuest(content::ChildProcessId embedder_process_id,
                                   int element_instance_id,
                                   int guest_instance_id,
                                   const base::DictValue& attach_params) {
  auto* guest_view =
      GuestViewBase::FromInstanceID(embedder_process_id, guest_instance_id);
  if (!guest_view)
    return;

  ElementInstanceKey key(embedder_process_id, element_instance_id);

  // If there is an existing guest attached to the element, then the embedder is
  // misbehaving.
  if (instance_id_map_.contains(key)) {
    bad_message::ReceivedBadMessage(embedder_process_id,
                                    bad_message::GVM_INVALID_ATTACH);
    return;
  }

  instance_id_map_[key] = guest_instance_id;
  reverse_instance_id_map_[guest_instance_id] = key;
  guest_view->SetAttachParams(attach_params);
}

bool GuestViewManager::IsOwnedByExtension(const GuestViewBase* guest) {
  return delegate_->IsOwnedByExtension(guest);
}

bool GuestViewManager::IsOwnedByControlledFrameEmbedder(
    const GuestViewBase* guest) {
  return delegate_->IsOwnedByControlledFrameEmbedder(guest);
}

int GuestViewManager::GetNextInstanceID() {
  return ++current_instance_id_;
}

base::WeakPtr<GuestViewManager> GuestViewManager::AsWeakPtr() {
  return weak_ptr_factory_.GetWeakPtr();
}

void GuestViewManager::CreateGuest(const std::string& view_type,
                                   content::RenderFrameHost* owner_rfh,
                                   const base::DictValue& create_params,
                                   UnownedGuestCreatedCallback callback) {
  OwnedGuestCreatedCallback ownership_transferring_callback = base::BindOnce(
      [](UnownedGuestCreatedCallback callback,
         std::unique_ptr<GuestViewBase> guest) {
        auto* raw_guest = guest.get();
        if (raw_guest) {
          raw_guest->GetGuestViewManager()->ManageOwnership(std::move(guest));
        }
        std::move(callback).Run(raw_guest);
      },
      std::move(callback));
  CreateGuestAndTransferOwnership(view_type, owner_rfh, nullptr, create_params,
                                  std::move(ownership_transferring_callback));
}

int GuestViewManager::CreateGuestAndTransferOwnership(
    const std::string& view_type,
    content::RenderFrameHost* owner_rfh,
    scoped_refptr<content::SiteInstance> site_instance,
    const base::DictValue& create_params,
    OwnedGuestCreatedCallback callback) {
  std::unique_ptr<GuestViewBase> guest =
      CreateGuestInternal(owner_rfh, view_type);
  if (!guest) {
    std::move(callback).Run(nullptr);
    return -1;
  }
  auto* raw_guest = guest.get();
  int guest_instance_id = guest->guest_instance_id();
  raw_guest->Init(std::move(guest), site_instance, create_params,
                  std::move(callback));
  return guest_instance_id;
}

std::unique_ptr<GuestViewBase> GuestViewManager::TransferOwnership(
    GuestViewBase* guest) {
  for (auto it = owned_guests_.begin(); it != owned_guests_.end(); ++it) {
    if (it->second.get() == guest) {
      std::unique_ptr<GuestViewBase> owned_guest = std::move(it->second);
      owned_guests_.erase(it);
      return owned_guest;
    }
  }

  return nullptr;
}

void GuestViewManager::ManageOwnership(std::unique_ptr<GuestViewBase> guest) {
  RenderProcessHost* owner_process = guest->owner_rfh()->GetProcess();
  DCHECK(owner_process);
  ObserveEmbedderLifetime(owner_process);
  owned_guests_.insert({owner_process->GetID(), std::move(guest)});
}

std::unique_ptr<content::WebContents>
GuestViewManager::CreateGuestWithWebContentsParams(
    const std::string& view_type,
    content::RenderFrameHost* owner_rfh,
    const content::WebContents::CreateParams& create_params) {
  std::unique_ptr<GuestViewBase> guest =
      CreateGuestInternal(owner_rfh, view_type);
  if (!guest)
    return nullptr;

  // TODO(crbug.com/40254126): For the noopener case, it would be better to
  // delay the creation of the guest contents until attachment.
  content::WebContents::CreateParams guest_create_params(create_params);
  guest_create_params.guest_delegate = guest.get();

  std::unique_ptr<content::WebContents> guest_web_contents =
      WebContents::Create(guest_create_params);
  const base::DictValue guest_params = base::DictValue();
  guest->SetCreateParams(guest_params, guest_create_params);
  guest->InitWithWebContents(guest_params, guest_web_contents.get());
  ManageOwnership(std::move(guest));
  // Ownership of the guest WebContents goes to the content layer until we get
  // it back in AddNewContents.
  return guest_web_contents;
}

SiteInstance* GuestViewManager::GetGuestSiteInstance(
    const content::StoragePartitionConfig& storage_partition_config) {
  for (auto [id, guest] : guests_by_instance_id_) {
    content::RenderFrameHost* guest_main_frame = guest->GetGuestMainFrame();
    if (guest_main_frame &&
        guest_main_frame->GetSiteInstance()
                ->GetSecurityPrincipal()
                .GetStoragePartitionConfig() == storage_partition_config) {
      return guest_main_frame->GetSiteInstance();
    }
  }
  return nullptr;
}

void GuestViewManager::ForEachUnattachedGuestContents(
    content::WebContents* owner_web_contents,
    base::FunctionRef<void(content::WebContents*)> fn) {
  CHECK(!base::FeatureList::IsEnabled(features::kGuestViewMPArch));
  for (auto [id, guest] : guests_by_instance_id_) {
    if (guest->owner_web_contents() == owner_web_contents &&
        !guest->attached() && guest->web_contents()) {
      fn(guest->web_contents());
    }
  }
}

void GuestViewManager::ForEachUnattachedGuestPage(
    content::Page& owner_page,
    base::FunctionRef<void(content::GuestPageHolder&)> fn) {
  CHECK(base::FeatureList::IsEnabled(features::kGuestViewMPArch));
  for (auto [id, guest] : guests_by_instance_id_) {
    if (guest->owned_guest_page() && guest->GetGuestMainFrame() &&
        &guest->owner_rfh()->GetPage() == &owner_page) {
      CHECK(!guest->attached());
      fn(*guest->owned_guest_page());
    }
  }
}

bool GuestViewManager::ForEachGuest(
    WebContents* owner_web_contents,
    base::FunctionRef<bool(content::WebContents*)> fn) {
  CHECK(!base::FeatureList::IsEnabled(features::kGuestViewMPArch));
  for (auto [id, guest] : guests_by_instance_id_) {
    if (!guest->web_contents() ||
        guest->owner_web_contents() != owner_web_contents) {
      continue;
    }

    if (fn(guest->web_contents())) {
      return true;
    }
  }
  return false;
}

WebContents* GuestViewManager::GetFullPageGuest(
    WebContents* embedder_web_contents) {
  WebContents* result = nullptr;
  ForEachGuest(
      embedder_web_contents, [&](content::WebContents* guest_web_contents) {
        auto* guest_view = GuestViewBase::FromWebContents(guest_web_contents);
        if (guest_view && guest_view->is_full_page_plugin()) {
          result = guest_web_contents;
          return true;
        }
        return false;
      });

  return result;
}

void GuestViewManager::AddGuest(GuestViewBase* guest) {
  const int guest_instance_id = guest->guest_instance_id();

  CHECK(CanUseGuestInstanceID(guest_instance_id));
  const auto [it, success] =
      guests_by_instance_id_.insert({guest_instance_id, guest});
  // The guest may already be tracked if we needed to recreate the guest
  // contents. In that case, the ID must still refer to the same guest when
  // re-adding it here.
  CHECK(success || it->second == guest);

  if (base::FeatureList::IsEnabled(features::kGuestViewMPArch)) {
    guest_page_frame_id_guestview_map_.insert(
        {guest->guest_main_frame_tree_node_id(), guest});
    // There's no need for an MPArch equivalent of `OnGuestAdded`, as features
    // can use existing WebContentsObservers.
  } else {
    WebContents* guest_web_contents = guest->web_contents();
    webcontents_guestview_map_.insert({guest_web_contents, guest});

    delegate_->OnGuestAdded(guest_web_contents);
  }
}

void GuestViewManager::RemoveGuest(GuestViewBase* guest, bool invalidate_id) {
  const int guest_instance_id = guest->guest_instance_id();

  if (base::FeatureList::IsEnabled(features::kGuestViewMPArch)) {
    guest_page_frame_id_guestview_map_.erase(
        guest->guest_main_frame_tree_node_id());
  } else {
    webcontents_guestview_map_.erase(guest->web_contents());
  }

  auto id_iter = reverse_instance_id_map_.find(guest_instance_id);
  if (id_iter != reverse_instance_id_map_.end()) {
    const ElementInstanceKey& instance_id_key = id_iter->second;
    instance_id_map_.erase(instance_id_key);
    reverse_instance_id_map_.erase(id_iter);
  }

  if (!invalidate_id) {
    return;
  }

  // We don't stop tracking the `guest` when `invalidate_id` is false as the
  // guest still exists and may go on to recreate its guest contents.
  guests_by_instance_id_.erase(guest_instance_id);

  // All the instance IDs that lie within [0, last_instance_id_removed_]
  // are invalid.
  // The remaining sparse invalid IDs are kept in |removed_instance_ids_| set.
  // The following code compacts the set by incrementing
  // |last_instance_id_removed_|.
  if (guest_instance_id == last_instance_id_removed_ + 1) {
    ++last_instance_id_removed_;
    // Compact.
    auto iter = removed_instance_ids_.begin();
    while (iter != removed_instance_ids_.end()) {
      int instance_id = *iter;
      // The sparse invalid IDs must not lie within
      // [0, last_instance_id_removed_]
      DCHECK_GT(instance_id, last_instance_id_removed_);
      if (instance_id != last_instance_id_removed_ + 1)
        break;
      ++last_instance_id_removed_;
      removed_instance_ids_.erase(iter++);
    }
  } else if (guest_instance_id > last_instance_id_removed_) {
    removed_instance_ids_.insert(guest_instance_id);
  }
}

GuestViewBase* GuestViewManager::GetGuestFromWebContents(
    content::WebContents* web_contents) {
  CHECK(!base::FeatureList::IsEnabled(features::kGuestViewMPArch));
  auto it = webcontents_guestview_map_.find(web_contents);
  return it == webcontents_guestview_map_.end() ? nullptr : it->second;
}

GuestViewBase* GuestViewManager::GetGuestFromRenderFrameHost(
    content::RenderFrameHost& rfh) {
  CHECK(base::FeatureList::IsEnabled(features::kGuestViewMPArch));
  content::RenderFrameHost* outermost_rfh = rfh.GetOutermostMainFrame();
  return GetGuestFromOutermostFrameTreeNodeId(
      outermost_rfh->GetFrameTreeNodeId());
}

GuestViewBase* GuestViewManager::GetGuestFromNavigationHandle(
    content::NavigationHandle& navigation_handle) {
  CHECK(base::FeatureList::IsEnabled(features::kGuestViewMPArch));
  if (content::RenderFrameHost* parent_or_outer =
          navigation_handle.GetParentFrameOrOuterDocument()) {
    return GetGuestFromRenderFrameHost(*parent_or_outer);
  }

  CHECK(navigation_handle.IsInOutermostMainFrame());
  return GetGuestFromOutermostFrameTreeNodeId(
      navigation_handle.GetFrameTreeNodeId());
}

GuestViewBase* GuestViewManager::GetGuestFromFrameTreeNodeId(
    content::FrameTreeNodeId frame_tree_node_id) {
  CHECK(base::FeatureList::IsEnabled(features::kGuestViewMPArch));
  content::WebContents* web_contents =
      content::WebContents::FromFrameTreeNodeId(frame_tree_node_id);
  // This lookup is safe, since we're only using it to get to the parent.
  content::RenderFrameHost* rfh =
      web_contents->UnsafeFindFrameByFrameTreeNodeId(frame_tree_node_id);
  if (rfh && rfh->GetParentOrOuterDocument()) {
    return GetGuestFromRenderFrameHost(*rfh->GetParentOrOuterDocument());
  }

  return GetGuestFromOutermostFrameTreeNodeId(frame_tree_node_id);
}

GuestViewBase* GuestViewManager::GetGuestFromOutermostFrameTreeNodeId(
    content::FrameTreeNodeId outermost_ftn_id) {
  CHECK(base::FeatureList::IsEnabled(features::kGuestViewMPArch));
  auto it = guest_page_frame_id_guestview_map_.find(outermost_ftn_id);
  return it == guest_page_frame_id_guestview_map_.end() ? nullptr : it->second;
}

void GuestViewManager::EmbedderProcessDestroyed(
    content::ChildProcessId embedder_process_id) {
  embedders_observed_.erase(embedder_process_id);

  // We can't just call std::multimap::erase here because destroying a guest
  // could trigger the destruction of another guest which is also owned by
  // `owned_guests_`. Recursively calling std::multimap::erase is unsafe (see
  // https://crbug.com/1450397). So we take ownership of all of the guests that
  // will be destroyed before erasing the entries from the map.
  std::vector<std::unique_ptr<GuestViewBase>> guests_to_destroy;
  const auto destroy_range = owned_guests_.equal_range(embedder_process_id);
  for (auto it = destroy_range.first; it != destroy_range.second; ++it) {
    guests_to_destroy.push_back(std::move(it->second));
  }
  owned_guests_.erase(embedder_process_id);
  guests_to_destroy.clear();

  CallViewDestructionCallbacks(embedder_process_id);
}

void GuestViewManager::ViewCreated(content::ChildProcessId embedder_process_id,
                                   int view_instance_id,
                                   const std::string& view_type) {
  if (guest_view_registry_.empty())
    RegisterGuestViewTypes();
  auto view_it = guest_view_registry_.find(view_type);
  if (view_it == guest_view_registry_.end()) {
    bad_message::ReceivedBadMessage(embedder_process_id,
                                    bad_message::GVM_INVALID_GUESTVIEW_TYPE);
    return;
  }

  // Register the cleanup callback for when this view is destroyed.
  if (view_it->second.cleanup_function) {
    RegisterViewDestructionCallback(
        embedder_process_id, view_instance_id,
        base::BindOnce(view_it->second.cleanup_function, context_,
                       embedder_process_id, view_instance_id));
  }
}

void GuestViewManager::ViewGarbageCollected(
    content::ChildProcessId embedder_process_id,
    int view_instance_id) {
  CallViewDestructionCallbacks(embedder_process_id, view_instance_id);
}

void GuestViewManager::CallViewDestructionCallbacks(
    content::ChildProcessId embedder_process_id,
    int view_instance_id) {
  // Find the callbacks for the embedder with ID |embedder_process_id|.
  auto embedder_it = view_destruction_callback_map_.find(embedder_process_id);
  if (embedder_it == view_destruction_callback_map_.end())
    return;
  auto& callbacks_for_embedder = embedder_it->second;

  // If |view_instance_id| is guest_view::kInstanceIDNone, then all callbacks
  // for this embedder should be called.
  if (view_instance_id == kInstanceIDNone) {
    // Call all callbacks for the embedder with ID |embedder_process_id|.
    for (auto& view_pair : callbacks_for_embedder) {
      auto& callbacks_for_view = view_pair.second;
      for (auto& callback : callbacks_for_view)
        std::move(callback).Run();
    }
    view_destruction_callback_map_.erase(embedder_it);
    return;
  }

  // Otherwise, call the callbacks only for the specific view with ID
  // |view_instance_id|.
  auto view_it = callbacks_for_embedder.find(view_instance_id);
  if (view_it == callbacks_for_embedder.end())
    return;
  auto& callbacks_for_view = view_it->second;
  for (auto& callback : callbacks_for_view)
    std::move(callback).Run();
  callbacks_for_embedder.erase(view_it);
}

void GuestViewManager::CallViewDestructionCallbacks(
    content::ChildProcessId embedder_process_id) {
  CallViewDestructionCallbacks(embedder_process_id, kInstanceIDNone);
}

std::unique_ptr<GuestViewBase> GuestViewManager::CreateGuestInternal(
    content::RenderFrameHost* owner_rfh,
    const std::string& view_type) {
  if (guest_view_registry_.empty())
    RegisterGuestViewTypes();

  auto it = guest_view_registry_.find(view_type);
  if (it == guest_view_registry_.end()) {
    NOTREACHED();
  }

  return it->second.create_function.Run(owner_rfh);
}

void GuestViewManager::RegisterGuestViewTypes() {
  delegate_->RegisterAdditionalGuestViewTypes(this);
}

void GuestViewManager::RegisterGuestViewType(
    const std::string& type,
    GuestViewCreateFunction create_function,
    GuestViewCleanUpFunction cleanup_function) {
  // If the GuestView type `type` is already registered, then there is nothing
  // more to do. If an existing entry in the registry was created by this
  // function for `type`, then registering again would have no effect, and
  // if it was registered elsewhere, then we do not want to overwrite it. Note
  // that it is possible for tests to have special test factory methods
  // registered here.
  if (guest_view_registry_.contains(type))
    return;

  guest_view_registry_.insert({type, {create_function, cleanup_function}});
}

void GuestViewManager::RegisterViewDestructionCallback(
    content::ChildProcessId embedder_process_id,
    int view_instance_id,
    base::OnceClosure callback) {
  RenderProcessHost* rph = RenderProcessHost::FromID(embedder_process_id);
  // The RenderProcessHost may already be gone.
  if (!rph) {
    std::move(callback).Run();
    return;
  }

  ObserveEmbedderLifetime(rph);

  view_destruction_callback_map_[embedder_process_id][view_instance_id]
      .push_back(std::move(callback));
}

void GuestViewManager::ObserveEmbedderLifetime(
    RenderProcessHost* embedder_process) {
  if (!embedders_observed_.count(embedder_process->GetID())) {
    embedders_observed_.insert(embedder_process->GetID());
    // EmbedderRenderProcessHostObserver owns itself.
    new EmbedderRenderProcessHostObserver(weak_ptr_factory_.GetWeakPtr(),
                                          embedder_process);
  }
}

bool GuestViewManager::IsGuestAvailableToContext(GuestViewBase* guest) {
  return delegate_->IsGuestAvailableToContext(guest);
}

void GuestViewManager::DispatchEvent(const std::string& event_name,
                                     base::DictValue args,
                                     GuestViewBase* guest,
                                     int instance_id) {
  // TODO(fsamuel): GuestViewManager should probably do something more useful
  // here like log an error if the event could not be dispatched.
  delegate_->DispatchEvent(event_name, std::move(args), guest, instance_id);
}

GuestViewBase* GuestViewManager::GetGuestByInstanceID(int guest_instance_id) {
  auto it = guests_by_instance_id_.find(guest_instance_id);
  return it == guests_by_instance_id_.end() ? nullptr : it->second;
}

bool GuestViewManager::CanEmbedderAccessInstanceIDMaybeKill(
    content::ChildProcessId embedder_render_process_id,
    int guest_instance_id) {
  if (!CanEmbedderAccessInstanceID(embedder_render_process_id,
                                   guest_instance_id)) {
    // The embedder process is trying to access a guest it does not own.
    bad_message::ReceivedBadMessage(
        embedder_render_process_id,
        bad_message::GVM_EMBEDDER_FORBIDDEN_ACCESS_TO_GUEST);
    return false;
  }
  return true;
}

bool GuestViewManager::CanUseGuestInstanceID(int guest_instance_id) {
  if (guest_instance_id <= last_instance_id_removed_)
    return false;
  return !removed_instance_ids_.contains(guest_instance_id);
}

bool GuestViewManager::CanEmbedderAccessInstanceID(
    content::ChildProcessId embedder_render_process_id,
    int guest_instance_id) {
  // TODO(crbug.com/41353094): Remove crash key once the cause of the kill is
  // known.
  static crash_reporter::CrashKeyString<32> bad_access_key("guest-bad-access");

  // The embedder is trying to access a guest with a negative or zero
  // instance ID.
  if (guest_instance_id <= kInstanceIDNone) {
    bad_access_key.Set("Nonpositive");
    return false;
  }

  // The embedder is trying to access an instance ID that has not yet been
  // allocated by GuestViewManager. This could cause instance ID
  // collisions in the future, and potentially give one embedder access to a
  // guest it does not own.
  if (guest_instance_id > current_instance_id_) {
    bad_access_key.Set("Unallocated");
    return false;
  }

  // We might get some late arriving messages at tear down. Let's let the
  // embedder tear down in peace.
  auto* guest_view = GetGuestByInstanceID(guest_instance_id);
  if (!guest_view) {
    return true;
  }

  // MimeHandlerViewGuests (PDF) may be embedded in a cross-process frame.
  // Other than MimeHandlerViewGuest, all other guest types are only permitted
  // to run in the main frame or its local subframes.
  const content::ChildProcessId allowed_embedder_render_process_id =
      guest_view->CanBeEmbeddedInsideCrossProcessFrames()
          ? guest_view->owner_rfh()->GetProcess()->GetID()
          : guest_view->owner_rfh()->GetMainFrame()->GetProcess()->GetID();

  if (embedder_render_process_id != allowed_embedder_render_process_id) {
    bad_access_key.Set("Bad embedder process");
    return false;
  }

  return true;
}

GuestViewManager::ElementInstanceKey::ElementInstanceKey()
    : embedder_process_id(content::ChildProcessHost::kInvalidUniqueID),
      element_instance_id(kInstanceIDNone) {}

GuestViewManager::ElementInstanceKey::ElementInstanceKey(
    content::ChildProcessId embedder_process_id,
    int element_instance_id)
    : embedder_process_id(embedder_process_id),
      element_instance_id(element_instance_id) {}

bool GuestViewManager::ElementInstanceKey::operator<(
    const GuestViewManager::ElementInstanceKey& other) const {
  return std::tie(embedder_process_id, element_instance_id) <
         std::tie(other.embedder_process_id, other.element_instance_id);
}

GuestViewManager::GuestViewData::GuestViewData(
    const GuestViewCreateFunction& create_function,
    const GuestViewCleanUpFunction& cleanup_function)
    : create_function(create_function), cleanup_function(cleanup_function) {}

GuestViewManager::GuestViewData::GuestViewData(const GuestViewData& other) =
    default;

GuestViewManager::GuestViewData::~GuestViewData() = default;

}  // namespace guest_view
