XPathEvaluator in Firefox addon - javascript

I am attempting to follow this article to evaluate an XPath expression. My code is copy/pasted from the article:
// Evaluate an XPath expression aExpression against a given DOM node
// or Document object (aNode), returning the results as an array
// thanks wanderingstan at morethanwarm dot mail dot com for the
// initial work.
function evaluateXPath(aNode, aExpr) {
var xpe = new XPathEvaluator();
var nsResolver = xpe.createNSResolver(aNode.ownerDocument == null ?
aNode.documentElement : aNode.ownerDocument.documentElement);
var result = xpe.evaluate(aExpr, aNode, nsResolver, 0, null);
var found = [];
var res;
while (res = result.iterateNext())
found.push(res);
return found;
}
However, I'm getting this error:
Message: ReferenceError: XPathEvaluator is not defined
Is Mozilla's article out of date, perhaps? Is there a more up-to-date article available on parsing XML in an SDK add-on?
Edit. When I tried it this way:
var {Cc, Ci} = require("chrome");
var domXPathEvaluator = Cc["#mozilla.org/dom/xpath-evaluator;1"].createInstance(Ci.nsIDOMXPathEvaluator);
I got a long error message:
- message = Component returned failure code: 0x80570019 (NS_ERROR_XPC_CANT_CREATE_WN) [nsIJSCID.createInstance]
- fileName = undefined
- lineNumber = 14
- stack = #undefined:14:undefined|#resource://helloworld-addon/index.js:14:25|run#resource://gre/modules/commonjs/sdk/addon/runner.js:145:19|startup/</<#resource://gre/modules/commonjs/sdk/addon/runner.js:86:7|Handler.prototype.process#resource://gre/modules/Promise-backend.js:920:23|this.PromiseWalker.walkerLoop#resource://gre/modules/Promise-backend.js:799:7|this.PromiseWalker.scheduleWalkerLoop/<#resource://gre/modules/Promise-backend.js:738:39|Promise*this.PromiseWalker.scheduleWalkerLoop#resource://gre/modules/Promise-backend.js:738:7|this.PromiseWalker.schedulePromise#resource://gre/modules/Promise-backend.js:762:7|this.PromiseWalker.completePromise#resource://gre/modules/Promise-backend.js:705:7|handler#resource://gre/modules/commonjs/sdk/addon/window.js:56:3|
- toString = function () /* use strict */ toString
edit 2. Here, I'll just post my whole code, because it's clear something stranger than I thought is going on. I've created a hello-world addon using the Mozilla tutorials including this one to display a popup. I've modified that further so that it will append text to a file, and modified that further to, I hope, parse and modify XML. So the resulting add-on is supposed to take text entered in the popup and append it to an XML file.
var data = require("sdk/self").data;
var text_entry = require("sdk/panel").Panel({
contentURL: data.url("text-entry.html"),
contentScriptFile: data.url("get-text.js")
});
const fooFile = "/Users/sabrina/Documents/addon/foo.xml";
var {Cc, Ci} = require("chrome");
var parser = Cc["#mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
//var domXPathEvaluator = Cc["#mozilla.org/dom/xpath-evaluator;1"].createInstance(Ci.nsIDOMXPathEvaluator);
var foo = parser.parseFromString(readTextFromFile(fooFile), "application/xml");
// Create a button
require("sdk/ui/button/action").ActionButton({
id: "show-panel",
label: "Show Panel",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick
});
// Show the panel when the user clicks the button.
function handleClick(state) {
text_entry.show();
}
text_entry.on("show", function() {
text_entry.port.emit("show");
});
text_entry.port.on("text-entered", function (text) {
console.log(text);
// appendTextToFile(text, "/Users/sabrina/Documents/addon/output.txt");
appendFoo(text);
text_entry.hide();
});
function appendFoo(text) {
var newNode = foo.createElement("blah");
newNode.innerHTML = text;
var mainFoo = evaluateXPath(foo, '/foo')[0];
mainFoo.appendChild(newNode);
foo.save(fooFile);
}
function evaluateXPath(aNode, aExpr) {
var xpe = new XPathEvaluator();
var nsResolver = xpe.createNSResolver(aNode.ownerDocument == null ?
aNode.documentElement : aNode.ownerDocument.documentElement);
//var result = domXPathEvaluator.evaluate(aExpr, aNode, null,
// domXPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
var found = [];
var res;
while (res = result.iterateNext())
found.push(res);
return found;
}
function readTextFromFile(filename) {
var fileIO = require("sdk/io/file");
var text = null;
if (fileIO.exists(filename)) {
var TextReader = fileIO.open(filename, "r");
if (!TextReader.closed) {
text = TextReader.read();
TextReader.close();
}
}
console.log(arguments.callee.name + ": have read " + text + " from " + filename);
return text;
}
function writeTextToFile(text, filename) {
var fileIO = require("sdk/io/file");
var TextWriter = fileIO.open(filename, "w");
if (!TextWriter.closed) {
TextWriter.write(text + "\n");
console.log(arguments.callee.name + ": have written " + text + " to " + filename);
TextWriter.close();
}
function appendTextToFile(text, filename) {
var textplus = readTextFromFile(filename) + text;
writeTextToFile(textplus, filename);
}
I run at the command line using jpm run which opens Firefox Developer Edition. I click the addon button, the popup comes up, I enter text, I hit return, and I see this in the console:
JPM undefined Starting jpm run on Sabrina's Helloworld Addon
Creating XPI
JPM undefined XPI created at /var/folders/gg/r_hp4hzs0gdfy70f__l18fmr0000gn/T/#helloworld-addon-0.0.1.xpi (46ms)
Created XPI at /var/folders/gg/r_hp4hzs0gdfy70f__l18fmr0000gn/T/#helloworld-addon-0.0.1.xpi
JPM undefined Creating a new profile
console.log: helloworld-addon: readTextFromFile: have read <?xml version="1.0" encoding="UTF-8"?>
<foo><blah>eek</blah><foo>
from /Users/sabrina/Documents/addon/foo.xml
console.log: helloworld-addon: ook
console.error: helloworld-addon:
JPM undefined Message: ReferenceError: XPathEvaluator is not defined
Stack:
evaluateXPath#resource://gre/modules/commonjs/toolkit/loader.js -> resource://helloworld-addon/index.js:63:9
appendFoo#resource://gre/modules/commonjs/toolkit/loader.js -> resource://helloworld-addon/index.js:57:19
#resource://gre/modules/commonjs/toolkit/loader.js -> resource://helloworld-addon/index.js:50:2
emitOnObject#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules/commonjs/sdk/event/core.js:112:9
emit#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules/commonjs/sdk/event/core.js:89:38
portEmit#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules/commonjs/sdk/content/sandbox.js:343:7
emitOnObject#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules/commonjs/sdk/event/core.js:112:9
emit#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules/commonjs/sdk/event/core.js:89:38
onContentEvent/<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules/commonjs/sdk/content/sandbox.js:384:5
delay/<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules/commonjs/sdk/lang/functional/concurrent.js:38:20
notify#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules/commonjs/sdk/timers.js:40:9

Non-authoritative, speculative answer
In a different question, Wladimir Palant (author of Adblock Plus, presumably he has good knowledge of firefox) said:
Yes, a lot of global classes available in the window context aren't there in SDK modules which are sandboxes.
Source: https://stackoverflow.com/a/10522459/3512867
This could explain why XPathEvaluator is not defined in the SDK addon.
The logical conclusion would be to use Firefox's Components object to access the nsIDOMXPathEvaluator interface. Which brings up the following error:
NS_ERROR_XPC_CANT_CREATE_WN
Looking into it takes us to this, from mozillazine's forums user "lithopsian":
That means it can't create a wrapper for a non-javascript interface.
Source: http://forums.mozillazine.org/viewtopic.php?f=19&t=2854793
I am unable to judge the credibility of that statement and while the linked bug reports seem to be relevant, I can not attest they actually are:
https://bugzilla.mozilla.org/show_bug.cgi?id=994964
https://bugzilla.mozilla.org/show_bug.cgi?id=1027095
https://bugzilla.mozilla.org/show_bug.cgi?id=1029104
Unless those informations are confirmed (or dispelled) by people with a deeper knowledge of Firefox's internal workings, I can only hesitantly conclude that the nsIDOMXPathEvaluator interface can simply not work in an SDK addon.

Related

Is it possible to create a simple parser and create a javascript file and then call that file with that parser?

I am trying to create a parser, a file. I am trying to use this file created to be parsed by the parser. The steps are:
Add pegjs with
Create an parser with var parserFile
Create file with var makeFile
Add contentFile, nameFile with var contentFile, nameFile and here: var makeFile
Use parser in var makeFile with var parserFile
// #author dikafon
// #license runFile, license: Open source, 05-10-2019
// #readme Include pegs.js and build parser, generate file, include grammar in file, download, run script.rF ( template, output )
var head = document.getElementsByTagName('head')[0];
var fileScript = document.createElement('script');
fileScript.type = 'text/javascript';
fileScript.src = 'https://pegjs.org/vendor/pegjs/peg.js';
head.appendChild(fileScript);
var runFile = (function () {
// make, Grammar
var parserFile;
parserFile = PEG.buildParser(
"start\n"+
"= comment def runFile msgbox rules_list\n"+
"comment = COMSTART (not_com/comment)* COMSTOP\n"+
"not_com = (!COMSTOP !COMSTART.)\n"+
"COMSTART='.'\n"+
"COMSTOP='.'\n"+
"def\n"+
"= def:'runFile'? __ { return runFile; }\n"+
"runFile\n"+
"= runFile:('bat'/'cmd'/'hta'/'vbs'/'rF') _ { return runFile;}\n"+
"msgbox\n"+
"= msgbox:('runFile')_ { return msgbox;}\n"+
"rules_list\n"+
"= '(' _ exp:[a-zA-Z]+ _ ')' { return [ exp.join('') ]; }\n"+
"_ = [ \t\r\n]*\n"+
"__ = [ \t\r\n]"
);
// make, File
var makeFile = document.createElement("a");
document.body.appendChild(a);
makeFile.style = "display: none";
// grammar how 'content, File' && 'name, File'
return function (contentFile, nameFile) {
// setting, file
var define = file,
blob = new Blob([text], {type: "text/plain;charset=utf-8"}),
url = window.URL.createObjectURL(blob);
makeFile.href = url;
makeFile.download = nameFile;
makeFile.click();
window.URL.revokeObjectURL(url);
};
}());
// content, file
var file = (function () {
var contentFile, nameFile, finishFile;
contentFile = (". runFile, license: Open source, 05-10-2019. \n"+ "def
runFile(rF) \n"+"msgbox('runFile');"+"\n");
finishFile = runFile(contentFile , nameFile);
nameFile = "script.rF";
})();
// call, file & grammar
// show
console.log(
". runFile, license: Open source, 05-10-2019. \n"+
"def runFile(rF) \n"+
"msgbox('runFile');"+
"\n"
);
// generate, file and download, run script ( contentFile, nameFile )
// build parser, parser.parse
console.log((parser.parse(runFile(file))));
Uncaught SyntaxError: Unexpected token ')'
Line 1, column 1: Expected "." but "2" found.
Until syntax errors are fixed, runtime errors should probably be ignored. There is a syntax error here:
var file = (function () { // <== opening bracket signals the possible start of an IIFE
...
} // <== closing bracket ")" expected
// call, file & grammar
// show
console.log( ...
Either the opening "(" or the missing closing ")" are syntax errors. The function on the right hand side of the assignment to file doesn't have a return statement, so I guess that it's the opening "(" that is the typo and an IIFE is not intended.
The first argument passed to runFile, i.e. contentFile, isn't used in the function.
The runFile function doesn't have a return statement so returns undefined. Assigning the result to finishFile seems pointless.
Once the syntax errors are fixed, there will be runtime errors.

document generation only works the first time

I'm using openxml in my HTML5 mobile app to generate word documents on the mobile device.
In general openxml works fine and straight forward, but I'm struggling with an annyoing problem.
The document generation only works the first time after I've started the app. This time I can open and view the document. Restart the app means:
- Redeploy from development machine
- Removing the app from the task pane (pushing aside; I assume the app is removed then?)
The second time I get the message the document is corrupted and I'm unable to view the file
UPDATE:
I can't reproduce this behaviour when I'm running the app connected to the remote debugger without having a breakpoint set. Doing it this way I always get a working document.
I doesn't make a difference wether I do any changes on the document or not. Simply open and saving reproduce this error.
After doing some research I've found that structure of the docx.zip file of the working and the corrupt file is the same. They also have the same file length. But in the corrupt docx there are some files I've found some files having a wrong/invalid CRC. See here an example when trying to get a corrupt file out of the zip. Other files are working as expected.
The properties for this file are->
(CRC in a working version is: 44D3906C)
Code for processing the doc-template:
/*
* Process the template
*/
function processTemplate(doc64, callback)
{
"use strict";
console.log("PROCESS TEMPLATE");
var XAttribute = Ltxml.XAttribute;
var XCData = Ltxml.XCData;
var XComment = Ltxml.XComment;
var XContainer = Ltxml.XContainer;
var XDeclaration = Ltxml.XDeclaration;
var XDocument = Ltxml.XDocument;
var XElement = Ltxml.XElement;
var XName = Ltxml.XName;
var XNamespace = Ltxml.XNamespace;
var XNode = Ltxml.XNode;
var XObject = Ltxml.XObject;
var XProcessingInstruction = Ltxml.XProcessingInstruction;
var XText = Ltxml.XText;
var XEntity = Ltxml.XEntity;
var cast = Ltxml.cast;
var castInt = Ltxml.castInt;
var W = openXml.W;
var NN = openXml.NoNamespace;
var wNs = openXml.wNs;
var doc = new openXml.OpenXmlPackage(doc64);
// add a paragraph to the beginning of the document.
var body = doc.mainDocumentPart().getXDocument().root.element(W.body);
var tpl_row = ((doc.mainDocumentPart().getXDocument().descendants(W.tbl)).elementAt(1).descendants(W.tr)).elementAt(2);
var newrow = new XElement(tpl_row);
doc.mainDocumentPart().getXDocument().descendants(W.tbl).elementAt(1).add(newrow);
// callback(doc);
var mod_file = null;
var newfile;
var path;
if (doc != null && doc != undefined ) {
mod_file = doc.saveToBlob();
// Start writing document
path = "Templates";
newfile = "Templates/Bau.docx";
console.log("WRITE TEMPLATE DOCUMENT");
fs.root.getFile("Templates/" + "MyGenerated.docx", {create: true, exclusive: false},
function(fileEntry)
{
fileEntry.createWriter(
function(fileWriter)
{
fileWriter.onwriteend = function(e) {
console.log("TEMPLATE DOCUMENT WRITTEN:"+e.target.length);
};
fileWriter.onerror = function(e) {
console.log("ERROR writing DOCUMENT:" + e.code + ";" + e.message);
};
var blobreader = new FileReader();
blobreader.onloadend = function()
{
fileWriter.write(blobreader.result); // reader.result contains the contents of blob as a typed array
};
blobreader.readAsArrayBuffer(mod_file);
},
null);
}, null);
};
Any ideas what I'm doing wrong?
Thanks for posting about the error. There were some issues with jszip.js that I encountered when I was developing the Open XML SDK for JavaScript.
At the following link, there is a sample javascript app that demonstrates generating a document.
Open XML SDK for JavaScript Demo
In that app you can save multiple DOCXs, one after another, and they are not corrupted.
In order to work on this issue, I need to be able to re-produce locally. Maybe you can take that little working web app and replace parts with your parts until it is generating invalid files?
Cheers, Eric
P.S. I am traveling and have intermittent access to internet. If you can continue the thread on OpenXmlDeveloper.org, then it will help me to answer quicker. :-)
What made it work for me, was changing the way of adding images (Parts) to the document. I was using the type "binary" for adding images to document. I changed this to "base64"
So I changed the source from:
mydoc.addPart( "/word/"+reltarget, openXml.contentTypes.png, "binary", fotodata ); // add Image Part to doc
to:
mydoc.addPart( "/word/"+reltarget, openXml.contentTypes.png, "base64", window.btoa(fotodata) ); // add Image Part to doc

File API writing file doesn't work Samsung smart tv SDK

I have this javascript code which makes possible writing in a file
{
var fileSystemObj = new FileSystem();
var fileObj = fileSystemObj.openCommonFile(curWidget.id +
‘/testFile.data’, ‘w’);
fileObj.writeLine(‘something to write.’);
fileSystemObj.closeCommonFile(fileObj);
}
but it doesn't work. Doesn't even display any error!
samsung developer forum (you may not see unless you sign in... )
I am quoting it.
case tvKey.KEY_RED:
alert('RED BUTTON!');
alert('CWID: '+curWidget.id);
try {
var fileSystemObj = new FileSystem();
var fileObj = fileSystemObj.openCommonFile(curWidget.id+'/testFile.data','w');
fileObj.writeLine('something to write.');
fileSystemObj.closeCommonFile(fileObj);
} catch (e) {
alert('Error: file handling: '+e);
}
break;
lead to error:
alert() : Error: file handling: TypeError: 'null' is not an object
(evaluating 'fileObj.writeLine')
Reading cause same problem.
and solution accepted in that link is:
I suppose that problem is that you have to create common dir (if does not exist ) at first :
var fileObj = fileSystemObj.openCommonFile(filePath, 'w');
if(!fileObj){
var bValid = fileSystemObj.isValidCommonPath(curWidget.id);
if (!bValid) {
fileSystemObj.createCommonDir(curWidget.id);
}
}
fileObj = fileSystemObj.openCommonFile(filePath, 'w');
fileObj.writeLine('something to write.');
fileSystemObj.closeCommonFile(fileObj);

Server Side Logging Of Client Side Javascript Crashes

I have a large complex web app with thousands of lines of Javascript. There is a small set of intermittent Javascript bugs that are report by users.
I think these are epiphenomena of race conditions - something has not initialised correctly and the Javascript crashes causing 'down stream' js not to run.
Is there anyway to get Javascript execution crashes to log back server side?
All the js logging libraries like Blackbird and Log4JavaScript are client-side only.
I have written a remote error logging function using window.onerror as suggested by #pimvdb
Err = {};
Err.Remoterr = {};
Err.Remoterr.onerror = function (msg, errorfileurl, lineno) {
var jsonstring, response, pageurl, cookies;
// Get some user input
response = prompt("There has been an error. " +
"It has been logged and will be investigated.",
"Put in comments (and e-mail or phone number for" +
" response.)");
// get some context of where and how the error occured
// to make debugging easier
pageurl = window.location.href;
cookies = document.cookie;
// Make the json message we are going to post
// Could use JSON.stringify() here if you are sure that
// JSON will have run when the error occurs
// http://www.JSON.org/js.html
jsonstring = "{\"set\": {\"jserr\": " +
"{\"msg\": \"" + msg + "\", " +
"\"errorfileurl\": \"" + errorfileurl + "\", " +
"\"pageurl\": \"" + pageurl + "\", " +
"\"cookies\": \"" + cookies + "\", " +
"\"lineno\": \"" + lineno + "\", " +
"\"response\": \"" + response + "\"}}}";
// Use the jquery cross-browser post
// http://api.jquery.com/jQuery.post/
// this assumes that no errors happen before jquery has initialised
$.post("?jserr", jsonstring, null, "json");
// I don't want the page to 'pretend' to work
// so I am going to return 'false' here
// Returning 'true' will clear the error in the browser
return false;
};
window.onerror = Err.Remoterr.onerror;
I deploy this between the head and body tags of the webpage.
You will want to change the JSON and the URL that you post it to depending on how you are going to log the data server side.
Take a look at https://log4sure.com (disclosure: I created it) - but it is really useful, check it out and decide for yourself. It allows you to log errors/event and also lets you create your custom log table. It also allows you to monitor your logs real-time. And the best part, its free.
You can also use bower to install it, use bower install log4sure
The set up code is really easy too:
// setup
var _logServer;
(function() {
var ls = document.createElement('script');
ls.type = 'text/javascript';
ls.async = true;
ls.src = 'https://log4sure.com/ScriptsExt/log4sure.min.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ls, s);
ls.onload = function() {
// use your token here.
_logServer = new LogServer("use-your-token-here");
};
})();
// example for logging text
_logServer.logText("your log message goes here.")
//example for logging error
divide = function(numerator, divisor) {
try {
if (parseFloat(value) && parseFloat(divisor)) {
throw new TypeError("Invalid input", "myfile.js", 12, {
value: value,
divisor: divisor
});
} else {
if (divisor == 0) {
throw new RangeError("Divide by 0", "myfile.js", 15, {
value: value,
divisor: divisor
});
}
}
} catch (e) {
_logServer.logError(e.name, e.message, e.stack);
}
}
// another use of logError in window.onerror
// must be careful with window.onerror as you might be overwriting some one else's window.onerror functionality
// also someone else can overwrite window.onerror.
window.onerror = function(msg, url, line, column, err) {
// may want to check if url belongs to your javascript file
var data = {
url: url,
line: line,
column: column,
}
_logServer.logError(err.name, err.message, err.stack, data);
};
// example for custom logs
var foo = "some variable value";
var bar = "another variable value";
var flag = "false";
var temp = "yet another variable value";
_logServer.log(foo, bar, flag, temp);

async line-by-line file reading

When I try to read a file synchronously, Firefox freezes.
When I try to read a file asynchronously, I get numbers instead of words.
This code snippet...
var MY_ID = "cbdeltrem1984#bol.com.br";
var em = Components.classes["#mozilla.org/extensions/manager;1"].getService(Components.interfaces.nsIExtensionManager);
var file = em.getInstallLocation(MY_ID).getItemFile(MY_ID, "wordlist.txt");
...plus this code snippet...
var appInfo=Components.classes["#mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULAppInfo);
var isOnBranch = appInfo.platformVersion.indexOf("1.8") == 0;
var ios=Components.classes["#mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
var fileURI=ios.newFileURI(file);
var channel = ios.newChannelFromURI(fileURI);
var observer = {
onStreamComplete : function(aLoader, aContext, aStatus, aLength, aResult) {
alert(aResult);
}
};
var sl = Components.classes["#mozilla.org/network/stream-loader;1"].createInstance(Components.interfaces.nsIStreamLoader);
if (isOnBranch) {
sl.init(channel, observer, null);
} else {
sl.init(observer);
channel.asyncOpen(sl, channel);
}
...alerts numbers instead of words.
How to read a file asynchronously line-by-line?
The below sample can read files asynchronously, without locking UI thread (works with FF4+ only though, since I used Modules). Doesn't read line-by-line... Also, I assumed it to be text file, though..
Components.utils.import("resource://gre/modules/NetUtil.jsm");
NetUtil.asyncFetch(file, function(inputStream, status) {
if (!Components.isSuccessCode(status)) {
// Handle error!
return;
}
// The file data is contained within inputStream.
// You can read it into a string with
var data = NetUtil.readInputStreamToString(inputStream, inputStream.available());
Components.utils.reportError("data:" + data);
});
Sources:
https://developer.mozilla.org/en/JavaScript_code_modules/NetUtil.jsm
https://developer.mozilla.org/en/Code_snippets/File_I%2F%2FO#Read_into_a_stream_or_a_string

Categories

Resources