Разрешить запись файла на веб-страницу с расширением Firefox

У меня есть веб-приложение, которое мой клиент использует для кассового аппарата. Что мне нужно сделать, так это создать локальный файл, поскольку программное обеспечение кассового аппарата должно читать из этого файла для печати.

До сих пор я использовал этот код:

netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
file.initWithPath(filePath);

К сожалению, с последней версией firefox это больше не работает, поэтому мне сказали, что мне нужно и надстройка для создания файла. Я пытался разработать надстройку (не знаю, успешно ли), и у меня есть main.js выглядит так:

var FileManager =
{
Write:
    function (File, Text)
    {
        if (!File) return;
        const unicodeConverter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
            .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);

        unicodeConverter.charset = "UTF-8";

        Text = unicodeConverter.ConvertFromUnicode(Text);
        const os = Components.classes["@mozilla.org/network/file-output-stream;1"]
          .createInstance(Components.interfaces.nsIFileOutputStream);
        os.init(File, 0x02 | 0x08 | 0x20, 0700, 0);
        os.write(Text, Text.length);
        os.close();
    },

Read:
    function (File)
    {
        if (!File) return;
        var res;

        const is = Components.classes["@mozilla.org/network/file-input-stream;1"]
            .createInstance(Components.interfaces.nsIFileInputStream);
        const sis = Components.classes["@mozilla.org/scriptableinputstream;1"]
            .createInstance(Components.interfaces.nsIScriptableInputStream);
        is.init(File, 0x01, 0400, null);
        sis.init(is);

        res = sis.read(sis.available());

        is.close();

        return res;
    },
}; 

Есть идеи, как мне использовать main.js? Где я его найду после установки надстройки? Мне нужно использовать что-то вроде этого: FileManager.Write (путь, текст).


person Leontin Lemnaru    schedule 12.12.2012    source источник


Ответы (1)


Извините за сверхпоздний ответ.

Если я правильно понимаю ваш вопрос, у вас есть приложение P.O.S, работающее в Firefox, которое обращается к какому-то локальному веб-серверу через HTTP. Клиентский JavaScript вашего приложения должен иметь возможность читать и записывать файлы из локальной файловой системы ПК браузера.

Если это верно, вы можете сделать это следующим образом. Вам нужно будет создать надстройку Firefox, самый простой вид которой называется надстройкой с «самозагрузкой» (или «без перезапуска»).


Аддон без перезапуска состоит из двух файлов:

  • bootstrap.js (файл JavaScript, содержащий ваш «привилегированный» код)
  • install.rdf (XML-файл, описывающий ваше дополнение к Firefrox)

Чтобы создать надстройку, просто поместите оба файла в верхний уровень (без папок!) ZIP-файла с расширением .xpi. Чтобы установить надстройку, перейдите к about:addons, затем в меню инструментов нажмите Install from file, найдите свой XPI, откройте его, а затем после небольшой задержки выберите Install.


В install.rdf вставьте что-то вроде этого:

<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 
     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
    <Description about="urn:mozilla:install-manifest">
        <em:id>youraddonname@yourdomain</em:id>
        <em:type>2</em:type>
        <em:name>Name of your addon</em:name>
        <em:version>1.0</em:version>
        <em:bootstrap>true</em:bootstrap>
        <em:description>Describe your addon.</em:description>
        <em:creator>Your name</em:creator>

        <!-- Firefox Desktop -->
        <em:targetApplication>
            <Description>
                <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
                <em:minVersion>4.0.*</em:minVersion>
                <em:maxVersion>29.0.*</em:maxVersion>
            </Description>
        </em:targetApplication>
    </Description>
</RDF>

Вам необходимо реализовать две обязательные функции JavaScript в bootstrap.js:

  • startup() - вызывается при установке аддона и при запуске браузера.
  • shutdown() - вызывается при удалении надстройки и при закрытии браузера.

Вы должны вызвать оставшуюся часть своего «привилегированного» кода в startup(). В целях гигиены вы можете (и, вероятно, должны) также реализовать функции install() и uninstall().

