chrome.app.getDetails() returns null - google-chrome

I was trying to get the version of my extension at run-time using chrome.app.getDetails().version and noticed that chrome.app.getDetails() returns null. Surprisingly, there is no talk about this in the online community and the function isn't even documented by the Google folks. Is there a permission I am missing? I do have tabs enabled.

Very old... I know
But in case if someone is looking for this, you can have your extension version reading the manifest file with chrome.runtime API and the getManifest Method.
Ex. in your background script:
var manifest = chrome.runtime.getManifest();
var current_version = manifest.version;
console.info('Current Version: ', current_version);
The object returned is a serialization of the full manifest file, so you can get all the info in the manifest file
So.. if you want for example all and only the matches of your content_scripts... for say something...
for(var i in manifest.content_scripts) {
console.log(manifest.content_scripts[i]['matches']));
}
Note: Stable since Chrome 22

It's undocumented because they might be moving getDetails to a different part of the API -- see this bug. It's currently working on my copy of Chrome (beta channel), but I wouldn't be surprised if they've disabled it in a newer release. In the meantime you can just do an AJAX query to get the manifest.json of your extension -- you can get its URI using chrome.extension.getURL("manifest.json").

Here is what I'm using to pull the current version.
var manifest = new XMLHttpRequest();
manifest.open("get", "/manifest.json", true);
manifest.onreadystatechange = function (e) { if (manifest.readyState == 4) {console.log(JSON.parse(manifest.responseText).version)} };
manifest.send({});

Related

Firebase Query.Once failed on Firefox and Google Chrome console, but not it JS Bin website

I have a problem with firebase, it read data using 'once', but it shows me Uncaught Error: Query.once failed: Was called with 1 argument. Expects at least 2 on Firefox console and Google Chrome, but the source code on JS Bin there's no error.
I got the code from here : https://stackoverflow.com/a/35526844/6780268
I copy the code in bracket, i try 2 different firebase.js, but still show me nothing.
var ref = new Firebase('https://stackoverflow.firebaseio.com/35526205');
ref.child('Highscore').once('value').then(function(snapshot) {
console.log(snapshot.val());
});
Is this normal? And how to fix this problem?
Sorry for my bad english.
You're likely using a different/newer version of Firebase than the one in the jsbin in the link.
In newer versions of Firebase, you get a reference to the same database location with:
firebase.initializeApp({ databaseURL: "https://stackoverflow.firebaseio.com" });
var ref = firebase.database().ref("35526205");
I recommend reading the Firebase documentation, the migration guide, and the web codelab.

Open a file from Google Sheets script

I've had a Google Sheets script running for some time (a year) that needs to read an HTML file from it's Google Drive directory. The code to open the file looks like this:
var myHtmlFile = UrlFetchApp.fetch("https://googledrive.com/host/0B1m........JtZzQ/myfile.htm");
... and I could use the HTM file for further parsing.
Suddenly, the code above is throwing an error 404.
Has anything changed recently, stopping me from opening the file?
After a discussion with 'azawaza' (thanks for all the tips), I have finally solved this, so I'm posting the resolution in case others fall into this.
It looks like the construct
https://googledrive.com/host/{public_folder_id}/myfile.htm
in UrlFetchApp.fetch(url, true) can no longer be used. It gives error 404.
I was getting it from the following construct (for simplicity, assuming there is only one parent folder of my spreadsheet):
...
var myId = DocsList.getFileById(SpreadsheetApp.getActive().getId());
var folderId = myId.getParents()[0].getId();
var url = "https://googledrive.com/host/" + folderId + "/myfile.htm";
// url looks like: https://googledrive.com/host/0B1m....JtZzQ/myfile.htm"
var httpResp = UrlFetchApp.fetch(url,true); //throws 404 !!!
// now, parse 'httpResp'
The solution that worked for me, is to find the file directly using this construct (again, assuming there is only one file of given name) :
var htmlCont = DocsList.find("myfile.htm")[0].getContentAsString();
// now, parse htmlCont
I don't know why the 'old' solution no longer works. As I mentioned it worked for a year.
UPDATE (May 2015)
The 'DocsList' has been deprecated, a new construct:
var files = DriveApp.getFilesByName(myURL);
if (files.hasNext()) {
var htmlCont = files.next().getBlob().getDataAsString()
}
has to be used instead
I find it strange that it ever worked before! If it did, it was probably a bug - pretty sure it was never intended to work like that with "local" files... I have never seen it mentioned anywhere that UrlFetchApp.fetch() can fetch "local" files like that.
A simple fix would be to just use proper full url of the file:
var myHtmlFile = UrlFetchApp.fetch("https://googledrive.com/host/{public_folder_id}/myfile.htm");
That will ensure your code complies with the API and does not break next time Google changes something.

