Reviewed by Darin Adler.
authorap@webkit.org <ap@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 13 Nov 2008 07:27:12 +0000 (07:27 +0000)
committerap@webkit.org <ap@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 13 Nov 2008 07:27:12 +0000 (07:27 +0000)
        https://bugs.webkit.org/show_bug.cgi?id=22203
        Implement Worker messaging

        No test cases included, because this functionality is disabled by default.

        The implementation is known to still have many race condition, but works quite well for
        testing.

        * WebCore.xcodeproj/project.pbxproj: Added WorkerTask.{h,cpp}.

        * bindings/js/JSDOMBinding.cpp:
        (WebCore::markActiveObjectsForContext): Re-worded comments a little.
        (WebCore::markCrossHeapDependentObjectsForContext): Existing cross-heap GC protocol was
        incorrect, changed it to a much simpler (but still incorrect) version.

        * dom/WorkerTask.cpp: Added.
        (WebCore::WorkerTask::~WorkerTask):
        * dom/WorkerTask.h: Added.
        Tasks posted to workers implement this new interface.

        * bindings/js/JSDedicatedWorkerCustom.cpp:
        (WebCore::JSDedicatedWorker::mark):
        (WebCore::JSDedicatedWorker::connect):
        * dom/DedicatedWorker.h:
        * dom/DedicatedWorker.idl:
        Auto-generate event listener attributes. Renamed startConversation() to connect(), tracking
        WHATWG discussions.

        * dom/DedicatedWorker.cpp:
        (WebCore::WorkerConnectTask::WorkerConnectTask): A task that performs worker-side connect()
        operations.
        (WebCore::DedicatedWorker::DedicatedWorker): Initialize WorkerThread pointer.
        (WebCore::DedicatedWorker::connect): Connect() creates a pair of entangled ports, and posts
        one to worker. Since message port registration in ScriptExecutionContext is not thread safe,
        this port starts with a null context pointer.
        (WebCore::DedicatedWorker::notifyFinished): Since Worker methods should work immediately
        after creation, we have to queue tasks until after a WorkerThread object is created. Then we
        forward all queued tasks to its queue.

        * dom/EventTarget.cpp: (WebCore::EventTarget::toWorkerContext):
        * dom/EventTarget.h:
        * bindings/js/JSEventTarget.cpp: (WebCore::toJS):
        Added cases for WorkerContext, which is now an EventTarget, too.

        * bindings/js/JSWorkerContext.h: Added JSWorkerContext::put() to make onconnect settable.
        * bindings/js/JSWorkerContext.cpp:
        (WebCore::JSWorkerContext::mark): Mark event listeners.
        (WebCore::JSWorkerContext::createPrototype): Fixed a typo, use the right StructureID.
        (WebCore::JSWorkerContext::put): Implemented.
        (WebCore::jsWorkerContextPrototypeFunctionAddEventListener): Added an EventTarget implementation.
        (WebCore::jsWorkerContextPrototypeFunctionRemoveEventListener): Ditto.
        (WebCore::jsWorkerContextPrototypeFunctionDispatchEvent): Ditto.
        (WebCore::jsWorkerContextOnconnect): Added.
        (WebCore::setJSWorkerContextOnconnect): Added.

        * bindings/js/WorkerScriptController.cpp: (WebCore::WorkerScriptController::evaluate):
        Made it actually work by adding necessary setup.

        * bindings/js/WorkerScriptController.h: (WebCore::WorkerScriptController::initScriptIfNeeded):
        Check the right variable - it is the wrapper that may not be initialized yet.

        * dom/ActiveDOMObject.cpp:
        (WebCore::ActiveDOMObject::ActiveDOMObject):
        (WebCore::ActiveDOMObject::~ActiveDOMObject):
        Assert being called from the correct thread, as active DOM object tracking is not thread safe.

        * dom/ScriptExecutionContext.h: Added a Task interface and a postTask() method, to be used
        for asynchronously executing tasks in context's thread.

        * dom/ScriptExecutionContext.cpp:
        (WebCore::ProcessMessagesSoonTask): Changed from a Timer to a Task
        (WebCore::ScriptExecutionContext::ScriptExecutionContext): Removed m_firedMessagePortTimer.
        It was an optimization that couldn't be easily preserved without introducing race conditions
        in multithreading case.
        (WebCore::ScriptExecutionContext::processMessagePortMessagesSoon): Use postTask().
        (WebCore::ScriptExecutionContext::dispatchMessagePortEvents): Added a comment explaining
        why it's OK to not ref() ports in a frozen copy.
        (WebCore::ScriptExecutionContext::createdMessagePort): Assert that we're not being called
        from a wrong thread.
        (WebCore::ScriptExecutionContext::destroyedMessagePort): Ditto.
        (WebCore::ScriptExecutionContextTaskTimer): Part of
        ScriptExecutionContext::Task implementation - use Timer if posting from main thread to main
        thread.
        (WebCore::ScriptExecutionContextTaskWorkerTask): Another part - use WorkerTask if posting
        to a worker.
        (WebCore::PerformTaskContext::PerformTaskContext): Finally, use callOnMainThread() if posting
        to main thread from a secondary one.
        (WebCore::performTask): A helper function for callOnMainThread().
        (WebCore::ScriptExecutionContext::postTask): Use one of the above implementations.

        * dom/MessagePort.h: Fixed message queue to keep EventData pointers - otherwise, we would
        ref/deref EventData::message from different threads, which is not allowed.

        * dom/MessagePort.cpp:
        (WebCore::MessagePortCloseEventTask): Use a task instead of a timer to work across threads.
        (WebCore::MessagePort::EventData::create): Updated for EventData being refcountable now.
        (WebCore::MessagePort::EventData::EventData): Ditto.
        (WebCore::MessagePort::MessagePort): ScriptExecutionContext is now allowed to be null at
        first, because we need to create ports for posting to other threads, and it is not possible
        to register in a context from another thread.
        (WebCore::MessagePort::clone): Always create ports with null contexts - it is now message
        receiver's job to set the context.
        (WebCore::MessagePort::postMessage): Enable posting to ports that are not attached to any
        context yet.
        (WebCore::MessagePort::startConversation): Ditto. Data port is always posted unattached.
        (WebCore::MessagePort::contextDestroyed): Assert that we had a context.
        (WebCore::MessagePort::attachToContext): Called when receiving a data port to register in
        context.
        (WebCore::MessagePort::scriptExecutionContext): Moved from header, as the function is virtual.
        (WebCore::MessagePort::dispatchMessages): Attach data port to receiving context. Use postTask().
        (WebCore::MessagePort::queueCloseEvent): Use postTask().
        (WebCore::MessagePort::hasPendingActivity): Reworded comment a little. As mentioned above,
        MessagePort cross-heap GC is still quite wrong.

        * dom/WorkerContext.h: Made WorkerContext an event target, added onconnect attribute.
        * dom/WorkerContext.cpp: Keep a pointer to WorkerThread. It is only used for debug assertions
        now, but there is no harm in tracking it in release builds, too.

        * dom/WorkerThread.cpp:
        (WebCore::WorkerThread::create): WorkerThread is refcountable, construct with create().
        (WebCore::WorkerThread::workerThread): Implemented a message loop.
        * dom/WorkerThread.h:
        (WebCore::WorkerThread::threadID): Also only used for assertions.
        (WebCore::WorkerThread::messageQueue): Return a reference to queue, so clients can post to it.

        * page/DOMWindow.cpp:
        (WebCore::DOMWindow::postMessage): MessagePort::clone() no longer takes a context, as it
        always sets it to null.
        (WebCore::DOMWindow::postMessageTimerFired): Attach data port to receiving context.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@38365 268f45cc-cd09-0410-ab3c-d52691b4dbfc

26 files changed:
WebCore/ChangeLog
WebCore/WebCore.xcodeproj/project.pbxproj
WebCore/bindings/js/JSDOMBinding.cpp
WebCore/bindings/js/JSDedicatedWorkerCustom.cpp
WebCore/bindings/js/JSEventTarget.cpp
WebCore/bindings/js/JSWorkerContext.cpp
WebCore/bindings/js/JSWorkerContext.h
WebCore/bindings/js/WorkerScriptController.cpp
WebCore/bindings/js/WorkerScriptController.h
WebCore/dom/ActiveDOMObject.cpp
WebCore/dom/DedicatedWorker.cpp
WebCore/dom/DedicatedWorker.h
WebCore/dom/DedicatedWorker.idl
WebCore/dom/EventTarget.cpp
WebCore/dom/EventTarget.h
WebCore/dom/MessagePort.cpp
WebCore/dom/MessagePort.h
WebCore/dom/ScriptExecutionContext.cpp
WebCore/dom/ScriptExecutionContext.h
WebCore/dom/WorkerContext.cpp
WebCore/dom/WorkerContext.h
WebCore/dom/WorkerTask.cpp [new file with mode: 0644]
WebCore/dom/WorkerTask.h [new file with mode: 0644]
WebCore/dom/WorkerThread.cpp
WebCore/dom/WorkerThread.h
WebCore/page/DOMWindow.cpp

