Title: [116792] trunk
Revision
116792
Author
simon.fra...@apple.com
Date
2012-05-11 11:56:32 -0700 (Fri, 11 May 2012)

Log Message

Source/WebCore: CVDisplayLink keeps running after a single requestAnimationFrame
https://bugs.webkit.org/show_bug.cgi?id=86174

Reviewed by Sam Weinig.

A DisplayRefreshMonitor would keep its CVDisplayLink alive for as long
as it had clients, and the client is the ScriptedAnimationController, which
lives on the document. So a single requestAnimationFrame call would kick
off a CVDisplayLink which lived until the document was destroyed.

Fix by having the DisplayRefreshMonitor kill itself if the CVDisplayLink
fires for 10 times with no scheduled callbacks (to avoid creation/deletion
thrash on pages that call requestAnimationFrames with short setTimeouts,
as some do).

Use a HashMap in DisplayRefreshMonitorManager for the set of DisplayRefreshMonitor,
with the displayID as the key (using UnsignedWithZeroKeyHashTraits<uint64_t> since
we want to allow for 0 to be a valid displayID).

Use a HashSet in DisplayRefreshMonitor for the client set, so that we don't have to
worry about adding clients twice.

Also fix a possible crash when the only client of a DisplayRefreshMonitor
was removed from inside the callback by making DisplayRefreshMonitor ref-counted,
with a protector.

Test: fast/animation/request-animation-frame-detach-element2.html

* platform/graphics/DisplayRefreshMonitor.cpp:
(WebCore::DisplayRefreshMonitor::DisplayRefreshMonitor): Initialize m_unscheduledFireCount
(WebCore::DisplayRefreshMonitor::handleDisplayRefreshedNotificationOnMainThread): Renamed
from refreshDisplayOnMainThread, since it doesn't just refresh the display.
(WebCore::DisplayRefreshMonitor::addClient): No longer inline.
(WebCore::DisplayRefreshMonitor::removeClient): No longer inline.
(WebCore::DisplayRefreshMonitor::displayDidRefresh): Keep track of m_unscheduledFireCount,
which we used to kill this monitor if it has been idle for a while.
Use a RefPtr<DisplayRefreshMonitor> to prevent deletion while running the callback.
Copy the clients to a vector to protect against mutating the set while enumerating it.
Notify the DisplayRefreshMonitorManager when we're done, so that it can decide
to delete inactive monitors.

(WebCore::DisplayRefreshMonitorManager::ensureMonitorForClient): find or allocate
a DisplayRefreshMonitor for a given client.
(WebCore::DisplayRefreshMonitorManager::registerClient): Simplified by use of HashMap.
(WebCore::DisplayRefreshMonitorManager::unregisterClient): Ditto.
(WebCore::DisplayRefreshMonitorManager::scheduleAnimation): Ditto. Uses
ensureMonitorForClient() since an earlier inactive monitor may have been removed.
(WebCore::DisplayRefreshMonitorManager::displayDidRefresh): Remove inactive
monitors.
* platform/graphics/DisplayRefreshMonitor.h: Make DisplayRefreshMonitor
ref-counted, to make it easier to avoid deletion while it's on the stack.
(WebCore::DisplayRefreshMonitor::create):
(DisplayRefreshMonitor):
(WebCore::DisplayRefreshMonitor::shouldBeTerminated):
(DisplayRefreshMonitorManager):
* platform/graphics/blackberry/DisplayRefreshMonitorBlackBerry.cpp: Attempt to
keep things building.
(WebCore::DisplayRefreshMonitor::~DisplayRefreshMonitor):
(WebCore::DisplayRefreshMonitor::displayLinkFired):
to give the manager a chance to kill this monitor.
* platform/graphics/mac/DisplayRefreshMonitorMac.cpp:
(WebCore::DisplayRefreshMonitor::~DisplayRefreshMonitor):
(WebCore::DisplayRefreshMonitor::displayLinkFired): Don't bail early
if not scheduled; we want to call handleDisplayRefreshedNotificationOnMainThread(),

LayoutTests: CVDisplayLink keeps running after a single requestAnimationFrame
https://bugs.webkit.org/show_bug.cgi?id=86174

