When writing a Chrome extension, given a tab, how can I get the URL of the previously-visited page in that tab? i.e. the url that will appear in the omnibar after I hit "back"?
Since I could not find any API approach, I just applied vux777's suggestion above: every time a page loads I store a mapping from its id to its URL. Then when I want to find the previous page of a tab, I can search for it there.
So, storage:
chrome.webNavigation.onCommitted.addListener(function (data) {
if (data.frameId !== 0) {
// Don't trigger on iframes
return;
}
var tabIdToUrl = {};
tabIdToUrl[data.tabId.toString()] = data.url;
chrome.storage.local.set(tabIdToUrl);
});
And retrieval:
chrome.storage.local.get(tabId, function (item) {
var url = item[tabId];
...
});
I am running into the same issue, really wished that chrome api could return both the before and after url at chrome.tabs.onUpdated event.
My solution is similar to #Oak, but instead of using chrome.storage.local I am using Window.sessionStorage due to the following two reasons:
chrome.storage.local behaves similarly to Window.localStorage, it persists even when the browser is closed and reopened. If you don't do cleanup yourself, your local storage will grow overtime with a lot of redundant information. With session storage, whenever you closed all of your browser instances (end of persistent background page's lifetime). it will conveniently forget everything :)
Window.sessionStorage stores data in strings only, which is good for this use case (tab.url), chrome.storage.local is a more powerful tool, you can save some space when you want to store objects.
my test case is something like this:
chrome.tabs.onUpdated.addListener(function(tabId,changeInfo,tab){
var newUrl = changeInfo.url;
if(newUrl){
window.sessionStorage[tabId] = newUrl;
}
});
Another approach uses the referrer of the page. This requires that:
there must be some way to retrieve the page referrer, either by loading a content script into the page that communicates the referrer to the extension, or by somehow inspecting the web navigation or request as it is happening in the background script to retrieve the Referer header (notice the typo)
the page that referred to the current page must have a referrer policy that provides sufficient information
content-script.js
// notify extension that a page has loaded its content script, and send referrer
chrome.runtime.sendMessage({ referrer: document.referrer });
background.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
console.log(sender.tab.id);
console.log(request.referrer);
});
Alternatively, the extension could query a tab to get its referrer. You must ensure the tab is ready (has a loaded content script):
content-script.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
sendResponse({ referrer: document.referrer });
});
background.js
function askTabForReferrer(tabId) {
chrome.tabs.sendMessage(tabId, {}, function(response) {
console.log(response.referrer);
});
}
const exisitingTabWithLoadedContentScriptId = 83;
askTabForReferrer(exisitingTabWithLoadedContentScriptId);
Related
Is there any way to get the previous URL in JavaScript? Something like this:
alert("previous url is: " + window.history.previous.href);
Is there something like that? Or should I just store it in a cookie? I only need to know so I can do transitions from the previous URL to the current URL without anchors and all that.
document.referrer
in many cases will get you the URL of the last page the user visited, if they got to the current page by clicking a link (versus typing directly into the address bar, or I believe in some cases, by submitting a form?). Specified by DOM Level 2. More here.
window.history allows navigation, but not access to URLs in the session for security and privacy reasons. If more detailed URL history was available, then every site you visit could see all the other sites you'd been to.
If you're dealing with state moving around your own site, then it's possibly less fragile and certainly more useful to use one of the normal session management techniques: cookie data, URL params, or server side session info.
If you want to go to the previous page without knowing the url, you could use the new History api.
history.back(); //Go to the previous page
history.forward(); //Go to the next page in the stack
history.go(index); //Where index could be 1, -1, 56, etc.
But you can't manipulate the content of the history stack on browser that doesn't support the HTML5 History API
For more information see the doc
If you are writing a web app or single page application (SPA) where routing takes place in the app/browser rather than a round-trip to the server, you can do the following:
window.history.pushState({ prevUrl: window.location.href }, null, "/new/path/in/your/app")
Then, in your new route, you can do the following to retrieve the previous URL:
window.history.state.prevUrl // your previous url
document.referrer is not the same as the actual URL in all situations.
I have an application where I need to establish a frameset with 2 frames. One frame is known, the other is the page I am linking from. It would seem that document.referrer would be ideal because you would not have to pass the actual file name to the frameset document.
However, if you later change the bottom frame page and then use history.back() it does not load the original page into the bottom frame, instead it reloads document.referrer and as a result the frameset is gone and you are back to the original starting window.
Took me a little while to understand this. So in the history array, document.referrer is not only a URL, it is apparently the referrer window specification as well. At least, that is the best way I can understand it at this time.
<script type="text/javascript">
document.write(document.referrer);
</script>
document.referrer serves your purpose, but it doesn't work for Internet Explorer versions earlier than IE9.
It will work for other popular browsers, like Chrome, Mozilla, Opera, Safari etc.
If anyone is coming from React-world, I ended up solving my use-case using a combination of history-library, useEffect and localStorage
When user selects new project:
function selectProject(customer_id: string, project_id: string){
const projectUrl = `/customer/${customer_id}/project/${project_id}`
localStorage.setItem("selected-project", projectUrl)
history.push(projectUrl)
}
When user comes back from another website. If there's something in localStorage, send him there.
useEffect(() => {
const projectUrl = localStorage.getItem("selected-project")
if (projectUrl) {
history.push(projectUrl)
}
}, [history])
When user has exited a project, empty localStorage
const selectProject = () => {
localStorage.removeItem("selected-project")
history.push("/")
}
I had the same issue on a SPA Single Page App, the easiest way I solved this issue was with local storage as follows:
I stored the url I needed in local storage
useEffect(() => {
const pathname = window.location.href; //this gives me current Url
localstorage.setItem('pageUrl',JSON.stringify(pathname))
}, []);
On the next screen (or few screens later) I fetched the url can replaced it as follows
useEffect(() => {
const pathname = localstorage.getItem('pageUrl');
return pathname ? JSON.parse(pathname) : ''
window.location.href = pathname; //this takes prevUrl from local storage and sets it
}, []);
Those of you using Node.js and Express can set a session cookie that will remember the current page URL, thus allowing you to check the referrer on the next page load. Here's an example that uses the express-session middleware:
//Add me after the express-session middleware
app.use((req, res, next) => {
req.session.referrer = req.protocol + '://' + req.get('host') + req.originalUrl;
next();
});
You can then check for the existance of a referrer cookie like so:
if ( req.session.referrer ) console.log(req.session.referrer);
Do not assume that a referrer cookie always exists with this method as it will not be available on instances where the previous URL was another website, the session was cleaned or was just created (first-time website load).
Wokaround that work even if document.referrer is empty:
let previousUrl = null;
if(document.referrer){
previousUrl = document.referrer;
sessionStorage.setItem("isTrickApplied",false);
}else{
let isTrickApplied= sessionStorage.getItem("isTrickApplied");
if(isTrickApplied){
previousUrl = sessionStorage.getItem("prev");
sessionStorage.setItem("isTrickApplied",false);
}else{
history.back(); //Go to the previous page
sessionStorage.setItem("prev",window.location.href);
sessionStorage.setItem("isTrickApplied",true);
history.forward(); //Go to the next page in the stack
}
}
the code below checks whether a url is loaded and then logs to the console. I would like to know if there is simple, clean method to check if a page is loaded from bfcache or http cache? Firefox documentation states that the load event should not be triggered if I go from URL A to B and then hit the back button to URL A, but this is not my experience, both load and PageShow is logged regardless, does anyone know why?
var tabs = require("sdk/tabs");
function onOpen(tab) {
tab.on("pageshow", logPageShow);
tab.on("load", logLoading);
}
function logPageShow(tab) {
console.log(tab.url + " -- loaded (maybe from bfcache?) ");
}
function logLoading(tab) {
console.log(tab.url + " -- loaded (not from bfcache) ");
}
tabs.on('open', onOpen);
I am not sure whether there is any purposeful API for that but a workaround that came to mind is to check value of the performance.timing.responseEnd - performance.timing.requestStart. If it is <= 5 then most likely it is HTTP or back-forward cache. Otherwise, it is a download from the web.
A way to recognize a return to the page through a back button instead of opening up a clean URL is to use history API. For example:
// on page load
var hasCameBack = window.history && window.history.state && window.history.state.customFlag;
if (!hasComeBack) {
// most likely, user has come by following a hyperlink or entering
// a URL into browser's address bar.
// we flag the page's state so that a back/forward navigation
// would reveal that on a comeback-kind of visist.
if (window.history) {
window.history.replaceState({ customFlag: true }, null, null);
}
}
else {
// handle the comeback visit situation
}
See also Manipulating the browser history article at MDN.
I need to detect the request/response headers from my browser. I'm storing these headers in temporary variables that I will use later.
I want also to reset these variables when a page in a tab is reloaded.
I think I figured out the commands thanks to the guides and other people's answers. Anyway I saw that when I want to detect the reload of a page, the reload event seems to be fired after some of the request headers of the page are retrieved again.
Here's an example of what I get immediately after I refresh the page:
Here's my code:
/* Listener to the page for reloading */
chrome.tabs.onUpdated.addListener( function(tabId,changeInfo,tab){
//Check changeInfo.url: check existence - if it does, then that means that the url was changed and thus not a refresh.
console.log(changeInfo)
if (changeInfo.url === undefined) {
//reset the variables for a certain tab id on its refresh
if (changeInfo.status == 'loading') {
console.log('Refresh happened for tab: '+ tabId)
//global variables
requestHeaders = {}
responseHeaders = {}
}
}
});
/**
* Stores HTTP request headers into an object with TabId and URI as key.
*/
chrome.webRequest.onBeforeSendHeaders.addListener(
function(req){
var tabId = req.tabId;
/* header memorization */
console.log( tabId, req.url );
});
I realize these calls are asynchronous and to concatenate them I should put the call to onBeforeSendHeaders inside the callback of tabs.onUpdated, but in this case I'm losing (as in the case I reset the buffers) some of them because some of the headers seem to be received before the onUpdated event is fired.
How can I do to capture all the HTTP requests from when the page is loaded? That is, is there a way to attach the headers capturing function before the page starts receiving them?
Do not use the chrome.tabs event at all. Rendering (chrome.tabs) is completely unrelated to fetching the resource (chrome.webRequest).
Every set of requests for a tab starts with loading the top-level frame. You can detect this kind of request by checking whether req.type == 'main_frame'.
Note that your code only works for one tab. You should probably store the data in a dictionary using the tabId as key, and delete the value of the dictionary when the chrome.tabs.onRemoved event is triggered.
I want to write an extension that does the following:
Defines a custom function
Allows Javascript code loaded from the Internet to run such a function
The function should take as a parameter an event listener. Basically, something like:
newApiFunctionDefinedInExtension( function( responseHeaders ){
console.log("Headers arrived!", responseHeaders );
} ;
Then using chrome.webRequest, my extension (which made newApiFunctionDefinedInExtension available in the first place) will call the listener (in the locally loaded page) every time response headers are received from the network.
I am new to Chrome extensions and cannot find a way to make that happen. It would be great to know:
How to make a function defined in a module available to the loaded page's scope
How to make such an EventEmitter -- is there a constructor class I can extend?
My goal is simple: the loaded page should define a function, and that function should be called every time there is a network connection.
Every webRequest event receives information about a request, including the ID of the originating tab.
So, assuming that the tab exists note 1, you can use the following flow:
// background.js
chrome.webRequest.onHeadersReceived.addListener(function(details) {
if (details.tabId == -1)
return; // Not related to any tab
chrome.tabs.sendMessage(details.tabId, {
responseHeaders: details.responseHeaders
});
}, {
urls: ['*://*/*'], // e.g. all http(s) URLs. See match patterns docs
// types: ['image'] // for example, defaults to **all** request types
}, ['responseHeaders']);
Then, in a content script (declared in the manifest file), you take the message and pass it to the web page:
// contentscript.js
chrome.runtime.onMessage.addListener(function(message) {
// Assuming that all messages from the background are meant for the page:
document.dispatchEvent(new CustomEvent('my-extension-event', {
detail: message
}));
});
After doing that, your web page can just receive these events as follows:
document.addEventListener('my-extension-event', function(event) {
var message = event.detail;
if (message.responseHeaders) {
// Do something with response headers
}
});
If you want to put an abstraction on top (e.g. implementing a custom EventEmitter), then you need to inject a script in the main execution environment, and declare your custom API over there.
note 1. For simplicity, I assumed that the tab existed. In reality, that is never true for type "main_frame" (and "sub_frame"), because the page has not yet been rendered. If you want to get response headers for the top-level/frame documents, then you need to temporarily store the response headers in some data structure (e.g. a queue / dictionary) in the background page, and send the data to the content script whenever the script is ready.
This can be implemented by using chrome.runtime.sendMessage in the content script to send a message to the background page. Then, whenever a page has loaded and the content script is ready, the background page can use sendResponse to deliver any queued messages.
I'm using Local Storage for a Chrome extensions. When I set the local storage using
localStorage['tracking'] = false;
... and then check to is local storage set using:
if(localStorage['tracking'])
{
alert('set');
} else {
alert('Not Set');
localStorage['tracking'] = true;
}
It works fine. It sets tracking to true if unset and remains set. But if I go to a new tab, and to the same page I was on in the other tab, it returns unset.
Is there any way for local storage to remain persistant, even after the browser has closed?
Edit: I am not setting the localStorage array element at the top if the script. Just realised that might confuse readers.
Try:
localStorage.setItem('tracking', false);
and
localStorage.getItem('tracking');
These are the methods used in the W3C API Specification.
Since the usage you mentioned is not specified, depending on browser implementation it is possible that you are adding new properties to the localStorage global variable in the current window object, which become unavailable when navigating away from the page.
LocalStorage works only with strings. Check this explanation.
I used to have the same problem. I would like to share my solution. However, my case may not be the same as your case.
My case ist that I had this code (localStrage['xxxx']='xxx';) in a content script. Then I had the problem when open the new tab or open new windows.
So, my solution is that I did not run this code by the content script directly. The content script will call 'chrome.extension.sendRequest' and the background.html will do set/read localStrage and return to the content script.
This will be the code in content script
chrome.extension.sendRequest({action: 'load'}, function (response) {
if(response){
alert('set');
} else {
alert('Not Set');
chrome.extension.sendRequest({action: 'set',value:'true'}, function (response) {});
}
}
This will be the code in background.html.
chrome.extension.onRequest.addListener(
function(request, sender, sendResponse) {
if (request.action == 'load') {
sendResponse(localStorage['tracking']);
}else if (request.action == 'set') {
localStorage['tracking']=request.value;
}
});