Merge branch 'release/1.5.0'
diff --git a/weinre.build/build.properties b/weinre.build/build.properties
index 14f8f76..1f3fdbf 100644
--- a/weinre.build/build.properties
+++ b/weinre.build/build.properties
@@ -8,7 +8,7 @@
 #-----------------------------------------------------------
 # weinre version
 #-----------------------------------------------------------
-WEINRE_VERSION: 1.4.0
+WEINRE_VERSION: 1.5.0
 
 #-----------------------------------------------------------
 # some common locations used in the ant scripts
diff --git a/weinre.build/build.xml b/weinre.build/build.xml
index b6893b3..1826ebd 100644
--- a/weinre.build/build.xml
+++ b/weinre.build/build.xml
@@ -273,7 +273,7 @@
     <target name="build-json-idl">
         <uptodate property="JSON_IDL_UPTODATE" targetfile="${CACHED}/json-idl/all-json-idls.js">
             <srcfiles dir="../${PROJECT_SERVER}/interfaces"    includes="*.idl"/>
-            <srcfiles dir="${VENDOR}/WebKit/WebCore/inspector" includes="*.idl"/>
+            <srcfiles dir="${VENDOR}/webkit/WebCore/inspector" includes="*.idl"/>
         </uptodate>
         
         <antcall target="build-json-idl-fresh"/>
@@ -290,19 +290,21 @@
         <delete dir="${CACHED}/json-idl"/>
         <mkdir  dir="${CACHED}/json-idl"/>
         
-        <simple-idl2json name="WeinreClientCommands"  oDir="${CACHED}/json-idl" iDir="../${PROJECT_SERVER}/interfaces"/>
-        <simple-idl2json name="WeinreClientEvents"    oDir="${CACHED}/json-idl" iDir="../${PROJECT_SERVER}/interfaces"/>
-        <simple-idl2json name="WeinreTargetCommands"  oDir="${CACHED}/json-idl" iDir="../${PROJECT_SERVER}/interfaces"/>
-        <simple-idl2json name="WeinreTargetEvents"    oDir="${CACHED}/json-idl" iDir="../${PROJECT_SERVER}/interfaces"/>
+        <simple-idl2json name="WeinreClientCommands"      oDir="${CACHED}/json-idl" iDir="../${PROJECT_SERVER}/interfaces"/>
+        <simple-idl2json name="WeinreClientEvents"        oDir="${CACHED}/json-idl" iDir="../${PROJECT_SERVER}/interfaces"/>
+        <simple-idl2json name="WeinreTargetCommands"      oDir="${CACHED}/json-idl" iDir="../${PROJECT_SERVER}/interfaces"/>
+        <simple-idl2json name="WeinreTargetEvents"        oDir="${CACHED}/json-idl" iDir="../${PROJECT_SERVER}/interfaces"/>
+        <simple-idl2json name="WeinreExtraClientCommands" oDir="${CACHED}/json-idl" iDir="../${PROJECT_SERVER}/interfaces"/>
+        <simple-idl2json name="WeinreExtraTargetEvents"   oDir="${CACHED}/json-idl" iDir="../${PROJECT_SERVER}/interfaces"/>
 
-        <simple-idl2json name="InjectedScriptHost"    oDir="${CACHED}/json-idl" iDir="${VENDOR}/WebKit/WebCore/inspector"/>
+        <simple-idl2json name="InjectedScriptHost"    oDir="${CACHED}/json-idl" iDir="${VENDOR}/webkit/WebCore/inspector"/>
         
-        <simple-idl2json name="Inspector"             oDir="${CACHED}/json-idl" iDir="${VENDOR}/WebKit/WebCore/inspector">
+        <simple-idl2json name="Inspector"             oDir="${CACHED}/json-idl" iDir="${VENDOR}/webkit/WebCore/inspector">
             <arg value="--anyType"/>
             <arg value="Value"/>
         </simple-idl2json>
         
-        <simple-idl2json name="InspectorFrontendHost" oDir="${CACHED}/json-idl" iDir="${VENDOR}/WebKit/WebCore/inspector">
+        <simple-idl2json name="InspectorFrontendHost" oDir="${CACHED}/json-idl" iDir="${VENDOR}/webkit/WebCore/inspector">
             <arg value="--anyType"/>
             <arg value="MouseEvent"/>
         </simple-idl2json>
@@ -346,11 +348,11 @@
     <target name="build-client">
         
         <copy todir="${WEB}/client/nls/English.lproj">
-            <fileset file="${VENDOR}/WebKit/WebCore/English.lproj/localizedStrings.js"/>
+            <fileset file="${VENDOR}/webkit/WebCore/English.lproj/localizedStrings.js"/>
         </copy>
 
         <copy todir="${WEB}/client">
-            <fileset dir="${VENDOR}/WebKit/WebCore/inspector/front-end"/>
+            <fileset dir="${VENDOR}/webkit/WebCore/inspector/front-end"/>
         </copy>
 
         <exec executable="python" failonerror="true" failifexecutionfails="true">
diff --git a/weinre.build/get-vendor.xml b/weinre.build/get-vendor.xml
index 3b49f23..4e2b477 100644
--- a/weinre.build/get-vendor.xml
+++ b/weinre.build/get-vendor.xml
@@ -124,28 +124,28 @@
         <delete dir="${VENDOR}/webkit"/>
         <mkdir  dir="${VENDOR}/webkit/WebCore"/>
         
