Is there any way to implement nsICommandLineHandler in a restartless add-on? - command-line

Is there any way to implement nsICommandLineHandler in a restartless add-on?
It seems possible from https://addons.mozilla.org/en-US/developers/docs/sdk/latest/modules/sdk/platform/xpcom.html , but this code (run from within exports.main) is not working for me:
var { Class } = require('sdk/core/heritage');
var { Unknown, Factory } = require('sdk/platform/xpcom');
var { Cc, Ci } = require('chrome');
var contractId = '#mozilla.org/commandlinehandler/general-startup;1?type=webappfind';
// Define a component
var CommandLineHandler = Class({
extends: Unknown,
get wrappedJSObject() this,
classDescription: "webAppFinder",
/* Not used by SDK, so commenting out
_xpcom_categories: [{
category: "command-line-handler",
// category names are sorted alphabetically. Typical command-line handlers use a
// category that begins with the letter "m".
entry: "m-webappfind"
}],
*/
helpInfo : " -webappfind Open My Application\n",
// nsICommandLineHandler
handle : function clh_handle(cmdLine) {
try {
console.log('good so far'); // Doesn't actually reach here
var fileStr = cmdLine.handleFlagWithParam("webappfind", false);
if (fileStr) {
console.log('made it');
}
}
catch (e) {
Cu.reportError("incorrect parameter passed to -webappfind on the command line.");
}
if (cmdLine.handleFlag("webappfind", false)) { // no argument
cmdLine.preventDefault = true;
throw 'A valid ID must be provided to webappfind';
}
},
hello: function() {return 'Hello World';}
});
// Create and register the factory
var factory = Factory({
contract: contractId,
// id: '{7f397cba-7a9a-4a05-9ca7-a5b8d7438c6c}', // Despite the docs saying one can add both, this doesn't work
Component: CommandLineHandler
});
I have the following code afterward which works...
// XPCOM clients can retrieve and use this new
// component in the normal way
var wrapper = Cc[contractId].createInstance(Ci.nsISupports);
var helloWorld = wrapper.wrappedJSObject;
console.log(helloWorld.hello());
...but Firefox is not accepting command line args as per this error:
Error: Warning: unrecognized command line flag -webappfind
Source file: resource://app/components/nsBrowserContentHandler.js
Line: 765
UPDATE
I've now taken #nmaier's advice to add categories and therefore added these lines afterward:
var catMan = Cc['#mozilla.org/categorymanager;1'].getService(Ci.nsICategoryManager); //
catMan.addCategoryEntry('command-line-handler', 'm-webappfind' /*contractId*/, contractId, false, true);
But I'm getting these 3 errors when invoking from the command line:
Error: [Exception... "'Failure' when calling method:
[nsIFactory::createInstance]" nsresult: "0x80004005
(NS_ERROR_FAILURE)" location: "native frame :: ::
:: line 0" data: no]
Contract ID
'#mozilla.org/commandlinehandler/general-startup;1?type=webappfind'
was registered as a command line handler for entry 'm-webappfind', but
could not be created.
Error: Warning: unrecognized command line flag -webappfind
Source file: resource://app/components/nsBrowserContentHandler.js
Line: 765

The SDK will not register categories for you.
Some remarks regarding categories can be found here:
https://stackoverflow.com/a/18366485/484441
But still, I'm not sure if bootstrapped extensions are actually started before the initial command line is processed. Trial and error, I guess...
Edit:
Your component does not specify any interfaces, hence it does only support nsISupports.
The SDK module docs state that you should add an interfaces: [ 'nsICommandLineHandler' ] property.

Related

How to see the value of a top level empty getter without running the code in Swift?

public var O_RDONLY: Int32 { get }
When I'm looking at stuff inside Darwin.sys.* or Darwin.POSIX.* for example, a lot of these constants are defined as getters. But how does one see the actual value without evaluating the code?
public var O_RDONLY: Int32 { get }
is what the Swift importer generates from the macro definition
#define O_RDONLY 0x0000 /* open for reading only */
in the <sys/fcntl.h> include file. Although this is a fixed value, known at compile time, the Swift importer does not show the value in the generated Swift interface.
Note also that a macro definition in a C header file may depend on other macros, and on other “variables” such as compiler flags, the processor architecture, etc.
I am not aware of a way to navigate to that C definition from a Swift file, or any other way to show the defined value in a pure Swift project. As a workaround, one can
add a C file to the project,
use the macro in some C function, and
“jump to definition” from there.
I ended up with the following solution:
const fs = require('fs');
const { exec } = require("child_process");
const getterRegEx = /^(.*)public var (.+): (.+) { get }(.*)$/;
const code = String(fs.readFileSync('./generatedSwift.swift'));
const lines = code.split('\n').map((line, i) => {
const arr = getterRegEx.exec(line);
if (arr) {
const [all, prefix, constant, type, suffix] = arr;
return `print("let ${constant}: ${type} = ", ${constant}, separator: "")`;
}
return `print("""\n${line}\n""")`;
});
lines.unshift('import Foundation');
fs.writeFileSync('./regeneratedSwift.swift', lines.join('\n'));
exec('swift ./regeneratedSwift.swift', (err, stdout, stderr) => {
if (err) {
console.error(`exec error: ${err}`);
return;
}
if (stderr) {
console.log(`stderr: ${stderr}`);
return;
}
console.log(`stdout: ${stdout}`);
});
Copy definitions generated by the XCode and save into a file named generatedSwift.swift the run node index.js in the same folder.
The output will contain the Swift code where all
public var Constant: Type { get }
are replaced with
let Constant = Value
and all other lines will remain the same.

Access to the elements of the page in the Page-Worker

I've created a page-worker in the extension
dup = pageWorker.Page({
contentScript: "self.port.on('alert', function(message) {"+
"console.log(message);"+
"document.querySelector('.test-element').title = message;"+
"});",
contentScriptWhen: "ready",
contentURL: "http://example.com/Licznik-beta/addon.html"
});
In "contentScript" I can relate to "document".
But I can not relate to the window, or function, or variable.
console.log(window) in contentScript return "TypeError: cyclic object value timers.js:43".
I do not understand how it works.
Can someone explain to me?
How to change it?
EDIT
I've added a few lines to the test:
self.port.on('addon-licznik', function () {
console.log(document);
console.log(window); // TypeError: cyclic object value timers.js:43
runFromAddon(); // ReferenceError: runFromAddon is not defined timers.js:43
});
Function: runFromAddon(); Of course there is.
Second test:
function funSet (tresc) {
var addonScript = document.querySelector(".addon-script");
if ( addonScript != undefined ) {
document.querySelector('head').removeChild( addonScript );
}
var script = document.createElement("script");
script.className = "addon-script";
script.textContent = tresc;
document.querySelector('head').appendChild(script);
}
function marmo (message) {
console.log(message);
funSet("console.log(window); runFromAddon();");
}
self.port.on('addon-licznik', marmo);
It works well.
Window → http://example.com/Licznik-beta/addon.html
runFromAddon-Log
If you're writing the HTML yourself, then use addon instead of self and attach the script to the page using <script></script> instead of contentScript(File). See Scripting trusted page content.
If you're not writing the HTML, then see Communicating with Page Scripts.

Calling class function within a constructor isn't being recognised

Answer:
It turns out I had neglected to use the new keyword when creating the class instance. The code in the question itself is fine.
Question:
I have a fairly simple class where the constructor calls another method on the class (editor_for_node). The call happens inside a jQuery each() loop, but I've also tried moving it outside.
define ['jquery'], ($) ->
class Editor
constructor: (#node, #data, #template) ->
#node.widgets().each (i, elem) =>
data = if #data then #data[i] else null
node = $(elem)
#editor_for_node node, data
editor_for_node: (node, data) ->
console.log 'hello!'
return {
'Editor': Editor,
}
When the line #editor_for_node node, data gets called, I get an error (in Firebug) saying this.editor_for_node is not a function.
I really can't see why this isn't working properly, the only possible source of weirdness that I can see is my use of require.js's define function at the start.
Edit: Generated output
(function() {
define(['jquery'], function($) {
var Editor;
Editor = (function() {
Editor.name = 'Editor';
function Editor(node, data, template) {
var _this = this;
this.node = node;
this.data = data;
this.template = template;
this.node.widgets().each(function(i, elem) {
data = _this.data ? _this.data[i] : null;
node = $(elem);
return _this.editor_for_node(node, data);
});
}
Editor.prototype.editor_for_node = function(node, data) {
return console.log('hello!');
};
return Editor;
})();
return {
'Editor': Editor
};
});
}).call(this);
First: Which version of CoffeeScript are you using? The fat arrow has been a source of bugs in certain previous releases.
If you're using the latest (1.3.1), then I'm going to go ahead and say that this is an indentation issue. When I copy and paste your code, it works fine. Are you mixing tabs and spaces? Verify that the compiled output contains the line
Editor.prototype.editor_for_node = ...
Update: See the comments on this answer. Turns out the problem was that the new keyword wasn't being used when invoking the constructor.

tinymce.dom.replace throws an exception concerning parentNode

I'm writing a tinyMce plugin which contains a section of code, replacing one element for another. I'm using the editor's dom instance to create the node I want to insert, and I'm using the same instance to do the replacement.
My code is as follows:
var nodeData =
{
"data-widgetId": data.widget.widgetKey(),
"data-instanceKey": "instance1",
src: "/content/images/icon48/cog.png",
class: "widgetPlaceholder",
title: data.widget.getInfo().name
};
var nodeToInsert = ed.dom.create("img", nodeData);
// Insert this content into the editor window
if (data.mode == 'add') {
tinymce.DOM.add(ed.getBody(), nodeToInsert);
}
else if (data.mode == 'edit' && data.selected != null) {
var instanceKey = $(data.selected).attr("data-instancekey");
var elementToReplace = tinymce.DOM.select("[data-instancekey=" + instanceKey + "]");
if (elementToReplace.length === 1) {
ed.dom.replace(elementToReplace[0], nodeToInsert);
}
else {
throw new "No element to replace with that instance key";
}
}
TinyMCE breaks during the replace, here:
replace : function(n, o, k) {
var t = this;
if (is(o, 'array'))
n = n.cloneNode(true);
return t.run(o, function(o) {
if (k) {
each(tinymce.grep(o.childNodes), function(c) {
n.appendChild(c);
});
}
return o.parentNode.replaceChild(n, o);
});
},
..with the error Cannot call method 'replaceChild' of null.
I've verified that the two argument's being passed into replace() are not null and that their parentNode fields are instantiated. I've also taken care to make sure that the elements are being created and replace using the same document instance (I understand I.E has an issue with this).
I've done all this development in Google Chrome, but I receive the same errors in Firefox 4 and IE8 also. Has anyone else come across this?
Thanks in advance
As it turns out, I was simply passing in the arguments in the wrong order. I should have been passing the node I wanted to insert first, and the node I wanted to replace second.

Unable to write to file with phonegap

Using the methods found on the phonegap api I'm trying to write to a file. This works in Android, but on an iOS device the writer is returning an error. Whenever I call writeFile() it returns an error, and the param passed into writeFail is -1. I cannot see why -1 is being passed into the error function, or why it's even failing to begin with. Has anyone else used the fileWriter on an iOS device, or can you see what I might be doing wrong?
function writeFile() {
var paths = navigator.fileMgr.getRootPaths();
var writer = new FileWriter(paths[0] + "write.txt");
writer.onwrite = writeSuccess;
writer.onerror = writeFail;
writer.write("some sample text");
// The file is now 'some sample text'
}
function writeSuccess() {
console.log("Write has succeeded");
}
function writeFail(evt) {
console.log(evt);
console.log(evt.target.error.code);
}
I had the same problem but I crawled through the mailing list and finally found the solution:
var writer = new FileWriter("write.txt");
This is it. Simply don't prepend the "Documents"-path. The documentation is wrong on that (still).
And don't forget to not use "readAsDataURL" as it would silently not work (on iOS). Hope I could help you.
If you want to write to a file this is the function(phonegap 2.5)
function fileWrite(filePath, text) {
var onFSWin = function(fileSystem) {
fileSystem.root.getFile(filePath, {create: true, exclusive: false}, onGetFileWin, onFSFail);
}
var onGetFileWin = function(fileEntry) {
fileEntry.createWriter(gotFileWriter, onFSFail);
}
var gotFileWriter = function(writer) {
writer.write(text);
}
var onFSFail = function(error) {
console.log(error.code);
}
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onFSWin, onFSFail);
}