소스 검색

fix: cache native properties to avoid being overridden

close #151
Gerald 8 년 전
부모
커밋
110c8281a7
3개의 변경된 파일33개의 추가작업 그리고 26개의 파일을 삭제
  1. 9 11
      src/injected/content/index.js
  2. 13 0
      src/injected/utils.js
  3. 11 15
      src/injected/web/index.js

+ 9 - 11
src/injected/content/index.js

@@ -1,4 +1,4 @@
-import { bindEvents, sendMessage, postData, inject } from '../utils';
+import { bindEvents, sendMessage, inject, attachFunction } from '../utils';
 import bridge from './bridge';
 import bridge from './bridge';
 import { tabOpen, tabClose, tabClosed } from './tabs';
 import { tabOpen, tabClose, tabClosed } from './tabs';
 import { onNotificationCreate, onNotificationClick, onNotificationClose } from './notifications';
 import { onNotificationCreate, onNotificationClick, onNotificationClose } from './notifications';
@@ -113,19 +113,17 @@ function getPopup() {
 }
 }
 
 
 function injectScript(data) {
 function injectScript(data) {
-  const [id, wrapperKeys, code] = data;
-  const func = (scriptId, cb, post, destId) => {
-    Object.defineProperty(window, `VM_${scriptId}`, {
-      value: cb,
-      configurable: true,
-    });
-    post(destId, { cmd: 'Injected', data: scriptId });
+  const [vId, wrapperKeys, code, vCallbackId] = data;
+  const func = (attach, id, cb, callbackId) => {
+    attach(id, cb);
+    const callback = window[callbackId];
+    if (callback) callback();
   };
   };
   const args = [
   const args = [
-    JSON.stringify(id),
+    attachFunction.toString(),
+    JSON.stringify(vId),
     `function(${wrapperKeys.join(',')}){${code}}`,
     `function(${wrapperKeys.join(',')}){${code}}`,
-    postData.toString(),
-    JSON.stringify(bridge.destId),
+    JSON.stringify(vCallbackId),
   ];
   ];
   inject(`!${func.toString()}(${args.join(',')})`);
   inject(`!${func.toString()}(${args.join(',')})`);
 }
 }

+ 13 - 0
src/injected/utils.js

@@ -1,6 +1,9 @@
 export { sendMessage, request } from 'src/common';
 export { sendMessage, request } from 'src/common';
 export Promise from 'core-js/library/fn/promise';
 export Promise from 'core-js/library/fn/promise';
 
 
+// cache native properties to avoid being overridden, see violentmonkey/violentmonkey#151
+export const { console, CustomEvent } = window;
+
 export function postData(destId, data) {
 export function postData(destId, data) {
   // Firefox issue: data must be stringified to avoid cross-origin problem
   // Firefox issue: data must be stringified to avoid cross-origin problem
   const e = new CustomEvent(destId, { detail: JSON.stringify(data) });
   const e = new CustomEvent(destId, { detail: JSON.stringify(data) });
@@ -51,3 +54,13 @@ export function bindEvents(srcId, destId, handle) {
   }, false);
   }, false);
   return data => { postData(destId, data); };
   return data => { postData(destId, data); };
 }
 }
+
+export function attachFunction(id, cb) {
+  Object.defineProperty(window, id, {
+    value(...args) {
+      cb(...args);
+      delete window[id];
+    },
+    configurable: true,
+  });
+}

+ 11 - 15
src/injected/web/index.js

@@ -1,4 +1,4 @@
-import { getUniqId, bindEvents, Promise } from '../utils';
+import { getUniqId, bindEvents, Promise, attachFunction, console } from '../utils';
 import { includes, forEach, map, utf8decode } from './helpers';
 import { includes, forEach, map, utf8decode } from './helpers';
 import bridge from './bridge';
 import bridge from './bridge';
 import { onRequestCreate, onRequestStart, onRequestCallback } from './requests';
 import { onRequestCreate, onRequestStart, onRequestCallback } from './requests';
@@ -23,7 +23,6 @@ export default function initialize(webId, contentId, props) {
 
 
 const store = {
 const store = {
   commands: {},
   commands: {},
-  ainject: {},
   values: {},
   values: {},
 };
 };
 
 
@@ -41,13 +40,6 @@ const handlers = {
   },
   },
   NotificationClicked: onNotificationClicked,
   NotificationClicked: onNotificationClicked,
   NotificationClosed: onNotificationClosed,
   NotificationClosed: onNotificationClosed,
-  Injected(id) {
-    const item = store.ainject[id];
-    const func = window[`VM_${id}`];
-    delete window[`VM_${id}`];
-    delete store.ainject[id];
-    if (item && func) runCode(item[0], func, item[1], item[2]);
-  },
   ScriptChecked(data) {
   ScriptChecked(data) {
     if (bridge.onScriptChecked) bridge.onScriptChecked(data);
     if (bridge.onScriptChecked) bridge.onScriptChecked(data);
   },
   },
@@ -105,8 +97,8 @@ function onLoadScripts(data) {
     const metaStr = matches ? matches[1] : '';
     const metaStr = matches ? matches[1] : '';
     const wrapper = wrapGM(script, metaStr, data.cache);
     const wrapper = wrapGM(script, metaStr, data.cache);
     // Must use Object.getOwnPropertyNames to list unenumerable properties
     // Must use Object.getOwnPropertyNames to list unenumerable properties
-    const wrapperKeys = Object.getOwnPropertyNames(wrapper);
-    const wrapperInit = map(wrapperKeys, name => `this["${name}"]=${name}`).join(';');
+    const argNames = Object.getOwnPropertyNames(wrapper);
+    const wrapperInit = map(argNames, name => `this["${name}"]=${name}`).join(';');
     const codeSlices = [`${wrapperInit};with(this)!function(){`];
     const codeSlices = [`${wrapperInit};with(this)!function(){`];
     forEach(requireKeys, key => {
     forEach(requireKeys, key => {
       const requireCode = data.require[key];
       const requireCode = data.require[key];
@@ -121,11 +113,15 @@ function onLoadScripts(data) {
     codeSlices.push('}.call(this);');
     codeSlices.push('}.call(this);');
     const codeConcat = codeSlices.join('\n');
     const codeConcat = codeSlices.join('\n');
     const name = script.custom.name || script.meta.name || script.props.id;
     const name = script.custom.name || script.meta.name || script.props.id;
-    const args = map(wrapperKeys, key => wrapper[key]);
+    const args = map(argNames, key => wrapper[key]);
     const thisObj = wrapper.window || wrapper;
     const thisObj = wrapper.window || wrapper;
-    const id = getUniqId();
-    store.ainject[id] = [name, args, thisObj];
-    bridge.post({ cmd: 'Inject', data: [id, wrapperKeys, codeConcat] });
+    const id = `VMin${getUniqId()}`;
+    const callbackId = `VMcb${getUniqId()}`;
+    attachFunction(callbackId, () => {
+      const func = window[id];
+      if (func) runCode(name, func, args, thisObj);
+    });
+    bridge.post({ cmd: 'Inject', data: [id, argNames, codeConcat, callbackId] });
   }
   }
   function run(list) {
   function run(list) {
     while (list.length) buildCode(list.shift());
     while (list.length) buildCode(list.shift());