Firefox addon ignore iframes - javascript

I'm trying to build an addon for LinkedIn but the contentscript is outputted in every frame...
My main.js:
exports.main = function() {
var pageMod = require("page-mod");
pageMod.PageMod({
include: "http://www.linkedin.com/*",
contentScriptWhen: 'ready',
contentScript: 'alert("test")'
});
};
by doing a check for frame elements i can do an action if its the top frame
if(window.frameElement === null){ alert("YEAH this is the right place!); }
But my content srcipt is complex and uses jquery, and this script still puts the script in every frame...

UPDATE, the SDK's page-mod api now supports 'attachTo', so you can do this instead:
var data = require("sdk/self").data;
var page = require('sdk/page-mod');
page.PageMod({
match:['*'],
contentScriptOptions: {},
contentScriptFile: [data.url('myScript.js')],
attachTo: 'top',
onAttach: function(worker) {
//set up hooks or other background behavior
},
});
See this more recent question for more info.
There are two approaches you could look into:
1. attach your content scripts using the tabs module. This works because the tabs module only deals with top-level documents. Here is a simple example:
https://builder.addons.mozilla.org/package/22176/latest/
2. do an initial load of a very small content script via page-mod, and then if thepage is something you really want to mod, inject scripts by sending them via port.emit messages. There is an example of this sort of scheme from the dotjs add-on:
https://github.com/rlr/dotjs-addon

Related

How to communicate with a webpage via browser plugin

How can I communicate from a JavaScript code of a webpage to the main code of the add-on?
For example, something like this: If some element is clicked, in the corresponding event handler of the page script, which is the syntax that can be used to send some message to the main code?
Specifically, something like this, where the frame now must be replaced by a generic webpage. Is it possible?
Edit: I have tried the suggested code, but how I had said, the application returns this error:
console.error: sherlock:
Message: ReferenceError: document is not defined
Stack:
A coding exception was thrown in a Promise resolution callback.
See https://developer.mozilla.org/Mozilla/JavaScript_code_modules/Promise.jsm/Promise
Full message: ReferenceError: document is not defined
Previously my question, I had infact tried something similar without any effect.
Yes it is possible.
document.onload = function() {
var elementYouWant = document.getElementById("someID");
elementYouWant.onclick = console.log("Yup.. It was clicked..");
};
Reference.
The answer to the question is not as trivial as it may seem at first sight. I had also thought of a logic of the type described in the Pogrindis' response.
But here, in the case of interaction between the main script (i.e. that of the add-on) and generic script of arbitrary documents, the pattern is different.
In summary, the interaction takes place in this way:
It is required the API page-mod.
Through the property includes of the object PageMod you create a reference to the document, specifying the URI (wildcards are allowed).
Via the contentScriptFile property it is set the URL of the .js file that will act as a vehicle between the main code and that of the document.
Here's an example that refers to the specific needs of the context in which I am. We have:
an add-on code (the main code);
a Sidebar type html document (gui1.html) loaded in the file that I
use as a simple UI (I advise against the use of Frames, since it does
not support many typical HTML features - eg the click on a link,
etc.) containing a link to a second document (gui2.html) which will then
be loaded into the browser tab (I needed this trick because the
Sidebar does not support localStorage, while it is necessary for me);
a script in the document.
We must create an exchange of information between the two elements. In my case the exchange is unidirectional, from the page script to the main one.
Here's the code (main.js):
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "resource://path/to/document/gui2.html",
contentScriptFile: data.url("listen.js"),
onAttach: function(worker) {
worker.port.on("gotElement", function(elementContent) {
console.log(elementContent);
});
}
});
and in the html page script:
<script type="text/javascript">
[...]
SOWIN = (navigator.userAgent.toLowerCase().indexOf("win") > -1) ? "win" : "nix";
if (SOWIN == "win") {
window.postMessage("win","*");
} else {
window.postMessage("Linux","*");
}
[...]
</script>
Finally in the JS file (listen.js) to be attached to the page script:
window.addEventListener('message', function(event) {
self.port.emit("gotElement", event.data);
}, false);
This is just a small example, but logic I would say that it is clear. The uploaded content scripts are not accessible directly from main.js (i.e. the add-on), but you can create a bidirectional communication through the exchange of messages. To achieve this we have to put ourselves in listening the event Attach of the page-mod. Then, it is passed a worker object to the listener; that worker may be used by the add-on for the exchange of messages.
Here are the references to have an exhaustive picture:
Interacting with page scripts
Communicating with other scripts
page-mod
port
Communicating using "port"
postMessage
Communicating using postMessage

