- Revision
- 91060
- Author
- [email protected]
- Date
- 2011-07-15 01:35:16 -0700 (Fri, 15 Jul 2011)
Log Message
LocalStorage: Changed the value type of ItemTable from TEXT to BLOB
to avoid string truncation.
This patch will also convert the exsisting ItemTable and perform
the DATA MIGRATION job.
https://bugs.webkit.org/show_bug.cgi?id=58762
Patch by Jonathan Dong <[email protected]> on 2011-07-15
Reviewed by Jeremy Orlow.
Tests: manual-tests/localstorage-value-truncation.html
* manual-tests/localstorage-value-truncation.html: Added.
Demostrate the testcase shown in bug 58762.
* platform/sql/SQLiteStatement.cpp:
(WebCore::SQLiteStatement::isColumnDeclearedAsBlob):
Test if the pre-defined type of column is BLOB.
* platform/sql/SQLiteStatement.h:
* storage/StorageAreaSync.cpp:
Change the localStorage value type from Text to BLOB to avoid the
value string truncation at \x00.
(WebCore::StorageAreaSync::openDatabase):
Change the database structure, use BLOB to replace the TEXT type of
value segment in ItemTable.
(WebCore::StorageAreaSync::migrateItemTableIfNeeded):
Migrate the ItemTable if the pre-defined type of value is TEXT.
(WebCore::StorageAreaSync::performImport):
Use getColumnBlobAsString() to import the BLOB value.
(WebCore::StorageAreaSync::sync):
Use bindBlob() to bind value string to the SQLiteStatement for the
database execution.
* storage/StorageAreaSync.h:
Modified Paths
Added Paths
Diff
Modified: trunk/Source/WebCore/ChangeLog (91059 => 91060)
--- trunk/Source/WebCore/ChangeLog 2011-07-15 08:22:23 UTC (rev 91059)
+++ trunk/Source/WebCore/ChangeLog 2011-07-15 08:35:16 UTC (rev 91060)
@@ -1,3 +1,36 @@
+2011-07-15 Jonathan Dong <[email protected]>
+
+ LocalStorage: Changed the value type of ItemTable from TEXT to BLOB
+ to avoid string truncation.
+ This patch will also convert the exsisting ItemTable and perform
+ the DATA MIGRATION job.
+ https://bugs.webkit.org/show_bug.cgi?id=58762
+
+ Reviewed by Jeremy Orlow.
+
+ Tests: manual-tests/localstorage-value-truncation.html
+
+ * manual-tests/localstorage-value-truncation.html: Added.
+ Demostrate the testcase shown in bug 58762.
+ * platform/sql/SQLiteStatement.cpp:
+ (WebCore::SQLiteStatement::isColumnDeclearedAsBlob):
+ Test if the pre-defined type of column is BLOB.
+ * platform/sql/SQLiteStatement.h:
+ * storage/StorageAreaSync.cpp:
+ Change the localStorage value type from Text to BLOB to avoid the
+ value string truncation at \x00.
+ (WebCore::StorageAreaSync::openDatabase):
+ Change the database structure, use BLOB to replace the TEXT type of
+ value segment in ItemTable.
+ (WebCore::StorageAreaSync::migrateItemTableIfNeeded):
+ Migrate the ItemTable if the pre-defined type of value is TEXT.
+ (WebCore::StorageAreaSync::performImport):
+ Use getColumnBlobAsString() to import the BLOB value.
+ (WebCore::StorageAreaSync::sync):
+ Use bindBlob() to bind value string to the SQLiteStatement for the
+ database execution.
+ * storage/StorageAreaSync.h:
+
2011-07-15 Kentaro Hara <[email protected]>
Clear the content of a search input form when 'Escape' is pressed.
Added: trunk/Source/WebCore/manual-tests/localstorage-value-truncation.html (0 => 91060)
--- trunk/Source/WebCore/manual-tests/localstorage-value-truncation.html (rev 0)
+++ trunk/Source/WebCore/manual-tests/localstorage-value-truncation.html 2011-07-15 08:35:16 UTC (rev 91060)
@@ -0,0 +1,31 @@
+<html>
+<body>
+ <p>In this test we need to find out if the browser can save the localStorage item correctly without truncated by the \x00 in the middle of the string.</p>
+<script type="text/_javascript_">
+ var key = 'TruncVal';
+ var value = '123\x00567';
+
+ var x = localStorage.getItem(key);
+ if (!x) {
+ localStorage.setItem(key, value);
+ document.write("<p>It hasn't got the '" + key + "' in the localStorage database, will create it using:<br>");
+ document.write("<code>localStorage.setItem('" + key + "', '" + value + "');</code><br>");
+ document.write("Now close your browser and start it again to see the results.</p>");
+ }
+ else {
+ document.write("<p>The value of " + key + " is: '" + x + "', the length is: " + x.length + "<br>");
+
+ if (x == value) {
+ document.write("PASS.");
+ }
+ else {
+ document.write("FAIL: The expected value is: '" + value + "', the expected length is: " + value.length);
+ }
+ document.write("</p><a href="" + key + "');\">remove '" + key + "' from localStorage</a>");
+ }
+</script>
+
+<p>This is for <a href=""
+</p>
+</body>
+</html>
Modified: trunk/Source/WebCore/platform/sql/SQLiteStatement.cpp (91059 => 91060)
--- trunk/Source/WebCore/platform/sql/SQLiteStatement.cpp 2011-07-15 08:22:23 UTC (rev 91059)
+++ trunk/Source/WebCore/platform/sql/SQLiteStatement.cpp 2011-07-15 08:35:16 UTC (rev 91060)
@@ -287,6 +287,17 @@
return sqlite3_column_type(m_statement, col) == SQLITE_NULL;
}
+bool SQLiteStatement::isColumnDeclaredAsBlob(int col)
+{
+ ASSERT(col >= 0);
+ if (!m_statement) {
+ if (prepare() != SQLITE_OK)
+ return false;
+ }
+
+ return equalIgnoringCase(String("BLOB"), String(reinterpret_cast<const UChar*>(sqlite3_column_decltype16(m_statement, col))));
+}
+
String SQLiteStatement::getColumnName(int col)
{
ASSERT(col >= 0);
Modified: trunk/Source/WebCore/platform/sql/SQLiteStatement.h (91059 => 91060)
--- trunk/Source/WebCore/platform/sql/SQLiteStatement.h 2011-07-15 08:22:23 UTC (rev 91059)
+++ trunk/Source/WebCore/platform/sql/SQLiteStatement.h 2011-07-15 08:35:16 UTC (rev 91060)
@@ -74,6 +74,7 @@
int columnCount();
bool isColumnNull(int col);
+ bool isColumnDeclaredAsBlob(int col);
String getColumnName(int col);
SQLValue getColumnValue(int col);
String getColumnText(int col);
Modified: trunk/Source/WebCore/storage/StorageAreaSync.cpp (91059 => 91060)
--- trunk/Source/WebCore/storage/StorageAreaSync.cpp 2011-07-15 08:22:23 UTC (rev 91059)
+++ trunk/Source/WebCore/storage/StorageAreaSync.cpp 2011-07-15 08:35:16 UTC (rev 91060)
@@ -33,6 +33,7 @@
#include "HTMLElement.h"
#include "SQLiteFileSystem.h"
#include "SQLiteStatement.h"
+#include "SQLiteTransaction.h"
#include "SecurityOrigin.h"
#include "StorageAreaImpl.h"
#include "StorageSyncManager.h"
@@ -254,7 +255,9 @@
return;
}
- if (!m_database.executeCommand("CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value TEXT NOT NULL ON CONFLICT FAIL)")) {
+ migrateItemTableIfNeeded();
+
+ if (!m_database.executeCommand("CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value BLOB NOT NULL ON CONFLICT FAIL)")) {
LOG_ERROR("Failed to create table ItemTable for local storage");
markImported();
m_databaseOpenFailed = true;
@@ -264,6 +267,49 @@
StorageTracker::tracker().setOriginDetails(m_databaseIdentifier, databaseFilename);
}
+void StorageAreaSync::migrateItemTableIfNeeded()
+{
+ if (!m_database.tableExists("ItemTable"))
+ return;
+
+ {
+ SQLiteStatement query(m_database, "SELECT value FROM ItemTable LIMIT 1");
+ // this query isn't ever executed.
+ if (query.isColumnDeclaredAsBlob(0))
+ return;
+ }
+
+ // alter table for backward compliance, change the value type from TEXT to BLOB.
+ static const char* commands[] = {
+ "DROP TABLE IF EXISTS ItemTable2",
+ "CREATE TABLE ItemTable2 (key TEXT UNIQUE ON CONFLICT REPLACE, value BLOB NOT NULL ON CONFLICT FAIL)",
+ "INSERT INTO ItemTable2 SELECT * from ItemTable",
+ "DROP TABLE ItemTable",
+ "ALTER TABLE ItemTable2 RENAME TO ItemTable",
+ 0,
+ };
+
+ SQLiteTransaction transaction(m_database, false);
+ transaction.begin();
+ for (size_t i = 0; commands[i]; ++i) {
+ if (!m_database.executeCommand(commands[i])) {
+ LOG_ERROR("Failed to migrate table ItemTable for local storage when executing: %s", commands[i]);
+ transaction.rollback();
+
+ // finally it will try to keep a backup of ItemTable for the future restoration.
+ // NOTICE: this will essentially DELETE the current database, but that's better
+ // than continually hitting this case and never being able to use the local storage.
+ // if this is ever hit, it's definitely a bug.
+ ASSERT_NOT_REACHED();
+ if (!m_database.executeCommand("ALTER TABLE ItemTable RENAME TO Backup_ItemTable"))
+ LOG_ERROR("Failed to save ItemTable after migration job failed.");
+
+ return;
+ }
+ }
+ transaction.commit();
+}
+
void StorageAreaSync::performImport()
{
ASSERT(!isMainThread());
@@ -286,7 +332,7 @@
int result = query.step();
while (result == SQLResultRow) {
- itemMap.set(query.getColumnText(0), query.getColumnText(1));
+ itemMap.set(query.getColumnText(0), query.getColumnBlobAsString(1));
result = query.step();
}
@@ -392,7 +438,7 @@
// If the second argument is non-null, we're doing an insert, so bind it as the value.
if (!it->second.isNull())
- query.bindText(2, it->second);
+ query.bindBlob(2, it->second);
int result = query.step();
if (result != SQLResultDone) {
Modified: trunk/Source/WebCore/storage/StorageAreaSync.h (91059 => 91060)
--- trunk/Source/WebCore/storage/StorageAreaSync.h 2011-07-15 08:22:23 UTC (rev 91059)
+++ trunk/Source/WebCore/storage/StorageAreaSync.h 2011-07-15 08:35:16 UTC (rev 91060)
@@ -102,6 +102,7 @@
mutable ThreadCondition m_importCondition;
mutable bool m_importComplete;
void markImported();
+ void migrateItemTableIfNeeded();
};
} // namespace WebCore