index 4b240d6..cd5953a 100644 (file)
@@ -1,3 +1,137 @@
+2008-11-12  Alexey Proskuryakov  <ap@webkit.org>
+
+        Reviewed by Darin Adler.
+
+        https://bugs.webkit.org/show_bug.cgi?id=22203
+        Implement Worker messaging
+
+        No test cases included, because this functionality is disabled by default.
+
+        The implementation is known to still have many race condition, but works quite well for
+        testing.
+
+        * WebCore.xcodeproj/project.pbxproj: Added WorkerTask.{h,cpp}.
+
+        * bindings/js/JSDOMBinding.cpp:
+        (WebCore::markActiveObjectsForContext): Re-worded comments a little.
+        (WebCore::markCrossHeapDependentObjectsForContext): Existing cross-heap GC protocol was
+        incorrect, changed it to a much simpler (but still incorrect) version.
+
+        * dom/WorkerTask.cpp: Added.
+        (WebCore::WorkerTask::~WorkerTask):
+        * dom/WorkerTask.h: Added.
+        Tasks posted to workers implement this new interface.
+
+        * bindings/js/JSDedicatedWorkerCustom.cpp:
+        (WebCore::JSDedicatedWorker::mark):
+        (WebCore::JSDedicatedWorker::connect):
+        * dom/DedicatedWorker.h:
+        * dom/DedicatedWorker.idl:
+        Auto-generate event listener attributes. Renamed startConversation() to connect(), tracking
+        WHATWG discussions.
+
+        * dom/DedicatedWorker.cpp:
+        (WebCore::WorkerConnectTask::WorkerConnectTask): A task that performs worker-side connect()
+        operations.
+        (WebCore::DedicatedWorker::DedicatedWorker): Initialize WorkerThread pointer.
+        (WebCore::DedicatedWorker::connect): Connect() creates a pair of entangled ports, and posts
+        one to worker. Since message port registration in ScriptExecutionContext is not thread safe,
+        this port starts with a null context pointer.
+        (WebCore::DedicatedWorker::notifyFinished): Since Worker methods should work immediately
+        after creation, we have to queue tasks until after a WorkerThread object is created. Then we
+        forward all queued tasks to its queue.
+
+        * dom/EventTarget.cpp: (WebCore::EventTarget::toWorkerContext):
+        * dom/EventTarget.h:
+        * bindings/js/JSEventTarget.cpp: (WebCore::toJS):
+        Added cases for WorkerContext, which is now an EventTarget, too.
+
+        * bindings/js/JSWorkerContext.h: Added JSWorkerContext::put() to make onconnect settable.
+        * bindings/js/JSWorkerContext.cpp:
+        (WebCore::JSWorkerContext::mark): Mark event listeners.
+        (WebCore::JSWorkerContext::createPrototype): Fixed a typo, use the right StructureID.
+        (WebCore::JSWorkerContext::put): Implemented.
+        (WebCore::jsWorkerContextPrototypeFunctionAddEventListener): Added an EventTarget implementation.
+        (WebCore::jsWorkerContextPrototypeFunctionRemoveEventListener): Ditto.
+        (WebCore::jsWorkerContextPrototypeFunctionDispatchEvent): Ditto.
+        (WebCore::jsWorkerContextOnconnect): Added.
+        (WebCore::setJSWorkerContextOnconnect): Added.
+
+        * bindings/js/WorkerScriptController.cpp: (WebCore::WorkerScriptController::evaluate):
+        Made it actually work by adding necessary setup.
+
+        * bindings/js/WorkerScriptController.h: (WebCore::WorkerScriptController::initScriptIfNeeded):
+        Check the right variable - it is the wrapper that may not be initialized yet.
+
+        * dom/ActiveDOMObject.cpp:
+        (WebCore::ActiveDOMObject::ActiveDOMObject):
+        (WebCore::ActiveDOMObject::~ActiveDOMObject):
+        Assert being called from the correct thread, as active DOM object tracking is not thread safe.
+
+        * dom/ScriptExecutionContext.h: Added a Task interface and a postTask() method, to be used
+        for asynchronously executing tasks in context's thread.
+
+        * dom/ScriptExecutionContext.cpp:
+        (WebCore::ProcessMessagesSoonTask): Changed from a Timer to a Task
+        (WebCore::ScriptExecutionContext::ScriptExecutionContext): Removed m_firedMessagePortTimer.
+        It was an optimization that couldn't be easily preserved without introducing race conditions
+        in multithreading case.
+        (WebCore::ScriptExecutionContext::processMessagePortMessagesSoon): Use postTask().
+        (WebCore::ScriptExecutionContext::dispatchMessagePortEvents): Added a comment explaining
+        why it's OK to not ref() ports in a frozen copy.
+        (WebCore::ScriptExecutionContext::createdMessagePort): Assert that we're not being called
+        from a wrong thread.
+        (WebCore::ScriptExecutionContext::destroyedMessagePort): Ditto.
+        (WebCore::ScriptExecutionContextTaskTimer): Part of
+        ScriptExecutionContext::Task implementation - use Timer if posting from main thread to main
+        thread.
+        (WebCore::ScriptExecutionContextTaskWorkerTask): Another part - use WorkerTask if posting
+        to a worker.
+        (WebCore::PerformTaskContext::PerformTaskContext): Finally, use callOnMainThread() if posting
+        to main thread from a secondary one.
+        (WebCore::performTask): A helper function for callOnMainThread().
+        (WebCore::ScriptExecutionContext::postTask): Use one of the above implementations.
+
+        * dom/MessagePort.h: Fixed message queue to keep EventData pointers - otherwise, we would
+        ref/deref EventData::message from different threads, which is not allowed.
+
+        * dom/MessagePort.cpp:
+        (WebCore::MessagePortCloseEventTask): Use a task instead of a timer to work across threads.
+        (WebCore::MessagePort::EventData::create): Updated for EventData being refcountable now.
+        (WebCore::MessagePort::EventData::EventData): Ditto.
+        (WebCore::MessagePort::MessagePort): ScriptExecutionContext is now allowed to be null at
+        first, because we need to create ports for posting to other threads, and it is not possible
+        to register in a context from another thread.
+        (WebCore::MessagePort::clone): Always create ports with null contexts - it is now message
+        receiver's job to set the context.
+        (WebCore::MessagePort::postMessage): Enable posting to ports that are not attached to any
+        context yet.
+        (WebCore::MessagePort::startConversation): Ditto. Data port is always posted unattached.
+        (WebCore::MessagePort::contextDestroyed): Assert that we had a context.
+        (WebCore::MessagePort::attachToContext): Called when receiving a data port to register in
+        context.
+        (WebCore::MessagePort::scriptExecutionContext): Moved from header, as the function is virtual.
+        (WebCore::MessagePort::dispatchMessages): Attach data port to receiving context. Use postTask().
+        (WebCore::MessagePort::queueCloseEvent): Use postTask().
+        (WebCore::MessagePort::hasPendingActivity): Reworded comment a little. As mentioned above,
+        MessagePort cross-heap GC is still quite wrong.
+
+        * dom/WorkerContext.h: Made WorkerContext an event target, added onconnect attribute.
+        * dom/WorkerContext.cpp: Keep a pointer to WorkerThread. It is only used for debug assertions
+        now, but there is no harm in tracking it in release builds, too.
+
+        * dom/WorkerThread.cpp:
+        (WebCore::WorkerThread::create): WorkerThread is refcountable, construct with create().
+        (WebCore::WorkerThread::workerThread): Implemented a message loop.
+        * dom/WorkerThread.h:
+        (WebCore::WorkerThread::threadID): Also only used for assertions.
+        (WebCore::WorkerThread::messageQueue): Return a reference to queue, so clients can post to it.
+
+        * page/DOMWindow.cpp:
+        (WebCore::DOMWindow::postMessage): MessagePort::clone() no longer takes a context, as it
+        always sets it to null.
+        (WebCore::DOMWindow::postMessageTimerFired): Attach data port to receiving context.
+
 2008-11-12  Dirk Schulze  <vbs85@gmx.de>
 
         Reviewed Darin Adler.
index 76837c9..c46ca5a 100644 (file)
                E107400E0E77BDC00033AF24 /* JSMessageChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = E107400C0E77BDC00033AF24 /* JSMessageChannel.h */; };
                E10743240E7835830033AF24 /* JSMessageChannelConstructor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E10743230E7835830033AF24 /* JSMessageChannelConstructor.cpp */; };
                E10743270E7835A50033AF24 /* JSMessageChannelConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = E10743260E7835A50033AF24 /* JSMessageChannelConstructor.h */; };
+               E108224B0EC3153A00E93953 /* WorkerTask.h in Headers */ = {isa = PBXBuildFile; fileRef = E108224A0EC3153A00E93953 /* WorkerTask.h */; };
+               E108224F0EC3156700E93953 /* WorkerTask.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E108224E0EC3156700E93953 /* WorkerTask.cpp */; };
                E10B937C0B73C00A003ED890 /* JSCustomXPathNSResolver.h in Headers */ = {isa = PBXBuildFile; fileRef = E10B937B0B73C00A003ED890 /* JSCustomXPathNSResolver.h */; };
                E10B93C30B73C291003ED890 /* JSCustomXPathNSResolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E10B93C20B73C291003ED890 /* JSCustomXPathNSResolver.cpp */; };
                E10B9B6C0B747599003ED890 /* NativeXPathNSResolver.h in Headers */ = {isa = PBXBuildFile; fileRef = E10B9B6A0B747599003ED890 /* NativeXPathNSResolver.h */; };
                E107400C0E77BDC00033AF24 /* JSMessageChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSMessageChannel.h; sourceTree = "<group>"; };
                E10743230E7835830033AF24 /* JSMessageChannelConstructor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSMessageChannelConstructor.cpp; sourceTree = "<group>"; };
                E10743260E7835A50033AF24 /* JSMessageChannelConstructor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSMessageChannelConstructor.h; sourceTree = "<group>"; };
+               E108224A0EC3153A00E93953 /* WorkerTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WorkerTask.h; sourceTree = "<group>"; };
+               E108224E0EC3156700E93953 /* WorkerTask.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WorkerTask.cpp; sourceTree = "<group>"; };
                E10B937B0B73C00A003ED890 /* JSCustomXPathNSResolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCustomXPathNSResolver.h; sourceTree = "<group>"; };
                E10B93C20B73C291003ED890 /* JSCustomXPathNSResolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSCustomXPathNSResolver.cpp; sourceTree = "<group>"; };
                E10B9B6A0B747599003ED890 /* NativeXPathNSResolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NativeXPathNSResolver.h; sourceTree = "<group>"; };
                                E1C363050EAF2D07007410BC /* WorkerLocation.cpp */,
                                E1C363000EAF2CC6007410BC /* WorkerLocation.h */,
                                E1C362BB0EAF29FB007410BC /* WorkerLocation.idl */,
