Writing B2G-safe JavaScript code can be a challenge because the JavaScript environment on B2G is profoundly different to the one found in Gecko and Firefox.
It is a particular challenge to write code that runs well under B2G because the error reporting facilities are not the greatest. Whereas you with Firefox can supply the --gecko-log -
flag to the mach marionette-test
command, discovering runtime errors for B2G is much harder. Setting the VERBOSE
output variable to 1
when running the make test-integration
target can help, but will unfortunately also give you a lot of blabber from npm and Node.js.
B2G shares a single global which means the source in the Marionette modules (the .js and .jsm files) are concatenated together. Practically this means you need to be careful how you name and expose your top-level constructs, since they are all exported to a single, global space. This also means, for example, that the usual EXPORTED_SYMBOLS
array has no effect in B2G, but must be defined for the code to work in Firefox.
Defining objects
Because objects share a single global, they must be exposed on the this
object to be available across modules. Objects that are defined in the local scope only will not be visible (“exported”) to other modules. This implicitly comes with the side effect that names within a component must be unique.
Top-level variables
Since Gecko recently changed the scoping of its let variables, let prefxied variables are not allowed in the top-level scope. It’s fine if they are defined inside a narrowing scope. Do note, however, that switch cases are not separate scopes in JavaScript.
You can also read more about the peculiarities of the new let behaviour from a discussion on the dev-platform@ mailing list.
If you get this wrong your binary will inexplicably crash. If you do a try job, introducing a let variable in Marionette will also cause all other test jobs to error.
Object inheritance
Perhaps the most awkward side effect of having a single global state across all your code is that the Object prototype is locked down, or frozen. Since all objects you create inherit from the global Object, they may not override any of the properties or methods defined in its superclass.
For example this means you can not supply your own toString
implementation, because this is a function already associated with the global Object
prototype.
If you try to define a toString
method,
this.Message = function() {}; Message.prototype.toString = function() { return "foo"; };
you will see an error message like
1443707269174 Marionette ERROR Error on starting server: TypeError: "toString" is read-only TypeError: "toString" is read-only @chrome://marionette/content/message.js:100:1
If you want to implement a B2G-safe toString
method on an object, you have to replace the full prototype of the object. Unfortunately it does not work to replace it with a new object and then define the method; the toString method must be part of the replacement.
You can do this with prototypes:
this.Message = function() {}; Message.prototype = { toString: function() { return "foo"; }, };
And with object expressions:
this.Message = object { toString() { return "foo"; } };