Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Workaround for initialization failure on Safari 7 #144

Closed
andygup opened this issue Jul 2, 2014 · 4 comments · Fixed by #148
Closed

Workaround for initialization failure on Safari 7 #144

andygup opened this issue Jul 2, 2014 · 4 comments · Fixed by #148

Comments

@andygup
Copy link

andygup commented Jul 2, 2014

In Safari v7, under certain conditions the library may not be initialized before you try to call indexedDB.open(). This can happen if you need the database open immediately when the page is loaded. The issue may be related to #115

The errors you may see include:
could not prepare statement (1 no such table: dbVersions)
DOMError

One workaround is to:

  • Open IndexedDBShim.js (non-minified)
  • Search for the line that contains the following " tx.executeSql("CREATE TABLE IF NOT EXISTS dbVersions (name VARCHAR(255), version INT);"
  • Modify the code as follows
tx.executeSql("CREATE TABLE IF NOT EXISTS dbVersions (name VARCHAR(255), version INT);", [],
    function(){ console.log("IndexedDB Shim INITIALIZED!");var event = new Event("IndexedDBShimInit"); window.dispatchEvent(event);},
    function(){
    idbModules.util.throwDOMException("Could not create table __sysdb__ to save DB versions");
});
  • In your main application listen for the following event before you attempt to open the database:

window.addEventListener("IndexedDBShimInit",load);

  • Rebuild the library
@andygup
Copy link
Author

andygup commented Jul 3, 2014

One additional change is needed. There are two use cases:

  1. If dbVersions doesn't exist
  2. If dbVersions does exist.

The above code only covers the first use case. The following code covers both. This code emits an initialization event if it's never been created, as well as if it already exists. You can listen for the IndexedDBShimInit (or whatever you want to call it) event to open the database for transactions. I tested the pattern below on Safari 7, Firefox 30, Chrome 35 and Chrome Canary for both use cases:

    sysdb.transaction(function(tx){
        tx.executeSql("SELECT * FROM dbVersions", [], function(t, data){
            console.log("IndexedDB Shim INITIALIZED!");var event = new Event("IndexedDBShimInit"); window.dispatchEvent(event);
        }, function(){
            // dbVersions does not exist, so creating it
            sysdb.transaction(function(tx){
                tx.executeSql("CREATE TABLE IF NOT EXISTS dbVersions (name VARCHAR(255), version INT);", [],
                    function(){ console.log("IndexedDB Shim dbVersions table created!");var event = new Event("IndexedDBShimInit"); window.dispatchEvent(event);},
                    function(){
                    idbModules.util.throwDOMException("Could not create table __sysdb__ to save DB versions");
                });
            });
        });
    }, function(){
        // sysdb Transaction failed
       idbModules.DEBUG && console.log("Error in sysdb transaction - when selecting from dbVersions", arguments);
    });

@unpollito
Copy link

Hello andygup, and thanks for the workaround. This seems to work like a charm in iOS now. However, I intend to run my code in both Android and iOS, and just listening to the IndexedDBShimInit event is a problem: in Android, the database is initialized before I can register any listeners.

What I have done is to set a global variable (i.e. window.INDEXEDDB_STARTED) at the same time that the IndexedDBShimInit event is dispatched. This allows me to know whether the database was initialized before I had time to register any listeners. However, I admit that this is a rather ugly solution (I'm not much of an expert with JS), so I'd like to ask - would there be a more elegant way of achieving the same purpose?

@andygup
Copy link
Author

andygup commented Jul 7, 2014

@davidmartingonzalez I'm not aware of a more elegant solution at this time. The problem is a typical web application life-cycle issue -- things simply have to load in the right order for everything to work correctly. Good idea on the global.

I have some additional suggestions for folks doing both Android and iOS based on additional testing:

  • Make sure the shim's script tag is below the script tag containing the event listener code. If possible place the shim's script tag at the very bottom of the app. This will ensure the event listener is initialized before the shim loads and self-initializes.
  • For Android and other desktop browsers you will also need to fire off an event if the browser doesn't support Web SQL.

Here is the updated 'hack' for the shim that supports both Android and iOS, and additionally it now works correctly with multiple reloads:

    // Change default size to avoid Safari quota bug:
    // https://github.com/axemclion/IndexedDBShim/issues/115
    var DEFAULT_DB_SIZE = 12 * 1024 * 1024;
    if (!window.openDatabase) {
        // If Web SQL isn't available we still need to send an init event!
        console.log("IndexedDB Shim Web SQL not supported!");var event = new Event("IndexedDBShimInit"); window.dispatchEvent(event);
        return;
    }
    // The sysDB to keep track of version numbers for databases
    var sysdb = window.openDatabase("__sysdb__", 1, "System Database", DEFAULT_DB_SIZE);
    sysdb.transaction(function(tx){
        tx.executeSql("SELECT * FROM dbVersions", [], function(t, data){
            console.log("IndexedDB Shim INITIALIZED!");var event = new Event("IndexedDBShimInit"); window.dispatchEvent(event);
        }, function(){
            // dbVersions does not exist, so creating it
            sysdb.transaction(function(tx){
                tx.executeSql("CREATE TABLE IF NOT EXISTS dbVersions (name VARCHAR(255), version INT);", [],
                    function(){ console.log("IndexedDB Shim INITIALIZED!");var event = new Event("IndexedDBShimInit"); window.dispatchEvent(event);},
                    function(){
                    idbModules.util.throwDOMException("Could not create table __sysdb__ to save DB versions");
                });
            });
        });
    }, function(){
        // sysdb Transaction failed
       idbModules.DEBUG && console.log("Error in sysdb transaction - when selecting from dbVersions", arguments);
    });

And, then in your main application you can set up the listener similar to this psuedo-code:

    <script>
    window.addEventListener("IndexedDBShimInit",load);

     function load(){
          // load database and start using it here.
     }
     </script>
     <script src="<sub-directory>/IndexedDBShim.js"></script>

@andygup
Copy link
Author

andygup commented Jul 7, 2014

@davidmartingonzalez I do know of several different approaches that would help alleviate this problem. However they involve changing the architecture of the shim. Those changes would need to be socialized with the owners of the shim as well as the user community (those who have already forked it) to see if they open to a new architecture and potentially breaking changes.

In an attempt to clarify the issues as I'm seeing them, here are several technical use cases for discussion:

Use case 1 - Ability to use the shim with Safari and Chrome (Android). When attempting to use the same pattern for loading and working with the shim in both Chrome and Safari, Safari appears to have a different approach to loading the library that is causing life-cycle issues and essentially the shim fails to work.

Use Case 2 - Ability to listen for initialization events from the shim. Developers should be informed if the following conditions are met:

  • The shim is finished with its initialization sequence
  • The shim initialized correctly
  • The shim failed to initialize and what errors occured.

Use Case 3 - Ability to restart the application and have the shim re-initialize correctly. I'm making a distinction between the initial load that creates the database and reloading the application and initializing a database that already exists.

Use Case 4 - Allow for graceful detection of initialization failures. By implementing a callback architecture or an event-driven architecture, developers can easily detect failures and handle them appropriately. The current architecture seems to require tricky web app detection code and life-cycle specific script loading to determine if the shim has initialized.

And, here is another potential approaches on changes to the shim:

Suggestion 1 - remove the shim's ability to self initialize. Instead allow the shim to be instantiated, for example: var shim = new IndexedDBShim(); This will offer precision control over the life-cycle of shim.

Suggestion 2 - use a callback in the constructor. The callback should return a boolean and any initialization errors. Example: var shim = new IndexedDBShim(function(success,error){ ... }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants