In my add-on, I'm using XUL to display dialog windows because I can customize their appearance to suit the add-on's general style (like a custom titlebar).
Using the migration guide, I'm able to do this easily. The thing is, I would like to call certain functions in the add-on's main module from the XUL dialog.
After a bit of searching I found the loader module, which seems to be able to do exactly what I want. But, I'm experiencing trouble in using it to access the main module.
First, I tried the obvious approach as mentioned in the documentation;
xul_dialog.js:
let {Loader} = Components.utils.import('resource://gre/modules/commonjs/toolkit/loader.js');
let loader = Loader.Loader({
paths: {
'toolkit/': 'resource://gre/modules/commonjs/toolkit/',
'': 'resource:///modules/',
'./': 'resource://<my-addon-name>/root/'
}
});
let main = Loader.main(loader, './main');
I got an error that './main' was not found at resource://<my-addon-name>/root/. Figuring that I was using the incorrect paths, I experimented a bit until I could remove all path associated errors.
xul_dialog.js:
let {Loader} = Components.utils.import('resource://gre/modules/commonjs/toolkit/loader.js');
let loader = Loader.Loader({
paths: {
'toolkit/': 'resource://gre/modules/commonjs/toolkit/',
'': 'resource://gre/modules/commonjs/',
'./': 'resource://<my-addon-id>-at-jetpack/<my-addon-name>/lib/'
}
});
let main = Loader.main(loader, './main');
This time I got a rather confusing error at loader.js, line 279.
Components is not available in this context.
Functionality provided by Components may be available in an SDK
module: https://jetpack.mozillalabs.com/sdk/latest/docs/
However, if you still need to import Components, you may use the
`chrome` module's properties for shortcuts to Component properties:
Shortcuts:
Cc = Components.classes
Ci = Components.interfaces
Cu = Components.utils
CC = Components.Constructor
Example:
let { Cc, Ci } = require('chrome');
I get the same error when I use Loader.Require(loader, {id: './main'}) instead of Loader.main. I even tried passing Components as globals when instantiating the loader, but without much luck.
I'm fairly certain that I'm doing a lot of things wrong. I don't understand why I'm getting the error, even after spending quite a bit of time in loader.js. Plus, I also think that there would be a better alternative than having to use the add-on id for the path to main.js; hard-coding it like that doesn't seem right.
Any help would be really appreciated.
What you have to do is to find a specific instance of Loader, not create a new one.
At main.js
const { id, name, prefixURI } = require("#loader/options");
//pass these to the XUL dialog
At xul.js (or whatever is the name of the xul dialog script)
Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm");
var extensionscope = XPIProvider.bootstrapScopes[id];
var mainjssandbox = extensionscope.loader.sandboxes[prefixURI + name + "/lib/main.js"];
Assuming there is a foo function at main.js, you can call it like
mainjssandbox.foo();
Of course don't expect things to work as if XUL and Add-on SDK actually blended into one thing.
If it is your XUL dialog that should interact with your add-on, then please don't use the Loader stuff and in particular do not go the XPIProvider.bootstrapScopes #paa suggested. While this might work (for now), it should be noted that it relies on tons of implementation details that are subject to change at any point making this solution extremely fragile.
Instead there are a couple of other options (not an exhaustive list):
If the SDK part opens the windows, you may use .openDialog which supports passing arguments to the created window, and these arguments can even be objects and functions. Also, you can have the window dispatch (custom) events, and which your SDK part can listen to by calling addEventListener on the window the .openDialog call returns.
If the window is created from somewhere else (e.g. from the AddonManager because of em:optionsURL) then the nsIObserverService is another way to communicate. The window could e.g. .notifyObservers on DOMContentLoaded containing a reference to itself. The SDK parts would just have to observe such notifications by addObserver.
Another way, a bit hacky but working, is the SDK part listening to new windows via nsIWindowWatcher.registerNotification and then injecting some API into e.g. browser.xul windows by XPCNativeWrapper.unwrap(subject.QueryInterface(Ci.nsIDOMWindow)).myAddonAPI = something.
Be sure to handle unloading or your add-on well - it is still restartless -, i.e. reverse any changes you've done and remove any observers and so on again.
If you want to interact with SDK addons not created by you, then the XPIProvider route might be the only feasible. But still, it might be worth contacting the add-on author first asking for the addition of some public API instead of hacking deep into the AddonManager and SDK loader internals.
PS:
Considering passing require or the global scope to your window via openDialog if you want to. Get the global scope by placing this into your main.js:
const globalScope = this;
Related
I have a rather large app made in Appcelerator Titanium, which I've not ported from the SDK version 3.2 because the Ti.Ui.Window's "url" property has been removed, which my application uses extensively. Unfortunately, I have not been able to find the new, correct way to do this. The info I'm finding out there does only point to the removal of the url property, or suggests that I should move to Alloy (which at the moment is not doable for me as it would require a complete rewrite of the app). Can anyone point me to an example of the right way this should be done?
If you're not using Alloy, then it's really a two step process. First you need to get the handle to the window. That is usually done with Ti.UI.createWindow (see http://docs.appcelerator.com/platform/latest/#!/api/Titanium.UI-method-createWindow). Now that you have a reference to the window, you simply open it. So,
var win = Ti.UI.createWindow({title: 'My first window'});
win.open();
Documentation on the window object is here. http://docs.appcelerator.com/platform/latest/#!/api/Titanium.UI.Window
If you have windows defined in other js files. ie. myWindow.js, then you can use require to get the js window. Have the code in your window return a "Window" object, then open that.
ie. myWindow.js
var win = Ti.UI.createWindow({title: 'Window from another file'});
return win;
Then in your calling file, don't use url, require the window:
var myNewWindow = require('myWindow');
myNewWindow.open();
You can see information about calling require here: http://docs.appcelerator.com/platform/latest/#!/api/Global-method-require
Hope that helps.
Ray
Goal: making a standalone modular JavaScript debuggin-utility (includes custom dom and event manipulation methods) to be used in the console (preferably Chrome) on any random sites of interests (with no backend access).
Usage: initially include module script directly via copy-paste to console or by creating a new script element pointing at myhomepage.com/shortandeasytoremember.js and call methods on the namespace from there on.
Problem: how to best make it persistent throughout the session on that webpage (so that I wouldn't need to reinclude it after every refresh) ?
Note: any additional browser compatibility is not required - as long as it works in the latest Chrome, it's all fine by me (but any effort in the compatibility department is always much appreciated for the sake of others). IF YOU READ THIS IN A FAR FUTURE and by then there exists a better solution than what is written down below, please take a moment to contribute with your superior knowledge.
What I currently have is an event listener on window.unload to save any state data to localStorage and a string to make it easier to reinclude the module after page reload using eval(localStorage.getItem('loadMyNS'));.
(function(ns, undefined){
'use strict';
//util methods on ns and few monkey patches for debugging ...
var store = 'if(!window.MyNS){' +
'var xyz9=document.createElement("script");' +
'xyz9.src="http://myhomepage.com/shortandeasytoremember.js";' +
'document.head.appendChild(xyz9);}';
localStorage.setItem('loadMyNS', store);
ns.save = function () {
// and use localStorage for some more data
// to be used by other methods after page reload
};
window.addEventListener('unload', ns.save, false);
}(window.MyNS = window.MyNS || {}));
(browsers with no localStorage or addEventListener may benifit from this article)
I've also concidered using the same schema with window.name instead of localStorage (as long as this still seems legid) just because writing eval(window.name) would take less typing ^^.
The trouble (one of them) I have with the "eval-script-tag-inclusion" is on the sites which block external non-https script sources. An ideal solution would be a globally accessible module which would live with state and methods included (and no initialization required after refresh) at least until I close the the window (or overwrite the ref ofc).
If that is currently absolutely not possible, a lesser solution yet still superior to my current code would suffice.
I use TypeScript to code my javascript file with Object Oriented Programing.
I want to use the node module https://npmjs.org/package/typescript-require to require my .ts files from other files.
I want to share my files in both server and client side. (Browser) And that's very important. Note that the folder /shared/ doesn't mean shared between client and server but between Game server and Web server. I use pomelo.js as framework, that's why.
For the moment I'm not using (successfully) the typescript-require library.
I do like that:
shared/lib/message.js
var Message = require('./../classes/Message');
module.exports = {
getNewInstance: function(message, data, status){
console.log(requireTs);// Global typescript-require instance
console.log(Message);
return new Message(message, data, status);
}
};
This file need the Message.js to create new instances.
shared/classes/Message.ts
class Message{
// Big stuff
}
try{
module.exports = Message;
}catch(e){}
At the end of the fil I add this try/catch to add the class to the module.exports if it exists. (It works, but it's not really a good way to do it, I would like to do better)
If I load the file from the browser, the module.export won't exists.
So, what I did above is working. Now if I try to use the typescript-require module, I'll change some things:
shared/lib/message.js
var Message = requireTs('./../classes/Message.ts');
I use requireTs instead of require, it's a global var. I precise I'm using .ts file.
shared/classes/Message.ts
export class Message{
// Big stuff
}
// remove the compatibility script at the end
Now, if I try like this and if I take a look to the console server, I get requireTs is object and Message is undefined in shared/lib/message.js.
I get the same if I don't use the export keyword in Message.ts. Even if I use my little script at the end I get always an error.
But there is more, I have another class name ValidatorMessage.ts which extends Message.ts, it's not working if I use the export keyword...
Did I did something wrong? I tried several other things but nothing is working, looks like the typescript-require is not able to require .ts files.
Thank you for your help.
Looking at the typescript-require library, I see it hasn't been updated for 9 months. As it includes the lib.d.ts typing central to TypeScript (and the node.d.ts typing), and as these have progressed greatly in the past 9 months (along with needed changes due to language updates), it's probably not compatible with the latest TypeScript releases (just my assumption, I may be wrong).
Sharing modules between Node and the browser is not easy with TypeScript, as they both use very different module systems (CommonJS in Node, and typically something like RequireJS in the browser). TypeScript emits code for one or the other, depending on the --module switch given. (Note: There is a Universal Module Definition (UMD) pattern some folks use, but TypeScript doesn't support this directly).
What goals exactly are you trying to achieve, and I may be able to offer some guidance.
I am doing the same and keep having issues whichever way I try to do things... The main problems for me are:
I write my typescript as namespaces and components, so there is no export module with multiple file compilation you have to do a hack to add some _exporter.ts at the end to add the export for your library-output.js to be importable as a module, this would require something like:
module.exports.MyRootNamespace = MyRootNamespace
If you do the above it works, however then you get the issue of when you need to reference classes from other modules (such as MyRootNamespace1.SomeClass being referenced by MyRootNamespace2.SomeOtherClass) you can reference it but then it will compile it into your library-output2.js file so you end up having duplicates of classes if you are trying to re-use typescript across multiple compiled targets (like how you would have 1 solution in VS and multiple projects which have their own dll outputs)
Assuming you are not happy with hacking the exports and/or duplicating your references then you can just import them into the global scope, which is a hack but works... however then when you decide you want to test your code (using whatever nodejs testing framework) you will need to mock out certain things, and as the dependencies for your components may not be included via a require() call (and your module may depend upon node_modules which are not really usable with global scope hacking) and this then makes it difficult to satisfy dependencies and mock certain ones, its like an all or nothing sort of approach.
Finally you can try to mitigate all these problems by using a typescript framework such as appex which allows you to run your typescript directly rather than the compile into js first, and while it seems very good up front it is VERY hard to debug compilation errors, this is currently my preferred way but I have an issue where my typescript compiles fine via tsc, but just blows up with a max stack size exception on appex, and I am at the mercy of the project maintainer to fix this (I was not able to find the underlying issue). There are also not many of these sort of projects out there however they make the issue of compiling at module level/file level etc a moot point.
Ultimately I have had nothing but problems trying to wrestle with Typescript to get it to work in a way which is maintainable and testable. I also am trying to re-use some of the typescript components on the clientside however if you go down the npm hack route to get your modules included you then have to make sure your client side uses a require compatible resource/package loader. As much as I would love to just use typescript on my client and my server projects, it just does not seem to want to work in a nice way.
Solution here:
Inheritance TypeScript with exported class and modules
Finally I don't use require-typescript but typescript.api instead, it works well. (You have to load lib.d.ts if you use it, else you'll get some errors on the console.
I don't have a solution to have the script on the browser yet. (Because of export keyword I have some errors client side) I think add a exports global var to avoid errors like this.
Thank you for your help Bill.
I'm taking a foray into Firefox extension development for the first time, and so far it's been pretty comfortable going, but I'm running into a problem; one of the things I need to do overwriting the built-in nsIPromptService and replacing it with something of my own instead.
I walked through the basic XPCOM component creation tutorial here and got the hello world one working:
https://developer.mozilla.org/en/creating_xpcom_components
And everything in that seems to work fine, but nothing I've been able to find or research shows how I can overwrite an interface from javascript. I've seen things in C++ and Java that seem to be able to overwrite the built-in components, but I can't find anything about doing this from javascript, and just trying to change the contract ID didn't work; when I try to get the service from the contract ID (as below), it just returns the original, built-in component version.
var myComponent = Components.classes['#mozilla.org/embedcomp/prompt-service;1']
.getService(Components.interfaces.nsIPromptService);
Is there something really obvious here that I'm missing? Is this the wrong way to go about overriding components (I can't seem to find anything anywhere, so I'm not really sure what I should be doing..).
Neil, thanks for the suggestion. That's what I thought I was doing (and I was), but if you're actually overriding a contract (instead of defining a new one), it looks like the answer is that you have to go to the nsIComponentRegistrar and actually register your factory (rather than relying on the chrome.manifest to handle it for you). An example of this would be:
Components.manager.nsIComponentRegistrar.registerFactory(CLASS_ID, CLASS_NAME, CONTRACT_ID, MyPromptServiceFactory);
Where the constans were:
const CLASS_ID = Components.ID("{a2112d6a-0e28-421f-b46a-25c0b308cbd0}");
// description
const CLASS_NAME = "My Prompt Service";
// textual unique identifier
const CONTRACT_ID = "#mozilla.org/embedcomp/prompt-service;1";
Where the CLASS_ID/CONTRACT_ID were the IDs for the pre-existing service.
You need to register your component using the contract id of the service that you want to override.
We develop an application in an embedded environment. It is a high level computing environment with a complete webbrowser on top of a busybox Linux system. The only exception is that the system has a limited amount of system memory.
Our application is built in JavaScript and runs inside a Webkit based webbrowser and consists of a lot of javascript modules that are loaded in sequence (Which is not very efficient).
Some modules provide common functionality that is used by several modules. We are in the process of converting our current javascript loader with requirejs, but there is one specific need we have to address first.
Is it possible to unload a module when it has been loaded using requirejs? Assume that we dynamically loads a module using :
require(["somemodule.js"], function(m) { m.run(); } );
That works well for loading and running 'somemodule' and also resolving all dependencies for 'somemodule' and the requirejs framework will store a reference to 'somemodule' for future requests.
If we at some point need to reclaim memory, e.g to be able to load and run an infinite number of modules, we have to start removing some of them after some time. Is that possible with requirejs without altering the internal implementation?
Has anyone dealt with this kind of problem before? Most single page JS apps runs in a webbrowser on a desktop PC where memory usage usually is not a major concern.
RequireJS does not have a built-in unload feature, but it could be added perhaps as an additional part you could build into it. If you would like to have that feature, feel free to propose it in the mailing list or as a GitHub issue.
If you wanted to experiment to see if it helps your situation, what you need to do is the following:
1) Remove the defined module from the RequireJS module cache. If you are not using the multiversion support, you can do something like:
var context = require.s.contexts['_'];
delete context.defined[moduleName];
delete context.specified[moduleName];
delete context.loaded[moduleName];
2) Then you can try removing the script tag to see if that helps:
var scripts = document.getElementsByTagName('script');
for (var i = scripts.length - 1; i >= 0; i--) {
var script = scripts[i];
if (script.getAttribute('data-requiremodule') === moduleName) {
script.parentNode.removeChild(script);
break;
}
}
Note that the module may not be garbage collected if another module holds on to it via the closure function(){} that defines that other module. That other module would need to be removed too.
You can try to limit that impact by not passing in the module as a function argument, but just use require("somemodule") inside the function definition whenever you want to get a hold of dependent modules, and not holding on to that require return value for too long.
Also, in your example above, for modules that use require.def to define themselves, it should look like this (without the .js suffix):
require(["somemodule"], function(m) { m.run(); } );
Try this: require.undef(moduleName)