Начните с реализации следующего кода в bootstrap.js:

const Cc = Components.classes;
const Ci = Components.interfaces;
let consoleService = Cc["@mozilla.org/consoleservice;1"]
                        .getService(Ci.nsIConsoleService);
let wm             = Cc["@mozilla.org/appshell/window-mediator;1"]
                        .getService(Ci.nsIWindowMediator);

function LOG(msg) {
  consoleService.logStringMessage("EXTENSION: "+msg);    
}

function startup() {
  try {
    LOG("starting up...");
    let windows = wm.getEnumerator("navigator:browser");
    while (windows.hasMoreElements()) {
      let chromeWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
      WindowListener.setupBrowserUI(chromeWindow);
    }
    wm.addListener(WindowListener);
    LOG("done startup.");
  } catch (e) {
    LOG("error starting up: "+e);
  }
}

function shutdown() {
  try {
    LOG("shutting down...");
    let windows = wm.getEnumerator("navigator:browser");
    while (windows.hasMoreElements()) {
      let chromeWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
      WindowListener.tearDownBrowserUI(chromeWindow);
    }
    wm.addListener(WindowListener);
    LOG("done shutdown.");
  } catch (e) {
    LOG("error shutting down: "+e);
  }
}

По сути, это вызывает WindowListener.setupBrowserUI() для каждого текущего и будущего окна вашего веб-браузера. WindowListener определяется следующим образом:

var WindowListener = {
  setupBrowserUI: function(chromeWindow) {
    chromeWindow.gBrowser.addEventListener('load', my_load_handler, true);
  },
  tearDownBrowserUI: function(chromeWindow) {
    chromeWindow.gBrowser.removeEventListener('load', my_load_handler, true);
  },
  onOpenWindow: function(xulWindow) {
    let chromeWindow = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIDOMWindow);
    chromeWindow.addEventListener("load", function listener() {
      chromeWindow.removeEventListener("load", listener, false);
      var domDocument = chromeWindow.document.documentElement;
      var windowType = domDocument.getAttribute("windowtype");
      if (windowType == "navigator:browser")
        WindowListener.setupBrowserUI(chromeWindow);
    }, false);
  },
  onCloseWindow: function(chromeWindow) { },
  onWindowTitleChange: function(chromeWindow, newTitle) { }
};

Это устанавливает прослушиватель событий для OpenWindow события и, в свою очередь, устанавливает прослушиватель событий для load событий в TabBrowser каждого ChromeWindow. Обработчик событий load определяется как:

