Skip to content

Commit 76fe5b6

Browse files
paulirishDevtools-frontend LUCI CQ
authored andcommitted
RPP: Reland hiding irrelevant buttons in standalone usages
With a revised signal for `canRecord` we now hide the RPP Record button, and others, in standalone scenarios. Bug:432043754 Change-Id: I7d470474ec0e481a2e257f2b40519e47b2b999bf Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6943527 Reviewed-by: Connor Clark <cjamcl@chromium.org> Commit-Queue: Paul Irish <paulirish@chromium.org>
1 parent b95c59a commit 76fe5b6

File tree

8 files changed

+69
-20
lines changed

8 files changed

+69
-20
lines changed

front_end/core/host/InspectorFrontendHost.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const MAX_RECORDED_HISTOGRAMS_SIZE = 100;
5050
const OVERRIDES_FILE_SYSTEM_PATH = '/overrides' as Platform.DevToolsPath.RawPathString;
5151

5252
/**
53-
* The InspectorFrontendHostStub is a stub interface used the frontend is loaded like a webpage. Examples:
53+
* The `InspectorFrontendHostStub` is a stub interface used the frontend is loaded like a webpage. Examples:
5454
* - devtools://devtools/bundled/devtools_app.html
5555
* - https://chrome-devtools-frontend.appspot.com/serve_rev/@030cc140435b0152645522b9864b75cac6c0a854/worker_app.html
5656
* - http://localhost:9222/devtools/inspector.html?ws=localhost:9222/devtools/page/xTARGET_IDx
@@ -484,18 +484,24 @@ export class InspectorFrontendHostStub implements InspectorFrontendHostAPI {
484484
}
485485

486486
/**
487-
* **Hosted mode** is when DevTools is loaded over `http(s)://` rather than from `devtools://`.
488-
* It does **not** indicate whether the frontend is connected to a valid CDP target.
487+
* Think of **Hosted mode** as "non-embedded" mode; you can see a devtools frontend URL as the tab's URL. It's an atypical way that DevTools is run.
488+
* Whereas in **Non-hosted** (aka "embedded"), DevTools is embedded and fully dockable. It's the common way DevTools is run.
489489
*
490-
* | Example case | Mode | Example URL |
491-
* | :--------------------------------------------------- | :------------- | :---------------------------------------------------------------------------- |
492-
* | typical devtools: (un)docked w/ native CDP bindings | **NOT Hosted** | `devtools://devtools/bundled/devtools_app.html?targetType=tab&...` |
493-
* | tab href is `devtools://…?ws=…` | **NOT Hosted** | `devtools://devtools/bundled/devtools_app.html?ws=localhost:9228/...` |
494-
* | tab href is `devtools://…` but no connection | **NOT Hosted** | `devtools://devtools/bundled/devtools_app.html` |
495-
* | tab href is `https://…?ws=` (connected) | **Hosted** | `https://chrome-devtools-frontend.appspot.com/serve_rev/@.../worker_app.html` |
496-
* | tab href is `http://…` but no connection | **Hosted** | `http://localhost:9222/devtools/inspector.html?ws=localhost:9222/...` |
490+
* **Hosted mode** == we're using the `InspectorFrontendHostStub`. impl. (@see `InspectorFrontendHostStub` class comment)
491+
* Whereas with **non-hosted** mode, native `DevToolsEmbedderMessageDispatcher` is used for CDP and more.
492+
*
493+
* Relationships to other signals:
494+
* - Hosted-ness does not indicate whether the frontend is _connected to a valid CDP target_.
495+
* - Being _"dockable"_ (aka `canDock`) is typically aligned but technically orthogonal.
496+
* - It's unrelated to the _tab's (main frame's) URL_. Though in non-hosted, the devtools frame origin will always be `devtools://devtools`.
497497
*
498-
* See also `canDock` which has similar semantics.
498+
* | Example case | Mode | Example devtools |
499+
* | :--------------------------------------------------- | :------------- | :---------------------------------------------------------------------------- |
500+
* | tab URL: anything. embedded DevTools w/ native CDP bindings | **NOT Hosted** | `devtools://devtools/bundled/devtools_app.html?targetType=tab&...` |
501+
* | tab URL: `devtools://…?ws=…` | **Hosted** | `devtools://devtools/bundled/devtools_app.html?ws=localhost:9228/...` |
502+
* | tab URL: `devtools://…` but no connection | **Hosted** | `devtools://devtools/bundled/devtools_app.html` |
503+
* | tab URL: `https://…` but no connection | **Hosted** | `https://chrome-devtools-frontend.appspot.com/serve_rev/@.../worker_app.html` |
504+
* | tab URL: `http://…?ws=` (connected) | **Hosted** | `http://localhost:9222/devtools/inspector.html?ws=localhost:9222/...` |
499505
*/
500506
isHostedMode(): boolean {
501507
return true;

front_end/core/sdk/Connections.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,10 @@ function createMainConnection(onConnectionLost: (message: Platform.UIString.Loca
281281
const ws = (wsParam ? `ws://${wsParam}` : `wss://${wssParam}`) as Platform.DevToolsPath.UrlString;
282282
return new WebSocketConnection(ws, onConnectionLost);
283283
}
284-
if (Host.InspectorFrontendHost.InspectorFrontendHostInstance.isHostedMode()) {
285-
// Hosted mode (e.g. `http://localhost:9222/devtools/inspector.html`) but no WebSocket URL.
284+
285+
const notEmbeddedOrWs = Host.InspectorFrontendHost.InspectorFrontendHostInstance.isHostedMode();
286+
if (notEmbeddedOrWs) {
287+
// eg., hosted mode (e.g. `http://localhost:9222/devtools/inspector.html`) without a WebSocket URL,
286288
return new StubConnection();
287289
}
288290

front_end/core/sdk/TargetManager.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {assertNotNullOrUndefined} from '../platform/platform.js';
1010
import type * as ProtocolClient from '../protocol_client/protocol_client.js';
1111
import * as Root from '../root/root.js';
1212

13+
import {StubConnection} from './Connections.js';
14+
import {RehydratingConnection} from './RehydratingConnection.js';
1315
import {SDKModel} from './SDKModel.js';
1416
import {Target, Type as TargetType} from './Target.js';
1517

@@ -310,6 +312,22 @@ export class TargetManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes
310312
return this.#browserTarget;
311313
}
312314

315+
/**
316+
* If this returns true, the target is not connected to a legit CDP server.
317+
* However, it's not exhaustive, so some `false` responses may be misleading.
318+
* (eg., tab URL of `devtools://devtools/bundled/devtools_app.html` uses a MainConnection but has no CDP server behind it).
319+
*/
320+
hasFakeConnection(): boolean {
321+
// There _may_ be a race condition hiding here on the router/connection creation.
322+
// So we play it safe and consider "no connection yet" as "not fake".
323+
const connection = this.primaryPageTarget()?.router()?.connection();
324+
if (!connection) {
325+
return false;
326+
}
327+
const isFakeConnection = (connection instanceof StubConnection) || (connection instanceof RehydratingConnection);
328+
return isFakeConnection;
329+
}
330+
313331
async maybeAttachInitialTarget(): Promise<boolean> {
314332
if (!Boolean(Root.Runtime.Runtime.queryParam('browserConnection'))) {
315333
return false;

front_end/models/live-metrics/LiveMetrics.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,13 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
534534

535535
const source = await InjectedScript.get();
536536

537-
const {identifier} = await this.#target.pageAgent().invoke_addScriptToEvaluateOnNewDocument({
537+
// Extra check in case the target was removed while we were initializing.
538+
// It's possible to be halfway-through enabling when the target is removed
539+
// eg try loading 'devtools://devtools/bundled/devtools_app.html?ws=127.0.0.1:99/blah'
540+
if (!this.#target) {
541+
return;
542+
}
543+
const {identifier} = await this.#target?.pageAgent().invoke_addScriptToEvaluateOnNewDocument({
538544
source,
539545
worldName: LIVE_METRICS_WORLD_NAME,
540546
runImmediately: true,

front_end/panels/timeline/TimelinePanel.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,13 +1053,26 @@ export class TimelinePanel extends Common.ObjectWrapper.eventMixin<EventTypes, t
10531053
}
10541054

10551055
/**
1056-
* Returns false if this was loaded in a standalone context such that recording is
1057-
* not possible, like an enhanced trace (which opens a new devtools window) or
1058-
* trace.cafe.
1056+
* Returns false if DevTools is in a standalone context where tracing/recording are NOT available.
1057+
*
1058+
* This includes scenarios like:
1059+
* - viewing an enhanced trace
1060+
* - viewing a trace in trace.cafe
1061+
* - other devtools_app.html scenarios without valid `ws=` param.
1062+
* - See also the `isHostedMode` comment in `InspectorFrontendHost.ts`
1063+
*
1064+
* Possible signals to find a no-record (NR) context:
1065+
* - `primaryPageTarget()?.sessionId` is empty in NR, but populated when viewing an enhanced trace.
1066+
* - `primaryPageTarget.#capabilitiesMask` There's a tracing capability but the advertised capabilities are quite unreliable.
1067+
* - `primaryPageTarget.targets().length === 1` Mostly correct for NC but its 2 when viewing an enhanced trace.
1068+
* - `primaryPageTarget.router().connection()` Perhaps StubConnection or RehydratingConnection but MainConnection is incorrectly used sometimes. (eg devtools://devtools/bundled/devtools_app.html)
1069+
* - `resourceTreeModel?.mainFrame === null`. Correct for NR, HOWEVER Node.js canRecord despite no main frame.
1070+
* - `rootTarget.type !== 'tab'` Has potential but it lies. (It's "browser" for Node despite a node type)
1071+
*
1072+
* The best signal, for now, is this combo (`isNode || hasMainFrame`), which is both well-maintained and correct in all known cases:
10591073
*/
10601074
private canRecord(): boolean {
1061-
// TODO(paulirish) Determine a more robust method as checking `primaryPageTarget()?.sessionId` isn't accurate.
1062-
return true;
1075+
return SDK.TargetManager.TargetManager.instance().hasFakeConnection() === false;
10631076
}
10641077

10651078
private populateToolbar(): void {

front_end/panels/timeline/TimingsTrackAppender.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ export class TimingsTrackAppender implements TrackAppender {
8383
timestampEvents.length === 0 && consoleTimings.length === 0) {
8484
return trackStartLevel;
8585
}
86+
// TODO(paulirish): these 5 sets of events should be merged and sorted by start time. This would allow for a denser packing.
8687
this.#appendTrackHeaderAtLevel(trackStartLevel, expanded);
8788
let newLevel = this.#appendExtensionsAtLevel(trackStartLevel);
8889
newLevel = this.#compatibilityBuilder.appendEventsAtLevel(performanceMarks, newLevel, this);

front_end/panels/timeline/components/LiveMetricsView.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,9 @@ export class LiveMetricsView extends LegacyWrapper.LegacyWrapper.WrappableCompon
385385
}
386386

387387
async #refreshFieldDataForCurrentPage(): Promise<void> {
388-
await this.#cruxManager.refresh();
388+
if (!this.#isNode) {
389+
await this.#cruxManager.refresh();
390+
}
389391
void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
390392
}
391393

front_end/ui/visual_logging/KnownContextValues.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2637,6 +2637,7 @@ export const knownContextValues = new Set([
26372637
'node',
26382638
'node-connection',
26392639
'node-id',
2640+
'node-js-debugging',
26402641
'node-removed',
26412642
'nodes',
26422643
'nominal',

0 commit comments

Comments
 (0)