I am redirecting WebRequests successfully using the experimental API in Chrome - I am wondering if there is a way to get to the request/URL of the document that caused the URL to be loaded. I.e. the HTML document's URL that triggered, say, the CSS file to load. Something along the lines of the pseudocode:
function onBeforeRequest(details) {
var incoming = details.url;
var referrer = referrer_from_details(details);
var outgoing;
if(referrer.match(someRE)) {
outgoing = "one place";
} else {
outgoing = "somewhere else";
}
return { redirectUrl: outgoing };
}
What I am missing is the referrer_from_details function. The closest thing I could find was to go via tab/frame IDs to get to the URL, but not only did that seem like the wrong way around, it also is asynchronous (AFAIK).
Anybody know how to get the referrer out?
You can access the Referer header in details.requestHeaders:
function(details) {
for (var i = 0; i < details.requestHeaders.length; ++i) {
if (details.requestHeaders[i].name === 'Referer') {
alert(details.requestHeaders[i].value);
break;
}
}
return {requestHeaders: details.requestHeaders};
}
Related
I need to get this small script inserted onto all of my website pages.
The script has to contain the token set for that domain.
I have 21 different domains.
Each domain has an HTML page with all the sites under that domain listed on it.
It isn't as easy as wwww.site.com/domain/path, not all the sites are under the same 'domain name' but are considered under that 'domain'.
So my thought was to write a script that upon the page loading, read in the site pathname and compare it to each list until a match is found.
Look at my array of {domain, tokens} and find the correct token for that domain and have a function write the script with the token and insert it into the page.
The Issue: AJAX will not quit the loop of searching the list of site URLs after a match has been found so this is all a massive drain on the server.
I know there have been several stackoverflow questions related to that very problem and I have tried all of the solutions posted. My real question is, how can I not use AJAX to read in and do the compare for the site pathnames?
Is there a better way to solve this problem?
*The key to using the 21 different HTML pages with the listed sites for each is, that list is dynamically updated with new site releases and deleted sites which happen frequently enough.
The script I need to be inserted is an analytics tracking code.
Latest Attempt at this (pseudo code because I can not post direct links/information)
let tokens = [{ domain: "Example",
url: "//example.html",
token: "ExampleToken"
},
etc
];
let index = 0;
jQuery( document ).ready(function()
{
loopyloop(); //start index at 0
});
function loopyloop()
{
jQuery.ajax({
type: "GET",
ajaxToken: index,
url: tokens[index].url,
success: function(data)
{
findToken(data);
},
error: function() {
console.log('fail');
}
});
}
function findToken(data)
{
//data is a very large string of <html> format, use replace to find a match with the domain
var urlRegex = /(https?:\/\/[^\s]+)/g;
data.replace(urlRegex, function(url) {
//compare the found URLs to the pathname/URL of the page
if (url.toLowerCase().indexOf(window.location.pathname.toLowerCase()) >= 0)
{
create_script(tokens[index].token, tokens[index].domain);
return false;
}
else if(index+1 < tokens.length)
{
index += 1;
console.log(index, tokens[index].domain);
loopyloop();
}
});
}
//this part has been tested and works correctly when called, no need to post this actual code
function create_script(token, domain)
{
console.log('token found: '+ domain, token);
}
I would try converting it to an async function, so that you can do a proper loop with await and break out of it when you're ready
Something potentially resembling this:
async function loopyLoop() {
try {
while(true) {
let data = await axios.get(``);
// some logic to extract token and compare it
if (somethingsomething) {
continue;
} else if (somethingelse) {
break;
}
}
} catch(e) {
console.log('loop errored');
console.log(e.toString());
}
}
How can I set the badge number on a specific tab only? So far I have a code that sets the badge number on all the tabs.. I've been reading around A LOT, but there doesn't seem to be a whole lot of information about this, so perhaps I will find a solution for this here.
I would like something like Adblock Plus, which sets the badge number for a specific tab. This is pretty easy in Chrome etc, but doesn't seem to be the case in Safari.
Does anyone know how extensions like Adblock plus shows the badge number on a specific tab?
So far I only have this code, but as mentioned, it sets the badge on all the tabs, which is not the result I want.
safari.extension.toolbarItems[0].badge = 2;
Edit:
I have been looking at the source code of Adblock plus, and a few other extensions that had this function. And it seems it is using some prototype.
Adblock plus background snippet:
BrowserAction.prototype = {
_set: function(name, value)
{
var toolbarItem = getToolbarItemForWindow(this._page._tab.browserWindow);
if (!toolbarItem)
{
return;
}
var property = toolbarItemProperties[name];
if (!property)
{
property = toolbarItemProperties[name] = {
pages: new ext.PageMap(),
global: toolbarItem[name]
};
}
property.pages.set(this._page, value);
if (isPageActive(this._page))
{
toolbarItem[name] = value;
}
},
setIcon: function(path)
{
this._set("image", safari.extension.baseURI + path.replace("$size", "16"));
},
setBadge: function(badge)
{
if (!badge)
{
this._set("badge", 0);
}
else if ("number" in badge)
{
this._set("badge", badge.number);
}
}
};
Content script (adblockplus.js)
FilterNotifier.on("filter.hitCount", function(filter, newValue, oldValue, page)
{
if (!(filter instanceof BlockingFilter) || !page)
{
return;
}
Prefs.blocked_total++;
var blocked = blockedPerPage.get(page) || 0;
blockedPerPage.set(page, ++blocked);
if (Prefs.show_statsinicon)
{
page.browserAction.setBadge(
{
color: badgeColor,
number: blocked
});
}
});
It seems this is how Adblock plus does it, but so far I haven't been able to replicate it. Still trying though..
Okay, so I finally found a solution for this, and thought I would share what I did, in case somebody else is in the same situation.
This morning I got the idea of storing the data in an array, when the user visits one of the websites I want to display the badge number on (doesn't store all websites the user visits), only if it matched one of the websites I wanted to target. I stored the following data in the array: root domain (example.com) and the badgeNumber.
For this to work, you need to make an array of the root domain of the websites you want to target, and then only execute the following when it matches, otherwise the array would fill up very quickly, and we don't want too much data in it.
In the global page, start by making an empty array to store the data
var badgeUpdateArray = [];
You then need to set up message handling in your global page as well.
safari.application.addEventListener('message', handleMessage, false);
function handleMessage(event) {
if(event.name === "setBadgeText"){
var id = badgeUpdateArray.length + 1;
var isFound = 0;
var found = badgeUpdateArray.some(function (el) {
if(el.identifier === event.message.identifier){
// Was found
isFound = 1;
}
});
if (isFound == 0) {
// Not found, add to the array
badgeUpdateArray.push({identifier:event.message.identifier,badgeNumber:event.message.badgeNumber});
}
// Set the badge number
safari.extension.toolbarItems[0].badge = event.message.badgeNumber;
}
}
Now we need to send the message from the content script to the global page. You need to get the root domain (example.com), I'm not getting into that here, as it's pretty easy. You will also need the badgeNumber value, this can be gathered from wherever (GET request, or elsewhere..)
Remember, only execute this code if the website matches your target domains.
var message = {
identifier: domain,
badgeNumber: rows.length
}
safari.self.tab.dispatchMessage("setBadgeText", message);
This will send the message, and store the data in the array, it will also set the badge number.
Now, for this to be working on different tabs, you will need to make an event handler for "activate" on the global page, this will run whenever a tab is active.
safari.application.addEventListener("activate", updateBadge, true);
function updateBadge(){
var cDomain = safari.application.activeBrowserWindow.activeTab.url;
cDomain = cDomain.replace("www3.","");
cDomain = cDomain.replace("www2.","");
cDomain = cDomain.replace("www1.","");
cDomain = cDomain.replace("www.","");
cDomain = new URL(cDomain);
cDomain = cDomain.hostname;
var id = badgeUpdateArray.length + 1;
var isFound = 0;
var badgeNumber = 0;
var found = badgeUpdateArray.some(function (el) {
badgeNumber = el.badgeNumber;
if(el.identifier === cDomain){
// Was found, set the badge number
isFound = 1;
safari.extension.toolbarItems[0].badge = el.badgeNumber;
}
});
if (isFound == 0) {
// Was not found
safari.extension.toolbarItems[0].badge = 0;
}
}
Hopefully I've got it all in here, and at least something that works, though I have to say that I would prefer an easier way of storing it.. like Chrome etc does it, with the tab API.
I want to get :hover css for multiple elements with js/jquery and I already have the working function but I get this eror:
SecurityError: The operation is insecure.
ruleList = sheetList[i].cssRules;
Function:
function getStyleBySelector(selector){
var sheetList = document.styleSheets;
var ruleList;
var i, j;
for (i=sheetList.length-1; i >= 0; i--){
ruleList = sheetList[i].cssRules;
for (j=0; j<ruleList.length; j++){
if (ruleList[j].type == CSSRule.STYLE_RULE && ruleList[j].selectorText == selector){
return ruleList[j].style;
}
}
}
return null;
}
getStyleBySelector('#desc a:hover').background
I know it's a CORS problem, because I import CSS from another domain + google fonts but I have this in my .htaccess
Access-Control-Allow-Origin "*"
This is more a firefox bug. Any help with this please?
There are three ways of solving this issue:
1) Don’t use the original CSS rules for it but query the DOM instead:
var $selector = document.querySelector('#desc a:hover');
var computedStyle;
var background = [];
// Read value of background and write into background array
for (var i = 0; i < $selector.length; i++) {
computedStyle = window.getComputedStyle($selector[i], null);
background.push(computedStyle.getPropertyValue('background'));
}
2) Exclude external styelsheets:
This excludes foreign stylesheets so you don’t run into security issues (but it then misses out matching selectors of external stylesheets)
var getCSSHost = function (href) {
var fakeLinkOfSheet = document.createElement('a');
fakeLinkOfSheet.href = href;
return fakeLinkOfSheet.host;
};
var sheetHost = getCSSHost(stylesheet.href);
if ((sheetHost !== window.location.host)) {
return; //exit early, i.e. return false
}
// else go on
3) Proxy external resources
As John suggested in his answer you could also proxy the external resources to the same server and port to get it working. This is a big effort and you should probably consider this if nothing else works only.
Since you cannot modify the header sent by Google/other domains. We have to go for a workaround.
I think a way to do this is that you set up a proxy to those resources: i.e., you request your server for those files, your server grab those files, sent them to you with appropriate header(i.e. set those CORS headers). And then you can do this.
I am trying to create a small Chrome Extension to add a header when a user is on one of our Public IP address. I am relatively new to javascript, and could use a little help.
Here is the function to get the public IP address:
function getIP(){
$.getJSON("http://ip-api.com/json",
function(data){
if(data.query == 'xxx.x.xxx.xxx'){var indist = 1;}else{var indist = 0;}
console.log(indist);
return(indist);
});
}
When I retrieve the Pubic IP
var indist = getIP();
Then the modification of headers in the Chrome Extension.
chrome.webRequest.onBeforeSendHeaders.addListener(
function(details) {
if(indist === 1){
details.requestHeaders.push({name:"X-YouTube-Edu-Filter",value:"xxxxxxxxxxxxx"});
return {requestHeaders: details.requestHeaders};}
},{urls: ["<all_urls>"]},["requestHeaders", "blocking"]
);
This is my first shot at it, and I am not getting the results, I would like. Ideally, when a user makes a request for anything on Youtube, I would like to check the users Public IP Address once, then add the header based on that address.
Any help would be greatly appreciated!
Your function getIP does not actually return anything, because $.getJSON is asynchronous.
Your code looks like this:
function getIP(){
$.getJSON("http://ip-api.com/json", someFunction);
}
And this reads as "queue up the request, and when it's finished call someFunction". Then the statement terminates, and your function getIP ends without returning anything.
The value you return in your callback is, therefore, lost.
Additionally, your scoping is wrong:
if(data.query == 'xxx.x.xxx.xxx') {
/* scope begins */
var indist = 1; // Local, only exists within this branch!
/* scope ends */
} else {
var indist = 0;
}
console.log(indist); // Is not related to above, probably undefined
As a simple fix, since you're using a global variable anyway in your onBeforeSendHeaders handler, just assign it inside the callback:
function getIP(){
$.getJSON(
"http://ip-api.com/json",
function(data) {
if(data.query == 'xxx.x.xxx.xxx') {
indist = 1; // GLOBAL variable
} else {
indist = 0;
}
console.log(indist);
}
);
}
That said, you probably want to limit the damage your code does. Running blocking code on EVERY SINGLE network request will slow Chrome to a crawl. Rewrite your code to only enable the handler if the condition is met, and then probably only run it on YouTube requests.
function getIP(){
$.getJSON(
"http://ip-api.com/json",
function(data) {
chrome.webRequest.onBeforeSendHeaders.removeListener(addHeader);
if(data.query == 'xxx.x.xxx.xxx') {
chrome.webRequest.onBeforeSendHeaders.addListener(
addHeader,
{urls: ["*://*.youtube.com/*"]},
["requestHeaders", "blocking"]
);
}
}
);
}
function addHeader(details) {
details.requestHeaders.push({name:"X-YouTube-Edu-Filter",value:"xxxxxxxxxxxxx"});
return {requestHeaders: details.requestHeaders};
}
I have a basic HTML text area which will be used to have URLs pasted into it. Once some URLs are passed into the text area, those will be sent to a server-side script for processing via AJAX. I'll be binding those whole process to a keyUp event.
The issue is: How will I know I'm sending valid URLs to the script with a client-side check? I don't want to start sending URLs to the PHP script without having them validated in Javascript/jQuery first.
This would be quite easy to solve of the text area accepted one URL only, but the text area needs to accept multiple URLs separated by line breaks. So for example, I'd need to validate this:
http://someurl.com/something.ex
https://someurl.com/somethingelse.ext
I-M-NOT-AN-URL
So from the above, only the URLs would be sent to the server and I-M-NOT-AN-URL would be ignored.
I've not tried anything in regards to this issue since I'm not very familiar with JS, nor found anything as I couldn't come up with a relevant search term I guess, so I'm asking here for help.
Any kind of help on how to tackle this issue would be appreciated.
Update
Based on the comments and answer below, I've come up with the following Javascript/jQuery. I don't know if it efficient, therefore I'm sharing it with you for feedback and help. I don't seem to know how to prepare logic that well in JS... That's lame from my side.
Anyway here I go:
var char_start = 10;
var index = 0;
var urls = $('textarea.remote-area');
var val_ary = [];
var urls_ary = [];
var single_url = '';
urls.keyup(function(){
if (urls.val().length >= char_start)
{
var has_lbrs = /\r|\n/i.test(urls.val());
if (has_lbrs) {
val_ary = urls.val().split('\n');
for (var i = 0; i < val_ary.length; i++)
{
if (!validate_url(val_ary[i]))
{
continue;
}
urls_ary[i] = val_ary[i];
}
}
else
{
if (validate_url(urls.val()))
{
single_url = urls.val();
}
}
if (urls_ary.length > 0)
{
for (var i = 0; i < urls_ary.length; i++)
{
$.ajax({
// do AJAX here.
});
}
}
else
{
$.ajax({
// do AJAX here.
});
}
}
});
function validate_url(url)
{
if(/^([a-z]([a-z]|\d|\+|-|\.)*):(\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*#)?((\[(|(v[\da-f]{1,}\.(([a-z]|\d|-|\.|_|~)|[!\$&'\(\)\*\+,;=]|:)+))\])|((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=])*)(:\d*)?)(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)*)*|(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)*)*)?)|((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)*)*)|((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)){0})(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)|\/|\?)*)?$/i.test(url)){
return true;
}
return false;
}
The jQuery validation plugin makes use of a method such as this:
var anyURL = "http://www.yahoo.com/";
if(/^([a-z]([a-z]|\d|\+|-|\.)*):(\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*#)?((\[(|(v[\da-f]{1,}\.(([a-z]|\d|-|\.|_|~)|[!\$&'\(\)\*\+,;=]|:)+))\])|((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=])*)(:\d*)?)(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)*)*|(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)*)*)?)|((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)*)*)|((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)){0})(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)|\/|\?)*)?$/i.test(anyURL)) {
/* the URL is valid */
} else {
/* the URL is invalid)
}
You can use that code directly or use the validation plugin itself.
Please note: it may be that the plugin has evolved and the actual code is different now. Nonetheless, the above should help you.