| // 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 "content/browser/font_access/font_access_manager_impl.h" |
| |
| #include <limits> |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/memory/read_only_shared_memory_region.h" |
| #include "base/sequence_checker.h" |
| #include "base/task/post_task.h" |
| #include "base/task/thread_pool.h" |
| #include "base/types/pass_key.h" |
| #include "content/browser/font_access/font_enumeration_cache.h" |
| #include "content/browser/permissions/permission_controller_impl.h" |
| #include "content/browser/renderer_host/frame_tree_node.h" |
| #include "content/browser/renderer_host/render_frame_host_impl.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/font_access_delegate.h" |
| #include "content/public/browser/global_routing_id.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/common/content_client.h" |
| #include "third_party/blink/public/common/features.h" |
| #include "third_party/blink/public/common/font_access/font_enumeration_table.pb.h" |
| #include "third_party/blink/public/mojom/frame/lifecycle.mojom-shared.h" |
| #include "third_party/blink/public/mojom/permissions/permission_status.mojom-shared.h" |
| |
| namespace content { |
| |
| // static |
| std::unique_ptr<FontAccessManagerImpl> FontAccessManagerImpl::Create() { |
| return std::make_unique<FontAccessManagerImpl>( |
| FontEnumerationCache::Create(), base::PassKey<FontAccessManagerImpl>()); |
| } |
| |
| // static |
| std::unique_ptr<FontAccessManagerImpl> FontAccessManagerImpl::CreateForTesting( |
| base::SequenceBound<FontEnumerationCache> font_enumeration_cache) { |
| return std::make_unique<FontAccessManagerImpl>( |
| std::move(font_enumeration_cache), |
| base::PassKey<FontAccessManagerImpl>()); |
| } |
| |
| FontAccessManagerImpl::FontAccessManagerImpl( |
| base::SequenceBound<FontEnumerationCache> font_enumeration_cache, |
| base::PassKey<FontAccessManagerImpl>) |
| : font_enumeration_cache_(std::move(font_enumeration_cache)), |
| results_task_runner_(content::GetUIThreadTaskRunner({})) {} |
| |
| FontAccessManagerImpl::~FontAccessManagerImpl() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| } |
| |
| void FontAccessManagerImpl::BindReceiver( |
| url::Origin origin, |
| GlobalRenderFrameHostId frame_id, |
| mojo::PendingReceiver<blink::mojom::FontAccessManager> receiver) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| receivers_.Add(this, std::move(receiver), |
| { |
| .origin = std::move(origin), |
| .frame_id = frame_id, |
| }); |
| } |
| |
| void FontAccessManagerImpl::EnumerateLocalFonts( |
| EnumerateLocalFontsCallback callback) { |
| DCHECK(base::FeatureList::IsEnabled(blink::features::kFontAccess)); |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| #if defined(PLATFORM_HAS_LOCAL_FONT_ENUMERATION_IMPL) |
| if (skip_privacy_checks_for_testing_) { |
| DidRequestPermission(std::move(callback), |
| blink::mojom::PermissionStatus::GRANTED); |
| return; |
| } |
| |
| const BindingContext& context = receivers_.current_context(); |
| |
| RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(context.frame_id); |
| if (rfh == nullptr) { |
| std::move(callback).Run( |
| blink::mojom::FontEnumerationStatus::kUnexpectedError, |
| base::ReadOnlySharedMemoryRegion()); |
| return; |
| } |
| |
| // Page Visibility is required for the API to function at all. |
| if (rfh->visibility() == blink::mojom::FrameVisibility::kNotRendered) { |
| std::move(callback).Run(blink::mojom::FontEnumerationStatus::kNotVisible, |
| base::ReadOnlySharedMemoryRegion()); |
| return; |
| } |
| |
| auto status = PermissionControllerImpl::FromBrowserContext( |
| rfh->GetProcess()->GetBrowserContext()) |
| ->GetPermissionStatusForFrame(PermissionType::FONT_ACCESS, |
| rfh, context.origin.GetURL()); |
| |
| if (status != blink::mojom::PermissionStatus::ASK) { |
| // Permission has been requested before. |
| DidRequestPermission(std::move(callback), std::move(status)); |
| return; |
| } |
| |
| // Transient User Activation only required before showing permission prompt. |
| // This action will consume it. |
| if (!rfh->HasTransientUserActivation()) { |
| std::move(callback).Run( |
| blink::mojom::FontEnumerationStatus::kNeedsUserActivation, |
| base::ReadOnlySharedMemoryRegion()); |
| return; |
| } |
| rfh->frame_tree_node()->UpdateUserActivationState( |
| blink::mojom::UserActivationUpdateType::kConsumeTransientActivation, |
| blink::mojom::UserActivationNotificationType::kNone); |
| |
| PermissionControllerImpl::FromBrowserContext( |
| rfh->GetProcess()->GetBrowserContext()) |
| ->RequestPermission( |
| PermissionType::FONT_ACCESS, rfh, context.origin.GetURL(), |
| /*user_gesture=*/true, |
| base::BindOnce(&FontAccessManagerImpl::DidRequestPermission, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback))); |
| #else |
| std::move(callback).Run(blink::mojom::FontEnumerationStatus::kUnimplemented, |
| base::ReadOnlySharedMemoryRegion()); |
| #endif |
| } |
| |
| void FontAccessManagerImpl::ChooseLocalFonts( |
| const std::vector<std::string>& selection, |
| ChooseLocalFontsCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| #if !defined(PLATFORM_HAS_LOCAL_FONT_ENUMERATION_IMPL) |
| std::move(callback).Run(blink::mojom::FontEnumerationStatus::kUnimplemented, |
| {}); |
| #else |
| const BindingContext& context = receivers_.current_context(); |
| |
| RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(context.frame_id); |
| if (rfh == nullptr) { |
| std::move(callback).Run( |
| blink::mojom::FontEnumerationStatus::kUnexpectedError, {}); |
| return; |
| } |
| |
| // Page Visibility is required for the API to function at all. |
| if (rfh->visibility() == blink::mojom::FrameVisibility::kNotRendered) { |
| std::move(callback).Run(blink::mojom::FontEnumerationStatus::kNotVisible, |
| {}); |
| return; |
| } |
| |
| // Transient User Activation required before showing the chooser. |
| // This action will consume it. |
| if (!rfh->HasTransientUserActivation()) { |
| std::move(callback).Run( |
| blink::mojom::FontEnumerationStatus::kNeedsUserActivation, {}); |
| return; |
| } |
| rfh->frame_tree_node()->UpdateUserActivationState( |
| blink::mojom::UserActivationUpdateType::kConsumeTransientActivation, |
| blink::mojom::UserActivationNotificationType::kNone); |
| |
| FontAccessDelegate* delegate = |
| GetContentClient()->browser()->GetFontAccessDelegate(); |
| // TODO(pwnall): It may be possible to replace the WeakPtr below with |
| // base::Unretained(), if the FontAccessChooser guarantees that |
| // the callback isn't run after the chooser is destroyed. |
| choosers_[context.frame_id] = delegate->RunChooser( |
| rfh, selection, |
| base::BindOnce(&FontAccessManagerImpl::DidChooseLocalFonts, |
| weak_ptr_factory_.GetWeakPtr(), context.frame_id, |
| std::move(callback))); |
| #endif |
| } |
| |
| void FontAccessManagerImpl::FindAllFonts(FindAllFontsCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| #if !defined(PLATFORM_HAS_LOCAL_FONT_ENUMERATION_IMPL) |
| std::move(callback).Run(blink::mojom::FontEnumerationStatus::kUnimplemented, |
| {}); |
| #else |
| font_enumeration_cache_ |
| .AsyncCall(&FontEnumerationCache::GetFontEnumerationData) |
| .Then(base::BindOnce(&FontAccessManagerImpl::DidFindAllFonts, |
| weak_ptr_factory_.GetWeakPtr(), |
| std::move(callback))); |
| #endif |
| } |
| |
| void FontAccessManagerImpl::DidRequestPermission( |
| EnumerateLocalFontsCallback callback, |
| blink::mojom::PermissionStatus status) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| #if !defined(PLATFORM_HAS_LOCAL_FONT_ENUMERATION_IMPL) |
| std::move(callback).Run(blink::mojom::FontEnumerationStatus::kUnimplemented, |
| base::ReadOnlySharedMemoryRegion()); |
| return; |
| #else |
| if (status != blink::mojom::PermissionStatus::GRANTED) { |
| std::move(callback).Run( |
| blink::mojom::FontEnumerationStatus::kPermissionDenied, |
| base::ReadOnlySharedMemoryRegion()); |
| return; |
| } |
| |
| // Per-platform delegation for obtaining cached font enumeration data occurs |
| // here, after the permission has been granted. |
| font_enumeration_cache_ |
| .AsyncCall(&FontEnumerationCache::GetFontEnumerationData) |
| .Then(base::BindOnce( |
| [](EnumerateLocalFontsCallback callback, FontEnumerationData data) { |
| std::move(callback).Run(data.status, std::move(data.font_data)); |
| }, |
| std::move(callback))); |
| #endif |
| } |
| |
| void FontAccessManagerImpl::DidFindAllFonts(FindAllFontsCallback callback, |
| FontEnumerationData data) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (data.status != blink::mojom::FontEnumerationStatus::kOk) { |
| std::move(callback).Run(data.status, {}); |
| return; |
| } |
| |
| const base::ReadOnlySharedMemoryMapping mapping = data.font_data.Map(); |
| if (mapping.size() > std::numeric_limits<int>::max()) { |
| std::move(callback).Run( |
| blink::mojom::FontEnumerationStatus::kUnexpectedError, {}); |
| return; |
| } |
| |
| blink::FontEnumerationTable table; |
| table.ParseFromArray(mapping.memory(), static_cast<int>(mapping.size())); |
| |
| std::vector<blink::mojom::FontMetadata> font_data; |
| for (const auto& element : table.fonts()) { |
| font_data.emplace_back(element.postscript_name(), element.full_name(), |
| element.family(), element.style(), element.italic(), |
| element.stretch(), element.weight()); |
| } |
| |
| std::move(callback).Run(data.status, std::move(font_data)); |
| } |
| |
| void FontAccessManagerImpl::DidChooseLocalFonts( |
| GlobalRenderFrameHostId frame_id, |
| ChooseLocalFontsCallback callback, |
| blink::mojom::FontEnumerationStatus status, |
| std::vector<blink::mojom::FontMetadataPtr> fonts) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| // The chooser has fulfilled its purpose. It's safe to dispose of it. |
| size_t erased = choosers_.erase(frame_id); |
| DCHECK_EQ(erased, 1u); |
| |
| std::move(callback).Run(std::move(status), std::move(fonts)); |
| } |
| |
| } // namespace content |