You need to be logged in to post in the forums. If you do not have an account, please sign up first.
How to disable and enable the extension button per tab in Opera 12?
Please install the extension in Opera 11.64 and Opera 12 and compare the behavior:http://files.myopera.com/QuHno/downloads/buttonActivation.oex
This extension does nothing but alert "clicked" if the button is clicked.
In Opera 11.64 the extension's button is inactive until the page is loaded and the tab gets full focus on a per tab base, meaning:
If the injected script is not injected, the button stays inactive.
In Opera 12 the button stays active all the time once it is activated which is quite bad for an extension that should do something with a page on click.
I already tried do do it with messaging from the injected script but I could activate the button only once and not on a per tab basis like it was until and including Opera 11.64.
Is that a bug in the extension? In Opera 12?
I really need the same behavior of 11.64 in 12 too, how can I get it back in 12?
Please consider, that I am no native English speaker
blog (en/de) | opera:gpu | houmpäidsch (de) | Extension: cleanPages (en)
blog (en/de) | opera:gpu | houmpäidsch (de) | Extension: cleanPages (en)
In Opera 12's new Windows and Tabs API, tabs.getFocused() will always return a BrowserTab object if there is a focused tab, even if messages cannot be posted to it, unlike Opera 11.64 where it will return null if there is a focused tab, but it is inaccessible.
Instead of just checking whether the tab is non-null, you should also check whether the tab's "port" property is also non-null. To keep compatibility with Opera 11.64 and below, I would define an "isAccessible" method and use that to check whether the button should be enabled or disabled:
Instead of just checking whether the tab is non-null, you should also check whether the tab's "port" property is also non-null. To keep compatibility with Opera 11.64 and below, I would define an "isAccessible" method and use that to check whether the button should be enabled or disabled:
function isAccessible(tab) {
return !!tab && !!tab.port;
}
// click handler
var tab = opera.extension.tabs.getFocused();
if (isAccessible(tab)) {
tab.postMessage('clicked');
}
// enableButton var tab = opera.extension.tabs.getFocused(); theButton.disabled = !isAccesible(tab);
Opera 12.15 - Win 8 Pro x64 All my Opera tools -Tab Vault: Save tabs for later -AutoStack: tabs opened from a stack stay there
Works so far as long as I access a new page in a new tab, but if i click on a link inside the tab, the button doesn't seem to become disabled during the phase when the page loads.
I thought I could change that by using the following:
and in the injected script by
and it works so far - apart if the page is really fast and contains no IFRAMEs, then the button stays inactive. Any solutions?
I thought I could change that by using the following:
window.addEventListener("load", function(){
function isAccessible(tab) {
return !!tab && !!tab.port;
}
var theButton, ToolbarUIItemProperties = {
disabled: true,
title: "buttonActivation",
icon: "icon18.png",
};
theButton = opera.contexts.toolbar.createItem(ToolbarUIItemProperties);
opera.contexts.toolbar.addItem(theButton);
theButton.addEventListener("click", function(){
var tab = opera.extension.tabs.getFocused();
if (isAccessible(tab)) {
tab.postMessage('clicked');
}
}, false);
// Set up a connection to the injected script
opera.extension.onconnect = function(event){
// make sure the button disabled until the tab is ready if the tab is reloaded
theButton.disabled = true;
// post some bogus so that the injected script can answer
event.source.postMessage('CONNECT');
// Activate the button after the injected script posted back that it is ready
opera.extension.onmessage = function(ev)
{
var message = ev.data;
if(message == 'LOADED')
{
opera.postError("Injected send: " + message);
theButton.disabled = false;
};
}
}
}, false);
and in the injected script by
// receiving a message to get the port to the background script
opera.extension.onmessage = function(event)
{
opera.postError("Background send " + event.data + " to: " + document.location.href);
// It makes no sense to start manipulating the page before it is loaded
window.onload = function()
{
// I don't want IFrames
if(window == window.parent)
{
event.source.postMessage("LOADED");
}
}
};
and it works so far - apart if the page is really fast and contains no IFRAMEs, then the button stays inactive. Any solutions?
Please consider, that I am no native English speaker
blog (en/de) | opera:gpu | houmpäidsch (de) | Extension: cleanPages (en)
blog (en/de) | opera:gpu | houmpäidsch (de) | Extension: cleanPages (en)
You don't need to use an onmessage handler to get a port to the background script. It is already available as "opera.extension". The script would fail if the page finishes loading before the background script sends it a message.
I would use window.addEventListener instead of window.onload so as not to break the site if it uses window.onload.
Also, I see no reason to redefine the onmessage handler in your background script every time a new page connects. You should be able to move that outside of the onconnect handler.
Keep in mind that onconnect will fire for tabs loaded in the background, so if you disable the button each time a page connects, a tab opening in the background could disable the button for the focused tab. You should check that the connecting tab is the focused tab before disabling the button. I'm not sure how to handle this in Opera 11.64 and below, but in Opera 12.00 it would look like this:
There is probably a better way to do all of this that would avoid the timing and tab focus issues, but I'm not sure what it is at the moment.
I would use window.addEventListener instead of window.onload so as not to break the site if it uses window.onload.
// injected script
window.addEventListener('DOMContentLoaded', function() {
if (window.top === window.self) { // more reliable check for whether this is not an iframe
opera.extension.postMessage('LOADED');
}
}, false);
Also, I see no reason to redefine the onmessage handler in your background script every time a new page connects. You should be able to move that outside of the onconnect handler.
Keep in mind that onconnect will fire for tabs loaded in the background, so if you disable the button each time a page connects, a tab opening in the background could disable the button for the focused tab. You should check that the connecting tab is the focused tab before disabling the button. I'm not sure how to handle this in Opera 11.64 and below, but in Opera 12.00 it would look like this:
opera.extension.onconnect = function(e){
if (e.tab == opera.extension.tabs.getFocused()) {
// make sure the button disabled until the tab is ready if the tab is reloaded
theButton.disabled = true;
}
}
There is probably a better way to do all of this that would avoid the timing and tab focus issues, but I'm not sure what it is at the moment.
Opera 12.15 - Win 8 Pro x64 All my Opera tools -Tab Vault: Save tabs for later -AutoStack: tabs opened from a stack stay there
Yes that all works so far but: If I enable the button in one tab, it is enabled for all tabs. It would be nice if it were possible without messaging the URL and checking against the opera.extension.tabs.getFocused().url ...
... and no, I don't mean for specific pages (which is possible) but in a way: Yes, you can click the button for this tab once but not twice - or so - without messing up the status of the button display for the other tabs. At the moment it is all or nothing, or really messy messaging with all its timing problems.
... and no, I don't mean for specific pages (which is possible) but in a way: Yes, you can click the button for this tab once but not twice - or so - without messing up the status of the button display for the other tabs. At the moment it is all or nothing, or really messy messaging with all its timing problems.
Please consider, that I am no native English speaker
blog (en/de) | opera:gpu | houmpäidsch (de) | Extension: cleanPages (en)
blog (en/de) | opera:gpu | houmpäidsch (de) | Extension: cleanPages (en)
19. June 2012, 19:26:38 (edited)
Tried to change button state this way but found some strange behavior:
When I switch to a new tab button becomes enabled only after I scroll that page or switch to other tab with port. But when I switch to tab with no port/or with undefined readyState it becomes disabled immediately.
To answer OP's question it is probably possible to assign new attribute for each tab (like opera.extension.tabs.getFocused().buttonState = false) in onconnect/onmessage handler and then in onfocus handler change button.disabled according to this value.
There is another question: how to detect what tab sent a message to handler? Is it always an active tab? There is an origin attribute but what if there are several tabs with the same address?
var v12 = (typeof opera.extension.tabs.getFocused().readyState !== 'undefined');
function isAccessible (tab) {
return !!tab && (v12 ? !!tab.port && tab.readyState === 'complete' : true);
}
function toggleButton () {
button.disabled = !isAccessible(opera.extension.tabs.getFocused());
}
function onConnectHandler (e) {
var atab = opera.extension.tabs.getFocused();
if (!atab || !e) return;
// if we got a message fom the menu
if (e.origin && ~e.origin.indexOf('menu.html') && ~e.origin.indexOf('widget://')) {
} else {
// Don't know why but there is no "tab" attribute in "e" event (Opera 12 b1467)
// So how to detect if onconnect is not for an active tab?
button.disabled = true;
// Start a timed loop
var loop = setInterval(function () {
// When the page has finished loading, turn the button on
if (isAccessible(atab)) {
button.disabled = false;
clearInterval(loop);
}
}, 100);
}
}
opera.extension.onconnect = onConnectHandler;
opera.extension.tabs.onfocus = toggleButton;
When I switch to a new tab button becomes enabled only after I scroll that page or switch to other tab with port. But when I switch to tab with no port/or with undefined readyState it becomes disabled immediately.
To answer OP's question it is probably possible to assign new attribute for each tab (like opera.extension.tabs.getFocused().buttonState = false) in onconnect/onmessage handler and then in onfocus handler change button.disabled according to this value.
There is another question: how to detect what tab sent a message to handler? Is it always an active tab? There is an origin attribute but what if there are several tabs with the same address?
I'm not sure if this works, but if it does, probably the best way to detect which tab sent a message is to compare the "port" properties. That is, iterate through all the tabs until you find one with a port that matches the message source.
Also, this is probably a more reliable way to detect Opera 12, since the browser could conceivably be opened with no tabs, but the extension object will always have a "tabGroups" property:
Also, be careful with tab events. tabs.onfocus doesn't seem to create any problems, but unless I'm doing something else bizarre in my AutoStack extension, setting a tabs.oncreate handler causes tabs to be url, readyState and port-less during and after the event. In other words, doing something when a tab is created and doing just about anything else with a tab seem to be mutually exclusive at the moment. (The bug report ID I have for this is DSK-362845.)
Also, this is probably a more reliable way to detect Opera 12, since the browser could conceivably be opened with no tabs, but the extension object will always have a "tabGroups" property:
v12 = !!opera.extension.tabGroups;
Also, be careful with tab events. tabs.onfocus doesn't seem to create any problems, but unless I'm doing something else bizarre in my AutoStack extension, setting a tabs.oncreate handler causes tabs to be url, readyState and port-less during and after the event. In other words, doing something when a tab is created and doing just about anything else with a tab seem to be mutually exclusive at the moment. (The bug report ID I have for this is DSK-362845.)
Opera 12.15 - Win 8 Pro x64 All my Opera tools -Tab Vault: Save tabs for later -AutoStack: tabs opened from a stack stay there
20. June 2012, 19:56:50 (edited)
Thanks. I think this will be enough for my purposes though it produces messy logic.
So here is my idea how to disable/enable tabs:
Injected script:
upd: Seems like BrowserTab objects don't keep custom attributes through tabs switching. It is left to rely on port check (or messaging request/response) in onfocus handler. 8/
So here is my idea how to disable/enable tabs:
var v12 = (typeof opera.extension.tabGroups !== 'undefined');
function toggleButton (e) {
var atab = opera.extension.tabs.getFocused();
button.disabled = v12 ? !atab.port : !atab;
}
function setButtonState (port, state) {
if (v12) {
// in case requesting tab is active
if (port === opera.extension.tabs.getFocused().port) {
button.disabled = !state;
}
}
...
// onconnect handler
setButtonState(e.source, false);
// onmessage handler
case 'status_enabled':
setButtonState(e.source, true);
break;
...
opera.extension.tabs.onfocus = toggleButton;
Injected script:
window.addEventListener('DOMContentLoaded', function () {
if (window.top === window.self) opera.extension.postMessage({ type: 'status_enabled' });
}, true);
upd: Seems like BrowserTab objects don't keep custom attributes through tabs switching. It is left to rely on port check (or messaging request/response) in onfocus handler. 8/
You could also keep the association yourself. Make a dictionary indexed by tab id and store the extra data there.
Then again, this requires you to make sure you delete the entries whenever a tab is closed, so port checking is probably slower but more reliable.
Then again, this requires you to make sure you delete the entries whenever a tab is closed, so port checking is probably slower but more reliable.
Opera 12.15 - Win 8 Pro x64 All my Opera tools -Tab Vault: Save tabs for later -AutoStack: tabs opened from a stack stay there
Forums » Dev.Opera » Opera Extensions Development Discussions