Reviewed by Sam Weinig.

Test removing a frame inside its requestAnimationFrame callback.

* fast/animation/request-animation-frame-detach-element2-expected.txt: Added.
* fast/animation/request-animation-frame-detach-element2.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (116791 => 116792)


--- trunk/LayoutTests/ChangeLog	2012-05-11 18:50:41 UTC (rev 116791)
+++ trunk/LayoutTests/ChangeLog	2012-05-11 18:56:32 UTC (rev 116792)
@@ -1,3 +1,15 @@
+2012-05-11  Simon Fraser  <simon.fra...@apple.com>
+
+        CVDisplayLink keeps running after a single requestAnimationFrame
+        https://bugs.webkit.org/show_bug.cgi?id=86174
+
+        Reviewed by Sam Weinig.
+        
+        Test removing a frame inside its requestAnimationFrame callback.
+
+        * fast/animation/request-animation-frame-detach-element2-expected.txt: Added.
+        * fast/animation/request-animation-frame-detach-element2.html: Added.
+
 2012-05-11  Ian Vollick  <voll...@chromium.org>
 
         [chromium] Ensure that animations continue to run when transform-style is changed

Added: trunk/LayoutTests/fast/animation/request-animation-frame-detach-element2-expected.txt (0 => 116792)


--- trunk/LayoutTests/fast/animation/request-animation-frame-detach-element2-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/animation/request-animation-frame-detach-element2-expected.txt	2012-05-11 18:56:32 UTC (rev 116792)
@@ -0,0 +1 @@
+Test passes is there is no crash.  
Property changes on: trunk/LayoutTests/fast/animation/request-animation-frame-detach-element2-expected.txt
___________________________________________________________________

Added: svn:mime-type

Added: svn:keywords

Added: svn:eol-style

Added: trunk/LayoutTests/fast/animation/request-animation-frame-detach-element2.html (0 => 116792)


--- trunk/LayoutTests/fast/animation/request-animation-frame-detach-element2.html	                        (rev 0)
+++ trunk/LayoutTests/fast/animation/request-animation-frame-detach-element2.html	2012-05-11 18:56:32 UTC (rev 116792)
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<script>
+if (window.layoutTestController) {
+    layoutTestController.dumpAsText();
+    layoutTestController.waitUntilDone();
+}
+window._onload_ = function() {
+    var el = document.getElementsByTagName("iframe")[0];
+    window.frames[0].webkitRequestAnimationFrame(function() {
+        el.parentNode.removeChild(el);
+    });
+
+    if (window.layoutTestController) {
+        window.setTimeout(function() {
+            layoutTestController.display();
+            layoutTestController.notifyDone();
+        }, 50);
+    }
+}
+</script>
+Test passes is there is no crash.
+<iframe></iframe>
+<iframe></iframe>
Property changes on: trunk/LayoutTests/fast/animation/request-animation-frame-detach-element2.html
___________________________________________________________________

Added: svn:mime-type

Added: svn:keywords

Added: svn:eol-style

Modified: trunk/Source/WebCore/ChangeLog (116791 => 116792)


