Skip to content

Commit ba3c245

Browse files
hashseedDevtools-frontend LUCI CQ
authored andcommitted
Add context menu to copy Network request payload
Fixed: 40206460 Change-Id: I6abce1ebeeeff6f7d5116ad2da3fcf33f836f57c Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6169136 Commit-Queue: Yang Guo <[email protected]> Reviewed-by: Danil Somsikov <[email protected]>
1 parent 8b387fb commit ba3c245

File tree

3 files changed

+37
-9
lines changed

3 files changed

+37
-9
lines changed

front_end/panels/network/RequestPayloadView.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,13 @@ import requestPayloadTreeStyles from './requestPayloadTree.css.js';
4949
import requestPayloadViewStyles from './requestPayloadView.css.js';
5050
const UIStrings = {
5151
/**
52-
*@description A context menu item in the Watch Expressions Sidebar Pane of the Sources panel and Network pane request.
52+
*@description A context menu item Payload View of the Network panel to copy a parsed value.
5353
*/
5454
copyValue: 'Copy value',
55+
/**
56+
*@description A context menu item Payload View of the Network panel to copy the payload.
57+
*/
58+
copyPayload: 'Copy',
5559
/**
5660
* @description Text in Request Payload View of the Network panel. This is a noun-phrase meaning the
5761
* payload of a network request.
@@ -160,17 +164,16 @@ export class RequestPayloadView extends UI.Widget.VBox {
160164
this.request.removeEventListener(SDK.NetworkRequest.Events.REQUEST_HEADERS_CHANGED, this.refreshFormData, this);
161165
}
162166

163-
private addEntryContextMenuHandler(treeElement: UI.TreeOutline.TreeElement, value: string): void {
167+
private addEntryContextMenuHandler(
168+
treeElement: UI.TreeOutline.TreeElement, menuItem: string, jslogContext: string, getValue: () => string): void {
164169
treeElement.listItemElement.addEventListener('contextmenu', event => {
165170
event.consume(true);
166171
const contextMenu = new UI.ContextMenu.ContextMenu(event);
167-
const decodedValue = decodeURIComponent(value);
168-
const copyDecodedValueHandler = (): void => {
172+
const copyValueHandler = (): void => {
169173
Host.userMetrics.actionTaken(Host.UserMetrics.Action.NetworkPanelCopyValue);
170-
Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(decodedValue);
174+
Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(getValue());
171175
};
172-
contextMenu.clipboardSection().appendItem(
173-
i18nString(UIStrings.copyValue), copyDecodedValueHandler, {jslogContext: 'copy-value'});
176+
contextMenu.clipboardSection().appendItem(menuItem, copyValueHandler, {jslogContext});
174177
void contextMenu.show();
175178
});
176179
}
@@ -249,8 +252,10 @@ export class RequestPayloadView extends UI.Widget.VBox {
249252
sourceTextElement.textContent = trim ? text.substr(0, MAX_LENGTH) : text;
250253

251254
const sourceTreeElement = new UI.TreeOutline.TreeElement(sourceTextElement);
255+
252256
treeElement.removeChildren();
253257
treeElement.appendChild(sourceTreeElement);
258+
this.addEntryContextMenuHandler(sourceTreeElement, i18nString(UIStrings.copyPayload), 'copy-payload', () => text);
254259
if (!trim) {
255260
return;
256261
}
@@ -350,7 +355,8 @@ export class RequestPayloadView extends UI.Widget.VBox {
350355
}
351356

352357
const paramTreeElement = new UI.TreeOutline.TreeElement(paramNameValue);
353-
this.addEntryContextMenuHandler(paramTreeElement, param.value);
358+
this.addEntryContextMenuHandler(
359+
paramTreeElement, i18nString(UIStrings.copyValue), 'copy-value', () => decodeURIComponent(param.value));
354360
paramsTreeElement.appendChild(paramTreeElement);
355361
}
356362

test/e2e/network/network-request-view_test.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ import {
1515
getResourcesPath,
1616
getTextContent,
1717
pasteText,
18+
readClipboard,
1819
step,
1920
typeText,
2021
waitFor,
2122
waitForAria,
2223
waitForElementWithTextContent,
2324
waitForFunction,
2425
} from '../../shared/helper.js';
25-
2626
import {CONSOLE_TAB_SELECTOR, focusConsolePrompt} from '../helpers/console-helpers.js';
2727
import {triggerLocalFindDialog} from '../helpers/memory-helpers.js';
2828
import {
@@ -487,6 +487,20 @@ describe('The Network Request view', () => {
487487
].flat();
488488

489489
assertOutlineMatches(expectedPayloadContent, payloadOutlineText);
490+
491+
// Context menu to copy single parsed entry.
492+
const parsedEntry = await waitForElementWithTextContent('alpha');
493+
await parsedEntry.click({button: 'right'});
494+
await (await waitForElementWithTextContent('Copy value')).click();
495+
assert.strictEqual(await readClipboard(), 'alpha');
496+
497+
// Context menu to copy the raw payload.
498+
const viewSource = await waitForElementWithTextContent('view source');
499+
await viewSource.click();
500+
const source = await waitForElementWithTextContent('id=42&param=a%20b');
501+
await source.click({button: 'right'});
502+
await (await waitForElementWithTextContent('Copy')).click();
503+
assert.strictEqual(await readClipboard(), 'id=42&param=a%20b');
490504
});
491505

492506
it('shows raw headers', async () => {

test/shared/helper.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,3 +824,11 @@ export async function raf(page: puppeteer.Page): Promise<void> {
824824
return new Promise(resolve => window.requestAnimationFrame(resolve));
825825
});
826826
}
827+
828+
export async function readClipboard() {
829+
const {frontend, browser} = getBrowserAndPages();
830+
await browser.defaultBrowserContext().overridePermissions(frontend.url(), ['clipboard-read']);
831+
const clipboard = await frontend.evaluate(async () => navigator.clipboard.readText());
832+
await browser.defaultBrowserContext().clearPermissionOverrides();
833+
return clipboard;
834+
}

0 commit comments

Comments
 (0)