request-list-context-menu.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. /* globals NetMonitorController, NetMonitorView, gNetwork */
  5. "use strict";
  6. const Services = require("Services");
  7. const { Task } = require("devtools/shared/task");
  8. const { Curl } = require("devtools/client/shared/curl");
  9. const { gDevTools } = require("devtools/client/framework/devtools");
  10. const { openRequestInTab } = require("devtools/client/netmonitor/open-request-in-tab");
  11. const Menu = require("devtools/client/framework/menu");
  12. const MenuItem = require("devtools/client/framework/menu-item");
  13. const { L10N } = require("./l10n");
  14. const { formDataURI, getFormDataSections } = require("./request-utils");
  15. loader.lazyRequireGetter(this, "HarExporter",
  16. "devtools/client/netmonitor/har/har-exporter", true);
  17. loader.lazyServiceGetter(this, "clipboardHelper",
  18. "@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");
  19. loader.lazyRequireGetter(this, "NetworkHelper",
  20. "devtools/shared/webconsole/network-helper");
  21. function RequestListContextMenu() {}
  22. RequestListContextMenu.prototype = {
  23. get selectedItem() {
  24. return NetMonitorView.RequestsMenu.selectedItem;
  25. },
  26. get items() {
  27. return NetMonitorView.RequestsMenu.items;
  28. },
  29. /**
  30. * Handle the context menu opening. Hide items if no request is selected.
  31. * Since visible attribute only accept boolean value but the method call may
  32. * return undefined, we use !! to force convert any object to boolean
  33. */
  34. open({ screenX = 0, screenY = 0 } = {}) {
  35. let selectedItem = this.selectedItem;
  36. let menu = new Menu();
  37. menu.append(new MenuItem({
  38. id: "request-menu-context-copy-url",
  39. label: L10N.getStr("netmonitor.context.copyUrl"),
  40. accesskey: L10N.getStr("netmonitor.context.copyUrl.accesskey"),
  41. visible: !!selectedItem,
  42. click: () => this.copyUrl(),
  43. }));
  44. menu.append(new MenuItem({
  45. id: "request-menu-context-copy-url-params",
  46. label: L10N.getStr("netmonitor.context.copyUrlParams"),
  47. accesskey: L10N.getStr("netmonitor.context.copyUrlParams.accesskey"),
  48. visible: !!(selectedItem &&
  49. NetworkHelper.nsIURL(selectedItem.attachment.url).query),
  50. click: () => this.copyUrlParams(),
  51. }));
  52. menu.append(new MenuItem({
  53. id: "request-menu-context-copy-post-data",
  54. label: L10N.getStr("netmonitor.context.copyPostData"),
  55. accesskey: L10N.getStr("netmonitor.context.copyPostData.accesskey"),
  56. visible: !!(selectedItem && selectedItem.attachment.requestPostData),
  57. click: () => this.copyPostData(),
  58. }));
  59. menu.append(new MenuItem({
  60. id: "request-menu-context-copy-as-curl",
  61. label: L10N.getStr("netmonitor.context.copyAsCurl"),
  62. accesskey: L10N.getStr("netmonitor.context.copyAsCurl.accesskey"),
  63. visible: !!(selectedItem && selectedItem.attachment),
  64. click: () => this.copyAsCurl(),
  65. }));
  66. menu.append(new MenuItem({
  67. type: "separator",
  68. visible: !!selectedItem,
  69. }));
  70. menu.append(new MenuItem({
  71. id: "request-menu-context-copy-request-headers",
  72. label: L10N.getStr("netmonitor.context.copyRequestHeaders"),
  73. accesskey: L10N.getStr("netmonitor.context.copyRequestHeaders.accesskey"),
  74. visible: !!(selectedItem && selectedItem.attachment.requestHeaders),
  75. click: () => this.copyRequestHeaders(),
  76. }));
  77. menu.append(new MenuItem({
  78. id: "response-menu-context-copy-response-headers",
  79. label: L10N.getStr("netmonitor.context.copyResponseHeaders"),
  80. accesskey: L10N.getStr("netmonitor.context.copyResponseHeaders.accesskey"),
  81. visible: !!(selectedItem && selectedItem.attachment.responseHeaders),
  82. click: () => this.copyResponseHeaders(),
  83. }));
  84. menu.append(new MenuItem({
  85. id: "request-menu-context-copy-response",
  86. label: L10N.getStr("netmonitor.context.copyResponse"),
  87. accesskey: L10N.getStr("netmonitor.context.copyResponse.accesskey"),
  88. visible: !!(selectedItem &&
  89. selectedItem.attachment.responseContent &&
  90. selectedItem.attachment.responseContent.content.text &&
  91. selectedItem.attachment.responseContent.content.text.length !== 0),
  92. click: () => this.copyResponse(),
  93. }));
  94. menu.append(new MenuItem({
  95. id: "request-menu-context-copy-image-as-data-uri",
  96. label: L10N.getStr("netmonitor.context.copyImageAsDataUri"),
  97. accesskey: L10N.getStr("netmonitor.context.copyImageAsDataUri.accesskey"),
  98. visible: !!(selectedItem &&
  99. selectedItem.attachment.responseContent &&
  100. selectedItem.attachment.responseContent.content
  101. .mimeType.includes("image/")),
  102. click: () => this.copyImageAsDataUri(),
  103. }));
  104. menu.append(new MenuItem({
  105. type: "separator",
  106. visible: !!selectedItem,
  107. }));
  108. menu.append(new MenuItem({
  109. id: "request-menu-context-copy-all-as-har",
  110. label: L10N.getStr("netmonitor.context.copyAllAsHar"),
  111. accesskey: L10N.getStr("netmonitor.context.copyAllAsHar.accesskey"),
  112. visible: !!this.items.length,
  113. click: () => this.copyAllAsHar(),
  114. }));
  115. menu.append(new MenuItem({
  116. id: "request-menu-context-save-all-as-har",
  117. label: L10N.getStr("netmonitor.context.saveAllAsHar"),
  118. accesskey: L10N.getStr("netmonitor.context.saveAllAsHar.accesskey"),
  119. visible: !!this.items.length,
  120. click: () => this.saveAllAsHar(),
  121. }));
  122. menu.append(new MenuItem({
  123. type: "separator",
  124. visible: !!selectedItem,
  125. }));
  126. menu.append(new MenuItem({
  127. id: "request-menu-context-resend",
  128. label: L10N.getStr("netmonitor.context.editAndResend"),
  129. accesskey: L10N.getStr("netmonitor.context.editAndResend.accesskey"),
  130. visible: !!(NetMonitorController.supportsCustomRequest &&
  131. selectedItem &&
  132. !selectedItem.attachment.isCustom),
  133. click: () => NetMonitorView.RequestsMenu.cloneSelectedRequest(),
  134. }));
  135. menu.append(new MenuItem({
  136. type: "separator",
  137. visible: !!selectedItem,
  138. }));
  139. menu.append(new MenuItem({
  140. id: "request-menu-context-newtab",
  141. label: L10N.getStr("netmonitor.context.newTab"),
  142. accesskey: L10N.getStr("netmonitor.context.newTab.accesskey"),
  143. visible: !!selectedItem,
  144. click: () => this.openRequestInTab()
  145. }));
  146. menu.append(new MenuItem({
  147. id: "request-menu-context-perf",
  148. label: L10N.getStr("netmonitor.context.perfTools"),
  149. accesskey: L10N.getStr("netmonitor.context.perfTools.accesskey"),
  150. visible: !!NetMonitorController.supportsPerfStats,
  151. click: () => NetMonitorView.toggleFrontendMode()
  152. }));
  153. menu.popup(screenX, screenY, NetMonitorController._toolbox);
  154. return menu;
  155. },
  156. /**
  157. * Opens selected item in a new tab.
  158. */
  159. openRequestInTab() {
  160. openRequestInTab(this.selectedItem.attachment);
  161. },
  162. /**
  163. * Copy the request url from the currently selected item.
  164. */
  165. copyUrl() {
  166. clipboardHelper.copyString(this.selectedItem.attachment.url);
  167. },
  168. /**
  169. * Copy the request url query string parameters from the currently
  170. * selected item.
  171. */
  172. copyUrlParams() {
  173. let { url } = this.selectedItem.attachment;
  174. let params = NetworkHelper.nsIURL(url).query.split("&");
  175. let string = params.join(Services.appinfo.OS === "WINNT" ? "\r\n" : "\n");
  176. clipboardHelper.copyString(string);
  177. },
  178. /**
  179. * Copy the request form data parameters (or raw payload) from
  180. * the currently selected item.
  181. */
  182. copyPostData: Task.async(function* () {
  183. let selected = this.selectedItem.attachment;
  184. // Try to extract any form data parameters.
  185. let formDataSections = yield getFormDataSections(
  186. selected.requestHeaders,
  187. selected.requestHeadersFromUploadStream,
  188. selected.requestPostData,
  189. gNetwork.getString.bind(gNetwork));
  190. let params = [];
  191. formDataSections.forEach(section => {
  192. let paramsArray = NetworkHelper.parseQueryString(section);
  193. if (paramsArray) {
  194. params = [...params, ...paramsArray];
  195. }
  196. });
  197. let string = params
  198. .map(param => param.name + (param.value ? "=" + param.value : ""))
  199. .join(Services.appinfo.OS === "WINNT" ? "\r\n" : "\n");
  200. // Fall back to raw payload.
  201. if (!string) {
  202. let postData = selected.requestPostData.postData.text;
  203. string = yield gNetwork.getString(postData);
  204. if (Services.appinfo.OS !== "WINNT") {
  205. string = string.replace(/\r/g, "");
  206. }
  207. }
  208. clipboardHelper.copyString(string);
  209. }),
  210. /**
  211. * Copy a cURL command from the currently selected item.
  212. */
  213. copyAsCurl: Task.async(function* () {
  214. let selected = this.selectedItem.attachment;
  215. // Create a sanitized object for the Curl command generator.
  216. let data = {
  217. url: selected.url,
  218. method: selected.method,
  219. headers: [],
  220. httpVersion: selected.httpVersion,
  221. postDataText: null
  222. };
  223. // Fetch header values.
  224. for (let { name, value } of selected.requestHeaders.headers) {
  225. let text = yield gNetwork.getString(value);
  226. data.headers.push({ name: name, value: text });
  227. }
  228. // Fetch the request payload.
  229. if (selected.requestPostData) {
  230. let postData = selected.requestPostData.postData.text;
  231. data.postDataText = yield gNetwork.getString(postData);
  232. }
  233. clipboardHelper.copyString(Curl.generateCommand(data));
  234. }),
  235. /**
  236. * Copy the raw request headers from the currently selected item.
  237. */
  238. copyRequestHeaders() {
  239. let selected = this.selectedItem.attachment;
  240. let rawHeaders = selected.requestHeaders.rawHeaders.trim();
  241. if (Services.appinfo.OS !== "WINNT") {
  242. rawHeaders = rawHeaders.replace(/\r/g, "");
  243. }
  244. clipboardHelper.copyString(rawHeaders);
  245. },
  246. /**
  247. * Copy the raw response headers from the currently selected item.
  248. */
  249. copyResponseHeaders() {
  250. let selected = this.selectedItem.attachment;
  251. let rawHeaders = selected.responseHeaders.rawHeaders.trim();
  252. if (Services.appinfo.OS !== "WINNT") {
  253. rawHeaders = rawHeaders.replace(/\r/g, "");
  254. }
  255. clipboardHelper.copyString(rawHeaders);
  256. },
  257. /**
  258. * Copy image as data uri.
  259. */
  260. copyImageAsDataUri() {
  261. let selected = this.selectedItem.attachment;
  262. let { mimeType, text, encoding } = selected.responseContent.content;
  263. gNetwork.getString(text).then(string => {
  264. let data = formDataURI(mimeType, encoding, string);
  265. clipboardHelper.copyString(data);
  266. });
  267. },
  268. /**
  269. * Copy response data as a string.
  270. */
  271. copyResponse() {
  272. let selected = this.selectedItem.attachment;
  273. let text = selected.responseContent.content.text;
  274. gNetwork.getString(text).then(string => {
  275. clipboardHelper.copyString(string);
  276. });
  277. },
  278. /**
  279. * Copy HAR from the network panel content to the clipboard.
  280. */
  281. copyAllAsHar() {
  282. let options = this.getDefaultHarOptions();
  283. return HarExporter.copy(options);
  284. },
  285. /**
  286. * Save HAR from the network panel content to a file.
  287. */
  288. saveAllAsHar() {
  289. let options = this.getDefaultHarOptions();
  290. return HarExporter.save(options);
  291. },
  292. getDefaultHarOptions() {
  293. let form = NetMonitorController._target.form;
  294. let title = form.title || form.url;
  295. return {
  296. getString: gNetwork.getString.bind(gNetwork),
  297. view: NetMonitorView.RequestsMenu,
  298. items: NetMonitorView.RequestsMenu.items,
  299. title: title
  300. };
  301. }
  302. };
  303. module.exports = RequestListContextMenu;