--- trunk/Source/WebCore/ChangeLog	2012-05-11 18:50:41 UTC (rev 116791)
+++ trunk/Source/WebCore/ChangeLog	2012-05-11 18:56:32 UTC (rev 116792)
@@ -1,3 +1,70 @@
+2012-05-11  Simon Fraser  <simon.fra...@apple.com>
+
+        CVDisplayLink keeps running after a single requestAnimationFrame
+        https://bugs.webkit.org/show_bug.cgi?id=86174
+        
+        Reviewed by Sam Weinig.
+        
+        A DisplayRefreshMonitor would keep its CVDisplayLink alive for as long
+        as it had clients, and the client is the ScriptedAnimationController, which
+        lives on the document. So a single requestAnimationFrame call would kick
+        off a CVDisplayLink which lived until the document was destroyed.
+        
+        Fix by having the DisplayRefreshMonitor kill itself if the CVDisplayLink
+        fires for 10 times with no scheduled callbacks (to avoid creation/deletion
+        thrash on pages that call requestAnimationFrames with short setTimeouts,
+        as some do).
+        
+        Use a HashMap in DisplayRefreshMonitorManager for the set of DisplayRefreshMonitor,
+        with the displayID as the key (using UnsignedWithZeroKeyHashTraits<uint64_t> since
+        we want to allow for 0 to be a valid displayID).
+        
+        Use a HashSet in DisplayRefreshMonitor for the client set, so that we don't have to
+        worry about adding clients twice.
+        
+        Also fix a possible crash when the only client of a DisplayRefreshMonitor
+        was removed from inside the callback by making DisplayRefreshMonitor ref-counted,
+        with a protector.
+
+        Test: fast/animation/request-animation-frame-detach-element2.html
+
+        * platform/graphics/DisplayRefreshMonitor.cpp:
+        (WebCore::DisplayRefreshMonitor::DisplayRefreshMonitor): Initialize m_unscheduledFireCount
+        (WebCore::DisplayRefreshMonitor::handleDisplayRefreshedNotificationOnMainThread): Renamed
+        from refreshDisplayOnMainThread, since it doesn't just refresh the display.
+        (WebCore::DisplayRefreshMonitor::addClient): No longer inline.
+        (WebCore::DisplayRefreshMonitor::removeClient): No longer inline.
+        (WebCore::DisplayRefreshMonitor::displayDidRefresh): Keep track of m_unscheduledFireCount,
+        which we used to kill this monitor if it has been idle for a while.
+        Use a RefPtr<DisplayRefreshMonitor> to prevent deletion while running the callback.
+        Copy the clients to a vector to protect against mutating the set while enumerating it.
+        Notify the DisplayRefreshMonitorManager when we're done, so that it can decide
+        to delete inactive monitors.
+        
+        (WebCore::DisplayRefreshMonitorManager::ensureMonitorForClient): find or allocate
+        a DisplayRefreshMonitor for a given client.
+        (WebCore::DisplayRefreshMonitorManager::registerClient): Simplified by use of HashMap.
+        (WebCore::DisplayRefreshMonitorManager::unregisterClient): Ditto.
+        (WebCore::DisplayRefreshMonitorManager::scheduleAnimation): Ditto. Uses
+        ensureMonitorForClient() since an earlier inactive monitor may have been removed.
+        (WebCore::DisplayRefreshMonitorManager::displayDidRefresh): Remove inactive
+        monitors.
+        * platform/graphics/DisplayRefreshMonitor.h: Make DisplayRefreshMonitor
+        ref-counted, to make it easier to avoid deletion while it's on the stack.
+        (WebCore::DisplayRefreshMonitor::create):
+        (DisplayRefreshMonitor):
+        (WebCore::DisplayRefreshMonitor::shouldBeTerminated):
+        (DisplayRefreshMonitorManager):
+        * platform/graphics/blackberry/DisplayRefreshMonitorBlackBerry.cpp: Attempt to
+        keep things building.
+        (WebCore::DisplayRefreshMonitor::~DisplayRefreshMonitor):
+        (WebCore::DisplayRefreshMonitor::displayLinkFired):
+        to give the manager a chance to kill this monitor.
+        * platform/graphics/mac/DisplayRefreshMonitorMac.cpp:
+        (WebCore::DisplayRefreshMonitor::~DisplayRefreshMonitor):
+        (WebCore::DisplayRefreshMonitor::displayLinkFired): Don't bail early
+        if not scheduled; we want to call handleDisplayRefreshedNotificationOnMainThread(),
+
 2012-05-11  Pavel Feldman  <pfeld...@chromium.org>
 
         Web Inspector: move canEditScriptSource and setScriptSource from DebuggerPresentationModel into ResourceBinding

Modified: trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp (116791 => 116792)


--- trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp	2012-05-11 18:50:41 UTC (rev 116791)
+++ trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp	2012-05-11 18:56:32 UTC (rev 116792)
@@ -57,6 +57,7 @@
     , m_active(true)
     , m_scheduled(false)
     , m_previousFrameDone(true)
+    , m_unscheduledFireCount(0)
     , m_displayID(displayID)
 #if PLATFORM(MAC)
     , m_displayLink(0)
