Greasy Fork 还支持 简体中文。

Storage Monitor/Debugger Hook

用于监控js对localStorage/sessionStorage的任何操作,或者在符合给定条件时进入断点

  1. // ==UserScript==
  2. // @name Storage Monitor/Debugger Hook
  3. // @namespace https://github.com/CC11001100/crawler-js-hook-framework-public/tree/master/005-storage-hook
  4. // @version 0.1
  5. // @description 用于监控js对localStorage/sessionStorage的任何操作,或者在符合给定条件时进入断点
  6. // @document https://github.com/CC11001100/crawler-js-hook-framework-public/tree/master/005-storage-hook
  7. // @author CC11001100
  8. // @match *://*/*
  9. // @run-at document-start
  10. // @grant none
  11. // ==/UserScript==
  12. (() => {
  13.  
  14. // 简介: 用于检测、调试浏览器中的localStorage和sessionStorage的任何操作
  15. // 本工具详细文档见:
  16. // Storage是什么: https://developer.mozilla.org/zh-CN/docs/Web/API/Storage
  17.  
  18. // 修改这里来打断点
  19. const storageDebuggerList = [
  20. "947e722bbefb8a455c278113042beadb",
  21.  
  22. // 允许使用字符串,字符串用来对name做完全相等的匹配
  23. // 对LocalStorage或SessionStorage的key为foo-name进行的任何操作都会进入断点
  24. // "foo-name",
  25.  
  26. // 字符串形式的增强版,允许使用正则表达式,正则表达式只用来匹配name
  27. // /^foo-prefix*/,
  28.  
  29. // 这才是一个完整的配置,可以比较精细的打断点
  30. // {
  31. //
  32. // // storageType { "local" | "session" | "all" }
  33. // "storageType": "all",
  34. //
  35. // // operationType { "get" | "set" | "remove" | "clear" | "key" | "all" }
  36. // "operationType": "all",
  37. //
  38. // // nameFilter { "string" | RegExp | null }
  39. // "nameFilter": "foo-name",
  40. //
  41. // // valueFilter { "string" | RegExp | null }
  42. // "valueFilter": "foo-value"
  43. //
  44. // }
  45. ]
  46.  
  47. // 可以禁用storage来辅助调试,不需要每次都去傻啦吧唧的删除,让它写不进去读不出来即可
  48. // 可以这样同时控制localStorage和sessionStorage是否可读和可写
  49. const enableStorage = {
  50. read: true,
  51. write: true
  52. }
  53.  
  54. // 支持的另一种配置方式:
  55. // 也可以精确的为每一个类型指定可读和可写
  56. // const enableStorage = {
  57. // localStorage: {
  58. // // localStorage是否是可读的
  59. // read: true,
  60. // // localStorage是否是可写的
  61. // write: true
  62. // },
  63. // sessionStorage: {
  64. // // sessionStorage是否是可读的
  65. // read: true,
  66. // // sessionStorage是否是可写的
  67. // write: true
  68. // }
  69. // }
  70.  
  71. // 在控制台打印日志时字体大小,根据自己喜好调整
  72. // 众所周知,12px是宇宙通用大小
  73. const consoleLogFontSize = 12;
  74.  
  75. // --------------------------------- 以下为程序内部逻辑,可忽略 ---------------------------------------------
  76.  
  77. // 防止重复注入
  78. const _cc11001100_hook_storage = window._cc11001100_hook_storage = window._cc11001100_hook_storage || {};
  79. if ("isInjectHook" in _cc11001100_hook_storage) {
  80. return
  81. }
  82. _cc11001100_hook_storage["isInjectHook"] = true
  83.  
  84. addHook("session", window.sessionStorage);
  85. addHook("local", window.localStorage);
  86.  
  87. /**
  88. * 为一个storage对象添加Hook,可以是localStorage或者sessionStorage
  89. *
  90. * @param storageTypeName { "local" | "session" }
  91. * @param storageObject { window.localStorage | window.sessionStorage}
  92. */
  93. function addHook(storageTypeName, storageObject) {
  94.  
  95. // getItem
  96. const storageGetItem = storageObject.getItem;
  97. storageObject.getItem = function (itemName) {
  98. const itemValue = storageGetItem.apply(this, [itemName]);
  99.  
  100. const valueStyle = `color: black; background: #85C1E9; font-size: ${consoleLogFontSize}px; font-weight: bold;`;
  101. const normalStyle = `color: black; background: #D6EAF8; font-size: ${consoleLogFontSize}px;`;
  102.  
  103. const message = [
  104.  
  105. normalStyle,
  106. now(),
  107.  
  108. normalStyle,
  109. "Storage Monitor: ",
  110.  
  111. valueStyle,
  112. "get",
  113.  
  114. normalStyle,
  115. " ",
  116.  
  117. valueStyle,
  118. `${storageTypeName} storage`,
  119.  
  120. normalStyle,
  121. ", name = ",
  122.  
  123. valueStyle,
  124. `${itemName}`,
  125.  
  126. normalStyle,
  127. ", value = ",
  128.  
  129. valueStyle,
  130. `${itemValue}`,
  131.  
  132. normalStyle,
  133. `, code location = ${cc11001100_getCodeLocation()}`
  134. ];
  135. console.log(genFormatArray(message), ...message);
  136.  
  137. testStorageDebugger(storageTypeName, "get", itemName, itemValue);
  138.  
  139. // 如果关闭读功能的话,则阻止其能够读到值
  140. if (!isStorageEnable(storageTypeName, "read")) {
  141. const message = [
  142.  
  143. normalStyle,
  144. now(),
  145.  
  146. normalStyle,
  147. "Storage Monitor: ",
  148.  
  149. normalStyle,
  150. "ignore ",
  151.  
  152. valueStyle,
  153. "get",
  154.  
  155. normalStyle,
  156. ` because disable `,
  157.  
  158. valueStyle,
  159. `${storageTypeName}`,
  160.  
  161. normalStyle,
  162. " ",
  163.  
  164. valueStyle,
  165. "read",
  166.  
  167. normalStyle,
  168. `, code location = ${cc11001100_getCodeLocation()}`
  169. ];
  170. console.log(genFormatArray(message), ...message);
  171. return null;
  172. }
  173.  
  174.  
  175.  
  176. return itemValue;
  177. }
  178. storageObject.getItem.toString = () => "function getItem() { [native code] }";
  179.  
  180. // setItem
  181. const storageSetItem = storageObject.setItem;
  182. storageObject.setItem = function (itemName, itemValue) {
  183.  
  184. const oldValue = storageGetItem.apply(this, [itemName]);
  185.  
  186. let valueStyle = "";
  187. let normalStyle = "";
  188.  
  189. if (oldValue == null) {
  190. // 认为是新增
  191. valueStyle = `color: black; background: #669934; font-size: ${consoleLogFontSize}px; font-weight: bold;`;
  192. normalStyle = `color: black; background: #65CC66; font-size: ${consoleLogFontSize}px;`;
  193.  
  194. const message = [
  195.  
  196. normalStyle,
  197. now(),
  198.  
  199. normalStyle,
  200. "Storage Monitor: ",
  201.  
  202. valueStyle,
  203. "set",
  204.  
  205. normalStyle,
  206. " ",
  207.  
  208. valueStyle,
  209. `${storageTypeName} storage`,
  210.  
  211. normalStyle,
  212. ", name = ",
  213.  
  214. valueStyle,
  215. `${itemName}`,
  216.  
  217. normalStyle,
  218. ", value = ",
  219.  
  220. valueStyle,
  221. `${itemValue}`,
  222.  
  223. normalStyle,
  224. `, code location = ${cc11001100_getCodeLocation()}`
  225. ];
  226. console.log(genFormatArray(message), ...message);
  227. } else {
  228. // 认为是修改
  229. valueStyle = `color: black; background: #FE9900; font-size: ${consoleLogFontSize}px; font-weight: bold;`;
  230. normalStyle = `color: black; background: #FFCC00; font-size: ${consoleLogFontSize}px;`;
  231.  
  232. const message = [
  233.  
  234. normalStyle,
  235. now(),
  236.  
  237. normalStyle,
  238. "Storage Monitor: ",
  239.  
  240. valueStyle,
  241. "set",
  242.  
  243. normalStyle,
  244. " ",
  245.  
  246. valueStyle,
  247. `${storageTypeName} storage`,
  248.  
  249. normalStyle,
  250. ", name = ",
  251.  
  252. valueStyle,
  253. `${itemName}`,
  254.  
  255. normalStyle,
  256. ", newValue = ",
  257.  
  258. valueStyle,
  259. `${itemValue}`,
  260.  
  261. ...(() => {
  262. if (oldValue === itemValue) {
  263. // 值没有发生改变
  264. return [
  265. normalStyle,
  266. ", value changed = ",
  267.  
  268. valueStyle,
  269. `false`
  270. ]
  271. } else {
  272. // 值发生了改变
  273. return [
  274. normalStyle,
  275. ", oldValue = ",
  276.  
  277. valueStyle,
  278. `${oldValue}`,
  279.  
  280. normalStyle,
  281. ", value changed = ",
  282.  
  283. valueStyle,
  284. `true`
  285. ]
  286. }
  287. })(),
  288.  
  289. normalStyle,
  290. `, code location = ${cc11001100_getCodeLocation()}`
  291. ];
  292. console.log(genFormatArray(message), ...message);
  293. }
  294.  
  295. testStorageDebugger(storageTypeName, "set", itemName, itemValue);
  296.  
  297. // 如果关闭写功能的话,则阻止其能够修改值
  298. if (!isStorageEnable(storageTypeName, "write")) {
  299. const message = [
  300.  
  301. normalStyle,
  302. now(),
  303.  
  304. normalStyle,
  305. "Storage Monitor: ",
  306.  
  307. normalStyle,
  308. "ignore ",
  309.  
  310. valueStyle,
  311. "set",
  312.  
  313. normalStyle,
  314. ` because disable `,
  315.  
  316. valueStyle,
  317. `${storageTypeName}`,
  318.  
  319. normalStyle,
  320. " ",
  321.  
  322. valueStyle,
  323. "write",
  324.  
  325. normalStyle,
  326. `, code location = ${cc11001100_getCodeLocation()}`
  327. ];
  328. console.log(genFormatArray(message), ...message);
  329. return null;
  330. }
  331.  
  332. return storageSetItem.apply(this, [itemName, itemValue]);
  333. }
  334. storageObject.setItem.toString = () => "function setItem() { [native code] }";
  335.  
  336. // removeItem
  337. const storageRemoveItem = storageObject.removeItem;
  338. storageObject.removeItem = function (itemName) {
  339.  
  340. const oldValue = storageGetItem.apply(this, [itemName]);
  341.  
  342. const valueStyle = `color: black; background: #E50000; font-size: ${consoleLogFontSize}px; font-weight: bold;`;
  343. const normalStyle = `color: black; background: #FF6766; font-size: ${consoleLogFontSize}px;`;
  344.  
  345. const message = [
  346.  
  347. normalStyle,
  348. now(),
  349.  
  350. normalStyle,
  351. "Storage Monitor: ",
  352.  
  353. valueStyle,
  354. "remove",
  355.  
  356. normalStyle,
  357. " ",
  358.  
  359. valueStyle,
  360. `${storageTypeName} storage`,
  361.  
  362. normalStyle,
  363. ", name = ",
  364.  
  365. valueStyle,
  366. `${itemName}`,
  367.  
  368. normalStyle,
  369. ", value = ",
  370.  
  371. valueStyle,
  372. `${oldValue}`,
  373.  
  374. normalStyle,
  375. `, code location = ${cc11001100_getCodeLocation()}`
  376. ];
  377. console.log(genFormatArray(message), ...message);
  378.  
  379. testStorageDebugger(storageTypeName, "remove", itemName, null);
  380.  
  381. // 如果关闭写功能的话,则阻止其能够修改值
  382. if (!isStorageEnable(storageTypeName, "write")) {
  383. const message = [
  384.  
  385. normalStyle,
  386. now(),
  387.  
  388. normalStyle,
  389. "Storage Monitor: ",
  390.  
  391. normalStyle,
  392. "ignore ",
  393.  
  394. valueStyle,
  395. "remove",
  396.  
  397. normalStyle,
  398. ` because disable `,
  399.  
  400. valueStyle,
  401. `${storageTypeName}`,
  402.  
  403. normalStyle,
  404. " ",
  405.  
  406. valueStyle,
  407. "write",
  408.  
  409. normalStyle,
  410. `, code location = ${cc11001100_getCodeLocation()}`
  411. ];
  412. console.log(genFormatArray(message), ...message);
  413. return null;
  414. }
  415.  
  416. return storageRemoveItem.apply(this, [itemName]);
  417. }
  418. storageObject.removeItem.toString = () => "function removeItem() { [native code] }";
  419.  
  420. // clear
  421. const storageClear = storageObject.clear;
  422. storageObject.clear = function () {
  423.  
  424. const valueStyle = `color: black; background: #E50000; font-size: ${consoleLogFontSize}px; font-weight: bold;`;
  425. const normalStyle = `color: black; background: #FF6766; font-size: ${consoleLogFontSize}px;`;
  426.  
  427. const message = [
  428.  
  429. normalStyle,
  430. now(),
  431.  
  432. normalStyle,
  433. "Storage Monitor: ",
  434.  
  435. valueStyle,
  436. "clear",
  437.  
  438. normalStyle,
  439. " ",
  440.  
  441. valueStyle,
  442. `${storageTypeName} storage`,
  443.  
  444. normalStyle,
  445. `, code location = ${cc11001100_getCodeLocation()}`
  446. ];
  447. console.log(genFormatArray(message), ...message);
  448.  
  449. testStorageDebugger(storageTypeName, "clear", null, null);
  450.  
  451. // 如果关闭写功能的话,则阻止其能够修改值
  452. if (!isStorageEnable(storageTypeName, "write")) {
  453. const message = [
  454.  
  455. normalStyle,
  456. now(),
  457.  
  458. normalStyle,
  459. "Storage Monitor: ",
  460.  
  461. normalStyle,
  462. "ignore ",
  463.  
  464. valueStyle,
  465. "clear",
  466.  
  467. normalStyle,
  468. ` because disable `,
  469.  
  470. valueStyle,
  471. `${storageTypeName}`,
  472.  
  473. normalStyle,
  474. " ",
  475.  
  476. valueStyle,
  477. "write",
  478.  
  479. normalStyle,
  480. `, code location = ${cc11001100_getCodeLocation()}`
  481. ];
  482. console.log(genFormatArray(message), ...message);
  483. return null;
  484. }
  485.  
  486. return storageClear.apply(this);
  487. }
  488. storageObject.clear.toString = () => "function clear() { [native code] }";
  489.  
  490. // key
  491. const storageKey = storageObject.key;
  492. storageObject.key = function (itemIndex) {
  493. const value = storageKey.apply(this, [itemIndex]);
  494.  
  495. const valueStyle = `color: black; background: #85C1E9; font-size: ${consoleLogFontSize}px; font-weight: bold;`;
  496. const normalStyle = `color: black; background: #D6EAF8; font-size: ${consoleLogFontSize}px;`;
  497.  
  498. const message = [
  499.  
  500. normalStyle,
  501. now(),
  502.  
  503. normalStyle,
  504. "Storage Monitor: ",
  505.  
  506. valueStyle,
  507. `key`,
  508.  
  509. normalStyle,
  510. " ",
  511.  
  512. valueStyle,
  513. `${storageTypeName} storage`,
  514.  
  515. normalStyle,
  516. `, itemIndex = `,
  517.  
  518. valueStyle,
  519. `${itemIndex}`,
  520.  
  521. normalStyle,
  522. ", value = ",
  523.  
  524. valueStyle,
  525. `${value}`,
  526.  
  527. normalStyle,
  528. `, code location = ${cc11001100_getCodeLocation()}`
  529. ];
  530. console.log(genFormatArray(message), ...message);
  531.  
  532. testStorageDebugger(storageTypeName, "key", null, value);
  533.  
  534. // 如果关闭读功能的话,则阻止其能够读到值
  535. if (!isStorageEnable(storageTypeName, "read")) {
  536. const message = [
  537.  
  538. normalStyle,
  539. now(),
  540.  
  541. normalStyle,
  542. "Storage Monitor: ",
  543.  
  544. normalStyle,
  545. "ignore ",
  546.  
  547. valueStyle,
  548. "key",
  549.  
  550. normalStyle,
  551. ` because disable `,
  552.  
  553. valueStyle,
  554. `${storageTypeName}`,
  555.  
  556. normalStyle,
  557. " ",
  558.  
  559. valueStyle,
  560. "read",
  561.  
  562. normalStyle,
  563. `, code location = ${cc11001100_getCodeLocation()}`
  564. ];
  565. console.log(genFormatArray(message), ...message);
  566. return null;
  567. }
  568.  
  569. return value;
  570. }
  571. storageObject.key.toString = () => "function key() { [native code] }";
  572.  
  573. }
  574.  
  575. /**
  576. * 对应类型的storage是否开启
  577. *
  578. * @param storageTypeName { "local" | "session" }
  579. * @param operationType { "read" | "write" }
  580. */
  581. function isStorageEnable(storageTypeName, operationType) {
  582. if (storageTypeName === "local") {
  583. return enableStorage["localStorage"][operationType]
  584. } else if (storageTypeName === "session") {
  585. return enableStorage["sessionStorage"][operationType]
  586. } else {
  587. return true
  588. }
  589. }
  590.  
  591. /**
  592. * 测试是否要进入断点
  593. *
  594. * @param storageType { "local" | "session" | "all" }
  595. * @param operationType { "get" | "set" | "remove" | "clear" | "key" | "all" }
  596. * @param name { "string" | null }
  597. * @param value { "string" | null }
  598. */
  599. function testStorageDebugger(storageType, operationType, name, value) {
  600. for (let storageDebugger of storageDebuggerList) {
  601. // 将鼠标移动到这里在变量上悬停查看其值,能够知道是命中了什么规则
  602. if (storageDebugger.testDebugger(storageType, operationType, name, value)) {
  603. debugger;
  604. }
  605. }
  606. }
  607.  
  608. // 断点规则
  609. class StorageDebugger {
  610.  
  611. /**
  612. *
  613. * @param storageType { "local" | "session" | "all" }
  614. * @param operationType { "get" | "set" | "remove" | "clear" | "key" | "all" }
  615. * @param nameFilter { "string" | RegExp | null }
  616. * @param valueFilter { "string" | RegExp | null }
  617. */
  618. constructor(storageType, operationType, nameFilter, valueFilter) {
  619. this.storageType = storageType;
  620. this.operationType = operationType;
  621. this.nameFilter = nameFilter;
  622. this.valueFilter = valueFilter;
  623. }
  624.  
  625. testDebugger(storageType, operationType, name, value) {
  626. if (!this.testByStorageType(storageType)) {
  627. return false
  628. }
  629. if (!this.testByOperationType(operationType)) {
  630. return false
  631. }
  632. if (this.nameFilter && !this.testByName(name)) {
  633. return false;
  634. }
  635. if (this.valueFilter && !this.testByValue(value)) {
  636. return false;
  637. }
  638. return true;
  639. }
  640.  
  641. testByStorageType(storageType) {
  642. if (storageType === "all" || this.storageType === "all") {
  643. return true
  644. }
  645. return this.storageType === storageType;
  646. }
  647.  
  648. testByOperationType(operationType) {
  649. if (operationType === "all" || this.operationType === "all") {
  650. return true
  651. }
  652. return this.operationType === operationType;
  653. }
  654.  
  655. testByName(name) {
  656.  
  657. if (!this.nameFilter) {
  658. return false
  659. }
  660.  
  661. if (!name) {
  662. return false
  663. }
  664.  
  665. if (typeof this.nameFilter === "string") {
  666. return this.nameFilter === name;
  667. } else if (typeof this.nameFilter instanceof RegExp) {
  668. return this.nameFilter.test(name)
  669. } else {
  670. return false;
  671. }
  672. }
  673.  
  674. testByValue(value) {
  675.  
  676. if (!this.valueFilter) {
  677. return false
  678. }
  679.  
  680. if (!value) {
  681. return false
  682. }
  683.  
  684. if (typeof this.valueFilter === "string") {
  685. return this.valueFilter === value;
  686. } else if (typeof this.valueFilter instanceof RegExp) {
  687. return this.valueFilter.test(value)
  688. } else {
  689. return false;
  690. }
  691. }
  692.  
  693. }
  694.  
  695. // 把storage的读写属性统一,方便后面程序处理
  696. (function convertEnableStorage() {
  697.  
  698. // 设置默认值
  699. enableStorage["localStorage"] = enableStorage["localStorage"] || {}
  700. enableStorage["sessionStorage"] = enableStorage["sessionStorage"] || {}
  701.  
  702. // 扩展read
  703. if ("read" in enableStorage) {
  704. enableStorage["localStorage"]["read"] = enableStorage["read"]
  705. enableStorage["sessionStorage"]["read"] = enableStorage["read"]
  706. delete enableStorage["read"]
  707. }
  708.  
  709. // 扩展write
  710. if ("write" in enableStorage) {
  711. enableStorage["localStorage"]["write"] = enableStorage["write"]
  712. enableStorage["sessionStorage"]["write"] = enableStorage["write"]
  713. delete enableStorage["write"]
  714. }
  715.  
  716. // 如果没有配置的话,则设置默认值
  717. if (!("write" in enableStorage["localStorage"])) {
  718. enableStorage["localStorage"]["write"] = true
  719. }
  720. if (!("read" in enableStorage["localStorage"])) {
  721. enableStorage["localStorage"]["read"] = true
  722. }
  723. if (!("write" in enableStorage["sessionStorage"])) {
  724. enableStorage["sessionStorage"]["write"] = true
  725. }
  726. if (!("read" in enableStorage["sessionStorage"])) {
  727. enableStorage["sessionStorage"]["read"] = true
  728. }
  729.  
  730. })();
  731.  
  732. // 把storage的断点规则转换为程序内部使用的格式
  733. (function convertStorageDebugger() {
  734. // const valueStyle = `color: black; background: #FF2121; font-size: ${Math.round(consoleLogFontSize * 1.5)}px; font-weight: bold;`;
  735. const normalStyle = `color: black; background: #FF2121; font-size: ${Math.round(consoleLogFontSize * 1.5)}px;`;
  736.  
  737. const newStorageDebuggerList = [];
  738. for (let x of storageDebuggerList) {
  739. if (typeof x === "string" || x instanceof RegExp) {
  740. // 如果设置的是名字,则只针对按名称操作的操作打断点
  741. newStorageDebuggerList.push(new StorageDebugger("all", "get", x, null));
  742. newStorageDebuggerList.push(new StorageDebugger("all", "set", x, null));
  743. newStorageDebuggerList.push(new StorageDebugger("all", "remove", x, null));
  744. } else {
  745.  
  746. // 检查设置项的合法性
  747. if ("storageType" in x && ["local", "session", "all"].indexOf(x["storageType"].toLowerCase()) === -1) {
  748. const message = [
  749. normalStyle,
  750. `${now()} Storage Monitor: storageType error, value = ${x["storageType"]}, need to be = { "local", "session", "all" }, so ignore this debugger = ${JSON.stringify(x)}`,
  751. ];
  752. console.log(genFormatArray(message), ...message);
  753. continue
  754. }
  755.  
  756. if ("operationType" in x && ["get", "set", "remove", "clear", "key", "all"].indexOf(x["operationType"].toLowerCase()) === -1) {
  757. const message = [
  758. normalStyle,
  759. `${now()} Storage Monitor: storageType error, value = ${x["operationType"]}, need to be { "get" | "set" | "remove" | "clear" | "key" | "all" }, so ignore this debugger = ${JSON.stringify(x)}`,
  760. ];
  761. console.log(genFormatArray(message), ...message);
  762. continue
  763. }
  764.  
  765. if (["nameFilter"] in x && (typeof x["nameFilter"] != "string") && !(x["nameFilter"] instanceof RegExp)) {
  766. const message = [
  767. normalStyle,
  768. `${now()} Storage Monitor: nameFilter config error, value = ${x["nameFilter"]}, need to be { string | Regexp | null }, so ignore this debugger = ${JSON.stringify(x)}`,
  769. ];
  770. console.log(genFormatArray(message), ...message);
  771. continue
  772. }
  773.  
  774. if (["valueFilter"] in x && (typeof x["valueFilter"] != "string") && !(x["valueFilter"] instanceof RegExp)) {
  775. const message = [
  776. normalStyle,
  777. `${now()} Storage Monitor: valueFilter config error, value = ${x["valueFilter"]}, need to be { string | Regexp | null }, so ignore this debugger = ${JSON.stringify(x)}`,
  778. ];
  779. console.log(genFormatArray(message), ...message);
  780. continue
  781. }
  782.  
  783. // TODO 出现了其它类型的key,是否配置错误呢?
  784.  
  785. const storageType = x["storageType"] || "all";
  786.  
  787. if ((x["name"] || x["value"]) && x["operationType"]) {
  788. const name = x["name"] || null;
  789. const value = x["value"] || null;
  790. newStorageDebuggerList.push(new StorageDebugger("all", "get", name, value));
  791. newStorageDebuggerList.push(new StorageDebugger("all", "set", name, value));
  792. newStorageDebuggerList.push(new StorageDebugger("all", "remove", name, value));
  793. } else {
  794. const operationType = x["operationType"] || "all";
  795. const name = x["name"] || null;
  796. const value = x["value"] || null;
  797. newStorageDebuggerList.push(new StorageDebugger(storageType, operationType, name, value));
  798. }
  799. }
  800. }
  801.  
  802. // 把原来的规则替换掉
  803. while (storageDebuggerList.pop()) {
  804. }
  805. for (let x of newStorageDebuggerList) {
  806. storageDebuggerList.push(x);
  807. }
  808. })();
  809.  
  810. // 奇奇怪怪的模板方式竟然一路被沿用下来...(*/ω\*)
  811. function genFormatArray(messageAndStyleArray) {
  812. const formatArray = [];
  813. for (let i = 0, end = messageAndStyleArray.length / 2; i < end; i++) {
  814. formatArray.push("%c%s");
  815. }
  816. return formatArray.join("");
  817. }
  818.  
  819. function now() {
  820. // 东八区专属...
  821. return "[" + new Date(new Date().getTime() + 1000 * 60 * 60 * 8).toJSON().replace("T", " ").replace("Z", "") + "] ";
  822. }
  823.  
  824. function cc11001100_getCodeLocation() {
  825. const callstack = new Error().stack.split("\n");
  826. while (callstack.length && callstack[0].indexOf("cc11001100_getCodeLocation") === -1) {
  827. callstack.shift();
  828. }
  829. callstack.shift();
  830. callstack.shift();
  831.  
  832. return callstack[0].trim();
  833. }
  834.  
  835. })();
  836.