The My Opera forums have been replaced with forums.opera.com. Please head over there to discuss Opera's products and features

See the new Forums

scriptStorage is always undefined

Forums » General Opera topics » User JavaScript

You need to be logged in to post in the forums. If you do not have an account, please sign up first.

Go to last post

6. November 2011, 13:33:16

dediggefedde

Posts: 8

scriptStorage is always undefined

Hi!

I tried to use opera.scriptStorage to let a user.js-script store informations between different pages.

However, The script tells me, that scriptStorage is undefined even though I set the script-storage-size in my config-page to 500...

I try following code:

if(window.opera){
	var storage=window.opera.scriptStorage;
	
	if(!storage){console.log("noscript");}else{
	console.log("script");
	GM_setValue=function(Name, Wert) {
		storage.setItem(Name,Wert);
		};
	GM_getValue=function(Name) {
		return storage.getItem(Name);
		};
	}	
}


Everytime I try to run it, console.log("noscript"); is triggered!

Do you know if I'm using it wrong or didn't set the right settings for using it?
I need much storage in this script, so cookies would be too small!

6. November 2011, 13:39:08

Frenzie

Posts: 15571

I presume you're trying to use it in GM compatibility mode (.user.js), but many of Opera's UserJS features only work with .js.
The DnD Sanctuary — a safety net for My Opera's demise.

6. November 2011, 17:06:08

dediggefedde

Posts: 8

Thanks for the hint!

I tried to rewrite the script, so I don't need to GM-compatibility...
As the script should be working on a site that build itself dynamically, I first check-looped until there is something like a body.

if(window.opera){
	var stopp=0;
	var stopp2=0;	
	function bereit(){
			var doomda= document.evaluate("//body",document.documentElement,null,XPathResult.ANY_TYPE,null).iterateNext();
			if(doomda){stopp=5;stor();}
			if(stopp<5){setTimeout(bereit,500);stopp++;}
	}	
	function stor(){
		if(window.opera.scriptStorage){stopp2=5;storage=window.opera.scriptStorage;start();}
		if(stopp2<5){setTimeout(stor,1000);stopp2++;}
	}
	bereit();
}else{
	start();
}


and "window.opera.scriptStorage" is always undefined!

One funny thing: the page itself has some iframes for its advertisement... If I don't filter the url, the script will be running in those iframes, too, and there is a scriptStorage! But not the Elements I need for my script^^

Are there some pages that doesn't accept scriptStorage?

6. November 2011, 20:42:25

Frenzie

Posts: 15571

Couldn't you just use addEventListener with DOMContentLoaded?

Anyway, got any URL or something for testing? Are you keeping a reference? See http://www.opera.com/docs/userjs/specs/#scriptstorage
The DnD Sanctuary — a safety net for My Opera's demise.

6. November 2011, 21:48:16

dediggefedde

Posts: 8

Thank you for the link (read it some times already^^) and the idea!
If I use DOMContentLoaded document.body is empty!


I go there: http://whitewinged.deviantart.com/
with this:
if(window.opera&&location.href.match(/http:\/\/.+\.deviantart\.com\/?$/i)){
	opera.addEventListener('DOMContentLoaded', new function(){alert(document.body.innerHTML);}, false);
}


to Topic: suddenly scriptStorage is available on the page!

Now, how to wait right for the body?^^

6. November 2011, 22:54:28

BtEO

Posts: 1022

DOMContentLoaded is a standard event type. Try using document.addEventListener()

And it's just function(){…} (i.e. no 'new') when defining a function within a event listener declaration. Though I couldn't tell you why.
MyOpera Community Enhancements — by xErath & BtEO
MyOpera Community Optimizations — by fearphage
Scribit improved posting tools for the MyOpera Community — by xErath
Improve Weeklies Blog — by MisterE & fearphage

7. November 2011, 16:10:33

dediggefedde

Posts: 8

Thanks for the advice!
The event is triggered now, but the old problem pops up again!

var storage;

function start(){
	if(!storage){console.log("noscript");}else{console.log("script");}
}

if(window.opera&&location.href.match(/http:\/\/.+\.deviantart\.com\/?$/i)){
	document.addEventListener('DOMContentLoaded', function(){storage=window.opera.scriptStorage;start();}, false);
}


It continues to give me "noscript"!

7. November 2011, 17:26:35

BtEO

Posts: 1022

Ahh, while the DOMContentLoaded only properly fires from document.addEventListener this has the side effect that it's no longer running in the User JavaScript thread, and doesn't have access to the special window.opera features. Try the following:
(function(){
	if (window.opera) {
		var storage = window.opera.scriptStorage;

		function start(){
			if(!storage){console.log("noscript");}else{console.log(storage);}
		}

		if(location.href.match(/http:\/\/.+\.deviantart\.com\/?$/i)){
			document.addEventListener('DOMContentLoaded', start, false);
		}
	}
})();
This wraps the whole script in an anonymous function (which you should be doing pretty much all the time unless you have a reason not to, it provides a degree of separation between your script and any scripts running on the page), and caches a reference to scriptStorage in the main UserJS thread local to the anonymous function. Because start() is also within that anonymous function it can access storage, but no script outside the anonymous function can see it, preventing any accidental leakage of scriptStorage's content.
MyOpera Community Enhancements — by xErath & BtEO
MyOpera Community Optimizations — by fearphage
Scribit improved posting tools for the MyOpera Community — by xErath
Improve Weeklies Blog — by MisterE & fearphage

7. November 2011, 21:55:18

spadija

Posts: 1643

You can do that a little more succinctly like this:
(function(opera, storage){
//code goes here
})(window.opera, window.opera.scriptStorage);


