Chrome extension attach properties to each tab - google-chrome

In a chrome extension, I've created tab properties that I'm trying to store with each tab. What is the best way to do this? I've looked into using localStorage, but it seems like there could be an easier way. The data is by no means permanent, it only exists as long as the tab does.

There's definitely no need to use localStorage. Without the notion "data is by no means permanent", one already knows that: tab IDs are unique within a session. From this fact, it follows that the data is non-persistent.
The best method to implement it is to maintain a hash of tab properties:
chrome.tabs.onCreated (optional, add initial info to tab hash)
chrome.tabs.onUpdated - (Add/) Update tab hash (URL is available)
chrome.tabs.onRemoved - Remove hash entry
Tab objects are not expensive: All properties are primitives (booleans, numbers, strings).
For instance (background page only):
var tabStore = {}; // <-- Collection of tabs
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
tabStore[tabId] = tab;
});
chrome.tabs.onRemoved.addListener(function(tabId) {
delete tabStore[tabId];
});

IMPORTANT ADDENDUM to Rob W's answer.
Make sure to also listen to tabs.onReplaced as well, and update the tabStore accordingly.
chrome.tabs.onReplaced.addListener(function(addedTabId, removedTabId) {
tabStore[addedTabId] = tabStore[removedTabId];
delete tabStore[removedTabId];
});
Chrome can change the id of a tab under the hood without warning or signs. As far as I know, the only place that this happens is the Google "instant search" when you type in a search into the address bar. It may be an edge case, but if you don't track this, it could end up being a very insidious problem.

Related

Update the document.referrer via history.pushState()

I'm using pushStates in my ajax-app to navigate from one "page" to another. Now, I'd like to see from what page I was coming from. But document.referrer always returns "". Or, when I open my app from another page (where it is linked), I got the URL from that other page.
Shouldn't these lines...
history.pushState({}, "Some title", "/some/valid/url/1");
history.pushState({}, "Some title", "/some/valid/url/2");
...produce a referrer like this:
http://somedomain.com/some/valid/url/1
?
Or in other words: Is there any way to set the document.referrer accordingly, or at least reset it to ""?
Note: I'm looking for solutions without caching the previous URL in some variable. I need something that really changes the document.referrer, because I cannot change the scripts that rely on it.
Short Answer: use window.location instead of history.pushState
Longer Answer:
document.referrer according to MDN: "The value is an empty string if the user navigated to the page directly (not through a link, but, for example, via a bookmark)"
Directly manipulating the history state will not be treated as following a link. You can simulate a link click by update window.location (MDN) which will also update the history automatically.
For example, load a tab with https://github.com/kanaka/mal/. Then type the following one line at a time (otherwise they are all run in a single javascript execution context and only the last location update applies)
console.log(history.length) // 2
console.log(document.referrer) // ""
window.location = "/kanaka/mal/tree/master/ada"
console.log(history.length) // 3
console.log(document.referrer) // "https://github.com/kanaka/mal"
window.location = "/kanaka/mal/tree/master/python"
console.log(history.length) // 4
console.log(document.referrer) // "https://github.com/kanaka/mal/tree/master/ada"

IE (or other) add input to web form and disable field

We use Cisco's Unified Attendant web interface at work and we have an occasional issue where the user sits down to answer calls for the company and out of habit sees the "extension" field and enters their own instead of the receptionist extension.
Is there any way that I can take control of this field and input info and then disable the field? To my knowledge we do have control of the server, but not the source.
While I don't believe I can do this with simply a page and some
js.... Is it possible to tell IE or FF to load a JavaScript file on
EVERY that it returns? If this isn't possible is something like this
possible with either a FF or IE extension/plugin? Is there a purely
js/html method I'm not thinking of?
I've looked into the JavaScript/html option using iframes, but beyond that I wasn't sure where to look next.
Here is an example using javascript for the desktop. You can easily create a shortcut to it and inform the people to use this specific shortcut.
var urlConstant = "http://www.google.com/";
var ie = new ActiveXObject("InternetExplorer.Application");
ie.visible = true;
ie.navigate(urlConstant);
while(ie.readystate != 4){
}
var document = ie.document;
document.getElementById("gbqfq").value = "stackoverflow";
Obviously, change the element id and the value to your liking.
You can also create it in vbscript. This is the quickest & easiest way.

How to handle popstate when url ceased to exist?

I would like to be able to do two things with html5 popstate, I'm not using any plugins just these two methods:
Push State:
function contentLoaded(...) {
...
window.history.pushState({ pageUrl: url }, url, url);
...
}
Pop State:
$(window).bind("popstate", function (e) {
if (event.state) {
loadContent(event.state.pageUrl);
}
});
Now if I delete a record, I want to avoid popping a state which couldn't be loaded, just skip it and try popping the next one.
The second question would be: How can I avoid try popping from an empty stack (I have a back button inside my app, but I can get rid of it with an appropriate reason), but keeping clear if the content couldn't be loaded OR if there is no more items in the stack.
History is not meant to be changed afterwards. You should separate the push/popstate functionality from the content loading functionality; think the "router" or "navigator" pattern in typical client-side mvc framework. If a state has become invalid, the content loading code can "redirect" to another state (by calling pushState), just as you would do in regular server-side app.
Just to remind, a client-side application should work identically whether the state was internally popped or the page was actually loaded using the same url, i.e. the HTML5 history support must be transparent, or in other words, the url alone must contain all the information to construct a particular view (but in the case of popstate, we can cheat and reuse the existing state to speed up things).