-        <exec executable="svn" dir="${VENDOR}/WebKit/WebCore">
+        <exec executable="svn" dir="${VENDOR}/webkit/WebCore">
             <arg value="export"/>
             <arg value="-r"/>
             <arg value="${WEBKIT_VERSION}"/>
             <arg value="${WEBKIT_URL_PREFIX}/Source/WebCore/inspector"/>
         </exec>
         
-        <exec executable="svn" dir="${VENDOR}/WebKit/WebCore">
+        <exec executable="svn" dir="${VENDOR}/webkit/WebCore">
             <arg value="export"/>
             <arg value="-r"/>
             <arg value="${WEBKIT_VERSION}"/>
             <arg value="${WEBKIT_URL_PREFIX}/Source/WebCore/English.lproj"/>
         </exec>
         
-        <exec executable="svn" dir="${VENDOR}/WebKit/WebCore">
+        <exec executable="svn" dir="${VENDOR}/webkit/WebCore">
             <arg value="export"/>
             <arg value="-r"/>
             <arg value="${WEBKIT_VERSION}"/>
             <arg value="${WEBKIT_URL_PREFIX}/Source/WebCore/css/CSSPropertyNames.in"/>
         </exec>
         
-        <exec executable="grep" outputproperty="gpl-grep-results" dir="${VENDOR}/WebKit/WebCore">
+        <exec executable="grep" outputproperty="gpl-grep-results" dir="${VENDOR}/webkit/WebCore">
             <arg value="-r"/>        
             <arg value="GPL"/>        
             <arg value="."/>        
diff --git a/weinre.build/scripts/changelog.sh b/weinre.build/scripts/changelog.sh
index 3b4c41c..af3a12f 100755
--- a/weinre.build/scripts/changelog.sh
+++ b/weinre.build/scripts/changelog.sh
@@ -8,7 +8,7 @@
 # ---
 
 git log "--pretty=format:- %s" $1.. | \
-    sed 's/#\([0-9]*\)/\<a href="https:\/\/github.com\/pmuellr\/weinre\/issues\/1"\>issue \1\<\/a\>./' |
+    sed 's/#\([0-9]*\)/\<a href="https:\/\/github.com\/phonegap\/weinre\/issues\/1"\>issue \1\<\/a\>./' |
     sed 's/^-/\<li\>/'
 
 #    sed 's/#\([0-9]*\)/[issue \1](https:\/\/github.com\/pmuellr\/weinre\/issues\/1)./'
diff --git a/weinre.doc/ChangeLog.body.html b/weinre.doc/ChangeLog.body.html
index 9429aba..432c26c 100644
--- a/weinre.doc/ChangeLog.body.html
+++ b/weinre.doc/ChangeLog.body.html
@@ -6,6 +6,52 @@
 -->
 
 <!-- ======================================================================= -->
+<h2>Downloads</h2>
+
+<p><a href="https://github.com/phonegap/weinre/downloads">https://github.com/phonegap/weinre/downloads</a>
+
+<!-- ======================================================================= -->
+<h2>2011/07/22 - version 1.5.0</h2>
+
+<ul>
+
+<li><p>The Resources panel now includes an active Databases section.  For any WebSQL
+database that you open, you should see an entry in the expanded section.  For each database,
+you'll see the list of tables owned by the database when the database section
+is expanded.  Note that you need to open
+the database to see it listed here.  Databases which you have previously created
+in other browsing sessions will not be displayed until they are first opened.
+
+<p>Clicking on a database table will dump the database contents to the panel.
+Clicking on the database itself will provide a prompter for you to run SQL 
+commands against that database.  For example, entering 
+<tt>"select * from YourTableNameHere"</tt> 
+will dump the table contents to the panel.
+
+<li><p>Some of the console functions, like <tt>console.log()</tt>, were not handling
+object arguments correctly.  You should now see a an object display.  For example,
+typing <tt>"window"</tt> or <tt>"console.log(window)"</tt> into the console 
+will show you the same object display for the window.
+
+<li><p>The ant build script did not have the correctly-cased file names for some 
+of the files, and the build failed.  This has been fixed.  You should be able
+to do a build on Linux, and other case-sensitive file systems.
+
+<li><p>While weinre already removes some of the Web Inspector panels wholesale,
+there were still a number of meaningless gee-gaws on the panels that we do show.  
+Those have been surgically removed with CSS.
+
+</ul>
+
+<p>issues closed:
+<ul>
+<li> <a href="https://github.com/phonegap/weinre/issues/7">issue 7</a> - support for WebSQL
+<li> <a href="https://github.com/phonegap/weinre/issues/8">issue 8</a> - console.log() etal don't handle object parameters
+<li> <a href="https://github.com/phonegap/weinre/issues/12">issue 12</a> - wrong-cased file names in ant scripts for Linux
+<li> <a href="https://github.com/phonegap/weinre/issues/16">issue 16</a> - hide stuff in the Web Inspector UI that's not used
+</ul>
+
+<!-- ======================================================================= -->
 <h2>2011/05/09 - version 1.4.0</h2>
 <ul>
 <li> better calculation of object's class name. closes <a href="https://github.com/pmuellr/weinre/issues/41">issue 41</a>.
