/* 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/. */ /* globals NetMonitorController, NetMonitorView, gNetwork */ "use strict"; const Services = require("Services"); const { Task } = require("devtools/shared/task"); const { Curl } = require("devtools/client/shared/curl"); const { gDevTools } = require("devtools/client/framework/devtools"); const { openRequestInTab } = require("devtools/client/netmonitor/open-request-in-tab"); const Menu = require("devtools/client/framework/menu"); const MenuItem = require("devtools/client/framework/menu-item"); const { L10N } = require("./l10n"); const { formDataURI, getFormDataSections } = require("./request-utils"); loader.lazyRequireGetter(this, "HarExporter", "devtools/client/netmonitor/har/har-exporter", true); loader.lazyServiceGetter(this, "clipboardHelper", "@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper"); loader.lazyRequireGetter(this, "NetworkHelper", "devtools/shared/webconsole/network-helper"); function RequestListContextMenu() {} RequestListContextMenu.prototype = { get selectedItem() { return NetMonitorView.RequestsMenu.selectedItem; }, get items() { return NetMonitorView.RequestsMenu.items; }, /** * Handle the context menu opening. Hide items if no request is selected. * Since visible attribute only accept boolean value but the method call may * return undefined, we use !! to force convert any object to boolean */ open({ screenX = 0, screenY = 0 } = {}) { let selectedItem = this.selectedItem; let menu = new Menu(); menu.append(new MenuItem({ id: "request-menu-context-copy-url", label: L10N.getStr("netmonitor.context.copyUrl"), accesskey: L10N.getStr("netmonitor.context.copyUrl.accesskey"), visible: !!selectedItem, click: () => this.copyUrl(), })); menu.append(new MenuItem({ id: "request-menu-context-copy-url-params", label: L10N.getStr("netmonitor.context.copyUrlParams"), accesskey: L10N.getStr("netmonitor.context.copyUrlParams.accesskey"), visible: !!(selectedItem && NetworkHelper.nsIURL(selectedItem.attachment.url).query), click: () => this.copyUrlParams(), })); menu.append(new MenuItem({ id: "request-menu-context-copy-post-data", label: L10N.getStr("netmonitor.context.copyPostData"), accesskey: L10N.getStr("netmonitor.context.copyPostData.accesskey"), visible: !!(selectedItem && selectedItem.attachment.requestPostData), click: () => this.copyPostData(), })); menu.append(new MenuItem({ id: "request-menu-context-copy-as-curl", label: L10N.getStr("netmonitor.context.copyAsCurl"), accesskey: L10N.getStr("netmonitor.context.copyAsCurl.accesskey"), visible: !!(selectedItem && selectedItem.attachment), click: () => this.copyAsCurl(), })); menu.append(new MenuItem({ type: "separator", visible: !!selectedItem, })); menu.append(new MenuItem({ id: "request-menu-context-copy-request-headers", label: L10N.getStr("netmonitor.context.copyRequestHeaders"), accesskey: L10N.getStr("netmonitor.context.copyRequestHeaders.accesskey"), visible: !!(selectedItem && selectedItem.attachment.requestHeaders), click: () => this.copyRequestHeaders(), })); menu.append(new MenuItem({ id: "response-menu-context-copy-response-headers", label: L10N.getStr("netmonitor.context.copyResponseHeaders"), accesskey: L10N.getStr("netmonitor.context.copyResponseHeaders.accesskey"), visible: !!(selectedItem && selectedItem.attachment.responseHeaders), click: () => this.copyResponseHeaders(), })); menu.append(new MenuItem({ id: "request-menu-context-copy-response", label: L10N.getStr("netmonitor.context.copyResponse"), accesskey: L10N.getStr("netmonitor.context.copyResponse.accesskey"), visible: !!(selectedItem && selectedItem.attachment.responseContent && selectedItem.attachment.responseContent.content.text && selectedItem.attachment.responseContent.content.text.length !== 0), click: () => this.copyResponse(), })); menu.append(new MenuItem({ id: "request-menu-context-copy-image-as-data-uri", label: L10N.getStr("netmonitor.context.copyImageAsDataUri"), accesskey: L10N.getStr("netmonitor.context.copyImageAsDataUri.accesskey"), visible: !!(selectedItem && selectedItem.attachment.responseContent && selectedItem.attachment.responseContent.content .mimeType.includes("image/")), click: () => this.copyImageAsDataUri(), })); menu.append(new MenuItem({ type: "separator", visible: !!selectedItem, })); menu.append(new MenuItem({ id: "request-menu-context-copy-all-as-har", label: L10N.getStr("netmonitor.context.copyAllAsHar"), accesskey: L10N.getStr("netmonitor.context.copyAllAsHar.accesskey"), visible: !!this.items.length, click: () => this.copyAllAsHar(), })); menu.append(new MenuItem({ id: "request-menu-context-save-all-as-har", label: L10N.getStr("netmonitor.context.saveAllAsHar"), accesskey: L10N.getStr("netmonitor.context.saveAllAsHar.accesskey"), visible: !!this.items.length, click: () => this.saveAllAsHar(), })); menu.append(new MenuItem({ type: "separator", visible: !!selectedItem, })); menu.append(new MenuItem({ id: "request-menu-context-resend", label: L10N.getStr("netmonitor.context.editAndResend"), accesskey: L10N.getStr("netmonitor.context.editAndResend.accesskey"), visible: !!(NetMonitorController.supportsCustomRequest && selectedItem && !selectedItem.attachment.isCustom), click: () => NetMonitorView.RequestsMenu.cloneSelectedRequest(), })); menu.append(new MenuItem({ type: "separator", visible: !!selectedItem, })); menu.append(new MenuItem({ id: "request-menu-context-newtab", label: L10N.getStr("netmonitor.context.newTab"), accesskey: L10N.getStr("netmonitor.context.newTab.accesskey"), visible: !!selectedItem, click: () => this.openRequestInTab() })); menu.append(new MenuItem({ id: "request-menu-context-perf", label: L10N.getStr("netmonitor.context.perfTools"), accesskey: L10N.getStr("netmonitor.context.perfTools.accesskey"), visible: !!NetMonitorController.supportsPerfStats, click: () => NetMonitorView.toggleFrontendMode() })); menu.popup(screenX, screenY, NetMonitorController._toolbox); return menu; }, /** * Opens selected item in a new tab. */ openRequestInTab() { openRequestInTab(this.selectedItem.attachment); }, /** * Copy the request url from the currently selected item. */ copyUrl() { clipboardHelper.copyString(this.selectedItem.attachment.url); }, /** * Copy the request url query string parameters from the currently * selected item. */ copyUrlParams() { let { url } = this.selectedItem.attachment; let params = NetworkHelper.nsIURL(url).query.split("&"); let string = params.join(Services.appinfo.OS === "WINNT" ? "\r\n" : "\n"); clipboardHelper.copyString(string); }, /** * Copy the request form data parameters (or raw payload) from * the currently selected item. */ copyPostData: Task.async(function* () { let selected = this.selectedItem.attachment; // Try to extract any form data parameters. let formDataSections = yield getFormDataSections( selected.requestHeaders, selected.requestHeadersFromUploadStream, selected.requestPostData, gNetwork.getString.bind(gNetwork)); let params = []; formDataSections.forEach(section => { let paramsArray = NetworkHelper.parseQueryString(section); if (paramsArray) { params = [...params, ...paramsArray]; } }); let string = params .map(param => param.name + (param.value ? "=" + param.value : "")) .join(Services.appinfo.OS === "WINNT" ? "\r\n" : "\n"); // Fall back to raw payload. if (!string) { let postData = selected.requestPostData.postData.text; string = yield gNetwork.getString(postData); if (Services.appinfo.OS !== "WINNT") { string = string.replace(/\r/g, ""); } } clipboardHelper.copyString(string); }), /** * Copy a cURL command from the currently selected item. */ copyAsCurl: Task.async(function* () { let selected = this.selectedItem.attachment; // Create a sanitized object for the Curl command generator. let data = { url: selected.url, method: selected.method, headers: [], httpVersion: selected.httpVersion, postDataText: null }; // Fetch header values. for (let { name, value } of selected.requestHeaders.headers) { let text = yield gNetwork.getString(value); data.headers.push({ name: name, value: text }); } // Fetch the request payload. if (selected.requestPostData) { let postData = selected.requestPostData.postData.text; data.postDataText = yield gNetwork.getString(postData); } clipboardHelper.copyString(Curl.generateCommand(data)); }), /** * Copy the raw request headers from the currently selected item. */ copyRequestHeaders() { let selected = this.selectedItem.attachment; let rawHeaders = selected.requestHeaders.rawHeaders.trim(); if (Services.appinfo.OS !== "WINNT") { rawHeaders = rawHeaders.replace(/\r/g, ""); } clipboardHelper.copyString(rawHeaders); }, /** * Copy the raw response headers from the currently selected item. */ copyResponseHeaders() { let selected = this.selectedItem.attachment; let rawHeaders = selected.responseHeaders.rawHeaders.trim(); if (Services.appinfo.OS !== "WINNT") { rawHeaders = rawHeaders.replace(/\r/g, ""); } clipboardHelper.copyString(rawHeaders); }, /** * Copy image as data uri. */ copyImageAsDataUri() { let selected = this.selectedItem.attachment; let { mimeType, text, encoding } = selected.responseContent.content; gNetwork.getString(text).then(string => { let data = formDataURI(mimeType, encoding, string); clipboardHelper.copyString(data); }); }, /** * Copy response data as a string. */ copyResponse() { let selected = this.selectedItem.attachment; let text = selected.responseContent.content.text; gNetwork.getString(text).then(string => { clipboardHelper.copyString(string); }); }, /** * Copy HAR from the network panel content to the clipboard. */ copyAllAsHar() { let options = this.getDefaultHarOptions(); return HarExporter.copy(options); }, /** * Save HAR from the network panel content to a file. */ saveAllAsHar() { let options = this.getDefaultHarOptions(); return HarExporter.save(options); }, getDefaultHarOptions() { let form = NetMonitorController._target.form; let title = form.title || form.url; return { getString: gNetwork.getString.bind(gNetwork), view: NetMonitorView.RequestsMenu, items: NetMonitorView.RequestsMenu.items, title: title }; } }; module.exports = RequestListContextMenu;