How to sync conflicting changes with Google Drive API

I have a google drive app which will auto-save changes. If you have two active sessions then they will overwrite each other. The app supports merging changes but I can't see how to safely integrate this with the drive API. Some options I have considered:
Version safe commits
Use google drive to "only update if current revision in drive == X otherwise fail"
If failed then fetch latest version, merge and retry
problem: I don't think drive supports this. Previous API versions used etags but I see no mention of this in the current documenation.
Pre-commit check
check current saved version, if still current, save
otherwise download, merge and update
problem: obvious race condition between clients
Post-commit check
save new version
if new version is as expected: done
if new version higher than expected: download previous version, merge and update
problem: I don't have much faith this is safe. I can see multiple clients getting in edit loops.
Google real-time api - field binding
replace file format with a google rt datamodel
problem: It would require redesigning just for google-rt
Google real-time api - document support
use google rt api external document support
problem: I don't think this solves the problem
I would really like a way to achieve #1 but any suggestions would be helpful. I would be happy enough with a basic locking / handover scheme between clients but I don't think Drive supports that either.
According to here, "If-Match" using etags still works. Not sure if it applies to data, but at least it applies to metadata.
To follow up on user1828559's answer, the following Java code seems to work well:
private File updateDriveFile(Drive drive, File file, byte[] data) throws IOException {
try {
ByteArrayContent mediaContent = new ByteArrayContent(MIME_TYPE, data);
Drive.Files.Update update = drive.files().update(file.getId(), file, mediaContent);
update.getRequestHeaders().setIfMatch(file.getEtag());
return update.execute();
}
catch (GoogleJsonResponseException e) {
if (isConflictError(e.getDetails())) {
logger.warn("ETag precondition failed, concurrent modification detected!");
return null;
}
throw e;
}
}
private boolean isConflictError(GoogleJsonError error) {
if (error.getCode() == 412) {
final List<GoogleJsonError.ErrorInfo> errors = error.getErrors();
if (errors != null && errors.size() == 1) {
final GoogleJsonError.ErrorInfo errorInfo = errors.get(0);
if ("header".equals(errorInfo.getLocationType()) &&
"If-Match".equals(errorInfo.getLocation()) &&
"conditionNotMet".equals(errorInfo.getReason()))
return true;
}
}
return false;
}

welcome page loads when Allow in incognito is checked/unchecked in Chrome

I am new to chrome extensions.I used chrome.runtime.onInstalled to load a html page whenever the extension is installed or updated.But when i am testing it in chrome, whenever i check/uncheck Allow in incognito the same html page loads each time.How to avoid this behaviour? I used "incognito":"split" in manifest.
I wish you'd posted the code so I could try to replicate the problem and give a specific solution but the easy solution is to use chrome storage API to save the extension's version when welcome.html is opened and compare it to the current version next time onInstalled is fired.
If the stored version is the same don't open it. If it's undefined or older, open it.
Get your extension's version by extracting it from chrome.extension.getURL("manifest.json")
Edit:
After a bit of googling it seems you can access the manifest more directly. Get the version number using the code below.
var version = chrome.runtime.getManifest().version;
Edit:
It seems the previous version is supplied in the callback when you update so you don't need to store anything. The object provided can be compared to the current version using chrome.runtime.getManifest().version
Something like this:
chrome.runtime.onInstalled.addListener(function (details) {
if(details.reason === "install"){
chrome.tabs.create({url: "welcome.html"});
}
else if(details.reason === "update"){
var currentVersion = chrome.runtime.getManifest().version;
var previousVersion = details.previousVersion;
if(previousVersion !== currentVersion){
chrome.tabs.create({url: "welcome.html"});
}
}
});
I don't think you can. I assume that when you uncheck "Allow in incognito", Chrome nukes the local state of the (split) incognito instance.

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.