src/vs/platform/browserView/common/browserHistory.ts

364 LOC · 350 covered · 14 uncovered · 97 ranges · 58 concepts · 43 introducers · 26 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the filebrowserHistory.test|title=BrowserHistoryStore handle.update propagates to entry and registers the favicon|occurrence=1 · 0 introduced LOCbrowserHistory.test|titl…browserHistory.test|title=BrowserHistoryStore handle.update with explicit `favicon: null` clears the entry icon|occurrence=1 · 0 introduced LOCbrowserHistory.test|titl…browserHistory.test|title=BrowserHistoryStore onDidChange fires for changes in either sub-store|occurrence=1 · 0 introduced LOCbrowserHistory.test|titl…browserHistory.ts ×1 · 3 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×1 · 2 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×4 · 6 introduced LOCbrowserHistory.ts ×4browserHistory.test|title=BrowserHistoryStore handle.delete removes the entry and GCs the orphaned favicon|occurrence=1 · 0 introduced LOCbrowserHistory.test|titl…browserHistory.ts ×1 · 2 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×3 · 7 introduced LOCbrowserHistory.ts ×3browserHistory.ts ×1 · 2 introduced LOCbrowserHistory.ts ×1browserHistory.test|title=BrowserFaviconsStore gc drops orphans and fires onDidChange only when something changes|occurrence=1 · 0 introduced LOCbrowserHistory.test|titl…browserHistory.ts ×2 · 5 introduced LOCbrowserHistory.ts ×2browserHistory.ts ×2 · 5 introduced LOCbrowserHistory.ts ×2browserHistory.ts ×1 · 2 introduced LOCbrowserHistory.ts ×1browserHistory.test|title=BrowserFaviconsStore onDidChange fires only when a new favicon is added|occurrence=1 · 0 introduced LOCbrowserHistory.test|titl…browserHistory.ts ×1 · 2 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×6 · 14 introduced LOCbrowserHistory.ts ×6browserHistory.ts ×2 · 5 introduced LOCbrowserHistory.ts ×2browserHistory.ts ×1 · 3 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×2 · 2 introduced LOCbrowserHistory.ts ×2browserHistory.test|title=BrowserFaviconsStore serialize then hydrate round-trips|occurrence=1 · 0 introduced LOCbrowserHistory.test|titl…browserHistory.ts ×5 · 15 introduced LOCbrowserHistory.ts ×5browserHistory.test|title=BrowserFaviconsStore register dedups by content and returns the same hash|occurrence=1 · 0 introduced LOCbrowserHistory.test|titl…browserHistory.ts ×1 · 9 introduced LOCbrowserHistory.ts ×1browserHistory.test|title=BrowserHistoryEntriesStore update changes title and icon, returns whether anything changed|occurrence=1 · 0 introduced LOCbrowserHistory.test|titl…browserHistory.ts ×1 · 2 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×1 · 1 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×1 · 1 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×1 · 2 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×1 · 2 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×1 · 3 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×1 · 1 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×1 · 2 introduced LOCbrowserHistory.ts ×1browserHistory.test|title=BrowserHistoryEntriesStore.hydrate backwards-compat accepts data matching prior snapshot shapes|occurrence=1 · 0 introduced LOCbrowserHistory.test|titl…browserHistory.ts ×4 · 10 introduced LOCbrowserHistory.ts ×4browserHistory.test|title=BrowserHistoryEntriesStore hydrate seeds the id counter from the max restored id|occurrence=1 · 0 introduced LOCbrowserHistory.test|titl…browserHistory.ts ×1 · 2 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×2 · 6 introduced LOCbrowserHistory.ts ×2browserHistory.ts ×1 · 1 introduced LOCbrowserHistory.ts ×1browserHistory.test|title=BrowserHistoryEntriesStore.hydrate backwards-compat undefined snapshot resets to an empty store|occurrence=1 · 0 introduced LOCbrowserHistory.test|titl…browserHistory.ts ×1 · 2 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×3 · 23 introduced LOCbrowserHistory.ts ×3browserHistory.test|title=BrowserHistoryEntriesStore add assigns monotonic ids and exposes items oldest-first|occurrence=1 · 0 introduced LOCbrowserHistory.test|titl…browserHistory.ts ×1 · 3 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×3 · 5 introduced LOCbrowserHistory.ts ×3browserHistory.ts ×1 · 1 introduced LOCbrowserHistory.ts ×1browserHistory.test|title=BrowserHistoryEntriesStore explicit is omitted from the entry when false|occurrence=1 · 0 introduced LOCbrowserHistory.test|titl…browserHistory.ts ×2 · 5 introduced LOCbrowserHistory.ts ×2browserHistory.ts ×2 · 7 introduced LOCbrowserHistory.ts ×2browserHistory.test|title=BrowserFaviconsStore hydrate accepts unknown-typed data matching the current snapshot shape|occurrence=1 · 0 introduced LOCbrowserHistory.test|titl…browserHistory.ts ×1 · 2 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×1 · 10 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×2 · 3 introduced LOCbrowserHistory.ts ×2browserHistory.ts ×1 · 2 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×1 · 2 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×1 · 3 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×1 · 5 introduced LOCbrowserHistory.ts ×1browserHistory.ts ×26 · 160 introduced LOCbrowserHistory.ts ×26browserHistory.test|title=BrowserFaviconsStore gc drops orphans and fires onDidChange only when something changes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserFaviconsStore gc drops orphans and fires onDidChange only when something changes|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserFaviconsStore hydrate accepts unknown-typed data matching the current snapshot shape|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserFaviconsStore hydrate accepts unknown-typed data matching the current snapshot shape|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserFaviconsStore onDidChange fires only when a new favicon is added|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserFaviconsStore onDidChange fires only when a new favicon is added|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserFaviconsStore register dedups by content and returns the same hash|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserFaviconsStore register dedups by content and returns the same hash|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserFaviconsStore serialize then hydrate round-trips|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserFaviconsStore serialize then hydrate round-trips|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryEntriesStore add assigns monotonic ids and exposes items oldest-first|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryEntriesStore add assigns monotonic ids and exposes items oldest-first|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryEntriesStore add beyond maxEntries evicts oldest|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryEntriesStore add beyond maxEntries evicts oldest|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryEntriesStore delete removes the targeted entry and leaves ids of others intact|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryEntriesStore delete removes the targeted entry and leaves ids of others intact|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryEntriesStore explicit is omitted from the entry when false|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryEntriesStore explicit is omitted from the entry when false|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryEntriesStore hydrate seeds the id counter from the max restored id|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryEntriesStore hydrate seeds the id counter from the max restored id|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryEntriesStore onDidChange fires for add, update, delete, clear|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryEntriesStore onDidChange fires for add, update, delete, clear|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryEntriesStore serialize then hydrate round-trips|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryEntriesStore serialize then hydrate round-trips|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryEntriesStore update changes title and icon, returns whether anything changed|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryEntriesStore update changes title and icon, returns whether anything changed|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryEntriesStore update ignores empty title|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryEntriesStore update ignores empty title|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryEntriesStore update of an unknown id is a no-op|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryEntriesStore update of an unknown id is a no-op|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryEntriesStore.hydrate backwards-compat accepts data matching prior snapshot shapes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryEntriesStore.hydrate backwards-compat accepts data matching prior snapshot shapes|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryEntriesStore.hydrate backwards-compat drops malformed entries and accepts the rest|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryEntriesStore.hydrate backwards-compat drops malformed entries and accepts the rest|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryEntriesStore.hydrate backwards-compat undefined snapshot resets to an empty store|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryEntriesStore.hydrate backwards-compat undefined snapshot resets to an empty store|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryStore add is a no-op when max entries is 0|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryStore add is a no-op when max entries is 0|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryStore add returns a handle whose id matches the underlying entry|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryStore add returns a handle whose id matches the underlying entry|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryStore clear wipes entries and favicons together|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryStore clear wipes entries and favicons together|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryStore favicons referenced by other entries are kept on delete|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryStore favicons referenced by other entries are kept on delete|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryStore handle.delete removes the entry and GCs the orphaned favicon|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryStore handle.delete removes the entry and GCs the orphaned favicon|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryStore handle.update propagates to entry and registers the favicon|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryStore handle.update propagates to entry and registers the favicon|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryStore handle.update with explicit `favicon: null` clears the entry icon|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryStore handle.update with explicit `favicon: null` clears the entry icon|occurrence=1browserHistory.test|titl…browserHistory.test|title=BrowserHistoryStore onDidChange fires for changes in either sub-store|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/browserView/test/common/browserHistory.test|title=BrowserHistoryStore onDidChange fires for changes in either sub-store|occurrence=1browserHistory.test|titl…Focused file · src/vs/platform/browserView/common/browserHistory.ts · 364 LOCcommon/browserHistory.ts

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- browserHistory.ts ×26
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { Emitter, Event } from '../../../base/common/event.js';
7 > import { StringSHA1 } from '../../../base/common/hash.js';
8 > import { Disposable } from '../../../base/common/lifecycle.js';
9 >
10 > /**
11 > * On-disk shape of a single history entry.
12 > * BACKWARDS COMPATIBILE. When evolving this interface, ensure older versions can still be handled gracefully.
13 > */
14 > export interface ISerializedBrowserHistoryEntry {
15 > readonly id: number;
16 > readonly url: string;
17 > /** Epoch ms when the entry was most recently visited. */
18 > readonly time: number;
19 > readonly title: string;
20 > /** Content hash key into the sibling favicons map. */
21 > readonly icon?: string;
22 > /**
23 > * Set when the navigation was initiated by the user (typing in the URL
24 > * bar, picking a suggestion, opening a new tab with a URL) rather than by
25 > * page script or link clicks. Always omitted when false to keep entries
26 > * small.
27 > */
28 > readonly explicit?: true;
29 > }
30 >
31 > /**
32 > * In-memory representation of a history entry. Currently identical to the
33 > * on-disk shape; the split exists so future in-memory-only fields can be
34 > * added here without changing the wire format.
35 > */
36 > export interface IBrowserHistoryEntry extends ISerializedBrowserHistoryEntry { }
37 >
38 > export interface IBrowserHistoryUpdate {
39 > /** URL may be updated e.g. during a redirect or in-page navigation. */
40 > readonly url?: string;
41 > readonly title?: string;
42 > /** Favicon data URI; hashed and deduped against the sibling favicons store. Pass `null` to explicitly clear. */
43 > readonly favicon?: string | null;
44 > }
45 >
46 > /**
47 > * Handle returned by {@link BrowserHistoryStore.add}. `update` and `delete`
48 > * are no-ops once the underlying entry has been evicted.
49 > */
50 > export interface IBrowserHistoryItemHandle {
51 > readonly id: number;
52 > update(patch: IBrowserHistoryUpdate): void;
53 > delete(): void;
54 > }
55 >
56 > /** Returned by {@link BrowserHistoryStore.add} when the store is disabled (max entries = 0). */
57 > const NOOP_HANDLE: IBrowserHistoryItemHandle = Object.freeze({
58 > id: -1,
59 > update: () => { },
60 > delete: () => { },
61 > });
62 >
63 > /**
64 > * On-disk shape of an entries snapshot. See {@link ISerializedBrowserHistoryEntry}
65 > * for the backwards-compatibility rules; the same constraints apply here.
66 > */
67 > export interface ISerializedBrowserHistoryEntriesSnapshot {
68 > readonly items: readonly ISerializedBrowserHistoryEntry[];
69 > }
70 >
71 > /**
72 > * On-disk shape of a favicons snapshot. See {@link ISerializedBrowserHistoryEntry}
73 > * for the backwards-compatibility rules; the same constraints apply here.
74 > */
75 > export interface ISerializedBrowserFaviconsSnapshot {
76 > /** Map from content hash to data URI. */
77 > readonly map: Readonly<Record<string, string>>;
78 > }
79 >
80 > const DEFAULT_MAX_ENTRIES = 200;
81 >
82 > export class BrowserHistoryEntriesStore extends Disposable {
83 >
84 > private _nextId: number = 1;
85 > private _items: IBrowserHistoryEntry[] = [];
86 > private _maxEntries: number;
87 >
88 > private readonly _onDidChange = this._register(new Emitter<void>());
89 > readonly onDidChange: Event<void> = this._onDidChange.event;
90 >
91 > constructor(maxEntries: number = DEFAULT_MAX_ENTRIES) {
92 > super(); browserHistory.ts ×1
93 > this._maxEntries = maxEntries;
94 > }
96 > get items(): readonly IBrowserHistoryEntry[] {
97 > return this._items; browserHistory.ts ×1
98 > }
100 > get maxEntries(): number {
101 > return this._maxEntries; browserHistory.ts ×5
102 > }
104 > setMaxEntries(max: number): void {
105 if (max < 0 || max === this._maxEntries) {
106 return;
107 }
108 this._maxEntries = max;
109 if (this._evictIfNeeded()) {
110 this._onDidChange.fire();
111 }
112 }
114 > add(url: string, title: string, faviconHash: string | undefined, userInitiated: boolean): IBrowserHistoryEntry {
115 > const entry: IBrowserHistoryEntry = userInitiated browserHistory.ts ×2
116 > ? { id: this._nextId++, url, time: Date.now(), title, icon: faviconHash, explicit: true } browserHistory.ts ×1
117 > : { id: this._nextId++, url, time: Date.now(), title, icon: faviconHash }; browserHistory.ts ×2
118 > this._items.push(entry);
119 > this._evictIfNeeded();
120 > this._onDidChange.fire();
121 > return entry;
122 > }
124 > update(id: number, patch: { url?: string; title?: string; faviconHash?: string | null }): boolean {
125 > const idx = this._indexOf(id); browserHistory.ts ×4
126 > if (idx === -1) {
127 > return false; browserHistory.ts ×1
128 > }
129 > const existing = this._items[idx]; browserHistory.ts ×1
130 > const nextTitle = patch.title && patch.title.length > 0 ? patch.title : existing.title; browserHistory.ts ×4
131 > const nextUrl = patch.url && patch.url.length > 0 ? patch.url : existing.url;
132 > // Distinguish "leave alone" (undefined) from explicit clear (null).
133 > const nextFaviconHash = patch.faviconHash === undefined
134 > ? existing.icon browserHistory.ts ×1
135 > : (patch.faviconHash ?? undefined); browserHistory.ts ×1
136 > // Update the time if the URL has been updated. browserHistory.ts ×4
137 > const nextTime = patch.url ? Date.now() : existing.time;
138 > if (nextUrl === existing.url && nextTitle === existing.title && nextFaviconHash === existing.icon && nextTime === existing.time) {
139 > return false; browserHistory.ts ×1
140 > }
141 > this._items[idx] = { ...existing, url: nextUrl, title: nextTitle, icon: nextFaviconHash, time: nextTime }; browserHistory.ts ×1
142 > this._onDidChange.fire();
143 > return true;
146 > delete(id: number): boolean {
147 > const idx = this._indexOf(id); browserHistory.ts ×2
148 > if (idx === -1) {
149 > return false; browserHistory.ts ×1
150 > }
151 > this._items.splice(idx, 1); browserHistory.ts ×2
152 > this._onDidChange.fire();
153 > return true;
154 > }
156 > clear(): void {
157 > if (this._items.length === 0 && this._nextId === 1) { browserHistory.ts ×2
158 > return; browserHistory.ts ×1
159 > }
160 > this._items = []; browserHistory.ts ×2
161 > this._nextId = 1;
162 > this._onDidChange.fire();
163 > }
165 > serialize(): ISerializedBrowserHistoryEntriesSnapshot {
166 > return { items: this._items.slice() }; browserHistory.ts ×1
167 > }
169 > hydrate(snapshot: ISerializedBrowserHistoryEntriesSnapshot | undefined): void {
170 > this._items = []; browserHistory.ts ×2
171 > this._nextId = 1;
172 > if (snapshot && Array.isArray(snapshot.items)) {
173 > for (const e of snapshot.items) { browserHistory.ts ×3
174 > if (isValidEntry(e)) {
175 > this._items.push(e);
176 > }
177 > }
178 > // Restored ids must not collide with future adds.
179 > for (const e of this._items) {
180 > if (e.id >= this._nextId) {
181 > this._nextId = e.id + 1;
182 > }
183 > }
184 > this._evictIfNeeded();
185 > }
186 > this._onDidChange.fire(); browserHistory.ts ×2
187 > }
189 > private _indexOf(id: number): number {
190 > // Walk newest-first; mutations target the just-added entry in the common case. browserHistory.ts ×3
191 > for (let i = this._items.length - 1; i >= 0; i--) {
192 > if (this._items[i].id === id) {
193 > return i; browserHistory.ts ×1
194 > }
196 > return -1; browserHistory.ts ×1
199 > private _evictIfNeeded(): boolean {
200 > if (this._items.length > this._maxEntries) { browserHistory.ts ×2
201 > this._items.splice(0, this._items.length - this._maxEntries); browserHistory.ts ×1
202 > return true;
203 > }
204 > return false; browserHistory.ts ×2
205 > }
207 >
208 > /**
209 > * Lives separately from {@link BrowserHistoryEntriesStore} so the (large)
210 > * favicon map is only rewritten when an image is added or removed, not on
211 > * every navigation.
212 > */
213 > export class BrowserFaviconsStore extends Disposable {
215 > private readonly _byHash = new Map<string, string>();
216 >
217 > private readonly _onDidChange = this._register(new Emitter<void>());
218 > readonly onDidChange: Event<void> = this._onDidChange.event;
220 > get(hash: string): string | undefined {
221 > return this._byHash.get(hash); browserHistory.ts ×1
222 > }
224 > register(dataUri: string): string {
225 > const sha = new StringSHA1(); browserHistory.ts ×1
226 > sha.update(dataUri);
227 > const hash = sha.digest();
228 > if (!this._byHash.has(hash)) {
229 > this._byHash.set(hash, dataUri);
230 > this._onDidChange.fire();
231 > }
232 > return hash;
233 > }
235 > gc(referenced: ReadonlySet<string>): void {
236 > if (this._byHash.size === 0) { browserHistory.ts ×2
237 > return; browserHistory.ts ×1
238 > }
239 > let changed = false; browserHistory.ts ×2
240 > for (const hash of this._byHash.keys()) {
241 > if (!referenced.has(hash)) {
242 > this._byHash.delete(hash); browserHistory.ts ×2
243 > changed = true;
244 > }
246 > if (changed) {
247 > this._onDidChange.fire(); browserHistory.ts ×2
248 > }
251 > clear(): void {
252 > if (this._byHash.size === 0) { browserHistory.ts ×3
253 > return;
254 > }
255 this._byHash.clear();
256 this._onDidChange.fire();
259 > serialize(): ISerializedBrowserFaviconsSnapshot {
260 > return { map: Object.fromEntries(this._byHash) }; browserHistory.ts ×1
261 > }
263 > hydrate(snapshot: ISerializedBrowserFaviconsSnapshot | undefined): void {
264 > this._byHash.clear(); browserHistory.ts ×1
265 > if (snapshot?.map && typeof snapshot.map === 'object') {
266 > for (const [k, v] of Object.entries(snapshot.map)) {
267 > if (typeof v === 'string') {
268 > this._byHash.set(k, v);
269 > }
270 > }
271 > }
272 > this._onDidChange.fire();
273 > }
275 >
276 > /**
277 > * Per-session browser history. The two sub-stores are exposed directly so
278 > * persistence layers can flush them independently.
279 > */
280 > export class BrowserHistoryStore extends Disposable {
281 >
282 > readonly entries: BrowserHistoryEntriesStore;
283 > readonly favicons: BrowserFaviconsStore;
284 >
285 > private readonly _onDidChange = this._register(new Emitter<void>());
286 > readonly onDidChange: Event<void> = this._onDidChange.event;
287 >
288 > constructor(maxEntries?: number) {
289 > super(); browserHistory.ts ×5
290 > this.entries = this._register(new BrowserHistoryEntriesStore(maxEntries));
291 > this.favicons = this._register(new BrowserFaviconsStore());
292 >
293 > this._register(this.entries.onDidChange(() => {
294 > this._gcFavicons(); browserHistory.ts ×6
295 > this._onDidChange.fire();
297 > this._register(this.favicons.onDidChange(() => this._onDidChange.fire()));
298 > }
300 > add(url: string, title: string, favicon?: string, userInitiated = false): IBrowserHistoryItemHandle {
301 > if (this.entries.maxEntries === 0) { browserHistory.ts ×5
302 > // History disabled: skip favicon hashing and entry creation entirely. browserHistory.ts ×1
303 > return NOOP_HANDLE;
304 > }
305 > const faviconHash = favicon ? this.favicons.register(favicon) : undefined; browserHistory.ts ×5
306 > const entry = this.entries.add(url, title, faviconHash, userInitiated);
307 > return this._handleFor(entry.id);
308 > }
310 > setMaxEntries(max: number): void {
311 this.entries.setMaxEntries(max);
312 }
314 > clear(): void {
315 > this.entries.clear(); browserHistory.ts ×3
316 > this.favicons.clear();
317 > }
319 > private _handleFor(id: number): IBrowserHistoryItemHandle {
320 > return { browserHistory.ts ×6
321 > id,
322 > update: patch => {
323 > const next: { url?: string; title?: string; faviconHash?: string | null } = {}; browserHistory.ts ×4
324 > if (patch.url !== undefined) {
325 next.url = patch.url;
326 }
327 > if (patch.title !== undefined) { browserHistory.ts ×4
328 > next.title = patch.title; browserHistory.ts ×1
329 > }
330 > if (patch.favicon !== undefined) { browserHistory.ts ×4
331 > // null is the explicit-clear sentinel; a data URI registers and references it. browserHistory.ts ×1
332 > next.faviconHash = patch.favicon === null ? null : this.favicons.register(patch.favicon);
333 > }
334 > this.entries.update(id, next); browserHistory.ts ×4
335 > },
336 > delete: () => { browserHistory.ts ×6
337 > this.entries.delete(id); browserHistory.ts ×1
338 > },
340 > }
342 > private _gcFavicons(): void {
343 > const referenced = new Set<string>(); browserHistory.ts ×6
344 > for (const e of this.entries.items) {
345 > if (e.icon) {
346 > referenced.add(e.icon); browserHistory.ts ×1
347 > }
349 > this.favicons.gc(referenced);
350 > }
352 >
353 > function isValidEntry(value: unknown): value is ISerializedBrowserHistoryEntry { browserHistory.ts ×3
354 > if (!value || typeof value !== 'object') {
355 > return false; browserHistory.ts ×1
356 > }
357 > const e = value as ISerializedBrowserHistoryEntry; browserHistory.ts ×3
358 > return typeof e.id === 'number'
359 > && typeof e.url === 'string'
360 > && typeof e.time === 'number'
361 > && typeof e.title === 'string'
362 > && (e.icon === undefined || typeof e.icon === 'string')
363 > && (e.explicit === undefined || e.explicit === true);
364 > }