firefox addon: cannot update tab URL using web extension - javascript

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

Related

Chrome Extension I can't get the tab title correctly in my custom extension history

I'm developing a plugin for Chrome and it has History feature in the plugin. For example, clicking the button creates a new tab and saves the URL address in the history. So this function works fine. But I want to get the document.title data of the tab that was opened in the past instead of the URL address. I tried doing this with tabs[tabs.length-1].title but it didn't get the title of the opened tab. Instead it got the title of the last tab that was already open. I guess this is the problem because the tab is not open yet. How can I solve this? Can you help me please?
This is my create tab function (If the tab is already open, don't open it again.):
function openTab(tab_url) {
var isTabActive = false;
var tabId = 0;
var tabTitle = "";
totalHistoryContent++;
chrome.tabs.query({}, function(tabs) {
for(var i=0;i<tabs.length;i++) {
if(tabs[i].url.toLowerCase().includes(tab_url.toLowerCase()) == true) {
isTabActive = true;
tabId = tabs[i].id;
tabTitle = tabs[i].title; // WORKING NICE
break;
}
}
if(isTabActive == false) {
chrome.tabs.create({ url:tab_url });
tabTitle = tabs[tabs.length - 1].title; // NOT WORKING CORRECTLY
} else{
chrome.tabs.update(tabId, {selected: true});
chrome.tabs.reload(tabId);
}
alert(tabTitle);
});
if(tab_url !== "chrome-extension://" + chrome.runtime.id + "/popup.html" && tab_url !== "chrome://extensions/?id=" + chrome.runtime.id) {
setHistory(tabTitle,49); //THIS FUNCTION SAVE TITLE TO HISTORY
}
}
One of the ways that you can handle this problem is sending an ajax request to the page's url
var xmlHttp = new XMLHttpRequest();
xmlHttp.open('GET', 'tab_url', true);
xmlHttp.onreadystatechange = function () {
if (
this.readyState == 4 &&
((this.status >= 200 && this.status < 300) ||
this.status == 304)
) {
var data = this.responseText;
var contentType = this.getResponseHeader("Content-Type"); //no i18n
if (contentType && contentType.match('text/html')) {
var doc = new DOMParser().parseFromString(data, "text/html");
console.log(doc.title);
}
}
};
xmlHttp.send();
Note: This may throw CORS error at most pages, so don't forget to add "<all_urls>" in permissions in manifest(assuming you are using manifest V2).
"permissions": [
"<all_urls>"
],
If you are using manifest V3, which is recommended, refer this documentation regarding permission
Other way is to send a message from chrome.tabs.create success callback to content script, if you don't use content script you have to inject code. In message response you can send the document's title using document.title.
Either way both will be asynchronous...

Firefox - website takes me to new tab automatically and I cannot stop it

So I have a website where I can select links and click a button to open them all at the same time. When I do that Firefox takes me to one of the newly opened links automatically.
I wanted to stop this behavior, so I looked and looked, and eventually found this option:
browser.tabs.loadDivertedInBackground
Now, when I set this to true, newly opened tabs never automatically take me to them. So if I click an ad on a site that normally opens in a new tab and takes me to it, now it doesn't happen. I also tried this code:
<p><a href="#" onclick="window.open('http://google.com');
window.open('http://yahoo.com');">Click to open Google and Yahoo</a></p>
This code opens 2 links at the same time. I was thinking maybe opening multiple links at the same time somehow overrides Firefox. But no, the links opened and I was not automatically taken to any of the new tabs.
Also must be said that I'm having this problem in Firefox 75 and 74. But when I try it in Firefox 55.0.2, I don't have the problem. In Firefox 55.0.2 the "browser.tabs.loadDivertedInBackground" actually works even on the website where I have the problem (I can't share the site because it's behind login).
This appears to be the code responsible to open multiple links on the website I have an issue with:
$(document).on('click', '.statbtn', function () {
var me = $(this);
var isAnyRowSelected = false;
$('.row-checkbox').each(function () {
var t = $(this);
if (t.is(':checked')) {
isAnyRowSelected = true;
$('select[name="status[' + t.val() + ']"]').val(me.attr('id'));
}
});
if(isAnyRowSelected == false){
bootbox.alert("No Orders Selected");
}
});
$(document).on('click', '.openlink', function () {
var me = $(this);
var isAnyRowSelected = false;
$($('.row-checkbox').get()).each(function () {
var t = $(this);
if (t.is(':checked')) {
isAnyRowSelected = true;
console.log();
var win = window.open(t.data('link'), '_blank');
if (win) {
win.focus();
} else {
bootbox.alert('Please allow popups for this website');
}
}
});
So I tried everything I could think of. Many changes to the about:config, restarting my browser, unticking the "When you open a link in a new tab, switch to it immediately" option in Firefox. But nothing works. When I open links from this one site using this specific button, I always get automatically taken to one of the newly opened tabs.
Here is a similar-ish problem - https://www.reddit.com/r/firefox/comments/bnu6qq/opening_new_tab_problem/
Any ideas why this happens and how to fix it? I mean, a website shouldn't be able or allowed to override Firefoxe's native setting, right?
Okay, because I don't wanna be an ass, here is the solution.
$(document).on('click', '.statbtn', function () {
var me = $(this);
var isAnyRowSelected = false;
$('.row-checkbox').each(function () {
var t = $(this);
if (t.is(':checked')) {
isAnyRowSelected = true;
$('select[name="status[' + t.val() + ']"]').val(me.attr('id'));
}
});
if(isAnyRowSelected == false){
bootbox.alert("No Orders Selected");
}
});
$(document).on('click', '.openlink', function () {
var me = $(this);
var isAnyRowSelected = false;
$($('.row-checkbox').get().reverse()).each(function () {
var t = $(this);
if (t.is(':checked')) {
isAnyRowSelected = true;
console.log();
// var win = window.open(t.data('link'), '_blank');
setTimeout(() => window.open(t.data('link'), '_blank'),1000);
// if (win) {
// win.focus();
// } else {
// bootbox.alert('Please allow popups for this website');
// }
}
});
if(isAnyRowSelected == false){
bootbox.alert("No Orders Selected");
}
});
Basically, adding a "setTimeout" fixed it. For some reason Firefox needed the delay to process things correctly, I guess, I think. Before the delay, the actions would happen instantly, and I'll just guess that Firefox couldn't "catch up" to it in order to apply the exemption of not navigating to new tabs. But a timeout delay fixed it.
And for anyone that may run into this with a similar issue, it also required an edit in Firefox in "about:config" to set this to True.
browser.tabs.loadDivertedInBackground
That's all folks :)

Redirect javascript failing in all but Chrome

Trying to have a webpage manage a redirect, if a deeplink fails to open. If the deeplink opens, great. if it doesn't within 2 seconds, I want it to go to my website.
<script type="javascript">
setTimeout(function () { window.location = "http://mywebsite.com"; }, 25);
window.location = "my://app";
</script>
I've tested in Chrome and it works, but Firefox, IE, and Safari all block the script.
Anyone have any idea on how to handle this?
window.location.assign("http:mywebsite.com") may be a better alternative as I believe calling that function fires some additional events that may make the lifecycle of the page easy to manage.
Also in about all except Chrome you can use an IFrame to attempt to launch your protocol handler. This will help prevent the page going to about:blank and/or your script stopping due to navigating away from the page.
var createIframe = function(id, url, timeout, callback) {
var iframe;
iframe = document.createElement("iframe");
iframe.hidden = true;
iframe.id = id;
iframe.src = url;
var data = {}
data.id = id;
data.iframe = iframe;
return setTimeout(callback, timeout, null, data);
}
createIframe('tempFrame', 'http://mywebsite.com', 25, function(err, data) {
if(!err && data){
var iframe = data.iframe;
var id = data.id;
iframe = document.getElementById(id);
iframe.parent.removeChild(iframe);
}
else {
console.log('There was an error createing and removeing the iframe');
}
}

Firefox Addon observer http-on-modify-request not working properly

I have a weird bug in my addon,
the addon itself needs to add a request header parameters for a specific domain,
it's all working, but the bug is, that the observer http-on-modify-request is not called at start, only if I reload the page, then it's working.
I mean:
I go to mysite.com/ - no header modified,
I reload page - header modefied
reload again - header modefied
new tab at mysite.com/ - no header modified
reload tab - header modefied
My code, I'm using the addon sdk:
exports.main = function(options,callbacks) {
// Create observer
httpRequestObserver =
{
observe: function(subject, topic, data)
{
if (topic == "http-on-modify-request") {
//only identify to specific preference domain
var windowsService = Cc['#mozilla.org/appshell/window-mediator;1'].getService(Ci.nsIWindowMediator);
var uri = windowsService.getMostRecentWindow('navigator:browser').getBrowser().currentURI;
var domainloc = uri.host;
if (domainloc=="mysite.com"){
var httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
httpChannel.setRequestHeader("x-test", "test", false);
}
}
},
register: function()
{
var observerService = Cc["#mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
observerService.addObserver(this, "http-on-modify-request", false);
},
unregister: function()
{
var observerService = Cc["#mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
observerService.removeObserver(this, "http-on-modify-request");
}
};
//register observer
httpRequestObserver.register();
};
exports.onUnload = function(reason) {
httpRequestObserver.unregister();
};
Please help me, I searched for hours with no results.
The code is working, but not at first time of page loading,
only if I reload.
The goal is that only on mysite.com, there will be a x-text=test header request, all the time, but only on mysite.com.
currentUri on browser is the Uri that is currently loaded in the tab. At "http-on-modify-request" notification time, the request is not sent to the server yet, so if it's a new tab, the browser doesn't have any currentUri. When you refresh the tab in place, it uses the uri of the current page, and it seemingly works.
Try this instead:
if (topic == "http-on-modify-request") {
var httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
var uri = httpChannel.URI;
var domainloc = uri.host;
//only identify to specific preference domain
if (domainloc == "mysite.com") {
httpChannel.setRequestHeader("x-test", "test", false);
}
}

Differences in Chrome on various OS

I'm developing a chrome extension and i've met a very strange bug - my code works well on Mac OS, but doesn't work on Windows and Linux versions of Chrome. Versions are the same.
function captureAllScreen() {
chrome.windows.getCurrent(function(w) {
chrome.tabs.captureVisibleTab(w.id, {"format":"png"}, function(response) {
var image = response;
var url;
chrome.tabs.getSelected(w.id, function(response) {
url = response.url;
});
var viewTabUrl = [chrome.extension.getURL('app.html'),
'?id=', id++].join('');
chrome.tabs.create({url: viewTabUrl}, function(tab) {
var targetId = tab.id;
var addSnapshotImageToTab = function(tabId, changedProps, tab) {
if (tabId != targetId || changedProps.status != "complete") {
return;
};
chrome.tabs.onUpdated.removeListener(addSnapshotImageToTab);
var views = chrome.extension.getViews();
for (var i = 0; i < views.length; i++) {
var view = views[i];
if (view.location.href == viewTabUrl) {
view.twm_Draw.sendScreen(image, url); //Application-specific method
break;
}
}
window.close();
};
chrome.tabs.onUpdated.addListener(addSnapshotImageToTab);
});
});
});
};
Update:
What i want to do with this code - is to take a screenshot and tab url and send it to my extension's page. When user clicks on my extension's icon - it opens a popup with two buttons, one of it fires this function.
In Mac Os everything works - this code takes a screenshot, tab url, opens new tab with my application and sends the data there. On Linux & Windows versions of chrome it doesn't send the data, after clicking the icon in the popup you just get a blank tab opened.
I think this part might be causing problems:
var url;
chrome.tabs.getSelected(w.id, function(response) {
url = response.url;
});
//using url
The rest of the code should be wrapped into callback function, otherwise order of execution is not guaranteed.
I'm guess it's only supported on Mac, whatever it does:
view.twm_Draw.sendScreen(image, url); //Application-specific method
I don't know about Unix but on Windows you can only get a screenshot using a NPAPI plugin like the Google extension for screen capture.

Categories

Resources