【问题标题】:nsIProtocol Example UnclearnsIProtocol 示例不清楚
【发布时间】:2014-07-24 05:13:03
【问题描述】:

我在 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 示例与插件做同样的事情,我会在复制粘贴该 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);

【问题讨论】:

  • @noitidart 将内容复制到 wiki 中,没有任何解释。 :p
  • 谁是 noitidart? :P 我想我在阅读更多内容时会弄清楚这一点:P 但他们没有展示如何正确卸载它。
  • JFYI,我恢复了 MDN 编辑。 “添加一个几乎没有用的示例,它根本没有解释任何内容,并且使用仅存在于 comm-central 中的接口并没有真正的帮助。”。虽然改进 wiki 无疑是一件值得欢迎的好事,但编辑实际上应该是改进,而不仅仅是增加混乱。
  • 啊,我知道我刚刚检查了那个 MDN 页面的历史记录,添加那个令人困惑的例子的人是 noit

标签: javascript firefox-addon


【解决方案1】:

好吧,让我们举一个更合理的自定义协议处理程序示例。

我决定实现一个ddg: 协议处理程序,一旦注册,就可以在地址栏中输入ddg:some search terms(除其他外),它会加载 DuckDuckGo 搜索页面以查找“一些搜索词”。

组件

需要实现nsIProtocolHandler接口。

这个示例组件所做的是将“重定向”到 DuckDuckGo(好吧,不是真正的重定向,但它返回了一个用于 duckduckgo.com 的频道)。见内联 cmets。

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 中测试

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

【讨论】:

  • 甚至比我修改后的示例更清晰。我依赖unloaders 数组。但是您在此不支持port。我不确定它的作用,但 moz comm central 有它。
  • 您的代码中究竟是什么阻止了 URI 更改为 https://duckduckgo.com/?q= 加上搜索词。我的代码将 url 更改为 twitter.com/ 加上搜索词。 :( 我以为我设置了channel.originalURI ,但仍然是twitter uri :(
  • Twitter 可能会在某个时候进行常规的 http 重定向或元刷新。
  • 你是对的。我应该实现剩余的nsIProtocolHandler 属性和方法,我因为懒惰而省略了。 :p
  • 哦,我什至不确定我是否得到了所有这些,我只是试图理解我之前发布的内容,他们有这个allowPort 的东西哈哈:我注意到插件内存有这个getURIFlags 我们需要那个吗?实际上现在你提到了剩下的 nsIProtocolHandler,我会去查找那个接口,看看它是否有它。哦,回答了:nsIAboutModule 组件说明了我们为什么需要它。
【解决方案2】:

哎呀,你打败了我。

我完全理解从 MDN 中删除的情况。

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

顺便说一句,我从 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

【讨论】:

  • :D :D 它是 blagoh 使用的,所以我只是在迎合他 :P 我为让这个家伙感到困惑而感到难过 :P
猜你喜欢
  • 1970-01-01
  • 2019-10-30
  • 2017-02-20
  • 1970-01-01
  • 2018-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-28
相关资源
最近更新 更多