Access-Control-Allow-Origin error in a chrome extension - google-chrome

I have a chrome extension which monitors the browser in a special way, sending some data to a web-server. In the current configuration this is the localhost. So the content script contains a code like this:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(data)...
xhr.open('GET', url, true);
xhr.send();
where url parameter is 'http://localhost/ctrl?params' (or http://127.0.0.1/ctrl?params - it doesn't matter).
Manifest-file contains all necessary permissions for cross-site requests.
The extension works fine on most sites, but on one site I get the error:
XMLHttpRequest cannot load http://localhost/ctrl?params. Origin http://www.thissite.com is not allowed by Access-Control-Allow-Origin.
I've tried several permissions which are proposed here (*://*/*, http://*/*, and <all_urls>), but no one helped to solve the problem.
So, the question is what can be wrong with this specific site (apparently there may be another sites with similar misbehaviour, and I'd like to know the nature of this), and how to fix the error?

(tl;dr: see two possible workarounds at the end of the answer)
This is the series of events that happens, which leads to the behavior that you see:
http://www.wix.com/ begins to load
It has a <script> tag that asynchronously loads the Facebook Connect script:
(function() {
var e = document.createElement('script');
e.type = 'text/javascript';
e.src = document.location.protocol +
'//connect.facebook.net/en_US/all.js';
e.async = true;
document.getElementById('fb-root').appendChild(e);
}());
Once the HTML (but not resources, including the Facebook Connect script) of the wix.com page loads, the DOMContentLoaded event fires. Since your content script uses "run_at" : "document_end", it gets injected and run at this time.
Your content script runs the following code (as best as I can tell, it wants to do the bulk of its work after the load event fires):
window.onload = function() {
// code that eventually does the cross-origin XMLHttpRequest
};
The Facebook Connect script loads, and it has its own load event handler, which it adds with this snippet:
(function() {
var oldonload=window.onload;
window.onload=function(){
// Run new onload code
if(oldonload) {
if(typeof oldonload=='string') {
eval(oldonload);
} else {
oldonload();
}
}
};
})();
(this is the first key part) Since your script set the onload property, oldonload is your script's load handler.
Eventually, all resources are loaded, and the load event handler fires.
Facebook Connect's load handler is run, which run its own code, and then invokes oldonload. (this is the second key part) Since the page is invoking your load handler, it's not running it in your script's isolated world, but in the page's "main world". Only the script's isolated world has cross-origin XMLHttpRequest access, so the request fails.
To see a simplified test case of this, see this page (which mimics http://www.wix.com), which loads this script (which mimics Facebook Connect). I've also put up simplified versions of the content script and extension manifest.
The fact that your load handler ends up running in the "main world" is most likely a manifestation of Chrome bug 87520 (the bug has security implications, so you might not be able to see it).
There are two ways to work around this:
Instead of using "run_at" : "document_end" and a load event handler, you can use the default running time (document_idle, after the document loads) and just have your code run inline.
Instead of adding your load event handler by setting the window.onload property, use window.addEventListener('load', func). That way your event handler will not be visible to the Facebook Connect, so it'll get run in the content script's isolated world.

The access control origin issue you're seeing is likely manifest in the headers for the response (out of your control), rather than the request (under your control).
Access-Control-Allow-Origin is a policy for CORS, set in the header. Using PHP, for example, you use a set of headers like the following to enable CORS:
header('Access-Control-Allow-Origin: http://blah.com');
header('Access-Control-Allow-Credentials: true' );
header('Access-Control-Allow-Headers: Content-Type, Content-Disposition, attachment');
If sounds like that if the server is setting a specific origin in this header, then your Chrome extension is following the directive to allow cross-domain (POST?) requests from only that domain.

Related

How to programmatically read-write scripts for offline usage in chrome extension?

I need to have predefined scripts, accessible from chrome content_script, that could be updated automatically from given URL.
Exactly what i do:
I have content_script.js. Inside it, i`d like to create iframe for current page from predefined html+css+js.Sometimes html or css or js can be changed. I want to avoid updating extension, instead, each time user have internet, he could load fresh html+css+js for further offline usage.
So, how to read and write some internal files within extension from content script (or delegate this task to background script)?
You can use HTML5 Filesystem to have a read/write place for files, or just store it as strings in chrome.storage (with "unlimitedStorage" permission as needed) for later reuse.
This code can then be executed in a content script using executeScript, or, if you enable 'unsafe-eval' for the extension CSP, in the main script (which is dangerous, and should be avoided in most cases).
Note that this Filesystem API has a warning that's it's only supported in Chrome, but that shouldn't be a problem (Firefox / WebExtensions platform explicitly reject self-update mechanisms).
You can do read extension file contents, but you can't write to extension folder since it is sandboxed.
To read an extension file, you can just send Ajax call using chrome.runtime.getURL("filepath") as url
var xhr = new XMLHttpRequest();
xhr.open('GET', chrome.runtime.getURL('your file path'), true);
xhr.onreadystatechange = function() {
if (chr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
var text = xhr.responseText;
// Do what you want using text
}
};
xhr.send();

Modify local storage via popup, and use stored values in contentscript

I'm trying my hand at creating a chrome extension, but am running into a wall.
I want to be able to use the browser-action popup to write/modify values into local storage (extension storage).
Then, I want to use the stored values in a content script.
From what I've read, it looks like I need a background file? but I'm not sure.
Some coded examples would be extremely appreciated!
Thanks for your help!
You can avoid using a background page as a proxy if you use chrome.storage API. It's a storage solution that is available from Content Scripts directly.
Here is a comparison between it and localStorage in the context of Chrome extensions.
An important thing to note is that it's asynchronous, making code slightly more complicated than using localStorage:
/* ... */
chrome.storage.local.get('key', function(value){
// You can use value here
});
// But not here, as it will execute before the callback
/* ... */
But to be fair, if you go with the background being the proxy for data, message passing is still asynchronous.
One can argue that once the data is passed, localStorage works as a synchronous cache.
But that localStorage object is shared with the web page, which is insecure, and nobody stops you from having your own synchronous storage cache, initialized once with chrome.storage.local.get(null, /*...*/) and kept up to date with a chrome.storage.onChanged listener.
Background pages can access the localStorage variables saved by your extension. Your content script only has access to the localStorage of the website open in a specific tab. You will therefore need to send the variables from the background page to the content script. The content script can then access these variables.
The following code saves a localStorage variable in the background script and then sends it to the content script for use.
Since you requested a coded example, I've written you one. This project would have a background page and a content script. Using localStorage in your popup will allow the background page to access these variables for use in the content script.
Something like this:
background.js
// When a tab is updated
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo) {
// When the tab has loaded
if(changeInfo.status == 'complete') {
// Query open tabs
chrome.tabs.query({'active': true, 'lastFocusedWindow': true}, function (tabs) {
// Get URL of current tab
var tabURL = tabs[0].url;
// If localStorage is not empty
if(localStorage.length != 0) {
// Set a local storage variable
localStorage.helloworld = "Hello World";
// Send message to content script
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
// Send request to show the notification
chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response) {
});
});
}
});
}
});
contentscript.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
// Use the local storage variable in some way
if(request.greeting == "hello") {
var hello = localStorage.helloworld;
// do something with the variable here
}
});
Once you have this working, consider switching to chrome.storage

Google Maps differences between async loading and general script tag

In an attempt to load google maps asynchronously I took a look at google's async page
Essentially I am looking for an alternative to document.write in the API and according to some users on this google group post Using the async version will handle this scenario.
My question is why would this script:
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&sensor=SET_TO_TRUE_OR_FALSE"
type="text/javascript"></script>
Be any different than:
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&sensor=TRUE_OR_FALSE&callback=initialize";
document.body.appendChild(script);
when the first and second both call the same js file which obviously has the document.write within it? Also why would an updated API want to consider using document.write over append if write generally goes against content security policy?
As a little background info I'm experimenting with Google's packaged apps and their csp doesn't allow for document.write.
One of the main advantage of loading scripts (or other resources) asynchronously/dynamically is that it can dramatically speed up your page load times.
From Google's Developer best practices:
https://developers.google.com/speed/docs/best-practices/rtt#PreferAsyncResources
When a browser parses a traditional script tag, it must wait for the
script to download, parse, and execute before rendering any HTML that
comes after it. With an asynchronous script, however, the browser can
continue parsing and rendering HTML that comes after the async script,
without waiting for that script to complete. When a script is loaded
asynchronously, it is fetched as soon as possible, but its execution
is deferred until the browser's UI thread is not busy doing something
else, such as rendering the web page.
Another trick I use to decide on whether or not to load a script (such as the Google Maps API) asynchronously is, I ask myself, "Is there a chance that the user will not see, benefit or interact with the results of the loaded script?". If the answer is yes, then I'll usually tie the loading of the script to some DOM event (such as button click etc).
In other words, if a user has to click a button on my web page to view my Google Map; why bother loading all that extra script if there's a chance the user will never even see it? Instead, load the script asynchronously when a button is clicked, and then load my map.
Actually the javascript file at maps.googleapis.com/maps/api/js is a dynamic one. The server responds with a different js file for different parameters. To know the difference, just load the following files from a browser address bar.
https://maps.googleapis.com/maps/api/js?v=3.exp
and
https://maps.googleapis.com/maps/api/js?v=3.exp&callback=initialize
You will notice that there is a "document.write" in the first js file as quoted below
function getScript(src) {
document.write('<' + 'script src="' + src + '"' +
' type="text/javascript"><' + '/script>');
}
whereas there is a document.createElement in the second case as follows
function getScript(src) {
var s = document.createElement('script');
s.src = src;
document.body.appendChild(s);
}
The difference is that, when a script is loaded synchronously, the browser waits for it to load completely and when the script calls document.write, the text is appended to the document being loaded. But asynchronous call is made once a document is fully loaded. As a result document.write would replace the existing document, and hence the browser ignores such a call from an asynchronously loaded script. When you load the js with "callback=initialize", the self executing function already contains the call back to initialize, and a modified function which can load further scripts asynchronously.
All you have to do is set a callback to be executed after the map script loads:
Then in your app's main .js file, define the callback:
window.myCallbackFuction = function() {
return console.log("Loaded Google Maps!");
// the rest of the maps initialization goes here, per the docs
};
The tricky part is refactoring your code so that any map-related code isn't executed until you're certain that the myCallbacFuction() was executed.
There is an example on how to load Google maps asynchronously. Basic idea is to create a script tag like you did and let this function be executed onload.

HTML5 Offline Functionality Doesn't Work When Browser Restarted

I am using the offline HTML5 functionality to cache my web application.
It works fine some of the time, but there are certain circumstances where it has weird behaviour. I am trying to figure out why, and how I can fix it.
I am using Sammy, and I think that might be related.
Here is when it goes wrong,
Browse to my page http://domain/App note: I haven't included a slash after the /App
I am then redirected to http://domain/App/#/ by sammy
Everything is cached (including images)
I go offline, I am using a virtual machine for this, so I unplug the virtual network adapter
I close the browser
I reopen the browser and browse to my page http://domain/App/#/
The content is showing except for the images
Everything works fine if in step #1 I browse to http://domain/App/ including the slash.
There are some other weird states it gets into where the sammy routes are not called, so the page remains blank, but I haven't been able to reliably replicate that.
??
UPDATE: The problem is that the above steps caused problems before. It is now working when I follow the above steps, so it is hard to say what is going on exactly. I am starting from a consistent state every time because I am starting from a snapshot in a VM.
My cache manifest looks like this,
CACHE MANIFEST
javascripts/jquery-1.4.2.js
javascripts/sammy/sammy.js
javascripts/json_store.js
javascripts/sammy/plugins/sammy.template.js
stylesheets/jsonstore.css
templates/item.template
templates/item_detail.template
images/1Large.jpg
images/1Small.jpg
images/2Large.jpg
images/2Small.jpg
images/3Large.jpg
images/3Small.jpg
images/4Large.jpg
images/4Small.jpg
index.html
I'm running into a similar issue as well.
I think part of the problem is that jquery ajax is misinterpreting the response. I believe sammy is using the jquery to make the ajax calls, which is leading to the errors.
Here's a code snippet i used to test for this (though not a solution)
this.get('#/', function (context) {
var uri = 'index.html';
// what i'm trying to call
context.partial(uri, {});// fails on some browsers after initial caching
// show's that jquery's ajax is misinterpreting
// the response
$.ajax({
url:uri,
success: function(data, textStatus, jqXHR){
alert('success')
alert(data);
},
error: function(jqXHR, textStatus, errorThrown){
alert('error')
if(jqXHR.status == 0){ // this is actually a success
alert(jqXHR.responseText);
}else{
alert('error code: ' + jqXHR.status) // probably a real error
}
}
});

Chrome extension post-install hook/API function: does it exist?

Is there a Chrome extension post install hook/API function that will let me perform an action after the plugin is installed or updated?
I would like to perform an action after my extension is installed, and only right after it is installed. This action should only be performed once (post-install or post-update) of the extension.
Update
Some people have proposed setting the version of the extension in localStorage, the problem that I have is that the content script that has access to localStorage is not loaded into the page when the plugin is first installed.
AFAIK after a plugin is installed, and it makes use of a content script injected into the tab/page, the page has to be reloaded.
I don't know how to access localStorage from the background page; localStorage can only be accessed from a content script.
To get the version number from the background page to the content script requires the use of chrome API function to execute scripts:
chrome.tabs.executeScript(null, {code:function_to_execute}, function() { // callback });
However, when you install a plugin, and the page that this plugin needs to inject a content script into is already loaded, it does not inject the content script, you have to reload the page.
update 2
Looking at some of the tips provided in more detail, for the purpose of saving the version number, it is possible to access the localStorage of the background page. However, for what I need to do, which is reload a specific tab at a specific URL (in order to make sure the content script is the newest version) after installing or updating a plugin, it ended up being unnecessary to bother with localStorage.
For the sake of staying on topic, the advice given about writing the version number to localStorage (in the background page) and then checking against the version number in the manifest file is good enough to allow someone to run a script the first time it is installed/or updated.
HowTo
Make manifest file available to the background page (note: this is taken from somewhere else, I don't take credit for it, but I can't remember the source, if you know, let me know and I will add it).
// MAKE MANIFEST FILE AVAILABLE
chrome.manifest = (function() {
var manifestObject = false;
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
manifestObject = JSON.parse(xhr.responseText);
}
};
xhr.open("GET", chrome.extension.getURL('/manifest.json'), false);
try {
xhr.send();
} catch(e) {
console.log('Couldn\'t load manifest.json');
}
return manifestObject;
})();
Now you can access your version number like this: chrome.manifest.version
To write to localStorage just pass it in like so: localStorage['my_plugin_version'] = chrome.manifest.version
You can do this using a background page. When the extension is installed, the background page is opened in the background, and thus executed. To make sure it's not executed every time, simply store a value in localStorage.