How to override JavaScript function from a Firefox

2019-05-04 09:14发布

问题:

I am trying to intercept calls to document.write for all pages. Setting up the interception inside the page by injecting a script like

function overrideDocWrite() {
 alert("Override called");
 document.write = function(w) {
  return function(s) {
   alert("special dom");
   w.call(this, wrapString(s));
  };
 }(document.write);
 alert("Override finished");
}

Is easy and works, but I would like my extension to setup the interception for each document object from inside the extension. I couldn't find a way to do this. I tried to listen for the "load" event and set up the interception there but it also fails. How do I hook calls to doc.write from an extension?

I made some progress:

var myExtension = {
  init: function() {
    var appcontent = document.getElementById("appcontent"); // browser
    if (appcontent)
      appcontent.addEventListener("DOMContentLoaded", myExtension.onPageLoad,
        true);
  },

  onPageLoad: function(aEvent) {
    var doc = aEvent.originalTarget; // doc is document that triggered "onload" event
    // do something with the loaded page.
    // doc.location is a Location object (see below for a link).
    // You can use it to make your code executed on certain pages only.
    alert("Override called");
    alert(doc);
    alert(doc.write);
    alert(doc.wrappedJSObject);
    alert(doc.wrappedJSObject.write);
    doc.wrappedJSObject.write = function(w) {
      return function(s) {
        alert("special dom");
        w.call(this, "(" + s + ")");
      };
    }(doc.write);
    alert("Override finished");
  }
}

This seem to work, but DOMContentLoaded is the wrong event for the job, because it is fired too late! Is there an earlier event to listen to?

回答1:

Ressurection of the question ! I got the answer. Here is a sample code :

const os    = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);

os.addObserver({
    observe : function(aWindow, aTopic, aData) {
        if (aWindow instanceof Ci.nsIDOMWindow && aTopic == 'content-document-global-created') {
            aWindow.wrappedJSObject.myfunction = function(){
                // Do some stuff . . .
            }
        }
    }
}, 'content-document-global-created', false);

The same goes for document with the event document-element-inserted as of gecko 2.0 .



回答2:

JavaScript uses a prototypical inheritance system, instead of having classes, objects have prototypes. Prototypes are real objects that are used as a reference to other objects for inheritance of methods and attributes.

The best strategy would be to override the method write in the prototype of "document" (which for the HTML document is HTMLDocument). This should effectively wrap the method for all instances of "document" inside the pages loaded in the browser since they all use the same prototype.

Instead of

document.write = function() { ... }

try something like this:

HTMLDocument.prototype.write= function() { ... }

UPDATE: It does not seem to be as easy as I initially thought, this does not seem to work at first try.