diff --git a/weinre.doc/Home.body.html b/weinre.doc/Home.body.html
index 8812b72..5cf5dc2 100644
--- a/weinre.doc/Home.body.html
+++ b/weinre.doc/Home.body.html
@@ -10,6 +10,14 @@
 Pronounced like the word "winery".  Or maybe like the word "weiner".  Who knows,
 really.
 
+<p>Interesting places:
+<ul>
+<li>GitHub: <a href="https://github.com/phonegap/weinre">https://github.com/phonegap/weinre</a>
+<li>Google Group: <a href="http://groups.google.com/group/weinre">http://groups.google.com/group/weinre</a>
+<li>weinre documentation: <a href="http://phonegap.github.com/weinre">http://phonegap.github.com/weinre</a>
+<li>Google Chrome Developer Tools: <a href="http://code.google.com/chrome/devtools/docs/overview.html">http://code.google.com/chrome/devtools/docs/overview.html</a>
+</ul>
+
 <p>It's a debugger for web pages, like 
 FireBug (for FireFox)
 and 
@@ -22,7 +30,9 @@
 <span class="weinre">weinre</span> reuses the user interface code from the
 <a href="http://trac.webkit.org/wiki/WebInspector">Web Inspector project at WebKit</a>,
 so if you've used Safari's Web Inspector or Chrome's Developer Tools,  
-<span class="weinre">weinre</span> will be very familiar. 
+<span class="weinre">weinre</span> will be very familiar.  If you're not
+familiar with Web Inspector, the link above to Google Chrome Developer Tools
+provides some documentation Real Web Inspector, which <span class="weinre">weinre</span> is based on.
 
 <!--
 <div class="note">
@@ -97,15 +107,6 @@
 how to use it, visit the <a href="TestDrive.html">Test Drive</a>.
 
 <!-- ======================================================== -->
-<h2>Places</h2>
-
-<ul>
-<li>GitHub: <a href="https://github.com/phonegap/weinre">https://github.com/phonegap/weinre</a>
-<li>Documentation: <a href="http://phonegap.github.com/weinre">http://phonegap.github.com/weinre</a>
-<li>Google Group: <a href="http://groups.google.com/group/weinre">http://groups.google.com/group/weinre</a>
-</ul>
-
-<!-- ======================================================== -->
 <h2>Supported Libraries and Platforms</h2>
 
 <p>Generally version numbers listed here are the earliest
diff --git a/weinre.server/interfaces/WeinreClientCommands.idl b/weinre.server/interfaces/WeinreClientCommands.idl
index b912cdf..d0f86cb 100644
--- a/weinre.server/interfaces/WeinreClientCommands.idl
+++ b/weinre.server/interfaces/WeinreClientCommands.idl
@@ -14,7 +14,7 @@
 
         void getTargets(out Object[] targets);
         void getClients(out Object[] clients);
-        
+
         void connectTarget(in string clientId, in string targetId);
         void disconnectTarget(in string clientId);
         
diff --git a/weinre.server/interfaces/WeinreExtraClientCommands.idl b/weinre.server/interfaces/WeinreExtraClientCommands.idl
new file mode 100644
index 0000000..5d6fe5c
--- /dev/null
+++ b/weinre.server/interfaces/WeinreExtraClientCommands.idl
@@ -0,0 +1,17 @@
+/*
+ * weinre is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ * 
+ * Copyright (c) 2011 IBM Corporation
+ */
+
+// extra messages from the client to the target
+
+module weinre {
+    interface WeinreExtraClientCommands {
+    
+        void getDatabases(out Object[] databaseRecords);
+
+    };
+}
+
diff --git a/weinre.server/interfaces/WeinreExtraTargetEvents.idl b/weinre.server/interfaces/WeinreExtraTargetEvents.idl
new file mode 100644
index 0000000..58a6b72
--- /dev/null
+++ b/weinre.server/interfaces/WeinreExtraTargetEvents.idl
@@ -0,0 +1,17 @@
+/*
+ * weinre is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ * 
+ * Copyright (c) 2011 IBM Corporation
+ */
+
+// extra messages from the target to the client
+
+module weinre {
+    interface WeinreExtraTargetEvents {
+    
+        void databaseOpened(in Object databaseRecord);
+
+    };
+}
+
diff --git a/weinre.server/interfaces/WeinreTargetEvents.idl b/weinre.server/interfaces/WeinreTargetEvents.idl
index 36af730..5326b4b 100644
--- a/weinre.server/interfaces/WeinreTargetEvents.idl
+++ b/weinre.server/interfaces/WeinreTargetEvents.idl
@@ -14,7 +14,7 @@
         void connectionDestroyed(string clientId, string targetId);
         
         void sendCallback(string callbackId, Object result);
-
+        
     };
 }
 
diff --git a/weinre.web/client/weinre/client.css b/weinre.web/client/weinre/client.css
index 0eccff5..196a9cb 100644
--- a/weinre.web/client/weinre/client.css
+++ b/weinre.web/client/weinre/client.css
@@ -19,6 +19,34 @@
      font-family: Monaco, Consolas, Lucida Console, dejavu sans mono, monospace;
 }
 
