On Sat, Jan 29, 2011 at 6:35 PM, Asen Bozhilov <[email protected]>wrote:

> The last week I explored some things about `localStorage'. The most
> interesting and meanwhile confusing things are described in two
> articles about `localStorage':
>
> <URL:
> http://www.bennadel.com/blog/2105-Exploring-HTML5-s-localStorage-Persistent-Client-Side-Key-Value-Pairs.htm
> >
> <URL: http://hacks.mozilla.org/2009/06/localstorage/>
> "The easiest way to use localStorage is to treat it like a regular object:"
>
> After reading these articles I start thinking about the setter and
> getter of storage instance referred by `window.localStorage'. The
> others questions are:
>
> Should I treat `localStorage' as a regular object?
> Does it safe if I do?
>
> So I did some tests, to answer on my questions.
> The test cases are in Firefox 3.6 under Debian.
>
> What happens with `localStorage' object when I put new item in
> associated list trough `setItem' method?
>
> var hasOwnP = Object.prototype.hasOwnProperty,
>    storage = window.localStorage;
>
> storage.clear(); //Clear associated list
>
> storage.setItem('foo', 'bar');
> console.log(hasOwnP.call(storage, 'foo')); //true
> console.log(storage.getItem('foo')); //bar
>
> storage.setItem('setItem', 'bar');
> console.log(hasOwnP.call(storage, 'setItem')); //false
> console.log(storage.getItem('setItem')); //bar
> console.log(typeof storage.setItem); //function
>
> storage.setItem('hasOwnProperty', 'bar');
> console.log(hasOwnP.call(storage, 'hasOwnProperty')); //false
> console.log(storage.getItem('hasOwnProperty')); //bar
> console.log(typeof storage.hasOwnProperty); //function
>
> Obviously `setItem' checks the prototype chain of `localStorage' for
> passed key. If key exist it does not add new own property to
> `localStorage' object. Just put this key/value in associated list. The
> interesting part is if passed key does not exist in the prototype
> chain of `localStorage', then it creates new own property of
> `localStorage' and put key/value in associated list.
>
> Everything seems safe and clear, but what happens if I treat this
> object as "regular" object?
> var hasOwnP = Object.prototype.hasOwnProperty,
>    storage = window.localStorage;
>
> storage.clear(); //Clear associated list
>
> storage.setItem = 'bar';
> console.log(hasOwnP.call(storage, 'setItem')); //true
> console.log(storage.getItem('setItem')); //bar
> console.log(typeof storage.setItem); //string
>
> storage.hasOwnProperty = 'bar';
> console.log(hasOwnP.call(storage, 'hasOwnProperty')); //true
> console.log(storage.getItem('hasOwnProperty')); //bar
> console.log(typeof storage.hasOwnProperty); //string
>
> Now the setter does not check for existence of these properties names
> in the prototype chain of `localStorage'. It directly add them as own
> properties and also add them as key in associated list with this
> storage instance.
>
> This answer on my two questions and the answer is, it's not really
> safe to treat `localStorage' as regular object. When I have to add
> key/value in the list I should use `setItem' method.
>
> Lets see the getter:
>
> var hasOwnP = Object.prototype.hasOwnProperty,
>    storage = window.localStorage;
>
> storage.clear(); //Clear associated list
>
> storage.setItem('foo', 'bar');
> console.log(storage.getItem('foo')); //bar as expected
> console.log(typeof storage.foo); //string as expected
>
> storage.setItem('setItem', 'bar');
> console.log(storage.getItem('setItem')); //bar as expected
> console.log(typeof storage.setItem); //function "unexpected"
>
> As I explained when `setItem' has called with `key` which exist as
> property name in the prototype chain of `localStorage' it will not
> create new own property name of `localStorage' with this name. From
> this point comes the "unexpected" result.
>
> While in my example it's not really unexpected result, it can be if
> there are third party extensions of `Object.prototype', because
> `localStorage' inherit from `Object.prototype'.
>
> var hasOwnP = Object.prototype.hasOwnProperty,
>    storage = window.localStorage;
>
> storage.clear();
>
> Object.prototype.foo = function () {};
>
> storage.setItem('foo', 'bar');
> console.log(storage.getItem('foo')); //bar as expected
> console.log(typeof storage.foo); //function
>
>
> Hope this thread makes some clarifications the members of JSMentors!
>

[...]

IIRC, the meta-characteristics of `localStorage` methods vary among
implementations. In WebKit, for example, `localStorage` methods are writable
(similar to FF3.6, as your tests show), so accidental (or malicious)
assignment to `getItem` will overwrite it. I filed a bug with WebKit on this
more than a year ago — https://bugs.webkit.org/show_bug.cgi?id=30996 but
there's still no resolution there, so I think the behavior is unchanged. I'm
guessing this problem doesn't bite people as often as I thought it would,
which would explain why the "bug" is still open. Similar issue exists in
Mozilla bug tracker at https://bugzilla.mozilla.org/show_bug.cgi?id=605054

But is it a bug really? As far as I know, web storage spec (
http://dev.w3.org/html5/webstorage/) makes no clarification on whether
interface members are writable; or enumerable/configurable for that matter.
WebIDL spec, however, does mention something about interface members having
attributes { [[Writable]]: true, [[Configurable]]: false, [[Enumerable]]:
false } (http://dev.w3.org/2006/webapi/WebIDL/#es-interfaces).

So it seems like writability of localStorage's getItem, setItem, clear, etc.
is actually in accordance to spec... which is probably not that great, since
it only makes for a more fragile implementation. The opposite argument could
be that [[writable]]: false would disallow "useful" cases of overwriting
localStorage methods, whatever those cases might be.

The issue with `setItem('...')` overwriting members of interface is another
one, and would need to be addressed separately by a web storage spec, it
seems. There's definitely nothing good about storage that's supposed to
handle arbitrary key/values, breaking with a certain set of keys (i.e. those
corresponding to interface members) :)

-- 
kangax

-- 
To view archived discussions from the original JSMentors Mailman list: 
http://www.mail-archive.com/[email protected]/

To search via a non-Google archive, visit here: 
http://www.mail-archive.com/[email protected]/

To unsubscribe from this group, send email to
[email protected]

Reply via email to