@@ -67,28 +68,56 @@
 {
 }
 
-void DisplayRefreshMonitor::refreshDisplayOnMainThread(void* data)
+void DisplayRefreshMonitor::handleDisplayRefreshedNotificationOnMainThread(void* data)
 {
     DisplayRefreshMonitor* monitor = static_cast<DisplayRefreshMonitor*>(data);
-    monitor->notifyClients();
+    monitor->displayDidRefresh();
 }
 
-void DisplayRefreshMonitor::notifyClients()
+void DisplayRefreshMonitor::addClient(DisplayRefreshMonitorClient* client)
 {
+    m_clients.add(client);
+}
+
+bool DisplayRefreshMonitor::removeClient(DisplayRefreshMonitorClient* client)
+{
+    DisplayRefreshMonitorClientSet::iterator it = m_clients.find(client);
+    if (it != m_clients.end()) {
+        m_clients.remove(it);
+        return true;
+    }
+    return false;
+}
+
+void DisplayRefreshMonitor::displayDidRefresh()
+{
     double timestamp;
     {
         MutexLocker lock(m_mutex);
+         if (!m_scheduled)
+            ++m_unscheduledFireCount;
+        else
+            m_unscheduledFireCount = 0;
+
         m_scheduled = false;
         timestamp = m_timestamp;
     }
 
-    for (size_t i = 0; i < m_clients.size(); ++i)
-        m_clients[i]->fireDisplayRefreshIfNeeded(timestamp);
+    // The call back can cause all our clients to be unregistered, so we need to protect
+    // against deletion until the end of the method.
+    RefPtr<DisplayRefreshMonitor> protector(this);
+    
+    Vector<DisplayRefreshMonitorClient*> clients;
+    copyToVector(m_clients, clients);
+    for (size_t i = 0; i < clients.size(); ++i)
+        clients[i]->fireDisplayRefreshIfNeeded(timestamp);
 
     {
         MutexLocker lock(m_mutex);
         m_previousFrameDone = true;
     }
+    
+    DisplayRefreshMonitorManager::sharedManager()->displayDidRefresh(this);
 }
 
 DisplayRefreshMonitorManager* DisplayRefreshMonitorManager::sharedManager()
@@ -97,13 +126,18 @@
     return &manager;
 }
 
-size_t DisplayRefreshMonitorManager::findMonitor(PlatformDisplayID displayID) const
+DisplayRefreshMonitor* DisplayRefreshMonitorManager::ensureMonitorForClient(DisplayRefreshMonitorClient* client)
 {
-    for (size_t i = 0; i < m_monitors.size(); ++i)
-        if (m_monitors[i]->displayID() == displayID)
-            return i;
-            
-    return notFound;
+    DisplayRefreshMonitorMap::iterator it = m_monitors.find(client->m_displayID);
+    if (it == m_monitors.end()) {
+        RefPtr<DisplayRefreshMonitor> monitor = DisplayRefreshMonitor::create(client->m_displayID);
+        monitor->addClient(client);
+        DisplayRefreshMonitor* result = monitor.get();
+        m_monitors.add(client->m_displayID, monitor.release());
+        return result;
+    }
+
+    return it->second.get();
 }
 
 void DisplayRefreshMonitorManager::registerClient(DisplayRefreshMonitorClient* client)
@@ -111,33 +145,22 @@
     if (!client->m_displayIDIsSet)
         return;
         
-    size_t index = findMonitor(client->m_displayID);
-    DisplayRefreshMonitor* monitor;
-    
-    if (index == notFound) {
-        monitor = new DisplayRefreshMonitor(client->m_displayID);
-        m_monitors.append(monitor);
-    } else
-        monitor = m_monitors[index];
-        
-    monitor->addClient(client);
+    ensureMonitorForClient(client);
 }
 
 void DisplayRefreshMonitorManager::unregisterClient(DisplayRefreshMonitorClient* client)
 {
     if (!client->m_displayIDIsSet)
         return;
-        
-    size_t index = findMonitor(client->m_displayID);
-    if (index == notFound)
+
+    DisplayRefreshMonitorMap::iterator it = m_monitors.find(client->m_displayID);
+    if (it == m_monitors.end())
         return;
     
-    DisplayRefreshMonitor* monitor = m_monitors[index];
+    DisplayRefreshMonitor* monitor = it->second.get();
     if (monitor->removeClient(client)) {
-        if (!monitor->hasClients()) {
-            m_monitors.remove(index);
-            delete monitor;
-        }
+        if (!monitor->hasClients())
+            m_monitors.remove(it);
     }
 }
 
