mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-03 18:47:53 +00:00
Bug 1442249 - Add Copy context menu to PropertiesView. r=Honza
Differential Revision: https://phabricator.services.mozilla.com/D23770 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
69592562e9
commit
62b3b67137
@ -917,7 +917,7 @@ netmonitor.trackingResource.tooltip=This URL matches a known tracker and it woul
|
||||
netmonitor.context.copy=Copy
|
||||
|
||||
# LOCALIZATION NOTE (netmonitor.context.copy.accesskey): This is the access key
|
||||
# for the copy sub-menu displayed in the context menu for a request
|
||||
# for the copy menu/sub-menu displayed in the context menu for a request
|
||||
netmonitor.context.copy.accesskey=C
|
||||
|
||||
# LOCALIZATION NOTE (netmonitor.context.copyUrl): This is the label displayed
|
||||
@ -994,6 +994,14 @@ netmonitor.context.saveImageAs=Save Image As
|
||||
# for the Copy Image As Data URI menu item displayed in the context menu for a request
|
||||
netmonitor.context.saveImageAs.accesskey=v
|
||||
|
||||
# LOCALIZATION NOTE (netmonitor.context.copyAll): This is the label displayed
|
||||
# on the context menu that copies all data
|
||||
netmonitor.context.copyAll=Copy All
|
||||
|
||||
# LOCALIZATION NOTE (netmonitor.context.copyAll.accesskey): This is the access key
|
||||
# for the Copy All menu item displayed in the context menu for a properties view panel
|
||||
netmonitor.context.copyAll.accesskey=A
|
||||
|
||||
# LOCALIZATION NOTE (netmonitor.context.copyAllAsHar): This is the label displayed
|
||||
# on the context menu that copies all as HAR format
|
||||
netmonitor.context.copyAllAsHar=Copy All As HAR
|
||||
|
@ -15,6 +15,7 @@ const { FILTER_SEARCH_DELAY } = require("../constants");
|
||||
|
||||
// Components
|
||||
const TreeViewClass = require("devtools/client/shared/components/tree/TreeView");
|
||||
const PropertiesViewContextMenu = require("../widgets/PropertiesViewContextMenu");
|
||||
const TreeView = createFactory(TreeViewClass);
|
||||
|
||||
loader.lazyGetter(this, "SearchBox", function() {
|
||||
@ -155,6 +156,23 @@ class PropertiesView extends Component {
|
||||
return TreeRow(props);
|
||||
}
|
||||
|
||||
onContextMenuRow(member, evt) {
|
||||
evt.preventDefault();
|
||||
|
||||
const { object } = member;
|
||||
|
||||
// Select the right clicked row
|
||||
this.selectRow(evt.currentTarget);
|
||||
|
||||
// if data exists and can be copied, then show the contextmenu
|
||||
if (typeof (object) === "object") {
|
||||
if (!this.contextMenu) {
|
||||
this.contextMenu = new PropertiesViewContextMenu({});
|
||||
}
|
||||
this.contextMenu.open(evt, { member, object: this.props.object });
|
||||
}
|
||||
}
|
||||
|
||||
renderValueWithRep(props) {
|
||||
const { member } = props;
|
||||
|
||||
@ -240,6 +258,7 @@ class PropertiesView extends Component {
|
||||
renderRow: renderRow || this.renderRowWithExtras,
|
||||
renderValue: renderValue || this.renderValueWithRep,
|
||||
openLink,
|
||||
onContextMenuRow: this.onContextMenuRow,
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
@ -0,0 +1,65 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { L10N } = require("../utils/l10n");
|
||||
|
||||
loader.lazyRequireGetter(this, "copyString", "devtools/shared/platform/clipboard", true);
|
||||
loader.lazyRequireGetter(this, "showMenu", "devtools/client/shared/components/menu/utils", true);
|
||||
|
||||
class PropertiesViewContextMenu {
|
||||
constructor(props) {
|
||||
this.props = props;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the context menu opening.
|
||||
* @param {*} event open event
|
||||
* @param {*} member member of the right-clicked row
|
||||
* @param {*} object the whole data object
|
||||
*/
|
||||
open(event = {}, { member, object }) {
|
||||
const menu = [];
|
||||
|
||||
menu.push({
|
||||
id: "properties-view-context-menu-copy",
|
||||
label: L10N.getStr("netmonitor.context.copy"),
|
||||
accesskey: L10N.getStr("netmonitor.context.copy.accesskey"),
|
||||
click: () => this.copySelected(member),
|
||||
});
|
||||
|
||||
menu.push({
|
||||
id: "properties-view-context-menu-copyall",
|
||||
label: L10N.getStr("netmonitor.context.copyAll"),
|
||||
accesskey: L10N.getStr("netmonitor.context.copyAll.accesskey"),
|
||||
click: () => this.copyAll(object),
|
||||
});
|
||||
|
||||
showMenu(menu, {
|
||||
screenX: event.screenX,
|
||||
screenY: event.screenY,
|
||||
});
|
||||
}
|
||||
|
||||
copyAll(data) {
|
||||
try {
|
||||
copyString(JSON.stringify(data));
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
copySelected({ object, hasChildren }) {
|
||||
if (hasChildren) {
|
||||
// If has children, copy the data as JSON
|
||||
try {
|
||||
copyString(JSON.stringify({ [object.name]: object.value }));
|
||||
} catch (error) {}
|
||||
} else {
|
||||
// Copy the data as key-value format
|
||||
copyString(`${object.name}: ${object.value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PropertiesViewContextMenu;
|
@ -3,6 +3,7 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DevToolsModules(
|
||||
'PropertiesViewContextMenu.js',
|
||||
'RequestListContextMenu.js',
|
||||
'RequestListHeaderContextMenu.js',
|
||||
'WaterfallBackground.js',
|
||||
|
@ -103,6 +103,8 @@ subsuite = clipboard
|
||||
subsuite = clipboard
|
||||
[browser_net_copy_url.js]
|
||||
subsuite = clipboard
|
||||
[browser_net_propertiesview-copy.js]
|
||||
subsuite = clipboard
|
||||
[browser_net_copy_params.js]
|
||||
subsuite = clipboard
|
||||
skip-if = (verify && !debug && (os == 'mac')) # bug 1328915, disable linux32 debug devtools for timeouts
|
||||
|
@ -0,0 +1,120 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Test if response JSON in PropertiesView can be copied
|
||||
*/
|
||||
|
||||
add_task(async function() {
|
||||
const { tab, monitor } = await initNetMonitor(JSON_BASIC_URL + "?name=nogrip");
|
||||
info("Starting test... ");
|
||||
|
||||
const { document, store, windowRequire } = monitor.panelWin;
|
||||
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
||||
|
||||
store.dispatch(Actions.batchEnable(false));
|
||||
|
||||
await performRequests(monitor, tab, 1);
|
||||
|
||||
const onResponsePanelReady = waitForDOM(document, "#response-panel .treeTable");
|
||||
store.dispatch(Actions.toggleNetworkDetails());
|
||||
EventUtils.sendMouseEvent({ type: "click" },
|
||||
document.querySelector("#response-tab"));
|
||||
await onResponsePanelReady;
|
||||
|
||||
const responsePanel = document.querySelector("#response-panel");
|
||||
|
||||
const objectRow = responsePanel.querySelectorAll(".objectRow")[1];
|
||||
const stringRow = responsePanel.querySelectorAll(".stringRow")[0];
|
||||
|
||||
/* Test for copy an object */
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" },
|
||||
objectRow);
|
||||
await waitForClipboardPromise(function setup() {
|
||||
monitor.panelWin.parent.document
|
||||
.querySelector("#properties-view-context-menu-copy").click();
|
||||
}, `{"obj":{"type":"string"}}`);
|
||||
|
||||
/* Test for copy all */
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" },
|
||||
objectRow);
|
||||
await waitForClipboardPromise(function setup() {
|
||||
monitor.panelWin.parent.document
|
||||
.querySelector("#properties-view-context-menu-copyall").click();
|
||||
}, `{"JSON":{"obj":{"type":"string"}},` +
|
||||
`"Response payload":{"EDITOR_CONFIG":{"text":` +
|
||||
`"{\\"obj\\": {\\"type\\": \\"string\\" }}","mode":"application/json"}}}`);
|
||||
|
||||
/* Test for copy a single row */
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" },
|
||||
stringRow);
|
||||
await waitForClipboardPromise(function setup() {
|
||||
monitor.panelWin.parent.document
|
||||
.querySelector("#properties-view-context-menu-copy").click();
|
||||
}, "type: string");
|
||||
|
||||
await teardown(monitor);
|
||||
});
|
||||
|
||||
/**
|
||||
* Test if response/request Cookies in PropertiesView can be copied
|
||||
*/
|
||||
|
||||
add_task(async function() {
|
||||
const { tab, monitor } = await initNetMonitor(SIMPLE_UNSORTED_COOKIES_SJS);
|
||||
info("Starting test... ");
|
||||
|
||||
const { document, store, windowRequire } = monitor.panelWin;
|
||||
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
||||
|
||||
store.dispatch(Actions.batchEnable(false));
|
||||
|
||||
tab.linkedBrowser.reload();
|
||||
|
||||
let wait = waitForNetworkEvents(monitor, 1);
|
||||
await wait;
|
||||
|
||||
wait = waitForDOM(document, ".headers-overview");
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
document.querySelectorAll(".request-list-item")[0]);
|
||||
await wait;
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
document.querySelectorAll(".request-list-item")[0]);
|
||||
EventUtils.sendMouseEvent({ type: "click" },
|
||||
document.querySelector("#cookies-tab"));
|
||||
|
||||
const cookiesPanel = document.querySelector("#cookies-panel");
|
||||
|
||||
const objectRows = cookiesPanel.querySelectorAll(".objectRow:not(.tree-section)");
|
||||
const stringRows = cookiesPanel.querySelectorAll(".stringRow");
|
||||
|
||||
const expectedResponseCookies = [
|
||||
{ bob: { httpOnly: true, value: "true" } },
|
||||
{ foo: { httpOnly: true, value: "bar" } },
|
||||
{ tom: { httpOnly: true, value: "cool" } }];
|
||||
for (let i = 0; i < objectRows.length; i++) {
|
||||
const cur = objectRows[i];
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" },
|
||||
cur);
|
||||
await waitForClipboardPromise(function setup() {
|
||||
monitor.panelWin.parent.document
|
||||
.querySelector("#properties-view-context-menu-copy").click();
|
||||
}, JSON.stringify(expectedResponseCookies[i]));
|
||||
}
|
||||
|
||||
const expectedRequestCookies = ["bob: true", "foo: bar", "tom: cool"];
|
||||
for (let i = 0; i < expectedRequestCookies.length; i++) {
|
||||
const cur = stringRows[objectRows.length + i];
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" },
|
||||
cur);
|
||||
await waitForClipboardPromise(function setup() {
|
||||
monitor.panelWin.parent.document
|
||||
.querySelector("#properties-view-context-menu-copy").click();
|
||||
}, expectedRequestCookies[i]);
|
||||
}
|
||||
|
||||
await teardown(monitor);
|
||||
});
|
@ -127,6 +127,8 @@ define(function(require, exports, module) {
|
||||
onSort: PropTypes.func,
|
||||
// Custom row click callback
|
||||
onClickRow: PropTypes.func,
|
||||
// Row context menu event handler
|
||||
onContextMenuRow: PropTypes.func,
|
||||
// Tree context menu event handler
|
||||
onContextMenuTree: PropTypes.func,
|
||||
// A header is displayed if set to true
|
||||
@ -368,6 +370,13 @@ define(function(require, exports, module) {
|
||||
this.selectRow(event.currentTarget);
|
||||
}
|
||||
|
||||
onContextMenu(member, event) {
|
||||
const onContextMenuRow = this.props.onContextMenuRow;
|
||||
if (onContextMenuRow) {
|
||||
onContextMenuRow.call(this, member, event);
|
||||
}
|
||||
}
|
||||
|
||||
getSelectedRow() {
|
||||
if (!this.state.selected || this.rows.length === 0) {
|
||||
return null;
|
||||
@ -539,6 +548,7 @@ define(function(require, exports, module) {
|
||||
id: member.path,
|
||||
ref: row => row && this.rows.push(row),
|
||||
onClick: this.onClickRow.bind(this, member.path),
|
||||
onContextMenu: this.onContextMenu.bind(this, member),
|
||||
});
|
||||
|
||||
// Render single row.
|
||||
|
Loading…
Reference in New Issue
Block a user