Firefox Add-on SDK open tab on first run - firefox

I am using the Firefox Add-on SDK, and I am attempting to open a tab as soon as my Firefox extension gets installed for the first time. The code below is in my main.js, but it doesn't seem to work. Any tips?
main.js:
var ss = require("simple-storage");
var tabs = require('tabs');
if (typeof(ss.storage.firstRun) === undefined) {
ss.storage.firstRun = false;
alert('First run');
tabs.open("http://www.google.com");
}

Try using the load install reason: https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/tutorials/load-and-unload.html

Your approach is correct but the typeof operator gives you a string so you have to compare it to a string:
if (typeof ss.storage.firstRun == "undefined") {
This way it should work.

Related

How to run on browser start

I'm new to Firefox addon development.
I have the following code:
var windows = require("sdk/windows").browserWindows;
windows.on("open", function() {
// do stuff
});
But this only runs for windows created after the browser is started, not the first one.
How can I fix this?
I know I could just copy the code outside of the open event, but then it also runs when the addon is installed and I don't want that.
I found the answer here.
exports.main = function (options, callbacks) {
if (options.loadReason == "startup") {
// do stuff
}
};

Firefox Extension building / SDK, click on a page dom element from within extension

I'm developing a Firefox extension using the latest SDK on FF 31.
In the past (before the SDK was around, years ago), it was possible for the extension to completely manipulate all parts of the page the user was on, for example, change dom elements, etc.
It seems like modifying dom elements is still supported, but I can't seem to be able to fire a "click" even on a DOM element created by the page (not by the extension).
// main.js
var data = require("sdk/self").data;
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "*",
contentScriptFile: [data.url("jquery.js"), data.url("my-addon.js")]
});
//my-addon.js
var z = $('a').filter(function(index) { return $(this).text() === "here"; });
if(z != null && z.length > 0){
alert("FOUND IT");
$(z).click();
}
The code above just looks for a link that has "here" as the exact text and tries to click on it. It finds it just fine, but the click action does not do anything.
Figured it out...
For anyone who has the same problem, add this to the top of your "my-addon.js" script
unsafeWindow.click_object = function(_item) {
_item.click();
}
Then you can call it like this (from the same my-addon.js script):
unsafeWindow.click_object($(z).get(0));

Force context menu to appear for form inputs

I'm trying to develop a ff addon that allows a user to right-click on a form element and perform a task associated with it.
Unfortunately somebody decided that the context menu shouldn't appear for form inputs in ff and despite long discussions https://bugzilla.mozilla.org/show_bug.cgi?id=433168, they still don't appear for checkboxes, radios or selects.
I did find this: https://developer.mozilla.org/en-US/docs/Offering_a_context_menu_for_form_controls but I cannot think how to translate the code to work with the new add-on SDK.
I tried dumping the javascript shown into a content script and also via the observer-service but to no avail.
I also cannot find the source for the recommended extension https://addons.mozilla.org/en-US/firefox/addon/form-control-context-menu/ which considering it was 'created specifically to demonstrate how to do this' is pretty frustrating.
This seems like very basic addon functionality, any help or links to easier documentation would be greatly appreciated.
** UPDATE **
I have added the following code in a file, required from main, that seems to do the trick.
var {WindowTracker} = require("window-utils");
var tracker = WindowTracker({
onTrack: function(window){
if (window.location.href == "chrome://browser/content/browser.xul") {
// This is a browser window, replace
// window.nsContextMenu.prototype.setTarget function
window.setTargetOriginal = window.nsContextMenu.prototype.setTarget;
window.nsContextMenu.prototype.setTarget = function(aNode, aRangeParent, aRangeOffset) {
window.setTargetOriginal.apply(this, arguments);
this.shouldDisplay = true;
};
};
}
, onUntrack: function(window) {
if (window.location.href == "chrome://browser/content/browser.xul") {
// In case we were called because the extension is uninstalled - restore
// original window.nsContextMenu.prototype.setTarget function
window.nsContextMenu.prototype.setTarget = window.setTargetOriginal;
};
}
});
Unfortunately this still does not bring up a context menu for disabled inputs, but this is not a show-stopper for me.
Many Thanks
The important piece of code in this extension can be seen here. It is very simple - it replaces nsContextMenu.prototype.setTarget function in each browser window and makes sure that it sets shouldDisplay flag for form controls.
The only problem translating this to Add-on SDK is that the high-level modules don't give you direct access to browser windows. You have to use the deprecated window-utils module. Something like this should work:
var {WindowTracker} = require("sdk/deprecated/window-utils");
var tracker = WindowTracker({
onTrack: function(window)
{
if (window.location.href == "chrome://browser/content/browser.xul")
{
// This is a browser window, replace
// window.nsContextMenu.prototype.setTarget function
}
},
onUntrack: function(window)
{
if (window.location.href == "chrome://browser/content/browser.xul")
{
// In case we were called because the extension is uninstalled - restore
// original window.nsContextMenu.prototype.setTarget function
}
}
});
Note that WindowTracker is supposed to be replaced in some future SDK version. Also, for reference: nsContextMenu implementation

