| // 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/containers/contains.h" |
| #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/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::Value::Dict& 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 (base::Contains(instance_id_map_, 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); |
| } |
| |
| void GuestViewManager::AttachGuest(int embedder_process_id, |
| int element_instance_id, |
| int guest_instance_id, |
| const base::Value::Dict& attach_params) { |
| GuestViewManager::AttachGuest(content::ChildProcessId(embedder_process_id), |
| element_instance_id, guest_instance_id, |
| 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::Value::Dict& 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::Value::Dict& 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::Value::Dict guest_params = base::Value::Dict(); |
| 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()->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 (base::Contains(guest_view_registry_, 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::Value::Dict 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 !base::Contains(removed_instance_ids_, 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 |