Skip to content

Commit beff1b5

Browse files
bmeurerDevtools-frontend LUCI CQ
authored andcommitted
[project_settings] Load devtools.json for DevTools-on-DevTools.
For the DevTools-on-DevTools case, we need to add some additional magic in the `ProjectSettingsModel` in order to also load the project settings from the well-known URI, mainly because the base URL is different from the origin (`devtools://devtools/bundled` vs. `devtools://devtools`). Bug: 399502654, 395037775 Change-Id: I7d22320124ab0790a2257b6be563b8499a343048 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6304925 Commit-Queue: Simon Zünd <[email protected]> Reviewed-by: Simon Zünd <[email protected]> Auto-Submit: Benedikt Meurer <[email protected]>
1 parent 0227afd commit beff1b5

File tree

2 files changed

+119
-4
lines changed

2 files changed

+119
-4
lines changed

front_end/models/project_settings/ProjectSettingsModel.test.ts

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ describe('ProjectSettingsModel', () => {
6060
const frame = sinon.createStubInstance(SDK.ResourceTreeModel.ResourceTreeFrame);
6161
resourceTreeModel.mainFrame = frame;
6262
sinon.stub(frame, 'securityOriginDetails').get(() => ({isLocalhost: false}));
63+
sinon.stub(frame, 'url').get(() => urlString`http://www.example.com/`);
6364

6465
const projectSettingsModel = ProjectSettingsModel.instance({
6566
forceNew: true,
@@ -92,8 +93,9 @@ describe('ProjectSettingsModel', () => {
9293
sinon.stub(frame, 'url').get(() => initiatorUrl);
9394
sinon.stub(frame, 'id').get(() => frameId);
9495

96+
const content = '{"workspace":{"root":"/home/foo","uuid":"8f7b028c-0323-485f-bcb9-b404edc0f186"}}';
9597
pageResourceLoader.loadResource.withArgs(url, sinon.match({target, frameId, initiatorUrl}))
96-
.returns(Promise.resolve({content: '{"workspace":{"root":"/home/foo","uuid":"foo"}}'}));
98+
.returns(Promise.resolve({content}));
9799

98100
const projectSettingsModel = ProjectSettingsModel.instance({
99101
forceNew: true,
@@ -106,7 +108,72 @@ describe('ProjectSettingsModel', () => {
106108
assert.deepEqual(projectSettings, {
107109
workspace: {
108110
root: '/home/foo' as Platform.DevToolsPath.RawPathString,
109-
uuid: 'foo',
111+
uuid: '8f7b028c-0323-485f-bcb9-b404edc0f186',
112+
}
113+
});
114+
});
115+
116+
it('doesn\'t load the devtools.json from DevTools-on-DevTools (without --custom-devtools-frontend)', async () => {
117+
const hostConfig = {devToolsWellKnown: {enabled: true}};
118+
const pageResourceLoader = sinon.createStubInstance(SDK.PageResourceLoader.PageResourceLoader);
119+
const targetManager = sinon.createStubInstance(SDK.TargetManager.TargetManager);
120+
121+
const target = sinon.createStubInstance(SDK.Target.Target);
122+
targetManager.primaryPageTarget.returns(target);
123+
124+
const resourceTreeModel = sinon.createStubInstance(SDK.ResourceTreeModel.ResourceTreeModel);
125+
target.model.withArgs(SDK.ResourceTreeModel.ResourceTreeModel).returns(resourceTreeModel);
126+
127+
const frame = sinon.createStubInstance(SDK.ResourceTreeModel.ResourceTreeFrame);
128+
resourceTreeModel.mainFrame = frame;
129+
sinon.stub(frame, 'url').get(() => urlString`devtools://devtools/bundled/devtools_app.html`);
130+
131+
const projectSettingsModel = ProjectSettingsModel.instance({
132+
forceNew: true,
133+
hostConfig,
134+
pageResourceLoader,
135+
targetManager,
136+
});
137+
138+
const projectSettings = await projectSettingsModel.projectSettingsPromise;
139+
assert.deepEqual(projectSettings, {});
140+
});
141+
142+
it('correctly loads the devtools.json for DevTools-on-DevTools (with --custom-devtools-frontend)', async () => {
143+
const hostConfig = {devToolsWellKnown: {enabled: true}};
144+
const pageResourceLoader = sinon.createStubInstance(SDK.PageResourceLoader.PageResourceLoader);
145+
const targetManager = sinon.createStubInstance(SDK.TargetManager.TargetManager);
146+
147+
const target = sinon.createStubInstance(SDK.Target.Target);
148+
targetManager.primaryPageTarget.returns(target);
149+
150+
const resourceTreeModel = sinon.createStubInstance(SDK.ResourceTreeModel.ResourceTreeModel);
151+
target.model.withArgs(SDK.ResourceTreeModel.ResourceTreeModel).returns(resourceTreeModel);
152+
153+
const url = urlString`devtools://devtools/bundled/.well-known/appspecific/com.chrome.devtools.json`;
154+
const frameId = 'mainFrame';
155+
const initiatorUrl = urlString`devtools://devtools/bundled/devtools_app.html?debugFrontend=true`;
156+
const frame = sinon.createStubInstance(SDK.ResourceTreeModel.ResourceTreeFrame);
157+
resourceTreeModel.mainFrame = frame;
158+
sinon.stub(frame, 'url').get(() => initiatorUrl);
159+
sinon.stub(frame, 'id').get(() => frameId);
160+
161+
const content = '{"workspace":{"root":"/path/to/front_end","uuid":"5a509b03-1da9-460d-bc38-0c8166ba0c41"}}';
162+
pageResourceLoader.loadResource.withArgs(url, sinon.match({target, frameId, initiatorUrl}))
163+
.returns(Promise.resolve({content}));
164+
165+
const projectSettingsModel = ProjectSettingsModel.instance({
166+
forceNew: true,
167+
hostConfig,
168+
pageResourceLoader,
169+
targetManager,
170+
});
171+
172+
const projectSettings = await projectSettingsModel.projectSettingsPromise;
173+
assert.deepEqual(projectSettings, {
174+
workspace: {
175+
root: '/path/to/front_end' as Platform.DevToolsPath.RawPathString,
176+
uuid: '5a509b03-1da9-460d-bc38-0c8166ba0c41',
110177
}
111178
});
112179
});

front_end/models/project_settings/ProjectSettingsModel.ts

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,50 @@ import * as Platform from '../../core/platform/platform.js';
77
import type * as Root from '../../core/root/root.js';
88
import * as SDK from '../../core/sdk/sdk.js';
99

10+
/** The security origin for all DevTools (front-end) resources. */
11+
const DEVTOOLS_SECURITY_ORIGIN = 'devtools://devtools';
12+
13+
/** The (absolute) path to the project settings file. */
14+
const WELL_KNOWN_DEVTOOLS_JSON_PATH = '/.well-known/appspecific/com.chrome.devtools.json';
15+
16+
/**
17+
* Checks if the origin of the `url` is `devtools://devtools` (meaning that it's
18+
* served by the `DevToolsDataSource` in Chromium) and it's path starts with
19+
* `/bundled/`.
20+
*
21+
* @param url the URL string to check.
22+
* @returns `true` if `url` refers to a resource in the Chromium DevTools bundle.
23+
*/
24+
function isDevToolsBundledURL(url: string): boolean {
25+
return url.startsWith(`${DEVTOOLS_SECURITY_ORIGIN}/bundled/`);
26+
}
27+
28+
/**
29+
* Checks if the `frame` should be considered local and safe for loading the
30+
* project settings from.
31+
*
32+
* This checks the security origin of `frame` for whether Chromium considers it
33+
* to be localhost. It also supports special logic for when the origin of the
34+
* `frame` is `'devtools://devtools'`, in which case we check whether the path
35+
* starts with `'/bundled/'` and `debugFrontend=true` is passed as a query
36+
* parameter (indicating that `--custom-devtools-frontend=` command line option
37+
* was used).
38+
*
39+
* @param frame the `ResourceTreeFrame` to check.
40+
* @returns `true` if `frame` is considered safe for loading the project settings.
41+
* @see https://goo.gle/devtools-json-design
42+
*/
43+
function isLocalFrame(frame: SDK.ResourceTreeModel.ResourceTreeFrame|null|undefined):
44+
frame is SDK.ResourceTreeModel.ResourceTreeFrame {
45+
if (!frame) {
46+
return false;
47+
}
48+
if (isDevToolsBundledURL(frame.url)) {
49+
return new URL(frame.url).searchParams.get('debugFrontend') === 'true';
50+
}
51+
return frame.securityOriginDetails?.isLocalhost ?? false;
52+
}
53+
1054
/**
1155
* The structure of the project settings.
1256
*
@@ -124,12 +168,16 @@ export class ProjectSettingsModel extends Common.ObjectWrapper.ObjectWrapper<Eve
124168

125169
async #loadAndValidateProjectSettings(target: SDK.Target.Target): Promise<ProjectSettings> {
126170
const frame = target.model(SDK.ResourceTreeModel.ResourceTreeModel)?.mainFrame;
127-
if (!frame?.securityOriginDetails?.isLocalhost) {
171+
if (!isLocalFrame(frame)) {
128172
return EMPTY_PROJECT_SETTINGS;
129173
}
130174
const initiatorUrl = frame.url;
131175
const frameId = frame.id;
132-
const url = new URL('/.well-known/appspecific/com.chrome.devtools.json', initiatorUrl);
176+
let url = WELL_KNOWN_DEVTOOLS_JSON_PATH;
177+
if (isDevToolsBundledURL(initiatorUrl)) {
178+
url = '/bundled' + url;
179+
}
180+
url = new URL(url, initiatorUrl).toString();
133181
const {content} = await this.#pageResourceLoader.loadResource(
134182
Platform.DevToolsPath.urlString`${url}`,
135183
{target, frameId, initiatorUrl},

0 commit comments

Comments
 (0)