blob: 228f575ea585d4a5c769e355a24ddd22dd78cc76 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef STORAGE_BROWSER_BLOB_BLOB_URL_REGISTRY_H_
#define STORAGE_BROWSER_BLOB_BLOB_URL_REGISTRY_H_
#include <map>
#include "base/component_export.h"
#include "base/functional/callback.h"
#include "base/sequence_checker.h"
#include "base/unguessable_token.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/unique_associated_receiver_set.h"
#include "mojo/public/cpp/bindings/unique_receiver_set.h"
#include "net/base/schemeful_site.h"
#include "storage/browser/blob/blob_storage_constants.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/blob/blob.mojom.h"
#include "third_party/blink/public/mojom/blob/blob_url_store.mojom.h"
#include "third_party/blink/public/mojom/devtools/inspector_issue.mojom.h"
class GURL;
namespace storage {
// This class stores the mapping of blob Urls to blobs.
class COMPONENT_EXPORT(STORAGE_BROWSER) BlobUrlRegistry {
public:
explicit BlobUrlRegistry(base::WeakPtr<BlobUrlRegistry> fallback = nullptr);
BlobUrlRegistry(const BlobUrlRegistry&) = delete;
BlobUrlRegistry& operator=(const BlobUrlRegistry&) = delete;
~BlobUrlRegistry();
enum class MappingStatus {
kIsMapped,
// TODO(crbug.com/387655548): Remove this case once there's sufficient data
// from the CrossPartitionSameOriginBlobURLFetch UseCounter. Currently, this
// case is treated separately because cross-origin Blob URL access is
// already blocked and shouldn't be measured w.r.t. deciding whether it's
// safe to restrict further based on storage partition. Once
// CrossPartitionSameOriginBlobURLFetch is removed, it'd be
// beneficial to show the DevTools Issue even in the cross-origin access
// case and simplify IsUrlMapped to return a bool.
kNotMappedCrossPartitionSameOrigin,
kNotMappedOther
};
// Binds receivers corresponding to connections from renderer frame
// contexts and stores them in `frame_receivers_`.
// `partitioning_blob_url_closure` runs when the storage_key check fails
// in `BlobURLStoreImpl::ResolveAsURLLoaderFactory` and increments the use
// counter.
void AddReceiver(
const blink::StorageKey& storage_key,
const url::Origin& renderer_origin,
int render_process_host_id,
mojo::PendingAssociatedReceiver<blink::mojom::BlobURLStore> receiver,
base::RepeatingCallback<
void(const GURL&,
std::optional<blink::mojom::PartitioningBlobURLInfo>)>
partitioning_blob_url_closure,
bool partitioning_disabled_by_policy = false);
// Binds receivers corresponding to connections from renderer worker
// contexts and stores them in `worker_receivers_`.
void AddReceiver(const blink::StorageKey& storage_key,
const url::Origin& renderer_origin,
int render_process_host_id,
mojo::PendingReceiver<blink::mojom::BlobURLStore> receiver,
bool partitioning_disabled_by_policy = false,
BlobURLValidityCheckBehavior validity_check_behavior =
BlobURLValidityCheckBehavior::DEFAULT);
// Returns the receivers corresponding to renderer frame contexts for use in
// tests.
auto& receivers_for_testing() { return frame_receivers_; }
// Creates a URL mapping from blob to the given URL. Returns false if
// there already is a map for the URL. The URL mapping will be associated with
// the `storage_key`, and most subsequent URL lookup attempts will require a
// matching StorageKey to succeed. `origin` is the origin of the Blob URL, and
// `render_process_host_id` is the ID of the process where the blob URL
// registration comes from.
bool AddUrlMapping(
const GURL& url,
mojo::PendingRemote<blink::mojom::Blob> blob,
const blink::StorageKey& storage_key,
const url::Origin& renderer_origin,
int render_process_host_id,
// TODO(crbug.com/40775506): Remove these once experiment is over.
const base::UnguessableToken& unsafe_agent_cluster_id,
const std::optional<net::SchemefulSite>& unsafe_top_level_site);
// Removes the given URL mapping associated with `storage_key`. Returns false
// if the URL wasn't mapped.
bool RemoveUrlMapping(const GURL& url, const blink::StorageKey& storage_key);
// Returns whether the URL is mapped to a blob and whether the URL is
// associated with `storage_key`.
MappingStatus IsUrlMapped(const GURL& blob_url,
const blink::StorageKey& storage_key) const;
// TODO(crbug.com/40775506): Remove this once experiment is over.
std::optional<base::UnguessableToken> GetUnsafeAgentClusterID(
const GURL& blob_url) const;
std::optional<net::SchemefulSite> GetUnsafeTopLevelSite(
const GURL& blob_url) const;
// Returns the blob from the given url. Returns a null remote if the mapping
// doesn't exist.
mojo::PendingRemote<blink::mojom::Blob> GetBlobFromUrl(const GURL& url);
size_t url_count() const { return url_to_blob_.size(); }
void AddTokenMapping(const base::UnguessableToken& token,
const GURL& url,
mojo::PendingRemote<blink::mojom::Blob> blob);
void RemoveTokenMapping(const base::UnguessableToken& token);
bool GetTokenMapping(const base::UnguessableToken& token,
GURL* url,
mojo::PendingRemote<blink::mojom::Blob>* blob);
// Returns the origin for a Blob URL navigation to `url`, given the precursor
// origin and target process information.
url::Origin GetOriginForNavigation(
const GURL& url,
const url::Origin& precursor_origin,
std::optional<int> target_render_process_host_id);
// Support adding a handler to be run when AddReceiver is called. This allows
// browser tests to intercept incoming BlobURLStore connections and swap in
// arbitrary BlobURLs to ensure that attempting to register certain blobs
// causes the renderer to be terminated.
using URLStoreCreationHook =
base::RepeatingCallback<void(BlobUrlRegistry*, mojo::ReceiverId)>;
static void SetURLStoreCreationHookForTesting(URLStoreCreationHook* hook);
base::WeakPtr<BlobUrlRegistry> AsWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
private:
SEQUENCE_CHECKER(sequence_checker_);
// Optional fallback BlobUrlRegistry. If lookups for URLs in this registry
// fail, they are retried in the fallback registry. This is used to allow
// "child" storage partitions to resolve URLs created by their "parent", while
// not allowing the reverse.
base::WeakPtr<BlobUrlRegistry> fallback_;
std::map<GURL, mojo::PendingRemote<blink::mojom::Blob>> url_to_blob_;
// TODO(crbug.com/40775506): Remove this once experiment is over.
std::map<GURL, base::UnguessableToken> url_to_unsafe_agent_cluster_id_;
std::map<GURL, net::SchemefulSite> url_to_unsafe_top_level_site_;
std::map<base::UnguessableToken,
std::pair<GURL, mojo::PendingRemote<blink::mojom::Blob>>>
token_to_url_and_blob_;
std::map<GURL, blink::StorageKey> url_to_storage_key_;
std::map<GURL, url::Origin> url_to_origin_;
std::map<GURL, int> url_to_render_process_host_id_;
// When the renderer uses the BlobUrlRegistry from a frame context or from a
// main thread worklet context, a navigation-associated interface is used to
// preserve message ordering. The receiver corresponding to that connection is
// an AssociatedReceiver and gets stored in `frame_receivers_`. For workers
// and threaded worklets, the receiver is a Receiver and gets stored in
// `worker_receivers_`.
mojo::UniqueAssociatedReceiverSet<blink::mojom::BlobURLStore>
frame_receivers_;
mojo::UniqueReceiverSet<blink::mojom::BlobURLStore> worker_receivers_;
base::WeakPtrFactory<BlobUrlRegistry> weak_ptr_factory_{this};
};
} // namespace storage
#endif // STORAGE_BROWSER_BLOB_BLOB_URL_REGISTRY_H_