Skip to content

Commit a4e66b1

Browse files
committed
port over (incomplete) history emulation
1 parent 8c9522e commit a4e66b1

File tree

5 files changed

+93
-78
lines changed

5 files changed

+93
-78
lines changed

packages/chrome/src/History.ts

Lines changed: 18 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ import { createState } from "dreamland/core";
22
import { browser } from "./Browser";
33
import { StatefulClass } from "./StatefulClass";
44
import type { Tab } from "./Tab";
5-
6-
import type { ScramjetClient, ScramjetFrame } from "@mercuryworkshop/scramjet";
5+
import { sendFrame } from "./IsolatedFrame";
76

87
// history api emulation
98
export class HistoryState extends StatefulClass {
@@ -13,6 +12,8 @@ export class HistoryState extends StatefulClass {
1312
favicon: string | null = null;
1413
timestamp: number;
1514

15+
virtual: boolean = false; // whether this state was created by pushState and can be navigated to without a full reload
16+
1617
constructor(partial?: Partial<HistoryState>) {
1718
super(createState(Object.create(HistoryState.prototype)));
1819
Object.assign(this, partial);
@@ -126,15 +127,27 @@ export class History {
126127
this.index = this.states.length - 1;
127128
}
128129

129-
if (navigate) {
130+
let newstate = this.states[this.index];
131+
132+
if (newstate.virtual) {
133+
sendFrame(this.tab, "history_go", {
134+
delta,
135+
});
136+
} else if (navigate) {
130137
this.justTriggeredNavigation = true;
131-
this.tab._directnavigate(this.states[this.index].url);
138+
this.tab._directnavigate(newstate.url);
139+
}
140+
141+
if (newstate.virtual || !navigate) {
142+
this.tab.url = newstate.url;
143+
this.tab.title = newstate.title;
144+
this.tab.icon = newstate.favicon;
132145
}
133146

134147
this.tab.canGoBack = this.canGoBack();
135148
this.tab.canGoForward = this.canGoForward();
136149

137-
return this.states[this.index];
150+
return newstate;
138151
}
139152
canGoBack(): boolean {
140153
return this.index > 0;
@@ -143,61 +156,3 @@ export class History {
143156
return this.index < this.states.length - 1;
144157
}
145158
}
146-
147-
export function addHistoryListeners(frame: ScramjetFrame, tab: Tab) {
148-
frame.addEventListener("navigate", (e) => {
149-
console.log("History push from navigate", e, tab.history.states);
150-
// this event is fired whenever location.href is set, or similar
151-
// importantly not fired when replaceState is called (we overwrite it ourselves in injectContext)
152-
153-
// behavior here is just to create a new history entry
154-
const url = new URL(e.url);
155-
tab.history.push(url, undefined, false);
156-
157-
console.log("History push from navigate", url, tab.history.states);
158-
});
159-
}
160-
161-
export function injectHistoryEmulation(client: ScramjetClient, tab: Tab) {
162-
// this is extremely problematic in terms of security but whatever
163-
client.global.addEventListener("beforeunload", (e: BeforeUnloadEvent) => {
164-
console.log("History beforeunload", e);
165-
});
166-
167-
client.Proxy("History.prototype.pushState", {
168-
apply(ctx) {
169-
console.log("STATE PUSH", ctx.args);
170-
ctx.return(undefined);
171-
},
172-
});
173-
174-
client.Proxy("History.prototype.replaceState", {
175-
apply(ctx) {
176-
console.log("STATE REPLACE", ctx.args);
177-
ctx.return(undefined);
178-
},
179-
});
180-
client.Proxy("History.prototype.back", {
181-
apply(ctx) {
182-
console.log("HISTORY BACK", ctx);
183-
tab.history.go(-1);
184-
ctx.return(undefined);
185-
},
186-
});
187-
client.Proxy("History.prototype.forward", {
188-
apply(ctx) {
189-
console.log("HISTORY FORWARD", ctx);
190-
tab.history.go(1);
191-
ctx.return(undefined);
192-
},
193-
});
194-
client.Proxy("History.prototype.go", {
195-
apply(ctx) {
196-
console.log("HISTORY GO", ctx);
197-
tab.history.go(ctx.args[0]);
198-
ctx.return(undefined);
199-
},
200-
});
201-
}
202-
203-
export function handleNavigate() {}

packages/chrome/src/IsolatedFrame.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {
22
ScramjetHeaders,
33
type ScramjetFetchContext,
44
type ScramjetFetchResponse,
5-
CookieStore,
5+
CookieJar,
66
handleFetch,
77
rewriteUrl,
88
setConfig,
@@ -89,7 +89,7 @@ export const bare = new BareClient();
8989

9090
type Controller = {
9191
controllerframe: HTMLIFrameElement;
92-
cookiestore: CookieStore;
92+
cookiestore: CookieJar;
9393
rootdomain: string;
9494
baseurl: URL;
9595
prefix: URL;
@@ -133,7 +133,7 @@ function makeController(url: URL): Controller {
133133
});
134134

135135
const prefix = new URL(baseurl.protocol + baseurl.host + cfg.prefix);
136-
const cookiestore = new CookieStore();
136+
const cookiestore = new CookieJar();
137137

138138
const controller = {
139139
controllerframe: frame,
@@ -379,13 +379,13 @@ async function makeAllResponse(): Promise<ScramjetFetchResponse> {
379379
let synctoken = 0;
380380
let syncPool: { [token: number]: (val: any) => void } = {};
381381
export function sendFrame<T extends keyof Framebound>(
382-
controller: Controller,
382+
tab: Tab,
383383
type: T,
384384
message: Framebound[T][0]
385385
): Promise<Framebound[T][1]> {
386386
let token = synctoken++;
387387

388-
controller.window.postMessage(
388+
tab.frame.frame.contentWindow!.postMessage(
389389
{
390390
$ipc$type: "request",
391391
$ipc$token: token,

packages/chrome/src/Tab.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
import { createDelegate, createState } from "dreamland/core";
22
import { StatefulClass } from "./StatefulClass";
33
import { browser } from "./Browser";
4-
import {
5-
addHistoryListeners,
6-
History,
7-
injectHistoryEmulation,
8-
type SerializedHistory,
9-
} from "./History";
4+
import { History, type SerializedHistory } from "./History";
105
import { NewTabPage } from "./pages/NewTabPage";
116
import { PlaygroundPage } from "./pages/PlaygroundPage";
127
import { createMenu } from "./components/Menu";
@@ -22,7 +17,6 @@ import {
2217
type ScramjetFetchContext,
2318
ScramjetController,
2419
type ScramjetFetchResponse,
25-
CookieStore,
2620
handleFetch,
2721
rewriteUrl,
2822
config,
@@ -47,7 +41,7 @@ export class Tab extends StatefulClass {
4741
devtoolsFrame: ScramjetFrame;
4842
screenshot: string | null = null;
4943

50-
icon: string;
44+
icon: string | null;
5145
justCreated: boolean = true;
5246

5347
history: History;

packages/inject/src/index.ts

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ export const chromeframe = top!;
99

1010
export const methods: FrameboundMethods = {
1111
async navigate({ url }) {
12-
return "a";
12+
window.location.href = url;
13+
},
14+
async history_go({ delta }) {
15+
client.natives.call("History.prototype.go", history, delta);
1316
},
1417
};
1518

@@ -105,8 +108,56 @@ function setupContextMenu() {
105108
});
106109
}
107110

111+
function setupHistoryEmulation() {
112+
client.Proxy("History.prototype.pushState", {
113+
apply(ctx) {
114+
sendChrome("history_pushState", {
115+
state: ctx.args[0],
116+
title: ctx.args[1],
117+
url: ctx.args[2],
118+
});
119+
120+
ctx.return(undefined);
121+
},
122+
});
123+
124+
client.Proxy("History.prototype.replaceState", {
125+
apply(ctx) {
126+
sendChrome("history_replaceState", {
127+
state: ctx.args[0],
128+
title: ctx.args[1],
129+
url: ctx.args[2],
130+
});
131+
132+
ctx.return(undefined);
133+
},
134+
});
135+
client.Proxy("History.prototype.back", {
136+
apply(ctx) {
137+
sendChrome("history_go", { delta: -1 });
138+
139+
ctx.return(undefined);
140+
},
141+
});
142+
client.Proxy("History.prototype.forward", {
143+
apply(ctx) {
144+
sendChrome("history_go", { delta: 1 });
145+
146+
ctx.return(undefined);
147+
},
148+
});
149+
client.Proxy("History.prototype.go", {
150+
apply(ctx) {
151+
sendChrome("history_go", { delta: ctx.args[0] });
152+
153+
ctx.return(undefined);
154+
},
155+
});
156+
}
157+
108158
setupTitleWatcher();
109159
setupContextMenu();
160+
setupHistoryEmulation();
110161

111162
// inform chrome of the current url
112163
// will happen if you get redirected/click on a link, etc, the chrome will have no idea otherwise

packages/inject/src/types.d.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,28 @@ export type Chromebound = {
3434
url: string;
3535
},
3636
];
37+
history_pushState: [
38+
{
39+
state: any;
40+
title: string;
41+
url: string;
42+
},
43+
];
44+
history_replaceState: [
45+
{
46+
state: any;
47+
title: string;
48+
url: string;
49+
},
50+
];
51+
history_go: [{ delta: number }];
3752
};
3853

3954
export type Framebound = {
4055
navigate: [
4156
{
4257
url: string;
4358
},
44-
string,
4559
];
60+
history_go: [{ delta: number }, void];
4661
};

0 commit comments

Comments
 (0)