Updated PYTHON code to correctly generate web_sql and re-wrote/re-enabled async web_sql test.

R=alanknight@google.com,vsm@google.com

Change-Id: I80e82f5aaa3c9748740031d8da139f79e2c0ab70
Reviewed-on: https://dart-review.googlesource.com/31080
Commit-Queue: Terry Lucas <terry@google.com>
Reviewed-by: Alan Knight <alanknight@google.com>
This commit is contained in:
Terry Lucas
2017-12-22 14:16:35 -08:00
committed by commit-bot@chromium.org
parent 7fbacb037d
commit 0c8294e4a0
11 changed files with 333 additions and 231 deletions
+26 -17
View File
@@ -30324,8 +30324,8 @@ class RtcPeerConnection extends EventTarget {
var completer = new Completer();
_setLocalDescription(description, () {
completer.complete();
}, (error) {
completer.completeError(error);
}, (exception) {
completer.completeError(exception);
});
return completer.future;
}
@@ -30344,8 +30344,8 @@ class RtcPeerConnection extends EventTarget {
var completer = new Completer();
_setRemoteDescription(description, () {
completer.complete();
}, (error) {
completer.completeError(error);
}, (exception) {
completer.completeError(exception);
});
return completer.future;
}
@@ -37965,19 +37965,6 @@ class Window extends EventTarget
@DocsEditable()
void _moveTo(int x, int y) native;
/// *Deprecated.*
@DomName('Window.openDatabase')
@DocsEditable()
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.SAFARI)
@Experimental()
// http://www.w3.org/TR/webdatabase/
@Experimental() // deprecated
@Creates('SqlDatabase')
SqlDatabase openDatabase(
String name, String version, String displayName, int estimatedSize,
[DatabaseCallback creationCallback]) native;
@DomName('Window.postMessage')
@DocsEditable()
void postMessage(/*any*/ message, String targetOrigin,
@@ -38921,6 +38908,28 @@ class Window extends EventTarget
_moveTo(p.x, p.y);
}
@JSName('openDatabase')
@DomName('Window.openDatabase')
SqlDatabase _openDatabase(
String name, String version, String displayName, int estimatedSize,
[DatabaseCallback creationCallback]) native;
@JSName('openDatabase')
@DomName('Window.openDatabase')
@DocsEditable()
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.SAFARI)
@Experimental()
@Creates('SqlDatabase')
SqlDatabase openDatabase(
String name, String version, String displayName, int estimatedSize) {
var db = _openDatabase(name, version, displayName, estimatedSize);
applyExtension('Database', db);
return db;
}
@DomName('Window.pageXOffset')
@DocsEditable()
int get pageXOffset => JS('num', '#.pageXOffset', this).round();
+34 -2
View File
@@ -23,6 +23,7 @@ import 'dart:_interceptors' show Interceptor;
import 'dart:_js_helper'
show
applyExtension,
convertDartClosureToJS,
Creates,
JSName,
@@ -123,11 +124,26 @@ class SqlDatabase extends Interceptor {
[SqlTransactionErrorCallback errorCallback,
VoidCallback successCallback]) native;
@JSName('transaction')
@DomName('Database.transaction')
@DocsEditable()
void transaction(SqlTransactionCallback callback,
void _transaction(SqlTransactionCallback callback,
[SqlTransactionErrorCallback errorCallback,
VoidCallback successCallback]) native;
@JSName('transaction')
@DomName('Database.transaction')
@DocsEditable()
Future<SqlTransaction> transaction() {
var completer = new Completer<SqlTransaction>();
_transaction((value) {
applyExtension('SQLTransaction', value);
completer.complete(value);
}, (error) {
completer.completeError(error);
});
return completer.future;
}
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
@@ -304,10 +320,26 @@ class SqlTransaction extends Interceptor {
throw new UnsupportedError("Not supported");
}
@JSName('executeSql')
@DomName('SQLTransaction.executeSql')
@DocsEditable()
void executeSql(String sqlStatement,
void _executeSql(String sqlStatement,
[List arguments,
SqlStatementCallback callback,
SqlStatementErrorCallback errorCallback]) native;
@JSName('executeSql')
@DomName('SQLTransaction.executeSql')
@DocsEditable()
Future<SqlResultSet> executeSql(String sqlStatement, [List arguments]) {
var completer = new Completer<SqlResultSet>();
_executeSql(sqlStatement, arguments, (transaction, resultSet) {
applyExtension('SQLResultSet', resultSet);
applyExtension('SQLResultSetRowList', resultSet.rows);
completer.complete(resultSet);
}, (transaction, error) {
completer.completeError(error);
});
return completer.future;
}
}
+93 -98
View File
@@ -3,131 +3,126 @@ library WebDBTest;
import 'dart:async';
import 'dart:html';
import 'dart:web_sql';
import 'package:unittest/unittest.dart';
import 'package:unittest/html_individual_config.dart';
Future<SqlTransaction> transaction(SqlDatabase db) {
final completer = new Completer<SqlTransaction>.sync();
db.transaction((SqlTransaction transaction) {
completer.complete(transaction);
}, (SqlError error) {
completer.completeError(error);
});
return completer.future;
}
import 'package:async_helper/async_helper.dart';
Future<SqlResultSet> createTable(
SqlTransaction transaction, String tableName, String columnName) {
final completer = new Completer<SqlResultSet>.sync();
final sql = 'CREATE TABLE $tableName ($columnName)';
transaction.executeSql(sql, [], (SqlTransaction tx, SqlResultSet rs) {
completer.complete(rs);
}, (SqlTransaction tx, SqlError error) {
completer.completeError(error);
});
return completer.future;
SqlTransaction transaction, String tableName, String columnName) async {
return transaction.executeSql('CREATE TABLE $tableName ($columnName)', []);
}
Future<SqlResultSet> insert(
SqlTransaction transaction, String tableName, String columnName, value) {
final completer = new Completer<SqlResultSet>.sync();
Future<SqlResultSet> insertTable(SqlTransaction transaction, String tableName,
String columnName, value) async {
final sql = 'INSERT INTO $tableName ($columnName) VALUES (?)';
transaction.executeSql(sql, [value], (SqlTransaction tx, SqlResultSet rs) {
completer.complete(rs);
}, (SqlTransaction tx, SqlError error) {
completer.completeError(error);
});
return completer.future;
return transaction.executeSql(sql, [value]);
}
Future<SqlResultSet> queryTable(SqlTransaction transaction, String tableName) {
final completer = new Completer<SqlResultSet>.sync();
Future<SqlResultSet> queryTable(
SqlTransaction transaction, String tableName) async {
final sql = 'SELECT * FROM $tableName';
transaction.executeSql(sql, [], (SqlTransaction tx, SqlResultSet rs) {
completer.complete(rs);
}, (SqlTransaction tx, SqlError error) {
completer.completeError(error);
});
return completer.future;
return transaction.executeSql(sql, []);
}
Future<SqlResultSet> dropTable(SqlTransaction transaction, String tableName,
[bool ignoreFailure = false]) {
final completer = new Completer<SqlResultSet>.sync();
final sql = 'DROP TABLE $tableName';
transaction.executeSql(sql, [], (SqlTransaction tx, SqlResultSet rs) {
completer.complete(rs);
}, (SqlTransaction tx, SqlError error) {
if (ignoreFailure) {
completer.complete(null);
} else {
completer.completeError(error);
}
});
return completer.future;
[bool ignoreFailure = false]) async {
try {
var result = await transaction.executeSql('DROP TABLE $tableName', []);
return result;
} catch (error) {
if (!ignoreFailure) throw error;
}
}
main() {
final tableName = 'test_table';
final columnName = 'test_data';
SqlDatabase db;
SqlTransaction tx;
Future setup() async {
if (SqlDatabase.supported) {
db = await window.openDatabase('test_db', '1.0', 'test_db', 1024 * 1024);
expect(db, isNotNull, reason: 'Unable to open database');
tx = await db.transaction();
expect(tx, isNotNull, reason: "Transaction not ready");
}
}
main() async {
useHtmlIndividualConfiguration();
group('supported', () {
await setup();
group('Database', () {
test('supported', () {
expect(SqlDatabase.supported, true);
});
});
group('functional', () {
test('unsupported throws', () {
var expectation = SqlDatabase.supported ? returnsNormally : throws;
expect(() {
window.openDatabase('test_db', '1.0', 'test_db', 1024 * 1024);
}, expectation);
});
test('Web Database', () {
// Skip if not supported.
if (!SqlDatabase.supported) {
return new Future.value();
group('Database', () {
test('Open/Transaction', () async {
if (!SqlDatabase.supported) return;
expect(tx, isNotNull, reason: "Transaction not ready");
// Should not succeed table doesn't exist to be dropped.
try {
await dropTable(tx, tableName);
expect(false, true, reason: "dropTable should fail");
} catch (error) {
expect(error.message,
"could not prepare statement (1 no such table: test_table)");
}
});
final tableName = 'test_table';
final columnName = 'test_data';
test('create', () async {
if (!SqlDatabase.supported) return;
final db = window.openDatabase('test_db', '1.0', 'test_db', 1024 * 1024);
expect(tx, isNotNull, reason: "Transaction not ready");
try {
SqlResultSet createResult =
await createTable(tx, tableName, columnName);
expect(createResult.insertId, 0);
} catch (error) {
expect(false, true, reason: "createTable failed - ${error.message}");
}
});
expect(db, isNotNull, reason: 'Unable to open database');
test('insert', () async {
if (!SqlDatabase.supported) return;
var tx;
return transaction(db).then((transaction) {
tx = transaction;
}).then((_) {
// Attempt to clear out any tables which may be lurking from previous
// runs.
return dropTable(tx, tableName, true);
}).then((_) {
return createTable(tx, tableName, columnName);
}).then((_) {
return insert(tx, tableName, columnName, 'Some text data');
}).then((_) {
return queryTable(tx, tableName);
}).then((resultSet) {
expect(resultSet.rows.length, 1);
var row = resultSet.rows.item(0);
expect(row.containsKey(columnName), isTrue);
expect(row[columnName], 'Some text data');
expect(resultSet.rows[0], row);
}).then((_) {
return dropTable(tx, tableName);
});
expect(tx, isNotNull, reason: "Transaction not ready");
try {
SqlResultSet insertResult =
await insertTable(tx, tableName, columnName, 'Some text data');
expect(insertResult.insertId, 1);
expect(insertResult.rowsAffected, 1);
} catch (error) {
expect(false, true, reason: "insert failed - ${error.message}");
}
});
test('query', () async {
if (!SqlDatabase.supported) return;
expect(tx, isNotNull, reason: "Transaction not ready");
try {
SqlResultSet queryResult = await queryTable(tx, tableName);
expect(queryResult.rows.length, 1);
expect(queryResult.rows[0]['test_data'], "Some text data");
} catch (error) {
expect(false, true, reason: "queryTable failed - ${error.message}");
}
});
test('cleanup', () async {
if (!SqlDatabase.supported) return;
expect(tx, isNotNull, reason: "Transaction not ready");
await dropTable(tx, tableName, true);
});
});
}
+95 -98
View File
@@ -5,127 +5,124 @@ import 'dart:html';
import 'dart:web_sql';
import 'package:unittest/unittest.dart';
Future<SqlTransaction> transaction(SqlDatabase db) {
final completer = new Completer<SqlTransaction>.sync();
db.transaction((SqlTransaction transaction) {
completer.complete(transaction);
}, (SqlError error) {
completer.completeError(error);
});
return completer.future;
}
import 'package:unittest/html_config.dart';
import 'package:async_helper/async_helper.dart';
Future<SqlResultSet> createTable(
SqlTransaction transaction, String tableName, String columnName) {
final completer = new Completer<SqlResultSet>.sync();
final sql = 'CREATE TABLE $tableName ($columnName)';
transaction.executeSql(sql, [], (SqlTransaction tx, SqlResultSet rs) {
completer.complete(rs);
}, (SqlTransaction tx, SqlError error) {
completer.completeError(error);
});
return completer.future;
SqlTransaction transaction, String tableName, String columnName) async {
return transaction.executeSql('CREATE TABLE $tableName ($columnName)', []);
}
Future<SqlResultSet> insert(
SqlTransaction transaction, String tableName, String columnName, value) {
final completer = new Completer<SqlResultSet>.sync();
Future<SqlResultSet> insertTable(SqlTransaction transaction, String tableName,
String columnName, value) async {
final sql = 'INSERT INTO $tableName ($columnName) VALUES (?)';
transaction.executeSql(sql, [value], (SqlTransaction tx, SqlResultSet rs) {
completer.complete(rs);
}, (SqlTransaction tx, SqlError error) {
completer.completeError(error);
});
return completer.future;
return transaction.executeSql(sql, [value]);
}
Future<SqlResultSet> queryTable(SqlTransaction transaction, String tableName) {
final completer = new Completer<SqlResultSet>.sync();
Future<SqlResultSet> queryTable(
SqlTransaction transaction, String tableName) async {
final sql = 'SELECT * FROM $tableName';
transaction.executeSql(sql, [], (SqlTransaction tx, SqlResultSet rs) {
completer.complete(rs);
}, (SqlTransaction tx, SqlError error) {
completer.completeError(error);
});
return completer.future;
return transaction.executeSql(sql, []);
}
Future<SqlResultSet> dropTable(SqlTransaction transaction, String tableName,
[bool ignoreFailure = false]) {
final completer = new Completer<SqlResultSet>.sync();
final sql = 'DROP TABLE $tableName';
transaction.executeSql(sql, [], (SqlTransaction tx, SqlResultSet rs) {
completer.complete(rs);
}, (SqlTransaction tx, SqlError error) {
if (ignoreFailure) {
completer.complete(null);
} else {
completer.completeError(error);
}
});
return completer.future;
[bool ignoreFailure = false]) async {
try {
var result = await transaction.executeSql('DROP TABLE $tableName', []);
return result;
} catch (error) {
if (!ignoreFailure) throw error;
}
}
main() {
group('supported', () {
final tableName = 'test_table';
final columnName = 'test_data';
SqlDatabase db;
SqlTransaction tx;
Future setup() async {
if (SqlDatabase.supported) {
db = await window.openDatabase('test_db', '1.0', 'test_db', 1024 * 1024);
expect(db, isNotNull, reason: 'Unable to open database');
tx = await db.transaction();
expect(tx, isNotNull, reason: "Transaction not ready");
}
}
main() async {
useHtmlConfiguration();
await setup();
group('Database', () {
test('supported', () {
expect(SqlDatabase.supported, true);
});
});
group('functional', () {
test('unsupported throws', () {
var expectation = SqlDatabase.supported ? returnsNormally : throws;
expect(() {
window.openDatabase('test_db', '1.0', 'test_db', 1024 * 1024);
}, expectation);
});
test('Web Database', () {
// Skip if not supported.
if (!SqlDatabase.supported) {
return new Future.value();
group('Database', () {
test('Open/Transaction', () async {
if (!SqlDatabase.supported) return;
expect(tx, isNotNull, reason: "Transaction not ready");
// Should not succeed table doesn't exist to be dropped.
try {
await dropTable(tx, tableName);
expect(false, true, reason: "dropTable should fail");
} catch (error) {
expect(error.message,
"could not prepare statement (1 no such table: test_table)");
}
});
final tableName = 'test_table';
final columnName = 'test_data';
test('create', () async {
if (!SqlDatabase.supported) return;
final db = window.openDatabase('test_db', '1.0', 'test_db', 1024 * 1024);
expect(tx, isNotNull, reason: "Transaction not ready");
try {
SqlResultSet createResult =
await createTable(tx, tableName, columnName);
expect(createResult.insertId, 0);
} catch (error) {
expect(false, true, reason: "createTable failed - ${error.message}");
}
});
expect(db, isNotNull, reason: 'Unable to open database');
test('insert', () async {
if (!SqlDatabase.supported) return;
var tx;
return transaction(db).then((transaction) {
tx = transaction;
}).then((_) {
// Attempt to clear out any tables which may be lurking from previous
// runs.
return dropTable(tx, tableName, true);
}).then((_) {
return createTable(tx, tableName, columnName);
}).then((_) {
return insert(tx, tableName, columnName, 'Some text data');
}).then((_) {
return queryTable(tx, tableName);
}).then((resultSet) {
expect(resultSet.rows.length, 1);
var row = resultSet.rows.item(0);
expect(row.containsKey(columnName), isTrue);
expect(row[columnName], 'Some text data');
expect(resultSet.rows[0], row);
}).then((_) {
return dropTable(tx, tableName);
});
expect(tx, isNotNull, reason: "Transaction not ready");
try {
SqlResultSet insertResult =
await insertTable(tx, tableName, columnName, 'Some text data');
expect(insertResult.insertId, 1);
expect(insertResult.rowsAffected, 1);
} catch (error) {
expect(false, true, reason: "insert failed - ${error.message}");
}
});
test('query', () async {
if (!SqlDatabase.supported) return;
expect(tx, isNotNull, reason: "Transaction not ready");
try {
SqlResultSet queryResult = await queryTable(tx, tableName);
expect(queryResult.rows.length, 1);
expect(queryResult.rows[0]['test_data'], "Some text data");
} catch (error) {
expect(false, true, reason: "queryTable failed - ${error.message}");
}
});
test('cleanup', () async {
if (!SqlDatabase.supported) return;
expect(tx, isNotNull, reason: "Transaction not ready");
await dropTable(tx, tableName, true);
});
});
}
-1
View File
@@ -29,7 +29,6 @@ html/no_linked_scripts_htmltest: Skip # Issue 29919
html/scripts_htmltest: Skip # Issue 29919
html/transferables_test: CompileTimeError # Issue 30975
html/two_scripts_htmltest: Skip # Issue 29919
html/websql_test: Pass, RuntimeError # Issue 31036
isolate/*: SkipByDesign # No support for dart:isolate in dart4web (http://dartbug.com/30538)
js/null_test: RuntimeError # Issue 30652
math/double_pow_test: RuntimeError # Issue 29922
+1 -2
View File
@@ -22037,8 +22037,7 @@
"open": {},
"openDatabase": {
"comment": "http://www.w3.org/TR/webdatabase/",
"dart_action": "experimental",
"support_level": "deprecated"
"support_level": "experimental"
},
"opener": {},
"orientation": {
+2
View File
@@ -304,6 +304,8 @@ interface Window : EventTarget {
[DartSuppress] void scroll(long x, long y);
[DartSuppress] void scroll(long x, long y, Dictionary scrollOptions);
[RaisesException] void scroll(long x, long y, optional Dictionary scrollOptions);
[DartSuppress] Database openDatabase(DOMString name, DOMString version, DOMString displayName, unsigned long estimatedSize, optional DatabaseCallback creationCallback);
};
[DartSupplemental]
+45 -11
View File
@@ -672,12 +672,51 @@ class HtmlDartGenerator(object):
callback_info = GetCallbackInfo(
self._database.GetInterface(info.callback_args[0].type_id))
# If more than one callback then the second argument is the error callback.
# Some error callbacks have 2 args (e.g., executeSql) where the second arg
# is the error - this is the argument we want.
error_callback = ""
if len(info.callback_args) > 1:
error_callback_info = GetCallbackInfo(
self._database.GetInterface(info.callback_args[1].type_id))
error_callbackNames = []
for paramInfo in error_callback_info.param_infos:
error_callbackNames.append(paramInfo.name)
errorCallbackVariables = ", ".join(error_callbackNames)
errorName = error_callback_info.param_infos[-1].name
error_callback = (',\n %s(%s) { completer.completeError(%s); }' %
(('%s : ' % info.callback_args[1].name
if info.requires_named_arguments and
info.callback_args[1].is_optional else ''),
errorCallbackVariables, errorName))
extensions = GetDDC_Extension(self._interface, info.declared_name)
if extensions:
ddc_extensions = "\n".join(extensions);
else:
ddc_extensions = ''
# Some callbacks have more than one parameters. If so use all of
# those parameters. However, if more than one argument use the
# type of the last argument to be returned e.g., executeSql the callback
# is (transaction, resultSet) and only the resultSet is returned SqlResultSet.
callbackArgsLen = len(callback_info.param_infos)
future_generic = ''
callbackVariables = ''
completerVariable = ''
if callbackArgsLen == 1:
callbackVariables = 'value'
completerVariable = callbackVariables
if callback_info.param_infos[0].type_id:
future_generic = '<%s>' % self._DartType(callback_info.param_infos[0].type_id)
elif callbackArgsLen > 1:
callbackNames = []
for paramInfo in callback_info.param_infos:
callbackNames.append(paramInfo.name)
callbackVariables = ",".join(callbackNames)
completerVariable = callbackNames[-1]
future_generic = '<%s>' % self._DartType(callback_info.param_infos[-1].type_id)
param_list = info.ParametersAsArgumentList()
metadata = ''
if '_RenamingAnnotation' in dir(self):
@@ -690,7 +729,7 @@ class HtmlDartGenerator(object):
' $ORIGINAL_FUNCTION($PARAMS_LIST\n'
' $NAMED_PARAM($VARIABLE_NAME) { '
'$DDC_EXTENSION\n'
'completer.complete($VARIABLE_NAME); }'
'completer.complete($COMPLETER_NAME); }'
'$ERROR_CALLBACK);\n'
' return completer.future;\n'
' }\n',
@@ -704,17 +743,12 @@ class HtmlDartGenerator(object):
NAMED_PARAM=('%s : ' % info.callback_args[0].name
if info.requires_named_arguments and
info.callback_args[0].is_optional else ''),
VARIABLE_NAME= '' if len(callback_info.param_infos) == 0 else 'value',
VARIABLE_NAME=callbackVariables,
COMPLETER_NAME=completerVariable,
DDC_EXTENSION=ddc_extensions,
ERROR_CALLBACK=('' if len(info.callback_args) == 1 else
(',\n %s(error) { completer.completeError(error); }' %
('%s : ' % info.callback_args[1].name
if info.requires_named_arguments and
info.callback_args[1].is_optional else ''))),
FUTURE_GENERIC = ('' if len(callback_info.param_infos) == 0 or
not callback_info.param_infos[0].type_id else
'<%s>' % self._DartType(callback_info.param_infos[0].type_id)),
ORIGINAL_FUNCTION = html_name)
ERROR_CALLBACK=error_callback,
FUTURE_GENERIC=future_generic,
ORIGINAL_FUNCTION=html_name)
def EmitHelpers(self, base_class):
if not self._members_emitter:
+13
View File
@@ -173,6 +173,7 @@ for interface in _removed_html_interfaces:
convert_to_future_members = monitored.Set(
'htmlrenamer.converted_to_future_members', [
'Database.transaction',
'DataTransferItem.getAsString',
'DirectoryEntry.getDirectory',
'DirectoryEntry.getFile',
@@ -190,6 +191,7 @@ convert_to_future_members = monitored.Set(
'Notification.requestPermission',
'RTCPeerConnection.setLocalDescription',
'RTCPeerConnection.setRemoteDescription',
'SQLTransaction.executeSql',
'StorageInfo.requestQuota',
'StorageQuota.requestQuota',
'Window.webkitRequestFileSystem',
@@ -213,6 +215,17 @@ ddc_extensions = monitored.Dict('ddcextensions.ddc_extensions', {
'applyExtension(\'Blob\', value);'
]
},
'Database': {
'transaction': [
'applyExtension(\'SQLTransaction\', value);'
],
},
'SQLTransaction': {
'executeSql': [
'applyExtension(\'SQLResultSet\', resultSet);'
'applyExtension(\'SQLResultSetRowList\', resultSet.rows);'
],
},
'Window': {
'webkitRequestFileSystem':[
'applyExtension(\'DOMFileSystem\', value);',
@@ -19,8 +19,8 @@ import 'dart:collection' hide LinkedList, LinkedListEntry;
import 'dart:_internal' show FixedLengthListMixin;
import 'dart:html';
import 'dart:html_common';
import 'dart:_js_helper' show convertDartClosureToJS, Creates, JSName, Native,
JavaScriptIndexingBehavior;
import 'dart:_js_helper' show applyExtension, convertDartClosureToJS, Creates,
JSName, Native, JavaScriptIndexingBehavior;
import 'dart:_foreign_helper' show JS;
import 'dart:_interceptors' show Interceptor;
@@ -227,6 +227,28 @@ $!MEMBERS
_moveTo(p.x, p.y);
}
@JSName('openDatabase')
@DomName('Window.openDatabase')
SqlDatabase _openDatabase(
String name, String version, String displayName, int estimatedSize,
[DatabaseCallback creationCallback]) native;
@JSName('openDatabase')
@DomName('Window.openDatabase')
@DocsEditable()
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.SAFARI)
@Experimental()
@Creates('SqlDatabase')
SqlDatabase openDatabase(
String name, String version, String displayName, int estimatedSize) {
var db = _openDatabase(name, version, displayName, estimatedSize);
applyExtension('Database', db);
return db;
}
@DomName('Window.pageXOffset')
@DocsEditable()
int get pageXOffset => JS('num', '#.pageXOffset', this).round();