nsIProtocol示例不清楚

时间:2014-07-23 17:49:57

标签: javascript firefox-addon

我在MDN上比较这个例子: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIProtocolHandler#Implementation

关于如何创建自定义协议的附加组件: https://addons.mozilla.org/en-US/firefox/files/browse/141969/file/components/AboutFosdem.js#top

有人可以详细说明它正在尝试做什么。 SMTP的东西让我失望。

我无法理解MDN上的那个例子正在做什么,无论它在没有chrome.manifst的情况下做了什么。我知道附加组件正在创建“fosdem:// blah”,其中blah是我想要的基于WhereToGo中的定义,但它使用chrome.manifest。

我认为mdn示例与addon做同样的事情,我会做这样的事情来设置我的自定义协议后复制粘贴mdn代码:

function myCustomBlah() {}

myCustomBlah.prototype =
  makeProtocolHandler("mycustomblah",
                      -1,
                      "b14c2b67-8680-4c11-8d63-9403c7d4f757"); //i can generate any id

var components = [myCustomBlah];
const NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

2 个答案:

答案 0 :(得分:4)

Alrighty让我们给出一个更合理的自定义协议处理程序示例。

我决定实现一个ddg:协议处理程序,一旦注册就可以用来在地址栏中输入ddg:some search terms(以及其他内容),它会加载DuckDuckGo搜索页面以进行“一些搜索”术语”。

组件

需要实现nsIProtocolHandler接口。

这个示例组件的作用是“重定向”到DuckDuckGo(好吧,不是真正重定向,但它返回duckduckgo.com的频道)。请参阅内联评论。

var {classes: Cc,
     interfaces: Ci,
     manager: Cm,
     results: Cr,
     Constructor: CC
    } = Components;
Cm.QueryInterface(Ci.nsIComponentRegistrar);

Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");

const SCHEME = "ddg";
const DDG_URI = Services.io.newURI("https://duckduckgo.com/?q=%s", null, null);

const nsIURI = CC("@mozilla.org/network/simple-uri;1", "nsIURI");

function DuckDuckGoProtocolHandler() {}
DuckDuckGoProtocolHandler.prototype = Object.freeze({
  classDescription: "DuckDuckGo Protocol Handler",
  contractID: "@mozilla.org/network/protocol;1?name=" + SCHEME,
  classID: Components.ID('{858ea860-129a-11e4-9191-0800200c9a66}'),
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler]),

  // nsIProtocolHandler
  scheme: SCHEME,
  defaultPort: -1, // No default port.

  // nsIProtocolHandler
  allowPort: function(port, scheme) {
    // This protocol handler does not support ports.
    return false;
  },

  // nsIProtocolHandler
  // Our protocol handler does not support authentication,
  // but it is OK to be loaded from any web-page, not just privileged pages""
  protocolFlags: Ci.nsIProtocolHandler.URI_NOAUTH |
                 Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE,

  // nsIProtocolHandler
  newURI: function(aSpec, aOriginCharset, aBaseURI) {
    // Nothing special here, actually. We were asked to create a new URI.

    // If there is a base-URI, this means that the browser tries to resolve
    // a dependent resource (an image, script) or the user clicked on a relative link.
    // In this case we cannot really return another "ddg" URI, but need to return
    // the proper https URI.
    if (aBaseURI && aBaseURI.scheme == SCHEME) {
      return Services.io.newURI(aSpec, aOriginCharset, DDG_URI);
    }

    // We don't care about the charset, so just ignore that
    // (we support what nsIURI supports).
    let rv = new nsIURI();
    rv.spec = aSpec;
    return rv;
  },

  // nsIProtocolHandler
  newChannel: function(aURI) {
    // We were asked to open a new channel.
    // We could implement an entirely custom channel that supports
    // (most of) nsIChannel. But that is tremendous work and outside
    // of the scope of this basic example (which is about protocol handlers and
    // not channels).
    // Or we can just return any other channel we can create.
    // Since we're going to implement the "ddg:" protocol, lets just open a
    // regular https channel to duckduckgo.com, use the URI as the search term
    // and return that channel.
    let spec = DDG_URI.spec.replace("%s", aURI.path);
    let channel = Services.io.newChannel(spec, aURI.originCharset, null);

    // Setting .originalURI will not only let other code know where this
    // originally came from, but the UI will actually show that .originalURI.
    channel.originalURI = aURI;

    return channel;
  }
});

chrome.manifest

中的组件注册

如果我们的组件是JavaScript组件,我们需要实现NSGetFactory 通过chrome.manifest注册。幸运的是,XPCOMUtils.jsm有一个帮手。

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

在自举附加组件(和Scratchpad)中注册

在bootstrapped / restartless附加组件(包括SDK附加组件)和Scratchpad中,由于chrome.manifest注册不可用,因此需要手动注册组件。

可以注册NSGetFactory(classID)的结果,但这里有一些代码手动创建工厂并注册它。

function Factory(component) {
  this.createInstance = function(outer, iid) {
    if (outer) {
      throw Cr.NS_ERROR_NO_AGGREGATION;
    }
    return new component();
  };
  this.register = function() {
    Cm.registerFactory(component.prototype.classID,
                       component.prototype.classDescription,
                       component.prototype.contractID,
                       this);
  };
  this.unregister = function() {
    Cm.unregisterFactory(component.prototype.classID, this);
  }
    Object.freeze(this);
  this.register();
}
var factory = new Factory(DuckDuckGoProtocolHandler);

