I am building a small add-on that interacts with the youtube website.
For injecting a custom script inside the page I use the convenient page-mod like so :
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "*",
contentScript: "window.alert('injected');",
contentScriptWhen : 'start',
attachTo: ["existing", "top"],
onAttach: function(worker) {
console.log(worker.tab.url);
}
});
When I navigate through pages, the 'injected' message shows up each time a new page is loaded. But when it comes to youtube I got no result.
My visited urls are in order :
https://www.google.com -> injected
https://www.google.com/?q=youtube -> injected
https://www.youtube.com -> injected
https://www.youtube.com/watch?v=lnUeovQ68Tw -> nothing
https://www.youtube.com/results?search_query=dark+side+of+the+moon -> nothing
and now on I never get an injected message if I stay on Youtube...
I noticed that when changing from video to video the url change but the webpage doesn't seem to reload...
I was able to get the injected message on a youtube video when I manually reload it (cmd+r or f5).
As I searched I found this article on page-mod's attachTo which could have been a solution, but event with the attachTo: ["existing", "top"] line, the result was the same...
Do you have any idea ?
Thanks
While looking in another firefox extension lib I found a solution to my question :
background.js
pageMod.PageMod({
include : youtubeRegex,
contentScriptFile : 'permanent.js',
onAttach : function(worker){
worker.port.on('update', function(){
console.log('update from contentscript');
worker.port.emit('include'); // call the script update
});
},
contentScriptWhen : 'start',
});
permanent.js
// 1st line of the file
self.port.emit("update");
// stuff called once
// ...
// stuff called on each udpate
self.port.on('include', function(){
window.alert('injected');
});
This way I get a injected even in the youtube website.
Hope it will help :)
YouTube changes the content of their site using JS. I am not sure if there are any events fired when youtube changes the location. The only event I know of is popstate, which fires, whenever the user navigates using the back or forward buttons (see https://developer.mozilla.org/en-US/docs/Web/Events/popstate).
Related
My manifest.json matches every URL. So, whenever, a URL is opened in Chrome, the content script is activated.
Current procedure:
(1) My background script updates the current empty URL to something.
(2) My content script is activated, because it's a match in the manifest.json. My content script needs to visit multiple URLs on the same website. It clicks on a link and navigates back (= everything in one tab) -> The content script calls itself again, because another URL is opened.
Problem:
I don't want content script to call itself multiple times. It should for example only activate, when it gets a message from the background script that a new URL was opened.
Thoughts:
chrome.windows.onCreated.addListener(function()) is around all my code in the background script. Is there something similar for the content script that i'm looking for?
Maybe something like:
window.addEventListener
browser.runtime.onMessage.addListener
browser.runtime.sendMessage
browser.runtime.onMessage
browser.tabs.sendMessage()
But i don't understand how to use them properly.
In your background script, do this:
chrome.windows.onCreated.addListener(function(tab) {
let msg = {txt: "execute"};
chrome.tabs.sendMessage(tab.id, msg);
};
And then in your content script, you can intercept this message via:
chrome.runtime.onMessage.addListener(function(msg){
if(msg.txt == "execute") {
doSomething();
}
});
Docs here.
https://developer.chrome.com/extensions/runtime#method-sendMessage
https://developer.chrome.com/extensions/runtime#event-onMessage
In my background script, or anywhere but a content script, I am trying to listen for the active tab, or a newly opened tab.
I have this, but this is apparently incorrect:
What I am trying to do is to inject a content-script into the active tab. I don't want my content script to run for every tab/window, just for select tabs.
Does anyone know how to inject a content script for certain tabs? I can't figure it out. I assume the best way to inject content scripts is from the background script.
If you want to run a code in already activated tab (chrome.tabs.query):
chrome.tabs.query({active: true, currentWindow: true}, function(foundTabs) {
const activeTabId = foundTabs[0].id;
chrome.tabs.executeScript(activeTabId, {
file: 'inject.js'
});
})
In case you want to inject a file into every newly activated tab (chrome.tabs.onActivated):
chrome.tabs.onActivated.addListener(function(activeInfo) {
chrome.tabs.executeScript(activeInfo.tabId, {
file: 'inject.js'
});
});
Be sure to check if you have injected the file already, in order to prevent multiple injections.
For both cases, permissions should include tabs.
I was missing the:
"webNavigation"
permission in manifest.json. After adding that permission, I now have:
chrome.webNavigation.onDOMContentLoaded.addListener(function (details) {
const tabId = details.tabId;
chrome.tabs.executeScript(tabId, {
file: 'inject.js'
});
});
now it works.
some chrome API we cant access in content script in that case do that code in background.js and do according action from background js. if you want to do some action in content script in that case you need to communicate using send message
I am completely new to FF extension creation, and I will be rather specific.
When I run a JS command in, for example, Firefox's built-in sandbox, it works just fine, so I would like to make a FF addon where on click the same JS command would execute. The command basically works in a web page:
javascript:$('.plus').click();
I suppose it doesn't have to work like this:
var Widget = require("widget").Widget;
var tabs = require('tabs');
exports.main = function() {
new Widget({
id: "user-widget-1",
label: "My Mozilla Widget",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function(event) {
javascript:$('.plus').click();
}
});
};
Thanks.
$(.plus) is jQuery syntax so you probably want to load jQuery as a content script into your widget. And you want to run your code as a content script as well, e.g.:
var data = require("sdk/self").data;
new Widget({
id: "user-widget-1",
label: "My Mozilla Widget",
contentURL: "http://example.com/foo.html",
contentScriptFile: [data.url("jquery.js"), data.url("myScript.js")]
});
And data/myScript.js would be something like:
$(document).ready(function() {
$('.plus').click()
});
If you don't want the click to happen when the document loads but rather on a specific event happening in your extension, you might want to look into communicating with content scripts. I also recommend reading a general overview of content scripts.
Use Page-Mod to inject content-scripts into a web page, content-script linked in a widget only works in the widget content. Moreover the widget API is deprecated from FF 29, I suggest you to use a Action button instead.
I got this problem during development of my own addon, but I get the same with the reddit example, so I'll use that for simplicity.
Using the exact code from the example found here, this is what happens.
Reddit Example
This example add-on creates a panel containing the mobile version of Reddit. When the user clicks on the title of a story in the panel, the add-on opens the linked story in a new tab in the main browser window.
To accomplish this the add-on needs to run a content script in the context of the Reddit page which intercepts mouse clicks on each title link and fetches the link's target URL. The content script then needs to send the URL to the add-on script.
main.js:
var data = require("self").data;
var reddit_panel = require("panel").Panel({
width: 240,
height: 320,
contentURL: "http://www.reddit.com/.mobile?keep_extension=True",
contentScriptFile: [data.url("jquery-1.4.4.min.js"),
data.url("panel.js")]
});
reddit_panel.port.on("click", function(url) {
require("tabs").open(url);
});
require("widget").Widget({
id: "open-reddit-btn",
label: "Reddit",
contentURL: "http://www.reddit.com/static/favicon.ico",
panel: reddit_panel
});
panel.js:
$(window).click(function (event) {
var t = event.target;
// Don't intercept the click if it isn't on a link.
if (t.nodeName != "A")
return;
// Don't intercept the click if it was on one of the links in the header
// or next/previous footer, since those links should load in the panel itself.
if ($(t).parents('#header').length || $(t).parents('.nextprev').length)
return;
// Intercept the click, passing it to the addon, which will load it in a tab.
event.stopPropagation();
event.preventDefault();
self.port.emit('click', t.toString());
});
The icon is displayed in the bar, and clicking it launches the panel. Clicking a link within the panel opens it in a new tab - just as described and expected.
Clicking the "next page" link within the tab successfully fetches the next page, within the panel - as expected.
Clicking a link on the 2nd page does NOT open it in a tab, it opens it WITHIN the panel.
Here's my guess: When the page reloads within the panel, it does not reload the script specified in the contentScriptFile. Does anyone else experience this? And is there a workaround?
I'm using SDK 1.0 and FF 5.0
Question cross-posted on the Addon forum here
Recieved this answer at the Mozilla forum, and will post here as well for future reference.
Perhaps you are seeing Bug 667664 - Panel content scripts don't work after reloading or changing location. I think the workaround is to load the content in an iframe.
Which I believe might be the case, I'll try this during the day and report back.
EDIT: iframe seems to do the trick
1- OPEN FIREBUG, on the console tab
2- OPEN YOUR GMAIL ACCOUNT,
3- when gmail is loaded, click on one of your label (at the left under the draft box)
4- WITH FIREBUG YOU SEE THAT THE PAGE DOES NOT COMLETLY RELAOD SINCE ALL PREVIOUS ACTION STILL THERE FOR THE CURRENT DOCUMENT, BUT THE BROWSER COMPLETLY ACT LIKE THE PAGE HAVE BEEN RELOADED, stop button browser own loading effect, etc...)
5- !!!!! this is it..!!!!
Does some on have a clue on how site like Gmail can make the browser load on ajax call ( I mean show the loading icon and all, history, etc)
I already know what to check for the history navigation but how in the world they can make the browser to act like this was a simple link that load a complete new page.
from what I see with things like firebug Gmail basically retrieve mail information in JSON and than use some Javascript to render it to the user. But how they make the browser load in the while.
In gmail once it is loaded, obviously they ain't load all the data, from all your folder in background, so when you click on some of your folder and the data is not already loaded they make the browser 'load' like if it were loading a complete new page, while they retrieve the information from their server with some ajax call ( in Firefox you see the browser act like when you click on a normal link, loading icon, stop (x) button activated, and all).
Is it clear?
I came up with some 'ugly' code to achieve my goal that work quite nice in FireFox and IE (sadly it seems to not work in Chrome/WebKit and Opera).
I tell the browser to go to a url that it will not be able to reach before the ajax call end, with window.location=. The browser start to load and than when the ajax call sucess I call window.stop() (window.document.execCommand('Stop') for IE) than innerHTML the ajax data in the document
To me its look ugly and since it not work properly in Chrome/Webkit, this is apparently not the way to go.
There are many ways to utilize AJAX.
Gmail needs to load a lot of files/data before something meaningful can be displayed for the users.
E.g. showing the folder tree first doesn't make sense if it's not clickable or not ready for any interactive use.
Hence, what they do is show something lightweight like a loading graphic/progress bar while asynchronously (behind the scene), pull more data from the server until they can populate the page with a full interface for usage.
I don't know how to explain further. Maybe wiki can help: http://en.wikipedia.org/wiki/Ajax_%28programming%29
http://www.stevesouders.com/blog/2009/04/27/loading-scripts-without-blocking/
Use one of the methods shown as triggering a browser busy state in the table on the page above.
document.getElementById('iframe').src = "http://www.exemple.com/browser_load.html";
They are using iFrame. By changing the source of the iFrame.
Sitepoint has a book "Build Your Own AJAX Applications" and they show some content (all?) in this tutorial:
http://articles.sitepoint.com/article/build-your-own-ajax-web-apps
They will guide you with your AJAX coding.
Think this is your answer:
http://www.obviously.com/tech_tips/slow_load_technique
Looks like gmail and facebook method (browser is showing page as "loading" with loading icons etc. - it is just simulating, because there is a background ajax request) :)
$(function($){
$('a').attr('onclick','return false;').click(function(){
var title = $(this).attr('title');
var href = $(this).attr('href');
$('title').html(title);
$('#content').load(href+' #content', function(){
history.pushState(null, null, href);
}, function(responseText) {
var title = responseText.match(/<title>([^<]*)/)[1];
document.title = title;
});
});
});
window.onpopstate = function( e ) {
var returnLocation = history.location || document.location;
var returnTitle = history.propertyName || document.title;
$('title').html(returnLocation.title)
$('#content').load(returnLocation.href+ ' #content', function(){
history.pushState(null, null, href);
}, function(responseText) {
var title = responseText.match(/<title>([^<]*)/)[1];
document.title = title;
});
}