Add-on to open local folders in Firefox

I have just started making an add-on with Firefox. This add-on is written in order to open a local folder outside FF. The folder is already opened by the browser. And in the context menu you will see an option to open the folder outside the browser (I use Win7).
This is the code that I used:
var contextMenu = require("context-menu");
var menuItem = contextMenu.Item({
label: "Open Local File",
context: contextMenu.URLContext("file:///*"),
contentScript: 'self.on("click", function() {'+
'openDir(document.URL);'+
'});',
});
function openDir(val)
{
if (val == "")
{
alert("Directory not defined");
return;
}
if(navigator.userAgent.indexOf("Firefox") == -1)
{
alert("Currently active folder links supported only for Mozilla Firefox web browser");
return;
}
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var localFile =
Components.classes["#mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
var env =
Components.classes["#mozilla.org/process/environment;1"]
.createInstance(Components.interfaces.nsIEnvironment);
var systemRoot = env.get("SystemRoot");
if (systemRoot == "")
{
alert("Unable to retrieve SystemRoot environment variable");
}
localFile.initWithPath(systemRoot + "\\explorer.exe");
var process =
Components.classes["#mozilla.org/process/util;1"]
.createInstance(Components.interfaces.nsIProcess);
process.init(localFile);
process.run(false, Array(val), 1);
}
Now the problem is that when I save the add-on under http://builder.addons.mozilla.org/... it cannot be compiled. Instead a red box shows up with the message "XPI not built". This is the log:
GET https://builder.addons.mozilla.org/xpi/test/.../ 404 NOT FOUND 236ms
What should I do?
The modified code:
var contextMenu = require("context-menu");
var menuItem = contextMenu.Item({
label: "Open Local File",
contentScript: 'self.on("context", function(node)'+
'{'+
' return node.ownerDocument.URL.indexOf("file:///") == 0;'+
'});'+
'self.on("click", function(node)' +
'{' +
' self.postMessage(node.ownerDocument.URL);' +
'});',
onMessage: function(url)
{
openDir(url);
}
}) ;
function openDir(val)
{
var {Cc, Ci} = require("chrome");
var ioService = Cc["#mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
var uri = ioService.newURI(val, null, null);
if (uri instanceof Ci.nsIFileURL && uri.file.isDirectory())
{
uri.file.QueryInterface(Ci.nsILocalFile).launch();
}
}
The Add-on Builder web application is there to package up your code and create an extension - Firefox merely installs the extension once it is done. You have an issue with the Add-on Builder, not one with Firefox. I can only recommend you to file a bug report.
Your code has numerous issues however:
It seems that you want to show your context menu item on pages using the file:/// URL scheme, not on links pointing to files. There is no predefined context for this, you will have to use the content script (see Specifying Contexts > In Content Scripts. Something like:
self.on("context", function(node)
{
return node.ownerDocument.URL.indexOf("file:///") == 0;
});
Function openDir() isn't defined in the content script, it is defined in your extension. This means that you have to send a message back to your extension with the URL (see last example in Handling Menu Item Clicks). Something like this:
contentScript: 'self.on("context", ...);' +
'self.on("click", function(node, data)' +
'{' +
' self.postMessage(node.ownerDocument.URL);' +
'});',
onMessage: function(url)
{
openDir(url);
}
Checking whether your code is running in Firefox is pointless - currently, the Add-on SDK only supports Firefox.
You should not use the deprecated PrivilegeManager.enablePrivilege method - your code is already running with highest privileges. You will need to use chrome authority however, extensions built with the Add-on SDK by default cannot access low-level functionality.
You shouldn't run Windows Explorer directly, use nsILocalFile.launch(), for directories it will run Windows Explorer (or whatever action is defined in the operating system to open directories). Altogether the code in openDir() should look like this:
var {Cc, Ci} = require("chrome");
var ioService = Cc["#mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
var uri = ioService.newURI(val, null, null);
if (uri instanceof Ci.nsIFileURL && uri.file.isDirectory())
uri.file.QueryInterface(Ci.nsILocalFile).launch();
Documentation: nsIIOService, nsIFileURL.

Event Listeners to know when a page is Bookmarked/Downloaded

Is there any way to track when a page is bookmarked or downloaded in Firefox? I
mean is there any event that is triggered on bookmarking or
downloading a page? I am using Add-on SDK for developing Add-on.
If not, then kindly suggest me some workarounds.
The browser window has <command> elements that get triggered when the user bookmarks or downloads a page. The former has ID Browser:AddBookmarkAs, the latter Browser:SavePage. The Add-on SDK itself doesn't give you access to them, so you need to use the chrome package to access XPCOM directly. Something like this:
// Add listener to all existing browser windows
var {Cc, Ci} = require("chrome");
var mediator = Cc["#mozilla.org/appshell/window-mediator;1"]
.getService(Ci.nsIWindowMediator);
var enumerator = mediator.getEnumerator("navigator:browser");
while (enumerator.hasMoreElements())
listenToWindow(enumerator.getNext().QueryInterface(Ci.nsIDOMWindow));
// Get notified when new browser windows open
var observers = require("observer-service");
observers.add("chrome-document-global-created", function(window)
{
if (window instanceof Ci.nsIDOMWindow && window.location.href == "chrome://browser/content/browser.xul")
listenToWindow(window);
});
function listenToWindow(window)
{
window.document
.getElementById("Browser:AddBookmarkAs")
.addEventListener("command", onBookmark, false);
window.document
.getElementById("Browser:SavePage")
.addEventListener("command", onSavePage, false);
}
This code isn't tested so there might be minor issues but the overall concept should be correct.
Edit: Actually, the same seems to be simpler if you use the internal window-utils package. Not sure whether the API provided by this package is stable however.
var windows = require("window-utils");
for (window in windows.browserWindowIterator)
listenToWindow(window);
var observers = require("observer-service");
observers.add("chrome-document-global-created", function(window)
{
if (window instanceof Ci.nsIDOMWindow && windows.isBrowser(window))
listenToWindow(window);
});
It is all in the addon sdk documentation. Although I must admit I did not see it the first time around.
https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/tutorials/event-targets.html
The following example is from the documentation.
Note that I had to add Cr to the require to make it work
as well as substitute Components.interfaces by Ci in the generateQI() call.
var {Cc, Ci, Cu, Cr} = require("chrome");
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
var bookmarkService = Cc["#mozilla.org/browser/nav-bookmarks-service;1"]
.getService(Ci.nsINavBookmarksService);
var bookmarkObserver = {
onItemAdded: function(aItemId, aFolder, aIndex) {
console.log("added ", bookmarkService.getBookmarkURI(aItemId).spec);
},
onItemVisited: function(aItemId, aVisitID, time) {
console.log("visited ", bookmarkService.getBookmarkURI(aItemId).spec);
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
};
exports.main = function() {
bookmarkService.addObserver(bookmarkObserver, false);
};
exports.onUnload = function() {
bookmarkService.removeObserver(bookmarkObserver);
}

Resources