How to make a simple firefox addon, which just executes stand-alone JS script on every new page?

I'm trying to make a simple firefox add-on, executing a simple stand-alone JS-script on every new page (say, just a simple alert after page loaded).
First, I've tried add-on sdk. It have been installed successfully, run tests, but was not able to execute even examples from any tutorial, so i tried to try XUL.
I downloaded 'xulschoolhello.xpi', unpacked it, modified content/browserOverlay.xul to this:
<?xml version="1.0"?>
<!DOCTYPE overlay SYSTEM
"chrome://xulschoolhello/locale/browserOverlay.dtd">
<overlay id="xulschoolhello-browser-overlay"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/x-javascript"
src="chrome://xulschoolhello/content/browserOverlay.js" />
</overlay>
and content/browserOverlay.js to this just to see if something happens:
window.alert(123)
but nothing happens after zip packing, installation and rebooting.
I'm quiet new to firefox extensions, so thanks for any help.
UPD.
I tried to make a very simple bootstrap.js:
var WindowListener = {
onOpenWindow: function(window) {
window.alert(123)
}
}
function startup(data, reason) {
var wm = Components.classes["#mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
wm.addListener(WindowListener);
}
function shutdown(data, reason) {}
function install(data, reason) {}
function uninstall(data, reason) {}
But it doestn alert. What am i doing wrong here?
This is a fully working bootstap addon that runs javascript everytime a url matching hostPatern of bing.com is loaded. Download the xpi from this gist here and install it to see it working:
https://gist.github.com/Noitidart/9287185
If you want to use this for yourself, then just edit the addDiv and removeDiv functions on the js you want to run. And to control which site edit hostPattern global var and decide if you want to listen to frames by setting global var of ignoreFrames.
To accomplish this with addon-sdk you do it like this:
var pageMod = require("page-mod");
const data = require("self").data;
exports.main = function() {
pageMod.PageMod({
include: ["https://www.bing.com/*","http://www.google.com/*"],
contentScriptWhen: 'ready',
/*contentScriptFile: [data.url("jquery.js"),data.url("script.js")],*/
onAttach: function(worker) {
console.log('loaded a page of interest');
}
});
I'm not too familiar with addon-sdk like i dont know how to set up the environment, but i heard once you get it setup its pretty simple. As you can see by comparing the amount of code. But in the bootstrap version you have fine control over everything thats why I prefer bootstrap, and most of the codes are cookie-cutter copy and paste.

Why this contentscript runs various times in a firefox add-on?

I am learning how to create a Firefox add-on. I want to do a simple add-on that will inject a script in a page. I have read the documentation but I can't solve this problem.
in the cfx run logs, I can see that it runs the script various times in the same page when it should do it only once.
main.js
var pageMod = require('sdk/page-mod')
var data = require('sdk/self').data
pageMod.PageMod({
include: ['*'],
contentScriptWhen: 'end',
contentScriptFile: data.url('starter.js')
})
starter.js
var script = document.createElement('script');
script.id = 'i-inject';
script.type = 'text/javascript';
script.src = 'http://localhost:8888/injectme.js';
document.getElementsByTagName('head')[0].appendChild(script);
console.log('injected');
Do you see this console.log('injected') ? I can see that it's printed 5 times in the console when I do cfx run and each time I reload the page. I don't understand that behaviour.
Any help greatly appreciated :)
I just finished asking the same question, for some reason multiple searches didn't lead me to this question until now.
The link you posted concerning iFrames lead me to this solution, which seems to be working well.
In Firefox, iFrames are still Pages, and by default the SDK doesn't discriminate between iFrames and top Pages, causing content scripts to attach to ANY loaded page ( or iFrame ) that meets your pageMod 'match' requirements. Rather than use page-mod, you can use tabs to inject the scripts and data you need.
var data = require("sdk/self").data;
var tabs = require('sdk/tabs');
tabs.on('ready', function(tab) {
var worker = tab.attach({
contentScriptOptions: {},
contentScriptFile: [data.url('myScript.js')]
});
// now set up your hooks:
worker.port.on('someKey', function(data) {
//do whatever with your data
});
});
Alternatively, you can modify the behavior of PageMod to only apply to the top page, which will prevent it from running in any iFrames on the page.
var data = require("sdk/self").data;
var pageMod = require('sdk/page-mod');
pageMod.PageMod({
match: [*],
attachTo: ["top"],
contentScriptOptions: {},
contentScriptFile: [data.url('myScript.js')],
onAttach: function(worker) {
//function body
}
});
More details on how to further control page-mod behavior can be found in their API docs

Why onAttach in page-mod module is not getting executed in Firefox add-on sdk?

I am writing a Firefox add-on which uses context-menu and page-mod modules.
This add-on adds a new menu item to the context menu when the context is input control. When the menu item is clicked, I request to the server to get some data and pushes the result to the content script which page-mod is using.
For some reason, onAttach function is not getting called. There are no errors in the console. I am not sure why it is not getting called. Here is what I am doing.
var data = require("self").data,
contextMenu = require("context-menu"),
request = require("request").Request;
var workers = [];
var pageMod = require("page-mod");
pageMod.PageMod({
include: '*',
contentScriptWhen: 'ready',
contentScriptFile: [data.url("jquery-1.8.2.min.js"), data.url("varnam.js")],
onAttach: function(worker) {
console.log("onAttach");
workers.push(worker);
worker.on("detach", function() {
var index = workers.indexOf(worker);
if (index >= 0) workers.splice(index, 1);
});
}
});
Any help to fix this issue would be great. Full code is available here.
When the url pattern begins with (or is just) an asterisk, then it is matched against urls with a scheme of http, https and ftp.
Since your test page is in the data directory its url scheme is resource. And that's why PageMod is not triggered.
You can add the test page url by doing something like
var varnam = pageMod.PageMod({
// blah blah
});
varnam.include.add(data.url("test.html"));

Communication via postMessage()

I'm working on a Firefox extension, and I need to be able to communicate between the addon script and the content scripts. I have one direction of this working: passing the URL of a script from the addon script to a content script. However, I need to be able to go in the reverse direction, as well. My main.js file looks like this:
var data = require("self").data;
var pageMod = require("page-mod");
pageMod.PageMod({
include: "https://trello.com/board/*",
contentScriptWhen: 'end',
contentScriptFile: data.url("scrumello_beta.user.js"),
onAttach: function(worker) {
worker.postMessage(data.url("scrumello_beta.js"));
worker.on("message", function(addonMessage)
{
console.log(addonMessage);
});
}
});
In the client script, I have the following method:
function OpenProcess(SCRNumber)
{
self.postMessage(SCRNumber);
}
However, when this method is called, I get the following error:
Timestamp: 8/7/2012 12:15:58 PM
Error: NS_ERROR_XPC_NOT_ENOUGH_ARGS: Not enough arguments [nsIDOMWindow.postMessage]
Source File: resource://jid0-3mulsijczmtjeuwkd5npayasqf8-at-jetpack/scogan-3/data/scrumello_beta.js
Line: 1038
This prevents the worker.on("message"... event from ever being triggered. As far as I know, postMessage only takes one argument, so any help here would be appreciated.
EDIT:
I've changed the postMessage call to
self.postMessage(SCRNumber, "*");
I wrapped it in console.log's, both of which are being printed, so I have to assume the message is actually being posted. However, the event handler in main.js never picks up the message, because the console.log I have in there is never printed.
Here's how I did it. (Notice that I never used self.postmessage)
Addon script (main.js) to content script communication:
contentPage = pageMod.PageMod({
onAttach: function(worker) {
// Post a message directly to the content script
worker.postMessage("any thing you want to respond");
// Depending on the message, respond with different data
worker.port.on('getFact', function() {
worker.postMessage("any thing you want to respond");
});
worker.port.on('getEnabled', function() {
worker.postMessage("any thing you want to respond");
});
}
});
--
Here's the content script responding to the add-on script:
// Get data from the addon script
self.on('message', function(msg) {
// Do something depending on the message passed
});
--
Last, the content script can communicate to the add-on script like this:
self.port.emit("message to send to add-on script")
The above code will trigger the worker.port.on code in the main.js.

Categories

Resources