├── .gitignore ├── README.md ├── background.js ├── content.js ├── hookajax2.zip ├── iframe ├── .babelrc ├── .gitignore ├── Main.js ├── Main.less ├── Replacer.js ├── Replacer.less ├── bundle.js ├── index.html ├── index.js ├── package-lock.json ├── package.json └── webpack.config.js ├── images ├── 128.png ├── 128_gray.png ├── 16.png ├── 16_gray.png ├── 32.png ├── 32_gray.png ├── 48.png ├── 48_gray.png └── hook222.png ├── manifest.json ├── package-lock.json └── pageScripts └── main.js /.gitignore: -------------------------------------------------------------------------------- 1 | au.sh 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | A chrome extension for modifing response text of ajax requests easily. You can use it to debug errors. 4 | 5 | 6 | ## Notes 7 | 1. You may have to restart chrome or refresh the current page after you added this extension. 8 | 2. It is recommended that you turn off this extension(the icon should be gray) when you are not using it. 9 | 3. This extension only overrides the response data in the XMLHTTPRequest object as well as the fetch method. The "real" response which you can see in DevTools' "Network" panel will not be changed. 10 | 11 | code reference https://raw.githubusercontent.com/YGYOOO/ajax-interceptor/master/ -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | // chrome.runtime.onInstalled.addListener(function() { 2 | // chrome.storage.sync.set({color: '#3aa757'}, function() { 3 | // console.log('The color is green.'); 4 | // }); 5 | // chrome.runtime.onInstalled.addListener(function(details) { 6 | // chrome.declarativeContent.onPageChanged.addRules([{ 7 | // conditions: [new chrome.declarativeContent.PageStateMatcher({ 8 | // pageUrl: {hostEquals: 'developer.chrome.com'}, 9 | // }) 10 | // ], 11 | // actions: [new chrome.declarativeContent.ShowPageAction()] 12 | // }]); 13 | // }); 14 | // }); 15 | 16 | 17 | // chrome.webNavigation.onBeforeNavigate.addListener(() => { 18 | // console.log(1) 19 | // chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { 20 | // chrome.tabs.executeScript( 21 | // tabs[0].id, 22 | // {code: `console.log('executeScript')`}); 23 | // }); 24 | // }); 25 | 26 | // chrome.webRequest.onBeforeRequest.addListener( 27 | // function(info) { 28 | // console.log("Cat intercepted: " + info.url); 29 | // // Redirect the lolcal request to a random loldog URL. 30 | // chrome.tabs.sendMessage(details.tabId, details, function(response) { 31 | // // 此处可以修改response... 32 | // redirectUrl = "data:application/json;charset=UTF-8;base64," + Base64.encode(newResponse) 33 | // }); 34 | // return ({cancel: false}); 35 | // }, 36 | // ({ 37 | // urls: ["*://*/*"], 38 | // types: ["script"] 39 | // }), 40 | // ["blocking"]); 41 | chrome.browserAction.onClicked.addListener(function(tab) { 42 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs){ 43 | chrome.tabs.sendMessage(tabs[0].id, "toggle"); 44 | }) 45 | }); 46 | 47 | // 接收iframe传来的信息,转发给content.js 48 | chrome.runtime.onMessage.addListener(msg => { 49 | if (msg.type === 'ajaxInterceptor' && msg.to === 'background') { 50 | if (msg.key === 'ajaxInterceptor_switchOn') { 51 | if (msg.value === true) { 52 | chrome.browserAction.setIcon({path: { 53 | 16: '/images/16.png', 54 | 32: '/images/32.png', 55 | 48: '/images/48.png', 56 | 128: '/images/128.png', 57 | }}); 58 | } else { 59 | chrome.browserAction.setIcon({path: { 60 | 16: '/images/16_gray.png', 61 | 32: '/images/32_gray.png', 62 | 48: '/images/48_gray.png', 63 | 128: '/images/128_gray.png', 64 | }}); 65 | } 66 | } 67 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs){ 68 | chrome.tabs.sendMessage(tabs[0].id, {...msg, to: 'content'}); 69 | }) 70 | } 71 | }); 72 | 73 | chrome.storage.local.get(['ajaxInterceptor_switchOn', 'ajaxInterceptor_rules'], (result) => { 74 | if (result.hasOwnProperty('ajaxInterceptor_switchOn')) { 75 | if (result.ajaxInterceptor_switchOn) { 76 | chrome.browserAction.setIcon({path: "/images/16.png"}); 77 | } else { 78 | chrome.browserAction.setIcon({path: "/images/16_gray.png"}); 79 | } 80 | } 81 | }); -------------------------------------------------------------------------------- /content.js: -------------------------------------------------------------------------------- 1 | // const elt = document.createElement("script"); 2 | // elt.innerHTML = "window.test = 1" 3 | // document.head.appendChild(elt); 4 | 5 | // 在页面上插入代码 6 | // const s1 = document.createElement('script'); 7 | // s1.setAttribute('type', 'text/javascript'); 8 | // s1.setAttribute('src', chrome.extension.getURL('pageScripts/defaultSettings.js')); 9 | // document.documentElement.appendChild(s1); 10 | 11 | // 在页面上插入代码 12 | const script = document.createElement('script'); 13 | script.setAttribute('type', 'text/javascript'); 14 | script.setAttribute('src', chrome.extension.getURL('pageScripts/main.js')); 15 | document.documentElement.appendChild(script); 16 | 17 | script.addEventListener('load', () => { 18 | chrome.storage.local.get(['ajaxInterceptor_switchOn', 'ajaxInterceptor_rules'], (result) => { 19 | if (result.hasOwnProperty('ajaxInterceptor_switchOn')) { 20 | postMessage({type: 'ajaxInterceptor', to: 'pageScript', key: 'ajaxInterceptor_switchOn', value: result.ajaxInterceptor_switchOn}); 21 | } 22 | if (result.ajaxInterceptor_rules) { 23 | postMessage({type: 'ajaxInterceptor', to: 'pageScript', key: 'ajaxInterceptor_rules', value: result.ajaxInterceptor_rules}); 24 | } 25 | }); 26 | }); 27 | 28 | 29 | let iframe; 30 | let iframeLoaded = false; 31 | 32 | // 只在最顶层页面嵌入iframe 33 | if (window.self === window.top) { 34 | 35 | document.onreadystatechange = () => { 36 | if (document.readyState === 'complete') { 37 | iframe = document.createElement('iframe'); 38 | iframe.className = "api-interceptor"; 39 | iframe.style.setProperty('height', '100%', 'important'); 40 | iframe.style.setProperty('width', '900px', 'important'); 41 | iframe.style.setProperty('min-width', '1px', 'important'); 42 | iframe.style.setProperty('position', 'fixed', 'important'); 43 | iframe.style.setProperty('top', '0', 'important'); 44 | iframe.style.setProperty('right', '0', 'important'); 45 | iframe.style.setProperty('left', 'auto', 'important'); 46 | iframe.style.setProperty('bottom', 'auto', 'important'); 47 | iframe.style.setProperty('z-index', '9999999999999', 'important'); 48 | iframe.style.setProperty('transform', 'translateX(920px)', 'important'); 49 | iframe.style.setProperty('transition', 'all .4s', 'important'); 50 | iframe.style.setProperty('box-shadow', '0 0 15px 2px rgba(0,0,0,0.12)', 'important'); 51 | iframe.frameBorder = "none"; 52 | iframe.src = chrome.extension.getURL("iframe/index.html") 53 | document.body.appendChild(iframe); 54 | let show = false; 55 | 56 | chrome.runtime.onMessage.addListener((msg, sender) => { 57 | if (msg == 'toggle') { 58 | show = !show; 59 | iframe.style.setProperty('transform', show ? 'translateX(0)' : 'translateX(920px)', 'important'); 60 | } 61 | 62 | return true; 63 | }); 64 | } 65 | } 66 | } 67 | 68 | 69 | // 接收background.js传来的信息,转发给pageScript 70 | chrome.runtime.onMessage.addListener(msg => { 71 | if (msg.type === 'ajaxInterceptor' && msg.to === 'content') { 72 | if (msg.hasOwnProperty('iframeScriptLoaded')) { 73 | if (msg.iframeScriptLoaded) iframeLoaded = true; 74 | } else { 75 | postMessage({...msg, to: 'pageScript'}); 76 | } 77 | } 78 | }); 79 | 80 | // 接收pageScript传来的信息,转发给iframe 81 | window.addEventListener("pageScript", function(event) { 82 | if (iframeLoaded) { 83 | chrome.runtime.sendMessage({type: 'ajaxInterceptor', to: 'iframe', ...event.detail}); 84 | } else { 85 | let count = 0; 86 | const checktLoadedInterval = setInterval(() => { 87 | if (iframeLoaded) { 88 | clearInterval(checktLoadedInterval); 89 | chrome.runtime.sendMessage({type: 'ajaxInterceptor', to: 'iframe', ...event.detail}); 90 | } 91 | if (count ++ > 500) { 92 | clearInterval(checktLoadedInterval); 93 | } 94 | }, 10); 95 | } 96 | }, false); 97 | 98 | // window.addEventListener("message", function(event) { 99 | // console.log(event.data) 100 | // }, false); 101 | 102 | // window.parent.postMessage({ type: "CONTENT", text: "Hello from the webpage!" }, "*"); 103 | 104 | 105 | // var s = document.createElement('script'); 106 | // s.setAttribute('type', 'text/javascript'); 107 | // s.innerText = `console.log('test')`; 108 | // document.documentElement.appendChild(s); 109 | 110 | -------------------------------------------------------------------------------- /hookajax2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenzhenjinma/hook-chrome-ajax/43bdfe5b7f33fa361bfe0b8107104f9d896002ca/hookajax2.zip -------------------------------------------------------------------------------- /iframe/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env", "@babel/preset-react"], 3 | "plugins": [ 4 | "@babel/plugin-proposal-class-properties" 5 | ] 6 | } -------------------------------------------------------------------------------- /iframe/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /iframe/Main.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import 'antd/dist/antd.css'; 3 | import {Switch, Collapse, Input, Select, Button, Badge, Tooltip} from 'antd'; 4 | import { ToastContainer, toast } from 'react-toastify'; 5 | import 'react-toastify/dist/ReactToastify.css'; 6 | const Panel = Collapse.Panel; 7 | 8 | import Replacer from './Replacer'; 9 | 10 | import './Main.less'; 11 | 12 | const buildUUID = () => { 13 | var dt = new Date().getTime(); 14 | var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { 15 | var r = (dt + Math.random()*16)%16 | 0; 16 | dt = Math.floor(dt/16); 17 | return (c=='x' ? r :(r&0x3|0x8)).toString(16); 18 | }); 19 | return uuid; 20 | } 21 | 22 | 23 | export default class Main extends Component { 24 | constructor() { 25 | super(); 26 | chrome.runtime.onMessage.addListener(({type, to, url, match}) => { 27 | if (type === 'ajaxInterceptor' && to === 'iframe') { 28 | const {interceptedRequests} = this.state; 29 | if (!interceptedRequests[match]) interceptedRequests[match] = []; 30 | 31 | const exits = interceptedRequests[match].some(obj => { 32 | if (obj.url === url) { 33 | obj.num++; 34 | return true; 35 | } 36 | return false; 37 | }); 38 | 39 | if (!exits) { 40 | interceptedRequests[match].push({url, num: 1}); 41 | } 42 | this.setState({interceptedRequests}, () => { 43 | if (!exits) { 44 | // 新增的拦截的url,会多展示一行url,需要重新计算高度 45 | this.updateAddBtnTop_interval(); 46 | } 47 | }) 48 | } 49 | }); 50 | 51 | chrome.runtime.sendMessage(chrome.runtime.id, {type: 'ajaxInterceptor', to: 'background', iframeScriptLoaded: true}); 52 | 53 | this.collapseWrapperHeight = -1; 54 | } 55 | 56 | state = { 57 | interceptedRequests: {}, 58 | } 59 | 60 | componentDidMount() { 61 | this.updateAddBtnTop_interval(); 62 | } 63 | 64 | 65 | updateAddBtnTop = () => { 66 | let curCollapseWrapperHeight = this.collapseWrapperRef ? this.collapseWrapperRef.offsetHeight : 0; 67 | if (this.collapseWrapperHeight !== curCollapseWrapperHeight) { 68 | this.collapseWrapperHeight = curCollapseWrapperHeight; 69 | clearTimeout(this.updateAddBtnTopDebounceTimeout); 70 | this.updateAddBtnTopDebounceTimeout = setTimeout(() => { 71 | this.addBtnRef.style.top = `${curCollapseWrapperHeight + 30}px`; 72 | }, 50); 73 | } 74 | } 75 | 76 | // 计算按钮位置 77 | updateAddBtnTop_interval = ({timeout = 1000, interval = 50 } = {}) => { 78 | const i = setInterval(this.updateAddBtnTop, interval); 79 | setTimeout(() => { 80 | clearInterval(i); 81 | }, timeout); 82 | } 83 | 84 | set = (key, value) => { 85 | // 发送给background.js 86 | chrome.runtime.sendMessage(chrome.runtime.id, {type: 'ajaxInterceptor', to: 'background', key, value}); 87 | chrome.storage && chrome.storage.local.set({[key]: value}); 88 | } 89 | 90 | forceUpdateDebouce = () => { 91 | console.log("window.setting.ajaxInterceptor_rules",window.setting.ajaxInterceptor_rules); 92 | clearTimeout(this.forceUpdateTimeout); 93 | this.forceUpdateTimeout = setTimeout(() => { 94 | this.forceUpdate(); 95 | toast("Saved👌👌👌"); 96 | }, 1000); 97 | } 98 | 99 | handleSingleSwitchChange = (switchOn, i) => { 100 | window.setting.ajaxInterceptor_rules[i].switchOn = switchOn; 101 | this.set('ajaxInterceptor_rules', window.setting.ajaxInterceptor_rules); 102 | 103 | // 这么搞主要是为了能实时同步window.setting.ajaxInterceptor_rules,并且让性能好一点 104 | this.forceUpdateDebouce(); 105 | } 106 | 107 | handleFilterTypeChange = (val, i) => { 108 | window.setting.ajaxInterceptor_rules[i].filterType = val; 109 | this.set('ajaxInterceptor_rules', window.setting.ajaxInterceptor_rules); 110 | 111 | this.forceUpdate(); 112 | } 113 | 114 | handleFilterCodeChange = (val, i) => { 115 | window.setting.ajaxInterceptor_rules[i].filterCode = val; 116 | this.set('ajaxInterceptor_rules', window.setting.ajaxInterceptor_rules); 117 | 118 | this.forceUpdate(); 119 | } 120 | 121 | handleMatchChange = (e, i) => { 122 | window.setting.ajaxInterceptor_rules[i].match = e.target.value; 123 | this.set('ajaxInterceptor_rules', window.setting.ajaxInterceptor_rules); 124 | 125 | this.forceUpdateDebouce(); 126 | } 127 | 128 | handleClickAdd = () => { 129 | window.setting.ajaxInterceptor_rules.push({match: '', switchOn: true, key: buildUUID()}); 130 | this.forceUpdate(this.updateAddBtnTop_interval); 131 | } 132 | 133 | handleClickRemove = (e, i) => { 134 | e.stopPropagation(); 135 | const {interceptedRequests} = this.state; 136 | const match = window.setting.ajaxInterceptor_rules[i].match; 137 | 138 | window.setting.ajaxInterceptor_rules = [ 139 | ...window.setting.ajaxInterceptor_rules.slice(0, i), 140 | ...window.setting.ajaxInterceptor_rules.slice(i + 1), 141 | ]; 142 | this.set('ajaxInterceptor_rules', window.setting.ajaxInterceptor_rules); 143 | 144 | delete interceptedRequests[match]; 145 | this.setState({interceptedRequests}, this.updateAddBtnTop_interval); 146 | } 147 | 148 | handleCollaseChange = ({timeout = 1200, interval = 50 }) => { 149 | this.updateAddBtnTop_interval(); 150 | } 151 | 152 | handleSwitchChange = () => { 153 | window.setting.ajaxInterceptor_switchOn = !window.setting.ajaxInterceptor_switchOn; 154 | this.set('ajaxInterceptor_switchOn', window.setting.ajaxInterceptor_switchOn); 155 | this.zeroAddBtn(); 156 | this.forceUpdate(); 157 | } 158 | zeroAddBtn=()=>{ 159 | if(window.setting.ajaxInterceptor_rules.length==0){ 160 | this.handleClickAdd(); 161 | } 162 | } 163 | 164 | render() { 165 | return ( 166 |
167 | 168 | 174 |
175 | {window.setting.ajaxInterceptor_rules && window.setting.ajaxInterceptor_rules.length > 0 ? ( 176 |
this.collapseWrapperRef = ref}> 177 | 182 | {window.setting.ajaxInterceptor_rules.map(({filterType = 'normal', match, overrideTxt, switchOn = true, key}, i) => ( 183 | e.stopPropagation()}> 187 | 188 | 193 | 197 | e.stopPropagation()} 202 | onChange={e => this.handleMatchChange(e, i)} 203 | /> 204 | 205 | this.handleSingleSwitchChange(val, i)} 209 | /> 210 |
219 | } 220 | > 221 | 227 | {/*
228 | Replace With: 229 |
230 |