var my_load_handler = function (evt) {
  try {
    var browserEnumerator = wm.getEnumerator("navigator:browser");
    while (browserEnumerator.hasMoreElements()) {
      var browserWin = browserEnumerator.getNext();
      var tabbrowser = browserWin.gBrowser;
      var numTabs = tabbrowser.browsers.length;
      for (var index = 0; index < numTabs; index++) {
        var currentBrowser = tabbrowser.getBrowserAtIndex(index);
        var domWindow = currentBrowser.contentWindow.wrappedJSObject;
        // identify your target page(s)...
        if (domWindow.location.href == 'http://yourserver/yourpage') {
          // install the privileged methods (if not already there)
          if (!domWindow.hasOwnProperty('__my_priv_members__') {
            install_my_privileged_methods(browserWin, domWindow);
          }
        }
      }
    }
  } catch (e) {
    LOG(e);
  }
}

Это нацелено на правильные страницы (путем проверки window.location.href и вызова install_my_privileged_methods их window объекта, который определяется как:

function install_my_privileged_methods(chromeWindow, domWindow) {
  install_privileged_method(chromeWindow, domWindow, 'WriteFile', 
    function(priv) {
      return function(File, Text, cb) {
        priv.call([File, Text], function(rstatus, rdata, rerror){
          if (cb) cb(rstatus, rerror);
        });
      };
    },
    function (chromeWindow, args, cb) {
      var [File, Text] = args;
      if (!File) return cb(0, null, "need a filename");
      try {
        const unicodeConverter = 
           Cc["@mozilla.org/intl/scriptableunicodeconverter"]
             .createInstance(Ci.nsIScriptableUnicodeConverter);
        unicodeConverter.charset = "UTF-8";
        Text = unicodeConverter.ConvertFromUnicode(Text);
        const os = Cc["@mozilla.org/network/file-output-stream;1"]
                     .createInstance(Ci.nsIFileOutputStream);
        os.init(File, 0x02 | 0x08 | 0x20, 0700, 0);
        os.write(Text, Text.length);
        os.close();
        cb(1, null, null);
      } catch (e) {
        cb(0, null, "error writing file: "+e);
      }
    }
  );

  install_privileged_method(chromeWindow, domWindow, 'ReadFile', 
    function(priv) {
      return function(File, cb) {
        priv.call([File], function(rstatus, rdata, rerror){
          if (cb) cb(rstatus, rdata, rerror);
        });
      };
    },
    function (chromeWindow, args, cb) {
      var [File] = args;
      if (!File) return cb(0, null, "need a filename");
      try {
        const is = Cc["@mozilla.org/network/file-input-stream;1"]
                     .createInstance(Ci.nsIFileInputStream);
        const sis = Cc["@mozilla.org/scriptableinputstream;1"]
                      .createInstance(Ci.nsIScriptableInputStream);
        is.init(File, 0x01, 0400, null);
        sis.init(is);
        var Text = sis.read(sis.available());    
        is.close();
        cb(1, Text, null);
      } catch (e) {
        cb(0, null, "error reading file: "+e);
      }
    }
  );
}

Я не тестировал этот код. Это прямой перевод того, что вы написали выше ... Я предполагаю, что это работает!

Это добавляет два специальных метода, WriteFile & ReadFile, к выбранным window объектам. В (непривилегированном) коде JavaScript вашего веб-приложения используйте их следующим образом:

var buffer = '...'; // the text to be written
window.WriteFile('C:\\path\\to\\file.txt', buffer, function(ok, errmsg) {
  if (!ok) alert(errmsg);
});

window.ReadFile('C:\\path\\to\\file.txt', function(ok, buffer, errmsg) { 
  if (!ok) return alert(errmsg);
  // use buffer here!
});

Наконец, install_privileged_method определяется как:

var install_privileged_method = (function(){
  var gensym = (function (){
    var __sym = 0;
    return function () { return '__sym_'+(__sym++); }
  })();
  return function (chromeWindow, target, slot, handler, methodFactory) {
    try {
      target.__pmcache__ = target.hasOwnProperty('__pmcache__')
        ? target.__pmcache__
        : { ticket_no: 0, callbacks: {}, namespace: gensym() };
      target[slot] = methodFactory({ call: function(fargs, fcb) {
        try {
          var ticket_no = target.__pmcache__.ticket_no++;
          target.__pmcache__.callbacks[ticket_no] = fcb;
          var cevent = target.document.createEvent("CustomEvent");
          cevent.initCustomEvent(
            target.__pmcache__.namespace+'.'+slot,
            true, true, { fargs: fargs, ticket_no: ticket_no }
          );
          target.dispatchEvent(cevent);
        } catch (ue) {
          fcb(0, null, 'untrusted dispatcher error: '+ue);
        }
      }});
      LOG("installed untrusted dispatcher for method '"+slot+"'.");
      target.addEventListener(
        target.__pmcache__.namespace+'.'+slot,
        function(cevent){
          var ticket_no = cevent.detail.ticket_no;
          var fargs = cevent.detail.fargs;
          var fcb = target.__pmcache__.callbacks[ticket_no];
          try {
            handler(chromeWindow, fargs, fcb);
          } catch (pe) {
            fcb(0, null, 'privileged handler error: '+pe);
          }
        },
        false,
        true
      );
      LOG("installed privileged handler for method '"+slot+"'.");
    } catch (ie) {
      LOG("ERROR installing handler/factory for privileged "+
          "method '"+slot+"': "+ie);
    }
  };
})();
person David-SkyMesh    schedule 05.02.2014