+                               E108224E0EC3156700E93953 /* WorkerTask.cpp */,
+                               E108224A0EC3153A00E93953 /* WorkerTask.h */,
                                E1C2C4280EACE0E0007E61FB /* WorkerThread.cpp */,
                                E1C2C4230EACE0BC007E61FB /* WorkerThread.h */,
                                F523D30902DE4476018635CA /* XMLTokenizer.cpp */,
                                0FC705210EB1815600B90AD8 /* AtomicStringHash.h in Headers */,
                                E11C9D9B0EB3681200E409DB /* ScriptExecutionContext.h in Headers */,
                                E1A643F20EC0972500779668 /* WorkerScriptController.h in Headers */,
+                               E108224B0EC3153A00E93953 /* WorkerTask.h in Headers */,
                                0FD723820EC8BD9300CA5DD7 /* FloatQuad.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                                E1C36CBD0EB08062007410BC /* JSDOMGlobalObject.cpp in Sources */,
                                E11C9DB00EB3699500E409DB /* ScriptExecutionContext.cpp in Sources */,
                                E1A643FD0EC097A000779668 /* WorkerScriptController.cpp in Sources */,
+                               E108224F0EC3156700E93953 /* WorkerTask.cpp in Sources */,
                                0FD723830EC8BD9300CA5DD7 /* FloatQuad.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
index 8676146..b2c4cd5 100644 (file)
@@ -289,8 +289,8 @@ void markDOMNodesForDocument(Document* doc)
 
 void markActiveObjectsForContext(JSGlobalData& globalData, ScriptExecutionContext* scriptExecutionContext)
 {
-    // If an element has pending activity that may result in listeners being called
-    // (e.g. an XMLHttpRequest), we need to keep all JS wrappers alive.
+    // If an element has pending activity that may result in event listeners being called
+    // (e.g. an XMLHttpRequest), we need to keep JS wrappers alive.
 
     const HashMap<ActiveDOMObject*, void*>& activeObjects = scriptExecutionContext->activeDOMObjects();
     HashMap<ActiveDOMObject*, void*>::const_iterator activeObjectsEnd = activeObjects.end();
@@ -308,7 +308,7 @@ void markActiveObjectsForContext(JSGlobalData& globalData, ScriptExecutionContex
     for (HashSet<MessagePort*>::const_iterator iter = messagePorts.begin(); iter != portsEnd; ++iter) {
         if ((*iter)->hasPendingActivity()) {
             DOMObject* wrapper = getCachedDOMObjectWrapper(globalData, *iter);
-            // An object with pending activity must have a wrapper to mark its listeners, so no null check.
+            // A port with pending activity must have a wrapper to mark its listeners, so no null check.
             if (!wrapper->marked())
                 wrapper->mark();
         }
@@ -321,6 +321,7 @@ void markCrossHeapDependentObjectsForContext(JSGlobalData& globalData, ScriptExe
     HashSet<MessagePort*>::const_iterator portsEnd = messagePorts.end();
     for (HashSet<MessagePort*>::const_iterator iter = messagePorts.begin(); iter != portsEnd; ++iter) {
         MessagePort* port = *iter;
+        ASSERT(port->scriptExecutionContext() == scriptExecutionContext);
         RefPtr<MessagePort> entangledPort = port->entangledPort();
         if (entangledPort) {
             // No wrapper, or wrapper is already marked - no need to examine cross-heap dependencies.
@@ -330,18 +331,14 @@ void markCrossHeapDependentObjectsForContext(JSGlobalData& globalData, ScriptExe
 
             // Don't use cross-heap model of marking on same-heap pairs. Otherwise, they will never be destroyed, because a port will mark its entangled one,
             // and it will never get a chance to be marked as inaccessible. So, the port will keep getting marked in this function.
-            if ((port->scriptExecutionContext() == entangledPort->scriptExecutionContext()) || (port->scriptExecutionContext()->isDocument() && entangledPort->scriptExecutionContext()->isDocument()))
+            if ((scriptExecutionContext == entangledPort->scriptExecutionContext())
+                 || (scriptExecutionContext->isDocument() && entangledPort->scriptExecutionContext() && entangledPort->scriptExecutionContext()->isDocument()))
                 continue;
 
-            // If the wrapper hasn't been marked during the mark phase of GC, then the port shouldn't protect its entangled one.
-            // It's important not to call this when there is no wrapper. E.g., if GC is triggered after a MessageChannel is created, but before its ports are used from JS,
-            // irreversibly telling the object that its (not yet existing) wrapper is inaccessible would be wrong. Similarly, ports posted via postMessage() may not
-            // have wrappers until delivered.
-            port->setJSWrapperIsInaccessible();
-
-            // If the port is protected by its entangled one, mark it.
-            // This is an atomic read of a boolean value, no synchronization between threads is required (at least on platforms that guarantee cache coherency).
-            if (!entangledPort->jsWrapperIsInaccessible())
+            // If the port is active, mark it.
+            // FIXME: This is not quite correct, because a pair of inaccessible ports will not be collected until manually closed, or until either script execution context is destroyed.
+            // Also, there is a race condition: if a message is posted after markActiveObjectsForContext, and then ports get unentangled, we will not mark receiving port, which will result in a crash.
+            if (entangledPort)
                 wrapper->mark();
         }
     }
index d70f55a..adead1d 100644 (file)
@@ -44,79 +44,22 @@ void JSDedicatedWorker::mark()
 {
     DOMObject::mark();
 
-    if (JSUnprotectedEventListener* listener = static_cast<JSUnprotectedEventListener*>(m_impl->onMessageListener()))
+    if (JSUnprotectedEventListener* listener = static_cast<JSUnprotectedEventListener*>(m_impl->onmessage()))
         listener->mark();
 
-    if (JSUnprotectedEventListener* listener = static_cast<JSUnprotectedEventListener*>(m_impl->onCloseListener()))
+    if (JSUnprotectedEventListener* listener = static_cast<JSUnprotectedEventListener*>(m_impl->onclose()))
         listener->mark();
 
-    if (JSUnprotectedEventListener* listener = static_cast<JSUnprotectedEventListener*>(m_impl->onErrorListener()))
+    if (JSUnprotectedEventListener* listener = static_cast<JSUnprotectedEventListener*>(m_impl->onerror()))
         listener->mark();
 }
 
-JSValue* JSDedicatedWorker::startConversation(ExecState* exec, const ArgList& args)
+JSValue* JSDedicatedWorker::connect(ExecState* exec, const ArgList& args)
 {
     DOMWindow* window = asJSDOMWindow(exec->lexicalGlobalObject())->impl();
     const UString& message = args.at(exec, 0)->toString(exec);
 
-    return toJS(exec, impl()->startConversation(window->document(), message).get());
-}
-
-void JSDedicatedWorker::setOnmessage(ExecState* exec, JSValue* value)
-{
-    Document* document = impl()->document();
-    if (!document)
-        return;
-    JSDOMWindow* window = toJSDOMWindow(document->frame());
-    if (!window)
-        return;
-    impl()->setOnMessageListener(window->findOrCreateJSUnprotectedEventListener(exec, value, true));
-}
-
-JSValue* JSDedicatedWorker::onmessage(ExecState*) const
-{
-    if (JSUnprotectedEventListener* listener = static_cast<JSUnprotectedEventListener*>(impl()->onMessageListener()))
-        if (JSObject* listenerObj = listener->listenerObj())
-            return listenerObj;
-    return jsNull();
-}
-
-void JSDedicatedWorker::setOnclose(ExecState* exec, JSValue* value)
-{
-    Document* document = impl()->document();
-    if (!document)
-        return;
-    JSDOMWindow* window = toJSDOMWindow(document->frame());
-    if (!window)
-        return;
-    impl()->setOnCloseListener(window->findOrCreateJSUnprotectedEventListener(exec, value, true));
-}
-
-JSValue* JSDedicatedWorker::onclose(ExecState*) const
-{
-    if (JSUnprotectedEventListener* listener = static_cast<JSUnprotectedEventListener*>(impl()->onCloseListener()))
-        if (JSObject* listenerObj = listener->listenerObj())
-            return listenerObj;
-    return jsNull();
-}
-
-void JSDedicatedWorker::setOnerror(ExecState* exec, JSValue* value)
-{
-    Document* document = impl()->document();
-    if (!document)
-        return;
-    JSDOMWindow* window = toJSDOMWindow(document->frame());
-    if (!window)
-        return;
-    impl()->setOnErrorListener(window->findOrCreateJSUnprotectedEventListener(exec, value, true));
-}
-
-JSValue* JSDedicatedWorker::onerror(ExecState*) const
-{
-    if (JSUnprotectedEventListener* listener = static_cast<JSUnprotectedEventListener*>(impl()->onErrorListener()))
-        if (JSObject* listenerObj = listener->listenerObj())
-            return listenerObj;
-    return jsNull();
+    return toJS(exec, impl()->connect(window->document(), message).get());
 }
 
 } // namespace WebCore
index 185756d..50a0f62 100644 (file)
@@ -30,7 +30,9 @@
 #include "JSEventListener.h"
 #include "JSEventTargetNode.h"
 #include "JSMessagePort.h"
+#include "JSWorkerContext.h"
 #include "JSXMLHttpRequestUpload.h"
+#include "WorkerContext.h"
 
 #if ENABLE(SVG)
 #include "SVGElementInstance.h"
@@ -70,7 +72,12 @@ JSValue* toJS(ExecState* exec, EventTarget* target)
 
     if (MessagePort* messagePort = target->toMessagePort())
         return toJS(exec, messagePort);
-    
+
+#if ENABLE(WORKERS)
+    if (WorkerContext* workerContext = target->toWorkerContext())
+        return toJSDOMGlobalObject(workerContext);
+#endif
+
     ASSERT_NOT_REACHED();
     return jsNull();
 }
index 7d4402c..7fdc5b8 100644 (file)
@@ -30,7 +30,9 @@
 
 #include "JSWorkerContext.h"
 
+#include "Event.h"
 #include "JSDOMBinding.h"
+#include "JSEventListener.h"
 #include "JSMessageChannelConstructor.h"
 #include "JSMessageEvent.h"
 #include "JSMessagePort.h"
@@ -42,7 +44,12 @@ using namespace JSC;
 
 namespace WebCore {
 
+static JSValue* jsWorkerContextPrototypeFunctionAddEventListener(ExecState*, JSObject*, JSValue*, const ArgList&);
+static JSValue* jsWorkerContextPrototypeFunctionRemoveEventListener(ExecState*, JSObject*, JSValue*, const ArgList&);
+static JSValue* jsWorkerContextPrototypeFunctionDispatchEvent(ExecState*, JSObject*, JSValue*, const ArgList&);
 JSValue* jsWorkerContextLocation(ExecState*, const Identifier&, const PropertySlot&);
+JSValue* jsWorkerContextOnconnect(ExecState*, const Identifier&, const PropertySlot&);
+void setJSWorkerContextOnconnect(ExecState*, JSObject*, JSValue*);
 JSValue* jsWorkerContextMessageChannel(ExecState*, const Identifier&, const PropertySlot&);
 void setJSWorkerContextMessageChannel(ExecState*, JSObject*, JSValue*);
 JSValue* jsWorkerContextMessagePort(ExecState*, const Identifier&, const PropertySlot&);
@@ -56,6 +63,9 @@ void setJSWorkerContextWorkerLocation(ExecState*, JSObject*, JSValue*);
 
 /*
 @begin JSWorkerContextPrototypeTable
+  addEventListener               jsWorkerContextPrototypeFunctionAddEventListener              DontDelete|Function 3
+  removeEventListener            jsWorkerContextPrototypeFunctionRemoveEventListener           DontDelete|Function 3
+  dispatchEvent                  jsWorkerContextPrototypeFunctionDispatchEvent                DontDelete|Function 2
 #  close                          jsWorkerContextPrototypeFunctionClose                         DontDelete|Function 0
 #  MessagePort methods?
 @end
@@ -65,8 +75,9 @@ void setJSWorkerContextWorkerLocation(ExecState*, JSObject*, JSValue*);
 @begin JSWorkerContextTable
   location                      jsWorkerContextLocation            DontDelete|ReadOnly
 #  onclose                       jsWorkerContextOnclose             DontDelete
-#  onconnect                     jsWorkerContextOnconnect           DontDelete
+  onconnect                     jsWorkerContextOnconnect                     DontDelete
 #  onmessage and other MessagePort attributes?
+#  navigator-like object for browser sniffing?
   MessageChannel                jsWorkerContextMessageChannel                DontDelete
   MessageEvent                  jsWorkerContextMessageEvent                  DontDelete
   MessagePort                   jsWorkerContextMessagePort                   DontDelete
@@ -113,6 +124,19 @@ void JSWorkerContext::mark()
     Base::mark();
 
     markActiveObjectsForContext(*globalData(), scriptExecutionContext());
+
+    if (JSUnprotectedEventListener* listener = static_cast<JSUnprotectedEventListener*>(m_impl->onconnect()))
+        listener->mark();
+
+    typedef WorkerContext::EventListenersMap EventListenersMap;
+    typedef WorkerContext::ListenerVector ListenerVector;
+    EventListenersMap& eventListeners = m_impl->eventListeners();
+    for (EventListenersMap::iterator mapIter = eventListeners.begin(); mapIter != eventListeners.end(); ++mapIter) {
+        for (ListenerVector::iterator vecIter = mapIter->second.begin(); vecIter != mapIter->second.end(); ++vecIter) {
+            JSUnprotectedEventListener* listener = static_cast<JSUnprotectedEventListener*>(vecIter->get());
+            listener->mark();
+        }
+    }
 }
 
 static const HashTable* getJSWorkerContextTable(ExecState* exec)
@@ -124,7 +148,12 @@ const ClassInfo JSWorkerContext::s_info = { "WorkerContext", 0, 0, getJSWorkerCo
 
 JSObject* JSWorkerContext::createPrototype(ExecState* exec)
 {
-    return new (exec) JSWorkerContextPrototype(JSWorkerLocationPrototype::createStructureID(exec->lexicalGlobalObject()->objectPrototype()));
+    return new (exec) JSWorkerContextPrototype(JSWorkerContextPrototype::createStructureID(exec->lexicalGlobalObject()->objectPrototype()));
+}
+
+void JSWorkerContext::put(ExecState* exec, const Identifier& propertyName, JSValue* value, PutPropertySlot& slot)
+{
+    lookupPut<JSWorkerContext, Base>(exec, propertyName, value, getJSWorkerContextTable(exec), this, slot);
 }
 
 bool JSWorkerContext::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
@@ -132,12 +161,69 @@ bool JSWorkerContext::getOwnPropertySlot(ExecState* exec, const Identifier& prop
     return getStaticValueSlot<JSWorkerContext, Base>(exec, getJSWorkerContextTable(exec), this, propertyName, slot);
 }
 
+JSValue* jsWorkerContextPrototypeFunctionAddEventListener(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
+{
+    if (!thisValue->isObject(&JSWorkerContext::s_info))
+        return throwError(exec, TypeError);
+    JSWorkerContext* workerContext = static_cast<JSWorkerContext*>(asObject(thisValue));
+    RefPtr<JSUnprotectedEventListener> listener = workerContext->findOrCreateJSUnprotectedEventListener(exec, args.at(exec, 1));
+    if (!listener)
+        return jsUndefined();
+    workerContext->impl()->addEventListener(args.at(exec, 0)->toString(exec), listener.release(), args.at(exec, 2)->toBoolean(exec));
+    return jsUndefined();
+}
+
+JSValue* jsWorkerContextPrototypeFunctionRemoveEventListener(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
+{
+    if (!thisValue->isObject(&JSWorkerContext::s_info))
+        return throwError(exec, TypeError);
+    JSWorkerContext* workerContext = static_cast<JSWorkerContext*>(asObject(thisValue));
+    JSUnprotectedEventListener* listener = workerContext->findJSUnprotectedEventListener(exec, args.at(exec, 1));
+    if (!listener)
+        return jsUndefined();
+    workerContext->impl()->removeEventListener(args.at(exec, 0)->toString(exec), listener, args.at(exec, 2)->toBoolean(exec));
+    return jsUndefined();
+}
+
+JSValue* jsWorkerContextPrototypeFunctionDispatchEvent(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
+{
+    if (!thisValue->isObject(&JSWorkerContext::s_info))
+        return throwError(exec, TypeError);
+    JSWorkerContext* workerContext = static_cast<JSWorkerContext*>(asObject(thisValue));
+    Event* evt = toEvent(args.at(exec, 0));
+
+    ExceptionCode ec = 0;
+    JSValue* result = jsBoolean(workerContext->impl()->dispatchEvent(evt, ec));
+    setDOMException(exec, ec);
+
+    return result;
+}
+
 JSValue* jsWorkerContextLocation(JSC::ExecState* exec, const Identifier&, const PropertySlot& slot)
 {
     WorkerContext* imp = static_cast<WorkerContext*>(static_cast<JSWorkerContext*>(asObject(slot.slotBase()))->impl());
     return toJS(exec, imp->location());
 }
 
+JSValue* jsWorkerContextOnconnect(JSC::ExecState* exec, const Identifier&, const PropertySlot& slot)
+{
+    WorkerContext* imp = static_cast<WorkerContext*>(static_cast<JSWorkerContext*>(asObject(slot.slotBase()))->impl());
+    if (JSUnprotectedEventListener* listener = static_cast<JSUnprotectedEventListener*>(imp->onconnect())) {
+        if (JSObject* listenerObj = listener->listenerObj())
+            return listenerObj;
+    }
+    return jsNull();
+}
+
+void setJSWorkerContextOnconnect(ExecState* exec, JSObject* thisObject, JSValue* value)
+{
+    WorkerContext* imp = static_cast<WorkerContext*>(static_cast<JSWorkerContext*>(thisObject)->impl());
+    JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(imp->scriptExecutionContext());
+    if (!globalObject)
+        return;
+    imp->setOnconnect(globalObject->findOrCreateJSUnprotectedEventListener(exec, value, true));
+}
+
 JSValue* jsWorkerContextMessageChannel(ExecState* exec, const Identifier&, const PropertySlot& slot)
 {
     return getDOMConstructor<JSMessageChannelConstructor>(exec, static_cast<JSWorkerContext*>(asObject(slot.slotBase())));
index b1c0e21..538c970 100644 (file)
@@ -42,6 +42,7 @@ namespace WebCore {
         virtual ~JSWorkerContext();
 
         static JSC::JSObject* createPrototype(JSC::ExecState*);
+        virtual void put(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSValue*, JSC::PutPropertySlot&);
         virtual bool getOwnPropertySlot(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::PropertySlot&);
         virtual const JSC::ClassInfo* classInfo() const { return &s_info; }
         static const JSC::ClassInfo s_info;
index 1a84162..6fac6d0 100644 (file)
@@ -67,6 +67,9 @@ void WorkerScriptController::initScript()
 
 JSValue* WorkerScriptController::evaluate(const String& sourceURL, int baseLine, const String& code)
 {
+    initScriptIfNeeded();
+    JSLock lock(false);
+
     ExecState* exec = m_workerContextWrapper->globalExec();
     m_workerContextWrapper->startTimeoutCheck();
     Completion comp = Interpreter::evaluate(exec, exec->dynamicGlobalObject()->globalScopeChain(), makeSource(code, sourceURL, baseLine), m_workerContextWrapper);
index 049777c..89d2634 100644 (file)
@@ -59,7 +59,7 @@ namespace WebCore {
     private:
         void initScriptIfNeeded()
         {
-            if (!m_workerContext)
+            if (!m_workerContextWrapper)
                 initScript();
         }
         void initScript();
index 5e46953..45db92a 100644 (file)
@@ -28,6 +28,8 @@
 #include "ActiveDOMObject.h"
 
 #include "ScriptExecutionContext.h"
+#include "WorkerContext.h"
+#include "WorkerThread.h"
 
 namespace WebCore {
 
@@ -35,13 +37,24 @@ ActiveDOMObject::ActiveDOMObject(ScriptExecutionContext* scriptExecutionContext,
     : m_scriptExecutionContext(scriptExecutionContext)
     , m_pendingActivityCount(0)
 {
+#if ENABLE(WORKERS)
+    ASSERT((m_scriptExecutionContext->isDocument() && isMainThread())
+        || (m_scriptExecutionContext->isWorkerContext() && currentThread() == static_cast<WorkerContext*>(m_scriptExecutionContext)->thread()->threadID()));
+#endif
+
     m_scriptExecutionContext->createdActiveDOMObject(this, upcastPointer);
 }
 
 ActiveDOMObject::~ActiveDOMObject()
 {
-    if (m_scriptExecutionContext)
+    if (m_scriptExecutionContext) {
+#if ENABLE(WORKERS)
+        ASSERT((m_scriptExecutionContext->isDocument() && isMainThread())
+            || (m_scriptExecutionContext->isWorkerContext() && currentThread() == static_cast<WorkerContext*>(m_scriptExecutionContext)->thread()->threadID()));
+#endif
+
         m_scriptExecutionContext->destroyedActiveDOMObject(this);
+    }
 }
 
 void ActiveDOMObject::contextDestroyed()
index a6faaf9..295b0dc 100644 (file)
 #include "EventNames.h"
 #include "ExceptionCode.h"
 #include "FrameLoader.h"
+#include "MessageEvent.h"
 #include "MessagePort.h"
 #include "SecurityOrigin.h"
-#include "ScriptExecutionContext.h"
 #include "Timer.h"
+#include "WorkerContext.h"
+#include "WorkerTask.h"
 #include "WorkerThread.h"
 #include <wtf/MainThread.h>
 
@@ -67,8 +69,39 @@ private:
     RefPtr<DedicatedWorker> m_worker;
 };
 
+class WorkerConnectTask : public WorkerTask {
+public:
+    WorkerConnectTask(const String& message, PassRefPtr<MessagePort> port)
+        : m_message(message.copy())
+        , m_port(port)
+    {
+    }
+
+    virtual void performTask(WorkerContext* context)
+    {
+        m_port->attachToContext(context);
+
+        RefPtr<Event> evt = MessageEvent::create(m_message, "", "", 0, m_port);
+
+        if (context->onconnect()) {
+            evt->setTarget(context);
+            evt->setCurrentTarget(context);
+            context->onconnect()->handleEvent(evt.get(), false);
+        }
+
+        ExceptionCode ec = 0;
+        context->dispatchEvent(evt.release(), ec);
+        ASSERT(!ec);
+    }
+
+private:
+    String m_message;
+    RefPtr<MessagePort> m_port;
+};
+
 DedicatedWorker::DedicatedWorker(const String& url, Document* document, ExceptionCode& ec)
     : ActiveDOMObject(document, this)
+    , m_thread(0)
 {
     m_scriptURL = document->completeURL(url);
     if (!m_scriptURL.isValid()) {
@@ -96,10 +129,17 @@ Document* DedicatedWorker::document() const
     return static_cast<Document*>(scriptExecutionContext());
 }
 
-PassRefPtr<MessagePort> DedicatedWorker::startConversation(ScriptExecutionContext* /*scriptExecutionContext*/, const String& /*message*/)
+PassRefPtr<MessagePort> DedicatedWorker::connect(ScriptExecutionContext* scriptExecutionContext, const String& message)
 {
-    // Not implemented.
-    return 0;
+    RefPtr<MessagePort> port = MessagePort::create(scriptExecutionContext);
+    RefPtr<MessagePort> otherPort = MessagePort::create(0);
+    MessagePort::entangle(port.get(), otherPort.get());
+    if (m_thread)
+        m_thread->messageQueue().append(new WorkerConnectTask(message, otherPort));
+    else
+        m_queuedEarlyTasks.append(new WorkerConnectTask(message, otherPort));
+
+    return port;
 }
 
 void DedicatedWorker::close()
