Bug 156613

Summary: Cannot access the SQLTransaction.constructor.prototype
Product: WebKit Reporter: Douglas Camata <d.camata>
Component: WebCore Misc.Assignee: Chris Dumez <cdumez>
Status: RESOLVED FIXED    
Severity: Normal CC: beidson, cdumez, commit-queue, darin, esprehn+autocc, kondapallykalyan, sam
Priority: P2 Keywords: WebExposed
Version: Safari 9   
Hardware: Mac   
OS: OS X 10.11   
URL: https://dev.w3.org/html5/webdatabase/#sqltransaction
Attachments:
Description Flags
simple test case
none
Patch none

Description Douglas Camata 2016-04-14 17:08:55 PDT
In any other browser, I can access and modify SQLTransaction.constructor.prototype, like this:

> var db = openDatabase('mydb', '1.0', 'my first database', 2 * 1024 * 1024);
< undefined
> db.transaction(function (tx) { console.log(tx.constructor.prototype) });
< undefined
-> SQLTransaction {Symbol(Symbol.toStringTag): "SQLTransaction"}

The reason I do this is mostly to add a sinonjs spy to the executeSql method for some tests. 

But when I try to do this with Safari I get a "TypeError: Attempted to wrap undefined property executeSql as function". I investigated a little bit and I saw that when I run this code inside Safari I don't get anything logged to my console. These are the options I tried:

> db.transaction(function (tx) { console.log(tx.constructor.prototype) });
< undefined
> db.transaction(function (tx) { console.log(tx.constructor) });
< undefined
> db.transaction(function (tx) { console.log(tx) });
< undefined

This is very awkward. All these examples works flawlessly in other browsers. Then I tried to get the transaction outside from the function, just for curiosity and I noticed something even more weird:

> db.transaction(function (tx) { global_transaction = tx });
< undefined
> global_transaction.constructor
< function Object() {
    [native code]
} = $3
> global_transaction.constructor.prototype
< {} = $2

Now I can access the constructor, but the prototype is empty. Because of this I can't properly test my code, which uses a lot of webSQL. 

Do you think this also has some interference with IndexedDB? Exactly ALL my tests using IndexedDB fails only on Safari too, but I still have to debug them a little bit more to correctly understand what is going on. 

