Please note, this is a STATIC archive of website developer.mozilla.org from November 2016, cach3.com does not collect or store any user information, there is no "phishing" involved.

Criando Módulos Reutilizáveis

Para seguir este tutorial você precisa do SDK instalado e conhecimento básico de cfx.

Com o SDK você não precisa manter tudo em um único arquivo "main.js". Você pode separar seu código em módulos separados com interfaces claramente definidas entre eles. Você então importa e usa estes módulos de outras partes de seu add-on usando a declaração require(), da mesma forma que você importa os módulos core do SDK como page-mod or panel.

Muitas vezes faz sentido estruturar um add-on muito grande ou complexo como uma coleção de módulos. Isso torna o desenho do add-on mais fácil de entender e fornece algum encapsulamento em que cada módulo exportará somente o que ele escolheu, então você pode mudar o módulo internamente sem quebrar seu usuário.

Uma vez que você fez isso, você pode empacotar os módulos e distribui-los independentemente de seu add-on, tornando-os disponíveis para outros desenvolvedores de add-on e efetivamente extendendo o SDK.

Neste tutorial faremos exatamente isso com o módulo que calcula hashes de arquivo.

Um add-on de hashing

Uma função hash leva uma string de qualquer tamanho de bytes, e produz uma string curta e de tamanho fixo de bytes como saída. É um modo útil para criar um "fingerprint" que pode ser usado para identificar um arquivo. MD5 é uma função hash comumente usada: embora não seja considerada segura, ela trabalha bem desconsiderando o contexto da segurança.

Aqui nós escreveremos um add-on que deixa o usuário escolher uma arquivo no disco e calcula seu hash. Para ambas operações nós usaremos as interfaces XPCOM.

File picker

Para deixar o usuário selecionar um arquivo nós usaremos  o nsIFilePicker. A documentação para esta interface inclui um exemplo que nós podemos adaptar como este:

var {Cc, Ci} = require("chrome");

function promptForFile() {
  const nsIFilePicker = Ci.nsIFilePicker;

  var fp = Cc["@mozilla.org/filepicker;1"]
           .createInstance(nsIFilePicker);

  var window = require("sdk/window/utils").getMostRecentBrowserWindow();
  fp.init(window, "Select a file", nsIFilePicker.modeOpen);
  fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText);

  var rv = fp.show();
  if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
    var file = fp.file;
    // Pega o caminho como string. Note que você normalmente não
    // precisará trabalhar com strings de caminho.
    var path = fp.file.path;
    // Trabalhe com o retorno de nsILocalFile...
  }
  return path;
}

Função Hash

Firefox tem suporte embutido para funções hash, exposto via interface XPCOM nsICryptoHash. A página da documentação para esta interface inclui um exemplo de calculadora de hash MD5 do conteúdo do arquivo, dado seu caminho. Nós adaptamos como esta:

var {Cc, Ci} = require("chrome");

// retorna o código hexadecimal de dois dígitos para um byte
function toHexString(charCode) {
  return ("0" + charCode.toString(16)).slice(-2);
}

function md5File(path) {
  var f = Cc["@mozilla.org/file/local;1"]
          .createInstance(Ci.nsILocalFile);
  f.initWithPath(path);
  var istream = Cc["@mozilla.org/network/file-input-stream;1"]           
                .createInstance(Ci.nsIFileInputStream);
  // abrindo para leitura
  istream.init(f, 0x01, 0444, 0);
  var ch = Cc["@mozilla.org/security/hash;1"]
           .createInstance(Ci.nsICryptoHash);
  // nós queremos usar o algoritmo MD5
  ch.init(ch.MD5);
  // isto diz para updateFromStream ler o arquivo todo
  const PR_UINT32_MAX = 0xffffffff;
  ch.updateFromStream(istream, PR_UINT32_MAX);
  // passe false aqui para conseguir os dados binários de volta
  var hash = ch.finish(false);

  // converte o hash binário para hex string.
  var s = Array.from(hash, (c, i) => toHexString(hash.charCodeAt(i))).join("");
  return s;
}

Colocando tudo junto

O add-on completo adiciona um botão ao Firfox: quando o usuário clica no botão, nós pedimos lhe para selecionar  um arquivo, e registramos o hash no console:

var {Cc, Ci} = require("chrome");

// retorna o código hexadecimal de dois dígitos para um byte
function toHexString(charCode) {
  return ("0" + charCode.toString(16)).slice(-2);
}

function md5File(path) {
  var f = Cc["@mozilla.org/file/local;1"]
          .createInstance(Ci.nsILocalFile);
  f.initWithPath(path);
  var istream = Cc["@mozilla.org/network/file-input-stream;1"]           
                .createInstance(Ci.nsIFileInputStream);
  // abrindo para leitura
  istream.init(f, 0x01, 0444, 0);
  var ch = Cc["@mozilla.org/security/hash;1"]
           .createInstance(Ci.nsICryptoHash);
  // nós queremos usar o algoritmo MD5
  ch.init(ch.MD5);
  // isto diz para updateFromStream ler o arquivo todo
  const PR_UINT32_MAX = 0xffffffff;
  ch.updateFromStream(istream, PR_UINT32_MAX);
  // passe false aqui para conseguir os dados binários de volta
  var hash = ch.finish(false);

  // converte o hash binário para hex string.
  var s = Array.from(hash, (c, i) => toHexString(hash.charCodeAt(i))).join("");
  return s;
}