请注意,在无重启的附加组件中,您还需要取消注册 再次关机!

factory.unregister();

在Scratchpad中进行测试

将组件代码和手动注册代码复制到暂存器中,将环境设置为浏览器,然后运行它。然后在标签中打开ddg:some search terms;)

It works!

答案 1 :(得分:1)

你真打败了我。

我完全理解从MDN中删除。

我写了这个例子,它有一个问题,它改变了URL。我会阅读你的解决方案并与我的相比。

btw我从about:addons-memory插件中解除了这个。在此示例中,如果您输入somecustomblah:noitidart。它会带你去twitter.com/noitidart。问题是,网址从somecustomblah:noitidart更改为twitter.com/Noitidart/,我尝试修复,我认为您的解决方案可能有@nmaier的答案。这种方式不需要chrome.manifest,您也可以使用reigsterComponents进行注册,并使用unregisterComponents取消注册。

确保根据您的协议生成自己的UUID:http://www.famkruithof.net/uuid/uuidgen

aDefaultPort的第三个参数允许您执行moz central正在执行的SMTP操作。您可以省略它,或者如果您不希望在使用端口时执行SMTP操作,则将其设置为null或undefined。

var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr, manager: Cm} = Components; //can make this const if not in scratchpad
Cm.QueryInterface(Ci.nsIComponentRegistrar);
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/XPCOMUtils.jsm');

var nsIProtocolHandler = Ci.nsIProtocolHandler; //const

var unloaders = [];

function makeProtocolHandler(aProtocol, aClassID, aDefaultPort) {
    var obj = {
        classID: Components.ID(aClassID),
        classDescription: 'blah blah blah',
        contractID: '@mozilla.org/network/protocol;1?name=' + aProtocol,
        QueryInterface: XPCOMUtils.generateQI([nsIProtocolHandler]),
        scheme: aProtocol,
        defaultPort: aDefaultPort,
        protocolFlags: nsIProtocolHandler.URI_NORELATIVE | nsIProtocolHandler.URI_NOAUTH | nsIProtocolHandler.URI_LOADABLE_BY_ANYONE, //You must specify either URI_LOADABLE_BY_ANYONE, URI_DANGEROUS_TO_LOAD, URI_IS_UI_RESOURCE, or URI_IS_LOCAL_FILE in order for your protocol to work.
        newURI: function(aSpec, aOriginCharset, aBaseURI) {
            var uri = Cc['@mozilla.org/network/simple-uri;1'].createInstance(Ci.nsIURI);
            uri.spec = aSpec;
            console.log('uri=', uri);
            return uri;
        },

        newChannel: function(aURI) {
            //throw Cr.NS_ERROR_NOT_IMPLEMENTED;
            /* Get twitterName from URL */
            var postProtocolPath = aURI.spec.split(":")[1];
            var uri = Services.io.newURI("http://twitter.com/" + postProtocolPath, null, null);
            var channel = Services.io.newChannelFromURI(uri); //, null).QueryInterface(Ci.nsIHttpChannel); //i dont think i need to QI nsIHttpChannel
            /* Determines whether the URL bar changes to the URL */
            //channel.setRequestHeader("X-Moz-Is-Feed", "1", false);
            channel.originalURI = aURI;
            return channel;
        },
        getURIFlags: function(aURI) 0 // i dont think i need this? do I?
    };

    if (aDefaultPort == undefined || aDefaultPort == null) {
        aDefaultPort = -1;
    } else {
        obj.allowPort = function(port, scheme) {
            return port == aDefaultPort;
        };
    }

    return obj;
}

var myComponents = [myCustomBlah];

function myCustomBlah() {}
myCustomBlah.prototype = makeProtocolHandler('mycustomblah', 'b14c2b67-8680-4c11-8d63-9403c7d4f757'); //this uuid (2nd argument) should be generated by you from here: http://www.famkruithof.net/uuid/uuidgen

//const NSGetFactory = XPCOMUtils.generateNSGetFactory(components);


function registerComponents() {
    for (let [y, cls] in Iterator(myComponents)) {
        console.info('y: ', y, 'cls: ', cls);
        try {
            var factory = {
                _cls: cls,
                createInstance: function(outer, iid) {
                    if (outer) {
                        throw Cr.NS_ERROR_NO_AGGREGATION;
                    }
                    return new cls();
                }
            };
            Cm.registerFactory(cls.prototype.classID, cls.prototype.classDescription, cls.prototype.contractID, factory);
            unloaders.push(function() {
                Cm.unregisterFactory(factory._cls.prototype.classID, factory);
            });
        } catch (ex) {
            console.warn('failed to register module: ', cls.name, 'exception thrown: ', ex);
        }
    }
}

function unregisterComponents() {
    for (var i = 0; i < unloaders.length; i++) {
        unloaders[i]();
    }
}

registerComponents(); //run this to make it work. once this is run follwoing examples above: typing "about:yabba" will take you to bings homepage
//unregisterComponents(); //do this to remove it //after running this typing about:yabba will take you to problem loading page