Note: this is my first bug report here, so if it's not good enough feel free to give suggestions on how to improve it. Thanks!
Comment 1 Brady Eidson 2016-04-14 20:33:22 PDT
(In reply to comment #0)
> In any other browser, I can access and modify
> SQLTransaction.constructor.prototype, like this:
> 
> > var db = openDatabase('mydb', '1.0', 'my first database', 2 * 1024 * 1024);
> < undefined
> > db.transaction(function (tx) { console.log(tx.constructor.prototype) });
> < undefined
> -> SQLTransaction {Symbol(Symbol.toStringTag): "SQLTransaction"}
> 
> The reason I do this is mostly to add a sinonjs spy to the executeSql method
> for some tests. 
> 
> But when I try to do this with Safari I get a "TypeError: Attempted to wrap
> undefined property executeSql as function". I investigated a little bit and
> I saw that when I run this code inside Safari I don't get anything logged to
> my console. These are the options I tried:
> 
> > db.transaction(function (tx) { console.log(tx.constructor.prototype) });
> < undefined
> > db.transaction(function (tx) { console.log(tx.constructor) });
> < undefined
> > db.transaction(function (tx) { console.log(tx) });
> < undefined
> 
> This is very awkward. All these examples works flawlessly in other browsers.
> Then I tried to get the transaction outside from the function, just for
> curiosity and I noticed something even more weird:
> 
> > db.transaction(function (tx) { global_transaction = tx });
> < undefined
> > global_transaction.constructor
> < function Object() {
>     [native code]
> } = $3
> > global_transaction.constructor.prototype
> < {} = $2
> 
> Now I can access the constructor, but the prototype is empty. Because of
> this I can't properly test my code, which uses a lot of webSQL. 

I don't know enough about the specifics of Javascript interfaces to know what is expected here or why WebKit differs from other browsers.

One thought, though, is that *only* Chrome and WebKit implement WebSQL natively. IE, Edge, and Firefox all do not have native support.

So if you're truly testing in something other than Chrome and WebKit, that means you're using a WebSQL polyfill instead of testing native WebSQL implementations.

> Do you think this also has some interference with IndexedDB? 

It definitely has nothing to do with IndexedDB.

> Exactly ALL my tests using IndexedDB fails only on Safari too, but I still have to debug
> them a little bit more to correctly understand what is going on. 

What version of Safari? Have you tried in the Safari Technology Preview?

Once you explore that, we'd love a separate bug (or bugs) on the IndexedDB issues you're seeing.
Comment 2 Douglas Camata 2016-04-22 13:51:13 PDT
I can confirm that even with Safari Technology Preview I can't access the `SQLTransaction.constructor.prototype` properly, as I do in Chrome, although I get better results than with latest stable version of Safari (which only gave me a lot of undefineds):

> db.transaction(function (tx) { console.log(tx.constructor.prototype) });
< undefined
[Log] {}
> db.transaction(function (tx) { console.log(tx.constructor) });
< undefined
[Log] function Object() {
    [native code]
}
> db.transaction(function (tx) { console.log(tx) });
< undefined
[Log] SQLTransaction {executeSql: function}

Positive: there are the SQLTransaction object, its constructor and the constructor's prototype. But the prototype is empty.
Comment 3 Brady Eidson 2016-04-22 14:08:35 PDT
You're putting code inside of comments - Can you please attach your reduced test case?
Comment 4 Douglas Camata 2016-04-22 14:14:56 PDT
(In reply to comment #3)
> You're putting code inside of comments - Can you please attach your reduced
> test case?

Are there any rules or style guide for these test cases or can it be really as simple as possible?
Comment 5 Brady Eidson 2016-04-22 14:16:40 PDT
(In reply to comment #4)
> (In reply to comment #3)
> > You're putting code inside of comments - Can you please attach your reduced
> > test case?
> 
> Are there any rules or style guide for these test cases or can it be really
> as simple as possible?

I'm not asking you to write an automated layout test that would land with any code change to fix the bug.

I'm asking for a test case that somebody can click in the browser and see the bug.

Do that however you want to - There's an "Add an attachment" link above ^^^^
Comment 6 Douglas Camata 2016-04-22 14:19:57 PDT
Created attachment 277099 [details]
simple test case

Simple test case to reproduce the bug. Just open and check the output in the Javascript console and you will see that the prototype of SQLTransaction's constructor is empty.
Comment 7 Chris Dumez 2016-04-22 14:22:46 PDT
(In reply to comment #6)
> Created attachment 277099 [details]
> simple test case
> 
> Simple test case to reproduce the bug. Just open and check the output in the
> Javascript console and you will see that the prototype of SQLTransaction's
> constructor is empty.

The test seems to work for me in the latest WebKit, I see in the console:
[Log] SQLTransaction {executeSql: function} (attachment.cgi, line 6)
[Log] function Object() { (attachment.cgi, line 7)
    [native code]
}
[Log] {} (attachment.cgi, line 8)
Comment 8 Douglas Camata 2016-04-22 14:25:31 PDT
(In reply to comment #7)
> (In reply to comment #6)
> > Created attachment 277099 [details]
> > simple test case
> > 
> > Simple test case to reproduce the bug. Just open and check the output in the
> > Javascript console and you will see that the prototype of SQLTransaction's
> > constructor is empty.
> 
> The test seems to work for me in the latest WebKit, I see in the console:
> [Log] SQLTransaction {executeSql: function} (attachment.cgi, line 6)
> [Log] function Object() { (attachment.cgi, line 7)
>     [native code]
> }
> [Log] {} (attachment.cgi, line 8)

The problem is that last line should have been something like this:

SQLTransaction {Symbol(Symbol.toStringTag): "SQLTransaction"}
* constructor: SQLTransaction()
* executeSql: executeSql()
* Symbol(Symbol.toStringTag): "SQLTransaction"
* __proto__: Object
Comment 9 Brady Eidson 2016-04-22 14:27:22 PDT
(In reply to comment #8)
> (In reply to comment #7)
> > (In reply to comment #6)
> > > Created attachment 277099 [details]
> > > simple test case
> > > 
> > > Simple test case to reproduce the bug. Just open and check the output in the
> > > Javascript console and you will see that the prototype of SQLTransaction's
> > > constructor is empty.
> > 
> > The test seems to work for me in the latest WebKit, I see in the console:
> > [Log] SQLTransaction {executeSql: function} (attachment.cgi, line 6)
> > [Log] function Object() { (attachment.cgi, line 7)
> >     [native code]
> > }
> > [Log] {} (attachment.cgi, line 8)
> 
> The problem is that last line should have been something like this:
> 
> SQLTransaction {Symbol(Symbol.toStringTag): "SQLTransaction"}
> * constructor: SQLTransaction()
> * executeSql: executeSql()
> * Symbol(Symbol.toStringTag): "SQLTransaction"
> * __proto__: Object

I doubt there's any spec on precisely how such things should be logged in the console, which is purely an advisory side function of the browser for developers.

Are there any other more severe differences in the native javascript, such as typeof or instanceof differing?
Comment 10 Chris Dumez 2016-04-22 14:30:16 PDT
Oh, I see, this is a generic problem in our bindings for every interface that is marked as [NoInterfaceObject], for e.g. Geolocation

navigator.geolocation.constructor
-> Object

but should be
function Geolocation() {
    [native code]
} = $3

I can take a look.
Comment 11 Douglas Camata 2016-04-22 14:32:44 PDT
(In reply to comment #9)
> (In reply to comment #8)
> > (In reply to comment #7)
> > > (In reply to comment #6)
> > > > Created attachment 277099 [details]
> > > > simple test case
> > > > 
> > > > Simple test case to reproduce the bug. Just open and check the output in the
> > > > Javascript console and you will see that the prototype of SQLTransaction's
> > > > constructor is empty.
> > > 
> > > The test seems to work for me in the latest WebKit, I see in the console:
> > > [Log] SQLTransaction {executeSql: function} (attachment.cgi, line 6)
> > > [Log] function Object() { (attachment.cgi, line 7)
> > >     [native code]
> > > }
> > > [Log] {} (attachment.cgi, line 8)
> > 
> > The problem is that last line should have been something like this:
> > 
> > SQLTransaction {Symbol(Symbol.toStringTag): "SQLTransaction"}
> > * constructor: SQLTransaction()
> > * executeSql: executeSql()
> > * Symbol(Symbol.toStringTag): "SQLTransaction"
> > * __proto__: Object
> 
> I doubt there's any spec on precisely how such things should be logged in
> the console, which is purely an advisory side function of the browser for
> developers.
> 
> Are there any other more severe differences in the native javascript, such
> as typeof or instanceof differing?

The problem is that WebKit is telling me through the prototype that SQLTransaction has no methods. It is expect that the prototype of SQLTransaction has at least two keys, which are the functions of a SQLTransaction: the constructor and the executeSql methods. 

The representation of the logged stuff in the console doesn't matter, but WebKit says the prototype is completely empty, but the object has 2 methods.
Comment 12 Chris Dumez 2016-04-22 14:36:50 PDT
I think we just need to drop the [NoInterfaceObject] from SQLTransaction. The WebIDL spec says:
"If the [NoInterfaceObject] extended attribute was not specified on the interface, then the interface prototype object must also have a property named “constructor” with attributes { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true } whose value is a reference to the interface object for the interface."

The spec for SQLTransaction says it should not be a [NoInterfaceObject]:
https://dev.w3.org/html5/webdatabase/#sqltransaction
Comment 13 Chris Dumez 2016-04-22 14:44:45 PDT
(In reply to comment #12)
> I think we just need to drop the [NoInterfaceObject] from SQLTransaction.
> The WebIDL spec says:
> "If the [NoInterfaceObject] extended attribute was not specified on the
> interface, then the interface prototype object must also have a property
> named “constructor” with attributes { [[Writable]]: true, [[Enumerable]]:
> false, [[Configurable]]: true } whose value is a reference to the interface
> object for the interface."
> 
> The spec for SQLTransaction says it should not be a [NoInterfaceObject]:
> https://dev.w3.org/html5/webdatabase/#sqltransaction

If I drop the [NoInterfaceObject] on SQLTransaction, the test case does behave as expected:
[Log] SQLTransaction (attachment.cgi, line 6)
No Properties.

SQLTransaction Prototype

[Log] function SQLTransaction() { (attachment.cgi, line 7)
    [native code]
}
[Log] SQLTransactionPrototype (attachment.cgi, line 8)

constructor: function()

executeSql: function()

Object Prototype
Comment 14 Douglas Camata 2016-04-22 14:52:58 PDT
(In reply to comment #13)
> (In reply to comment #12)
> > I think we just need to drop the [NoInterfaceObject] from SQLTransaction.
> > The WebIDL spec says:
> > "If the [NoInterfaceObject] extended attribute was not specified on the
> > interface, then the interface prototype object must also have a property
> > named “constructor” with attributes { [[Writable]]: true, [[Enumerable]]:
> > false, [[Configurable]]: true } whose value is a reference to the interface
> > object for the interface."
> > 
> > The spec for SQLTransaction says it should not be a [NoInterfaceObject]:
> > https://dev.w3.org/html5/webdatabase/#sqltransaction
> 
> If I drop the [NoInterfaceObject] on SQLTransaction, the test case does
> behave as expected:
> [Log] SQLTransaction (attachment.cgi, line 6)
> No Properties.
> 
> SQLTransaction Prototype
> 
> [Log] function SQLTransaction() { (attachment.cgi, line 7)
>     [native code]
> }
> [Log] SQLTransactionPrototype (attachment.cgi, line 8)
> 
> constructor: function()
> 
> executeSql: function()
> 
> Object Prototype

Great, that's exactly what is expected, thanks! Do you know if there is any temporary workaround that I can use while this fix isn't released?
Comment 15 Chris Dumez 2016-04-22 15:01:01 PDT
(In reply to comment #14)
> (In reply to comment #13)
> > (In reply to comment #12)
> > > I think we just need to drop the [NoInterfaceObject] from SQLTransaction.
> > > The WebIDL spec says:
> > > "If the [NoInterfaceObject] extended attribute was not specified on the
> > > interface, then the interface prototype object must also have a property
> > > named “constructor” with attributes { [[Writable]]: true, [[Enumerable]]:
> > > false, [[Configurable]]: true } whose value is a reference to the interface
> > > object for the interface."
> > > 
> > > The spec for SQLTransaction says it should not be a [NoInterfaceObject]:
> > > https://dev.w3.org/html5/webdatabase/#sqltransaction
> > 
> > If I drop the [NoInterfaceObject] on SQLTransaction, the test case does
> > behave as expected:
> > [Log] SQLTransaction (attachment.cgi, line 6)
> > No Properties.
> > 
> > SQLTransaction Prototype
> > 
> > [Log] function SQLTransaction() { (attachment.cgi, line 7)
> >     [native code]
> > }
> > [Log] SQLTransactionPrototype (attachment.cgi, line 8)
> > 
> > constructor: function()
> > 
> > executeSql: function()
> > 
> > Object Prototype
> 
> Great, that's exactly what is expected, thanks! Do you know if there is any
> temporary workaround that I can use while this fix isn't released?

Besides not relying on our SQL* interfaces having a valid 'constructor' property, I don't know of a good workaround unfortunately.
Comment 16 Chris Dumez 2016-04-22 15:21:50 PDT
Created attachment 277106 [details]
Patch
Comment 17 WebKit Commit Bot 2016-04-22 17:57:40 PDT
Comment on attachment 277106 [details]
Patch

Clearing flags on attachment: 277106

Committed r199942: <http://trac.webkit.org/changeset/199942>
Comment 18 WebKit Commit Bot 2016-04-22 17:57:44 PDT
All reviewed patches have been landed.  Closing bug.