Extends the Chrome Developer Tools, adding a new Network Panel in the Developer Tools window with better searching and response previews. https://leviolson.com/posts/chrome-ext-better-network-panel
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

411 lines
14 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. function Console() {}
  2. Console.Type = {
  3. LOG: "log",
  4. DEBUG: "debug",
  5. INFO: "info",
  6. WARN: "warn",
  7. ERROR: "error",
  8. GROUP: "group",
  9. GROUP_COLLAPSED: "groupCollapsed",
  10. GROUP_END: "groupEnd"
  11. };
  12. Console.addMessage = function (type, format, args) {
  13. chrome.runtime.sendMessage({
  14. command: "sendToConsole",
  15. tabId: chrome.devtools.inspectedWindow.tabId,
  16. args: escape(JSON.stringify(Array.prototype.slice.call(arguments, 0)))
  17. });
  18. };
  19. // Generate Console output methods, i.e. Console.log(), Console.debug() etc.
  20. (function () {
  21. var console_types = Object.getOwnPropertyNames(Console.Type);
  22. for (var type = 0; type < console_types.length; ++type) {
  23. var method_name = Console.Type[console_types[type]];
  24. Console[method_name] = Console.addMessage.bind(Console, method_name);
  25. }
  26. })();
  27. BNPChrome.controller("PanelController", function PanelController($scope, toolbar, parse) {
  28. $scope.uniqueid = 1000000;
  29. $scope.activeId = null;
  30. $scope.requests = {};
  31. $scope.masterRequests = [];
  32. $scope.filteredRequests = [];
  33. $scope.showAll = true;
  34. $scope.limitNetworkRequests = true;
  35. $scope.showOriginal = false;
  36. $scope.currentDetailTab = "tab-response";
  37. $scope.myResponseCodeMirror = null;
  38. $scope.activeCookies = [];
  39. $scope.activeHeaders = [];
  40. $scope.activePostData = [];
  41. $scope.activeRequest = [];
  42. $scope.activeResponseData = [];
  43. $scope.activeResponseCookies = [];
  44. $scope.activeResponseHeaders = [];
  45. $scope.activeCode = null;
  46. $scope.filter = "";
  47. $scope.showIncomingRequests = true;
  48. $scope.init = function (type) {
  49. $("#tabs").tabs();
  50. $scope.initChrome();
  51. this.createToolbar();
  52. };
  53. $scope.initChrome = function () {
  54. chrome.devtools.network.onRequestFinished.addListener(function (request) {
  55. // do not show requests to chrome extension resources
  56. if (request.request.url.startsWith("chrome-extension://")) {
  57. return;
  58. }
  59. $scope.handleRequest(request);
  60. });
  61. };
  62. $scope.filterRequests = function () {
  63. const searchString = $scope.filter.toLowerCase();
  64. if (!searchString) $scope.filteredRequests = $scope.masterRequests;
  65. $scope.filteredRequests = $scope.masterRequests.filter(function (x) {
  66. if (x && x.searchIndex && x.searchIndex.includes(searchString)) return true;
  67. });
  68. };
  69. $scope.handleRequest = function (har_entry) {
  70. $scope.addRequest(har_entry, har_entry.request.method, har_entry.request.url, har_entry.response.status, null);
  71. };
  72. $scope.createToolbar = function () {
  73. toolbar.createButton("search", "Search Code", false, function () {
  74. // ga('send', 'event', 'button', 'click', 'Search Code');
  75. $scope.$apply(function () {
  76. if ($scope.myResponseCodeMirror) {
  77. $scope.myResponseCodeMirror.execCommand("find");
  78. }
  79. });
  80. });
  81. toolbar.createToggleButton(
  82. "embed",
  83. "JSON Parsing",
  84. false,
  85. function () {
  86. // ga('send', 'event', 'button', 'click', 'Toggle JSON Parsing');
  87. $scope.$apply(function () {
  88. $scope.showOriginal = !$scope.showOriginal;
  89. $scope.selectDetailTab($scope.currentDetailTab);
  90. // $scope.displayCode();
  91. });
  92. },
  93. true
  94. );
  95. toolbar.createButton("download3", "Download", false, function () {
  96. // ga('send', 'event', 'button', 'click', 'Download');
  97. $scope.$apply(function () {
  98. var blob = new Blob([JSON.stringify($scope.requests)], { type: "application/json;charset=utf-8" });
  99. saveAs(blob, "BNPChromeExport.json");
  100. });
  101. });
  102. toolbar.createButton("upload3", "Import", true, function () {
  103. // ga('send', 'event', 'button', 'click', 'Import');
  104. $scope.$apply(function () {
  105. $("#ImportInput").click();
  106. });
  107. });
  108. toolbar.createToggleButton(
  109. "meter",
  110. "Limit network requests to 500",
  111. false,
  112. function () {
  113. // ga('send', 'event', 'button', 'click', 'Toggle Limit Network Request');
  114. $scope.$apply(function () {
  115. $scope.limitNetworkRequests = !$scope.limitNetworkRequests;
  116. });
  117. },
  118. true
  119. );
  120. toolbar.createButton("blocked", "Clear", false, function () {
  121. // ga('send', 'event', 'button', 'click', 'Clear');
  122. $scope.$apply(function () {
  123. $scope.clear();
  124. });
  125. });
  126. $(".toolbar").replaceWith(toolbar.render());
  127. //clears the input value so you can reload the same file
  128. document.getElementById("ImportInput").addEventListener(
  129. "click",
  130. function () {
  131. this.value = null;
  132. },
  133. false
  134. );
  135. document.getElementById("ImportInput").addEventListener("change", readFile, false);
  136. function readFile(evt) {
  137. const files = evt.target.files;
  138. const file = files[0];
  139. const reader = new FileReader();
  140. reader.onload = function () {
  141. $scope.importFile(this.result);
  142. };
  143. reader.readAsText(file);
  144. }
  145. };
  146. $scope.importFile = function (data) {
  147. $scope.$apply(function () {
  148. const importHar = JSON.parse(data);
  149. for (i in importHar) {
  150. $scope.handleRequest(importHar[i]);
  151. }
  152. });
  153. };
  154. $scope.addRequest = function (data, request_method, request_url, response_status) {
  155. $scope.$apply(function () {
  156. const requestId = $scope.uniqueid;
  157. $scope.uniqueid = $scope.uniqueid + 1;
  158. if (data.request != null) {
  159. data["request_data"] = $scope.createKeypairs(data.request);
  160. if (data.request.cookies != null) {
  161. data.cookies = $scope.createKeypairsDeep(data.request.cookies);
  162. }
  163. if (data.request.headers != null) {
  164. data.headers = $scope.createKeypairsDeep(data.request.headers);
  165. }
  166. if (data.request.postData != null) {
  167. data.postData = $scope.createKeypairs(data.request.postData);
  168. }
  169. }
  170. if (data.response != null) {
  171. data["response_data"] = $scope.createKeypairs(data.response);
  172. data.response_data.response_body = "Loading " + requestId;
  173. if (data.response.cookies != null) {
  174. data["response_cookies"] = $scope.createKeypairsDeep(data.response.cookies);
  175. }
  176. if (data.response.headers != null) {
  177. data["response_headers"] = $scope.createKeypairsDeep(data.response.headers);
  178. }
  179. }
  180. data["request_method"] = request_method;
  181. if (request_url.includes("apexremote")) {
  182. try {
  183. let text =
  184. data && data.request && data.request.postData && data.request.postData.text
  185. ? JSON.parse(data.request.postData.text)
  186. : "";
  187. data["request_apex_type"] =
  188. text.data && typeof text.data[1] === "string" ? text.data[1] : JSON.stringify(text.data);
  189. data["request_apex_method"] = text.method || "";
  190. } catch (e) {
  191. console.debug("Error", e);
  192. }
  193. }
  194. data["request_url"] = request_url;
  195. data["response_status"] = response_status;
  196. data["id"] = requestId;
  197. $scope.requests[requestId] = data; // master
  198. data.searchIndex = JSON.stringify(data).toLowerCase();
  199. // console.debug('SearchIndex', data.searchIndex)
  200. $scope.masterRequests.push(data);
  201. $scope.filteredRequests.push(data);
  202. data.getContent(function (content, encoding) {
  203. try {
  204. $scope.requests[requestId].response_data.response_body = JSON.stringify(
  205. JSON.parse(content),
  206. null,
  207. 4
  208. );
  209. } catch (e) {}
  210. });
  211. $scope.cleanRequests();
  212. });
  213. };
  214. $scope.cleanRequests = function () {
  215. if ($scope.limitNetworkRequests === true) {
  216. if ($scope.masterRequests.length >= 500) $scope.masterRequests.shift();
  217. const keys = Object.keys($scope.requests).reverse().slice(500);
  218. keys.forEach(function (key) {
  219. if ($scope.requests[key]) {
  220. delete $scope.requests[key];
  221. }
  222. });
  223. }
  224. $scope.filterRequests();
  225. };
  226. $scope.clear = function () {
  227. $scope.requests = {};
  228. $scope.activeId = null;
  229. $scope.masterRequests = [];
  230. $scope.filteredRequests = [];
  231. $scope.activeCookies = [];
  232. $scope.activeHeaders = [];
  233. $scope.activePostData = [];
  234. $scope.activeRequest = [];
  235. $scope.activeResponseData = [];
  236. $scope.activeResponseDataPreview = "";
  237. $scope.activeResponseCookies = [];
  238. $scope.activeResponseHeaders = [];
  239. $scope.activeCode = null;
  240. $scope.showIncomingRequests = true;
  241. };
  242. $scope.setActive = function (requestId) {
  243. if (!$scope.requests[requestId]) {
  244. return;
  245. }
  246. $scope.activeId = requestId;
  247. $scope.activeCookies = $scope.requests[requestId].cookies;
  248. $scope.activeHeaders = $scope.requests[requestId].headers;
  249. $scope.activePostData = $scope.requests[requestId].postData;
  250. $scope.activeRequest = $scope.requests[requestId].request_data;
  251. $scope.activeResponseData = $scope.requests[requestId].response_data;
  252. $scope.activeResponseDataPreview = $scope.requests[requestId].response_data.response_body;
  253. $scope.activeResponseCookies = $scope.requests[requestId].response_cookies;
  254. $scope.activeResponseHeaders = $scope.requests[requestId].response_headers;
  255. $scope.activeCode = $scope.requests[requestId].response_data.response_body;
  256. };
  257. $scope.getClass = function (requestId) {
  258. if (requestId === $scope.activeId) {
  259. return "selected";
  260. } else {
  261. return "";
  262. }
  263. };
  264. $scope.createKeypairs = function (data) {
  265. let keypairs = [];
  266. if (!(data instanceof Object)) {
  267. return keypairs;
  268. }
  269. $.each(data, function (key, value) {
  270. if (!(value instanceof Object)) {
  271. keypairs.push({
  272. name: key,
  273. value: value
  274. });
  275. }
  276. });
  277. return keypairs;
  278. };
  279. $scope.createKeypairsDeep = function (data) {
  280. let keypairs = [];
  281. if (!(data instanceof Object)) {
  282. return keypairs;
  283. }
  284. $.each(data, function (key, value) {
  285. keypairs.push({
  286. name: value.name,
  287. value: value.value
  288. });
  289. });
  290. return keypairs;
  291. };
  292. $scope.$watch("activeCode", function (newVal, oldVal) {
  293. $scope.displayCode("tab-response-codemirror", $scope.activeCode, "myResponseCodeMirror", 3);
  294. $scope.displayCode("tab-request-codemirror", $scope.flatten($scope.activePostData), "myRequestCodeMirror", 6);
  295. });
  296. $scope.selectDetailTab = function (tabId, external) {
  297. $scope.currentDetailTab = tabId;
  298. if (external) {
  299. $("#tabs a[href='#" + tabId + "']").trigger("click");
  300. }
  301. if (tabId === "tab-response")
  302. $scope.displayCode("tab-response-codemirror", $scope.activeCode, "myResponseCodeMirror", 3);
  303. if (tabId === "tab-request")
  304. $scope.displayCode(
  305. "tab-request-codemirror",
  306. $scope.flatten($scope.activePostData),
  307. "myRequestCodeMirror",
  308. 6
  309. );
  310. };
  311. $scope.flatten = function (input) {
  312. if (input && typeof input === "object")
  313. return input.map(function (x) {
  314. var tmp = {};
  315. tmp[x.name] = x.value;
  316. return tmp;
  317. });
  318. };
  319. $scope.displayCode = function (elementId, input, scopeVar, depth) {
  320. if (input != null) {
  321. document.getElementById(elementId).style.visibility = "visible";
  322. let content;
  323. if ($scope.showOriginal) {
  324. content = JSON.stringify(parse(input, 0, 1), null, 4);
  325. } else {
  326. content = JSON.stringify(parse(input, 0, depth), null, 4);
  327. }
  328. if ($scope[scopeVar]) {
  329. $scope[scopeVar].getDoc().setValue(content);
  330. $scope[scopeVar].refresh();
  331. return;
  332. }
  333. document.getElementById(elementId).innerHTML = "";
  334. const codeMirror = CodeMirror(document.getElementById(elementId), {
  335. value: content,
  336. mode: "application/json",
  337. theme: "neat",
  338. lineNumbers: true,
  339. lineWrapping: false,
  340. readOnly: true,
  341. foldGutter: true,
  342. gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"]
  343. });
  344. $scope[scopeVar] = codeMirror;
  345. }
  346. };
  347. $scope.getPretty = function (source) {
  348. let code = JSON.stringify(parse(source, 0, 1), null, 4);
  349. return code;
  350. const options = {
  351. source: code,
  352. mode: "beautify", // beautify, diff, minify, parse
  353. lang: "auto",
  354. inchar: " " // indent character
  355. };
  356. const pd = prettydiff(options); // returns and array: [beautified, report]
  357. const pretty = pd[0];
  358. return pretty;
  359. };
  360. });