@@ -137,9 +177,20 @@ void DedicatedWorker::notifyFinished(CachedResource* resource)
         dispatchErrorEvent();
         unsetPendingActivity(this);
     } else  {
-        WorkerThread::create(m_scriptURL, m_cachedScript->script(), this)->start();
+        // FIXME: When can we unsetPendingActivity()? Currently, worker objects are never collected, and threads never exit.
+
+        RefPtr<WorkerThread> thread = WorkerThread::create(m_scriptURL, m_cachedScript->script(), this);
+        m_thread = thread.get();
+
+        unsigned taskCount = m_queuedEarlyTasks.size();
+        for (unsigned i = 0; i < taskCount; ++i)
+            m_thread->messageQueue().append(m_queuedEarlyTasks[i]);
+
+        m_thread->start();
     }
 
+    m_queuedEarlyTasks.clear();
+
     m_cachedScript->removeClient(this);
     m_cachedScript = 0;
 }
index ed24112..2fd592f 100644 (file)
@@ -45,6 +45,8 @@ namespace WebCore {
     class ScriptExecutionContext;
     class MessagePort;
     class String;
+    class WorkerTask;
+    class WorkerThread;
 
     typedef int ExceptionCode;
 
@@ -55,18 +57,18 @@ namespace WebCore {
 
         Document* document() const;
 
-        PassRefPtr<MessagePort> startConversation(ScriptExecutionContext*, const String& message);
+        PassRefPtr<MessagePort> connect(ScriptExecutionContext*, const String& message);
         void close();
         void postMessage(const String& message, MessagePort* port = 0);
 
-        void setOnMessageListener(PassRefPtr<EventListener> eventListener) { m_onMessageListener = eventListener; }
-        EventListener* onMessageListener() const { return m_onMessageListener.get(); }
+        void setOnmessage(PassRefPtr<EventListener> eventListener) { m_onMessageListener = eventListener; }
+        EventListener* onmessage() const { return m_onMessageListener.get(); }
 
-        void setOnCloseListener(PassRefPtr<EventListener> eventListener) { m_onCloseListener = eventListener; }
-        EventListener* onCloseListener() const { return m_onCloseListener.get(); }
+        void setOnclose(PassRefPtr<EventListener> eventListener) { m_onCloseListener = eventListener; }
+        EventListener* onclose() const { return m_onCloseListener.get(); }
 
-        void setOnErrorListener(PassRefPtr<EventListener> eventListener) { m_onErrorListener = eventListener; }
-        EventListener* onErrorListener() const { return m_onErrorListener.get(); }
+        void setOnerror(PassRefPtr<EventListener> eventListener) { m_onErrorListener = eventListener; }
+        EventListener* onerror() const { return m_onErrorListener.get(); }
 
     private:
         friend class WorkerThreadScriptLoadTimer;
@@ -81,9 +83,13 @@ namespace WebCore {
         KURL m_scriptURL;
         CachedResourceHandle<CachedScript> m_cachedScript;
 
+        WorkerThread* m_thread;
+
         RefPtr<EventListener> m_onMessageListener;
         RefPtr<EventListener> m_onCloseListener;
         RefPtr<EventListener> m_onErrorListener;
+
+        Vector<RefPtr<WorkerTask> > m_queuedEarlyTasks; // Tasks are queued here until there's a thread object created.
     };
 
 } // namespace WebCore
index 5e74683..0ef8965 100644 (file)
@@ -29,12 +29,12 @@ module threads {
     interface [CustomMarkFunction, Conditional=WORKERS] DedicatedWorker {
 
         // These also used for shared workers.
-        attribute [Custom] EventListener onclose;
-        attribute [Custom] EventListener onerror;
+        attribute EventListener onclose;
+        attribute EventListener onerror;
 
         // These are only for dedicated workers.
-        attribute [Custom] EventListener onmessage;
-        [Custom] MessagePort startConversation(in DOMString message);
+        attribute EventListener onmessage;
+        [Custom] MessagePort connect(in DOMString message);
         void close();
         void postMessage(in DOMString message, in [Optional] MessagePort port);
 
index 12b6e8c..835efe6 100644 (file)
@@ -78,6 +78,13 @@ MessagePort* EventTarget::toMessagePort()
     return 0;
 }
 
+#if ENABLE(WORKERS)
+WorkerContext* EventTarget::toWorkerContext()
+{
+    return 0;
+}
+#endif
+
 #ifndef NDEBUG
 void forbidEventDispatch()
 {
index da2130c..2430ae0 100644 (file)
@@ -45,6 +45,7 @@ namespace WebCore {
     class RegisteredEventListener;
     class ScriptExecutionContext;
     class SVGElementInstance;
+    class WorkerContext;
     class XMLHttpRequest;
     class XMLHttpRequestUpload;
 
@@ -65,6 +66,9 @@ namespace WebCore {
 #if ENABLE(SVG)
         virtual SVGElementInstance* toSVGElementInstance();
 #endif
+#if ENABLE(WORKERS)
+        virtual WorkerContext* toWorkerContext();
+#endif
 
         virtual ScriptExecutionContext* scriptExecutionContext() const = 0;
 
index 8ef184b..5193472 100644 (file)
 
 namespace WebCore {
 
-class CloseMessagePortTimer : public TimerBase {
+class MessagePortCloseEventTask : public ScriptExecutionContext::Task {
 public:
-    CloseMessagePortTimer(PassRefPtr<MessagePort> port)
+    static PassRefPtr<MessagePortCloseEventTask> create(PassRefPtr<MessagePort> port)
+    {
+        return adoptRef(new MessagePortCloseEventTask(port));
+    }
+
+private:
+    MessagePortCloseEventTask(PassRefPtr<MessagePort> port)
         : m_port(port)
     {
         ASSERT(m_port);
     }
 
-private:
-    virtual void fired()
+    virtual void performTask(ScriptExecutionContext* context)
     {
         ASSERT(!m_port->active());
+        ASSERT(context == m_port->scriptExecutionContext());
 
         // Closing may destroy the port, dispatch any remaining messages now.
         if (m_port->queueIsOpen())
             m_port->dispatchMessages();
 
         m_port->dispatchCloseEvent();
-        delete this;
     }
 
     RefPtr<MessagePort> m_port;
 };
 
-MessagePort::EventData::EventData()
+PassRefPtr<MessagePort::EventData> MessagePort::EventData::create(const String& message, PassRefPtr<MessagePort> port)
 {
+    return adoptRef(new EventData(message, port));
 }
 
-MessagePort::EventData::EventData(const String& message, PassRefPtr<DOMWindow> window, PassRefPtr<MessagePort> messagePort)
+MessagePort::EventData::EventData(const String& message, PassRefPtr<MessagePort> messagePort)
     : message(message.copy())
-    , window(window)
     , messagePort(messagePort)
 {
-    ASSERT(!this->window || isMainThread());
 }
 
 MessagePort::EventData::~EventData()
@@ -83,9 +87,9 @@ MessagePort::MessagePort(ScriptExecutionContext* scriptExecutionContext)
     , m_queueIsOpen(false)
     , m_scriptExecutionContext(scriptExecutionContext)
     , m_pendingCloseEvent(false)
-    , m_jsWrapperIsInaccessible(false)
 {
-    scriptExecutionContext->createdMessagePort(this);
+    if (scriptExecutionContext)
+        scriptExecutionContext->createdMessagePort(this);
 }
 
 MessagePort::~MessagePort()
@@ -97,7 +101,7 @@ MessagePort::~MessagePort()
         m_scriptExecutionContext->destroyedMessagePort(this);
 }
 
-PassRefPtr<MessagePort> MessagePort::clone(ScriptExecutionContext* newOwner, ExceptionCode& ec)
+PassRefPtr<MessagePort> MessagePort::clone(ExceptionCode& ec)
 {
     if (!m_entangledPort) {
         ec = INVALID_STATE_ERR;
@@ -105,11 +109,11 @@ PassRefPtr<MessagePort> MessagePort::clone(ScriptExecutionContext* newOwner, Exc
     }
 
     RefPtr<MessagePort> remotePort = m_entangledPort;
-    RefPtr<MessagePort> newPort = MessagePort::create(newOwner);
+    RefPtr<MessagePort> newPort = MessagePort::create(0);
 
     // Move all the events in the port message queue of original port to the port message queue of new port, if any, leaving the new port's port message queue in its initial closed state.
     // If events are posted (e.g. from a worker thread) while this code is executing, there is no guarantee whether they end up in the original or new port's message queue.
-    EventData eventData;
+    RefPtr<EventData> eventData;
     while (m_messageQueue.tryGetMessage(eventData))
         newPort->m_messageQueue.append(eventData);
 
@@ -124,7 +128,7 @@ void MessagePort::postMessage(const String& message, ExceptionCode& ec)
 
 void MessagePort::postMessage(const String& message, MessagePort* dataPort, ExceptionCode& ec)
 {
-    if (!m_entangledPort || !m_scriptExecutionContext || !m_entangledPort->m_scriptExecutionContext)
+    if (!m_entangledPort || !m_scriptExecutionContext)
         return;
 
     RefPtr<MessagePort> newMessagePort;
@@ -133,31 +137,27 @@ void MessagePort::postMessage(const String& message, MessagePort* dataPort, Exce
             ec = INVALID_ACCESS_ERR;
             return;
         }
-        newMessagePort = dataPort->clone(m_entangledPort->m_scriptExecutionContext, ec);
+        newMessagePort = dataPort->clone(ec);
         if (ec)
             return;
     }
 
-    DOMWindow* window = (m_scriptExecutionContext->isDocument() && m_entangledPort->m_scriptExecutionContext->isDocument()) ?
-        static_cast<Document*>(m_scriptExecutionContext)->domWindow() : 0;
-    m_entangledPort->m_messageQueue.append(EventData(message, window, newMessagePort));
-    if (m_entangledPort->m_queueIsOpen)
+    m_entangledPort->m_messageQueue.append(EventData::create(message, newMessagePort));
+    if (m_entangledPort->m_queueIsOpen && m_entangledPort->m_scriptExecutionContext)
         m_entangledPort->m_scriptExecutionContext->processMessagePortMessagesSoon();
 }
 
 PassRefPtr<MessagePort> MessagePort::startConversation(ScriptExecutionContext* scriptExecutionContext, const String& message)
 {
     RefPtr<MessagePort> port1 = MessagePort::create(scriptExecutionContext);
-    if (!m_entangledPort || !m_scriptExecutionContext || !m_entangledPort->m_scriptExecutionContext)
+    if (!m_entangledPort || !m_scriptExecutionContext)
         return port1;
-    RefPtr<MessagePort> port2 = MessagePort::create(m_entangledPort->m_scriptExecutionContext);
+    RefPtr<MessagePort> port2 = MessagePort::create(0);
 
     entangle(port1.get(), port2.get());
 
-    DOMWindow* window = (m_scriptExecutionContext->isDocument() && m_entangledPort->m_scriptExecutionContext->isDocument()) ?
-        static_cast<Document*>(m_scriptExecutionContext)->domWindow() : 0;
-    m_entangledPort->m_messageQueue.append(EventData(message, window, port2));
-    if (m_entangledPort->m_queueIsOpen)
+    m_entangledPort->m_messageQueue.append(EventData::create(message, port2));
+    if (m_entangledPort->m_queueIsOpen && m_entangledPort->m_scriptExecutionContext)
         m_entangledPort->m_scriptExecutionContext->processMessagePortMessagesSoon();
     return port1;
 }
@@ -209,6 +209,8 @@ void MessagePort::unentangle()
 
 void MessagePort::contextDestroyed()
 {
+    ASSERT(m_scriptExecutionContext);
+
     if (m_entangledPort) {
         RefPtr<MessagePort> survivingPort = m_entangledPort;
         unentangle();
@@ -218,17 +220,36 @@ void MessagePort::contextDestroyed()
     m_scriptExecutionContext = 0;
 }
 
+void MessagePort::attachToContext(ScriptExecutionContext* scriptExecutionContext)
+{
+    ASSERT(!m_scriptExecutionContext);
+    ASSERT(!m_queueIsOpen);
+
+    m_scriptExecutionContext = scriptExecutionContext;
+    m_scriptExecutionContext->createdMessagePort(this);
+    
+    // FIXME: Need to call processMessagePortMessagesSoon()?
+}
+
+ScriptExecutionContext* MessagePort::scriptExecutionContext() const
+{
+    return m_scriptExecutionContext;
+}
+
 void MessagePort::dispatchMessages()
 {
     // Messages for contexts that are not fully active get dispatched too, but JSAbstractEventListener::handleEvent() doesn't call handlers for these.
+    // FIXME: Such messages should be dispatched if the document returns from page cache. They are only allowed to be lost if the document is discarded.
     ASSERT(queueIsOpen());
 
-    EventData eventData;
+    RefPtr<EventData> eventData;
     while (m_messageQueue.tryGetMessage(eventData)) {
-        ASSERT(!eventData.window || isMainThread());
-        ASSERT(!eventData.window || scriptExecutionContext()->isDocument());
 
-        RefPtr<Event> evt = MessageEvent::create(eventData.message, "", "", eventData.window, eventData.messagePort);
+        ASSERT(!eventData->messagePort || !eventData->messagePort->m_scriptExecutionContext);
+        if (eventData->messagePort)
+            eventData->messagePort->attachToContext(m_scriptExecutionContext);
+
+        RefPtr<Event> evt = MessageEvent::create(eventData->message, "", "", 0, eventData->messagePort);
 
         if (m_onMessageListener) {
             evt->setTarget(this);
@@ -247,8 +268,7 @@ void MessagePort::queueCloseEvent()
     ASSERT(!m_pendingCloseEvent);
     m_pendingCloseEvent = true;
 
-    CloseMessagePortTimer* timer = new CloseMessagePortTimer(this);
-    timer->startOneShot(0);
+    m_scriptExecutionContext->postTask(MessagePortCloseEventTask::create(this));
 }
 
 void MessagePort::dispatchCloseEvent()
@@ -321,8 +341,8 @@ bool MessagePort::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec)
 
 bool MessagePort::hasPendingActivity()
 {
-    // We only care about the result of this function when there is no entangled port, or it is inaccessible, so no more messages can be added to the queue.
-    // Thus, using MessageQueue::isEmpty() does not cause a race condition here.
+    // In multi-threaded case, messages can be added to the queue asynchronously, but this can only happen if there is an accessible
+    // entangled port, in which case markCrossHeapDependentObjectsForContext will prevent collecting the port.
     return m_pendingCloseEvent || (m_queueIsOpen && !m_messageQueue.isEmpty());
 }
 
index 8d0c14e..23e1912 100644 (file)
@@ -41,7 +41,6 @@
 namespace WebCore {
 
     class AtomicStringImpl;
-    class DOMWindow;
     class Event;
     class Frame;
     class ScriptExecutionContext;
@@ -53,7 +52,7 @@ namespace WebCore {
         static PassRefPtr<MessagePort> create(ScriptExecutionContext* scriptExecutionContext) { return adoptRef(new MessagePort(scriptExecutionContext)); }
         ~MessagePort();
 
-        PassRefPtr<MessagePort> clone(ScriptExecutionContext*, ExceptionCode&);
+        PassRefPtr<MessagePort> clone(ExceptionCode&); // Returns a port that isn't attached to any context.
 
         bool active() const { return m_entangledPort; }
         void postMessage(const String& message, ExceptionCode&);
@@ -69,7 +68,8 @@ namespace WebCore {
         void unentangle();
 
         void contextDestroyed();
-        virtual ScriptExecutionContext* scriptExecutionContext() const { return m_scriptExecutionContext; }
+        void attachToContext(ScriptExecutionContext*);
+        virtual ScriptExecutionContext* scriptExecutionContext() const;
 
         virtual MessagePort* toMessagePort() { return this; }
 
@@ -95,11 +95,8 @@ namespace WebCore {
         void setOnclose(PassRefPtr<EventListener> eventListener) { m_onCloseListener = eventListener; }
         EventListener* onclose() const { return m_onCloseListener.get(); }
 
-        void setJSWrapperIsInaccessible() { m_jsWrapperIsInaccessible = true; }
-        bool jsWrapperIsInaccessible() const { return m_jsWrapperIsInaccessible; }
-
     private:
-        friend class CloseMessagePortTimer;
+        friend class MessagePortCloseEventTask;
 
         MessagePort(ScriptExecutionContext*);
 
@@ -110,16 +107,17 @@ namespace WebCore {
 
         MessagePort* m_entangledPort;
         
-        struct EventData {
-            EventData();
-            EventData(const String&, PassRefPtr<DOMWindow>, PassRefPtr<MessagePort>);
+        struct EventData : public ThreadSafeShared<EventData> {
+            static PassRefPtr<EventData> create(const String& message, PassRefPtr<MessagePort>);
             ~EventData();
 
             String message;
-            RefPtr<DOMWindow> window;
             RefPtr<MessagePort> messagePort;
+
+        private:
+            EventData(const String& message, PassRefPtr<MessagePort>);
         };
-        MessageQueue<EventData> m_messageQueue;
+        MessageQueue<RefPtr<EventData> > m_messageQueue;
         bool m_queueIsOpen;
 
         ScriptExecutionContext* m_scriptExecutionContext;
@@ -129,8 +127,7 @@ namespace WebCore {
 
         EventListenersMap m_eventListeners;
 
-        bool m_pendingCloseEvent;
-        bool m_jsWrapperIsInaccessible;
+        bool m_pendingCloseEvent; // The port is GC protected while waiting for a close event to be dispatched.
     };
 
 } // namespace WebCore
index 5bb076a..d84a38b 100644 (file)
 #include "ScriptExecutionContext.h"
 
 #include "ActiveDOMObject.h"
+#include "Document.h"
 #include "MessagePort.h"
 #include "Timer.h"
+#include "WorkerContext.h"
+#include "WorkerTask.h"
+#include "WorkerThread.h"
+#include <wtf/MainThread.h>
 #include <wtf/PassRefPtr.h>
 
 namespace WebCore {
 
-class MessagePortTimer : public TimerBase {
+class ProcessMessagesSoonTask : public ScriptExecutionContext::Task {
 public:
-    MessagePortTimer(PassRefPtr<ScriptExecutionContext> context)
-        : m_context(context)
+    static PassRefPtr<ProcessMessagesSoonTask> create()
     {
+        return adoptRef(new ProcessMessagesSoonTask);
     }
 
-private:
-    virtual void fired()
+    virtual void performTask(ScriptExecutionContext* context)
     {
-        m_context->dispatchMessagePortEvents();
-        delete this;
+        context->dispatchMessagePortEvents();
     }
-
-    RefPtr<ScriptExecutionContext> m_context;
 };
 
 ScriptExecutionContext::ScriptExecutionContext()
-    : m_firedMessagePortTimer(false)
 {
 }
 
@@ -73,13 +73,7 @@ ScriptExecutionContext::~ScriptExecutionContext()
 
 void ScriptExecutionContext::processMessagePortMessagesSoon()
 {
-    if (m_firedMessagePortTimer)
-        return;
-
-    MessagePortTimer* timer = new MessagePortTimer(this);
-    timer->startOneShot(0);
-
-    m_firedMessagePortTimer = true;
+    postTask(ProcessMessagesSoonTask::create());
 }
 
 void ScriptExecutionContext::dispatchMessagePortEvents()
@@ -90,11 +84,11 @@ void ScriptExecutionContext::dispatchMessagePortEvents()
     Vector<MessagePort*> ports;
     copyToVector(m_messagePorts, ports);
 
-    m_firedMessagePortTimer = false;
-
     unsigned portCount = ports.size();
     for (unsigned i = 0; i < portCount; ++i) {
         MessagePort* port = ports[i];
+        // The port may be destroyed, and another one created at the same address, but this is safe, as the worst that can happen
+        // as a result is that dispatchMessages() will be called needlessly.
         if (m_messagePorts.contains(port) && port->queueIsOpen())
             port->dispatchMessages();
     }
@@ -103,12 +97,22 @@ void ScriptExecutionContext::dispatchMessagePortEvents()
 void ScriptExecutionContext::createdMessagePort(MessagePort* port)
 {
     ASSERT(port);
+#if ENABLE(WORKERS)
+    ASSERT((isDocument() && isMainThread())
+        || (isWorkerContext() && currentThread() == static_cast<WorkerContext*>(this)->thread()->threadID()));
+#endif
+
     m_messagePorts.add(port);
 }
 
 void ScriptExecutionContext::destroyedMessagePort(MessagePort* port)
 {
     ASSERT(port);
+#if ENABLE(WORKERS)
+    ASSERT((isDocument() && isMainThread())
+        || (isWorkerContext() && currentThread() == static_cast<WorkerContext*>(this)->thread()->threadID()));
+#endif
+
     m_messagePorts.remove(port);
 }
 
@@ -134,5 +138,85 @@ void ScriptExecutionContext::destroyedActiveDOMObject(ActiveDOMObject* object)
     m_activeDOMObjects.remove(object);
 }
 
+ScriptExecutionContext::Task::~Task()
+{
+}
+
+class ScriptExecutionContextTaskTimer : public TimerBase {
+public:
+    ScriptExecutionContextTaskTimer(PassRefPtr<Document> context, PassRefPtr<ScriptExecutionContext::Task> task)
+        : m_context(context)
+        , m_task(task)
+    {
+    }
+
+private:
+    virtual void fired()
+    {
+        m_task->performTask(m_context.get());
+        delete this;
+    }
+
+    RefPtr<Document> m_context;
+    RefPtr<ScriptExecutionContext::Task> m_task;
+};
+
+#if ENABLE(WORKERS)
+class ScriptExecutionContextTaskWorkerTask : public WorkerTask {
+public:
+    static PassRefPtr<ScriptExecutionContextTaskWorkerTask> create(PassRefPtr<ScriptExecutionContext::Task> task)
+    {
+        return adoptRef(new ScriptExecutionContextTaskWorkerTask(task));
+    }
+
+private:
+    ScriptExecutionContextTaskWorkerTask(PassRefPtr<ScriptExecutionContext::Task> task)
+        : m_task(task)
+    {
+    }
+
+    virtual void performTask(WorkerContext* context)
+    {
+        m_task->performTask(context);
+    }
+
+    RefPtr<ScriptExecutionContext::Task> m_task;
+};
+#endif
+
+struct PerformTaskContext {
+    PerformTaskContext(ScriptExecutionContext* scriptExecutionContext, PassRefPtr<ScriptExecutionContext::Task> task)
+        : scriptExecutionContext(scriptExecutionContext)
+        , task(task)
+    {
+    }
+
+    RefPtr<ScriptExecutionContext> scriptExecutionContext;
+    RefPtr<ScriptExecutionContext::Task> task;
+};
+
+static void performTask(void* ctx)
+{
+    PerformTaskContext* ptctx = reinterpret_cast<PerformTaskContext*>(ctx);
+    ptctx->task->performTask(ptctx->scriptExecutionContext.get());
+    delete ptctx;
+}
+
+void ScriptExecutionContext::postTask(PassRefPtr<Task> task)
+{
+    if (isDocument()) {
+        if (isMainThread()) {
+            ScriptExecutionContextTaskTimer* timer = new ScriptExecutionContextTaskTimer(static_cast<Document*>(this), task);
+            timer->startOneShot(0);
+        } else {
+            callOnMainThread(performTask, new PerformTaskContext(this, task));
+        }
+    } else {
+        ASSERT(isWorkerContext());
+#if ENABLE(WORKERS)
+        static_cast<WorkerContext*>(this)->thread()->messageQueue().append(ScriptExecutionContextTaskWorkerTask::create(task));
+#endif
+    }
+}
 
 } // namespace WebCore
index 565dbe0..ee0f7ef 100644 (file)
@@ -29,6 +29,8 @@
 
 #include <wtf/HashMap.h>
 #include <wtf/HashSet.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/Threading.h>
 
 namespace WebCore {
 
@@ -64,10 +66,17 @@ namespace WebCore {
         void ref() { refScriptExecutionContext(); }
         void deref() { derefScriptExecutionContext(); }
 
+        class Task : public ThreadSafeShared<Task> {
+        public:
+            virtual ~Task();
+            virtual void performTask(ScriptExecutionContext*) = 0;
+        };
+
+        void postTask(PassRefPtr<Task>); // Executes the task on context's thread asynchronously.
+
     private:
         virtual const KURL& virtualURL() const = 0;
 
-        bool m_firedMessagePortTimer;
         HashSet<MessagePort*> m_messagePorts;
 
         HashMap<ActiveDOMObject*, void*> m_activeDOMObjects;
index 534c647..aeaa332 100644 (file)
 
 #if ENABLE(WORKERS)
 
-#include "SecurityOrigin.h"
 #include "WorkerContext.h"
+
+#include "Event.h"
+#include "EventException.h"
+#include "SecurityOrigin.h"
 #include "WorkerLocation.h"
+#include "WorkerTask.h"
 
 namespace WebCore {
 
-WorkerContext::WorkerContext(const KURL& url)
+WorkerContext::WorkerContext(const KURL& url, WorkerThread* thread)
     : m_url(url)
     , m_location(WorkerLocation::create(url))
     , m_securityOrigin(SecurityOrigin::create(url))
     , m_script(this)
+    , m_thread(thread)
 {
 }
 
@@ -46,6 +51,12 @@ WorkerContext::~WorkerContext()
 {
 }
 
+ScriptExecutionContext* WorkerContext::scriptExecutionContext() const
+{
+    return const_cast<WorkerContext*>(this);
+}
+
+
 const KURL& WorkerContext::virtualURL() const
 {
     return m_url;
@@ -61,6 +72,57 @@ KURL WorkerContext::completeURL(const String& url) const
     return KURL(m_location->url(), url);
 }
 
+void WorkerContext::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool)
+{
+    EventListenersMap::iterator iter = m_eventListeners.find(eventType);
+    if (iter == m_eventListeners.end()) {
+        ListenerVector listeners;
+        listeners.append(eventListener);
+        m_eventListeners.add(eventType, listeners);
+    } else {
+        ListenerVector& listeners = iter->second;
+        for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) {
+            if (*listenerIter == eventListener)
+                return;
+        }
+        
+        listeners.append(eventListener);
+        m_eventListeners.add(eventType, listeners);
+    }    
+}
+
+void WorkerContext::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool useCapture)
+{
+    EventListenersMap::iterator iter = m_eventListeners.find(eventType);
+    if (iter == m_eventListeners.end())
+        return;
+    
+    ListenerVector& listeners = iter->second;
+    for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) {
+        if (*listenerIter == eventListener) {
+            listeners.remove(listenerIter - listeners.begin());
+            return;
+        }
+    }
+}
+
+bool WorkerContext::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec)
+{
+    if (event->type().isEmpty()) {
+        ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR;
+        return true;
+    }
+    
+    ListenerVector listenersCopy = m_eventListeners.get(event->type());
+    for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) {
+        event->setTarget(this);
+        event->setCurrentTarget(this);
+        listenerIter->get()->handleEvent(event.get(), false);
+    }
+    
+    return !event->defaultPrevented();
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(WORKERS)
index b43ae7a..ae3bc3f 100644 (file)
@@ -29,6 +29,9 @@
 
 #if ENABLE(WORKERS)
 
+#include "AtomicStringHash.h"
+#include "EventListener.h"
+#include "EventTarget.h"
 #include "KURL.h"
 #include "ScriptExecutionContext.h"
 #include "WorkerScriptController.h"
 namespace WebCore {
 
     class WorkerLocation;
+    class WorkerTask;
+    class WorkerThread;
 
-    class WorkerContext : public RefCounted<WorkerContext>, public ScriptExecutionContext {
+    class WorkerContext : public RefCounted<WorkerContext>, public ScriptExecutionContext, public EventTarget {
     public:
-        static PassRefPtr<WorkerContext> create(const KURL& url)
+        static PassRefPtr<WorkerContext> create(const KURL& url, WorkerThread* thread)
         {
-            return adoptRef(new WorkerContext(url));
+            return adoptRef(new WorkerContext(url, thread));
         }
 
         virtual ~WorkerContext();
 
         virtual bool isWorkerContext() const { return true; }
 
+        virtual ScriptExecutionContext* scriptExecutionContext() const;
+
         const KURL& url() const { return m_url; }
         virtual KURL completeURL(const String&) const;
         virtual SecurityOrigin* securityOrigin() const { return m_securityOrigin.get(); }
@@ -58,6 +65,20 @@ namespace WebCore {
         WorkerLocation* location() const { return m_location.get(); }
 
         WorkerScriptController* script() { return &m_script; }
+        WorkerThread* thread() { return m_thread; }
+
+        virtual WorkerContext* toWorkerContext() { return this; }
+
+        virtual void addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture);
+        virtual void removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture);
+        virtual bool dispatchEvent(PassRefPtr<Event>, ExceptionCode&);
+
+        void setOnconnect(PassRefPtr<EventListener> eventListener) { m_onconnectListener = eventListener; }
+        EventListener* onconnect() const { return m_onconnectListener.get(); }
+
+        typedef Vector<RefPtr<EventListener> > ListenerVector;
+        typedef HashMap<AtomicString, ListenerVector> EventListenersMap;
+        EventListenersMap& eventListeners() { return m_eventListeners; }
 
         using RefCounted<WorkerContext>::ref;
         using RefCounted<WorkerContext>::deref;
@@ -65,8 +86,10 @@ namespace WebCore {
     private:
         virtual void refScriptExecutionContext() { ref(); }
         virtual void derefScriptExecutionContext() { deref(); }
+        virtual void refEventTarget() { ref(); }
+        virtual void derefEventTarget() { deref(); }
 
-        WorkerContext(const KURL& url);
+        WorkerContext(const KURL&, WorkerThread*);
 
         virtual const KURL& virtualURL() const;
 
@@ -75,6 +98,10 @@ namespace WebCore {
         RefPtr<SecurityOrigin> m_securityOrigin;
 
         WorkerScriptController m_script;
+        WorkerThread* m_thread;
+
+        RefPtr<EventListener> m_onconnectListener;
+        EventListenersMap m_eventListeners;
     };
 
 } // namespace WebCore
diff --git a/WebCore/dom/WorkerTask.cpp b/WebCore/dom/WorkerTask.cpp
new file mode 100644 (file)
index 0000000..4a22c59
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ *
+ */
+
+#include "config.h"
+
+#if ENABLE(WORKERS)
+
+#include "WorkerTask.h"
+
+namespace WebCore {
+
+WorkerTask::~WorkerTask()
+{
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WORKERS)
diff --git a/WebCore/dom/WorkerTask.h b/WebCore/dom/WorkerTask.h
new file mode 100644 (file)
index 0000000..a842ce2
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ *
+ */
+
+#ifndef WorkerTask_h
+#define WorkerTask_h
+
+#if ENABLE(WORKERS)
+
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+    class WorkerContext;
+
+    class WorkerTask : public ThreadSafeShared<WorkerTask> {
+    public:
+        virtual ~WorkerTask();
+        virtual void performTask(WorkerContext*) = 0;
+    };
+
+} // namespace WebCore
+
+#endif // ENABLE(WORKERS)
+
+#endif // WorkerTask_h
index 895dde7..fa5dc82 100644 (file)
 #include "DedicatedWorker.h"
 #include "JSWorkerContext.h"
 #include "WorkerContext.h"
+#include "WorkerTask.h"
+
+using namespace JSC;
 
 namespace WebCore {
 
+PassRefPtr<WorkerThread> WorkerThread::create(const KURL& scriptURL, const String& sourceCode, PassRefPtr<DedicatedWorker> workerObject)
+{
+    return adoptRef(new WorkerThread(scriptURL, sourceCode, workerObject));
+}
+
 WorkerThread::WorkerThread(const KURL& scriptURL, const String& sourceCode, PassRefPtr<DedicatedWorker> workerObject)
     : m_threadID(0)
     , m_scriptURL(scriptURL.string().copy())
@@ -62,13 +70,21 @@ void* WorkerThread::workerThreadStart(void* thread)
 
 void* WorkerThread::workerThread()
 {
-    RefPtr<WorkerContext> workerContext = WorkerContext::create(KURL(m_scriptURL));
+    RefPtr<WorkerContext> workerContext = WorkerContext::create(KURL(m_scriptURL), this);
     WorkerScriptController* script = workerContext->script();
 
-    // This is temporary code for testing. The thread will run a message loop.
     script->evaluate(m_scriptURL, 1, m_sourceCode);
 
+    while (true) {
+        RefPtr<WorkerTask> task;
+        if (!m_messageQueue.waitForMessage(task))
+            break;
+
+        task->performTask(workerContext.get());
+    }
+
     workerContext = 0;
+    m_threadID = 0;
 
     deref();
     return 0;
index 4d54a20..91a2666 100644 (file)
@@ -30,8 +30,9 @@
 #if ENABLE(WORKERS)
 
 #include "PlatformString.h"
+#include "WorkerTask.h"
+#include <wtf/MessageQueue.h>
 #include <wtf/PassRefPtr.h>
-#include <wtf/Threading.h>
 
 namespace WebCore {
 
@@ -40,13 +41,13 @@ namespace WebCore {
 
     class WorkerThread : public ThreadSafeShared<WorkerThread> {
     public:
-        static PassRefPtr<WorkerThread> create(const KURL& scriptURL, const String& sourceCode, PassRefPtr<DedicatedWorker> workerObject)
-        {
-            return adoptRef(new WorkerThread(scriptURL, sourceCode, workerObject));
-        }
+        static PassRefPtr<WorkerThread> create(const KURL& scriptURL, const String& sourceCode, PassRefPtr<DedicatedWorker>);
 
         bool start();
 
+        ThreadIdentifier threadID() const { return m_threadID; }
+        MessageQueue<RefPtr<WorkerTask> >& messageQueue() { return m_messageQueue; }
+
     private:
         WorkerThread(const KURL&, const String& sourceCode, PassRefPtr<DedicatedWorker>);
 
@@ -58,6 +59,8 @@ namespace WebCore {
         String m_scriptURL;
         String m_sourceCode;
         RefPtr<DedicatedWorker> m_workerObject;
+
+        MessageQueue<RefPtr<WorkerTask> > m_messageQueue;
     };
 
 } // namespace WebCore
index c8b2700..a477308 100644 (file)
@@ -372,7 +372,7 @@ void DOMWindow::postMessage(const String& message, MessagePort* messagePort, con
 
     RefPtr<MessagePort> newMessagePort;
     if (messagePort)
-        newMessagePort = messagePort->clone(document(), ec);
+        newMessagePort = messagePort->clone(ec);
     if (ec)
         return;
 
@@ -405,6 +405,11 @@ void DOMWindow::postMessageTimerFired(PostMessageTimer* t)
         }
     }
 
+    MessagePort* messagePort = timer->event()->messagePort();
+    ASSERT(!messagePort || !messagePort->scriptExecutionContext());
+    if (messagePort)
+        messagePort->attachToContext(document());
+
     document()->dispatchWindowEvent(timer->event());
 }