Skip to content

Commit 8dd0bd6

Browse files
pfaffeDevtools-frontend LUCI CQ
authored andcommitted
Reveal throttling rules when clicking the throttle indicator
Bug: 40434685 Change-Id: I4ae42e4c801503b6ce61e7d7b6b7713754f36f0f Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7122022 Commit-Queue: Philip Pfaffe <pfaffe@chromium.org> Reviewed-by: Eric Leese <leese@chromium.org>
1 parent bf3eaa5 commit 8dd0bd6

10 files changed

Lines changed: 119 additions & 27 deletions

front_end/core/common/Revealer.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ const UIStrings = {
2626
* @description The UI destination when right clicking an item that can be revealed
2727
*/
2828
networkPanel: 'Network panel',
29+
/**
30+
* @description The UI destination when right clicking an item that can be revealed
31+
*/
32+
requestConditionsDrawer: 'Request conditions drawer',
2933
/**
3034
* @description The UI destination when right clicking an item that can be revealed
3135
*/
@@ -181,6 +185,7 @@ export const RevealerDestination = {
181185
CHANGES_DRAWER: i18nLazyString(UIStrings.changesDrawer),
182186
ISSUES_VIEW: i18nLazyString(UIStrings.issuesView),
183187
NETWORK_PANEL: i18nLazyString(UIStrings.networkPanel),
188+
REQUEST_CONDITIONS_DRAWER: i18nLazyString(UIStrings.requestConditionsDrawer),
184189
TIMELINE_PANEL: i18nLazyString(UIStrings.timelinePanel),
185190
APPLICATION_PANEL: i18nLazyString(UIStrings.applicationPanel),
186191
SOURCES_PANEL: i18nLazyString(UIStrings.sourcesPanel),

front_end/core/sdk/NetworkManager.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1939,11 +1939,13 @@ export class RequestConditions extends Common.ObjectWrapper.ObjectWrapper<Reques
19391939
return this.#conditionsAppliedForTestPromise;
19401940
}
19411941

1942-
conditionsForId(appliedNetworkConditionsId: string): {
1943-
conditions: Conditions,
1944-
urlPattern?: string,
1945-
}|undefined {
1946-
return this.#requestConditionsById.get(appliedNetworkConditionsId);
1942+
conditionsForId(appliedNetworkConditionsId: string): AppliedNetworkConditions|undefined {
1943+
const requestConditions = this.#requestConditionsById.get(appliedNetworkConditionsId);
1944+
if (!requestConditions) {
1945+
return undefined;
1946+
}
1947+
const {conditions, urlPattern} = requestConditions;
1948+
return new AppliedNetworkConditions(conditions, appliedNetworkConditionsId, urlPattern);
19471949
}
19481950
}
19491951

@@ -1958,6 +1960,12 @@ export namespace RequestConditions {
19581960

19591961
let multiTargetNetworkManagerInstance: MultitargetNetworkManager|null;
19601962

1963+
export class AppliedNetworkConditions {
1964+
constructor(
1965+
readonly conditions: Conditions, readonly appliedNetworkConditionsId: string, readonly urlPattern?: string) {
1966+
}
1967+
}
1968+
19611969
export class MultitargetNetworkManager extends Common.ObjectWrapper.ObjectWrapper<MultitargetNetworkManager.EventTypes>
19621970
implements SDKModelObserver<NetworkManager> {
19631971
#userAgentOverride = '';
@@ -2327,10 +2335,7 @@ export class MultitargetNetworkManager extends Common.ObjectWrapper.ObjectWrappe
23272335
}, allowRemoteFilePaths));
23282336
}
23292337