Select multiple tabs in Chrome

I want to write an extension, which can allow users to use their mouses to select multiple tabs in Chrome (the select tabs will be highlighted), just like some multiple-tab handler extensions for Firefox, I have searched but didn't find any useful information, so I am wondering if there is a way to do it, or it's technically possible to build such an extension given Chrome's design?
Thanks!
Currently I'm using Google Chrome v20.0.1132.47 m
I can say this is already possible on that version.
All you need to do is to hold-down the CTRL key on the keyboard and then single-left click on each desired tab to multi-select them.
In addition, once they're selected, you can drag-drop them off the main window to create an independent session with those tabs on the new session. Really useful on dual monitors.
You can use the windows get function to get all of the tabs in a specified window or instead use getAll and iterate through each window then each tab within each of those windows. You must specify the option populate:true to get the tabs array to return populated. The property highlighted should tell you if that tab is highlighted.
chrome.windows.get(integer windowId, {populate:true}, function (window) {
var highlightedTabs = new Array();
for(var index in window.tabs)
if(window.tabs[index].highlighted)
highlightedTabs.push(windows.tabs[index]);
// your code here
});
For a full reference to the format of the tab object see below:
http://code.google.com/chrome/extensions/tabs.html#types

Clear all fields in a form upon going back with browser back button

I need a way to clear all the fields within a form when a user uses the browser back button. Right now, the browser remembers all the last values and displays them when you go back.
More clarification on why I need this
I've a disabled input field whose value is auto-generated using an algorithm to make it unique within a certain group of data. Once I've submitted the form and data is entered into the database, user should not be able to use the same value again to submit the same form. Hence I've disabled the input field in the first place. But if the user uses the browser back button, the browser remembers the last value and the same value is retained in the input field. Hence the user can submit the form with the same value again.
What I don't understand is what exactly happens when you press the browser back button. It seem like the entire page is retrieved from cache without ever contacting the server if the page size is within the browser cache limit. How do I ensure that the page is loaded from the server regardless of browser setting when you press the browser back button?
Another way without JavaScript is to use <form autocomplete="off"> to prevent the browser from re-filling the form with the last values.
See also this question
Tested this only with a single <input type="text"> inside the form, but works fine in current Chrome and Firefox, unfortunately not in IE10.
Modern browsers implement something known as back-forward cache (BFCache). When you hit back/forward button the actual page is not reloaded (and the scripts are never re-run).
If you have to do something in case of user hitting back/forward keys - listen for BFCache pageshow and pagehide events:
window.addEventListener("pageshow", () => {
// update hidden input field
});
See more details for Gecko and WebKit implementations.
I came across this post while searching for a way to clear the entire form related to the BFCache (back/forward button cache) in Chrome.
In addition to what Sim supplied, my use case required that the details needed to be combined with Clear Form on Back Button?.
I found that the best way to do this is in allow the form to behave as it expects, and to trigger an event:
$(window).bind("pageshow", function() {
var form = $('form');
// let the browser natively reset defaults
form[0].reset();
});
If you are not handling the input events to generate an object in JavaScript, or something else for that matter, then you are done. However, if you are listening to the events, then at least in Chrome you need to trigger a change event yourself (or whatever event you care to handle, including a custom one):
form.find(':input').not(':button,:submit,:reset,:hidden').trigger('change');
That must be added after the reset to do any good.
If you need to compatible with older browsers as well "pageshow" option might not work. Following code worked for me.
$(window).load(function() {
$('form').get(0).reset(); //clear form data on page load
});
This is what worked for me.
$(window).bind("pageshow", function() {
$("#id").val('');
$("#another_id").val('');
});
I initially had this in the $(document).ready section of my jquery, which also worked. However, I heard that not all browsers fire $(document).ready on hitting back button, so I took it out. I don't know the pros and cons of this approach, but I have tested on multiple browsers and on multiple devices, and no issues with this solution were found.
Because I have some complicated forms with some fields that are pre-fill by JS, clearing all fields is not suitable for me. So I found this solution, it detects the page was accessed by hitting the back/forward button and then does a page reload to get everything back to its original state. I think it will be useful to someone:
window.onpageshow = function(event) {
if (event.persisted || performance.getEntriesByType("navigation")[0].type === 'back_forward') {
location.reload();
}
};
As indicated in other answers setting autocomplete to "off" does the trick, but in php, what worked for me looks like this...
$form['select_state'] = array(
'#type' => 'select',
'#attributes' => array('autocomplete' =>'off'),
'#options' => $options_state,
'#default_value' => 'none');