Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement XPCOM component (nsIContentPolicy) in bootstrapped Firefox extension

I have a bootstrapped extension for Firefox. And now I want to implement nsIContentPolicy XPCOM component. I wrote a component module code. And now I want to register this component. The reason I want to register component is that I want to add my component to nsICategoryManager.addCategoryEntry with "content-policy" category.

var {Cc, Ci, Cu} = require("chrome");

Cu.import("resource://gre/modules/XPCOMUtils.jsm");

//console.error("Running interceptor");

function Interceptor() 
}

Interceptor.prototype = {

    classDescription: "DeferredTo HTTP requests Interceptor",
    classID: "{B5B3D9A0-08FC-11E3-8253-5EF06188709B}",
    contractID: "@deferredto.com/Interceptor;1",
    QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPolicy]),

    shouldLoad : function dt_shouldLoad(aContentType, aContentLocation, aRequestOrigin, aContext, aMimeTypeGuess, aExtra) {
        console.log("dt_shouldLoad");

        if (contentLocation.scheme != "http" && contentLocation.scheme != "https")
            return Ci.nsIContentPolicy.ACCEPT;

        let result = Ci.nsIContentPolicy.ACCEPT;

        // we should check for TYPE_SUBDOCUMENT as well if we want frames.
        if ((Ci.nsIContentPolicy.TYPE_DOCUMENT == aContentType) &&
            SOME_REGULAR_EXPRESSION.test(aContentLocation.spec)) {
            // do stuff here, possibly changing result.
        }
        return result;
    },

    shouldProcess: function ILO_shouldProcess() Ci.nsIContentPolicy.ACCEPT,

    _xpcom_categories: [
        { category: "content-policy", service: true }
    ],
    classInfo: XPCOMUtils.generateCI(
    {classID: Components.ID("{B5B3D9A0-08FC-11E3-8253-5EF06188709B}"),
     contractID: "@deferredto.com/Interceptor;1",
     classDescription: "Interceptor implements nsIContentPolicy to block images that are not yet at screen @DeferredTo",
     interfaces: [
                  Ci.nsIContentPolicy,
                  ],
     flags: Ci.nsIClassInfo.SINGLETON})
}

var components = [Interceptor];

var NSGetFactory = XPCOMUtils.generateNSGetFactory([Interceptor]);

Questions:

  • Is it possible to register the component from bootstrapped extension?
  • Is it possible to register the component from restartless extension?
  • Is it possible to use nsICategoryManager.addCategoryEntry "content-policy" without component?
  • How to register the component in bootstrapped extension or somehow add new "content-policy" category entry?

I've added to harness-options.js

"requirements": {
"sdk/page-mod": "sdk/page-mod",
"sdk/self": "sdk/self",
"chrome": "chrome"},

That is how I try to import module:

var {Cc, Ci, Cu} = require("chrome");
Cu.import("resource://deferredto/lib/interceptor.js");

I' ve tried many paths ))) But none works. resource entry in chrome.manifest file does not allowed for bootstrapped extensions. The path to component module file is: resources/deferredto/lib/interceptor.js

like image 556
Dmitry Kaigorodov Avatar asked Aug 21 '13 10:08

Dmitry Kaigorodov


2 Answers

Adblock Plus, which is restartless but not using the SDK, registers an nsIContentPolicy implementation at runtime, just like your SDK would. There are probably a few SDK add-ons registering components at run time, but I don't know any open source ones that I would recommend to look at off the top of my head.

A few points regarding that ABP implementation and what to change to make it work with the SDK:

  • The category manager is available via Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager).
  • The component registrar should be available by requiring also components from the chrome module and then components.manager.getService(Ci.nsIComponentRegistrar).
  • As Adblock Plus, you must unregister your component yourself on unload.
  • The unload part is unfortunely also a bit tricking, since you cannot synchronously unregister your component and the category entry, due to Bug 753687. Adblock Plus therefore does it async using Util.runAsync, which just dispatches a runnable (event, if you like) to the main thread. I don't think you can use any SDK stuff here, as the SDK will clean up before any async code gets a chance to run, so you'd need to use a low-level XPCOM runnable (or timer) yourself.
  • Your code will register your component at runtime. You won't touch harness-options or anything like that.

(I also implemented a generic component register function myself, but that again is not SDK code, and would need to be adapted to run in the SDK, just like the ABP one. It is also very similar to the ABP one.)

like image 190
nmaier Avatar answered Oct 21 '22 18:10

nmaier


Now my nsIContentPolicy sdk-based component look like this. File interceptor.js:

'use strict';

var { Class } = require('sdk/core/heritage');
var xpcom = require('sdk/platform/xpcom');
var { Cc, Ci, Cu, Cm } = require('chrome');
var categoryManager = Cc["@mozilla.org/categorymanager;1"]
                      .getService(Ci.nsICategoryManager);


// nsIDOMNode
const TYPE_DOCUMENT_NODE        = Ci.nsIDOMNode.DOCUMENT_NODE;


/// Interceptor


var contractId = "@deferredto.com/Interceptor;1";

var Interceptor = Class({
  extends:  xpcom.Unknown,
  interfaces: [ 'nsIContentPolicy' ],
  get wrappedJSObject() this,

  shouldLoad : function dt_shouldLoad(contentType, contentLocation, requestOrigin, context, mimeTypeGuess, extra) {

        let result = Ci.nsIContentPolicy.ACCEPT;

        return result;
    },

    shouldProcess: function () Ci.nsIContentPolicy.ACCEPT
});

var factory = xpcom.Factory({
  contract: contractId,
  Component: Interceptor,
  unregister: false // see https://bugzilla.mozilla.org/show_bug.cgi?id=753687
});

/// unload 
var unload = require("sdk/system/unload");

unload.when(function() {
  function trueUnregister() {
    categoryManager.deleteCategoryEntry("content-policy", contractId, false);
    try {
      console.log("xpcom.isRegistered(factory)="  + xpcom.isRegistered(factory));
      console.log("trueUnregister");
      xpcom.unregister(factory);
      console.log("xpcom.isRegistered(factory)="  + xpcom.isRegistered(factory));
    } catch (ex) {
        Cu.reportError(ex);
    }      
  }
  if ("dispatch" in Cu) {
    console.log('"dispatch" in Cu');
    Cu.dispatch(trueUnregister, trueUnregister);
  } else {
    console.log('"dispatch" not! in Cu');
    Cu.import("resource://gre/modules/Services.jsm");
    Services.tm.mainThread.dispatch(trueUnregister, 0);
  }
});


//xpcom.register(factory);

var interceptor = Cc[contractId].createInstance(Ci.nsIContentPolicy);

categoryManager.deleteCategoryEntry("content-policy", contractId, false);
categoryManager.addCategoryEntry("content-policy", contractId, contractId, false, true);

And you can use it from sdk like this:

var interceptor = require("./interceptor");
like image 22
Dmitry Kaigorodov Avatar answered Oct 21 '22 16:10

Dmitry Kaigorodov