Also, you can get Opera to restrict your script to a specific site with a special comment block at the top:
// ==UserScript==
// @include       http://*.deviantart.com/*
// ==/UserScript==

Add as many @include rules as you want and Opera will add the script whenever one of them matches the page URL.

11. November 2011, 13:48:08

dediggefedde

Posts: 8

Thank you for your helpful scripts!
Everything works now, except for a event-problem:

If I define something like

obj.onclick=function(){alert(storage.getItem('test'));}

I get an "Referenceerror: security Violation" Exception.

Is there a way to use storage + events?

12. November 2011, 00:55:39

spadija

Posts: 1643

What happens if you try this?
obj.addEventListener('click', function() {
    alert(storage.getItem('test'));
}, false);

12. November 2011, 02:43:26

dediggefedde

Posts: 8

Well, I found a way to use userscripts (.user.js) and scriptStorage, so I tried again to avoid addeventlistener, as this is not permitted...

But I found my error:

I wrote a function to add Events like this:
function eventmod(obj, event, callback){
	var bef="obj.";
	if(window.opera){
		bef+="on"+event+"=callback";
	}else{
		if(document.addEventListener){
			bef+="addEventListener('"+event+"',callback)";
		}else{	
			bef+="attachEvent('on"+event+"',callback)";
		}
	}
	eval(bef);
}


the security-error occured as I didn't wrote ...+"=callback" but ...+"="+callback; ^^

Now, everything is working fine^^
Thanks for your help!

12. November 2011, 07:52:23

spadija

Posts: 1643

Originally posted by dediggefedde:

Well, I found a way to use userscripts (.user.js) and scriptStorage, so I tried again to avoid addeventlistener, as this is not permitted...


I'm not sure what you mean by this. addEventListener is always permitted. window.opera.addEventListener is not permitted in GM mode (*.user.js) but <object>.addEventListener is never forbidden.

12. November 2011, 11:56:24

Frenzie

Posts: 15571

Originally posted by spadija:

window.opera.addEventListener is not permitted in GM mode


I'd phrase that as "not available." Not permitted is rendering certain things to canvas. </nitpick>
The DnD Sanctuary — a safety net for My Opera's demise.

12. November 2011, 19:29:25

spadija

Posts: 1643

Same difference.

Actually, scriptStorage shouldn't be available in GM mode either, so I'm surprised you were able to get it to work.

I don't recommend using eval() though. If there is any possibility that an untrusted page can call your eventmod function, you're letting the page call anything it wants with access to anything in eventmod's scope. For example, if I call this:
eventmod(window, "click=function(){}; window.opera.scriptStorage.clear(); a", null)

and eventmod has access to window.opera.scriptStorage, I just tricked your function into clearing all the data stored by your script.

12. November 2011, 21:45:14

dediggefedde

Posts: 8

Ah, thanks for your advices!
Sorry I said it wrong: I'm using a user.js now, but with a Gm-emulator (as a .js-file) from here: http://userscripts.org/scripts/show/105153

with that, scriptstorage is available within user.js...
I tried to use addEventListener, but somehow it didin't work in Opera, so I worked with onevent=function...
As I wanted to write a cross-browser-script, and firefox seem to not like onevents, (and IE only attachevent) I wrote this function...

@spadija:
Thanks for your advice!
I don't know, which functions are available to other scripts... I thought, eventmod would be only available to my script as I'm using it in a anonymous function... I think I will not pass a function but an index to a defined function into this to prevent this abuse... Is that enough or can someone then manipulate those defined functions?

13. November 2011, 01:07:22

spadija

Posts: 1643

There is no reason addEventListener should be failing. How are you trying to use it?

As for the security concern, generally if you wrap everything in an anonymous function, you should be safe, but you have to be careful about scope leakage. Here's an example of what I mean. If you give something in global scope a reference to a scope inside your anonymous function, the anonymous function no longer hides the object being referenced. Also, note that, unlike with the on____ events, if you use addEventListener to attach event listeners, you cannot get a reference to the listener function once it is attached. The example I created uses document.onclick to get a reference to the foo function inside the anonymous function, but had I used document.addEventListener('click', foo, false) instead, the code in global scope would have no way of getting to the foo function.

Also, eval() is slower than running regular code. The best practice would be to avoid using eval() unless you really have a good reason to use it.

13. November 2011, 02:10:40

dediggefedde

Posts: 8

Ah... Thank you very much for the example about the global scope!

I think I'm quite safe from this problems, as I don't set a eval-function to the on-events, but only functions that searches the pages for elements and alter its content... I use eval only script-intern...
I think, eval is the only way to change the way of events depending of the browser, isn't it?

I was using document.getElementById('uid').addEventListener('click',function(){alert('test');});
however, there is simply no reaction to this code^^
if I write document.getElementById('uid').onclick=function(){alert('test');};
everything seems to be fine^^

13. November 2011, 11:48:22

Frenzie

Posts: 15571

Originally posted by spadija:

Same difference.


Nuhuh. The error message is completely different. bigsmile (But yes, in practice it doesn't work so there's no difference in that regard.)

Originally posted by dediggefedde:

I was using document.getElementById('uid').addEventListener('click',function(){alert('test');});


Wfm in Opera 11.60, however in 11.50 and earlier you have to add the ,false since it doesn't default to that.
The DnD Sanctuary — a safety net for My Opera's demise.

13. November 2011, 19:06:45

spadija

Posts: 1643

Yep. addEventListener takes three arguments. the last one is only useful when you need to change the order events happen in, so you usually leave it as false. In 11.50 and earlier, the function silently fails if you omit the last argument, though will be fixed in 11.60+.

Forums » General Opera topics » User JavaScript