Diff
Modified: trunk/LayoutTests/ChangeLog (108866 => 108867)
--- trunk/LayoutTests/ChangeLog 2012-02-25 00:23:38 UTC (rev 108866)
+++ trunk/LayoutTests/ChangeLog 2012-02-25 00:26:48 UTC (rev 108867)
@@ -1,3 +1,16 @@
+2012-02-24 Joshua Bell <jsb...@chromium.org>
+
+ [V8] IndexedDB: IDBTransaction objects leak in Workers
+ https://bugs.webkit.org/show_bug.cgi?id=79308
+
+ Reviewed by Tony Chang.
+
+ * fast/js/resources/js-test-pre.js:
+ (startWorker): Return Worker object so onerror can be hooked.
+ * storage/indexeddb/resources/transaction-abort-workers.js: Added.
+ * storage/indexeddb/transaction-abort-workers-expected.txt: Added.
+ * storage/indexeddb/transaction-abort-workers.html: Added.
+
2012-02-24 Adrienne Walker <e...@google.com>
[chromium] Unreviewed, mark fast/files/workers tests as flaky crashers
Modified: trunk/LayoutTests/fast/js/resources/js-test-pre.js (108866 => 108867)
--- trunk/LayoutTests/fast/js/resources/js-test-pre.js 2012-02-25 00:23:38 UTC (rev 108866)
+++ trunk/LayoutTests/fast/js/resources/js-test-pre.js 2012-02-25 00:26:48 UTC (rev 108867)
@@ -498,6 +498,8 @@
debug('Got error from worker: ' + event.message);
finishJSTest();
}
+
+ return worker;
}
if (isWorker()) {
Added: trunk/LayoutTests/storage/indexeddb/resources/transaction-abort-workers.js (0 => 108867)
--- trunk/LayoutTests/storage/indexeddb/resources/transaction-abort-workers.js (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/resources/transaction-abort-workers.js 2012-02-25 00:26:48 UTC (rev 108867)
@@ -0,0 +1,132 @@
+if (this.importScripts) {
+ importScripts('../../../fast/js/resources/js-test-pre.js');
+ importScripts('shared.js');
+}
+
+description("Test IndexedDB workers, recursion, and transaction termination.");
+
+function test()
+{
+ shouldBeTrue("'webkitIndexedDB' in self");
+ shouldBeFalse("webkitIndexedDB == null");
+
+ shouldBeTrue("'webkitIDBCursor' in self");
+ shouldBeFalse("webkitIDBCursor == null");
+
+ evalAndLog("request = webkitIndexedDB.open('transaction-abort-worker')");
+ request._onerror_ = unexpectedErrorCallback;
+ request._onsuccess_ = function () {
+ evalAndLog("db = request.result");
+ evalAndLog("request = db.setVersion('1')");
+ request._onerror_ = unexpectedErrorCallback;
+ request._onblocked_ = unexpectedBlockedCallback;
+ request._onsuccess_ = function () {
+ deleteAllObjectStores(db);
+ evalAndLog("trans = request.result");
+ evalAndLog("db.createObjectStore('store')");
+ trans._oncomplete_ = createTransaction;
+ };
+ };
+}
+
+function createTransaction()
+{
+ debug("");
+ debug("createTransaction():");
+ evalAndLog("transaction = db.transaction('store')");
+ evalAndLog("store = transaction.objectStore('store')");
+ transaction._oncomplete_ = unexpectedCompleteCallback;
+ transaction._onerror_ = unexpectedErrorCallback;
+ transaction._onabort_ = transactionAborted;
+}
+
+function transactionAborted()
+{
+ testPassed("Transaction aborted");
+ evalAndExpectException("store.get(0)", "webkitIDBDatabaseException.TRANSACTION_INACTIVE_ERR");
+ recursionTest();
+}
+
+function recursionTest()
+{
+ debug("");
+ debug("recursionTest():");
+ evalAndLog("transaction = db.transaction('store')");
+ evalAndLog("store = transaction.objectStore('store')");
+ transaction._oncomplete_ = transactionCompleted;
+ transaction._onabort_ = unexpectedAbortCallback;
+ evalAndLog("store.get(0)");
+ testPassed("transaction is active");
+ recurse(1);
+}
+
+function recurse(count)
+{
+ debug("recursion depth: " + count);
+ evalAndLog("store.get(0)");
+ testPassed("transaction is still active");
+ if (count < 3) {
+ recurse(count + 1);
+ }
+ debug("recursion depth: " + count);
+ evalAndLog("store.get(0)");
+ testPassed("transaction is still active");
+}
+
+function transactionCompleted()
+{
+ testPassed("transaction completed");
+ evalAndExpectException("store.get(0)", "webkitIDBDatabaseException.TRANSACTION_INACTIVE_ERR");
+
+ debug("");
+ debug("trying a timeout callback:");
+ evalAndLog("setTimeout(timeoutTest, 0)");
+}
+
+function timeoutTest()
+{
+ debug("");
+ debug("timeoutTest():");
+
+ evalAndLog("transaction = db.transaction('store')");
+ evalAndLog("store = transaction.objectStore('store')");
+ transaction._oncomplete_ = unexpectedCompleteCallback;
+ transaction._onabort_ = function () {
+ testPassed("transaction started in setTimeout() callback aborted");
+ evalAndExpectException("store.get(0)", "webkitIDBDatabaseException.TRANSACTION_INACTIVE_ERR");
+
+ errorTest();
+ };
+}
+
+function errorTest()
+{
+ debug("");
+ debug("errorTest():");
+ evalAndLog("self.old_onerror = self.onerror");
+ evalAndLog("self._onerror_ = errorHandler");
+ throw new Error("ignore this");
+}
+
+function errorHandler(e)
+{
+ debug("");
+ debug("errorHandler():");
+ // FIXME: Should be able to stop the error here, but it isn't an Event object.
+ // evalAndLog("event.preventDefault()");
+ evalAndLog("self._onerror_ = self.old_onerror");
+ evalAndLog("transaction = db.transaction('store')");
+ evalAndLog("store = transaction.objectStore('store')");
+ transaction._oncomplete_ = unexpectedCompleteCallback;
+ transaction._onerror_ = unexpectedErrorCallback;
+ transaction._onabort_ = errorTransactionAborted;
+}
+
+function errorTransactionAborted()
+{
+ testPassed("Transaction aborted");
+ evalAndExpectException("store.get(0)", "webkitIDBDatabaseException.TRANSACTION_INACTIVE_ERR");
+ finishJSTest();
+}
+
+test();
Added: trunk/LayoutTests/storage/indexeddb/transaction-abort-workers-expected.txt (0 => 108867)
--- trunk/LayoutTests/storage/indexeddb/transaction-abort-workers-expected.txt (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/transaction-abort-workers-expected.txt 2012-02-25 00:26:48 UTC (rev 108867)
@@ -0,0 +1,82 @@
+[Worker] Test IndexedDB workers, recursion, and transaction termination.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Starting worker: resources/transaction-abort-workers.js
+PASS [Worker] 'webkitIndexedDB' in self is true
+PASS [Worker] webkitIndexedDB == null is false
+PASS [Worker] 'webkitIDBCursor' in self is true
+PASS [Worker] webkitIDBCursor == null is false
+[Worker] request = webkitIndexedDB.open('transaction-abort-worker')
+[Worker] db = request.result
+[Worker] request = db.setVersion('1')
+[Worker] Deleted all object stores.
+[Worker] trans = request.result
+[Worker] db.createObjectStore('store')
+[Worker]
+[Worker] createTransaction():
+[Worker] transaction = db.transaction('store')
+[Worker] store = transaction.objectStore('store')
+PASS [Worker] Transaction aborted
+[Worker] Expecting exception from store.get(0)
+PASS [Worker] Exception was thrown.
+PASS [Worker] code is webkitIDBDatabaseException.TRANSACTION_INACTIVE_ERR
+[Worker]
+[Worker] recursionTest():
+[Worker] transaction = db.transaction('store')
+[Worker] store = transaction.objectStore('store')
+[Worker] store.get(0)
+PASS [Worker] transaction is active
+[Worker] recursion depth: 1
+[Worker] store.get(0)
+PASS [Worker] transaction is still active
+[Worker] recursion depth: 2
+[Worker] store.get(0)
+PASS [Worker] transaction is still active
+[Worker] recursion depth: 3
+[Worker] store.get(0)
+PASS [Worker] transaction is still active
+[Worker] recursion depth: 3
+[Worker] store.get(0)
+PASS [Worker] transaction is still active
+[Worker] recursion depth: 2
+[Worker] store.get(0)
+PASS [Worker] transaction is still active
+[Worker] recursion depth: 1
+[Worker] store.get(0)
+PASS [Worker] transaction is still active
+PASS [Worker] transaction completed
+[Worker] Expecting exception from store.get(0)
+PASS [Worker] Exception was thrown.
+PASS [Worker] code is webkitIDBDatabaseException.TRANSACTION_INACTIVE_ERR
+[Worker]
+[Worker] trying a timeout callback:
+[Worker] setTimeout(timeoutTest, 0)
+[Worker]
+[Worker] timeoutTest():
+[Worker] transaction = db.transaction('store')
+[Worker] store = transaction.objectStore('store')
+PASS [Worker] transaction started in setTimeout() callback aborted
+[Worker] Expecting exception from store.get(0)
+PASS [Worker] Exception was thrown.
+PASS [Worker] code is webkitIDBDatabaseException.TRANSACTION_INACTIVE_ERR
+[Worker]
+[Worker] errorTest():
+[Worker] self.old_onerror = self.onerror
+[Worker] self._onerror_ = errorHandler
+[Worker]
+[Worker] errorHandler():
+[Worker] self._onerror_ = self.old_onerror
+[Worker] transaction = db.transaction('store')
+[Worker] store = transaction.objectStore('store')
+Got expected error from worker, ignoring
+event.preventDefault()
+PASS [Worker] Transaction aborted
+[Worker] Expecting exception from store.get(0)
+PASS [Worker] Exception was thrown.
+PASS [Worker] code is webkitIDBDatabaseException.TRANSACTION_INACTIVE_ERR
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/storage/indexeddb/transaction-abort-workers.html (0 => 108867)
--- trunk/LayoutTests/storage/indexeddb/transaction-abort-workers.html (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/transaction-abort-workers.html 2012-02-25 00:26:48 UTC (rev 108867)
@@ -0,0 +1,31 @@
+<html>
+<head>
+<script src=""
+<script src=""
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+var worker = startWorker('resources/transaction-abort-workers.js');
+
+// FIXME: It should be possible for the worker to set self.onerror to catch the event
+// and call event.preventDefault(), but in the current Worker implementation the raw
+// exception is seen by the event handler in the worker, not an ErrorEvent object.
+
+var orig_onerror = worker.onerror;
+worker._onerror_ = function (event) {
+ if (event.message === "Uncaught Error: ignore this") {
+ debug("Got expected error from worker, ignoring");
+ evalAndLog("event.preventDefault()");
+ } else if (orig_onerror) {
+ orig_onerror(event);
+ }
+};
+
+
+</script>
+<script src=""
+</body>
+</html>
Modified: trunk/Source/WebCore/ChangeLog (108866 => 108867)
--- trunk/Source/WebCore/ChangeLog 2012-02-25 00:23:38 UTC (rev 108866)
+++ trunk/Source/WebCore/ChangeLog 2012-02-25 00:26:48 UTC (rev 108867)
@@ -1,3 +1,27 @@
+2012-02-24 Joshua Bell <jsb...@chromium.org>
+
+ [V8] IndexedDB: IDBTransaction objects leak in Workers
+ https://bugs.webkit.org/show_bug.cgi?id=79308
+
+ Use a V8RecursionScope whenever Workers call into script, so that
+ post-script side-effects can be executed.
+
+ Reviewed by Tony Chang.
+
+ Test: storage/indexeddb/transaction-abort-workers.html
+
+ * bindings/v8/ScheduledAction.cpp:
+ (WebCore::ScheduledAction::execute):
+ * bindings/v8/V8WorkerContextErrorHandler.cpp:
+ (WebCore::V8WorkerContextErrorHandler::callListenerFunction):
+ * bindings/v8/V8WorkerContextEventListener.cpp:
+ (WebCore::V8WorkerContextEventListener::callListenerFunction):
+ * bindings/v8/WorkerContextExecutionProxy.cpp: Replace custom recursion tracking.
+ (WebCore::WorkerContextExecutionProxy::WorkerContextExecutionProxy):
+ (WebCore::WorkerContextExecutionProxy::runScript):
+ * bindings/v8/WorkerContextExecutionProxy.h:
+ (WorkerContextExecutionProxy):
+
2012-02-24 Gregg Tavares <g...@google.com>
Limit WebGL Errors in Console to 10 per context
Modified: trunk/Source/WebCore/bindings/v8/ScheduledAction.cpp (108866 => 108867)
--- trunk/Source/WebCore/bindings/v8/ScheduledAction.cpp 2012-02-25 00:23:38 UTC (rev 108866)
+++ trunk/Source/WebCore/bindings/v8/ScheduledAction.cpp 2012-02-25 00:26:48 UTC (rev 108867)
@@ -41,6 +41,7 @@
#include "V8Binding.h"
#include "V8Proxy.h"
+#include "V8RecursionScope.h"
#include "WorkerContext.h"
#include "WorkerContextExecutionProxy.h"
#include "WorkerThread.h"
@@ -141,7 +142,8 @@
{
// In a Worker, the execution should always happen on a worker thread.
ASSERT(workerContext->thread()->threadID() == currentThread());
-
+
+ V8RecursionScope recursionScope(workerContext);
WorkerScriptController* scriptController = workerContext->script();
if (!m_function.IsEmpty() && m_function->IsFunction()) {
Modified: trunk/Source/WebCore/bindings/v8/V8WorkerContextErrorHandler.cpp (108866 => 108867)
--- trunk/Source/WebCore/bindings/v8/V8WorkerContextErrorHandler.cpp 2012-02-25 00:23:38 UTC (rev 108866)
+++ trunk/Source/WebCore/bindings/v8/V8WorkerContextErrorHandler.cpp 2012-02-25 00:26:48 UTC (rev 108867)
@@ -37,6 +37,7 @@
#include "EventNames.h"
#include "ErrorEvent.h"
#include "V8Binding.h"
+#include "V8RecursionScope.h"
namespace WebCore {
@@ -55,6 +56,7 @@
v8::Local<v8::Function> callFunction = v8::Local<v8::Function>::Cast(listener);
v8::Local<v8::Object> thisValue = v8::Context::GetCurrent()->Global();
v8::Handle<v8::Value> parameters[3] = { v8String(errorEvent->message()), v8String(errorEvent->filename()), v8::Integer::New(errorEvent->lineno()) };
+ V8RecursionScope recursionScope(context);
returnValue = callFunction->Call(thisValue, 3, parameters);
}
return returnValue;
Modified: trunk/Source/WebCore/bindings/v8/V8WorkerContextEventListener.cpp (108866 => 108867)
--- trunk/Source/WebCore/bindings/v8/V8WorkerContextEventListener.cpp 2012-02-25 00:23:38 UTC (rev 108866)
+++ trunk/Source/WebCore/bindings/v8/V8WorkerContextEventListener.cpp 2012-02-25 00:26:48 UTC (rev 108867)
@@ -37,6 +37,7 @@
#include "V8Binding.h"
#include "V8DOMWrapper.h"
#include "V8Event.h"
+#include "V8RecursionScope.h"
#include "WorkerContext.h"
#include "WorkerContextExecutionProxy.h"
@@ -90,6 +91,7 @@
return v8::Local<v8::Value>();
v8::Handle<v8::Value> parameters[1] = { jsEvent };
+ V8RecursionScope recursionScope(context);
v8::Local<v8::Value> result = handlerFunction->Call(receiver, 1, parameters);
if (WorkerContextExecutionProxy* proxy = workerProxy(context))
Modified: trunk/Source/WebCore/bindings/v8/WorkerContextExecutionProxy.cpp (108866 => 108867)
--- trunk/Source/WebCore/bindings/v8/WorkerContextExecutionProxy.cpp 2012-02-25 00:23:38 UTC (rev 108866)
+++ trunk/Source/WebCore/bindings/v8/WorkerContextExecutionProxy.cpp 2012-02-25 00:26:48 UTC (rev 108867)
@@ -44,6 +44,7 @@
#include "V8DOMMap.h"
#include "V8DedicatedWorkerContext.h"
#include "V8Proxy.h"
+#include "V8RecursionScope.h"
#include "V8SharedWorkerContext.h"
#include "Worker.h"
#include "WorkerContext.h"
@@ -81,7 +82,6 @@
WorkerContextExecutionProxy::WorkerContextExecutionProxy(WorkerContext* workerContext)
: m_workerContext(workerContext)
- , m_recursion(0)
{
initIsolate();
}
@@ -234,7 +234,7 @@
return v8::Local<v8::Value>();
// Compute the source string and prevent against infinite recursion.
- if (m_recursion >= kMaxRecursionDepth) {
+ if (V8RecursionScope::recursionLevel() >= kMaxRecursionDepth) {
v8::Local<v8::String> code = v8ExternalString("throw RangeError('Recursion too deep')");
script = V8Proxy::compileScript(code, "", TextPosition::minimumPosition());
}
@@ -248,9 +248,8 @@
// Run the script and keep track of the current recursion depth.
v8::Local<v8::Value> result;
{
- m_recursion++;
+ V8RecursionScope recursionScope(m_workerContext);
result = script->Run();
- m_recursion--;
}
// Handle V8 internal error situation (Out-of-memory).
Modified: trunk/Source/WebCore/bindings/v8/WorkerContextExecutionProxy.h (108866 => 108867)
--- trunk/Source/WebCore/bindings/v8/WorkerContextExecutionProxy.h 2012-02-25 00:23:38 UTC (rev 108866)
+++ trunk/Source/WebCore/bindings/v8/WorkerContextExecutionProxy.h 2012-02-25 00:26:48 UTC (rev 108867)
@@ -86,7 +86,6 @@
WorkerContext* m_workerContext;
v8::Persistent<v8::Context> m_context;
- int m_recursion;
Vector<Event*> m_events;
};