@@ -146,13 +169,21 @@
     if (!client->m_displayIDIsSet)
         return false;
         
-    size_t i = findMonitor(client->m_displayID);
-    ASSERT(i != notFound);
+    DisplayRefreshMonitor* monitor = ensureMonitorForClient(client);
 
     client->m_scheduled = true;
-    return m_monitors[i]->requestRefreshCallback();
+    return monitor->requestRefreshCallback();
 }
 
+void DisplayRefreshMonitorManager::displayDidRefresh(DisplayRefreshMonitor* monitor)
+{
+    if (monitor->shouldBeTerminated()) {
+        DisplayRefreshMonitorMap::iterator it = m_monitors.find(monitor->displayID());
+        ASSERT(it != m_monitors.end());
+        m_monitors.remove(it);
+    }
+}
+
 void DisplayRefreshMonitorManager::windowScreenDidChange(PlatformDisplayID displayID, DisplayRefreshMonitorClient* client)
 {
     if (client->m_displayIDIsSet && client->m_displayID == displayID)

Modified: trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitor.h (116791 => 116792)


--- trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitor.h	2012-05-11 18:50:41 UTC (rev 116791)
+++ trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitor.h	2012-05-11 18:56:32 UTC (rev 116792)
@@ -29,8 +29,11 @@
 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
 
 #include "PlatformScreen.h"
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
 #include <wtf/Threading.h>
-#include <wtf/Vector.h>
 #if PLATFORM(BLACKBERRY)
 #include <BlackBerryPlatformAnimation.h>
 #endif
@@ -86,9 +89,13 @@
 // Monitor for display refresh messages for a given screen
 //
 
-class DisplayRefreshMonitor {
+class DisplayRefreshMonitor : public RefCounted<DisplayRefreshMonitor> {
 public:
-    DisplayRefreshMonitor(PlatformDisplayID);
+    static PassRefPtr<DisplayRefreshMonitor> create(PlatformDisplayID displayID)
+    {
+        return adoptRef(new DisplayRefreshMonitor(displayID));
+    }
+    
     ~DisplayRefreshMonitor();
     
     // Return true if callback request was scheduled, false if it couldn't be
@@ -97,30 +104,33 @@
     void windowScreenDidChange(PlatformDisplayID);
     
     bool hasClients() const { return m_clients.size(); }
-    void addClient(DisplayRefreshMonitorClient* client) { m_clients.append(client); }
-    bool removeClient(DisplayRefreshMonitorClient* client)
+    void addClient(DisplayRefreshMonitorClient*);
+    bool removeClient(DisplayRefreshMonitorClient*);
+    
+    PlatformDisplayID displayID() const { return m_displayID; }
+
+    bool shouldBeTerminated() const
     {
-        size_t i = m_clients.find(client);
-        if (i == notFound)
-            return false;
-        m_clients.remove(i);
-        return true;
+        const int maxInactiveFireCount = 10;
+        return !m_scheduled && m_unscheduledFireCount > maxInactiveFireCount;
     }
     
-    PlatformDisplayID displayID() const { return m_displayID; }
-
 private:
-    void notifyClients();
-    static void refreshDisplayOnMainThread(void* data);
+    DisplayRefreshMonitor(PlatformDisplayID);
 
+    void displayDidRefresh();
+    static void handleDisplayRefreshedNotificationOnMainThread(void* data);
+
     double m_timestamp;
     bool m_active;
     bool m_scheduled;
     bool m_previousFrameDone;
+    int m_unscheduledFireCount; // Number of times the display link has fired with no clients.
     PlatformDisplayID m_displayID;
-    DisplayRefreshMonitorManager* m_manager;
     Mutex m_mutex;
-    Vector<DisplayRefreshMonitorClient*> m_clients;
+    
+    typedef HashSet<DisplayRefreshMonitorClient*> DisplayRefreshMonitorClientSet;
+    DisplayRefreshMonitorClientSet m_clients;
 #if PLATFORM(BLACKBERRY)
 public:
     void displayLinkFired();
@@ -154,11 +164,15 @@
     void windowScreenDidChange(PlatformDisplayID, DisplayRefreshMonitorClient*);
 
 private:
+    friend class DisplayRefreshMonitor;
+    void displayDidRefresh(DisplayRefreshMonitor*);
+    
     DisplayRefreshMonitorManager() { }
+    DisplayRefreshMonitor* ensureMonitorForClient(DisplayRefreshMonitorClient*);
 
-    size_t findMonitor(PlatformDisplayID) const;
-    
-    Vector<DisplayRefreshMonitor*> m_monitors;
+    // We know nothing about the values of PlatformDisplayIDs, so use UnsignedWithZeroKeyHashTraits.
+    typedef HashMap<uint64_t, RefPtr<DisplayRefreshMonitor>, WTF::IntHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t> > DisplayRefreshMonitorMap;
+    DisplayRefreshMonitorMap m_monitors;
 };
 
 }

Modified: trunk/Source/WebCore/platform/graphics/blackberry/DisplayRefreshMonitorBlackBerry.cpp (116791 => 116792)


--- trunk/Source/WebCore/platform/graphics/blackberry/DisplayRefreshMonitorBlackBerry.cpp	2012-05-11 18:50:41 UTC (rev 116791)
+++ trunk/Source/WebCore/platform/graphics/blackberry/DisplayRefreshMonitorBlackBerry.cpp	2012-05-11 18:56:32 UTC (rev 116792)
@@ -40,7 +40,7 @@
 DisplayRefreshMonitor::~DisplayRefreshMonitor()
 {
     stopAnimationClient();
-    cancelCallOnMainThread(DisplayRefreshMonitor::refreshDisplayOnMainThread, this);
+    cancelCallOnMainThread(DisplayRefreshMonitor::handleDisplayRefreshedNotificationOnMainThread, this);
 }
 
 void DisplayRefreshMonitor::startAnimationClient()
@@ -83,7 +83,7 @@
 
     m_timestamp = currentTime();
 
-    callOnMainThread(refreshDisplayOnMainThread, this);
+    callOnMainThread(handleDisplayRefreshedNotificationOnMainThread, this);
 }
 
 }