function promptForFile() {
  var window = require("sdk/window/utils").getMostRecentBrowserWindow();
  const nsIFilePicker = Ci.nsIFilePicker;

  var fp = Cc["@mozilla.org/filepicker;1"]
           .createInstance(nsIFilePicker);
  fp.init(window, "Select a file", nsIFilePicker.modeOpen);
  fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText);

  var rv = fp.show();
  if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
    var file = fp.file;
    // Pega o caminho como string. Note que você normalmente não 
    // precisará trabalhar com strings de caminho.
    var path = fp.file.path;
    // Trabalhe com o retorno de nsILocalFile...
  }
  return path;
}

require("sdk/ui/button/action").ActionButton({
  id: "show-panel",
  label: "Show Panel",
  icon: {
    "16": "./icon-16.png"
  },
  onClick: function() {
    console.log(md5File(promptForFile()));
  }
});

Isso funciona, mas main.js está agora ficando mais longo e sua lógica mais difícil de entender. This works , but main.js is now getting longer and its logic is harder to understand. Vamos levar os códigos do "file picker" e do "hashing code" para módulos separados.

Criando módulos separados

filepicker.js

Primeiro criamos um novo arquivo no diretório "lib" chamado "filepicker.js". Copiamos o código do seletor de arquivos, e adicionamos a seguinte linha de código no fim dele:

exports.promptForFile = promptForFile;

Isso define a interface pública do novo módulo.

Então "filepicker.js" deve parecer com isto:

var {Cc, Ci} = require("chrome");

function promptForFile() {
  var window = require("sdk/window/utils").getMostRecentBrowserWindow();
  const nsIFilePicker = Ci.nsIFilePicker;

  var fp = Cc["@mozilla.org/filepicker;1"]
           .createInstance(nsIFilePicker);
  fp.init(window, "Select a file", nsIFilePicker.modeOpen);
  fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText);

  var rv = fp.show();
  if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
    var file = fp.file;
    // Get the path as string. Note that you usually won't
    // need to work with the string paths.
    var path = fp.file.path;
    // work with returned nsILocalFile...
  }
  return path;
}

exports.promptForFile = promptForFile;

md5.js

Próximo, crie um outro arquivo no "lib", chamado "md5.js". Copie o código do hashing, e adicione esta linha ao seu fim:

exports.hashFile = md5File;

O arquivo completo parece com isto:

var {Cc, Ci} = require("chrome");

//retorna o código hexadecimal de dois dígitos para um byte
function toHexString(charCode) {
  return ("0" + charCode.toString(16)).slice(-2);
}

function md5File(path) {
  var f = Cc["@mozilla.org/file/local;1"]
          .createInstance(Ci.nsILocalFile);
  f.initWithPath(path);
  var istream = Cc["@mozilla.org/network/file-input-stream;1"]           
                .createInstance(Ci.nsIFileInputStream);
  // abrindo para leitura
  istream.init(f, 0x01, 0444, 0);
  var ch = Cc["@mozilla.org/security/hash;1"]
           .createInstance(Ci.nsICryptoHash);
  // nós queremos usar o algoritmo MD5
  ch.init(ch.MD5);
  // isto diz para updateFromStream ler o arquivo todo
  const PR_UINT32_MAX = 0xffffffff;
  ch.updateFromStream(istream, PR_UINT32_MAX);
  // passe false aqui para conseguir os dados binários de volta
  var hash = ch.finish(false);

  // converte o hash binário para hex string.
  var s = Array.from(hash, (c, i) => toHexString(hash.charCodeAt(i))).join("");
  return s;
}

exports.hashFile = md5File;

main.js

Finalmente, atualizamos o main.js para importar estes dois módulos e usá-los:

var filepicker = require("./filepicker.js");
var md5 = require("./md5.js");

require("sdk/ui/button/action").ActionButton({
  id: "show-panel",
  label: "Show Panel",
  icon: {
    "16": "./icon-16.png"
  },
  onClick: function() {
    console.log(md5.hashFile(filepicker.promptForFile()));
  }
});

Você pode distribuir estes módulos para outros desenvolvedores, também. Eles podem copia-los em algum lugar do add-on, e inclui-los usando require() do mesmo modo.

Aprendendo Mais

Para ver alguns módulos que as pessoas já desenvolveram, veja a página community-developed. Para aprender como usar módulos de terceiros em seu próprio código, veja o tutorial adicionando itens de menu.

Etiquetas do documento e colaboradores

Etiquetas: 
 Colaboradores desta página: arai, Pheanor
 Última atualização por: arai,