Index: lib/command_line.js |
=================================================================== |
new file mode 100644 |
--- /dev/null |
+++ b/lib/command_line.js |
@@ -0,0 +1,134 @@ |
+/* |
+ * A bootstrapped extension must load its own command-line handler, because it won't load from chrome.manifest. See |
+ * https://developer.mozilla.org/en-US/docs/Chrome_Registration and its section "Instructions supported in bootstrapped |
+ * add-ons". This is only barely mentioned in |
+ * https://developer.mozilla.org/en-US/docs/Extensions/Bootstrapped_extensions, and only in passing at that. The upshot |
+ * is that you have to replicate what would otherwise happen in chrome.manifest. |
+ * |
+ * A command line handler must implement the interface nsICommandLineHandler and, in order to hook into the command line |
+ * system, must register itself in the category "command-line-handler". See |
+ * https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsICommandLineHandler |
+ * The value of the category entry is a service contract ID, which is used to construct an instance of the handler. |
+ * |
+ * The way to construct an instance given a contract ID is to implement nsIFactory and, in order that hook into the |
+ * instantiation system, must register itself with the component registrar. The component registrar is also the |
+ * component manager, but through the interface nsIComponentRegistrar, which is not the default interface. |
+ */ |
+ |
+Cu.import( "resource://gre/modules/XPCOMUtils.jsm" ); |
+ |
+let { Bootstrap_XPCOM } = require( "bootstrap_xpcom" ); |
+ |
+//----------------------------------------------------------------------------------------- |
+// Command_Line |
+//----------------------------------------------------------------------------------------- |
+ |
+/** |
+ * The command line handler singleton. |
+ * |
+ * This object supplies its own factory to XPCOM, so there's no need for a constructor function; instead, the factory |
+ * function simply returns 'this' (essentially, though it passes it through QueryInterface first). |
+ */ |
+var Command_Line = new Bootstrap_XPCOM.Singleton_class( |
+ "ABP Crawler - Command Line Handler", |
+ Components.ID( "{771575E6-62FE-48CB-BC24-EAEFDDC1CA1D}" ), |
+ "@adblockplus.org/abpcrawler/command-line;1", |
+ [ Ci.nsICommandLineHandler ], |
+ [ |
+ { |
+ category: "command-line-handler", |
+ // The entry starts with "k" so that it has slightly higher priority than ordinary command line handlers. |
+ entry: "k-abpcrawler" |
+ } |
+ ] ); |
+ |
+Command_Line.helpInfo = "" + |
+ // - - - - - - - - - - - - - - | wrap here |
+ "AdBlock Plus Crawler\n" + |
+ " -abpcrawler Start a crawl. Must specify both an input and\n" + |
+ " an output.\n" + |
+ " -input_file <path> Use <path> as input file from which to compile\n" + |
+ " instructions for the crawl.\n" + |
+ " -output_dir <path> Use <path> as output directory to contain a\n" + |
+ " file of crawl results.\n" + |
+ " -output_base <name> Use <name> as the base name for a file of crawl\n" + |
+ " results. Optional. Default='crawl_results'.\n" + |
+ " -max_tabs <N> Maximum number of tabs for simultananeous\n" + |
+ " loading of target sites.\n"; |
+ |
+/** |
+ * Set a startup hook to run |
+ * @param f |
+ */ |
+Command_Line.set_startup_hook = function( f ) |
+{ |
+ this._startup_hook = f; |
+}; |
+ |
+/** |
+ * The actual handler. |
+ * |
+ * @param {nsICommandLine} ff_command_line |
+ * The Firefox command line |
+ */ |
+Command_Line.handle = function( ff_command_line ) |
+{ |
+ dump( "Command line handler: abpcrawler\n" ); |
+ |
+ if ( !ff_command_line.handleFlag( "abpcrawler", false ) ) |
+ { |
+ /* |
+ * There's no '--abpcrawler' option on the command line. As a result we don't try to interpret any other |
+ * command line flags. |
+ */ |
+ return; |
+ } |
+ var flags = this.flags = { abpcrawler: true }; |
+ /* |
+ * The '--abpcrawler' argument indicates that this invocation is designated as a crawl session. As a result, |
+ * we don't perform the default action, which is opening the start page in the browser for interactive use. |
+ */ |
+ ff_command_line.preventDefault = true; |
+ |
+ let x = ff_command_line.handleFlagWithParam( "input_file", false ); |
+ if ( x ) flags.input_file = x; |
+ x = ff_command_line.handleFlagWithParam( "output_dir", false ); |
+ if ( x ) flags.output_dir = x; |
+ x = ff_command_line.handleFlagWithParam( "output_base", false ); |
+ if ( x ) flags.output_base = x; |
+ x = ff_command_line.handleFlagWithParam( "max_tabs", false ); |
+ if ( x ) flags.max_tabs = x; |
+ |
+ /* |
+ * Some experimentation revealed that this command line handler runs _after_ observer notifications for topic |
+ * 'final-ui-startup'. (This seems like a design failure, since it ought to happen after the add-on has an opportunity |
+ * to initialize itself at 'profile-after-change' but before anything else. Whatever.) As a result, we call a startup |
+ * hook here. |
+ */ |
+ if ( this._startup_hook ) |
+ { |
+ try |
+ { |
+ this._startup_hook(); |
+ } |
+ catch ( e ) |
+ { |
+ dump( "Command_Line/handle/startup hook: Unexpected exception" |
+ + (("message" in e) ? ": " + e.message : "") + ".\n" ); |
+ } |
+ } |
+}; |
+ |
+exports.Command_Line = Command_Line; |
+try |
+{ |
+ /* |
+ * We must call init() after handle() is defined, so that if the category manager triggers an immediate event |
+ * that we have already initialized fully. |
+ */ |
+ Command_Line.init(); |
+} |
+catch ( e ) |
+{ |
+ dump( "command_line.js: Unexpected exception during init(): " + e.message ); |
+} |