Changeset 47078 in webkit


Ignore:
Timestamp:
Aug 11, 2009 6:05:22 PM (15 years ago)
Author:
atwilson@chromium.org
Message:

WebCore: SharedWorkers do not exit when the last parent document exits
https://bugs.webkit.org/show_bug.cgi?id=28170

Reviewed by David Levin.

Prevents Documents from being suspended/placed in the page cache if they are associated with shared workers.

Added vector cache instead of nested hash tables for SharedWorker repository.

Added SharedWorkerRepository::documentDetached API.

  • dom/Document.cpp:

(WebCore::Document::detach):
Notifies SharedWorkerRepository when the document is closing.

  • loader/FrameLoader.cpp:

Updated FrameLoader to not cache the Document if it is associated with a SharedWorker (since we can't suspend workers yet, we need to shut them down).
(WebCore::FrameLoader::canCachePageContainingThisFrame):
(WebCore::FrameLoader::logCanCacheFrameDecision):

  • workers/DefaultSharedWorkerRepository.cpp:

(WebCore::SharedWorkerProxy::create):
(WebCore::SharedWorkerProxy::isClosing):
Renamed from closing().
(WebCore::SharedWorkerProxy::matches):
Added manual equality function to replace old hash lookup.
(WebCore::SharedWorkerProxy::isDocumentInWorkerDocuments):
Checks to see if a document is in the worker's list of documents. Used to determine if page is suspendable.
(WebCore::SharedWorkerProxy::SharedWorkerProxy):
(WebCore::SharedWorkerProxy::addToWorkerDocuments):
Added tracking of the worker's list of documents for lifecycle purposes.
(WebCore::SharedWorkerProxy::documentDetached):
Shuts down the proxy when the last associated document is detached.
(WebCore::SharedWorkerProxy::close):
Marks the proxy as closed so it is no longer shared with new instances.
(WebCore::SharedWorkerProxy::workerContextDestroyed):
Removes the proxy from the repository/frees the proxy when the associated SharedWorkerContext is destroyed.
(WebCore::DefaultSharedWorkerRepository::workerScriptLoaded):
closing()->isClosing()
(WebCore::SharedWorkerRepository::documentDetached):
(WebCore::SharedWorkerRepository::hasSharedWorkers):
Used by FrameLoader to determine if a page has shared workers and so cannot be suspended/cached.
(WebCore::DefaultSharedWorkerRepository::hasSharedWorkers):
(WebCore::DefaultSharedWorkerRepository::removeProxy):
Invoked by workerContextDestroyed() to remove a SharedWorkerProxy from the repository.
(WebCore::DefaultSharedWorkerRepository::documentDetached):
(WebCore::DefaultSharedWorkerRepository::connectToWorker):
(WebCore::DefaultSharedWorkerRepository::getProxy):

  • workers/DefaultSharedWorkerRepository.h:
  • workers/SharedWorkerRepository.h:

LayoutTests: SharedWorkers do not exit when the last parent document exits.
https://bugs.webkit.org/show_bug.cgi?id=28170

Reviewed by David Levin.

Added more tests to check that previous incarnations of the SharedWorker "name" are shut down.

  • fast/workers/shared-worker-replace-global-constructor.html-disabled:

Fixed incorrect path to common script.

  • fast/workers/shared-worker-shared-expected.txt:
  • fast/workers/shared-worker-shared.html-disabled:

Added more tests for sharing, including initial test to make sure previous incarnations of shared worker were closed.

  • fast/workers/worker-replace-global-constructor.html:

Removed extraneous closing HTML tag.

Location:
trunk
Files:
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r47076 r47078  
     12009-08-11  Drew Wilson  <atwilson@google.com>
     2
     3        Reviewed by David Levin.
     4
     5        SharedWorkers do not exit when the last parent document exits.
     6        https://bugs.webkit.org/show_bug.cgi?id=28170
     7
     8        Added more tests to check that previous incarnations of the SharedWorker "name" are shut down.
     9
     10        * fast/workers/shared-worker-replace-global-constructor.html-disabled:
     11        Fixed incorrect path to common script.
     12        * fast/workers/shared-worker-shared-expected.txt:
     13        * fast/workers/shared-worker-shared.html-disabled:
     14        Added more tests for sharing, including initial test to make sure previous incarnations of shared worker were closed.
     15        * fast/workers/worker-replace-global-constructor.html:
     16        Removed extraneous closing HTML tag.
     17
    1182009-08-11  Chris Fleizach  <cfleizach@apple.com>
    219
  • trunk/LayoutTests/fast/workers/shared-worker-replace-global-constructor.html-disabled

    r46852 r47078  
    33<div id=result></div>
    44<script src="resources/shared-worker-create-common.js"></script>
    5 <script src="worker-replace-global-constructor.js">
     5<script src="resources/worker-replace-global-constructor.js">
    66</script>
    77</body>
  • trunk/LayoutTests/fast/workers/shared-worker-shared-expected.txt

    r46925 r47078  
    22
    33PASS: Exception thrown when creating SharedWorker with different URLs but same name: Error: URL_MISMATCH_ERR: DOM Exception 21
     4PASS: Accessing new instance of shared worker: self.foo: undefined
    45PASS: Setting global variable in shared worker: self.foo = 1234: 1234
    56PASS: Accessing simultaneously-loaded instance of shared worker: self.foo: 1234
  • trunk/LayoutTests/fast/workers/shared-worker-shared.html-disabled

    r46925 r47078  
    1414
    1515// Load two workers simultaneously, to ensure that simultaneous loads also yield the same instance.
     16// Loading a worker named "name" tests that workers shutdown when the parent document exits, because other tests also create workers with that same name but with different URLs.
    1617var worker = new SharedWorker('resources/shared-worker-common.js', 'name');
    1718var worker2 = new SharedWorker('resources/shared-worker-common.js', 'name');
     
    2627
    2728// Set something in global context in one worker, read value back on other worker, to make sure they are truly shared.
    28 worker.port.postMessage("eval self.foo = 1234");
    29 
     29worker.port.postMessage("eval self.foo");
    3030worker.port.onmessage = function(event)
    3131{
    32     log((event.data == "self.foo = 1234: 1234" ? "PASS: " : "FAIL: ") + "Setting global variable in shared worker: " + event.data);
    33     worker2.port.postMessage("eval self.foo");
    34     worker2.port.onmessage = function(event)
     32    log((event.data == "self.foo: undefined" ? "PASS: " : "FAIL: ") + "Accessing new instance of shared worker: " + event.data);
     33    worker.port.postMessage("eval self.foo = 1234");
     34    worker.port.onmessage = function(event)
    3535    {
    36         log((event.data == "self.foo: 1234" ? "PASS: " : "FAIL: ") + "Accessing simultaneously-loaded instance of shared worker: " + event.data);
    37         testNewWorker();
     36        log((event.data == "self.foo = 1234: 1234" ? "PASS: " : "FAIL: ") + "Setting global variable in shared worker: " + event.data);
     37        worker2.port.postMessage("eval self.foo");
     38        worker2.port.onmessage = function(event)
     39        {
     40            log((event.data == "self.foo: 1234" ? "PASS: " : "FAIL: ") + "Accessing simultaneously-loaded instance of shared worker: " + event.data);
     41            testNewWorker();
     42        }
    3843    }
    3944}
  • trunk/LayoutTests/fast/workers/worker-replace-global-constructor.html

    r46852 r47078  
    66</script>
    77</body>
    8 </html>
  • trunk/WebCore/ChangeLog

    r47076 r47078  
     12009-08-11  Drew Wilson  <atwilson@google.com>
     2
     3        Reviewed by David Levin.
     4
     5        SharedWorkers do not exit when the last parent document exits
     6        https://bugs.webkit.org/show_bug.cgi?id=28170
     7
     8        Prevents Documents from being suspended/placed in the page cache if they are associated with shared workers.
     9
     10        Added vector cache instead of nested hash tables for SharedWorker repository.
     11
     12        Added SharedWorkerRepository::documentDetached API.
     13
     14        * dom/Document.cpp:
     15        (WebCore::Document::detach):
     16        Notifies SharedWorkerRepository when the document is closing.
     17        * loader/FrameLoader.cpp:
     18        Updated FrameLoader to not cache the Document if it is associated with a SharedWorker (since we can't suspend workers yet, we need to shut them down).
     19        (WebCore::FrameLoader::canCachePageContainingThisFrame):
     20        (WebCore::FrameLoader::logCanCacheFrameDecision):
     21        * workers/DefaultSharedWorkerRepository.cpp:
     22        (WebCore::SharedWorkerProxy::create):
     23        (WebCore::SharedWorkerProxy::isClosing):
     24        Renamed from closing().
     25        (WebCore::SharedWorkerProxy::matches):
     26        Added manual equality function to replace old hash lookup.
     27        (WebCore::SharedWorkerProxy::isDocumentInWorkerDocuments):
     28        Checks to see if a document is in the worker's list of documents. Used to determine if page is suspendable.
     29        (WebCore::SharedWorkerProxy::SharedWorkerProxy):
     30        (WebCore::SharedWorkerProxy::addToWorkerDocuments):
     31        Added tracking of the worker's list of documents for lifecycle purposes.
     32        (WebCore::SharedWorkerProxy::documentDetached):
     33        Shuts down the proxy when the last associated document is detached.
     34        (WebCore::SharedWorkerProxy::close):
     35        Marks the proxy as closed so it is no longer shared with new instances.
     36        (WebCore::SharedWorkerProxy::workerContextDestroyed):
     37        Removes the proxy from the repository/frees the proxy when the associated SharedWorkerContext is destroyed.
     38        (WebCore::DefaultSharedWorkerRepository::workerScriptLoaded):
     39        closing()->isClosing()
     40        (WebCore::SharedWorkerRepository::documentDetached):
     41        (WebCore::SharedWorkerRepository::hasSharedWorkers):
     42        Used by FrameLoader to determine if a page has shared workers and so cannot be suspended/cached.
     43        (WebCore::DefaultSharedWorkerRepository::hasSharedWorkers):
     44        (WebCore::DefaultSharedWorkerRepository::removeProxy):
     45        Invoked by workerContextDestroyed() to remove a SharedWorkerProxy from the repository.
     46        (WebCore::DefaultSharedWorkerRepository::documentDetached):
     47        (WebCore::DefaultSharedWorkerRepository::connectToWorker):
     48        (WebCore::DefaultSharedWorkerRepository::getProxy):
     49        * workers/DefaultSharedWorkerRepository.h:
     50        * workers/SharedWorkerRepository.h:
     51
    1522009-08-11  Chris Fleizach  <cfleizach@apple.com>
    253
  • trunk/WebCore/dom/Document.cpp

    r46592 r47078  
    109109#include "SelectionController.h"
    110110#include "Settings.h"
     111#include "SharedWorkerRepository.h"
    111112#include "StyleSheetList.h"
    112113#include "TextEvent.h"
     
    13491350    // in order to stop media elements
    13501351    documentWillBecomeInactive();
    1351    
     1352
     1353#if ENABLE(SHARED_WORKERS)
     1354    SharedWorkerRepository::documentDetached(this);
     1355#endif
     1356
    13521357    if (m_frame) {
    13531358        FrameView* view = m_frame->view();
  • trunk/WebCore/loader/FrameLoader.cpp

    r46978 r47078  
    8888#include "SegmentedString.h"
    8989#include "Settings.h"
     90#include "SharedWorkerRepository.h"
    9091#include "TextResourceDecoder.h"
    9192#include "WindowFeatures.h"
     
    18131814        && !m_frame->document()->hasOpenDatabases()
    18141815#endif
     1816#if ENABLE(SHARED_WORKERS)
     1817        && !SharedWorkerRepository::hasSharedWorkers(m_frame->document())
     1818#endif
    18151819        && !m_frame->document()->usingGeolocation()
    18161820        && m_currentHistoryItem
     
    19581962        if (m_frame->document()->hasOpenDatabases())
    19591963            { PCLOG("   -Frame has open database handles"); cannotCache = true; }
     1964#endif
     1965#if ENABLE(SHARED_WORKERS)
     1966        if (SharedWorkerRepository::hasSharedWorkers(m_frame->document()))
     1967            { PCLOG("   -Frame has associated SharedWorkers"); cannotCache = true; }
    19601968#endif
    19611969        if (m_frame->document()->usingGeolocation())
  • trunk/WebCore/workers/DefaultSharedWorkerRepository.cpp

    r47036 r47078  
    3636
    3737#include "ActiveDOMObject.h"
     38#include "Document.h"
    3839#include "MessagePort.h"
    3940#include "NotImplemented.h"
     
    4344#include "SharedWorker.h"
    4445#include "SharedWorkerContext.h"
     46#include "SharedWorkerRepository.h"
    4547#include "SharedWorkerThread.h"
    4648#include "WorkerLoaderProxy.h"
     
    4850#include "WorkerScriptLoader.h"
    4951#include "WorkerScriptLoaderClient.h"
    50 
     52#include <wtf/HashSet.h>
    5153#include <wtf/Threading.h>
    5254
     
    5557class SharedWorkerProxy : public ThreadSafeShared<SharedWorkerProxy>, public WorkerLoaderProxy, public WorkerReportingProxy {
    5658public:
    57     static PassRefPtr<SharedWorkerProxy> create(const String& name, const KURL& url) { return adoptRef(new SharedWorkerProxy(name, url)); }
     59    static PassRefPtr<SharedWorkerProxy> create(const String& name, const KURL& url, PassRefPtr<SecurityOrigin> origin) { return adoptRef(new SharedWorkerProxy(name, url, origin)); }
    5860
    5961    void setThread(PassRefPtr<SharedWorkerThread> thread) { m_thread = thread; }
    6062    SharedWorkerThread* thread() { return m_thread.get(); }
    61     bool closing() const { return m_closing; }
     63    bool isClosing() const { return m_closing; }
     64    void close();
    6265    KURL url() const { return m_url.copy(); }
    6366    String name() const { return m_name.copy(); }
     67    bool matches(const String& name, PassRefPtr<SecurityOrigin> origin) const { return name == m_name && origin->equal(m_origin.get()); }
    6468
    6569    // WorkerLoaderProxy
     
    7781    void addToWorkerDocuments(ScriptExecutionContext*);
    7882
     83    bool isInWorkerDocuments(Document* document) { return m_workerDocuments.contains(document); }
     84
     85    // Removes a detached document from the list of worker's documents. May set the closing flag if this is the last document in the list.
     86    void documentDetached(Document*);
     87
    7988private:
    80     SharedWorkerProxy(const String& name, const KURL&);
     89    SharedWorkerProxy(const String& name, const KURL&, PassRefPtr<SecurityOrigin>);
    8190    bool m_closing;
    8291    String m_name;
    8392    KURL m_url;
     93    // The thread is freed when the proxy is destroyed, so we need to make sure that the proxy stays around until the SharedWorkerContext exits.
    8494    RefPtr<SharedWorkerThread> m_thread;
     95    RefPtr<SecurityOrigin> m_origin;
     96    HashSet<Document*> m_workerDocuments;
    8597};
    8698
    87 SharedWorkerProxy::SharedWorkerProxy(const String& name, const KURL& url)
     99SharedWorkerProxy::SharedWorkerProxy(const String& name, const KURL& url, PassRefPtr<SecurityOrigin> origin)
    88100    : m_closing(false)
    89101    , m_name(name.copy())
    90102    , m_url(url.copy())
    91 {
     103    , m_origin(origin)
     104{
     105    // We should be the sole owner of the SecurityOrigin, as we will free it on another thread.
     106    ASSERT(m_origin->hasOneRef());
    92107}
    93108
     
    119134    // Nested workers are not yet supported, so passed-in context should always be a Document.
    120135    ASSERT(context->isDocument());
    121     // FIXME: track referring documents so we can shutdown the thread when the last one exits and remove the proxy from the cache.
     136    ASSERT(!isClosing());
     137    Document* document = static_cast<Document*>(context);
     138    m_workerDocuments.add(document);
     139}
     140
     141void SharedWorkerProxy::documentDetached(Document* document)
     142{
     143    if (isClosing())
     144        return;
     145    // Remove the document from our set (if it's there) and if that was the last document in the set, mark the proxy as closed.
     146    m_workerDocuments.remove(document);
     147    if (!m_workerDocuments.size())
     148        close();
     149}
     150
     151void SharedWorkerProxy::close()
     152{
     153    ASSERT(!isClosing());
     154    m_closing = true;
     155    // Stop the worker thread - the proxy will stay around until we get workerThreadExited() notification.
     156    if (m_thread)
     157        m_thread->stop();
     158}
     159
     160void SharedWorkerProxy::postExceptionToWorkerObject(const String&, int, const String&)
     161{
     162    // FIXME: Log exceptions to all parent documents.
     163    notImplemented();
     164}
     165
     166void SharedWorkerProxy::postConsoleMessageToWorkerObject(MessageDestination, MessageSource, MessageType, MessageLevel, const String&, int, const String&)
     167{
     168    // FIXME: Log console messages to all parent documents.
     169    notImplemented();
     170}
     171
     172// When close() is invoked, mark the proxy as closing so we don't share it with any new requests.
     173void SharedWorkerProxy::workerContextClosed()
     174{
     175    m_closing = true;
     176}
     177
     178void SharedWorkerProxy::workerContextDestroyed()
     179{
     180    // The proxy may be freed by this call, so do not reference it any further.
     181    DefaultSharedWorkerRepository::instance().removeProxy(this);
    122182}
    123183
     
    204264{
    205265    MutexLocker lock(m_lock);
    206     if (proxy.closing())
     266    if (proxy.isClosing())
    207267        return;
    208268
     
    219279{
    220280    DefaultSharedWorkerRepository::instance().connectToWorker(worker, port, url, name, ec);
     281}
     282
     283void SharedWorkerRepository::documentDetached(Document* document)
     284{
     285    DefaultSharedWorkerRepository::instance().documentDetached(document);
     286}
     287
     288bool SharedWorkerRepository::hasSharedWorkers(Document* document)
     289{
     290    return DefaultSharedWorkerRepository::instance().hasSharedWorkers(document);
     291}
     292
     293bool DefaultSharedWorkerRepository::hasSharedWorkers(Document* document)
     294{
     295    MutexLocker lock(m_lock);
     296    for (unsigned i = 0; i < m_proxies.size(); i++) {
     297        if (m_proxies[i]->isInWorkerDocuments(document))
     298            return true;
     299    }
     300    return false;
     301}
     302
     303void DefaultSharedWorkerRepository::removeProxy(SharedWorkerProxy* proxy)
     304{
     305    MutexLocker lock(m_lock);
     306    for (unsigned i = 0; i < m_proxies.size(); i++) {
     307        if (proxy == m_proxies[i].get()) {
     308            m_proxies.remove(i);
     309            return;
     310        }
     311    }
     312}
     313
     314void DefaultSharedWorkerRepository::documentDetached(Document* document)
     315{
     316    MutexLocker lock(m_lock);
     317    for (unsigned i = 0; i < m_proxies.size(); i++)
     318        m_proxies[i]->documentDetached(document);
    221319}
    222320
     
    248346    // Items in the cache are freed on another thread, so copy the URL before creating the origin, to make sure no references to external strings linger.
    249347    RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url.copy());
    250     SharedWorkerNameMap* nameMap = m_cache.get(origin);
    251     if (!nameMap) {
    252         nameMap = new SharedWorkerNameMap();
    253         m_cache.set(origin, nameMap);
    254     }
    255 
    256     RefPtr<SharedWorkerProxy> proxy = nameMap->get(name);
    257     if (!proxy.get()) {
    258         proxy = SharedWorkerProxy::create(name, url);
    259         nameMap->set(proxy->name(), proxy);
    260     }
    261     return proxy;
     348    for (unsigned i = 0; i < m_proxies.size(); i++) {
     349        if (!m_proxies[i]->isClosing() && m_proxies[i]->matches(name, origin))
     350            return m_proxies[i];
     351    }
     352    // Proxy is not in the repository currently - create a new one.
     353    RefPtr<SharedWorkerProxy> proxy = SharedWorkerProxy::create(name, url, origin.release());
     354    m_proxies.append(proxy);
     355    return proxy.release();
    262356}
    263357
  • trunk/WebCore/workers/DefaultSharedWorkerRepository.h

    r46925 r47078  
    3434#if ENABLE(SHARED_WORKERS)
    3535
    36 #include "SharedWorkerRepository.h"
     36#include "ExceptionCode.h"
    3737#include "StringHash.h"
    3838#include <wtf/HashMap.h>
    3939#include <wtf/Noncopyable.h>
     40#include <wtf/PassOwnPtr.h>
     41#include <wtf/PassRefPtr.h>
    4042#include <wtf/RefPtr.h>
    4143#include <wtf/Threading.h>
     
    4345namespace WebCore {
    4446
     47    class Document;
    4548    class KURL;
     49    class MessagePortChannel;
    4650    class ScriptExecutionContext;
    47     class SecurityOrigin;
     51    class SharedWorker;
    4852    class SharedWorkerProxy;
    49 
    50     struct SecurityOriginHash;
    51     struct SecurityOriginTraits;
     53    class String;
    5254
    5355    // Platform-specific implementation of the SharedWorkerRepository static interface.
     
    6062        void connectToWorker(PassRefPtr<SharedWorker>, PassOwnPtr<MessagePortChannel>, const KURL&, const String& name, ExceptionCode&);
    6163
     64        // Notification that a document has been detached.
     65        void documentDetached(Document*);
     66
     67        // Removes the passed SharedWorkerProxy from the repository.
     68        void removeProxy(SharedWorkerProxy*);
     69
     70        bool hasSharedWorkers(Document*);
     71
    6272        static DefaultSharedWorkerRepository& instance();
    6373    private:
     
    6979        Mutex m_lock;
    7080
    71         typedef HashMap<String, RefPtr<SharedWorkerProxy> > SharedWorkerNameMap;
    72         typedef HashMap<RefPtr<SecurityOrigin>, SharedWorkerNameMap*, SecurityOriginHash> SharedWorkerProxyCache;
    73 
    74         // Items in this cache may be freed on another thread, so all keys and values must be either copied before insertion or thread safe.
    75         SharedWorkerProxyCache m_cache;
     81        // List of shared workers. Expectation is that there will be a limited number of shared workers, and so tracking them in a Vector is more efficient than nested HashMaps.
     82        typedef Vector<RefPtr<SharedWorkerProxy> > SharedWorkerProxyRepository;
     83        SharedWorkerProxyRepository m_proxies;
    7684    };
    7785
  • trunk/WebCore/workers/SharedWorkerRepository.h

    r46925 r47078  
    3535
    3636#include "ExceptionCode.h"
    37 
    3837#include <wtf/PassOwnPtr.h>
    3938#include <wtf/PassRefPtr.h>
     
    4140namespace WebCore {
    4241
     42    class Document;
    4343    class KURL;
    4444    class MessagePortChannel;
     
    5151        // Connects the passed SharedWorker object with the specified worker thread, creating a new thread if necessary.
    5252        static void connect(PassRefPtr<SharedWorker>, PassOwnPtr<MessagePortChannel>, const KURL&, const String& name, ExceptionCode&);
     53
     54        // Invoked when a document has been detached.
     55        static void documentDetached(Document*);
     56
     57        // Returns true if the passed document is associated with any SharedWorkers.
     58        static bool hasSharedWorkers(Document*);
    5359    private:
    5460        SharedWorkerRepository() { }
Note: See TracChangeset for help on using the changeset viewer.