injected.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. (function(){
  2. // avoid running repeatedly due to new document.documentElement
  3. if(window.VM) return;window.VM=1;
  4. /**
  5. * http://www.webtoolkit.info/javascript-utf8.html
  6. */
  7. function utf8decode (utftext) {
  8. var string = "";
  9. var i = 0;
  10. var c = 0, c1 = 0, c2 = 0, c3 = 0;
  11. while ( i < utftext.length ) {
  12. c = utftext.charCodeAt(i);
  13. if (c < 128) {string += String.fromCharCode(c);i++;}
  14. else if((c > 191) && (c < 224)) {
  15. c2 = utftext.charCodeAt(i+1);
  16. string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
  17. i += 2;
  18. } else {
  19. c2 = utftext.charCodeAt(i+1);
  20. c3 = utftext.charCodeAt(i+2);
  21. string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
  22. i += 3;
  23. }
  24. }
  25. return string;
  26. }
  27. // Messages
  28. chrome.runtime.onMessage.addListener(function(req,src) {
  29. var maps={
  30. Command:command,
  31. GetPopup:getPopup,
  32. GetBadge:getBadge,
  33. HttpRequested:httpRequested,
  34. },f=maps[req.cmd];
  35. if(f) f(req.data,src);
  36. });
  37. function getPopup(){
  38. // XXX: only scripts run in top level window are counted
  39. if(top===window)
  40. chrome.runtime.sendMessage({cmd:'SetPopup',data:[menu,ids]});
  41. }
  42. function getBadge(){
  43. // XXX: only scripts run in top level window are counted
  44. if(top===window)
  45. chrome.runtime.sendMessage({cmd:'SetBadge',data:total});
  46. }
  47. // Communicator
  48. var comm={
  49. vmid:'VM'+Math.random(),
  50. state:0,
  51. utf8decode:utf8decode,
  52. // Array functions
  53. // to avoid using prototype functions
  54. // since they may be changed by page scripts
  55. inArray:function(arr, item) {
  56. for(var i=0;i<arr.length;i++)
  57. if(arr[i]==item) return true;
  58. return false;
  59. },
  60. extendArray:function(arr, arr2) {
  61. for(var i=0;i<arr2.length;i++) arr.push(arr2[i]);
  62. },
  63. forEach:function(arr, func, args) {
  64. for(var i=0;i<arr.length;i++) {
  65. var a=[arr[i]];
  66. if(args) this.extendArray(a,args);
  67. func.apply(arr,a);
  68. }
  69. },
  70. prop1:Object.getOwnPropertyNames(window),
  71. prop2:(function(n,p){
  72. while(n=Object.getPrototypeOf(n)) p=p.concat(Object.getOwnPropertyNames(n));
  73. return p;
  74. })(window,[]),
  75. init:function(s,d){
  76. var t=this;
  77. t.sid=t.vmid+s;
  78. t.did=t.vmid+d;
  79. document.addEventListener(t.sid,t['handle'+s].bind(t),false);
  80. t.load=t.checkLoad=function(){};
  81. },
  82. post:function(d){
  83. var e=document.createEvent("MutationEvent");
  84. e.initMutationEvent(this.did,false,false,null,null,null,JSON.stringify(d),e.ADDITION);
  85. document.dispatchEvent(e);
  86. },
  87. handleR:function(e){
  88. var o=JSON.parse(e.attrName),comm=this,maps={
  89. LoadScript:comm.loadScript.bind(comm),
  90. Command:function(o){
  91. var f=comm.command[o];
  92. if(f) f();
  93. },
  94. GotRequestId:function(o){comm.qrequests.shift().start(o);},
  95. HttpRequested:function(o){
  96. var c=comm.requests[o.id];
  97. if(c) c.callback(o);
  98. },
  99. },f=maps[o.cmd];
  100. if(f) f(o.data);
  101. },
  102. loadScript:function(o){
  103. var start=[],idle=[],end=[],cache,require,values,comm=this,urls={},
  104. Request=(function(){
  105. // request functions
  106. function reqAbort(){comm.post({cmd:'AbortRequest',data:this.id});}
  107. // request object functions
  108. function callback(d){
  109. var i,t=this,c=t.details['on'+d.type];
  110. if(c) {
  111. if(d.data.response) {
  112. if(!t.data.length) {
  113. if(d.resType) { // blob or arraybuffer
  114. var m=d.data.response.match(/^data:(.*?);base64,(.*)$/);
  115. if(!m) d.data.response=null;
  116. else {
  117. var b=window.atob(m[2]);
  118. if(t.details.responseType=='blob') {
  119. t.data.push(new Blob([b],{type:m[1]}));
  120. } else { // arraybuffer
  121. m=new Uint8Array(b.length);
  122. for(i=0;i<b.length;i++) m[i]=b.charCodeAt(i);
  123. t.data.push(m.buffer);
  124. }
  125. }
  126. } else if(t.details.responseType=='json') // json
  127. t.data.push(JSON.parse(d.data.response));
  128. else // text
  129. t.data.push(d.data.response);
  130. }
  131. d.data.response=t.data[0];
  132. }
  133. c(d.data);
  134. }
  135. if(d.type=='loadend') delete comm.requests[t.id];
  136. }
  137. function start(id){
  138. var t=this,data={
  139. id:id,
  140. method:t.details.method,
  141. url:t.details.url,
  142. data:t.details.data,
  143. //async:!t.details.synchronous,
  144. user:t.details.user,
  145. password:t.details.password,
  146. headers:t.details.headers,
  147. overrideMimeType:t.details.overrideMimeType,
  148. };
  149. t.id=id;
  150. comm.requests[id]=t;
  151. if(comm.inArray(['arraybuffer','blob'],t.details.responseType)) data.responseType='blob';
  152. comm.post({cmd:'HttpRequest',data:data});
  153. }
  154. return function(details){
  155. var t={
  156. details:details,
  157. callback:callback,
  158. start:start,
  159. req:{
  160. abort:reqAbort,
  161. },
  162. data:[],
  163. },a=document.createElement('a');
  164. a.setAttribute('href',details.url);
  165. details.url=a.href;
  166. comm.qrequests.push(t);
  167. comm.post({cmd:'GetRequestId'});
  168. return t.req;
  169. };
  170. })();
  171. comm.command={};comm.requests={};comm.qrequests=[];
  172. /*
  173. * Wrap functions and properties
  174. */
  175. // wrapper_old: wrap all functions and properties at the start
  176. // DEPRECATED
  177. /*function wrapper_old(){
  178. function wrapFunction(o,i,c){
  179. var f=function(){
  180. var r;
  181. try{r=Function.apply.apply(o[i],[o,arguments]);}
  182. catch(e){console.log('Error calling '+i+': \n'+e.stack);}
  183. if(c) r=c(r);return r;
  184. };
  185. f.__proto__=o[i];f.prototype=o[i].prototype;
  186. return f;
  187. }
  188. function wrapWindow(w){return w==window?t:w;}
  189. function wrapItem(key,wrap){
  190. try{ // avoid reading protected data
  191. if(typeof window[key]=='function') {
  192. if(wrap) t[key]=wrapFunction(window,key,wrapWindow);
  193. else t[key]=window[key];
  194. } else Object.defineProperty(t,key,{
  195. get:function(){return wrapWindow(window[key]);},
  196. set:function(v){window[key]=v;},
  197. });
  198. }catch(e){}
  199. }
  200. var t=this;
  201. comm.prop1.forEach(function(i){wrapItem(i);});
  202. comm.prop2.forEach(function(i){wrapItem(i,true);});
  203. }*/
  204. // wrapper: wrap functions as needed, return and set properties
  205. function wrapper(){
  206. function wrapItem(key,wrap){
  207. var type=null,value,apply=Function.apply;
  208. // avoid using prototype functions
  209. // since some script authors change them unexpectedly
  210. // (e.g. Array.indexOf)
  211. function initProperty() {
  212. if(!comm.inArray(['function','custom'],type)) {
  213. value=window[key];
  214. type=typeof value;
  215. if(type=='function'&&wrap) {
  216. var o=value;
  217. value=function(){
  218. var r;
  219. try {
  220. r=apply.apply(o,[window,arguments]);
  221. } catch(e) {
  222. console.log('Error calling '+key+':\n'+e.stack);
  223. }
  224. return r===window?t:r;
  225. };
  226. value.__proto__=o;
  227. value.prototype=o.prototype;
  228. }
  229. }
  230. }
  231. try {
  232. Object.defineProperty(t,key,{
  233. get:function(){
  234. initProperty();
  235. return value===window?t:value;
  236. },
  237. set:function(v){
  238. initProperty();
  239. value=v;
  240. if(type!='function') window[key]=v;
  241. type='custom';
  242. },
  243. });
  244. } catch(e) {
  245. // ignore protected data
  246. }
  247. }
  248. var t=this;
  249. comm.forEach(comm.prop1,wrapItem,[]);
  250. comm.forEach(comm.prop2,wrapItem,[true]);
  251. }
  252. function wrapGM(c){
  253. // Add GM functions
  254. // Reference: http://wiki.greasespot.net/Greasemonkey_Manual:API
  255. var gm={},value=values[c.uri]||{},w,g=c.meta.grant||[];
  256. if(!g.length||g.length==1&&g[0]=='none') { // @grant none
  257. w={};g.pop();
  258. } else {
  259. w=new wrapper();
  260. }
  261. if(!comm.inArray(g,'unsafeWindow')) g.push('unsafeWindow');
  262. function propertyToString(){return 'Property for Violentmonkey: designed by Gerald';}
  263. function addProperty(name,prop,obj){
  264. if('value' in prop) prop.writable=false;
  265. prop.configurable=false;
  266. Object.defineProperty(obj,name,prop);
  267. if(typeof obj[name]=='function') obj[name].toString=propertyToString;
  268. }
  269. var resources=c.meta.resources||{},gf={
  270. unsafeWindow:{value:window},
  271. GM_info:{get:function(){
  272. var m=c.code.match(/\/\/\s+==UserScript==\s+([\s\S]*?)\/\/\s+==\/UserScript==\s/),
  273. script={
  274. description:c.meta.description||'',
  275. excludes:c.meta.exclude.concat(),
  276. includes:c.meta.include.concat(),
  277. matches:c.meta.match.concat(),
  278. name:c.meta.name||'',
  279. namespace:c.meta.namespace||'',
  280. resources:{},
  281. 'run-at':c.meta['run-at']||'document-end',
  282. unwrap:false,
  283. version:c.meta.version||'',
  284. },
  285. o={};
  286. addProperty('script',{value:{}},o);
  287. addProperty('scriptMetaStr',{value:m?m[1]:''},o);
  288. addProperty('scriptWillUpdate',{value:c.update},o);
  289. addProperty('version',{value:undefined},o);
  290. for(m in script) addProperty(m,{value:script[m]},o.script);
  291. for(m in c.meta.resources) addProperty(m,{value:c.meta.resources[m]},o.script.resources);
  292. return o;
  293. }},
  294. GM_deleteValue:{value:function(key){delete value[key];comm.post({cmd:'SetValue',data:{uri:c.uri,values:value}});}},
  295. GM_getValue:{value:function(k,d){
  296. var v=value[k];
  297. if(v) {
  298. k=v[0];
  299. v=v.slice(1);
  300. switch(k){
  301. case 'n': d=Number(v);break;
  302. case 'b': d=v=='true';break;
  303. case 'o': try{d=JSON.parse(v);}catch(e){console.log(e);}break;
  304. default: d=v;
  305. }
  306. }
  307. return d;
  308. }},
  309. GM_listValues:{value:function(){return Object.getOwnPropertyNames(value);}},
  310. GM_setValue:{value:function(key,val){
  311. var t=(typeof val)[0];
  312. switch(t){
  313. case 'o':val=t+JSON.stringify(val);break;
  314. default:val=t+val;
  315. }
  316. value[key]=val;comm.post({cmd:'SetValue',data:{uri:c.uri,values:value}});
  317. }},
  318. GM_getResourceText:{value:function(name){
  319. var i,u;
  320. for(i in resources) if(name==i) {
  321. u=cache[resources[i]];
  322. if(u) u=comm.utf8decode(window.atob(u));
  323. return u;
  324. }
  325. }},
  326. GM_getResourceURL:{value:function(name){
  327. var i,u,r,j,b;
  328. for(i in resources) if(name==i) {
  329. i=resources[i];u=urls[i];
  330. if(!u&&(r=cache[i])) {
  331. r=window.atob(r);
  332. b=new Uint8Array(r.length);
  333. for(j=0;j<r.length;j++) b[j]=r.charCodeAt(j);
  334. b=new Blob([b]);
  335. urls[i]=u=URL.createObjectURL(b);
  336. }
  337. return u;
  338. }
  339. }},
  340. GM_addStyle:{value:function(css){
  341. if(document.head) {
  342. var v=document.createElement('style');
  343. v.innerHTML=css;
  344. document.head.appendChild(v);
  345. return v;
  346. }
  347. }},
  348. GM_log:{value:function(d){console.log(d);}},
  349. GM_openInTab:{value:function(url){
  350. var a=document.createElement('a');
  351. a.href=url;a.target='_blank';a.click();
  352. }},
  353. GM_registerMenuCommand:{value:function(cap,func,acc){
  354. comm.command[cap]=func;comm.post({cmd:'RegisterMenu',data:[cap,acc]});
  355. }},
  356. GM_xmlhttpRequest:{value:function(details){
  357. return Request(details);
  358. }},
  359. };
  360. comm.forEach(g,function(i){var o=gf[i];if(o) addProperty(i,o,gm);});
  361. return [w,gm];
  362. }
  363. function run(l){while(l.length) runCode(l.shift());}
  364. function runCode(c){
  365. var req=c.meta.require||[],i,r=[],code=[],w=wrapGM(c);
  366. comm.forEach(Object.getOwnPropertyNames(w[1]),function(i){r.push(i+'=g["'+i+'"]');});
  367. if(r.length) code.push('var '+r.join(',')+';delete g;with(this)(function(){');
  368. for(i=0;i<req.length;i++) if(r=require[req[i]]) code.push(r);
  369. code.push(c.code);code.push('}).call(window);');
  370. code=code.join('\n');
  371. try{
  372. (new Function('g',code)).call(w[0],w[1]);
  373. }catch(e){
  374. console.log('Error running script: '+(c.custom.name||c.meta.name||c.id)+'\n'+e.message);
  375. //console.log('Error running script: '+(c.custom.name||c.meta.name||c.id)+'\n'+e.stack);
  376. }
  377. }
  378. comm.load=function(){run(end);run(idle);};
  379. comm.checkLoad=function(){
  380. if(!comm.state&&comm.inArray(['interactive','complete'],document.readyState)) comm.state=1;
  381. if(comm.state) comm.load();
  382. };
  383. require=o.require;
  384. cache=o.cache;
  385. values=o.values;
  386. comm.forEach(o.scripts,function(i,l){
  387. if(i&&i.enabled) {
  388. switch(i.custom['run-at']||i.meta['run-at']){
  389. case 'document-start': l=start;break;
  390. case 'document-idle': l=idle;break;
  391. default: l=end;
  392. }
  393. l.push(i);
  394. }
  395. });
  396. run(start);comm.checkLoad();
  397. },
  398. },menu=[],ids=[],total=0;
  399. function handleC(e){
  400. var o=JSON.parse(e.attrName),maps={
  401. SetValue:function(o){
  402. chrome.runtime.sendMessage({cmd:'SetValue',data:o});
  403. },
  404. RegisterMenu:function(o){
  405. if(window.top===window) menu.push(o);
  406. },
  407. GetRequestId:getRequestId,
  408. HttpRequest:httpRequest,
  409. AbortRequest:abortRequest,
  410. },f=maps[o.cmd];
  411. if(f) f(o.data);
  412. }
  413. function command(o){
  414. comm.post({cmd:'Command',data:o});
  415. }
  416. // Requests
  417. var requests={};
  418. function getRequestId() {
  419. chrome.runtime.sendMessage({cmd:'GetRequestId'},function(id){
  420. requests[id]=1;
  421. comm.post({cmd:'GotRequestId',data:id});
  422. });
  423. }
  424. function httpRequest(details) {
  425. chrome.runtime.sendMessage({cmd:'HttpRequest',data:details});
  426. }
  427. function httpRequested(data) {
  428. if(requests[data.id]) {
  429. if(data.type=='loadend') delete requests[data.id];
  430. comm.post({cmd:'HttpRequested',data:data});
  431. }
  432. }
  433. function abortRequest(id) {
  434. chrome.runtime.sendMessage({cmd:'AbortRequest',data:id});
  435. }
  436. // For injected scripts
  437. function objEncode(o){
  438. var t=[],i;
  439. for(i in o) {
  440. if(!o.hasOwnProperty(i)) continue;
  441. if(typeof o[i]=='function') t.push(i+':'+o[i].toString());
  442. else t.push(i+':'+JSON.stringify(o[i]));
  443. }
  444. return '{'+t.join(',')+'}';
  445. }
  446. function initCommunicator(){
  447. var s=document.createElement('script'),d=document.documentElement,C='C',R='R';
  448. s.innerHTML='('+(function(c,R,C){
  449. c.init(R,C);
  450. document.addEventListener("DOMContentLoaded",function(e){
  451. c.state=1;c.load();
  452. },false);
  453. c.checkLoad();
  454. }).toString()+')('+objEncode(comm)+',"'+R+'","'+C+'")';
  455. d.appendChild(s);d.removeChild(s);
  456. comm.handleC=handleC;comm.init(C,R);
  457. chrome.runtime.sendMessage({cmd:'GetInjected',data:location.href},loadScript);
  458. }
  459. function loadScript(o){
  460. o.scripts.forEach(function(i){
  461. ids.push(i.id);if(i.enabled) total++;
  462. });
  463. comm.post({cmd:'LoadScript',data:o});
  464. }
  465. initCommunicator();
  466. })();