Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Bug 746131: CFX in JS - Use an SDK addon called by cfx in python that is going to install the xpi #410

Closed
wants to merge 19 commits into from

4 participants

@ochameau
Owner

Still a work in progress but feedback is welcomed.
(Based on top of PR #406)
https://bugzilla.mozilla.org/show_bug.cgi?id=746131

cfx/cfx.js
((19 lines not shown))
+}
+
+function main() {
+ let options = getOptions();
+
+ console.log("Install addon at : "+options.xpiPath);
+ AddonInstaller.install(options.xpiPath).then(function () {
+ console.log("Addon installed");
+ });
+}
+
+try {
+ main();
+}
+catch(e) {
+ console.log("CFX.js exception:\n" + e);
@Gozala Owner
Gozala added a note

maybe console.exception probably would be better

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/cfx.js
@@ -0,0 +1,36 @@
+const AddonInstaller = require("api-utils/addon/installer");
+const file = require("api-utils/file");
+const system = require("api-utils/system");
+
+function getOptions() {
+ let optionsFile = system.env["CFX_OPTIONS_FILE"];
+ if (!optionsFile) {
+ throw new Error("Unable to locate options file, environnement not set.");
+ }
+ let data = null;
+ try {
+ data = file.read(optionsFile);
@Gozala Owner
Gozala added a note

Maybe we should read this async as well (XHR) ?

@Gozala Owner
Gozala added a note

Has not @ZER0 landed a readURI util function yet ?

@ochameau Owner

Still nothing, bug 748086.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Gozala
Owner

I think it would be useful to add Readme.md to cfx folder explaining what it dose or how to use it. I'm not sure I understand how how to cause cfx to use it.

cfx/cfx.js
((16 lines not shown))
+ }
+ console.log("got options: "+data);
+ return JSON.parse(data);
+}
+
+function main() {
+ let options = getOptions();
+
+ console.log("Install addon at : "+options.xpiPath);
+ AddonInstaller.install(options.xpiPath).then(function () {
+ console.log("Addon installed");
+ });
+}
+
+try {
+ main();
@Gozala Owner
Gozala added a note

Might be worth doing following instead:

if (require.main === module) main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xulrunner-app/CommandLineHandler.js
@@ -0,0 +1,83 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
@Gozala Owner
Gozala added a note

Currently established convention for module names is hypen-delimited-name.js let's stick to it please.

@Gozala Owner
Gozala added a note

Ok, now I see that it's not really a module, but still not sure it's a good enough reason to don't follow conventions. Although I'm ok with having it as is if you feel very strongly about it (in that case probably .jsm would make more sense though)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/README.md
@@ -0,0 +1,101 @@
+cfx.js
+======
@Gozala Owner
Gozala added a note

Nit: We usually use # H1 instead of

H1
==
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/README.md
@@ -0,0 +1,101 @@
+cfx.js
+======
+
+cfx.js is an SDK addon meant to replace python command line application.
+This addon is currently quite limited but it is going to implement all features
+of cfx python application in order to remove python dependency.
+It currently just install an XPI file and run it into the Firefox.
@Gozala Owner
Gozala added a note

into -> in a

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/README.md
@@ -0,0 +1,101 @@
+cfx.js
+======
+
+cfx.js is an SDK addon meant to replace python command line application.
+This addon is currently quite limited but it is going to implement all features
+of cfx python application in order to remove python dependency.
+It currently just install an XPI file and run it into the Firefox.
+For now, it is not meant to be used standalone, it is only an utility addon
+used by cfx python application.
@Gozala Owner
Gozala added a note

I'd say helper addon used by an add-on sdk.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/README.md
((13 lines not shown))
+
+ * Activate your SDK environnement
+
+ $ source bin/activate (linux/mac)
+ - or -
+ $ bin\activate (window)
+
+ * Go to cfx.js directory
+
+ $ cd cfx/
+
+ * Generate cfx.js xpi
+
+ $ cfx xpi
+
+ * Eventually copy it to the xulrunner application template
@Gozala Owner
Gozala added a note

Either title or this section is confusing. Is it embedding into app ?
Or do you mean install instructions ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/README.md
((25 lines not shown))
+
+ $ cfx xpi
+
+ * Eventually copy it to the xulrunner application template
+
+ $ mv cfx.xpi xulrunner-app/
+
+How does it work
+================
+
+ * Set an environnement variable called `CFX_OPTIONS_FILE`
+ with an absolute path to a JSON file that contains data needed for actions
+ to be accomplished by this addon
+ * Run firefox with this addon installed, or run it through the xulrunner
+ template.
+ * The addon retrieve that JSON object and execute actions based on these data
@Gozala Owner
Gozala added a note

Maybe: The addon reads JSON from CFX_OPTIONS_FILE and performs tasks described by it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/README.md
((36 lines not shown))
+ with an absolute path to a JSON file that contains data needed for actions
+ to be accomplished by this addon
+ * Run firefox with this addon installed, or run it through the xulrunner
+ template.
+ * The addon retrieve that JSON object and execute actions based on these data
+
+(We aren't putting JSON content into environnement variable as its size is
+ limited)
+
+Expected JSON Data passed to cfx.js
+===================================
+
+ {
+ command: "string" // Name of the command to execute
+ options: "object" // Arguments needed to execute this command
+ }
@Gozala Owner
Gozala added a note

Is it a key value map ?

@Gozala Owner
Gozala added a note

Could you write some other (real) example this one is very confusing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/README.md
((38 lines not shown))
+ * Run firefox with this addon installed, or run it through the xulrunner
+ template.
+ * The addon retrieve that JSON object and execute actions based on these data
+
+(We aren't putting JSON content into environnement variable as its size is
+ limited)
+
+Expected JSON Data passed to cfx.js
+===================================
+
+ {
+ command: "string" // Name of the command to execute
+ options: "object" // Arguments needed to execute this command
+ }
+
+Available commands
@Gozala Owner
Gozala added a note

Supported commands

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/README.md
((45 lines not shown))
+Expected JSON Data passed to cfx.js
+===================================
+
+ {
+ command: "string" // Name of the command to execute
+ options: "object" // Arguments needed to execute this command
+ }
+
+Available commands
+==================
+
+== install-xpi ==
+
+ Install a given xpi into the currently running firefox/xulrunner application
+
+ Expected options:
@Gozala Owner
Gozala added a note

Required options

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/README.md
((47 lines not shown))
+
+ {
+ command: "string" // Name of the command to execute
+ options: "object" // Arguments needed to execute this command
+ }
+
+Available commands
+==================
+
+== install-xpi ==
+
+ Install a given xpi into the currently running firefox/xulrunner application
+
+ Expected options:
+ {
+ xpiPath: "string" // Absolute path to the xpi to install
@Gozala Owner
Gozala added a note

Would be nice if we could stick to same convention either "xpi-path" or "installXpi". Also I think it could be just path here as install-xpi makes it quite obvious.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/README.md
((41 lines not shown))
+
+(We aren't putting JSON content into environnement variable as its size is
+ limited)
+
+Expected JSON Data passed to cfx.js
+===================================
+
+ {
+ command: "string" // Name of the command to execute
+ options: "object" // Arguments needed to execute this command
+ }
+
+Available commands
+==================
+
+== install-xpi ==
@Gozala Owner
Gozala added a note

Nit: Not sure it's even valid markdown we usually use

## install-xpi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/README.md
((57 lines not shown))
+
+ Install a given xpi into the currently running firefox/xulrunner application
+
+ Expected options:
+ {
+ xpiPath: "string" // Absolute path to the xpi to install
+ }
+
+== build-xpi ==
+
+ Build an addon SDK addon.
+
+ Expected options:
+ {
+ xpiPath: "string" // Absolute path to the xpi we want to build
+ harnessRootDir: "string" // Absolute path to the temaplate folder.
@Gozala Owner
Gozala added a note

can it be just template ? or template-path / templatePath

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/README.md
((60 lines not shown))
+ Expected options:
+ {
+ xpiPath: "string" // Absolute path to the xpi to install
+ }
+
+== build-xpi ==
+
+ Build an addon SDK addon.
+
+ Expected options:
+ {
+ xpiPath: "string" // Absolute path to the xpi we want to build
+ harnessRootDir: "string" // Absolute path to the temaplate folder.
+ // All files from this folder are going to be
+ // written in the xpi
+ manifest: "string" // Content of the `install.rdf` to write in the xpi
@Gozala Owner
Gozala added a note

Can we call it something else but manifest ? It's pretty confusing since we'll be call manifest something completely different. Maybe install-rdf or something like that ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/README.md
((61 lines not shown))
+ {
+ xpiPath: "string" // Absolute path to the xpi to install
+ }
+
+== build-xpi ==
+
+ Build an addon SDK addon.
+
+ Expected options:
+ {
+ xpiPath: "string" // Absolute path to the xpi we want to build
+ harnessRootDir: "string" // Absolute path to the temaplate folder.
+ // All files from this folder are going to be
+ // written in the xpi
+ manifest: "string" // Content of the `install.rdf` to write in the xpi
+ harnessOptions: {
@Gozala Owner
Gozala added a note

harnessOptions was pretty unfortunate name, that we always wanted to change. Maybe we can fix this now ?

@ochameau Owner

I'm fine renaming this to manifest?
But I don't think it really worth modifying the final name of harness-options.json file in xpi.

@Gozala Owner
Gozala added a note

I'm fine renaming this to manifest?
But I don't think it really worth modifying the final name of harness-options.json file in xpi.

That's a different discussion, and I'd like to learn you're argument, but let's defer it for the future.

@ochameau Owner

Some scripts in the wild are unpacking xpi files and are expecting to find this manifest file.
https://github.com/mozilla/amo-validator
I wouldn't be surprised if these guys are reading it:
https://bugzilla.mozilla.org/show_bug.cgi?id=730776#c0
Flightdeck team are most likely playing with it.
There is a chance to break stuff in the wild although it doesn't add a big value to rename this file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/cfx.js
((22 lines not shown))
+ let { command, options } = getOptions();
+ if (command === "install-xpi") {
+ AddonInstaller.install(options.xpiPath)
+ .then(
+ null,
+ function onError(error) {
+ console.log("Failed to install addon: " + error);
+ });
+ }
+ else if (command === "build-xpi") {
+ xpi.build(options);
+ }
+ else if (command == "no-quit") {
+ // Test command in order to simulate a run that never quits
+ let { Cu } = require("chrome");
+ let { Services } = Cu.import("resource://gre/modules/Services.jsm");
@Gozala Owner
Gozala added a note

Could you please move imports to the top of the file ? I think you're trying to optimize here, but I think that JSM will be loaded anyway and require chrome is cheap.

@Gozala Owner
Gozala added a note

Also I think it would be much simpler and clear to use a service instead:

Cc['@mozilla.org/toolkit/app-startup;1'].
  getService(Ci.nsIAppStartup).
  enterLastWindowClosingSurvivalArea()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/cfx.js
((7 lines not shown))
+ let optionsFile = system.env["CFX_OPTIONS_FILE"];
+ if (!optionsFile) {
+ throw new Error("Unable to locate options file, environnement not set.");
+ }
+ let data = null;
+ try {
+ data = file.read(optionsFile);
+ }
+ catch(e) {
+ throw new Error("Unable to read options file: " + optionsFile + "\n" + e);
+ }
+ return JSON.parse(data);
+}
+
+function main() {
+ let { command, options } = getOptions();
@Gozala Owner
Gozala added a note

I think we should read files async, which could be delayed for the future, but API should be adjusted accordingly.

@ochameau Owner

I'd prefer waiting for such asynchronous file API to be here before changing the layout of this code. It should be straightforward to do later.

@Gozala Owner
Gozala added a note

The problem is not weather it will be simple change or not, problem is an API breakage, that's why I prefer to start with async API from the beginning.

@ochameau Owner

There is no API exposed here. Nothing is exported out of cfx.js. Am I misunderstanding something?

@Gozala Owner
Gozala added a note

Right, I have not realized that :) In that case sure feel free to keep as is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xulrunner-app/CommandLineHandler.js
((48 lines not shown))
+ // data object sent to bootstrap's startup method:
+ // An object crafted by AddonManager code, but jetpack only uses
+ // resourceURI attribute
+ let data = {
+ resourceURI: xpiJarURI
+ };
+
+ // load reason code, always use 'startup'
+ let reasonCode = 1;
+
+ // Fake AddonManager behavior by calling startup method
+ bootstrap.startup(data, reasonCode);
+
+ // Fake 'final-ui-startup' which is expected to fire in
+ // `api-utils/addon/runner.js`
+ Services.obs.notifyObservers(null, "final-ui-startup", null);
@Gozala Owner
Gozala added a note

I'm not sure it's a good idea. I think we should dispatch our custom notification and handle it from addon/runner

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xulrunner-app/CommandLineHandler.js
@@ -0,0 +1,83 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This XPCOM components allows to run a sdk addon as a xulrunner application
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+/*
@Gozala Owner
Gozala added a note

It's little confusing why do you you use https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsICommandLine for this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xulrunner-app/application.ini
@@ -0,0 +1,11 @@
+[App]
+Vendor=Mozilla
+Name=CFX in Javascript
@Gozala Owner
Gozala added a note

I think "Cuddlefish" would make more sense here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xulrunner-app/chrome.manifest
@@ -0,0 +1,3 @@
+component {537df286-d9ae-4c7a-a633-6266b1325289} CommandLineHandler.js
+contract @mozilla.org/stdoutapp/clh;1 {537df286-d9ae-4c7a-a633-6266b1325289}
+category command-line-handler x-default @mozilla.org/stdoutapp/clh;1
@ochameau Owner

I'm not following you here?
In any case, according to this documentation I changed x-default to m-cfx.
And used a more specific xpcom name @mozilla.org/cfx/clh;1

@Gozala Owner
Gozala added a note

I thought x-deafult was a special entry that was not documented on MDN, so I suggested to document it. But m-cfx is fine as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@ochameau
Owner

I've tried to address all your comments, except the one I replied to.
And I forgot to commit test file in the previous revision ...

cfx/README.md
((53 lines not shown))
+
+### install-xpi
+
+ Install a given xpi into the currently running firefox/xulrunner application
+
+ Required `options` object:
+ {
+ path: "string" // Absolute path to the xpi to install
+ }
+
+### build-xpi
+
+ Build an addon SDK addon.
+
+ Required `options` object:
+ {
@Gozala Owner
Gozala added a note

You're still mixing two naming conventions in the single JSON, please stick to either camel case or dash delimited naming convention. The later one seems to be more popular in JSON so I'd prefer that one.

@ochameau Owner

I used camel case in order to be able to use destructuring and/or regular attribute access like option.xpiPath/let { xpiPath } = options;, instead of only options["xpi-path"]. Having said that I'm fine about dash-delimited too.

@Gozala Owner
Gozala added a note

I see why you used camel case and it's convenient, but I still think that consistency is more important. So if you don't mind let's go with either dash-delimited keys or single word names. I think you could just use xpi, template, rdf as names.

@ochameau Owner

Renamed to dash-delimited. I kept long names as they are explicit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/README.md
@@ -0,0 +1,99 @@
+# cfx.js
+
+cfx.js is an SDK addon meant to replace python command line application.
+This addon is currently quite limited but it is going to implement all features
+of cfx python application in order to remove python dependency.
+It currently just allow to build an XPI file.
+For now, it is not meant to be used standalone, it is an helper addon used by
+add-on sdk `cfx` command line application.
+
+
+# How does it work
+
+ * cfx python application set `CFX_OPTIONS_FILE` environnement variable to an
+ absolute path to a JSON file that contains necessary information to execute
+ some commands,
@Gozala Owner
Gozala added a note

I don't think you want to end with , here.

@ochameau Owner

Actually I did. Isn't the same rule between french and english ?
When you have multiple bullets, you end up with coma for all bullets expect the last one, with a point.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/README.md
((6 lines not shown))
+It currently just allow to build an XPI file.
+For now, it is not meant to be used standalone, it is an helper addon used by
+add-on sdk `cfx` command line application.
+
+
+# How does it work
+
+ * cfx python application set `CFX_OPTIONS_FILE` environnement variable to an
+ absolute path to a JSON file that contains necessary information to execute
+ some commands,
+ * Then, cfx python application run it through the xulrunner template,
+ * Finally, the addon reads JSON from `CFX_OPTIONS_FILE` and performs tasks
+ described by it.
+
+(We aren't putting JSON content into environnement variable as the size of an
+ environnement variable is quite limited)
@Gozala Owner
Gozala added a note

I don't think such implementation details needs to be in readme. If you really want it, just add "(use JSON file since options may not fit in environment variable)" at the end of first bullet (line 15).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/README.md
((46 lines not shown))
+
+ {
+ command: "string" // Name of the command to execute
+ options: "object" // Arguments needed to execute this command
+ }
+
+## Supported commands
+
+### install-xpi
+
+ Install a given xpi into the currently running firefox/xulrunner application
+
+ Required `options` object:
+ {
+ path: "string" // Absolute path to the xpi to install
+ }
@Gozala Owner
Gozala added a note

I think more complete examples would help a lot:

{
  "command": "install-xpi",
  "options": {
    "path": "/path/to/addon.xpi"
  }
}

Maybe also note what the path format will be for windows.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/README.md
((59 lines not shown))
+ {
+ path: "string" // Absolute path to the xpi to install
+ }
+
+### build-xpi
+
+ Build an addon SDK addon.
+
+ Required `options` object:
+ {
+ xpiPath: "string" // Absolute path to the xpi we want to build
+ templatePath: "string" // Absolute path to the temaplate folder.
+ // All files from this folder are going to be
+ // written in the xpi
+ installRdf: "string" // Content of the `install.rdf` to write in the xpi
+ harnessOptions: {
@Gozala Owner
Gozala added a note

Maybe we can change harnessOptions to metadata ?

@ochameau Owner

Do you mind renaming this old beast in the next step ? i.e. when we are going to build the manifest from JS side.
Just because it is called harness options all over the place in python, so I'd like to avoid using two different names in two sides, nor modifying the whole python code.

@Gozala Owner
Gozala added a note

Sure, just was trying to come up with a better name, no rush though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/README.md
((82 lines not shown))
+ "test": section, // section name => absolute path to it
+ "lib": section
+ }
+ },
+ locale: { // A dictionnary of available locales for this addon
+ // keys are language code and values are another dictionnary
+ // with all translated strings
+ "en-US": { // language code
+ "key": "translation" // key to translate => translated key
+ }
+ }
+ // All these `harnessOptions` attributes are used to build the xpi
+ // but you can pass other attributes. They will be written into
+ // `harness-options.json` at xpi's root folder
+ },
+ extraHarnessOptions: "dictionnary" // A set of additional attributes to
@Gozala Owner
Gozala added a note

I think just extra would do here as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/cfx.js
@@ -0,0 +1,62 @@
+const { Cc, Ci } = require("chrome");
+const AddonInstaller = require("api-utils/addon/installer");
+const file = require("api-utils/file");
+const system = require("api-utils/system");
+const xpi = require("./xpi");
+const { CfxError } = require("./exception");
+
+function getOptions() {
+ let optionsFile = system.env["CFX_OPTIONS_FILE"];
+ if (!optionsFile) {
+ throw new Error("Unable to locate options file, environnement not set.");
@Gozala Owner
Gozala added a note

environnement -> environment

Also I'd say "environment variable is not set" instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/cfx.js
((3 lines not shown))
+const file = require("api-utils/file");
+const system = require("api-utils/system");
+const xpi = require("./xpi");
+const { CfxError } = require("./exception");
+
+function getOptions() {
+ let optionsFile = system.env["CFX_OPTIONS_FILE"];
+ if (!optionsFile) {
+ throw new Error("Unable to locate options file, environnement not set.");
+ }
+ let data = null;
+ try {
+ data = file.read(optionsFile);
+ }
+ catch(e) {
+ throw new Error("Unable to read options file: " + optionsFile + "\n" + e);
@Gozala Owner
Gozala added a note

Nit: We tend to just use Error without new.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/cfx.js
((10 lines not shown))
+ if (!optionsFile) {
+ throw new Error("Unable to locate options file, environnement not set.");
+ }
+ let data = null;
+ try {
+ data = file.read(optionsFile);
+ }
+ catch(e) {
+ throw new Error("Unable to read options file: " + optionsFile + "\n" + e);
+ }
+ return JSON.parse(data);
+}
+
+function main() {
+ let { command, options } = getOptions();
+ if (command === "install-xpi") {
@Gozala Owner
Gozala added a note

I would find it easier to read & maintain if it was something like:

commands[command] || commands.help(options)

Where commands is a dictionary of command functions that extract their options and do whatever they need to.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/cfx.js
((32 lines not shown))
+ }
+ else if (command === "build-xpi") {
+ xpi.build(options);
+ }
+ else if (command == "no-quit") {
+ // Test command in order to simulate a run that never quits
+ Cc['@mozilla.org/toolkit/app-startup;1'].
+ getService(Ci.nsIAppStartup).
+ enterLastWindowClosingSurvivalArea();
+ }
+ else {
+ console.log("Unknown cfxjs command '" + command + "'");
+ }
+}
+
+try {
@Gozala Owner
Gozala added a note

Also if you're ok with previous suggestion I would rather move try / catch into main itself so it will end up like:

function main() {
  try {
    let { command, options } = getOptions();
    commands[command] || commands.help(options)
  } catch (error) {
    if (error instanceof CfxError) {
      // Stacktrace isn't usefull in case of custom cfx exceptions,
      // prints custom message instead
      console.log(e);
    }
    else {
      console.error("Unknown internal error in cfx.js:");
      console.exception(e);
    }
    system.exit(system.E_FORCE);
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/exception.js
@@ -0,0 +1,23 @@
+// Set of custom exception class for cfx.js
+// Designed to be handled nicely from main module
+
+function CfxError(name) {
+ this.name = name;
+}
+CfxError.prototype = new Error();
@Gozala Owner
Gozala added a note

Please use CfxError.prototype = Object.create(Error.prototype) as new Error is not quite right.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/exception.js
@@ -0,0 +1,23 @@
+// Set of custom exception class for cfx.js
+// Designed to be handled nicely from main module
+
+function CfxError(name) {
@Gozala Owner
Gozala added a note

Honestly I don't see any value in making this prototype hierarchies. They just make whole thing look more complicated. This would have worked just fine:

function cfxError(message, name) {
  var error = Error(message);
  error.cfxError = true;
  error.name = name || 'CFXError'
  return error;
}

function InvalidArgument(message) {
  return cfxError(message, 'InvalidArgument')
}

function InternalCfxError(message) {
  return cfxError(message, 'InternalCfxError')
}
@Gozala Owner
Gozala added a note

Although in catch you would just did if (error.cfxError) { ...

@Gozala Owner
Gozala added a note

After reading code of other modules, I'm convinced that you don't even need this module. Just set error.cfxError = true on throw or define helper error functions in the appropriate modules.

@ochameau Owner

I'm expecting these exceptions to be used by all cfx modules, in order to classify kind of errors and print the most meaningfull error message we can. And especially distinguish user error from internal errors.
Otherwise, implementation wise, I've just followed this page: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error
I'm fine with dropping the prototype usage in favor of your code snippet, but I think it make sense to keep this module in order to reuse these normalized errors?

@Gozala Owner
Gozala added a note

I think what I was trying to say is that this complicates things, as you like to say users will have to learn about these error types. If only desire is to handle such errors differently we could just set isInternal or alike property and avoid a lot of complexity. If you want to have custom error types it's still ok by me (just make sure to use Object.create(Error.prototype) instead), although I would prefer more simpler approach.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/preferences.js
((71 lines not shown))
+ }
+
+ return DOMSerializer.serializeToString(root);
+}
+exports.generateOptionsXul = generateOptionsXul;
+
+function generatePrefsJS(options, jetpackId) {
+ let pref_list = []
+
+ for (let key in options) {
+ let pref = options[key];
+ if ('value' in pref) {
+ let value = pref.value;
+
+ // isFloat
+ // TODO: Understand why python code ignore float and only floats?
@Gozala Owner
Gozala added a note

Please use function form of typeof as suggested by style guide.

@Gozala Owner
Gozala added a note

Maybe @warner can tell us why python code was ignoring floats ?

@warner Owner
warner added a note

No idea. The code (python in python-lib/cuddlefish/options_defaults.py and JS in python-lib/cuddlefish/app-extension/bootstrap.js) was ignoring floats when I got there. Looks like Erik Vold added that in 299ec79. Can prefs be floats? The comments on that commit suggest they have to be integers.

@ochameau Owner

@erikvold would you be able to take a look at this and confirm that I implemented preferences correctly in JS?

@ochameau Owner

Please use function form of typeof as suggested by style guide.

https://github.com/mozilla/addon-sdk/wiki/Coding-style-guide
There isn't anything about typeof?

@Gozala Owner
Gozala added a note

There isn't anything about typeof?

Inherited from general mozilla style guide :)
https://developer.mozilla.org/En/Developer_Guide/Coding_Style#Operators

Good point, I'll add that to our style guide as well!

@erikvold Owner
Can prefs be floats? 

no, unless a string type is used.

@erikvold Owner
@erikvold would you be able to take a look at this and confirm that I implemented preferences correctly in JS?

Sure I'll take a look tonight.

@erikvold Owner

the preferences.js file looks good to me @ochameau I'll have time to test it out tomorrow night.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/preferences.js
((72 lines not shown))
+
+ return DOMSerializer.serializeToString(root);
+}
+exports.generateOptionsXul = generateOptionsXul;
+
+function generatePrefsJS(options, jetpackId) {
+ let pref_list = []
+
+ for (let key in options) {
+ let pref = options[key];
+ if ('value' in pref) {
+ let value = pref.value;
+
+ // isFloat
+ // TODO: Understand why python code ignore float and only floats?
+ if (typeof value == "number" && value % 1 !== 0)
@Gozala Owner
Gozala added a note

could you write helper function isFload and use it here if (isFloat(value)) .... would be much easier to understand.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/preferences.js
((74 lines not shown))
+}
+exports.generateOptionsXul = generateOptionsXul;
+
+function generatePrefsJS(options, jetpackId) {
+ let pref_list = []
+
+ for (let key in options) {
+ let pref = options[key];
+ if ('value' in pref) {
+ let value = pref.value;
+
+ // isFloat
+ // TODO: Understand why python code ignore float and only floats?
+ if (typeof value == "number" && value % 1 !== 0)
+ continue;
+ else if (typeof value == "boolean")
@Gozala Owner
Gozala added a note

Please prefer === unless type conversion is desired (as suggested by style guide)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/preferences.js
((77 lines not shown))
+function generatePrefsJS(options, jetpackId) {
+ let pref_list = []
+
+ for (let key in options) {
+ let pref = options[key];
+ if ('value' in pref) {
+ let value = pref.value;
+
+ // isFloat
+ // TODO: Understand why python code ignore float and only floats?
+ if (typeof value == "number" && value % 1 !== 0)
+ continue;
+ else if (typeof value == "boolean")
+ value = pref.value ? "true" : "false";
+ else if (typeof value == "string")
+ value = "\"" + pref.value.replace(/"/g, "\\\"") + "\"";
@Gozala Owner
Gozala added a note

Please add comment explaining what this does and more importantly why. Example of pref => normalizedPref would also be helpful.

@ochameau Owner

I refactored this code. It was the pure equivalent of python code, but we can use JSON.stringify instead.
The goal here is to stringify the preference value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Gozala Gozala commented on the diff
cfx/preferences.js
@@ -0,0 +1,103 @@
+const { Cc, Ci } = require("chrome");
@Gozala Owner
Gozala added a note

license block + "use strict"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Gozala Gozala commented on the diff
cfx/exception.js
@@ -0,0 +1,23 @@
+// Set of custom exception class for cfx.js
@Gozala Owner
Gozala added a note

Please "use strict"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Gozala Gozala commented on the diff
cfx/xpi.js
@@ -0,0 +1,271 @@
@Gozala Owner
Gozala added a note

license block + "use strict"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Gozala Gozala commented on the diff
cfx/test/test-xpi.js
@@ -0,0 +1,177 @@
@Gozala Owner
Gozala added a note

license block + "use strict"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Gozala Gozala commented on the diff
cfx/cfx.js
@@ -0,0 +1,62 @@
@Gozala Owner
Gozala added a note

license block + "use strict"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/preferences.js
((81 lines not shown))
+ let pref = options[key];
+ if ('value' in pref) {
+ let value = pref.value;
+
+ // isFloat
+ // TODO: Understand why python code ignore float and only floats?
+ if (typeof value == "number" && value % 1 !== 0)
+ continue;
+ else if (typeof value == "boolean")
+ value = pref.value ? "true" : "false";
+ else if (typeof value == "string")
+ value = "\"" + pref.value.replace(/"/g, "\\\"") + "\"";
+ else
+ value = pref.value;
+
+ prefKey = "extensions." + jetpackId + "." + pref.name;
@Gozala Owner
Gozala added a note

You forgot to define prefKey

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/preferences.js
((63 lines not shown))
+ pref.name + "');");
+ setting.appendChild(button);
+ }
+ else if (pref.type == "boolint") {
+ setting.setAttribute("on", pref.on);
+ setting.setAttribute("off", pref.off);
+ }
+ root.appendChild(setting);
+ }
+
+ return DOMSerializer.serializeToString(root);
+}
+exports.generateOptionsXul = generateOptionsXul;
+
+function generatePrefsJS(options, jetpackId) {
+ let pref_list = []
@Gozala Owner
Gozala added a note

camelCase please.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xpi.js
((4 lines not shown))
+const file = require("api-utils/file");
+const preferences = require("./preferences");
+const { InternalCfxError, InvalidArgument } = require("./exception");
+
+const FILE_SEPARATOR = require("api-utils/runtime").OS === "WINNT" ? "\\" : "/";
+
+// Utility function which allow to concatenate file path fragments
+// This function accept n-th arguments. It basically join given string with
+// the correct file separator depending on your OS
+function removeEmpty(value) {
+ return value !== ""
+}
+function joinPath() {
+ let list = Array.slice(arguments);
+ return list.filter(removeEmpty).join(FILE_SEPARATOR);
+}
@Gozala Owner
Gozala added a note

I"m fine this, but I think we should probably import node's path module. I have being using it intensively for linker too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/preferences.js
((16 lines not shown))
+ throw new InvalidArgument("The '" + pref.name + "' pref requires a 'title'");
+
+ // Make sure that the pref type is a valid inline pref type
+ if (VALID_PREF_TYPES.indexOf(pref.type) === -1)
+ throw new InvalidArgument(pref.type + " is not a valid inline pref type");
+
+ // Make sure the 'control' type has a 'label'
+ if (pref.type == "control" && !("label" in pref))
+ throw new InvalidArgument("The 'control' inline pref type requires a 'label'");
+
+ // TODO: Check that pref["type"] matches default value type
+ }
+}
+exports.validate = validate;
+
+function createXULDocument(content) {
@Gozala Owner
Gozala added a note

I think nsIDOMParser is the right way to go about this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/package.json
@@ -0,0 +1,7 @@
+{
+ "id": "cfx",
+ "main": "cfx.js",
@Gozala Owner
Gozala added a note

please use: "main": "./cfx.js". Also could you please add name and description ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xpi.js
((11 lines not shown))
+// This function accept n-th arguments. It basically join given string with
+// the correct file separator depending on your OS
+function removeEmpty(value) {
+ return value !== ""
+}
+function joinPath() {
+ let list = Array.slice(arguments);
+ return list.filter(removeEmpty).join(FILE_SEPARATOR);
+}
+
+// Utility function to ignore unwanted files in .xpi
+const IGNORED_FILE_PREFIXES = ["."];
+const IGNORED_FILE_SUFFIXES = ["~", ".swp"];
+const IGNORED_DIRS = [".git", ".svn", ".hg"];
+
+function isAcceptableDirectory(name) {
@Gozala Owner
Gozala added a note

I would rename to isRecognizedDirectory or isSupportedDirectory. Acceptable feels to harsh to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xpi.js
@@ -0,0 +1,271 @@
+const { Cc, Ci } = require("chrome");
+
+const { ZipWriter } = require("./zip");
+const file = require("api-utils/file");
@Gozala Owner
Gozala added a note

It's little strange that you use both nsIFile and this module wrapping it together. I'd suggest to stick either one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xpi.js
@@ -0,0 +1,271 @@
+const { Cc, Ci } = require("chrome");
+
+const { ZipWriter } = require("./zip");
+const file = require("api-utils/file");
+const preferences = require("./preferences");
+const { InternalCfxError, InvalidArgument } = require("./exception");
+
+const FILE_SEPARATOR = require("api-utils/runtime").OS === "WINNT" ? "\\" : "/";
+
+// Utility function which allow to concatenate file path fragments
+// This function accept n-th arguments. It basically join given string with
+// the correct file separator depending on your OS
+function removeEmpty(value) {
@Gozala Owner
Gozala added a note

It's little confusing to have comment of joinPath on top of this function. I would move it below this function and maybe add comment saying that it's internal utility used by joinPath.

@Gozala Owner
Gozala added a note

Also name of this function is very confusing. It does not removes anything it just checks if value is empty. I'd suggest to rename it to isNotEmpty instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xpi.js
((27 lines not shown))
+ return IGNORED_DIRS.indexOf(name) === -1;
+}
+
+function isAcceptableFile(name, blackList) {
+ for (let i = 0; i < IGNORED_FILE_PREFIXES.length; i++) {
+ let prefix = IGNORED_FILE_PREFIXES[i];
+ if (name.substr(0, prefix.length) == prefix)
+ return false;
+ }
+ for (let i = 0; i < IGNORED_FILE_SUFFIXES.length; i++) {
+ let suffix = IGNORED_FILE_SUFFIXES[i];
+ if (name.substr(-1 * suffix.length) == suffix)
+ return false;
+ }
+ return true;
+}
@Gozala Owner
Gozala added a note

I think it would be way simpler if you would leveraged Array methods instead:

function isIgnoredFile(name) {
  return IGNORED_FILE_PREFIXES.some(function(prefix) {
    return name.indexOf(prefix) === 0
  }) || IGNORED_FILE_SUFFIXES.some(function(suffix) {
    return name.substr(-1 * suffix.length) === suffix
  })
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Gozala Gozala commented on the diff
cfx/xpi.js
((43 lines not shown))
+
+// Utility function in order to read a folder recursively
+// Returns a list of all sub-directories. Each directory is represented with
+// an object contains following attributes:
+// - path: relative path the the directory (is an empty string for root folder)
+// - dirnames: a list of all directory names existing in this directory
+// - filenames: a list of all file names available in this directory
+function walkDir(path) {
+ let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+ try {
+ file.initWithPath(path);
+ } catch(e) {
+ throw new Error("This directory path is not valid : " + path + "\n" + e);
+ }
+ let directories = [];
+ function readDir(path, file) {
@Gozala Owner
Gozala added a note

I don't force to a change here, but I do think that leveraging array map / filter / reduce would made this a lot easier:

function isDirectory(entry) {
  return entry.isDirectory();
}

function isFile(entry) {
  return !isDirectory(entry)
}

function getLeafName(entry) {
  return entry.leafName
}

function readDir(path) {
  let file = MozFile(path)
  let entries = enumerator2array(file.directoryEntries)
  let dirs = entries.filter(isDirectory).map(getLeafName).filter(isAcceptableDirectory)
  let files = entries.filter(isFile).map(getLeafName).filter(isAcceptableFile)

  return dirs.reduce(function(result, name) {
    return result.concat(readDir(joinPath(path, name)))
  }, [{ path: path, dirnames: dirs, filenames: files }])
}
@Gozala Owner
Gozala added a note

After reading it even further I think you could make just return array of dir paths and file paths without creating these objects:

function readDir(path) {
  let file = MozFile(path)
  let entries = enumerator2array(file.directoryEntries)
  let dirs = entries.filter(isDirectory).
    map(getLeafName).
    filter(isAcceptableDirectory).
    map(function() { return joinPath(path, name); });

  let files = entries.filter(isFile).
    map(getLeafName).
    filter(isAcceptableFile).
    map(function() { return joinPath(path, name); });

   let nested = dirs.map(readDir);

   return { dirs: dirs.concat(nested.dirs), files: files.concat(nested.files) }
}
@ochameau Owner

walkDir behaves like os.walk from python: http://www.saltycrane.com/blog/2007/03/python-oswalk-example/
The only difference is that path is a relative path from the original path given to walkDir. (Just because we don't have relpath function and I didn't wanted to implement it)

Here it looks like you return all directories and all files without being able to figure out from which folder each of these are.
The first version seems to implement the same behavior than os.walk.

I'm not sure it is easier to follow especially in lines around reduce usage. Whereas the original code can surely be read by any guy used to read js xpcom code. I'd really like that jetpack code can be easily read/patched/maintained by any mozilla-central devs and that we end up using similar pratices to existing mozilla-central code:
http://mxr.mozilla.org/mozilla-central/search?string=.directoryEntries&find=\.js&findi=&filter=^[^\0]*%24&hitlimit=&tree=mozilla-central
Not necessary all SDK code, but especially the low level one, which interact with xpcom and can be broken by new mozilla-central revision. If we would like to be integrated with other mozilla teams, we will need to unhide our unit tests on tinderbox and allow them to understand what they broke and why. So that they can land fixes in addon-sdk repo while landing a modification in mozilla-central. I'm considering such abstraction on top of xpcom interfaces as being negative about this, even if it can be usefull for new addon developers.

@Gozala Owner
Gozala added a note

Anyway it was just a suggestion that IMO would have improved maintainability, but I'm ok leaving as is. On your argument regarding moz-central, I'm not fully convinced, we do try to innovate which is very hard without doing things old way. Not very important here anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xpi.js
((81 lines not shown))
+// Utility function for zip library usage. zip library only accept `/` as file
+// separator for relative in-zip paths.
+function makeZipPath(path) {
+ return path.split(FILE_SEPARATOR).join("/");
+}
+
+// Return a pretty printed JSON string with 2 spaces indentation
+function prettyPrintJSON(json) {
+ return JSON.stringify(json, null, 2);
+}
+
+// Write icon files
+function writeIcons(zip, harnessOptions) {
+ if ('icon' in harnessOptions) {
+ zip.addFile("icon.png", harnessOptions.icon);
+ delete harnessOptions.icon;
@Gozala Owner
Gozala added a note

Why delete ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xpi.js
((95 lines not shown))
+ zip.addFile("icon.png", harnessOptions.icon);
+ delete harnessOptions.icon;
+ }
+ if ('icon64' in harnessOptions) {
+ zip.addFile("icon64.png", harnessOptions.icon64);
+ delete harnessOptions.icon64;
+ }
+}
+
+// Write default preferences and xul options document (opened from about:addons)
+function writePreferences(zip, jetpackId, preferences) {
+ // Handle `preferences` from package.json
+ if (preferences) {
+ preferences.validate(preferences);
+
+ opts_xul = preferences.generateOptionsXul(preferences,
@Gozala Owner
Gozala added a note

you introduce global here and please use camelCase. No need to follow python that closely :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xpi.js
((99 lines not shown))
+ zip.addFile("icon64.png", harnessOptions.icon64);
+ delete harnessOptions.icon64;
+ }
+}
+
+// Write default preferences and xul options document (opened from about:addons)
+function writePreferences(zip, jetpackId, preferences) {
+ // Handle `preferences` from package.json
+ if (preferences) {
+ preferences.validate(preferences);
+
+ opts_xul = preferences.generateOptionsXul(preferences,
+ jetpackID);
+ zip.addData("options.xul", opts_xul);
+
+ prefs_js = preferences.generatePrefsJS(preferences,
@Gozala Owner
Gozala added a note

global!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xpi.js
((147 lines not shown))
+ // another dictionnary whose keys are section name (lib or test) and final
+ // value is absolute path to this package's section
+ zip.mkdir("resources");
+ for (let packageName in packages) {
+ // Always write the top directory, even if it contains no files, since
+ // the harness will try to access it.
+ zip.mkdir('resources/' + packageName);
+ for (let sectionName in packages[packageName]) {
+ let abs_dirname = packages[packageName][sectionName]
+ let base_arcpath = ["resources", packageName, sectionName].join("/")
+ // Always write the top directory, even if it contains no files, since
+ // the harness will try to access it.
+ zip.mkdir(base_arcpath);
+ // cp -r stuff from abs_dirname/ into ZIP/resources/RESOURCEBASE/
+ for each (let directory in walkDir(abs_dirname)) {
+ let goodfiles = directory.filenames;
@Gozala Owner
Gozala added a note

This name is confusing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xpi.js
((130 lines not shown))
+ for each(let name in directory.dirnames) {
+ let relpath = joinPath(directory.path, name);
+ zip.mkdir(makeZipPath(relpath));
+ }
+ for each(let name in directory.filenames) {
+ if (["install.rdf", "application.ini"].indexOf(name) !== -1)
+ continue;
+ let relpath = joinPath(directory.path, name);
+ let abspath = joinPath(templatePath, relpath);
+ zip.addFile(makeZipPath(relpath), abspath);
+ }
+ }
+}
+
+// Write all packages to resources/ folder
+function writePackages(zip, packages, limitTo, xpiPath) {
@Gozala Owner
Gozala added a note

Please add comments describing arguments, it's far from obvious in this case. Also If I understand limitTo correctly it should probably be named relativeTo instead.

@ochameau Owner

limitTo is the white-list of file to accept in xpi file, used to implement strip-xpi feature.
I've added comment on top of all write* functions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xpi.js
((186 lines not shown))
+ }
+ }
+ }
+ }
+}
+
+// Write locale manifest and locale files to the xpi
+function writeLocales(zip, locales) {
+ // We store a sorted list of locales
+ let languages = Object.keys(locales).sort();
+ let localesManifest = {
+ locales: languages
+ };
+ zip.addData("locales.json", prettyPrintJSON(localesManifest) + "\n");
+ zip.mkdir("locale/");
+ for each(let language in languages) {
@Gozala Owner
Gozala added a note

Please prefer standard array.forEach over non-standard for each

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xpi.js
((198 lines not shown))
+ };
+ zip.addData("locales.json", prettyPrintJSON(localesManifest) + "\n");
+ zip.mkdir("locale/");
+ for each(let language in languages) {
+ let locale = locales[language];
+ zip.addData("locale/" + language + ".json", prettyPrintJSON(locale));
+ }
+}
+
+// Write `harness-options.json` manifest file at xpi root
+function writeManifest(zip, harnessOptions, extraHarnessOptions) {
+ // TODO: print better error message on harness-options loading error
+ // "Error: Component returned failure code: 0x80520012 (NS_ERROR_FILE_NOT_FOUND) [nsIChannel.open]"
+
+ // Include extra manifest options given manually to cfx
+ for (let key in extraHarnessOptions) {
@Gozala Owner
Gozala added a note

Would not it make more sense to combine extraHarnessOptions with harnessOptions itself ? What's the point of accepting two different options that get combined later anyway ?

@ochameau Owner

extraHarnessOptions is given by the user while harnessOptions is fully computed by python code.
I'd prefer keeping it as-is in order to avoid combining them in python side.
It is something we will easily cleanup when we will build a cleaner manifest from js side.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xpi.js
((230 lines not shown))
+ throw new InternalCfxError(
+ "Missing `templatePath` in cfxjs options");
+ if (!("locale" in options.harnessOptions))
+ throw new InternalCfxError(
+ "Missing `locale` in cfxjs options harnessOptions attribute");
+ let harnessOptions = options.harnessOptions;
+
+ let zip = new ZipWriter(options.xpiPath);
+
+ try {
+ zip.addData("install.rdf", options.installRdf);
+
+ writeIcons(zip, harnessOptions);
+
+ writePreferences(zip, harnessOptions.jetpackID,
+ harnessOptions.preferences ? harnessOptions.preferences
@Gozala Owner
Gozala added a note

Why not harnessOptions.preferences || null ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xpi.js
((232 lines not shown))
+ if (!("locale" in options.harnessOptions))
+ throw new InternalCfxError(
+ "Missing `locale` in cfxjs options harnessOptions attribute");
+ let harnessOptions = options.harnessOptions;
+
+ let zip = new ZipWriter(options.xpiPath);
+
+ try {
+ zip.addData("install.rdf", options.installRdf);
+
+ writeIcons(zip, harnessOptions);
+
+ writePreferences(zip, harnessOptions.jetpackID,
+ harnessOptions.preferences ? harnessOptions.preferences
+ : null);
+ delete harnessOptions.preferences;
@Gozala Owner
Gozala added a note

Why delete ? Please at least document! Some for all the following deletes.

@ochameau Owner

There is some delete in order to avoid writing these attribute into harness-options.json file.
I've added some comment on top of this one, I hope it is clear for following ones?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xpi.js
((252 lines not shown))
+ options.xpiPath);
+ delete harnessOptions.packages;
+
+ writeLocales(zip, harnessOptions.locale);
+ delete harnessOptions.locale;
+
+ writeManifest(zip, harnessOptions, options.extraHarnessOptions);
+ }
+ catch(e) {
+ // In case or any error, we delete the eventually created xpi file
+ // in order to avoid processing it in futher steps in python code
+ zip.close();
+ try {
+ file.remove(options.xpiPath);
+ }
+ catch(e) {};
@Gozala Owner
Gozala added a note

Capturing error and doing nothing with it is no good, either don't catch or do something with it. If this error is really useless then use finally { throw e } instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Gozala Gozala commented on the diff
cfx/xulrunner-app/application.ini
@@ -0,0 +1,11 @@
+[App]
+Vendor=Mozilla
+Name=Cuddlefish in Javascript
+Version=1.0
+BuildID=20120101
+Copyright=
+ID=cfx-js@mozilla.org
@Gozala Owner
Gozala added a note

Can we use cfx-js in all other places instead of cfx.js then ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Gozala Gozala commented on the diff
cfx/xulrunner-app/command-line-handler.js
((44 lines not shown))
+ Services.scriptloader.loadSubScript(
+ xpiJarURI.spec + "bootstrap.js",
+ bootstrap);
+
+ // data object sent to bootstrap's startup method:
+ // An object crafted by AddonManager code, but jetpack only uses
+ // resourceURI attribute
+ let data = {
+ resourceURI: xpiJarURI
+ };
+
+ // load reason code, always use 'enable'
+ let reasonCode = 3;
+
+ // Fake AddonManager behavior by calling startup method
+ bootstrap.startup(data, reasonCode);
@Gozala Owner
Gozala added a note

I'm not convinced that pretending that it's enable is a good idea, I think we should use our own reasonCode to make it possible to handle this case differently and to avoid confusing other parts of the platform.

@ochameau Owner

Any new custom reason code will have to be handled differently in bootstrap.js and/or addon/runner, I'd like to avoid that.
I don't see why we would have different startup code between running as xulrunner app, firefox addon, thunderbird addon, ...
But I can easily agree that using enable is a bit hacky as it is enabled during startup. So ideally we would just use startup reason. But in case of startup, addon/runner is waiting for some event to happen before running main module.
In previous patch version, I used startup and dispatched the expected event.
What do you think about modifying addon/runner in order to use startup but avoid waiting for any event?
I'm wondering if it make sense to listen to any event in * case:
https://github.com/mozilla/addon-sdk/blob/master/packages/api-utils/lib/addon/runner.js#L15

@Gozala Owner
Gozala added a note

We can talk about changing how / when do we bootstrap add-ons, but I'd rather have that discussion under separate thread and definitely as separate step. I don't like enable or startup as both may be fired by an actual platform causing strange behaviors. In general pretending that we do something that we don't asks for problems.

I think change to handle diff stratup reason can be very minimal. What is actually a reason you'd like to avoid that ?

@ochameau Owner

For the same reason that forces us to have a buildstep. I thought that it was important to build and use cfx-js as a 100% regular SDK addon. This command-line-handler.js component just fakes AddonManager action. It is like having cfx-js.xpi installed in this xulrunner app. Note that no other component other have access to addon's bootstrap.js. So no other startup event can be fired to it.
I could have just use AddonManager API in order to install/run the addon. Then I'd not have been able to change the reason code.

In term of dogfooding, it is quite important to avoid using any different path from regular addons. In this particular case, I don't see why running an addon from xulrunner (instead of firefox) should change the startup reason code ? What would be this startup code ? command-line-startup ?

I may see your point. You want to do different actions during bootstrap based on the platform, then I'm not sure reason code should be used for that. We may prefer application id or something else.

@Gozala Owner
Gozala added a note

I think we can start with this and then change if turns out to be problematic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xulrunner-app/command-line-handler.js
((55 lines not shown))
+ // load reason code, always use 'enable'
+ let reasonCode = 3;
+
+ // Fake AddonManager behavior by calling startup method
+ bootstrap.startup(data, reasonCode);
+ }
+ catch(e) {
+ let msg = "Exception while running boostrap.js:\n" + e + "\n" + e.stack;
+ dump(msg);
+ Cu.reportError(msg);
+ }
+}
+
+// Register a nsICommandLineHandler xpcom object in order to call
+// runApplication method
+function CommandLineHandler() {}
@Gozala Owner
Gozala added a note

I still don't understand why we register nsICommandLineHandler if we don't handle command line arguments.

@ochameau Owner

It would allow us to handle them in a next step.
One alternative would be to use another kind of xpcom that will register itself in a category to instanciate it at app startup.
But as soon as we will have to handle command line arguments, we will have to use this form back.

@Gozala Owner
Gozala added a note

I don't really object to using nsICommandLineHandler it was just unclear why, maybe you could put your comment above as a comment somewhere so it's clear for people wondering.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Gozala Gozala commented on the diff
cfx/zip.js
((13 lines not shown))
+const { COMPRESSION_DEFAULT } = Ci.nsIZipWriter;
+
+function createNsFile(path) {
+ let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+ try {
+ file.initWithPath(path);
+ } catch(e) {
+ throw new Error("This zip file path is not valid : " + path + "\n" + e);
+ }
+ return file;
+}
+
+const converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
+ createInstance(Ci.nsIScriptableUnicodeConverter);
+converter.charset = "UTF-8";
+function streamForData(data) {
@Gozala Owner
Gozala added a note

I think nsIStringInputStream is more efficient and more appropriate alternative. For examples see:
https://developer.mozilla.org/en/Creating_Sandboxed_HTTP_Connections#Creating_HTTP_POSTs

@ochameau Owner

It doesn't seem to accept exotic UTF8 characters.
@Mossop, any advice about utf8 string and xpcoms? :)

  let inputStream = Cc["@mozilla.org/io/string-input-stream;1"]  
                  .createInstance(Ci.nsIStringInputStream);
  inputStream.setData(data, data.length);
  return inputStream;
  const converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
                  createInstance(Ci.nsIScriptableUnicodeConverter);
  converter.charset = "UTF-8";
  return converter.convertToInputStream(data);

I'm currently using second form in order to build an inputstream for nsIZipWriter:
zw.addEntryStream(pathInZip, 0, COMPRESSION_DEFAULT, stream, false);
data is a string containing wild UTF8 characters.
While second form works, the first doesn't throw any exception, but the file isn't written in zip file.

@Gozala Owner
Gozala added a note

That is strange, I would have expected nsIStringInputStream to inherit whatever encoding string was using, but yeah I never understood when to use which stream. If that does not works lets leave as is then. Although if we could get someone to explain which stream should be used when that would be great.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Gozala Gozala commented on the diff
cfx/zip.js
((17 lines not shown))
+ try {
+ file.initWithPath(path);
+ } catch(e) {
+ throw new Error("This zip file path is not valid : " + path + "\n" + e);
+ }
+ return file;
+}
+
+const converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
+ createInstance(Ci.nsIScriptableUnicodeConverter);
+converter.charset = "UTF-8";
+function streamForData(data) {
+ return converter.convertToInputStream(data);
+}
+
+exports.ZipWriter = function (zipPath, mode) {
@Gozala Owner
Gozala added a note

I personally don't see much value in building such class like a construct and would prefer more node fs-ish API:

exports.open = function(path, mode) {}
exports.mkdir = function(zd, path) {}
exports.addFile = function(zd, to, from) {}
exports.write = function(zd, path, content) {}
exports.close = function(zd) {}

That being said if we do it, it definitely would make more sense as an incremental step after.

@ochameau Owner

Sure, I can change this. What should openreturn?
Some empty object that will be used to fetch the nsIZipWriter instance via namespace, or the nsIZipWriter instance itself directly, or some other object?

@Gozala Owner
Gozala added a note

I'm fine with either of these options. I'd probably suggest something like:

// Maybe just define some private name in module
var name = Math.random().toString(32).substr(2)

// And use that name to define internal properties
return Object.defineProperty({}, name, {
  value: nsIZipWriter
})
@Gozala Owner
Gozala added a note

It also maybe worth adapting ES.next private names shim I wrote until we get an actual one
once bug 645416 is fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Gozala
Owner

I have pointed out various stuff in around, some of which are just a remarks. You should definitely make sure to add "use strict" and license blocks to all JS files, that would have could all of the undeclared variables that I've pointed out. I took at the python code as well, but it would be great if someone with more expertise could take a look at it as well, maybe @warner or @mhammond could find some time for that.

I'm not quite comfortable with a build step that users have to perform and wonder If there is something we could do about it to avoid that.

Feel free to request a meeting if you want to go through thee together faster ;)

@Gozala
Owner

I have pointed out various stuff in around, some of which are just a remarks. You should definitely make sure to add "use strict" and license blocks to all JS files, that would have could all of the undeclared variables that I've pointed out. I took at the python code as well, but it would be great if someone with more expertise could take a look at it as well, maybe @warner or @mhammond could find some time for that.

I'm not quite comfortable with a build step that users have to perform and wonder If there is something we could do about it to avoid that.

Feel free to request a meeting if you want to go through thee together faster ;)

@ochameau
Owner

I addressed all comments except ones where I've added comments + the one about nsIDOMParser (haven't had time to looked at it - #410 (comment))

cfx/preferences.js
((83 lines not shown))
+ return typeof(value) == "number" && value % 1 !== 0;
+}
+
+/**
+ * Based on preferences manifest written in package.json file of an addon,
+ * returns the necessary prefs.js file content. This file is going to set
+ * default preference values when the addon will be installed.
+ */
+function generatePrefsJS(options, jetpackId) {
+ let prefList = []
+
+ for (let key in options) {
+ let pref = options[key];
+ if (!('value' in pref))
+ continue;
+ // TODO: Understand why python code ignore float and only floats?
@erikvold Owner

the only acceptable types are bool, int, and string. So that is why floats were ignored. It might be better to throw a warning, or error here tho, and the other acceptable JSON types (ie: object, array, and null) should be treated the same as a float.

@ochameau Owner

I've updated this patch again in order to accept only bool, int or string and throw an error otherwise.
Feel free to tell me if there is still something wrong.

@Gozala Owner
Gozala added a note

I think this would have being little easier:

if (!isPrimitive(pref.value) || isFloat(pref.value)) { ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@ochameau
Owner

About build step. I agree it is kind of painfull to have such build step. The benefit is that cfx.js is just a regular SDK "addon". And if we want to keep it as being a regular SDK addon, we have to face the mandatory build step.
We can make it easier with some build-update script. Otherwise we can do some hacks like this:
ochameau@ochameau:cfx.js...ochameau:cfx.js-no-build
Where we instanciate a custom loader to make it work dynamically. But I'd prefer to avoid such thing. I don't think it is a good idea to maintain such code in cfx itself. We should either integrate such behavior into the SDK itself or at least modify bootstrap/cuddlefish/loader in order to make it super easy. (In this experiement cfx.js works, but I'm lacking of addon-kit, self support, locales, ...)
Would it be something we can later iterate on?

@Gozala
Owner

Would it be something we can later iterate on?

This is huge change for a users, I believe decisions on weather we should or not do such changes is up to project manager @dcm-moz / @canuckistani I personally would rather see custom cfx-js specefic loader instance than a build step. Alternatively we could do bundle build add-on or do it transparently from users.

We should either integrate such behavior into the SDK itself or at least modify bootstrap/cuddlefish/loader in order to
make it super easy

Do you have something specific in mind ?

cfx/xpi.js
((187 lines not shown))
+ * List of all white-listed files. If given, only file whose absolute path
+ * is in this list are copied into the xpi.
+ * @param {String} xpiPath
+ * Absolute path to the xpi file. Used to avoid copying the xpi in itself!
+ */
+function writePackages(zip, packages, limitTo, xpiPath) {
+ // `packages` is a dictionnary whose keys are package name and values are
+ // another dictionnary whose keys are section name (lib or test) and final
+ // value is absolute path to this package's section
+ zip.mkdir("resources");
+ for (let packageName in packages) {
+ // Always write the top directory, even if it contains no files, since
+ // the harness will try to access it.
+ zip.mkdir('resources/' + packageName);
+ for (let sectionName in packages[packageName]) {
+ let abs_dirname = packages[packageName][sectionName]
@Gozala Owner
Gozala added a note

camelCase please, here and below. Also little more descriptive names would help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/xpi.js
((314 lines not shown))
+ if (!("limit-to" in options))
+ throw new InternalCfxError(
+ "Missing `limit-to` in cfxjs options");
+ let limitTo = options["limit-to"];
+
+ let zip = new ZipWriter(xpiPath);
+
+ try {
+ zip.addData("install.rdf", installRdf);
+
+ writeIcons(zip, harnessOptions);
+
+ writePreferences(zip, harnessOptions.jetpackID,
+ harnessOptions.preferences || null);
+ // We delete harnessOptions attributes in order to avoid writing them
+ // into xpi's manifest file.
@Gozala Owner
Gozala added a note

I would honestly prefer something like serialize(harnessOptions) that would omit keys that should not be written (or maybe in writeManifest itself can do it). That way cherry-picking of properties is encapsulated in one location that is easy to find / maintain in contrast to deletes all over the place.

@ochameau Owner
ochameau added a note

I pushed new revision with some modification on python side in order to avoid adding such attribute in first place.
So that harnessOptions will only contain attribute that have to end up in harness-options.json file.
All others (locale, packages, icon, icon64) are now in options object.
This changed allowed me to prepare the next step. So now the JS side receive SDK and Addon absolute path,
so that you can start building manifest in JS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Gozala Gozala commented on the diff
cfx/xpi.js
((334 lines not shown))
+ writePackages(zip, harnessOptions.packages, limitTo, xpiPath);
+ delete harnessOptions.packages;
+
+ writeLocales(zip, harnessOptions.locale);
+ delete harnessOptions.locale;
+
+ writeManifest(zip, harnessOptions, extraHarnessOptions);
+ }
+ catch(e) {
+ // In case or any error, we delete the eventually created xpi file
+ // in order to avoid processing it in futher steps in python code
+ zip.close();
+ try {
+ // We do not care about any error while removing the xpi file,
+ // we try to ensure that we do not leave any temporary file around.
+ // It may not even be created.
@Gozala Owner
Gozala added a note

What's a point of catching and re-throwing more detailed errors from rm if they are ignored anyway ?

@ochameau Owner
ochameau added a note

Here I ignore errors from rm but not errors from the upper try/catch statement. i.e. errors happening in one of these writeSomething functions. I'm using finally as suggested in previous comment #410 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Gozala
Owner

I made few minor points and I think only few things need to be sorted out before we can land:

  • Need to make sure that build step is fine by project managers and there is no better way to go about this.
  • Document why nsICommandLineHandler is used (comments).

In addition you might want to:

  • Get someone to do a python review, I looked at it and code makes sense to me, but I don't have enough expertise to tell if you do something in a way that considered to be a bad practice or if it will work for all python versions etc..
  • I don't know if you still plan to do this https://github.com/mozilla/addon-sdk/pull/410/files#r1047385 or if you were planning to do it in the separate step.

Happy to see this coming along nicely!

@ochameau
Owner

I'm not sure it is clear about the build step. You won't have to build cfx.js if you want to use the SDK, but only if you want to patch cfx javascript code!

@Gozala
Owner

I'm not sure it is clear about the build step. You won't have to build cfx.js if you want to use the SDK, but only if you want > to patch cfx javascript code!

Ok then I clearly don't understand how this works :( Could we maybe talk on vidyo tomorrow morning I think little go through would be very helpful.

cfx/cfx.js
@@ -0,0 +1,80 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { Cc, Ci } = require("chrome");
+const AddonInstaller = require("api-utils/addon/installer");
+const file = require("api-utils/file");
+const system = require("api-utils/system");
+const xpi = require("./xpi");
+
+function getOptions() {
+ let optionsFile = system.env["CFX_OPTIONS_FILE"];
@Gozala Owner
Gozala added a note

Nit: Why not just system.env.CFX_OPTIONS_FILE ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/preferences.js
((18 lines not shown))
+function validate(options) {
+ for (let key in options) {
+ let pref = options[key];
+ // Make sure there is a 'title'
+ if (!("title" in pref))
+ throw new InvalidArgument("The '" + pref.name + "' pref requires a 'title'");
+
+ // Make sure that the pref type is a valid inline pref type
+ if (VALID_PREF_TYPES.indexOf(pref.type) === -1)
+ throw new InvalidArgument(pref.type + " is not a valid inline pref type");
+
+ // Make sure the 'control' type has a 'label'
+ if (pref.type == "control" && !("label" in pref))
+ throw new InvalidArgument("The 'control' inline pref type requires a 'label'");
+
+ // TODO: Check that pref["type"] matches default value type
@Gozala Owner
Gozala added a note

Wanna subbmit a follow-up bug for this and put a link here, to make sure we do that at some point ?

@ochameau Owner
ochameau added a note

Filled: https://bugzilla.mozilla.org/show_bug.cgi?id=772126
This TODO comes from python code. Good candidate for a follow-up!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
cfx/preferences.js
((24 lines not shown))
+
+ // Make sure that the pref type is a valid inline pref type
+ if (VALID_PREF_TYPES.indexOf(pref.type) === -1)
+ throw new InvalidArgument(pref.type + " is not a valid inline pref type");
+
+ // Make sure the 'control' type has a 'label'
+ if (pref.type == "control" && !("label" in pref))
+ throw new InvalidArgument("The 'control' inline pref type requires a 'label'");
+
+ // TODO: Check that pref["type"] matches default value type
+ }
+}
+exports.validate = validate;
+
+function createXULDocument(content) {
+ let str = "<?xml version=\"1.0\"?>" + content;
@Gozala Owner
Gozala added a note

Nit: not sure it's worth appending this header to the content, I think content should probably come with it's own.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Gozala
Owner

@ochameau Thanks for the hard work. I think it's looking good now!
I still would like to hold on landing till our weekly meeting so we can discuss what's the best time / place for this to land. With that it would be also great if we could have a vidyo meeting on Monday where we could walk though the whole patch to make sure I don't miss(understood) something.

@ochameau
Owner

Some late changes. I had to fix an issue in preferences support. nsIDOMParser/nsIDOMSerializer are throwing errors with XUL document. options.xul file were badly generated.
I discovered that during a stress test session. I've tweaked the code a bit in order to avoid generating different files.
For example, python code sort JSON object keys and used only one space for indentation.
I'd like to avoid introducing any unecessary difference in order to ease automatic repacking.

@Gozala
Owner

I just was trying to integrate my cfx-js work with this and run into issues when running tests. For whatever reason it fails with a following trace:

Running tests on Firefox 18.0a1/Gecko 18.0a1 ({ec8030f7-c20a-464f-9b0e-13a3a9e97384}) under darwin/x86.
error: TEST FAILED: test-xpi.testIt (exception)
error: An exception occurred.
Traceback (most recent call last):
  File "resource://cfx-at-jetpack/api-utils/lib/timer.js", line 28, in notify
    callback.apply(null, args);
  File "resource://cfx-at-jetpack/test-harness/lib/run-tests.js", line 59, in null
    onDone: onDone});
  File "resource://cfx-at-jetpack/test-harness/lib/harness.js", line 314, in runTests
    nextIteration();
  File "resource://cfx-at-jetpack/test-harness/lib/harness.js", line 240, in nextIteration
    onDone: nextIteration
  File "resource://cfx-at-jetpack/api-utils/lib/unit-test.js", line 20, in findAndRunTests
    function (tests) {
  File "resource://cfx-at-jetpack/api-utils/lib/unit-test-finder.js", line 73, in findTests
    cb(tests);
  File "resource://cfx-at-jetpack/api-utils/lib/unit-test.js", line 23, in null
    onDone: options.onDone});
  File "resource://cfx-at-jetpack/api-utils/lib/unit-test.js", line 417, in startMany
    runNextTest(this);
  File "resource://cfx-at-jetpack/api-utils/lib/unit-test.js", line 412, in runNextTest
    self.start({test: test, onDone: runNextTest});
  File "resource://cfx-at-jetpack/api-utils/lib/unit-test.js", line 435, in start
    this.test.testFunction(this);
  File "resource://cfx-at-jetpack/api-utils/lib/unit-test-finder.js", line 25, in runTest
    test(runner);
  File "resource://cfx-at-jetpack/cfx-js/tests/test-xpi.js", line 65, in null
    writeFile(file.join(section, "test.js"), rootJsFile);
  File "resource://cfx-at-jetpack/cfx-js/tests/test-xpi.js", line 57, in writeFile
    let testFile = file.open(path, "w");
  File "resource://cfx-at-jetpack/api-utils/lib/file.js", line 143, in open
    stream.init(file, openFlags, permFlags, 0);
[Exception... "Component returned failure code: 0x80520015 (NS_ERROR_FILE_ACCESS_DENIED) [nsIFileOutputStream.init]"  nsresult: "0x80520015 (NS_ERROR_FILE_ACCESS_DENIED)"  location: "JS frame :: resource://cfx-at-jetpack/api-utils/lib/loader.js -> resource://cfx-at-jetpack/api-utils/lib/file.js :: open :: line 143"  data: no]

I'll be investigating into this further, but just wanted to make you aware.

@ochameau
Owner

It is failing for me too, but on other assertions:

info: executing 'test-xpi.testIt'
info: pass: a == b == "test-install-rdf@jetpack"
info: pass: a == b == "test install.rdf"
info: pass: a == b == "test install.rdf description"
info: pass: a == b == "1.2"
info: pass: a == b == "test creator"
info: pass: a == b == "icon"
info: pass: a == b == "icon64"
error: TEST FAILED: test-xpi.testIt (failure)
error: fail: "{\n \"extra\": \"option\",\n \"jetpackID\": \"foo@bar.com\",\n \"m
anifest\": {}\n}" != "{\n  \"jetpackID\": \"foo@bar.com\",\n  \"manifest\": {},\
n  \"extra\": \"option\"\n}"
info: Traceback (most recent call last):
  File "resource:///modules/XPIProvider.jsm", line 4111, in null
    this.callback(this.addons);
  File "resource:///modules/XPIProvider.jsm", line 5163, in null
    aCallback(aAddons[0]);
  File "resource:///modules/XPIProvider.jsm", line 3193, in null
    aCallback(createWrapper(aAddon));
  File "resource://gre/modules/AddonManager.jsm", line 1556, in null
    safeCall(aCallback, aAddon);
  File "resource://gre/modules/AddonManager.jsm", line 80, in safeCall
    aCallback.apply(null, args);
  File "resource://cfx-at-jetpack/cfx-js/tests/test-xpi.js", line 153, in null
    prettyJSON(expectedHarnessOptions));
  File "resource://cfx-at-jetpack/cfx-js/tests/test-xpi.js", line 139, in assert
FileContent
    test.assertEqual(data, content);
  File "resource://cfx-at-jetpack/api-utils/lib/unit-test.js", line 164, in asse
rtEqual
    this.fail(message);
  File "resource://cfx-at-jetpack/api-utils/lib/unit-test.js", line 71, in fail
    this.console.trace();
info: pass: a == b == "function install() {}; function startup() {};"
info: pass: a == b == "An arbitrary file from template in a folder"
info: pass: a == b == "root js file"
info: pass: a == b == "js file in a sub folder"
info: pass: a == b == "root js file"
info: pass: a == b == "js file in a sub folder"
info: pass: assertion successful
info: pass: assertion successful
info: pass: assertion successful
info: pass: assertion successful
error: fail: "{\n \"locales\": [\n  \"en-US\",\n  \"en-reverse\"\n ]\n}\n" != "{
\n  \"locales\": [\n    \"en-US\",\n    \"en-reverse\"\n  ]\n}\n"
info: Traceback (most recent call last):
  File "resource:///modules/XPIProvider.jsm", line 4111, in null
    this.callback(this.addons);
  File "resource:///modules/XPIProvider.jsm", line 5163, in null
    aCallback(aAddons[0]);
  File "resource:///modules/XPIProvider.jsm", line 3193, in null
    aCallback(createWrapper(aAddon));
  File "resource://gre/modules/AddonManager.jsm", line 1556, in null
    safeCall(aCallback, aAddon);
  File "resource://gre/modules/AddonManager.jsm", line 80, in safeCall
    aCallback.apply(null, args);
  File "resource://cfx-at-jetpack/cfx-js/tests/test-xpi.js", line 173, in null
    prettyJSON({locales: ["en-US", "en-reverse"]}) + "\n");
  File "resource://cfx-at-jetpack/cfx-js/tests/test-xpi.js", line 139, in assert
FileContent
    test.assertEqual(data, content);
  File "resource://cfx-at-jetpack/api-utils/lib/unit-test.js", line 164, in asse
rtEqual
    this.fail(message);
  File "resource://cfx-at-jetpack/api-utils/lib/unit-test.js", line 71, in fail
    this.console.trace();
error: fail: "{\n \"key\": \"uo\u0131\u0287\u0250\u05DFsu\u0250\u0279\u0287\"\n}
" != "{\n  \"key\": \"uo\u0131\u0287\u0250\u05DFsu\u0250\u0279\u0287\"\n}"
info: Traceback (most recent call last):
  File "resource:///modules/XPIProvider.jsm", line 4111, in null
    this.callback(this.addons);
  File "resource:///modules/XPIProvider.jsm", line 5163, in null
    aCallback(aAddons[0]);
  File "resource:///modules/XPIProvider.jsm", line 3193, in null
    aCallback(createWrapper(aAddon));
  File "resource://gre/modules/AddonManager.jsm", line 1556, in null
    safeCall(aCallback, aAddon);
  File "resource://gre/modules/AddonManager.jsm", line 80, in safeCall
    aCallback.apply(null, args);
  File "resource://cfx-at-jetpack/cfx-js/tests/test-xpi.js", line 175, in null
    prettyJSON({key: reverseTranslation}));
  File "resource://cfx-at-jetpack/cfx-js/tests/test-xpi.js", line 139, in assert
FileContent
    test.assertEqual(data, content);
  File "resource://cfx-at-jetpack/api-utils/lib/unit-test.js", line 164, in asse
rtEqual
    this.fail(message);
  File "resource://cfx-at-jetpack/api-utils/lib/unit-test.js", line 71, in fail
    this.console.trace();
error: fail: "{\n \"key\": \"translation\"\n}" != "{\n  \"key\": \"translation\"
\n}"
info: Traceback (most recent call last):
  File "resource:///modules/XPIProvider.jsm", line 4111, in null
    this.callback(this.addons);
  File "resource:///modules/XPIProvider.jsm", line 5163, in null
    aCallback(aAddons[0]);
  File "resource:///modules/XPIProvider.jsm", line 3193, in null
    aCallback(createWrapper(aAddon));
  File "resource://gre/modules/AddonManager.jsm", line 1556, in null
    safeCall(aCallback, aAddon);
  File "resource://gre/modules/AddonManager.jsm", line 80, in safeCall
    aCallback.apply(null, args);
  File "resource://cfx-at-jetpack/cfx-js/tests/test-xpi.js", line 177, in null
    prettyJSON({key: usTranslation}));
  File "resource://cfx-at-jetpack/cfx-js/tests/test-xpi.js", line 139, in assert
FileContent
    test.assertEqual(data, content);
  File "resource://cfx-at-jetpack/api-utils/lib/unit-test.js", line 164, in asse
rtEqual
    this.fail(message);
  File "resource://cfx-at-jetpack/api-utils/lib/unit-test.js", line 71, in fail
    this.console.trace();

17 of 21 tests passed.

The following tests failed:
  test-xpi.testIt: failure

I'll try to fix that tomorrow.

@ochameau
Owner

From what I remember I fixed review comments before landing that to cfx-js upstream repo branch.

@ochameau ochameau closed this
@erikvold
Owner

Reopening this, I'll try to pick it up where it was left off.

@erikvold erikvold reopened this
@erikvold
Owner

Ah I see it was merge to the cfx-js branch

@erikvold erikvold closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 21, 2012
  1. @ochameau
Commits on Jun 22, 2012
  1. @ochameau

    Fix cfx on MacOS 32bit.

    ochameau authored
Commits on Jun 26, 2012
  1. @ochameau

    Fix broken tests.

    ochameau authored
  2. @ochameau
  3. @ochameau

    Address review comments.

    ochameau authored
  4. @ochameau

    Add comment.

    ochameau authored
Commits on Jun 27, 2012
  1. @ochameau

    Improve README readability.

    ochameau authored
  2. @ochameau
  3. @ochameau

    Address review comment.

    ochameau authored
Commits on Jul 3, 2012
  1. @ochameau
Commits on Jul 4, 2012
  1. @ochameau

    Avoid adding attributes to `harnessOptions` that should not end up in…

    ochameau authored
    … harness-options.json. And pass SDK and Addon path to JS.
  2. @ochameau

    Update docs.

    ochameau authored
Commits on Jul 10, 2012
  1. @ochameau

    Fix preferences support and ensure generating same options.xul file f…

    ochameau authored
    …or the same data against python implementation.
  2. @ochameau

    Avoid generating different serialization for the same data in harness…

    ochameau authored
    …-options.json (compared to python impl.)
  3. @ochameau

    Address review comment.

    ochameau authored
Commits on Jul 12, 2012
  1. @ochameau
  2. @ochameau
Commits on Jul 23, 2012
  1. @ochameau
Commits on Aug 2, 2012
  1. @ochameau
Something went wrong with that request. Please try again.