// Copyright (c) 2026 Anthropic, PBC. // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. #ifndef ELECTRON_SHELL_BROWSER_PRELOAD_CODE_CACHE_H_ #define ELECTRON_SHELL_BROWSER_PRELOAD_CODE_CACHE_H_ #include #include #include #include #include "base/containers/span.h" #include "base/files/file_path.h" #include "crypto/hash.h" namespace content { class RenderFrameHost; } namespace electron::preload_code_cache { // sha256 of a preload script's source bytes. using SourceHash = std::array; // One preload served to a frame: its cache id and the sha256 of the exact // bytes the browser served. using ServedPreload = std::pair; // Cache id for a webPreferences.preload script (stable across launches). std::string IdForWebPreferencesPreload(const base::FilePath& path); // Who a cache entry belongs to: the frame's BrowserContext (its data dir, // under which the disk tier lives) plus its renderer's process lock (site, // origin keying, sandboxing, guest and cross-origin-isolation status, // storage partition). Entries are only ever served back within an equal // scope, so a blob produced by one principal's renderer is never handed to // another. Scope is the security boundary for cached blobs; see the note in // SetFromRenderer(). |dir| is empty for off-the-record contexts (memory tier // only). struct Scope { Scope(); Scope(const Scope&); Scope(Scope&&); ~Scope(); base::FilePath dir; std::string context_id; std::string process_lock; }; Scope ScopeForFrame(content::RenderFrameHost* rfh); // Returns the V8 code cache blob recorded for |id| within |scope| and bound // to source bytes whose sha256 is |source_hash| — or empty. Checks an // in-memory tier first, then |scope.dir|. Disk I/O is synchronous (call behind // a ScopedAllowBlocking) and happens at most once per (scope, id) per session. // // Entries are bound to the source hash: unless its optional SHA256 source // hashing is enabled, V8's CachedData source check hashes only the source // *length*, so without this a same-length source change would execute stale // bytecode. V8 still validates version/flags at consume time. std::vector Get(const Scope& scope, const std::string& id, const SourceHash& source_hash); // Records the preloads — id plus the sha256 of the exact bytes — that the // browser served to |rfh| in a RendererStartupData push, replacing any // previous record for that frame. SetFromRenderer() only accepts cache // writes for tuples recorded here: the browser, not the renderer, is the // authority for which source bytes a cache entry validates against. The // record's lifetime is tied to the frame (cleared when the frame or its // WebContents goes away). void RecordServedPreloads(content::RenderFrameHost* rfh, std::vector served); // Stores a code cache blob reported by |rfh|'s renderer for a preload that // was served to it. The write is dropped unless RecordServedPreloads() // recorded the id for this exact frame and |claimed_source_hash| equals the // browser-recorded hash of the served bytes. The claimed hash is only a // cross-check (it detects a write racing a newer push after the preload // changed on disk); what is persisted is the browser-recorded hash, keyed by // the frame's Scope — the renderer never supplies the metadata that future // loads validate against. The in-memory tier is written immediately; the // disk write is fire-and-forget on the thread pool. void SetFromRenderer(content::RenderFrameHost* rfh, const std::string& id, base::span claimed_source_hash, base::span blob); } // namespace electron::preload_code_cache #endif // ELECTRON_SHELL_BROWSER_PRELOAD_CODE_CACHE_H_