+.panel.resources .cookie-storage-tree-item {
+	display: none;
+}
+
+.panel.resources .frame-storage-tree-item {
+	display: none;
+}
+
+.panel.resources .application-cache-storage-tree-item {
+	display: none;
+}
+
+.panel.timeline .resources-size-graph-sidebar-item {
+	display: none;
+}
+
+#main-status-bar .timeline-category-rendering {
+	display: none;
+}
+
+#search {
+	display: none;
+}
+
+#search-toolbar-label {
+	display: none;
+}
+
 .weinre-connector-item {
     font-size: 140%;
 }
diff --git a/weinre.web/demo/weinre-demo-pieces.html b/weinre.web/demo/weinre-demo-pieces.html
index f572778..51b261f 100644
--- a/weinre.web/demo/weinre-demo-pieces.html
+++ b/weinre.web/demo/weinre-demo-pieces.html
@@ -9,10 +9,8 @@
 <head>
 <meta name="viewport" content="user-scalable=no, width=device-width, height=device-height">
 <title>weinre demo</title>
-<link rel="stylesheet" href="weinre-demo.css">
-<script src="weinre-demo.js"></script>
+
 <script src="/modjewel-require.js"></script>
-<script src="/json2.js"></script>
 <script type="text/javascript">require("modjewel").warnOnRecursiveRequire(true)</script>
 <script src="/scooj.transportd.js"></script>
 <script src="/weinre/common/Ex.transportd.js"></script>
@@ -44,12 +42,17 @@
 <script src="/weinre/target/ElementHighlighter.transportd.js"></script>
 <script src="/weinre/target/InjectedScript.js"></script>
 <script src="/weinre/target/Timeline.transportd.js"></script>
+<script src="/weinre/target/SqlStepper.transportd.js"></script>
+<script src="/weinre/target/WeinreExtraClientCommandsImpl.transportd.js"></script>
 <script src="/interfaces/all-json-idls-min.js"></script>
 
 <script type="text/javascript">
     require("weinre/common/Weinre").getClass().showNotImplemented()
     require('weinre/target/Target').getClass().main()
 </script>
+
+<link rel="stylesheet" href="weinre-demo.css">
+<script src="weinre-demo.js"></script>
 </head>
 
 <body onload="onLoad()">
diff --git a/weinre.web/demo/weinre-demo.js b/weinre.web/demo/weinre-demo.js
index e0b95e3..32c72b5 100644
--- a/weinre.web/demo/weinre-demo.js
+++ b/weinre.web/demo/weinre-demo.js
@@ -12,6 +12,8 @@
 var buttonClearOutput
 var outputElement 
 var storageIndex = 0
+var db
+var otherDB 
 
 // set the id based on the hash
 var hash = location.href.split("#")[1]
@@ -25,6 +27,11 @@
     if (!outputElement)     outputElement     = document.getElementById("output")
     
     buttonStartStuff.addEventListener("click", function() {
+        lastClickTime = new Date().toString()
+        if (db) db.transaction(addClick)
+        
+        openTheOtherDatabase()
+        
         if (!started) {
             buttonStartStuff.value = "stop stuff"
             startStuff()
@@ -40,6 +47,7 @@
         outputElement.innerHTML = ""
     })
     
+    openTheDatabase()
 }
 
 //------------------------------------------------------------------------------
@@ -97,6 +105,64 @@
 }
 
 //------------------------------------------------------------------------------
