How to access local file via chrome URL?

Talk about add-ons and extension development.
Post Reply
Bananeweizen
Posts: 2
Joined: November 3rd, 2004, 12:13 am

How to access local file via chrome URL?

Post by Bananeweizen »

Hi all,

in my Wikipedia extension I want to read a locale specific XML file from "chrome://wikipedia/locale/templates.xml". I have code using the nsILocalFile interface which definitely works for windows filename like c:\test.txt using initWithPath() but does not work for such a chrome URL. What do I need to do to translate the chrome URL to a local file name or do I need to use another interface?

Ciao and Thanks, Michael.
Torisugari
Posts: 1634
Joined: November 4th, 2002, 8:34 pm
Location: Kyoto, Nippon (GMT +9)
Contact:

Re: How to access local file via chrome URL?

Post by Torisugari »

Bananeweizen wrote:What do I need to do to translate the chrome URL to a local file name or do I need to use another interface?


A file in a package is not a normal local file, so there might not be no "path". How about such a script?

Code: Select all

function getContents(aURL){
  var ioService=Components.classes["@mozilla.org/network/io-service;1"]
    .getService(Components.interfaces.nsIIOService);
  var scriptableStream=Components
    .classes["@mozilla.org/scriptableinputstream;1"]
    .getService(Components.interfaces.nsIScriptableInputStream);

  var channel=ioService.newChannel(aURL,null,null);
  var input=channel.open();
  scriptableStream.init(input);
  var str=scriptableStream.read(input.available());
  scriptableStream.close();
  input.close();
  return str;
}

try{
  alert(getContents("chrome://browser/content/browser.css"));
  alert(getContents("http://www.mozillazine.org/"));
}catch(e){alert(e)}


Note: Such a synch reading freezes the UI during its operation. If the contents is too large to put up with waiting for the result, please use asyncOpen instead.
http://www.xulplanet.com/references/xpc ... _asyncOpen
User avatar
mcm_ham
Posts: 1747
Joined: June 16th, 2004, 6:09 am
Location: Christchurch, New Zealand

Post by mcm_ham »

Very useful Torisugari,

I have one problem though, in my extension I'm allowing the user to set the URL path to load from. On Deer Park Alpha though if it is set to a directory like "file:///C:/" then "channel.open()" works (on Fx 1.0.6 it throws a not implemented exception) and crashes on the scriptable stream read because "input.available()" returns a very large number.

Secondly I've found when reading files from a website address (http://...), it often does not read more then a single packet (I'm guessing) cutting the file off. Some astute person helped by suggesting this code which fixed that problem.

Code: Select all

var length, str = "";

if (channel.contentLength > 0)
  length = channel.contentLength;
else
  length = input.available();
     
while (str.length < length)
  str += streamIO.read(length - str.length);

Instead of:

Code: Select all

var str=scriptableStream.read(input.available());


Edit
Have filed Bug 304414 for the first issue.
dan61psu
Posts: 9
Joined: January 24th, 2007, 3:59 pm

Post by dan61psu »

How could I extend the example Torisugari posted so that I can stream in well formed XML from a chrome path and then manipulate it/read it via DOM methods???

Thanks!
Jfingland
Posts: 4
Joined: March 21st, 2008, 7:43 pm

Post by Jfingland »

First note that you can read an xml file with the chrome url, but for writing you need something like the following...

Code: Select all

function chromeToPath (aPath) {

   if (!aPath || !(/^chrome:/.test(aPath)))
      return; //not a chrome url
   var rv;
   
      var ios = Components.classes['@mozilla.org/network/io-service;1'].getService(Components.interfaces["nsIIOService"]);
        var uri = ios.newURI(aPath, "UTF-8", null);
        var cr = Components.classes['@mozilla.org/chrome/chrome-registry;1'].getService(Components.interfaces["nsIChromeRegistry"]);
        rv = cr.convertChromeURL(uri).spec;

        if (/^file:/.test(rv))
          rv = this.urlToPath(rv);
        else
          rv = this.urlToPath("file://"+rv);

      return rv;
}

