This is my first time learning to build a firefox addon. I want store all the open tabs in a window and for that I require sdk/tabs.
Here is my js file:
/*
Given the name of a beast, get the URL to the corresponding image.
*/
debugger;
var tabs = require("sdk/tabs");
function beastNameToURL(beastName) {
switch (beastName) {
case "Save Session":
debugger;
for (let tab of tabs)
console.log(tab.url);
return;
case "Load Session":
debugger;
return chrome.extension.getURL("beasts/snake.jpg");
case "Turtle":
return chrome.extension.getURL("beasts/turtle.jpg");
}
}
/*
Listen for clicks in the popup.
If the click is not on one of the beasts, return early.
Otherwise, the text content of the node is the name of the beast we want.
Inject the "beastify.js" content script in the active tab.
Then get the active tab and send "beastify.js" a message
containing the URL to the chosen beast's image.
*/
document.addEventListener("click", function(e) {
if (!e.target.classList.contains("btn")) {
return;
}
var chosenBeast = e.target.textContent;
var chosenBeastURL = beastNameToURL(chosenBeast);
chrome.tabs.executeScript(null, {
file: "/content_scripts/beastify.js"
});
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {beastURL: chosenBeastURL});
});
});
When I reach the var tabs = require("sdk/tabs") line I get a Reference error.
Github : https://github.com/sagar-shah/Session-manifest
Kindly let me know how do I resolve this error. This being my first time with add-ons I am completely lost.
Thanks in advance.
Update:
Tried to declare it globally in the js file. Now I am getting undefined error for tabs.
Update2:
I was mixing up development using sdk and webextensions as pointed out by #matagus. I have decided to go with development using the webextensions. Link to the new repository has been updated.
The error is on package.json line 6: you're telling to the addon sdk that the main file of your addon is manage.json. According to [the docs] the value of main should be:
A string representing the name of a program module that is located in one of the top-level module directories specified by lib. Defaults to "index.js".
So you need to change its value to index.js.
Besides that, I think you're missing a difference between Firefox addon built using the addon-sdk (which do not have a ´manifest.json´ and that you build using jpm tool) and the new WebExtensions which do require you to write a ´manifest.json´ like the one already have.
UPDATE:
Again: you're missing the difference between WebExtensions and SDK-based addons. Now you made a WebExtension but you're trying to use the SDK. It isn't possible. Just use chrome.tabs directly instead of trying to import it from the sdk (var tabs = require("sdk/tabs");).
I got task to write an add-on for Firefox which will add an div element to existing page. I downloaded Add-on SDK and wrote a main.js file that looks like this:
var data = require("sdk/self").data;
require("sdk/tabs").on("ready", ExecuteAd);
function ExecuteAd(tab) {
if ( tab.url.indexOf("some url checking") > -1 ) {
var image = "http://www.lavasoft.com/img/product_icons/aaw11/free.png";
var link = "http://www.google.me";
tab.attach({
contentScriptFile: data.url("myscript.js"),
contentScript: "appendFunc('"+image+"', '"+link+"');"
//contentScript: "alert('Works');"
});
}
}
When I execute command cfx run it starts Firefox and if I go to specific web pages this script works. But when I create XPI file with cfx xpi and then click on Firefox and open that file it installs my add-on but now when I go to same web pages I gave been before add-on does not work. I have this external Javascript file which is stored in folder 'data'.
appendFunc is in myscript.js file.
How to make my extension work in production environment not just testing environment? I think that main problem is that it does not find this data/myscript.js (does it include in .xpi file?)
Don't mix contentScript and contentScriptFile. Also, you cannot know what of both is loaded first.
Instead load your script, and communicate using port.
main.js
var data = require("sdk/self").data;
require("sdk/tabs").on("ready", ExecuteAd);
function ExecuteAd(tab) {
var image = "http://www.lavasoft.com/img/product_icons/aaw11/free.png";
var link = "http://www.google.me";
var worker = tab.attach({
contentScriptFile: data.url("myscript.js")
});
worker.port.emit("showAd", {image: image, link: link});
}
myscript.js
self.port.on("showAd", function(data) {
console.log("showing ad", data.link, data.image);
});
Also, it sounds like PageMod would be a better choice for what you're doing.
PS: Also consult the Add-on Policies if you're planning to host on the addons.mozilla.org website. The policies e.g. prohibit injecting ads that a) aren't clearly marked as such and b) where the user did not opt-in prior to that.
I have created a custom URL protocol handler.
http://
mailto://
custom://
I have registered a WinForms application to respond accordingly. This all works great.
But I would like to be able to gracefully handle the case where the user doesn't have the custom URL protocol handler installed, yet.
In order to be able to do this I need to be able to detect the browser's registered protocol handlers, I would assume from JavaScript. But I have been unable to find a way to poll for the information. I am hoping to find a solution to this problem.
Thanks for any ideas you might be able to share.
This would be a very, very hacky way to do this... but would this work?
Put the link in as normal...
But attach an onclick handler to it, that sets a timer and adds an onblur handler for the window
(in theory) if the browser handles the link (application X) will load stealing the focus from the window...
If the onblur event fires, clear the timer...
Otherwise in 3-5seconds let your timeout fire... and notify the user "Hmm, looks like you don't have the Mega Uber Cool Application installed... would you like to install it now? (Ok) (Cancel)"
Far from bulletproof... but it might help?
There's no great cross-browser way to do this. In IE10+ on Win8+, a new msLaunchUri api enables you to launch a protocol, like so:
navigator.msLaunchUri('skype:123456',
function()
{
alert('success');
},
function()
{
alert('failed');
}
);
If the protocol is not installed, the failure callback will fire. Otherwise, the protocol will launch and the success callback will fire.
I discuss this topic a bit further here:
https://web.archive.org/web/20180308105244/https://blogs.msdn.microsoft.com/ieinternals/2011/07/13/understanding-protocols/
This topic is of recent (2021) interest; see https://github.com/fingerprintjs/external-protocol-flooding for discussion.
HTML5 defines Custom scheme and content handlers (to my knowledge Firefox is the only implementor so far), but unfortunately there is currently no way to check if a handler already exists—it has been proposed, but there was no follow-up. This seems like a critical feature to use custom handlers effectively and we as developers should bring attention to this issue in order to get it implemented.
There seems to be no straightforward way via javascript to detect the presence of an installed app that has registered a protocol handler.
In the iTunes model, Apple provides urls to their servers, which then provide pages that run some javascript:
http://ax.itunes.apple.com/detection/itmsCheck.js
So the iTunes installer apparently deploys plugins for the major browsers, whose presence can then be detected.
If your plugin is installed, then you can be reasonably sure that redirecting to your app-specific url will succeed.
What seams the most easy solution is to ask the user the first time.
Using a Javascript confirm dialog per example:
You need this software to be able to read this link. Did you install it ?
if yes: create a cookie to not ask next time; return false and the link applies
if false: window.location.href = '/downloadpage/'
If you have control of the program you're trying to run (the code), one way to see if the user was successful in running the application would be to:
Before trying to open the custom protocol, make an AJAX request to a server script that saves the user's intent in a database (for example, save the userid and what he wanted to do).
Try to open the program, and pass on the intent data.
Have the program make a request to the server to remove the database entry (using the intent data to find the correct row).
Make the javascript poll the server for a while to see if the database entry is gone. If the entry is gone, you'll know the user was successful in opening the application, otherwise the entry will remain (you can remove it later with cronjob).
I have not tried this method, just thought it.
I was able to finally get a cross-browser (Chrome 32, Firefox 27, IE 11, Safari 6) solution working with a combination of this and a super-simple Safari extension. Much of this solution has been mentioned in one way or another in this and this other question.
Here's the script:
function launchCustomProtocol(elem, url, callback) {
var iframe, myWindow, success = false;
if (Browser.name === "Internet Explorer") {
myWindow = window.open('', '', 'width=0,height=0');
myWindow.document.write("<iframe src='" + url + "'></iframe>");
setTimeout(function () {
try {
myWindow.location.href;
success = true;
} catch (ex) {
console.log(ex);
}
if (success) {
myWindow.setTimeout('window.close()', 100);
} else {
myWindow.close();
}
callback(success);
}, 100);
} else if (Browser.name === "Firefox") {
try {
iframe = $("<iframe />");
iframe.css({"display": "none"});
iframe.appendTo("body");
iframe[0].contentWindow.location.href = url;
success = true;
} catch (ex) {
success = false;
}
iframe.remove();
callback(success);
} else if (Browser.name === "Chrome") {
elem.css({"outline": 0});
elem.attr("tabindex", "1");
elem.focus();
elem.blur(function () {
success = true;
callback(true); // true
});
location.href = url;
setTimeout(function () {
elem.off('blur');
elem.removeAttr("tabindex");
if (!success) {
callback(false); // false
}
}, 1000);
} else if (Browser.name === "Safari") {
if (myappinstalledflag) {
location.href = url;
success = true;
} else {
success = false;
}
callback(success);
}
}
The Safari extension was easy to implement. It consisted of a single line of injection script:
myinject.js:
window.postMessage("myappinstalled", window.location.origin);
Then in the web page JavaScript, you need to first register the message event and set a flag if the message is received:
window.addEventListener('message', function (msg) {
if (msg.data === "myappinstalled") {
myappinstalledflag = true;
}
}, false);
This assumes the application which is associated with the custom protocol will manage the installation of the Safari extension.
In all cases, if the callback returns false, you know to inform the user that the application (i.e., it's custom protocol) is not installed.
You say you need to detect the browser's protocol handlers - do you really?
What if you did something like what happens when you download a file from sourceforge? Let's say you want to open myapp://something. Instead of simply creating a link to it, create a link to another HTML page accessed via HTTP. Then, on that page, say that you're attempting to open the application for them. If it doesn't work, they need to install your application, which they can do by clicking on the link you'll provide. If it does work, then you're all set.
This was a recommended approach for IE by Microsoft support
http://msdn.microsoft.com/en-us/library/ms537503%28VS.85%29.aspx#related_topics
"If you have some control over the binaries being installed on a user’s machine, checking the UA in script seems like a relevant approach:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\5.0\User Agent\Post Platform
" -- By M$ support
Every web page has access to the userAgent string and if you drop a custom post platform value, detecting this in javascript using navigator.userAgent is quite simple.
Fortunately, other major browsers like Firefox and Chrome (barring Safari :( ), do not throw "page not found" errors when a link with a custom protocol is clicked and the protocol is not installed on the users machine. IE is very unforgiving here, any trick to click in a invisible frame or trap javascript errors does not work and ends up with ugly "webpage cannot be displayed" error. The trick we use in our case is to inform users with browser specific images that clicking on the custom protocol link will open an application. And if they do not find the app opening up, they can click on an "install" page. In terms of XD this wprks way better than the ActiveX approach for IE.
For FF and Chrome, just go ahead and launch the custom protocol without any detection. Let the user tell you what he sees.
For Safari, :( no answers yet
I'm trying to do something similar and I just discovered a trick that works with Firefox. If you combine it with the trick for IE you can have one that works on both main browsers (I'm not sure if it works in Safari and I know it doesn't work in Chrome)
if (navigator.appName=="Microsoft Internet Explorer" && document.getElementById("testprotocollink").protocolLong=="Unknown Protocol") {
alert("No handler registered");
} else {
try {
window.location = "custom://stuff";
} catch(err) {
if (err.toString().search("NS_ERROR_UNKNOWN_PROTOCOL") != -1) {
alert("No handler registered");
}
}
}
In order for this to work you also need to have a hidden link somewhere on the page, like this:
<a id="testprotocollink" href="custom://testprotocol" style="display: none;">testprotocollink</a>
It's a bit hacky but it works. The Firefox version unfortunately still pops up the default alert that comes up when you try to visit a link with an unknown protocol, but it will run your code after the alert is dismissed.
You can try something like this:
function OpenCustomLink(link) {
var w = window.open(link, 'xyz', 'status=0,toolbar=0,menubar=0,height=0,width=0,top=-10,left=-10');
if(w == null) {
//Work Fine
}
else {
w.close();
if (confirm('You Need a Custom Program. Do you want to install?')) {
window.location = 'SetupCustomProtocol.exe'; //URL for installer
}
}
}
This is not a trivial task; one option might be to use signed code, which you could leverage to access the registry and/or filesystem (please note that this is a very expensive option). There is also no unified API or specification for code signing, so you would be required to generate specific code for each target browser. A support nightmare.
Also, I know that Steam, the gaming content delivery system, doesn't seem to have this problem solved either.
Here's another hacky answer that would require (hopefully light) modification to your application to 'phone home' on launch.
User clicks link, which attempts to launch the application. A unique
identifier is put in the link, so that it's passed to the
application when it launches. Web app shows a spinner or something of that nature.
Web page then starts checking for a
'application phone home' event from an app with this same unique ID.
When launched, your application does an HTTP post to your web app
with the unique identifier, to indicate presence.
Either the web page sees that the application launched, eventually, or moves on with a 'please download' page.
ZeroClipboard doesn't work and it doesn't throw any errors (javascript console).
The website is hosted on a HTTPS webserver running on localhost. Both the website and the SWF are served over HTTPS by the same server.
The SWF is loaded and positioned correctly over the button (with ID: testButton). When right-clicking on the button, the flash context menu (About Adobe Flash Player 11.7...) is shown.
However, none of the events, not even "load", gets fired.
I am working on this problem for two days months now and i can't find a solution.
Additional info:
ZeroClipboard version: v1.1.7, also tried v1.2.0-beta.3, edit: This also happens with v1.3.1
Browser: Chromium 28 on Mac OSX
Official test website is working
Here is my code:
var clip = new ZeroClipboard(document.getElementById("testButton"), {
moviePath: "media/zeroclipboard.swf"
});
clip.on("dataRequested", function(client, args) {
client.setText("Copy me!");
});
clip.on("load", function(client) {
alert("movie is loaded");
});
clip.on("complete", function(client, args) {
alert("Copied text to clipboard: " + args.text);
});
clip.on("mouseover", function(client) {
alert("mouse over");
});
clip.on("mouseout", function(client) {
alert("mouse out");
});
clip.on("mousedown", function(client) {
alert("mouse down");
});
clip.on("mouseup", function(client) {
alert("mouse up");
});
After months I finally found a solution:
ZeroClipboard doesn't work out-of-the-box in CommonJS environments in the browser, even it says it does.
The fix for that is assigning the module scope variable (e.g. ZeroClipboard) to the global window object:
ZeroClipboard = require("zeroclipboard");
window.ZeroClipboard = ZeroClipboard;
I created a bug report on this:
https://github.com/zeroclipboard/zeroclipboard/issues/332
I'm not sure if this would help~ but have you tried changing your moviePath a little bit?
Like this:
{moviePath:"//YOURSERVER/path/ZeroClipboard.swf"}
I was trying to use ZeroCLipboard in the whole month and most of the time I fail because I got the path wrong in one way or another...
Good luck anyways~
If you extract the project exactly, the .SWF file is located here:
moviePath: "../zeroclipboard.swf"
I solved editing this line on ZeroClipboard.min.js
return a+"ZeroClipboard.swf"
to:
return "//YOUR/PATH/TO/ZeroClipboard.swf"
Would just like to point out for anybody else who finds this question first: ZeroClipboard does not work when looking at the file locally i.e. file://path/index.html. This is because of Adobe's security policies blocking the file:// protocol.
https://stackoverflow.com/a/9450359/710377
If you're already using Node.js at all (even just for the package manager) you can spin up a basic web server on the command line to test ZeroClipboard with npm install http-server -g and http-server /path/ (assuming that npm stuff is already on your path).
LOG:
Runtime Installer begin with version 3.3.0.3650 on Mac OS 10.7.4 x86
Commandline is: -updatecheck
Installed runtime (3.3.0.3650) located at /Library/Frameworks/Adobe AIR.framework
Performing pingback request
Failure during pingback request: [ErrorEvent type="error" bubbles=false cancelable=falsr eventPhase=2 text="Unhandled exception Error: Error #3001" errorID=3001]
Runtime Installer end with exit code 0
It works fine on Windows but fails for Mac.
Digging around I found out that the error code #3001 has something to do with file/directory permission issues.
Checked /Users/internetslave/Library/Application Support/Adobe permissions seems ok. source.
Checked /Library/Frameworks/Adobe AIR.framework seems ok too.
Both had drwxr-xr-x.
UPDATE: permission is not an issue, successfully updated other applications on the same system.
var appUpdater;
function checkForUpdates() {
appUpdater = new air.ApplicationUpdaterUI();
appUpdater.configurationFile = new air.File("app:/update/update-config.xml");
appUpdater.addEventListener(air.ErrorEvent.ERROR, onCheckForUpdatesError);
appUpdater.initialize();
setTimeout(function() {
appUpdater.checkNow();
}, 500);
}
function onCheckForUpdatesError(event) {
alert(event.toString());
}
Cant seem to post the update configuration and descriptor files here.
For those might stumble upon the same problem.
The problem was the ApplicationUpdaterUI has not finished initializing when the method checkNow is called.
So either, change the second parameter of the setTimeout to a higher value or place this part
appUpdater = new air.ApplicationUpdaterUI();
appUpdater.configurationFile = new air.File("app:/update/update-config.xml");
appUpdater.addEventListener(air.ErrorEvent.ERROR, onCheckForUpdatesError);
appUpdater.initialize();
in page onLoad event handler and attach the checkNow method to a button onClick event
function checkUpdates() {
appUpdater.checkNow();
}
<input type="button" onclick="checkUpdates()" />
Thanks!