Getting your scripts to run in Google Chrome

in Script development
Subscribe to Getting your scripts to run in Google Chrome 35 posts, 10 voices



Yansky Scriptwright

I'm currently working on getting one of my scripts to work in Google Chrome. I thought I'd post some of my code for others that may want to do the same.
Two things to note:
1.The @include's will need to be manually added with flow statements (e.g. if(document.URL=='http://foo.com/meh'||document.URL=='http://foo.com/sneh'){...)
2.There's no way to replicate the GM_xmlhttprequest's cross-domain ability (although the google gears dev pages say this may be implemented in the future)

(function(){
	if(document.URL.match('foo.com')){
	
		console.log('script is running');
	
		/******
			Google Gears stuff
		******/	
		// Copyright 2007, Google Inc.
		//
		// Redistribution and use in source and binary forms, with or without
		// modification, are permitted provided that the following conditions are met:
		//
		//  1. Redistributions of source code must retain the above copyright notice,
		//     this list of conditions and the following disclaimer.
		//  2. Redistributions in binary form must reproduce the above copyright notice,
		//     this list of conditions and the following disclaimer in the documentation
		//     and/or other materials provided with the distribution.
		//  3. Neither the name of Google Inc. nor the names of its contributors may be
		//     used to endorse or promote products derived from this software without
		//     specific prior written permission.
		//
		// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
		// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
		// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
		// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
		// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
		// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
		// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
		// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
		// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
		// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
		//
		// Sets up google.gears.*, which is *the only* supported way to access Gears.
		//
		// Circumvent this file at your own risk!
		//
		// In the future, Gears may automatically define google.gears.* without this
		// file. Gears may use these objects to transparently fix bugs and compatibility
		// issues. Applications that use the code below will continue to work seamlessly
		// when that happens.


		// We are already defined. Hooray!
		if (window.google && google.gears) {
			return;
		}

		var factory = null;

		// Firefox
		if (typeof GearsFactory != 'undefined') {
			factory = new GearsFactory();
		} 
		else {
			// IE
			try {
				factory = new ActiveXObject('Gears.Factory');
				// privateSetGlobalObject is only required and supported on IE Mobile on
				// WinCE.
				if (factory.getBuildInfo().indexOf('ie_mobile') != -1) {
					factory.privateSetGlobalObject(this);
				}
			} 
			catch (e) {
				// Safari
				if ((typeof navigator.mimeTypes != 'undefined')	&& navigator.mimeTypes["application/x-googlegears"]) {
					factory = document.createElement("object");
					factory.style.display = "none";
					factory.width = 0;
					factory.height = 0;
					factory.type = "application/x-googlegears";
					document.documentElement.appendChild(factory);
				}
			}
		}

		// *Do not* define any objects if Gears is not installed. This mimics the
		// behavior of Gears defining the objects in the future.
		if (!factory) {
			return;
		}

		// Now set up the objects, being careful not to overwrite anything.
		//
		// Note: In Internet Explorer for Windows Mobile, you can't add properties to
		// the window object. However, global objects are automatically added as
		// properties of the window object in all browsers.
		if (!window.google) {
			google = {};
		}

		if (!google.gears) {
			google.gears = {factory: factory};
		}

		var db = google.gears.factory.create('beta.database');
		db.open('database-wlr');
		db.execute('create table if not exists WLR (GMSetKey text, GMSetVal text)');
		db.close();

		/******
			Greasemonkey API replacements
		******/	
		
		function GM_setValue(sN, sV){
			
			db.open('database-wlr');
			//I cant get REPLACE or ON DUPLICATE KEY UPDATE to work properly so this will have to do for the moment.
			var rs = db.execute('select GMSetKey from WLR');
			var dbExec = "insert";
			while (rs.isValidRow()) {
				if(rs.field(0) == 'gkey'){
					dbExec = "UPDATE";
					break;
				}
				rs.next();
			}
			if(dbExec == "insert"){
				db.execute("insert into WLR values (?, ?)", [sN, sV]);
			}
			else{
				db.execute("UPDATE WLR SET GMSetKey='"+sN+"', GMSetVal='"+sV+"'");
			}		
			db.close();

		}
		function GM_getValue(gV){
			
			db.open('database-wlr');
			var getGMV = db.execute("select GMSetVal from WLR WHERE GMSetKey = '"+gV+"' LIMIT 1").field(0);
			db.close();
			return getGMV; //should return undefined if it's not in the database
			
		}

		var gmstDocHead = document.getElementsByTagName('head')[0];
		
		function GM_addStyle(s){
		
			var gm_style = document.createElement('style');
			gm_style.type="text/css";
			gm_style.textContent=s;
			gmstDocHead.appendChild(gm_style);
		
		};		
		
		function GM_log(t){
		
			console.log(t);
		
		};		
	}
}

More info:
http://dev.chromium.org/developers/design-docum...
http://code.google.com/apis/gears/

 
sizzlemctwizzle Scriptwright

Awesome. Lets create a group.

1.The @include's will need to be manually added with flow statements (e.g. if(document.URL=='http://foo.com/meh'||document.URL=='http://foo.com/sneh'){...)

I hate doing things manually. Here's a function to automate things a bit.

function GM_testUrl(include) {
    return (new RegExp(include.replace(/(\/|\?|\.|\^|\,|\+)/g, "\\\$1").replace(/\*/g, '.*')).test(document.URL)) ? true : false;
}

if (GM_testUrl("http://*.google.com/*")) {
// Code here
}

If Chrome supports E4X I could write some code to make this even easier for us.

 
Avindra V.G. Scriptwright

function GM_testUrl(include) { return (new RegExp(include.replace(/(\/|\?|\.|\^|\,|\+)/g, "\\\$1").replace(/\*/g, '.*')).test(document.URL)) ? true : false; }

is the same as

function GM_testUrl(include) {
    return new RegExp(include.replace(/(\/|\?|\.|\^|\,|\+)/g, "\\\$1").replace(/\*/g, ".*")).test(document.URL);
}

 
sizzlemctwizzle Scriptwright

lol true haha. nice catch. I'm always forgetting types when they're not explicitly declared. Anyway, I was gonna make it handle more than one include. This function was inspired by the convertToRegExp PHP function.

function GM_testUrl(includes, excludes) {
  regTest = function(url) { return new RegExp(url.replace(/(\/|\?|\.|\^|\,|\+)/g, "\\\$1").replace(/\*/g, ".*")).test(document.URL); }
  if (typeof excludes != "undefined") {
    if (typeof excludes == "string") excludes = [excludes];
    for (exclude in excludes) if (regTest(excludes[exclude])) return false;
  }
  if (typeof includes == "string") includes = [includes];
  for (include in includes) if (regTest(includes[include])) return true;
  return false;
}

if (GM_testUrl(["http://*.google.com/*", "http://*.facebook.com/*"], "http://*.facebook.com/home.php?*")) {
  // This is equal to:
  // @include http://*.google.com/*
  // @include http://*.facebook.com/*
  // @exclude http://*.facebook.com/home.php?*
  // Please note that you can either pass a string or an array for either includes or excludes
  // Also note that includes is required, whereas excludes is optional
  // Finally, an exclude can override and include
}

 
sizzlemctwizzle Scriptwright

Sorry if this is a stupid question, but if this is for running Greasemonkey scripts in Chrome, then why do we need the bit for IE, Safari, and Firefox?

 
Joel H Scriptwright

Sizzle, welcome to the wonderful world of user agent strings:
http://webaim.org/blog/user-agent-string-history/

Basically, because Chrome is based on Safari, that's necessary.

On an unrelated note, if there's code to handle IE in some snippet I steal (possibly from myself) I tend to leave it in, just in case I want that same effect on a page that I host later.

-Joel

 
sizzlemctwizzle Scriptwright

But if the userscripts are running on the client-side how the hell would Safari ever risk running them?

 
Yansky Scriptwright


Sorry if this is a stupid question, but if this is for running Greasemonkey scripts in Chrome, then why do we need the bit for IE, Safari, and Firefox?

You can probably remove that bit. I just copied the whole gears_init.js file. ( http://code.google.com/apis/gears/gears_init.js )

Here's a good tutorial on using google gears with GM:
http://code.google.com/apis/gears/articles/gear...

 
sizzlemctwizzle Scriptwright

so I take it that in chrome google.gears is already defined and you don't need to check if it exists?

 
sdfghjklkjhg... Scriptwright

Google greasemetal.

 
Yansky Scriptwright

Seems they now support @include
http://dev.chromium.org/developers/design-docum...

 
sizzlemctwizzle Scriptwright

Yansky wrote:
Seems they now support @includehttp://dev.chromium.org/developers/design-docum...

Yeah but the don't support @exclude and they also want us to use @match, but I refuse to do that just for them.

 
MetaEd User

I am brand new to GM so take what I write with a grain of salt.

Chrome 2.0.180.0 supports @include. However I cannot get cross domain XHR to work.

 
aeosynth User
FirefoxX11

Bump. I just updated a couple of my userscripts to be chrome compatible with this snippet:

//chrome compatibility; opera doesn't have localStorage
if (typeof GM_log == 'undefined') {

	function GM_addStyle(css) {
		var style = document.createElement('style');
		style.textContent = css;
		document.getElementsByTagName('head')[0].appendChild(style);
	}

	function GM_deleteValue(name) {
		localStorage.removeItem(name);
	}

	function GM_getValue(name, defaultValue) {
		return localStorage.getItem(name) || defaultValue;
	}

	function GM_log(message) {
		console.log(message);
	}

	function GM_registerMenuCommand(name, funk) {
	//todo
	}

	function GM_setValue(name, value) {
		localStorage.setItem(name, value);
	}
}

 
sizzlemctwizzle Scriptwright
FirefoxMacintosh

aeosynth wrote:
Bump. I just updated a couple of my userscripts to be chrome compatible
Thanks for the code. That also makes your scripts Safari compatible as well. You forgot unsafeWindow:
if(typeof unsafeWindow==='undefined') {
        unsafeWindow = window;
        function GM_addStyle(css) {
		var style = document.createElement('style');
		style.textContent = css;
		document.getElementsByTagName('head')[0].appendChild(style);
	}

	function GM_deleteValue(name) {
		localStorage.removeItem(name);
	}

	function GM_getValue(name, defaultValue) {
		return localStorage.getItem(name) || defaultValue;
	}

	function GM_log(message) {
		console.log(message);
	}

	function GM_setValue(name, value) {
		localStorage.setItem(name, value);
	}
}

 
aeosynth User
FirefoxX11

Since localStorage only stores strings, I had to modify the GM_getValue wrapper:

if (typeof GM_log == 'undefined') {
	unsafeWindow = window;

	function GM_addStyle(css) {
		var style = document.createElement('style');
		style.textContent = css;
		document.getElementsByTagName('head')[0].appendChild(style);
	}

	function GM_deleteValue(name) {
		localStorage.removeItem(name);
	}

	function GM_getValue(name, defaultValue) {
		var value = localStorage.getItem(name);
		if (value == 'false')
			return false;
		return value || defaultValue;
	}

	function GM_log(message) {
		console.log(message);
	}

	function GM_registerMenuCommand(name, funk) {
	//todo
	}

	function GM_setValue(name, value) {
		localStorage.setItem(name, value);
	}
}

@sizzlemctwizzle: j/w why you're checking for strict equality.

 
Yansky Scriptwright
FirefoxWindows

Chrome doesn't support localStorage.

http://userscripts.org/topics/39408

 
aeosynth User
FirefoxX11

Chrome (Chromium) does support localStorage.
http://codereview.chromium.org/366032

localStorage works fine in my dev channel Chrome, but I haven't tried it in stable (which doesn't exist for Linux yet) or unstable, so you may be right for those versions. A lot can happen in a month, though.

 
Yansky Scriptwright
FirefoxWindows

aeosynth wrote:
Chrome (Chromium) does support localStorage.
http://codereview.chromium.org/366032

Awesome, that's great to see.

Now we just need Opera to get off it's arse and implement it and we'll have them all. :)

 
GIJoe Scriptwright
SeamonkeyMacintosh

