| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/renderer_host/document_associated_data.h" |
| |
| #include <utility> |
| |
| #include "base/check.h" |
| #include "base/check_op.h" |
| #include "base/containers/map_util.h" |
| #include "base/containers/queue.h" |
| #include "base/functional/callback_forward.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/no_destructor.h" |
| #include "content/browser/navigation_or_document_handle.h" |
| #include "content/browser/renderer_host/frame_tree.h" |
| #include "content/browser/renderer_host/page_factory.h" |
| #include "content/browser/renderer_host/render_frame_host_impl.h" |
| #include "content/public/browser/document_service.h" |
| #include "content/public/browser/document_service_internal.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "net/cookies/cookie_setting_override.h" |
| #include "third_party/abseil-cpp/absl/container/flat_hash_map.h" |
| #include "third_party/blink/public/common/tokens/tokens.h" |
| |
| namespace content { |
| |
| namespace { |
| auto& GetDocumentTokenMap() { |
| static base::NoDestructor< |
| absl::flat_hash_map<blink::DocumentToken, RenderFrameHostImpl*>> |
| map; |
| return *map; |
| } |
| } // namespace |
| |
| RenderFrameHostImpl* DocumentAssociatedData::GetDocumentFromToken( |
| base::PassKey<RenderFrameHostImpl>, |
| const blink::DocumentToken& token) { |
| return base::FindPtrOrNull(GetDocumentTokenMap(), token); |
| } |
| |
| DocumentAssociatedData::DocumentAssociatedData( |
| RenderFrameHostImpl& document, |
| const blink::DocumentToken& token) |
| : token_(token), weak_factory_(&document) { |
| auto [_, inserted] = GetDocumentTokenMap().insert({token_, &document}); |
| CHECK(inserted); |
| |
| // Only create page object for the main document as the PageImpl is 1:1 with |
| // main document. |
| if (!document.GetParent()) { |
| PageDelegate* page_delegate = document.frame_tree()->page_delegate(); |
| DCHECK(page_delegate); |
| owned_page_ = PageFactory::Create(document, *page_delegate); |
| } |
| } |
| |
| DocumentAssociatedData::~DocumentAssociatedData() { |
| TRACE_EVENT0("navigation", "DocumentAssociatedData::~DocumentAssociatedData"); |
| base::ScopedUmaHistogramTimer histogram_timer( |
| "Navigation.DocumentAssociatedDataDestructor"); |
| decltype(services_) services; |
| std::swap(services_, services); |
| for (auto& service : services) { |
| service->WillBeDestroyed( |
| DocumentServiceDestructionReason::kEndOfDocumentLifetime); |
| service->ResetAndDeleteThisInternal({}); |
| } |
| |
| // Explicitly clear all user data here, so that the other fields of |
| // DocumentAssociatedData are still valid while user data is being destroyed. |
| ClearAllUserData(); |
| |
| // Explicitly clear all PageUserData here before destruction of |owned_page_| |
| // (A std::unique_ptr's stored pointer value is (intentionally) undefined |
| // during destruction (e.g. it could be nullptr)), so that |owned_page_| and |
| // the other fields of DocumentAssociatedData are still valid and accessible |
| // from RenderFrameHost interface while its page user data is being destroyed. |
| if (owned_page_) { |
| owned_page_->ClearAllUserData(); |
| } |
| |
| // Last in case any DocumentService / DocumentUserData service destructors try |
| // to look up RenderFrameHosts by DocumentToken. |
| CHECK_EQ(1u, GetDocumentTokenMap().erase(token_)); |
| } |
| |
| void DocumentAssociatedData::set_navigation_or_document_handle( |
| scoped_refptr<NavigationOrDocumentHandle> handle) { |
| navigation_or_document_handle_ = std::move(handle); |
| } |
| |
| void DocumentAssociatedData::AddService( |
| internal::DocumentServiceBase* service, |
| base::PassKey<internal::DocumentServiceBase>) { |
| services_.push_back(service); |
| } |
| |
| void DocumentAssociatedData::RemoveService( |
| internal::DocumentServiceBase* service, |
| base::PassKey<internal::DocumentServiceBase>) { |
| std::erase(services_, service); |
| } |
| |
| void DocumentAssociatedData::AddPostPrerenderingActivationStep( |
| base::OnceClosure callback) { |
| CHECK_EQ(GetSafeRef()->GetLifecycleState(), |
| RenderFrameHost::LifecycleState::kPrerendering); |
| post_prerendering_activation_callbacks_.push(std::move(callback)); |
| } |
| |
| void DocumentAssociatedData::RunPostPrerenderingActivationSteps() { |
| CHECK_NE(GetSafeRef()->GetLifecycleState(), |
| RenderFrameHost::LifecycleState::kPrerendering); |
| while (!post_prerendering_activation_callbacks_.empty()) { |
| std::move(post_prerendering_activation_callbacks_.front()).Run(); |
| post_prerendering_activation_callbacks_.pop(); |
| } |
| } |
| |
| void DocumentAssociatedData::PutCookieSettingOverride( |
| net::CookieSettingOverride cookie_setting_override) { |
| cookie_setting_overrides_.Put(cookie_setting_override); |
| } |
| |
| void DocumentAssociatedData::RemoveCookieSettingOverride( |
| net::CookieSettingOverride cookie_setting_override) { |
| cookie_setting_overrides_.Remove(cookie_setting_override); |
| } |
| |
| } // namespace content |