+function sqlSuccess(tx, resultSet) {
+    console.log("SQL Success!")
+}
+
+//------------------------------------------------------------------------------
+function sqlError(tx, error) {
+    console.log("SQL Error " + error.code + ": " + error.message)
+}
+
+//------------------------------------------------------------------------------
+var lastClickTime
+
+function addClick(tx) {
+    var sql = "insert into clicks (date) values (?)"
+    tx.executeSql(sql, [lastClickTime], null, sqlError)
+}
+
+//------------------------------------------------------------------------------
+function clearDatabase(tx, resultSet) {
+    var sql = "delete from clicks"
+    tx.executeSql(sql, null, null, sqlError);
+}
+
+//------------------------------------------------------------------------------
+function createDatabase(tx) {
+    var schema = "clicks (id integer primary key, date text)"
+    var sql = "create table if not exists " + schema
+    
+    tx.executeSql(sql, null, clearDatabase, sqlError);
+}
+
+//------------------------------------------------------------------------------
+function createDatabase_other(tx) {
+    var schema = "clicks_other (id integer primary key, other text)"
+    var sql = "create table if not exists " + schema
+    
+    tx.executeSql(sql, null, null, sqlError);
+}
+
+//------------------------------------------------------------------------------
+function openTheDatabase() {
+    if (window.openDatabase) {
+        db = window.openDatabase("clicks_db", "1.0", "clicks_db", 8192)
+        db.transaction(createDatabase)
+    }
+}
+
+//------------------------------------------------------------------------------
+function openTheOtherDatabase() {
+    if (otherDB) return
+    
+    if (window.openDatabase) {
+        otherDB = window.openDatabase("clicks_other_db", "1.0", "clicks_other_db", 8192)
+        otherDB.transaction(createDatabase_other)
+    }
+}
+
+//------------------------------------------------------------------------------
 function output(string) {
     var element = document.createElement("div")
     element.innerHTML = string
diff --git a/weinre.web/modules/weinre/client/Client.scoop b/weinre.web/modules/weinre/client/Client.scoop
index f1368d8..73c499a 100644
--- a/weinre.web/modules/weinre/client/Client.scoop
+++ b/weinre.web/modules/weinre/client/Client.scoop
@@ -18,6 +18,7 @@
 requireClass ./InspectorBackendImpl
 requireClass ./InspectorFrontendHostImpl
 requireClass ./WeinreClientEventsImpl
+requireClass ./WeinreExtraTargetEventsImpl
 requireClass ./RemotePanel
 
 //-----------------------------------------------------------------------------
@@ -47,7 +48,10 @@
 
     // create the client commands proxy
     Weinre.WeinreClientCommands = messageDispatcher.createProxy("WeinreClientCommands")
+    Weinre.WeinreExtraClientCommands = messageDispatcher.createProxy("WeinreExtraClientCommands")
 
+    messageDispatcher.registerInterface("WeinreExtraTargetEvents", new WeinreExtraTargetEventsImpl(), false)
+    
     // register WebInspector interface
     messageDispatcher.registerInterface("WebInspector", WebInspector, false)
     
diff --git a/weinre.web/modules/weinre/client/WeinreClientEventsImpl.scoop b/weinre.web/modules/weinre/client/WeinreClientEventsImpl.scoop
index 459373d..7fd6a65 100644
--- a/weinre.web/modules/weinre/client/WeinreClientEventsImpl.scoop
+++ b/weinre.web/modules/weinre/client/WeinreClientEventsImpl.scoop
@@ -9,6 +9,8 @@
 requireClass ../common/Callback
 requireClass ../common/Weinre
 
+requireClass ./WeinreExtraTargetEventsImpl
+
 //-----------------------------------------------------------------------------
 class WeinreClientEventsImpl(client)
     this.client = client
@@ -67,6 +69,10 @@
     
     WebInspector.inspectedURLChanged(target.url)
 
+    Weinre.WeinreExtraClientCommands.getDatabases(function(databaseRecords) {
+        WeinreExtraTargetEventsImpl.addDatabaseRecords(databaseRecords)
+    })
+    
 //-----------------------------------------------------------------------------
 method connectionDestroyed(/*int*/ clientChannel, /*int*/ targetChannel)
 
diff --git a/weinre.web/modules/weinre/client/WeinreExtraTargetEventsImpl.scoop b/weinre.web/modules/weinre/client/WeinreExtraTargetEventsImpl.scoop
new file mode 100644
index 0000000..76abaad
--- /dev/null
+++ b/weinre.web/modules/weinre/client/WeinreExtraTargetEventsImpl.scoop
@@ -0,0 +1,48 @@
+
+/*
+ * weinre is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ * 
+ * Copyright (c) 2011 IBM Corporation
+ */
+
+//-----------------------------------------------------------------------------
+class WeinreExtraTargetEventsImpl
+
+//-----------------------------------------------------------------------------
+method databaseOpened(databaseRecord)
+    WeinreExtraTargetEventsImpl.addDatabaseRecords([databaseRecord])
+    
+//-----------------------------------------------------------------------------
+static method addDatabaseRecords(databaseRecords)
+    // databaseRecord.id
+    // databaseRecord.domain
+    // databaseRecord.name
+    // databaseRecord.version
+    
+    if (!WebInspector.panels) return
+    if (!WebInspector.panels.resources) return
+    if (!WebInspector.panels.resources._databases) return
+    
+    var existingDbs = WebInspector.panels.resources._databases
+    var existingDbNames = {}
+    
+    for (var i=0; i<existingDbs.length; i++) {
+        existingDbNames[existingDbs[i].name] = existingDbs[i]
+    }
+    
+    for (var i=0; i<databaseRecords.length; i++) {
+        if (existingDbNames[databaseRecords[i].name]) continue
+        
+        var databaseRecord = databaseRecords[i]
+        var database = new WebInspector.Database(
+            databaseRecord.id,
+            databaseRecord.domain,
+            databaseRecord.name,
+            databaseRecord.version
+            )
+        
+        WebInspector.panels.resources.addDatabase(database)
+    }
+    
+    
diff --git a/weinre.web/modules/weinre/common/Native.scoop b/weinre.web/modules/weinre/common/Native.scoop
index 705faa7..af28f0b 100644
--- a/weinre.web/modules/weinre/common/Native.scoop
+++ b/weinre.web/modules/weinre/common/Native.scoop
@@ -25,6 +25,7 @@
     Native.original.SessionStorage_setItem    = window.sessionStorage ? window.sessionStorage.setItem    : null
     Native.original.SessionStorage_removeItem = window.sessionStorage ? window.sessionStorage.removeItem : null
     Native.original.SessionStorage_clear      = window.sessionStorage ? window.sessionStorage.clear      : null
+    Native.original.openDatabase              = window.openDatabase
 
     Native.clearInterval             = function() { return Native.original.clearInterval.apply( window, [].slice.call(arguments))}
     Native.clearTimeout              = function() { return Native.original.clearTimeout.apply(  window, [].slice.call(arguments))}
@@ -38,3 +39,5 @@
     Native.SessionStorage_setItem    = function() { return Native.original.SessionStorage_setItem.apply(    window.sessionStorage, [].slice.call(arguments))}
     Native.SessionStorage_removeItem = function() { return Native.original.SessionStorage_removeItem.apply( window.sessionStorage, [].slice.call(arguments))}
     Native.SessionStorage_clear      = function() { return Native.original.SessionStorage_clear.apply(      window.sessionStorage, [].slice.call(arguments))}
+    Native.openDatabase              = function() { return Native.original.openDatabase.apply( window, [].slice.call(arguments))}
+
diff --git a/weinre.web/modules/weinre/target/Console.scoop b/weinre.web/modules/weinre/target/Console.scoop
index b528b4d..3d95827 100644
--- a/weinre.web/modules/weinre/target/Console.scoop
+++ b/weinre.web/modules/weinre/target/Console.scoop
@@ -73,7 +73,7 @@
 //-----------------------------------------------------------------------------
 method _generic(level, messageParts)
     
-    var message = messageParts[0]
+    var message = messageParts[0].toString()
     
     var parameters = []
     for (var i=0; i<messageParts.length; i++) {
diff --git a/weinre.web/modules/weinre/target/SqlStepper.scoop b/weinre.web/modules/weinre/target/SqlStepper.scoop
new file mode 100644
index 0000000..3789812
--- /dev/null
+++ b/weinre.web/modules/weinre/target/SqlStepper.scoop
@@ -0,0 +1,92 @@
+
+/*
+ * weinre is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ * 
+ * Copyright (c) 2010, 2011 IBM Corporation
+ */
+
+requireClass ../common/Binding
+
+//-----------------------------------------------------------------------------
+class SqlStepper(steps)
+    if (!(this instanceof SqlStepper)) return new SqlStepper(steps)
+    
+    this.__context = {}
+    
+    var context = this.__context
+    context.steps = steps
+    
+//-----------------------------------------------------------------------------
+method run(db, errorCallback)
+    var context = this.__context
+    
+    if (context.hasBeenRun) 
+        throw new Ex(arguments, "stepper has already been run")
+        
+    context.hasBeenRun = true
+        
+    context.db            = db
+    context.errorCallback = errorCallback
+    context.nextStep      = 0
+    
+    context.ourErrorCallback = new Binding(this, ourErrorCallback)
+    context.runStep          = new Binding(this, runStep)
+    this.executeSql          = new Binding(this, executeSql)
+    
+    db.transaction(context.runStep)
+    
+//-----------------------------------------------------------------------------
+function executeSql(statement, data)
+    var context = this.__context
+    
+    context.tx.executeSql(statement, data, context.runStep, context.ourErrorCallback)
+
+//-----------------------------------------------------------------------------
+function ourErrorCallback(tx, sqlError)
+    var context = this.__context
+    
+    context.errorCallback.call(this, sqlError)
+    
+//-----------------------------------------------------------------------------
+function runStep(tx, resultSet)
+    var context = this.__context
+    
+    if (context.nextStep >= context.steps.length) return
+    
+    context.tx = tx
+    
+    context.currentStep = context.nextStep
+    context.nextStep++
+    
+    var step = context.steps[context.currentStep]
+    
+    step.call(this, resultSet)
+
+//-----------------------------------------------------------------------------
+static method example(db, id)
+    function step1() {
+        this.executeSql("SELECT name FROM sqlite_master WHERE type='table'")
+    }
+    
+    function step2(resultSet) {
+        var rows = resultSet.rows
+        var result = []
+        for (var i=0; i<rows.length; i++) {
+            var name = rows.item(i).name
+            if (name == "__WebKitDatabaseInfoTable__") continue
+        
+            result.push(name)
+        }
+        
+        console.log("[" + this.id + "] table names: " + result.join(", "))
+    }
+
+    function errorCb(sqlError) {
+        
+        console.log("[" + this.id + "] sql error:" + sqlError.code + ": " + sqlError.message)
+    }
+    
+    var stepper = new SqlStepper([step1, step2])
+    stepper.id = id
+    stepper.run(db, errorCb)
diff --git a/weinre.web/modules/weinre/target/Target.scoop b/weinre.web/modules/weinre/target/Target.scoop
index 0a4d81d..bb3eeae 100644
--- a/weinre.web/modules/weinre/target/Target.scoop
+++ b/weinre.web/modules/weinre/target/Target.scoop
@@ -21,6 +21,7 @@
 requireClass ./InjectedScriptHostImpl
 
 requireClass ./WeinreTargetEventsImpl
+requireClass ./WeinreExtraClientCommandsImpl
 
 requireClass ./WiConsoleImpl
 requireClass ./WiCSSImpl
@@ -45,15 +46,16 @@
     }
     
 //------------------------------------------------------------------------------
-method setWeinreServerURLFromScriptSrc()
+method setWeinreServerURLFromScriptSrc(element)
     if (window.WeinreServerURL) return
 
-    var element = this.getTargetScriptElement()
-    var pattern = /(http:\/\/(.*?)\/)/
-    var match   = pattern.exec(element.src)
-    if (match) {
-        window.WeinreServerURL = match[1]
-        return 
+    if (element) {
+        var pattern = /(http:\/\/(.*?)\/)/
+        var match   = pattern.exec(element.src)
+        if (match) {
+            window.WeinreServerURL = match[1]
+            return 
+        }
     }
     
     var message = "unable to calculate the weinre server url; explicity set the variable window.WeinreServerURL instead" 
@@ -62,14 +64,25 @@
 
 
 //-----------------------------------------------------------------------------
-method setWeinreServerIdFromScriptSrc()
+method setWeinreServerIdFromScriptSrc(element)
     if (window.WeinreServerId) return
 
     var element = this.getTargetScriptElement()
 
-    var hash = element.src.split("#")[1]
-    if (!hash) hash = "anonymous"
-    
+    var hash = "anonymous"
+    if (element) {
+        var attempt = element.src.split("#")[1]
+        if (attempt) {
+            hash = attempt
+        }
+        else {
+            attempt = location.hash.split("#")[1]
+            if (attempt) {
+                hash = attempt
+            }
+        }
+    }
+
     window.WeinreServerId = hash
 
 //-----------------------------------------------------------------------------
@@ -92,8 +105,9 @@
 method initialize()
     var self = this
 
-    this.setWeinreServerURLFromScriptSrc()
-    this.setWeinreServerIdFromScriptSrc()
+    var element = this.getTargetScriptElement()
+    this.setWeinreServerURLFromScriptSrc(element)
+    this.setWeinreServerIdFromScriptSrc(element)
     
     if (window.WeinreServerURL[window.WeinreServerURL.length-1] != "/") {
         window.WeinreServerURL += "/"
@@ -121,13 +135,13 @@
 
     Weinre.wi = {}
     
-    Weinre.wi.Console          = new WiConsoleImpl()
-    Weinre.wi.CSS              = new WiCSSImpl()
-    Weinre.wi.Database         = new WiDatabaseImpl()
-    Weinre.wi.DOM              = new WiDOMImpl()
-    Weinre.wi.DOMStorage       = new WiDOMStorageImpl()
-    Weinre.wi.Inspector        = new WiInspectorImpl()
-    Weinre.wi.Runtime          = new WiRuntimeImpl()
+    Weinre.wi.Console                   = new WiConsoleImpl()
+    Weinre.wi.CSS                       = new WiCSSImpl()
+    Weinre.wi.Database                  = new WiDatabaseImpl()
+    Weinre.wi.DOM                       = new WiDOMImpl()
+    Weinre.wi.DOMStorage                = new WiDOMStorageImpl()
+    Weinre.wi.Inspector                 = new WiInspectorImpl()
+    Weinre.wi.Runtime                   = new WiRuntimeImpl()
     
     messageDispatcher.registerInterface("Console",          Weinre.wi.Console          , false)
     messageDispatcher.registerInterface("CSS",              Weinre.wi.CSS              , false)
@@ -136,8 +150,9 @@
     messageDispatcher.registerInterface("DOMStorage",       Weinre.wi.DOMStorage       , false)
     messageDispatcher.registerInterface("Inspector",        Weinre.wi.Inspector        , false)
     messageDispatcher.registerInterface("Runtime",          Weinre.wi.Runtime          , false)
-    
-    messageDispatcher.registerInterface("WeinreTargetEvents", new WeinreTargetEventsImpl(), true)
+
+    messageDispatcher.registerInterface("WeinreExtraClientCommands",  new WeinreExtraClientCommandsImpl() , true)
+    messageDispatcher.registerInterface("WeinreTargetEvents",         new WeinreTargetEventsImpl(),         true)
 
     Weinre.wi.ApplicationCacheNotify = messageDispatcher.createProxy("ApplicationCacheNotify")
     Weinre.wi.ConsoleNotify          = messageDispatcher.createProxy("ConsoleNotify")
@@ -147,7 +162,8 @@
     Weinre.wi.InspectorNotify        = messageDispatcher.createProxy("InspectorNotify")
     Weinre.wi.TimelineNotify         = messageDispatcher.createProxy("TimelineNotify")
     
-    Weinre.WeinreTargetCommands  = messageDispatcher.createProxy("WeinreTargetCommands")
+    Weinre.WeinreTargetCommands     = messageDispatcher.createProxy("WeinreTargetCommands")
+    Weinre.WeinreExtraTargetEvents  = messageDispatcher.createProxy("WeinreExtraTargetEvents")
 
     messageDispatcher.getWebSocket().addEventListener("open", Binding(this, this.cb_webSocketOpened))
     
diff --git a/weinre.web/modules/weinre/target/WeinreExtraClientCommandsImpl.scoop b/weinre.web/modules/weinre/target/WeinreExtraClientCommandsImpl.scoop
new file mode 100644
index 0000000..28d6843
--- /dev/null
+++ b/weinre.web/modules/weinre/target/WeinreExtraClientCommandsImpl.scoop
@@ -0,0 +1,24 @@
+
+/*
+ * weinre is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ * 
+ * Copyright (c) 2010, 2011 IBM Corporation
+ */
+
+requireClass ../common/Weinre
+
+requireClass ./WiDatabaseImpl
+requireClass ./Console
+
+//-----------------------------------------------------------------------------
+class WeinreExtraClientCommandsImpl
+
+//-----------------------------------------------------------------------------
+method getDatabases(callback)
+    if (!callback) return
+    
+    var result = WiDatabaseImpl.getDatabases()
+    
+    Weinre.WeinreTargetCommands.sendClientCallback(callback, [result])
+
diff --git a/weinre.web/modules/weinre/target/WiDatabaseImpl.scoop b/weinre.web/modules/weinre/target/WiDatabaseImpl.scoop
index 7b71765..e9fa6df 100644
--- a/weinre.web/modules/weinre/target/WiDatabaseImpl.scoop
+++ b/weinre.web/modules/weinre/target/WiDatabaseImpl.scoop
@@ -7,17 +7,165 @@
  */
 
 requireClass ../common/Weinre
+requireClass ../common/Native
+requireClass ../common/IDGenerator
+requireClass ./SqlStepper
 
 //-----------------------------------------------------------------------------
 class WiDatabaseImpl
+    if (!window.openDatabase) return
+    
+    window.openDatabase = wrappedOpenDatabase
 
 //-----------------------------------------------------------------------------
-method getDatabaseTableNames(/*int*/ databaseId, callback)
-    // callback: function(/*any[]*/ tableNames)
-    Weinre.notImplemented(arguments.callee.signature)
+static method getDatabases
+    var result = []
+    
+    for (var id in id2db) {
+        result.push(id2db[id])
+    }
+    
+    return result
 
+//-----------------------------------------------------------------------------
+function logSqlError(sqlError)
+    console.log("SQL Error " + sqlError.code + ": " + sqlError.message)
+
+//-----------------------------------------------------------------------------
+function getTableNames_step_1() 
+    this.executeSql("SELECT name FROM sqlite_master WHERE type='table'");
+
+//-----------------------------------------------------------------------------
+function getTableNames_step_2(resultSet) 
+    var rows = resultSet.rows
+    var result = []
+    for (var i=0; i<rows.length; i++) {
+        var name = rows.item(i).name
+        if (name == "__WebKitDatabaseInfoTable__") continue
+        
+        result.push(name)
+    }
+    
+    Weinre.WeinreTargetCommands.sendClientCallback(this.callback, [result])
+
+//-----------------------------------------------------------------------------
+method getDatabaseTableNames( databaseId, callback)
+    var db = dbById(databaseId)
+    if (!db) return
+  
+    var stepper = SqlStepper([
+        getTableNames_step_1, 
+        getTableNames_step_2
+    ])
+    
+    stepper.callback = callback
+    stepper.run(db, logSqlError)
+
+//-----------------------------------------------------------------------------
+function executeSQL_step_1()
+    this.executeSql(this.query);
+
+//-----------------------------------------------------------------------------
+function executeSQL_step_2(resultSet)
+    var columnNames = []
+    var values      = []
+    var rows        = resultSet.rows
+    
+    for (var i=0; i<rows.length; i++) {
+        var row = rows.item(i)
+        
+        if (i == 0) {
+            for (var propName in row) {
+                columnNames.push(propName)
+            }
+        }
+        
+        for (var j=0; j<columnNames.length; j++) {
+            values.push(row[columnNames[j]])
+        }
+    }
+
+    Weinre.wi.DatabaseNotify.sqlTransactionSucceeded(this.txid, columnNames, values)
+
+//-----------------------------------------------------------------------------
+function executeSQL_error(sqlError)
+    var error = {
+        code:    sqlError.code,
+        message: sqlError.message
+    }
+    
+    Weinre.wi.DatabaseNotify.sqlTransactionFailed(this.txid, error)
 
 //-----------------------------------------------------------------------------
 method executeSQL(/*int*/ databaseId, /*string*/ query, callback)
-    // callback: function(/*boolean*/ success, /*int*/ transactionId)
-    Weinre.notImplemented(arguments.callee.signature)
+    var db = dbById(databaseId)
+    if (!db) return
+    
+    var txid = Weinre.targetDescription.channel + "-" + IDGenerator.next()
+    
+    var stepper = SqlStepper([
+        executeSQL_step_1,
+        executeSQL_step_2,
+    ])
+    
+    stepper.txid     = txid
+    stepper.query    = query
+    stepper.callback = callback
+    stepper.run(db, executeSQL_error)
+    
+    if (callback) {
+        Weinre.WeinreTargetCommands.sendClientCallback(callback, [true, txid])
+    }
+    
+
+//-----------------------------------------------------------------------------
+function wrappedOpenDatabase(name, version, displayName, estimatedSize, creationCallback)
+
+    var db = Native.openDatabase(name, version, displayName, estimatedSize, creationCallback)
+    
+    dbAdd(db, name, version)
+    
+    return db    
+    
+//-----------------------------------------------------------------------------
+init 
+    var id2db   = {}
+    var name2db = {}
+
+//-----------------------------------------------------------------------------
+function dbById(id) 
+    var record = id2db[id]
+    if (!record) return null
+    return record.db
+    
+//-----------------------------------------------------------------------------
+function dbRecordById(id) 
+    return id2db[id]
+    
+//-----------------------------------------------------------------------------
+function dbRecordByName(name) 
+    return name2db[name]
+    
+//-----------------------------------------------------------------------------
+function dbAdd(db, name, version)
+    
+    var record = dbRecordByName(name)
+    if (record) return record
+    
+    record = {}
+    record.id      = IDGenerator.next()
+    record.domain  = window.location.origin
+    record.name    = name
+    record.version = version
+    record.db      = db
+    
+    id2db[record.id] = record
+    name2db[name]    = record
+
+    var payload = {}
+    payload.id      = record.id
+    payload.domain  = record.domain
+    payload.name    = name
+    payload.version = version
+    
+    Weinre.WeinreExtraTargetEvents.databaseOpened(payload)