I'm trying to build a browser for extension for myself. The idea is that when I click the icon of the plugin, it opens a page. I then want to execute some code after that new page has finished loading but somehow it doesn't work.
var result;
chrome.browserAction.onClicked.addListener(function() {
chrome.history.search(
{ text: "", maxResults: 100}, //object
function(results) { //callback
for(var item in results) {
var currItem = results[item];
if (currItem.url.indexOf("some_domain") > -1) {
result = results[item];
break;
}
}
//Go to website
chrome.tabs.create({
'url': result.url
}, function(tab) {
new_tabId = tab.id;
});
}
);
});
Now here comes the part that fails:
chrome.webNavigation.onCompleted.addListener(function(details) {
// if (check for correct URL here) {
var videos = document.getElementsByTagName("video");
var video = videos[0];
alert(videos.length); <--- always Zero! Why??
video.load();
video.play();
video.addEventListener("ended", function() { ... });
// }
});
They are both in the same background script and I do not have a content script.
The permissions in the manifest are "tabs", "history", "webNavigation"
When I check with the developer console and do:
document.getElementsByTagName("video").length I do get the correct number.
As implied by wOxxOm, what will prevent your code from working is that you are attempting to access the DOM from a background script. Specifically, the code:
var videos = document.getElementsByTagName("video");
var video = videos[0];
alert(videos.length); <--- always Zero! Why??
video.load();
video.play();
video.addEventListener("ended", function() { ... });
will not function in a background script. If you want to do this you will need to load/execute a content script.
Related
I'd like to know how to update URL addresses in Firefox using Web Extensions.
I'm trying to port a simple extension I've created with Chrome APIs to Firefox, but I don't really understand the tab URL mechanisms in Firefox.
This extension was made to switch between YouTube desktop/TV version with a click.
It works well on Chrome, but I don't know why it's not working on Firefox.
UPDATE 1: Placing most important code block related to the question:
chromeApi.browserAction.onClicked.addListener(function(tab) {
var actionUrl = '';
var tabUrl = tab.url;
if (getCurrentPageVersion(tabUrl) !== undefined) {
actionUrl = getConvertedActionUrl(tabUrl);
if (actionUrl !== tabUrl) {
chromeApi.tabs.update(tab.id, {url: actionUrl});
}
}
});
Full source
(function(chromeApi) {
getCurrentPageVersion = function (tabUrl) {
var ytValidRegex = /^(https?\:\/\/)?(www\.)?(youtube\.com|youtu\.?be)/g;
var ytValidStdPageRegex = /^(https?\:\/\/)?(www\.)?(youtube\.com|youtu\.?be)?(\/watch\?v=).+$/g;
var ytValidTvPageRegex = /^(https?\:\/\/)?(www\.)?(youtube\.com|youtu\.?be)?(\/tv#\/watch(\/video)?\/(idle|control)\?v=).+$/g;
if (!ytValidRegex.test(tabUrl)) {
return undefined;
} else if (ytValidStdPageRegex.test(tabUrl)) {
return "std";
} else if (ytValidTvPageRegex.test(tabUrl)) {
return "tv";
}
return undefined;
};
getConvertedActionUrl = function (tabUrl) {
var result = '';
var shortStdYtUrlRegex = /\/watch\?v=.+/g;
var shortTvYtUrlRegex = /\/tv#\/watch\/video\/(idle|control)\?v=.+/g;
var shortStdYtUrlReplaceRegex = /\/watch\?v=/g;
var shortTvYtUrlReplaceRegex = /\/tv#\/watch\/video\/(idle|control)\?v=/g;
if (shortStdYtUrlRegex.test(tabUrl)) {
result = tabUrl.replace(shortStdYtUrlReplaceRegex, '/tv#/watch/idle?v=');
}
else {
result = tabUrl.replace(shortTvYtUrlReplaceRegex, '/watch?v=');
}
// YouTube standard website video url
//https://www.youtube.com/watch?v=9tRDQK2MtRs
// YouTube TV url
//https://www.youtube.com/tv#/watch/video/idle?v=9tRDQK2MtRs
return result;
}
onInit = function () {
};
// Called when the user clicks on the browser action.
chromeApi.browserAction.onClicked.addListener(function(tab) {
var actionUrl = '';
var tabUrl = tab.url;
if (getCurrentPageVersion(tabUrl) !== undefined) {
actionUrl = getConvertedActionUrl(tabUrl);
if (actionUrl !== tabUrl) {
chromeApi.tabs.update(tab.id, {url: actionUrl});
}
}
});
chromeApi.tabs.onUpdated.addListener(function(tabId, changeInfo, tab){
if(!changeInfo.url) return; // URL did not change
// Might be better to analyze the URL to exclude things like anchor changes
var pageVersion = getCurrentPageVersion(tab.url);
if (pageVersion === undefined) return;
/* ... */
chromeApi.browserAction.setBadgeText({text: pageVersion.toUpperCase(), tabId: tab.id});
});
chromeApi.tabs.onCreated.addListener(function(tab){
var pageVersion = getCurrentPageVersion(tab.url);
if (pageVersion === undefined) return;
/* ... */
chromeApi.browserAction.setBadgeText({text: pageVersion.toUpperCase(), tabId: tab.id});
});
})(chrome);
If you pay attention, the core functionality happens on the chromeApi.browserAction.onClicked event, whenever you click the add-on/extension button.
The extension updates correctly between each YouTube version in Chrome, but in Firefox, this one redirects to YouTube TV once and never goes back to the desktop version no matter how many times you click on it.
But there's something weird in Firefox: browser history is updated correctly whenever the tab.update method is called, but it redirects to the TV version by itself again.
IMPORTANT: Both Firefox/Chrome extensions are using the currentTab permission, so it's not an extension issue by itself.
Extension on GitHub
UPDATE 2 (2018-11-25): I've updated the source code based on previous feedback
I am making a chrome extension to keep refreshing a page unless stop button is chosen. But i am able to do it only once. Here is my code for background.js
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
switch(request.type) {
case "table-row-count_start":
alert("Refershing started");
RefreshAndCount();
break;
case "table-row-count_stop":
alert("Stop Refershing");
break;
}
return true;
});
var RefreshAndCount = function() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {type: "table-row-count"});
chrome.browserAction.setBadgeText({tabId: tabs[0].id, text: "Counting!"});
});
};
In content.js I did this :
chrome.extension.onMessage.addListener(function(message, sender, sendResponse) {
alert(message.type);
switch(message.type) {
case "table-row-count":
var x = document.querySelector('table').rows.length;
chrome.storage.sync.set({'value': x}, function() {
console.log('Settings saved');
});
chrome.storage.sync.get(["value"], function(items){
console.log(items);
});
alert("Row count = " + x);
setTimeout(function(){
location.reload();
},100);
break;
}
});
chrome.storage.onChanged.addListener(function(changes, namespace) {
for (key in changes) {
if(key=='value'){
var storageChange = changes[key];
console.log('Storage key "%s" in namespace "%s" changed. ' +
'Old value was "%s", new value is "%s".',
key,
namespace,
storageChange.oldValue,
storageChange.newValue);
}
}
});
After refresh I want to print the current row count alert everytime. Please help how to do this .
This work fine, for a single refresh but after that I again had to choose the start button from popup.
I want some way that I need not click start button again and the whole process repeats, storing the previous row count in cache or something.
popup.js
window.onload = function() {
document.getElementById("mystartbutton").onclick = function() {
chrome.extension.sendMessage({
type: "table-row-count_start"
});
}
document.getElementById("mystopbutton").onclick = function() {
chrome.extension.sendMessage({
type: "table-row-count_stop"
});
}
}
Also help me How to keep on refershing that page even if I switch to other tab or minimise my chrome ?
You can use the chrome.storage.local to store data that will be saved over time and over context where you use it. You can set a boolean to true or false to enable or disable autoreload. Then you only have to set it at click on browser action and check it in the content-script to know if you have to reload.
A possible and simple implemtation should be like this : (It depends of the expected behavior)
content.js (have to be injected in the page to autoreload)
var reloadDuration = 5000;
function autoreload()
{
chrome.local.storage.get("autoreload_enabled", function(result)
{
if(result.autoreload_enabled)
{
chrome.runtime.sendMessage({type: "table-row-count"});
window.location.reload();
}
}
}
setTimeout(autoreload, reloadDuration);
This will reload your page every reloadDuration if the boolean set in chrome local storage named autoreload_enabled is true.
The code in buttonClicked() function and onTabCreate() function worked when the button is the BrowserAction icon.
But when I added a popup.html and created few buttons in it. When "Trello" button is clicked. I want the code in buttonClicked() to be executed, I put this code in eventPage.js.
The control is reaching the function, but I'm unable to capture the visible Tab now.
Why is it so? How can I fix this?
As evident from the code, imageData is holding the image of the tab (3rd line in buttonClicked())
The last 4th line in buttonClicked(), calls onTabCreate(). InonTabCreate(), I'm passing the imageData, but I'm not receiving the image.
I'll furnish more info if needed.
Thanks
buttonClicked()-
function buttonClicked() {
chrome.tabs.captureVisibleTab(null, {}, function (image) {
imageData = image;
createdTabUrl = chrome.extension.getURL('cardCreate.html');
chrome.tabs.query({ active: true }, function (tabs) {
var i, tab;
// Only select the current active tab, not any background tab or dev tools
for (i = 0; i < tabs.length; i += 1) {
// TODO: more robust way to check if current tab is a page from this extension (either when I get a static extension id or with a flag)
if (tabs[i].url.match(/^http/) || tabs[i].url.match(/^chrome-extension.*\/cardCreate\.html$/)) {
tab = tabs[i];
}
}
chrome.tabs.create({ url: createdTabUrl, index: (tab.index || 0) + 1 }, onTabCreated);
});
});
}
onTabCreate()-
// TODO: more robust way to send image data to page ?
function onTabCreated(tab) {
setTimeout(function (){
chrome.runtime.sendMessage({ imageData: imageData }, function(response) {
// Callback does nothing
});
}, 1000);
var views = chrome.extension.getViews();
for (var i = 0; i < views.length; i++) {
var view = views[i];
// If this view has the right URL and hasn't been used yet...
if (view.location.href == createdTabUrl) {
console.log("----------------------");
// console.log(view);
// console.log(view.location.href);
}
}
}
chrome.browserAction.onClicked.addListener(buttonClicked);
EDIT :
window.onload = function() {
document.getElementById('loginText').addEventListener('click', login);
document.getElementById('Trello').addEventListener('click', buttonClicked);
//listener for ButtonClick
// In popup.html , I included eventPage.js as well.
};
Referring to this question: Add-on Builder: ContentScript and back to Addon code?
Here is my addon code:
var widget = widgets.Widget({
id: "addon",
contentURL: data.url("icon.png"),
onClick: function() {
var workers = [];
for each (var tab in windows.activeWindow.tabs) {
var worker = tab.attach({contentScriptFile: [data.url("jquery.js"), data.url("myScript.js")]});
workers.push(worker);
}
}
});
And here is myScript.js:
var first = $(".avatar:first");
if (first.length !== 0) {
var url = first.attr("href");
self.port.emit('got-url', {url: url});
}
Now that I have multiple workers where do I put
worker.port.on('got-url', function(data) {
worker.tab.url = data.url;
});
Since in the other question I only had one worker but now I have an array of workers.
The code would be:
// main.js:
var data = require("self").data;
var windows = require("windows").browserWindows;
var widget = require("widget").Widget({
id: "addon",
label: "Some label",
contentURL: data.url("favicon.png"),
onClick: function() {
//var workers = [];
for each (var tab in windows.activeWindow.tabs) {
var worker = tab.attach({
contentScriptFile: [data.url("jquery.js"),
data.url("inject.js")]
});
worker.port.on('got-url', function(data) {
console.log(data.url);
// worker.tab.url = data.url;
});
worker.port.emit('init', true);
console.log("got here");
//workers.push(worker);
}
}
});
// inject.js
$(function() {
self.port.on('init', function() {
console.log('in init');
var first = $(".avatar:first");
if (first.length !== 0) {
var url = first.attr("href");
console.log('injected!');
self.port.emit('got-url', {url: url});
}
});
});
Edit: sorry, should have actually run the code, we had a timing issue there where the content script was injected before the worker listener was set up, so the listener was not yet created when the 'got-url' event was emitted. I work around this by deferring any action in the content script until the 'init' event is emitted into the content script.
Here's a working example on builder:
https://builder.addons.mozilla.org/addon/1045470/latest/
The remaining issue with this example is that there is no way to tell if a tab has been injected by our add-on, so we will 'leak' or use more memory every time the widget is clicked. A better approach might be to inject the content script using a page-mod when it is loaded, and only emit the 'init' event in the widget's onclick handler.
I'm writing the extension which will change layout of google in my browser.
My script using external css file when browser shows google.com. And it works fine before I opening a new tag - css is cleared. How can I match my css only for google search page?
window.addEventListener("load", function() { myExtension.init(); }, false);
var myExtension = {
init: function() {
var appcontent = document.getElementById("appcontent"); // browser
if(appcontent)
appcontent.addEventListener("DOMContentLoaded", myExtension.onPageLoad, true);
var messagepane = document.getElementById("messagepane"); // mail
if(messagepane)
messagepane.addEventListener("load", function(event) { myExtension.onPageLoad(event); }, true);
},
onPageLoad: function(aEvent) {
var patt_g=new RegExp('google.com','g');
Firebug.Console.log('Hide my ass started');
this.doc = aEvent.originalTarget; // doc is document that triggered "onload" event
CSSProvider.init();
if(patt_g.test(this.doc.location.href)) {
Firebug.Console.log('Hide my ass -> in');
CSSProvider.loadCSS();
} else {
Firebug.Console.log('Hide my ass -> out');
CSSProvider.unloadCSS();
}
// add event listener for page unload
aEvent.originalTarget.defaultView.addEventListener("unload", function(event){ myExtension.onPageUnload(event); }, true);
},
onPageUnload: function(aEvent) {
Firebug.Console.log('Hide my ass deleted');
if(patt_g.test(this.doc.location.href) ) {
Firebug.Console.log('Hide my ass -> out');
CSSProvider.unloadCSS();
}
}
};
var CSSProvider = {
init: function(){
this.sss = Components.classes["#mozilla.org/content/style-sheet-service;1"]
.getService(Components.interfaces.nsIStyleSheetService);
this.ios = Components.classes["#mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
this.uri = this.ios.newURI("chrome://hms/content/style.css", null, null);
this.isRegistered = this.sss.sheetRegistered(this.uri, this.sss.USER_SHEET)
},
loadCSS: function(){
if(!this.isRegistered){
this.sss.loadAndRegisterSheet(this.uri, this.sss.USER_SHEET);
}
},
unloadCSS: function(){
if(this.isRegistered){
this.sss.unregisterSheet(this.uri, this.sss.USER_SHEET);
}
}
};
Don't load/unload the CSS file each time a tab is opened - adding a user stylesheet will always apply it to all existing tabs, unloading it will remove it from all tabs. Simply add the stylesheet when your extension loads and put the CSS rules in the stylesheet into a #-moz-document section:
#-moz-document domain(google.com)
{
...
}
Documentation: https://developer.mozilla.org/en/CSS/#-moz-document