Browse Source

Improve wrapper, fix #30

Wrap methods of `window` and bind them to `window`.
Wrap properties of `window` and make them read only.
Gerald 10 years ago
parent
commit
5a36db7d5f
3 changed files with 101 additions and 81 deletions
  1. 8 2
      src/background/requests.js
  2. 2 2
      src/background/utils.js
  3. 91 77
      src/injected.js

+ 8 - 2
src/background/requests.js

@@ -152,9 +152,15 @@ var requests = function () {
   chrome.webRequest.onBeforeRequest.addListener(function (req) {
     // onBeforeRequest is fired for local files too
     if (/\.user\.js([\?#]|$)/.test(req.url)) {
+      var noredirect = {redirectUrl: 'javascript:history.back()'};
       var x = new XMLHttpRequest();
       x.open('GET', req.url, false);
-      x.send();
+      try {
+        x.send();
+      } catch (e) {
+        console.log(e);
+        return noredirect;
+      }
       if ((!x.status || x.status == 200) && !/^\s*</.test(x.responseText)) {
         if (req.tabId < 0)
           chrome.tabs.create({
@@ -166,7 +172,7 @@ var requests = function () {
               url: chrome.extension.getURL('/options/index.html') + '#confirm/' + encodeURIComponent(req.url) + '/' + encodeURIComponent(t.url),
             });
           });
-        return {redirectUrl: 'javascript:history.back()'};
+        return noredirect;
       }
     }
   }, {

+ 2 - 2
src/background/utils.js

@@ -1,6 +1,6 @@
 var scriptUtils = {
   isRemote: function (url) {
-    return url && !/^(file|data):/.test(url);
+    return url && !(/^(file|data):/.test(url));
   },
   fetch: function (url, type, headers) {
     var xhr = new XMLHttpRequest;
@@ -61,7 +61,7 @@ var scriptUtils = {
       custom: {},
       enabled: 1,
       update: 1,
-      code: '// ==UserScript==\n// @name New Script\n// ==/UserScript==\n',
+      code: '// ==UserScript==\n// @name New Script\n// @namespace Violentmonkey Scripts\n// @include *\n// @grant none\n// ==/UserScript==\n',
     };
     script.meta = scriptUtils.parseMeta(script.code);
     return script;

+ 91 - 77
src/injected.js

@@ -14,6 +14,18 @@ var _ = {
       });
     });
   },
+
+  includes: function (arr, item) {
+    var length = arr.length;
+    for (var i = 0; i < length; i ++)
+      if (arr[i] === item) return true;
+    return false;
+  },
+  forEach: function (arr, func, context) {
+    var length = arr.length;
+    for (var i = 0; i < length; i ++)
+      if (func.call(context, arr[i], i, arr) === false) break;
+  },
 };
 
 /**
@@ -70,6 +82,75 @@ function setBadge(){
 }
 
 // Communicator
+function getWrapper() {
+  // http://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects
+  // http://developer.mozilla.org/docs/Web/API/Window
+  var comm = this;
+  var wrapper = {};
+  // Wrap methods
+  comm.forEach([
+    'eval',
+    // 'uneval',
+    'isFinite',
+    'isNaN',
+    'parseFloat',
+    'parseInt',
+    'decodeURI',
+    'decodeURIComponent',
+    'encodeURI',
+    'encodeURIComponent',
+
+    'addEventListener',
+    'alert',
+    'atob',
+    'blur',
+    'btoa',
+    'clearInterval',
+    'clearTimeout',
+    'close',
+    'confirm',
+    'dispatchEvent',
+    'find',
+    'focus',
+    'getComputedStyle',
+    'getSelection',
+    'matchMedia',
+    'moveBy',
+    'moveTo',
+    'open',
+    'openDialog',
+    'postMessage',
+    'print',
+    'prompt',
+    'removeEventListener',
+    'resizeBy',
+    'resizeTo',
+    'scroll',
+    'scrollBy',
+    'scrollByLines',
+    'scrollByPages',
+    'scrollTo',
+    'setInterval',
+    'setTimeout',
+    'stop',
+  ], function (name) {
+    var method = window[name];
+    wrapper[name] = function () {
+      return method.apply(window, arguments);
+    };
+  });
+  // Wrap properties
+  Object.getOwnPropertyNames(window).forEach(function (name) {
+    if (typeof window[name] === 'function') return;
+    Object.defineProperty(wrapper, name, {
+      get: function () {
+        var value = window[name];
+        return value === window ? wrapper : value;
+      },
+    });
+  });
+  return wrapper;
+}
 var comm = {
   vmid: 'VM_' + _.getUniqId(),
   state: 0,
@@ -79,27 +160,9 @@ var comm = {
   // Array functions
   // to avoid using prototype functions
   // since they may be changed by page scripts
-  inArray: function(arr, item) {
-    for (var i = 0; i < arr.length; i ++)
-      if (arr[i] == item) return true;
-    return false;
-  },
-  extendArray: function(arr, arr2) {
-    for(var i = 0; i < arr2.length; i ++) arr.push(arr2[i]);
-  },
-  forEach: function(arr, func, args) {
-    for(var i = 0; i < arr.length; i ++) {
-      var _args = [arr[i]];
-      if(args) this.extendArray(_args, args);
-      func.apply(arr, _args);
-    }
-  },
+  includes: _.includes,
+  forEach: _.forEach,
 
-  prop1: Object.getOwnPropertyNames(window),
-  prop2: (function(n,p){
-    while(n=Object.getPrototypeOf(n)) p=p.concat(Object.getOwnPropertyNames(n));
-    return p;
-  })(window,[]),
   init: function(srcId, destId) {
     var comm = this;
     comm.sid = comm.vmid + srcId;
@@ -200,7 +263,7 @@ var comm = {
       };
       t.id = id;
       comm.requests[id] = t;
-      if(comm.inArray(['arraybuffer', 'blob'], t.details.responseType))
+      if(comm.includes(['arraybuffer', 'blob'], t.details.responseType))
         data.responseType = 'blob';
       comm.post({cmd: 'HttpRequest', data: data});
     }
@@ -229,55 +292,7 @@ var comm = {
       return t.req;
     };
   },
-  initWrapper: function() {
-    // wrapper: wrap functions as needed, return and set properties
-    function wrapItem(key, thisObj, wrap) {
-      var type;
-      var value;
-      var apply = Function.apply;
-      function initProperty() {
-        if(!comm.inArray(['function', 'custom'], type)) {
-          value = window[key];
-          type = typeof value;
-          if(wrap && type == 'function') {
-            var func = value;
-            value = function () {
-              var r;
-              try {
-                r = apply.apply(func, [window, arguments]);
-              } catch(e) {
-                console.log('[Violentmonkey] Error calling ' + key + ':\n' + e.stack);
-              }
-              return r === window ? thisObj : r;
-            };
-            Object.setPrototypeOf(value, func);
-            value.prototype = func.prototype;
-          }
-        }
-      }
-      try {
-        Object.defineProperty(thisObj, key, {
-          get: function () {
-            initProperty();
-            return value === window ? thisObj : value;
-          },
-          set: function (_value) {
-            initProperty();
-            value = _value;
-            if(type != 'function') window[key] = _value;
-            type = 'custom';
-          },
-        });
-      } catch(e) {
-        // ignore protected data
-      }
-    }
-    var comm = this;
-    comm.Wrapper = function () {
-      comm.forEach(comm.prop1, wrapItem, [this]);
-      comm.forEach(comm.prop2, wrapItem, [this, true]);
-    };
-  },
+  getWrapper: getWrapper,
   wrapGM: function(script, value, cache) {
     // Add GM functions
     // Reference: http://wiki.greasespot.net/Greasemonkey_Manual:API
@@ -285,16 +300,15 @@ var comm = {
     var gm = {};
     var grant = script.meta.grant || [];
     var urls = {};
-    if (!grant.length || grant.length == 1 && grant[0] == 'none')
+    if (!grant.length || grant.length == 1 && grant[0] == 'none') {
       // @grant none
       grant.pop();
-    else {
-      if (!comm.Wrapper) comm.initWrapper();
-      gm['window'] = new comm.Wrapper();
+    } else {
+      gm['window'] = comm.getWrapper();
     }
     value = value || {};
-    if(!comm.inArray(grant, 'unsafeWindow')) grant.push('unsafeWindow');
-    if(!comm.inArray(grant, 'GM_info')) grant.push('GM_info');
+    if(!comm.includes(grant, 'unsafeWindow')) grant.push('unsafeWindow');
+    if(!comm.includes(grant, 'GM_info')) grant.push('GM_info');
     function propertyToString() {
       return '[Violentmonkey property]';
     }
@@ -529,7 +543,7 @@ var comm = {
       }, 0);
     };
     comm.checkLoad = function() {
-      if (!comm.state && comm.inArray(['interactive', 'complete'], document.readyState))
+      if (!comm.state && comm.includes(['interactive', 'complete'], document.readyState))
         comm.state = 1;
       if (comm.state) comm.load();
     };