Skip to content

Commit 69d05e4

Browse files
committed
[chrome] ux: fix race conditions in frame unfocus logic and unfocus frames when dragging a tab
1 parent db228bc commit 69d05e4

File tree

5 files changed

+58
-20
lines changed

5 files changed

+58
-20
lines changed

packages/chrome/src/Browser.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,6 @@ export class Browser extends StatefulClass {
9292
sessionDownloadHistory: Stateful<DownloadEntry>[] = [];
9393
globalDownloadHistory: Stateful<DownloadEntry>[] = [];
9494

95-
unfocusframes: boolean = false;
96-
9795
cookieJar: CookieJar = new CookieJar();
9896

9997
downloadProgress = 0;

packages/chrome/src/components/Menu.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { Icon } from "./Icon";
1111
import type { IconifyIcon } from "@iconify/types";
1212
import { emToPx } from "../utils";
1313
import { isPuter } from "../main";
14+
import { requestUnfocusFrames } from "./Shell";
1415

1516
export const closeMenu = createDelegate<void>();
1617

@@ -41,8 +42,10 @@ export function Menu(
4142
this.x = 0;
4243
this.y = 0;
4344

45+
const [lock, unlock] = requestUnfocusFrames();
46+
4447
const close = () => {
45-
browser.unfocusframes = false;
48+
unlock();
4649

4750
window.removeEventListener("click", ev, { capture: true });
4851
window.removeEventListener("contextmenu", ev, { capture: true });
@@ -66,7 +69,7 @@ export function Menu(
6669
};
6770

6871
cx.mount = () => {
69-
browser.unfocusframes = true;
72+
lock();
7073
document.body.appendChild(cx.root);
7174
const { width, height } = cx.root.getBoundingClientRect();
7275
const docWidth = document.documentElement.clientWidth;

packages/chrome/src/components/Omnibar/Omnibox.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { SiteOptionsButton } from "./SiteOptionsButton";
2525
import { Favicon } from "../Favicon";
2626
import { UrlInput } from "./UrlInput";
2727
import { Suggestion } from "./Suggestion";
28+
import { requestUnfocusFrames } from "../Shell";
2829

2930
export const focusOmnibox = createDelegate<void>();
3031

@@ -76,6 +77,8 @@ export function Omnibox(
7677
this.value = "";
7778
this.trendingSuggestions = [];
7879

80+
const [lock, unlock] = requestUnfocusFrames();
81+
7982
cx.mount = () => {
8083
setContextMenu(cx.root, [
8184
{
@@ -179,11 +182,11 @@ export function Omnibox(
179182
const activate = () => {
180183
this.subtleinput = false;
181184
this.active = true;
182-
browser.unfocusframes = true;
185+
lock();
183186

184187
const handleClickOutside = (e: MouseEvent) => {
185188
this.active = false;
186-
browser.unfocusframes = false;
189+
unlock();
187190
e.preventDefault();
188191

189192
document.body.removeEventListener("click", handleClickOutside);

packages/chrome/src/components/Shell.tsx

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,26 @@
1-
import { css, type ComponentContext } from "dreamland/core";
1+
import { createDelegate, css, type ComponentContext } from "dreamland/core";
22
import { browser } from "../Browser";
33
import { forceScreenshot, popTab, pushTab } from "../Browser";
44
import { takeScreenshotGDM } from "../screenshot";
55

6+
let locks: Symbol[] = [];
7+
let setUnfocus = createDelegate<boolean>();
8+
export function requestUnfocusFrames(): [() => void, () => void] {
9+
let lock = Symbol();
10+
return [
11+
() => {
12+
setUnfocus(true);
13+
locks.push(lock);
14+
},
15+
() => {
16+
locks = locks.filter((l) => l !== lock);
17+
if (locks.length === 0) {
18+
setUnfocus(false);
19+
}
20+
},
21+
];
22+
}
23+
624
export function Shell(_, cx: ComponentContext) {
725
pushTab.listen((tab) => {
826
// paint the iframes
@@ -12,6 +30,9 @@ export function Shell(_, cx: ComponentContext) {
1230
let mouseMoveListen = (e: MouseEvent) => {
1331
tab.devtoolsWidth = window.innerWidth - e.clientX;
1432
};
33+
34+
const [lock, unlock] = requestUnfocusFrames();
35+
1536
cx.root.appendChild(
1637
<div
1738
class="container"
@@ -20,10 +41,7 @@ export function Shell(_, cx: ComponentContext) {
2041
class:active={use(browser.activetab).map((t) => t === tab)}
2142
class:showframe={use(tab.internalpage).map((t) => !t)}
2243
>
23-
<div
24-
class="mainframecontainer"
25-
class:unfocus={use(browser.unfocusframes)}
26-
>
44+
<div class="mainframecontainer">
2745
{use(tab.internalpage)}
2846
{tab.frame.frame}
2947
</div>
@@ -34,21 +52,18 @@ export function Shell(_, cx: ComponentContext) {
3452
>
3553
<div
3654
on:mousedown={(e: MouseEvent) => {
37-
browser.unfocusframes = true;
55+
lock();
3856
document.body.style.cursor = "ew-resize";
3957
window.addEventListener("mousemove", mouseMoveListen);
4058
window.addEventListener("mouseup", () => {
41-
browser.unfocusframes = false;
59+
unlock();
4260
window.removeEventListener("mousemove", mouseMoveListen);
4361
document.body.style.cursor = "";
4462
});
4563
}}
4664
class="divider"
4765
></div>
48-
<div
49-
class="devtoolsframecontainer"
50-
class:unfocus={use(browser.unfocusframes)}
51-
>
66+
<div class="devtoolsframecontainer">
5267
{/*{tab.devtoolsFrame.frame}*/}
5368
</div>
5469
</div>
@@ -73,6 +88,23 @@ export function Shell(_, cx: ComponentContext) {
7388
// tab.screenshot = await takeScreenshotSvg(container);
7489
}
7590
});
91+
setUnfocus.listen((unfocus) => {
92+
if (unfocus) {
93+
cx.root
94+
.querySelectorAll(".mainframecontainer, .devtoolsframecontainer")
95+
.forEach((el) => {
96+
if (!(el instanceof HTMLElement)) return;
97+
el.style.pointerEvents = "none";
98+
});
99+
} else {
100+
cx.root
101+
.querySelectorAll(".mainframecontainer, .devtoolsframecontainer")
102+
.forEach((el) => {
103+
if (!(el instanceof HTMLElement)) return;
104+
el.style.pointerEvents = "";
105+
});
106+
}
107+
});
76108

77109
return <div></div>;
78110
}
@@ -84,9 +116,6 @@ Shell.style = css`
84116
width: 100%;
85117
position: relative;
86118
}
87-
.unfocus {
88-
pointer-events: none;
89-
}
90119
.container {
91120
position: absolute;
92121
width: 100%;

packages/chrome/src/components/TabStrip/TabStrip.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { browser, forceScreenshot, pushTab } from "../../Browser";
1616
import { defaultFaviconUrl } from "../../assets/favicon";
1717
import { DragTab } from "./DragTab";
1818
import { markDirty } from "../../storage";
19+
import { requestUnfocusFrames } from "../Shell";
1920

2021
type VisualTab = {
2122
tab: Tab;
@@ -48,6 +49,8 @@ export function TabStrip(
4849
this.currentlydragging = -1;
4950
this.visualtabs = [];
5051

52+
const [lock, unlock] = requestUnfocusFrames();
53+
5154
const TAB_PADDING = 6;
5255
const TAB_MAX_SIZE = 231;
5356
const TAB_TRANSITION = "250ms ease";
@@ -167,11 +170,13 @@ export function TabStrip(
167170
tab.dragpos = -1;
168171
layoutTabs(true);
169172
this.currentlydragging = -1;
173+
unlock();
170174
});
171175

172176
const mouseDown = (e: MouseEvent, tab: VisualTab) => {
173177
if (e.button != 0) return;
174178
this.currentlydragging = tab.tab.id;
179+
lock();
175180

176181
const rect = tab.root.getBoundingClientRect();
177182
tab.root.style.zIndex = "100";

0 commit comments

Comments
 (0)