| // Copyright 2018 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/renderer_host/code_cache_host_impl.h" |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/task/post_task.h" |
| #include "base/threading/thread.h" |
| #include "build/build_config.h" |
| #include "content/browser/cache_storage/cache_storage_cache.h" |
| #include "content/browser/cache_storage/cache_storage_cache_handle.h" |
| #include "content/browser/cache_storage/cache_storage_context_impl.h" |
| #include "content/browser/cache_storage/cache_storage_manager.h" |
| #include "content/browser/child_process_security_policy_impl.h" |
| #include "content/browser/code_cache/generated_code_cache.h" |
| #include "content/browser/code_cache/generated_code_cache_context.h" |
| #include "content/browser/renderer_host/render_process_host_impl.h" |
| #include "content/browser/storage_partition_impl.h" |
| #include "content/public/browser/resource_context.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/common/content_features.h" |
| #include "mojo/public/cpp/bindings/strong_binding.h" |
| #include "net/base/io_buffer.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| |
| using blink::mojom::CacheStorageError; |
| |
| namespace content { |
| |
| namespace { |
| |
| void NoOpCacheStorageErrorCallback(CacheStorageCacheHandle cache_handle, |
| CacheStorageError error) {} |
| |
| base::Optional<url::Origin> GetRendererOrigin(const GURL& url, |
| int render_process_id) { |
| GURL requesting_url = |
| ChildProcessSecurityPolicyImpl::GetInstance()->GetOriginLock( |
| render_process_id); |
| |
| if (!requesting_url.is_valid() || !url.is_valid()) |
| return base::nullopt; |
| |
| url::Origin origin = url::Origin::Create(requesting_url); |
| |
| // Don't cache the code corresponding to unique origins. The same-origin |
| // checks should always fail for unique origins but the serialized value of |
| // unique origins does not ensure this. |
| if (origin.unique()) |
| return base::nullopt; |
| |
| return origin; |
| } |
| |
| } // namespace |
| |
| CodeCacheHostImpl::CodeCacheHostImpl( |
| int render_process_id, |
| scoped_refptr<CacheStorageContextImpl> cache_storage_context, |
| scoped_refptr<GeneratedCodeCacheContext> generated_code_cache_context) |
| : render_process_id_(render_process_id), |
| cache_storage_context_(std::move(cache_storage_context)), |
| generated_code_cache_context_(std::move(generated_code_cache_context)), |
| weak_ptr_factory_(this) {} |
| |
| CodeCacheHostImpl::~CodeCacheHostImpl() { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| } |
| |
| // static |
| void CodeCacheHostImpl::Create( |
| int render_process_id, |
| scoped_refptr<CacheStorageContextImpl> cache_storage_context, |
| scoped_refptr<GeneratedCodeCacheContext> generated_code_cache_context, |
| blink::mojom::CodeCacheHostRequest request) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| mojo::MakeStrongBinding( |
| std::make_unique<CodeCacheHostImpl>( |
| render_process_id, std::move(cache_storage_context), |
| std::move(generated_code_cache_context)), |
| std::move(request)); |
| } |
| |
| void CodeCacheHostImpl::DidGenerateCacheableMetadata( |
| blink::mojom::CodeCacheType cache_type, |
| const GURL& url, |
| base::Time expected_response_time, |
| const std::vector<uint8_t>& data) { |
| if (!url.SchemeIsHTTPOrHTTPS()) { |
| mojo::ReportBadMessage("Invalid URL scheme for code cache."); |
| return; |
| } |
| |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| if (!base::FeatureList::IsEnabled(features::kIsolatedCodeCache)) { |
| // Only store Javascript (not WebAssembly) code in the single-keyed cache. |
| if (cache_type == blink::mojom::CodeCacheType::kJavascript) { |
| base::PostTaskWithTraits( |
| FROM_HERE, {BrowserThread::UI}, |
| base::BindOnce(&CodeCacheHostImpl::DidGenerateCacheableMetadataOnUI, |
| render_process_id_, url, expected_response_time, |
| data)); |
| } else { |
| mojo::ReportBadMessage("Single-keyed code cache is Javascript only."); |
| return; |
| } |
| } else { |
| GeneratedCodeCache* code_cache = GetCodeCache(cache_type); |
| if (!code_cache) |
| return; |
| |
| base::Optional<url::Origin> requesting_origin = |
| GetRendererOrigin(url, render_process_id_); |
| if (!requesting_origin) |
| return; |
| |
| code_cache->WriteData(url, *requesting_origin, expected_response_time, |
| data); |
| } |
| } |
| |
| void CodeCacheHostImpl::FetchCachedCode(blink::mojom::CodeCacheType cache_type, |
| const GURL& url, |
| FetchCachedCodeCallback callback) { |
| GeneratedCodeCache* code_cache = GetCodeCache(cache_type); |
| if (!code_cache) { |
| std::move(callback).Run(base::Time(), std::vector<uint8_t>()); |
| return; |
| } |
| |
| base::Optional<url::Origin> requesting_origin = |
| GetRendererOrigin(url, render_process_id_); |
| if (!requesting_origin) { |
| std::move(callback).Run(base::Time(), std::vector<uint8_t>()); |
| return; |
| } |
| |
| auto read_callback = base::BindRepeating( |
| &CodeCacheHostImpl::OnReceiveCachedCode, weak_ptr_factory_.GetWeakPtr(), |
| base::Passed(&callback)); |
| code_cache->FetchEntry(url, *requesting_origin, read_callback); |
| } |
| |
| void CodeCacheHostImpl::ClearCodeCacheEntry( |
| blink::mojom::CodeCacheType cache_type, |
| const GURL& url) { |
| GeneratedCodeCache* code_cache = GetCodeCache(cache_type); |
| if (!code_cache) |
| return; |
| |
| base::Optional<url::Origin> requesting_origin = |
| GetRendererOrigin(url, render_process_id_); |
| if (!requesting_origin) |
| return; |
| |
| code_cache->DeleteEntry(url, *requesting_origin); |
| } |
| |
| void CodeCacheHostImpl::DidGenerateCacheableMetadataInCacheStorage( |
| const GURL& url, |
| base::Time expected_response_time, |
| const std::vector<uint8_t>& data, |
| const url::Origin& cache_storage_origin, |
| const std::string& cache_storage_cache_name) { |
| scoped_refptr<net::IOBuffer> buf = |
| base::MakeRefCounted<net::IOBuffer>(data.size()); |
| if (!data.empty()) |
| memcpy(buf->data(), &data.front(), data.size()); |
| |
| cache_storage_context_->cache_manager()->OpenCache( |
| cache_storage_origin, CacheStorageOwner::kCacheAPI, |
| cache_storage_cache_name, |
| base::BindOnce(&CodeCacheHostImpl::OnCacheStorageOpenCallback, |
| weak_ptr_factory_.GetWeakPtr(), url, |
| expected_response_time, buf, data.size())); |
| } |
| |
| GeneratedCodeCache* CodeCacheHostImpl::GetCodeCache( |
| blink::mojom::CodeCacheType cache_type) { |
| if (!generated_code_cache_context_) |
| return nullptr; |
| |
| if (cache_type == blink::mojom::CodeCacheType::kJavascript) |
| return generated_code_cache_context_->generated_js_code_cache(); |
| |
| DCHECK_EQ(blink::mojom::CodeCacheType::kWebAssembly, cache_type); |
| return generated_code_cache_context_->generated_wasm_code_cache(); |
| } |
| |
| void CodeCacheHostImpl::OnReceiveCachedCode(FetchCachedCodeCallback callback, |
| const base::Time& response_time, |
| const std::vector<uint8_t>& data) { |
| // TODO(crbug.com/867848): Pass the data as a mojo data pipe instead |
| // of vector<uint8> |
| std::move(callback).Run(response_time, data); |
| } |
| |
| void CodeCacheHostImpl::OnCacheStorageOpenCallback( |
| const GURL& url, |
| base::Time expected_response_time, |
| scoped_refptr<net::IOBuffer> buf, |
| int buf_len, |
| CacheStorageCacheHandle cache_handle, |
| CacheStorageError error) { |
| if (error != CacheStorageError::kSuccess || !cache_handle.value()) |
| return; |
| CacheStorageCache* cache = cache_handle.value(); |
| cache->WriteSideData( |
| base::BindOnce(&NoOpCacheStorageErrorCallback, std::move(cache_handle)), |
| url, expected_response_time, buf, buf_len); |
| } |
| |
| // static |
| void CodeCacheHostImpl::DidGenerateCacheableMetadataOnUI( |
| int render_process_id, |
| const GURL& url, |
| base::Time expected_response_time, |
| const std::vector<uint8_t>& data) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| RenderProcessHost* host = RenderProcessHost::FromID(render_process_id); |
| if (!host) |
| return; |
| |
| // Use the same priority for the metadata write as for script |
| // resources (see defaultPriorityForResourceType() in WebKit's |
| // CachedResource.cpp). Note that WebURLRequest::PriorityMedium |
| // corresponds to net::LOW (see ConvertWebKitPriorityToNetPriority() |
| // in weburlloader_impl.cc). |
| const net::RequestPriority kPriority = net::LOW; |
| host->GetStoragePartition()->GetNetworkContext()->WriteCacheMetadata( |
| url, kPriority, expected_response_time, data); |
| } |
| |
| } // namespace content |