2330-
appliedRequestConditions(requestInternal: NetworkRequest): {
2331-
conditions: Conditions,
2332-
urlPattern?: string,
2333-
}|undefined {
2338+
appliedRequestConditions(requestInternal: NetworkRequest): AppliedNetworkConditions|undefined {
23342339
if (!requestInternal.appliedNetworkConditionsId) {
23352340
return undefined;
23362341
}

front_end/panels/network/NetworkDataGridNode.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -958,7 +958,7 @@ export class NetworkRequestNode extends NetworkNode {
958958
return this.requestInternal.resourceType() === Common.ResourceType.resourceTypes.Prefetch;
959959
}
960960

961-
throttlingConditions(): {conditions: SDK.NetworkManager.Conditions, urlPattern?: string}|undefined {
961+
throttlingConditions(): SDK.NetworkManager.AppliedNetworkConditions|undefined {
962962
return SDK.NetworkManager.MultitargetNetworkManager.instance().appliedRequestConditions(this.requestInternal);
963963
}
964964

@@ -1522,6 +1522,7 @@ export class NetworkRequestNode extends NetworkNode {
15221522
throttlingConditions.conditions.title();
15231523
const icon = IconButton.Icon.create('watch');
15241524
icon.title = i18nString(UIStrings.wasThrottled, {PH1: throttlingConditionsTitle});
1525+
icon.addEventListener('click', () => void Common.Revealer.reveal(throttlingConditions));
15251526
cell.append(icon);
15261527
}
15271528
if (this.requestInternal.duration > 0) {

front_end/panels/network/NetworkLogView.test.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,9 +1260,15 @@ Invoke-WebRequest -UseBasicParsing -Uri "url-header-und-content-overridden"`]);
12601260

12611261
await assertScreenshot('network-log/throttled-request.png');
12621262

1263-
assert.deepEqual(
1264-
Array.from(container.querySelectorAll('devtools-icon')).map(e => e.title),
1265-
['Other (throttled to 3G)', 'Request was throttled (3G)']);
1263+
await RenderCoordinator.done();
1264+
const icons = Array.from(container.querySelectorAll('devtools-icon'));
1265+
assert.deepEqual(icons.map(e => e.title), ['Other (throttled to 3G)', 'Request was throttled (3G)']);
1266+
1267+
const appliedConditions = SDK.NetworkManager.MultitargetNetworkManager.instance().appliedRequestConditions(request);
1268+
assert.exists(appliedConditions);
1269+
const revealStub = sinon.stub(Common.Revealer.RevealerRegistry.instance(), 'reveal');
1270+
icons[1].click();
1271+
sinon.assert.calledOnceWithExactly(revealStub, appliedConditions, false);
12661272
});
12671273
});
12681274

front_end/panels/network/RequestConditionsDrawer.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ import * as Protocol from '../../generated/protocol.js';
88
import * as Logs from '../../models/logs/logs.js';
99
import {assertScreenshot, dispatchClickEvent, renderElementIntoDOM} from '../../testing/DOMHelpers.js';
1010
import {createTarget, registerNoopActions, updateHostConfig} from '../../testing/EnvironmentHelpers.js';
11+
import {expectCall} from '../../testing/ExpectStubCall.js';
1112
import {describeWithMockConnection, setMockConnectionResponseHandler} from '../../testing/MockConnection.js';
1213
import {createViewFunctionStub} from '../../testing/ViewFunctionHelpers.js';
14+
import * as UI from '../../ui/legacy/legacy.js';
15+
import * as PanelUtils from '../utils/utils.js';
1316

1417
import * as Network from './network.js';
1518

@@ -240,4 +243,27 @@ describeWithMockConnection('RequestConditionsDrawer', () => {
240243
sinon.assert.calledOnceWithExactly(increasePriority, condition);
241244
sinon.assert.calledOnceWithExactly(decreasePriority, condition);
242245
});
246+
247+
it('highlights conditions', async () => {
248+
const requestConditionsDrawer = new Network.RequestConditionsDrawer.RequestConditionsDrawer();
249+
UI.Context.Context.instance().setFlavor(
250+
Network.RequestConditionsDrawer.RequestConditionsDrawer, requestConditionsDrawer);
251+
const index = 0;
252+
const urlPattern = 'http://example.com/*bar' as SDK.NetworkManager.URLPatternConstructorString;
253+
const conditions = SDK.NetworkManager.RequestCondition.createFromSetting({
254+
urlPattern,
255+
enabled: true,
256+
conditions: 'NO_THROTTLING' as SDK.NetworkManager.ThrottlingConditionKey,
257+
});
258+
conditions.ruleIds.add('abc');
259+
SDK.NetworkManager.MultitargetNetworkManager.instance().requestConditions.add(conditions);
260+
const item = requestConditionsDrawer.renderItem(conditions, /* editable=*/ true, index);
261+
const viewShown = expectCall(sinon.stub(UI.ViewManager.ViewManager.instance(), 'showView').resolves());
262+
const highlighted = expectCall(sinon.stub(PanelUtils.PanelUtils, 'highlightElement'));
263+
void Network.RequestConditionsDrawer.RequestConditionsDrawer.reveal(
264+
new SDK.NetworkManager.AppliedNetworkConditions(SDK.NetworkManager.NoThrottlingConditions, 'abc', urlPattern));
265+
266+
assert.deepEqual(['network.blocked-urls'], await viewShown);
267+
assert.deepEqual([item], await highlighted);
268+
});
243269
});

front_end/panels/network/RequestConditionsDrawer.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import * as UI from '../../ui/legacy/legacy.js';
1818
import {Directives, html, type LitTemplate, nothing, render} from '../../ui/lit/lit.js';
1919
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
2020
import * as MobileThrottling from '../mobile_throttling/mobile_throttling.js';
21+
import * as PanelUtils from '../utils/utils.js';
2122

2223
import requestConditionsDrawerStyles from './requestConditionsDrawer.css.js';
2324

@@ -196,6 +197,7 @@ export class RequestConditionsDrawer extends UI.Widget.VBox implements
196197
private blockedCountForUrl: Map<Platform.DevToolsPath.UrlString, number>;
197198
#throttledCount = new Map<string, number>();
198199
#view: View;
200+
#listElements = new WeakMap<SDK.NetworkManager.RequestCondition, HTMLElement>();
199201

200202
constructor(target?: HTMLElement, view = DEFAULT_VIEW) {
201203
super(target, {
@@ -252,6 +254,7 @@ export class RequestConditionsDrawer extends UI.Widget.VBox implements
252254
const blockedCount = this.blockedRequestsCount(condition);
253255
const throttledCount = this.#throttledRequestsCount(condition);
254256
const element = document.createElement('div');
257+
this.#listElements.set(condition, element);
255258
element.classList.add('blocked-url');
256259
const toggle = (e: Event): void => {
257260
if (editable) {
@@ -528,6 +531,20 @@ export class RequestConditionsDrawer extends UI.Widget.VBox implements
528531
super.willHide();
529532
UI.Context.Context.instance().setFlavor(RequestConditionsDrawer, null);
530533
}
534+
535+
static async reveal(appliedConditions: SDK.NetworkManager.AppliedNetworkConditions): Promise<void> {
536+
await UI.ViewManager.ViewManager.instance().showView('network.blocked-urls');
537+
const drawer = UI.Context.Context.instance().flavor(RequestConditionsDrawer);
538+
if (!drawer) {
539+
console.assert(!!drawer, 'Drawer not initialized');
540+
return;
541+
}
542+
const conditions = drawer.manager.requestConditions.conditions.find(
543+
condition => condition.ruleIds.has(appliedConditions.appliedNetworkConditionsId) &&
544+
condition.constructorString && condition.constructorString === appliedConditions.urlPattern);
545+
const element = (conditions && drawer.#listElements.get(conditions));
546+
element && PanelUtils.PanelUtils.highlightElement(element);
547+
}
531548
}
532549

533550
export class ActionDelegate implements UI.ActionRegistration.ActionDelegate {
@@ -550,3 +567,12 @@ export class ActionDelegate implements UI.ActionRegistration.ActionDelegate {
550567
return false;
551568
}
552569
}
570+
571+
export class AppliedConditionsRevealer implements
572+
Common.Revealer.Revealer<SDK.NetworkManager.AppliedNetworkConditions> {
573+
async reveal(request: SDK.NetworkManager.AppliedNetworkConditions): Promise<void> {
574+
if (request.urlPattern) {
575+
await RequestConditionsDrawer.reveal(request);
576+
}
577+
}
578+
}

front_end/panels/network/RequestTimingView.test.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import * as Common from '../../core/common/common.js';
56
import * as Platform from '../../core/platform/platform.js';
67
import * as SDK from '../../core/sdk/sdk.js';
78
import * as Protocol from '../../generated/protocol.js';
@@ -176,13 +177,15 @@ describeWithLocale('ResourceTimingView', () => {
176177
});
177178

178179
it('Timing table shows throttling indicator', async () => {
180+
stubNoopSettings();
179181
const container = document.createElement('div');
180182
renderElementIntoDOM(container, {includeCommonStyles: true});
181183

182184
const request = createNetworkRequest(
183185
Protocol.Network.ServiceWorkerRouterSource.Cache, Protocol.Network.ServiceWorkerRouterSource.Cache);
184186
const timeRanges = NetworkTimeCalculator.calculateRequestTimeRanges(request, 100);
185187

188+
const wasThrottled = new SDK.NetworkManager.AppliedNetworkConditions(SDK.NetworkManager.Slow3GConditions, '');
186189
const input: Parameters<typeof Network.RequestTimingView.DEFAULT_VIEW>[0] = {
187190
requestUnfinished: false,
188191
requestStartTime: 0,
@@ -193,11 +196,16 @@ describeWithLocale('ResourceTimingView', () => {
193196
timeRanges,
194197
calculator: new NetworkTimeCalculator.NetworkTimeCalculator(true),
195198
serverTimings: [],
196-
wasThrottled: SDK.NetworkManager.Slow3GConditions,
197-
199+
wasThrottled
198200
};
199201

200202
Network.RequestTimingView.DEFAULT_VIEW(input, {}, container);
201203
await assertScreenshot('network/request-timing-view-throttling.png');
204+
205+
const icon = container.querySelector<HTMLElement>('devtools-icon[name=watch]');
206+
assert.exists(icon);
207+
const revealStub = sinon.stub(Common.Revealer.RevealerRegistry.instance(), 'reveal');
208+
icon.click();
209+
sinon.assert.calledOnceWithExactly(revealStub, wasThrottled, false);
202210
});
203211
});

front_end/panels/network/RequestTimingView.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -307,11 +307,16 @@ interface ViewInput {
307307
serverTimings: SDK.ServerTiming.ServerTiming[];
308308
fetchDetails?: UI.TreeOutline.TreeOutlineInShadow;
309309
routerDetails?: UI.TreeOutline.TreeOutlineInShadow;
310-
wasThrottled?: SDK.NetworkManager.Conditions;
310+
wasThrottled?: SDK.NetworkManager.AppliedNetworkConditions;
311311
}
312312

313313
type View = (input: ViewInput, output: object, target: HTMLElement) => void;
314314
export const DEFAULT_VIEW: View = (input, output, target) => {
315+
const revealThrottled = (): void => {
316+
if (input.wasThrottled) {
317+
void Common.Revealer.reveal(input.wasThrottled);
318+
}
319+
};
315320
const scale = 100 / (input.endTime - input.startTime);
316321
const isClickable = (range: NetworkTimeCalculator.RequestTimeRange): boolean =>
317322
range.name === 'serviceworker-respondwith' || range.name === 'serviceworker-routerevaluation';
@@ -376,11 +381,11 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
376381
}
377382
};
378383

379-
const throttledRequestTitle = input.wasThrottled ?
380-
i18nString(
381-
UIStrings.wasThrottled,
382-
{PH1: typeof input.wasThrottled.title === 'string' ? input.wasThrottled.title : input.wasThrottled.title()}) :
383-
undefined;
384+
const throttledRequestTitle = input.wasThrottled ? i18nString(UIStrings.wasThrottled, {
385+
PH1: typeof input.wasThrottled.conditions.title === 'string' ? input.wasThrottled.conditions.title :
386+
input.wasThrottled.conditions.title()
387+
}) :
388+
undefined;
384389

385390
const classes = classMap({
386391
['network-timing-table']: true,
@@ -503,7 +508,7 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
503508
</x-link>
504509
<td></td>
505510
<td class=${input.wasThrottled ? 'throttled' : ''} title=${ifDefined(throttledRequestTitle)}>
506-
${input.wasThrottled ? html` <devtools-icon name=watch ></devtools-icon>` : nothing}
511+
${input.wasThrottled ? html` <devtools-icon name=watch @click=${revealThrottled}></devtools-icon>` : nothing}
507512
${i18n.TimeUtilities.secondsToString(input.totalDuration, true)}
508513
</td>
509514
</tr>
@@ -578,7 +583,7 @@ export class RequestTimingView extends UI.Widget.VBox {
578583
requestUnfinished: false,
579584
fetchDetails: this.#fetchDetailsTree(),
580585
routerDetails: this.#routerDetailsTree(),
581-
wasThrottled: conditions?.urlPattern ? conditions.conditions : undefined,
586+
wasThrottled: conditions?.urlPattern ? conditions : undefined,
582587
timeRanges,
583588
};
584589
this.#view(input, {}, this.contentElement);

front_end/panels/network/network-meta.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,3 +521,14 @@ Common.Revealer.registerRevealer({
521521
return new Network.NetworkPanel.NetworkLogWithFilterRevealer();
522522
},
523523
});
524+
525+
Common.Revealer.registerRevealer({
526+
contextTypes() {
527+
return [SDK.NetworkManager.AppliedNetworkConditions];
528+
},
529+
destination: Common.Revealer.RevealerDestination.NETWORK_PANEL,
530+
async loadRevealer() {
531+
const Network = await loadNetworkModule();
532+
return new Network.RequestConditionsDrawer.AppliedConditionsRevealer();
533+
},
534+
});

front_end/panels/timeline/components/NetworkRequestTooltip.test.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,9 @@ describeWithMockConnection('NetworkRequestTooltip', () => {
1515
const networkRequest = sinon.createStubInstance(SDK.NetworkRequest.NetworkRequest);
1616
sinon.stub(SDK.TraceObject.RevealableNetworkRequest, 'create')
1717
.returns(new SDK.TraceObject.RevealableNetworkRequest(networkRequest));
18-
sinon.stub(SDK.NetworkManager.MultitargetNetworkManager.instance(), 'appliedRequestConditions').returns({
19-
conditions: SDK.NetworkManager.Slow3GConditions,
20-
urlPattern: 'https://example.com',
21-
});
18+
sinon.stub(SDK.NetworkManager.MultitargetNetworkManager.instance(), 'appliedRequestConditions')
19+
.returns(new SDK.NetworkManager.AppliedNetworkConditions(
20+
SDK.NetworkManager.Slow3GConditions, '', 'https://example.com'));
2221
const tooltip = new TimelineComponents.NetworkRequestTooltip.NetworkRequestTooltip();
2322
renderElementIntoDOM(tooltip, {includeCommonStyles: true});
2423
const data: TimelineComponents.NetworkRequestTooltip.NetworkTooltipData = {

0 commit comments

Comments
 (0)