function urlToPath (aPath) {

    if (!aPath || !/^file:/.test(aPath))
      return ;
    var rv;
   var ph = Components.classes["@mozilla.org/network/protocol;1?name=file"]
        .createInstance(Components.interfaces.nsIFileProtocolHandler);
    rv = ph.getFileFromURLSpec(aPath).path;
    return rv;
}


the above will translate chrome urls to local file adreesses... in another function you could...

Code: Select all

function doXmlStuff () {
   var somefile = chromeToPath("chrome://path/to/file");
   var xmlDoc;
   var file = Components.classes["@mozilla.org/file/local;1"]
         .createInstance(Components.interfaces.nsILocalFile);
   file.initWithPath(somefile);
   if (!(file.exists())) {
      var fcStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
         .createInstance(Components.interfaces.nsIFileOutputStream);
      fcStream.init(file, 0x02 | 0x08 | 0x20, 0666, 0); // write, create, truncate

      var data = '<xml><rootnode></rootnode>';
      fcStream.write(data,data.length);
      fcStream.close
   }

   xmlDoc = document.implementation.createDocument("","",null);
   xmlDoc.load("chrome://piratequesting/content/trainingLog.xml");
   xmlDoc.onload = function () {
      xmlDoc.documentElement.appendChild(....whatever you want to append....);
      var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
                         .createInstance(Components.interfaces.nsIFileOutputStream);
      //clear the file for writing the new doc
      foStream.init(file, 0x02 | 0x08 | 0x20, 0666, 0); // write, create, truncate
      var ser = new XMLSerializer();
      //write the serialized XML to file
      ser.serializeToStream(xmlDoc, foStream, "");
      foStream.close();   
   }
}


Hope this helps. There may be a faster way to do things but this gets the job done.
Kybuk
Posts: 4
Joined: June 18th, 2006, 8:22 am

Re: How to access local file via chrome URL?

Post by Kybuk »

So, is there a way to get a file content from within xpi package ?

I need do it in js xpcom.

When I try Torisugari approach (second post in this theme) I get

Code: Select all

[Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsIChannel.open]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: ...


When I try next code (content descriptor in chrome.manifest has "contentacessible=yes" flag)

Code: Select all

var domdoc = Components.classes["@mozilla.org/xul/xul-document;1"].createInstance (Components.interfaces.nsIDOMXULDocument);
var doc = domdoc.implementation.createDocument("","",null);
doc.async = false;
doc. load("chrome://myext/content/overlay.xul");

I get

Code: Select all

[Exception... "Access to restricted URI denied"  code: "1012" nsresult: "0x805303f4 (NS_ERROR_DOM_BAD_URI)"  location: ...
immemorial to be born
max1million
Posts: 2810
Joined: November 15th, 2004, 5:03 am

Re: How to access local file via chrome URL?

Post by max1million »

XMLHttpRequest can read xml from chrome:// (even jarred) or file:// like in this reply. If you keep file out of jar you can use like in this reply to get a file:// url, or the var file is an nsIFile, and see Getting_special_files

say if the user specified file location not set and file not in profile folder, read a default from chrome:// or extension's folder,

and for modified xml, if the user specified file location not set, save then read from profile

note: all for chrome code not web page, (requesting xul I had troubles with)
Kybuk
Posts: 4
Joined: June 18th, 2006, 8:22 am

Re: How to access local file via chrome URL?

Post by Kybuk »

Now I have some more info.
channel. open () fails if document being opened is already used as overlay. (i.e. there is an 'overlay' line in chrome.manifest).
I can't use XMLHttpRequest object because it isn't defined in js xpcom runtime environment. I tried to use nsIXMLHttpRequest but it returns 'null' in '.responseXML' ('.async' property was set to 'false').
immemorial to be born
Post Reply