I use this: http://userscripts.org/topics/41177#posts-198077

Now we just need Opera to get off it's arse and implement it...
So true...

 
sizzlemctwizzle Scriptwright
FirefoxMacintosh

aeosynth wrote:
@sizzlemctwizzle: j/w why you're checking for strict equality.
Why not? It doesn't matter either way. I'm going to use your code to implement a cross-browser version of GM_config. Your welcome to help if you want.

 
Yansky Scriptwright
FirefoxWindows

I've been playing around with the latest version of Chromium. So far scripts seem to work quite well with the new install-as-extension method. localStorage works as aeosynth mentioned.

You can download the latest builds of Chrome here: http://build.chromium.org/buildbot/snapshots/

 
Avindra V.G. Scriptwright
FirefoxWindows

So guys..... as far as I know, Chrome doesn't support accessing page variables? Pretty silly. Of course, they dub this "security".

Anyone know a quick workaround? Command line switches to un-stupid this?

 
w35l3y Scriptwright
FirefoxWindows

chrome doesnt have unsafeWindow ?!

 
Yansky Scriptwright
FirefoxWindows

This post has been marked as spam. Do you
or

Avindra V.G. wrote:
So guys..... as far as I know, Chrome doesn't support accessing page variables? Pretty silly. Of course, they dub this "security".
Anyone know a quick workaround? Command line switches to un-stupid this?

It's weird actually; unsafeWindow does seem to be passed to the script when it is installed as an extension (Screenshot: http://img.photobucket.com/albums/v215/thegoodd... ), but I don't think it's working properly.

BTW, some interesting stuff shows up when you call console.log(unsafeWindow) - Screenshot: http://img.photobucket.com/albums/v215/thegoodd...

Cross
Presentational HTML allowed.
Use <code> for inline code and <pre> for code blocks. Use &lt; and &gt; for literal < and >.
We help break paragraphs and link your links.
or cancel