Title: [108867] trunk
Revision
108867
Author
jsb...@chromium.org
Date
2012-02-24 16:26:48 -0800 (Fri, 24 Feb 2012)

Log Message

[V8] IndexedDB: IDBTransaction objects leak in Workers
https://bugs.webkit.org/show_bug.cgi?id=79308

Source/WebCore:

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):

LayoutTests:

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.

Modified Paths

Added Paths

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;
     };
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to