Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
255 views
in Technique[技术] by (71.8m points)

javascript - nsIProtocol Example Unclear

I was comparing this example on MDN: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIProtocolHandler#Implementation

To this add-on on how to create custom protocol: https://addons.mozilla.org/en-US/firefox/files/browse/141969/file/components/AboutFosdem.js#top

Can someone please detail what exactly it's trying to do. The SMTP stuff threw me off.

I couldn't understand what that example on MDN is doing, its doing it whatever its doing without a chrome.manifst. I know the add-on is creating "fosdem://blah" where blah is whatever I want based on the definitions in WhereToGo but it uses a chrome.manifest.

I'm thinking that the mdn example is doing same thing as addon and I would do something like this to set my custom protocol after copy pasting that mdn code:

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);
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Alrighty lets give a more sensible example of a custom protocol handler.

I decided to implement a ddg: protocol handler, that once registered can be used to type ddg:some search terms into the address bar (among other things) and it will load the DuckDuckGo search page for "some search terms".

The component

One needs to implement the nsIProtocolHandler interface.

What this example component does is "redirect" to DuckDuckGo (well, not really redirect, but it returns a channel for duckduckgo.com). See comments inline.

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;
  }
});

Component registration in chrome.manifest

We need to implement NSGetFactory if our component is a JavaScript component registered via chrome.manifest. Luckily, XPCOMUtils.jsm has a helper for that.

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

Registration in bootstrapped add-ons (and Scratchpad)

In bootstrapped/restartless add-ons (incl. SDK add-ons) and Scratchpad, one will need to register the component manually, as chrome.manifest registration is not available.

One could register the result of NSGetFactory(classID), but here is some code creating a Factory manually and registering it.

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);

Please note that in restartless add-ons you'll also need to unregister it again on shutdown!

factory.unregister();

Testing in a Scratchpad

Copy the component code and the manual registration code into a scratchpad, set the Enviroment to Browser, and run it. Then open ddg:some search terms in a tab ;)

It works!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...