Modified: trunk/Source/WebCore/platform/graphics/mac/DisplayRefreshMonitorMac.cpp (116791 => 116792)


--- trunk/Source/WebCore/platform/graphics/mac/DisplayRefreshMonitorMac.cpp	2012-05-11 18:50:41 UTC (rev 116791)
+++ trunk/Source/WebCore/platform/graphics/mac/DisplayRefreshMonitorMac.cpp	2012-05-11 18:56:32 UTC (rev 116792)
@@ -54,7 +54,7 @@
         m_displayLink = 0;
     }
 
-    cancelCallOnMainThread(DisplayRefreshMonitor::refreshDisplayOnMainThread, this);
+    cancelCallOnMainThread(DisplayRefreshMonitor::handleDisplayRefreshedNotificationOnMainThread, this);
 }
 
 bool DisplayRefreshMonitor::requestRefreshCallback()
@@ -87,7 +87,7 @@
 void DisplayRefreshMonitor::displayLinkFired(double nowSeconds, double outputTimeSeconds)
 {
     MutexLocker lock(m_mutex);
-    if (!m_scheduled || !m_previousFrameDone)
+    if (!m_previousFrameDone)
         return;
 
     m_previousFrameDone = false;
@@ -95,7 +95,7 @@
     double webKitNow = currentTime();
     m_timestamp = webKitNow - nowSeconds + outputTimeSeconds;
     
-    callOnMainThread(refreshDisplayOnMainThread, this);
+    callOnMainThread(handleDisplayRefreshedNotificationOnMainThread, this);
 }
 
 }
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to