Skip to content

Commit 8bb3ab4

Browse files
authored
chore(trace): local network access error message (#38221)
Signed-off-by: Simon Knott <[email protected]>
1 parent 86b465c commit 8bb3ab4

File tree

3 files changed

+37
-19
lines changed

3 files changed

+37
-19
lines changed

packages/trace-viewer/src/sw/main.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ function loadTrace(clientId: string, url: URL, isContextRequest: boolean, progre
100100
return loadedTrace;
101101
const promise = innerLoadTrace(traceUrl, progress);
102102
loadedTraces.set(traceUrl, promise);
103+
promise.catch(() => loadedTraces.delete(traceUrl));
103104
return promise;
104105
}
105106

@@ -119,7 +120,14 @@ async function innerLoadTrace(traceUrl: string, progress: Progress): Promise<Loa
119120
throw new Error('Could not load trace. Did you upload a Playwright HTML report instead? Make sure to extract the archive first and then double-click the index.html file or put it on a web server.');
120121
if (error instanceof TraceVersionError)
121122
throw new Error(`Could not load trace from ${traceUrl}. ${error.message}`);
122-
throw new Error(`Could not load trace from ${traceUrl}. Make sure a valid Playwright Trace is accessible over this url.`);
123+
124+
let message = `Could not load trace from ${traceUrl}. Make sure a valid Playwright Trace is accessible over this url.`;
125+
126+
const lnaPermission = await navigator.permissions.query({ name: 'local-network-access' as PermissionName }).catch(() => { });
127+
if (lnaPermission && lnaPermission.state !== 'granted')
128+
message += `\n\nIf your trace is in a local or private network, please grant permission for Local Network Access.`; // workbenchLoader.tsx opens the prompt when it sees this message.
129+
130+
throw new Error(message);
123131
}
124132
const snapshotServer = new SnapshotServer(traceLoader.storage(), sha1 => traceLoader.resourceForSha1(sha1));
125133
return { traceLoader, snapshotServer };
@@ -132,12 +140,6 @@ async function doFetch(event: FetchEvent): Promise<Response> {
132140
if (request.url.startsWith('chrome-extension://'))
133141
return fetch(request);
134142

135-
if (request.headers.get('x-pw-serviceworker') === 'forward') {
136-
const request = new Request(event.request);
137-
request.headers.delete('x-pw-serviceworker');
138-
return fetch(request);
139-
}
140-
141143
const url = new URL(request.url);
142144
let relativePath: string | undefined;
143145
if (request.url.startsWith(self.registration.scope))
@@ -282,5 +284,8 @@ function isLiveTrace(traceUrl: string): boolean {
282284
}
283285

284286
self.addEventListener('fetch', function(event: FetchEvent) {
287+
if (event.request.headers.get('x-pw-serviceworker') === 'skip')
288+
return false;
289+
285290
event.respondWith(doFetch(event));
286291
});

packages/trace-viewer/src/ui/workbenchLoader.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ body .drop-target {
5555
font-weight: bold;
5656
text-align: center;
5757
margin: 30px;
58+
white-space: pre-line;
5859
}
5960

6061
.drop-target input {

packages/trace-viewer/src/ui/workbenchLoader.tsx

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,22 @@ export const WorkbenchLoader: React.FunctionComponent<{
124124
}
125125
}, []);
126126

127+
const fetchTrace = React.useCallback(async (traceURL: string): Promise<string | undefined> => {
128+
const params = new URLSearchParams();
129+
params.set('trace', traceURL);
130+
const response = await fetch(`contexts?${params.toString()}`);
131+
if (!response.ok) {
132+
const { error } = await response.json();
133+
setProcessingErrorMessage(error);
134+
return error;
135+
}
136+
const contextEntries = await response.json();
137+
const model = new TraceModel(traceURL, contextEntries);
138+
setProgress({ done: 0, total: 0 });
139+
setProcessingErrorMessage(null);
140+
setModel(model);
141+
}, []);
142+
127143
React.useEffect(() => {
128144
(async () => {
129145
if (!traceURL) {
@@ -138,25 +154,21 @@ export const WorkbenchLoader: React.FunctionComponent<{
138154
try {
139155
navigator.serviceWorker.addEventListener('message', swListener);
140156
setProgress({ done: 0, total: 1 });
141-
142-
const params = new URLSearchParams();
143-
params.set('trace', traceURL);
144-
const response = await fetch(`contexts?${params.toString()}`);
145-
if (!response.ok) {
157+
let error = await fetchTrace(traceURL);
158+
if (error?.includes('please grant permission for Local Network Access')) {
159+
// fetching the asset opens the permission prompt. but only from window, not from SW (https://issues.chromium.org/issues/460180743)
160+
await fetch(traceURL, { method: 'HEAD', headers: { 'x-pw-serviceworker': 'skip' } });
161+
error = await fetchTrace(traceURL);
162+
}
163+
if (error) {
146164
if (!isServer)
147165
setTraceURL(undefined);
148-
setProcessingErrorMessage((await response.json()).error);
149-
return;
150166
}
151-
const contextEntries = await response.json();
152-
const model = new TraceModel(traceURL, contextEntries);
153-
setProgress({ done: 0, total: 0 });
154-
setModel(model);
155167
} finally {
156168
navigator.serviceWorker.removeEventListener('message', swListener);
157169
}
158170
})();
159-
}, [isServer, traceURL, uploadedTraceName]);
171+
}, [isServer, traceURL, uploadedTraceName, fetchTrace]);
160172

161173
const showLoading = progress.done !== progress.total && progress.total !== 0 && !processingErrorMessage;
162174

0 commit comments

Comments
 (0)