Title: [114374] trunk
Revision
114374
Author
[email protected]
Date
2012-04-17 07:02:07 -0700 (Tue, 17 Apr 2012)

Log Message

Asserts in XMLHttpRequestProgressEventThrottle
https://bugs.webkit.org/show_bug.cgi?id=81506

Patch by Allan Sandfeld Jensen <[email protected]> on 2012-04-17
Reviewed by Julien Chaffraix.

Source/WebCore:

The asserts were incorrectly triggered because suspending active DOM objects
(which suspends the XMLHttpRequestProgressEventThrottle) doesn't stop _javascript_
from running or suspend any running loader we may have. The previous code would
assume those 2 cases were impossible.

When XmlHttpRequest::open is called or data is received while the XmlHttpRequest object
is suspended the object may attempt to dispatch events. This patch defers these events
until the object is resumed.

Progress events are coalesced similar to normal throttling, and readystate-change events
are coalesced to avoid identical events emitted right after eachother.

On resume the events are dispatched after a timer to avoid interfering with
ScriptExecutionContext which is iterating over suspended objects.

Test: fast/events/pagehide-xhr-open.html

* xml/XMLHttpRequestProgressEventThrottle.cpp:
(WebCore::XMLHttpRequestProgressEventThrottle::XMLHttpRequestProgressEventThrottle):
(WebCore::XMLHttpRequestProgressEventThrottle::dispatchProgressEvent):
(WebCore::XMLHttpRequestProgressEventThrottle::dispatchEvent):
(WebCore::XMLHttpRequestProgressEventThrottle::flushProgressEvent):
(WebCore::XMLHttpRequestProgressEventThrottle::dispatchDeferredEvents):
(WebCore::XMLHttpRequestProgressEventThrottle::fired):
(WebCore::XMLHttpRequestProgressEventThrottle::suspend):
(WebCore::XMLHttpRequestProgressEventThrottle::resume):
* xml/XMLHttpRequestProgressEventThrottle.h:
(XMLHttpRequestProgressEventThrottle):

LayoutTests:

Tests that xmlhttprequest.open does not assert in when called from onpagehide.

* fast/events/pagehide-xhr-open-expected.txt: Added.
* fast/events/pagehide-xhr-open.html: Added.
* fast/events/resources/subframe-xmlhttprequest.html: Added.
* platform/chromium/test_expectations.txt:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (114373 => 114374)


--- trunk/LayoutTests/ChangeLog	2012-04-17 13:44:43 UTC (rev 114373)
+++ trunk/LayoutTests/ChangeLog	2012-04-17 14:02:07 UTC (rev 114374)
@@ -1,3 +1,17 @@
+2012-04-17  Allan Sandfeld Jensen  <[email protected]>
+
+        Asserts in XMLHttpRequestProgressEventThrottle
+        https://bugs.webkit.org/show_bug.cgi?id=81506
+
+        Reviewed by Julien Chaffraix.
+
+        Tests that xmlhttprequest.open does not assert in when called from onpagehide.
+
+        * fast/events/pagehide-xhr-open-expected.txt: Added.
+        * fast/events/pagehide-xhr-open.html: Added.
+        * fast/events/resources/subframe-xmlhttprequest.html: Added.
+        * platform/chromium/test_expectations.txt:
+
 2012-04-17  Mike Reed  <[email protected]>
 
         Need to rebaseline these images after skia 3695 lands

Added: trunk/LayoutTests/fast/events/pagehide-xhr-open-expected.txt (0 => 114374)


--- trunk/LayoutTests/fast/events/pagehide-xhr-open-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/events/pagehide-xhr-open-expected.txt	2012-04-17 14:02:07 UTC (rev 114374)
@@ -0,0 +1,12 @@
+CONSOLE MESSAGE: line 6: XMLHttpRequest in subframe-1 created
+CONSOLE MESSAGE: line 5: Loaded pagehide-timeout-go-back.html, going back
+CONSOLE MESSAGE: line 17: Restored page from page cache.
+Tries to trigger an assert in xmlhttprequest during pagehide.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+ 

Added: trunk/LayoutTests/fast/events/pagehide-xhr-open.html (0 => 114374)


--- trunk/LayoutTests/fast/events/pagehide-xhr-open.html	                        (rev 0)
+++ trunk/LayoutTests/fast/events/pagehide-xhr-open.html	2012-04-17 14:02:07 UTC (rev 114374)
@@ -0,0 +1,34 @@
+<html>
+<script src=""
+<body>
+
+<iframe src="" id="a-frame"></iframe> <iframe src="" id="b-frame"></iframe>
+
+<script type="text/_javascript_">
+description('Tries to trigger an assert in xmlhttprequest during pagehide.');
+
+if (window.layoutTestController)
+    layoutTestController.overridePreference('WebKitUsesPageCachePreferenceKey', 1);
+
+
+_onpageshow_ = function(event)
+{
+    if (event.persisted) {
+        console.log('Restored page from page cache.');
+        if (!window.wasFinishJSTestCalled)
+            finishJSTest();
+    }
+}
+
+_onload_ = function()
+{
+    setTimeout(function() {
+        location.href = '';
+    }, 0);
+}
+var successfullyParsed = true;
+var jsTestIsAsync = true;
+</script>
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/fast/events/resources/subframe-xmlhttprequest.html (0 => 114374)


--- trunk/LayoutTests/fast/events/resources/subframe-xmlhttprequest.html	                        (rev 0)
+++ trunk/LayoutTests/fast/events/resources/subframe-xmlhttprequest.html	2012-04-17 14:02:07 UTC (rev 114374)
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script>
+        function test() {
+            window.top.console.log('XMLHttpRequest in subframe-1 created');
+            var xhr = new XMLHttpRequest();
+            xhr.open('GET', 'about:empty', true);
+        }
+    </script>
+</head>
+<body>
+I am subframe-1
+</body>
+</html>

Modified: trunk/LayoutTests/platform/chromium/test_expectations.txt (114373 => 114374)


--- trunk/LayoutTests/platform/chromium/test_expectations.txt	2012-04-17 13:44:43 UTC (rev 114373)
+++ trunk/LayoutTests/platform/chromium/test_expectations.txt	2012-04-17 14:02:07 UTC (rev 114374)
@@ -511,6 +511,7 @@
 // on which renderer process to use for each of them.
 WONTFIX SKIP : fast/dom/Window/timer-resume-on-navigation-back.html = TIMEOUT FAIL
 WONTFIX SKIP : fast/events/pagehide-timeout.html = TIMEOUT
+WONTFIX SKIP : fast/events/pagehide-xhr-open.html = TIMEOUT
 WONTFIX SKIP : fast/events/pageshow-pagehide-on-back-cached-with-frames.html = TIMEOUT
 WONTFIX SKIP : fast/events/pageshow-pagehide-on-back-cached.html = TIMEOUT FAIL
 WONTFIX SKIP : fast/events/suspend-timers.html = TIMEOUT

Modified: trunk/Source/WebCore/ChangeLog (114373 => 114374)


--- trunk/Source/WebCore/ChangeLog	2012-04-17 13:44:43 UTC (rev 114373)
+++ trunk/Source/WebCore/ChangeLog	2012-04-17 14:02:07 UTC (rev 114374)
@@ -1,3 +1,39 @@
+2012-04-17  Allan Sandfeld Jensen  <[email protected]>
+
+        Asserts in XMLHttpRequestProgressEventThrottle
+        https://bugs.webkit.org/show_bug.cgi?id=81506
+
+        Reviewed by Julien Chaffraix.
+
+        The asserts were incorrectly triggered because suspending active DOM objects
+        (which suspends the XMLHttpRequestProgressEventThrottle) doesn't stop _javascript_
+        from running or suspend any running loader we may have. The previous code would
+        assume those 2 cases were impossible.
+
+        When XmlHttpRequest::open is called or data is received while the XmlHttpRequest object
+        is suspended the object may attempt to dispatch events. This patch defers these events
+        until the object is resumed.
+
+        Progress events are coalesced similar to normal throttling, and readystate-change events
+        are coalesced to avoid identical events emitted right after eachother.
+
+        On resume the events are dispatched after a timer to avoid interfering with 
+        ScriptExecutionContext which is iterating over suspended objects.
+
+        Test: fast/events/pagehide-xhr-open.html
+
+        * xml/XMLHttpRequestProgressEventThrottle.cpp:
+        (WebCore::XMLHttpRequestProgressEventThrottle::XMLHttpRequestProgressEventThrottle):
+        (WebCore::XMLHttpRequestProgressEventThrottle::dispatchProgressEvent):
+        (WebCore::XMLHttpRequestProgressEventThrottle::dispatchEvent):
+        (WebCore::XMLHttpRequestProgressEventThrottle::flushProgressEvent):
+        (WebCore::XMLHttpRequestProgressEventThrottle::dispatchDeferredEvents):
+        (WebCore::XMLHttpRequestProgressEventThrottle::fired):
+        (WebCore::XMLHttpRequestProgressEventThrottle::suspend):
+        (WebCore::XMLHttpRequestProgressEventThrottle::resume):
+        * xml/XMLHttpRequestProgressEventThrottle.h:
+        (XMLHttpRequestProgressEventThrottle):
+
 2012-04-16  Vsevolod Vlasov  <[email protected]>
 
         Web Inspector: [Chromium] Crash when inspecting empty IndexedDB object store.

Modified: trunk/Source/WebCore/xml/XMLHttpRequestProgressEventThrottle.cpp (114373 => 114374)


--- trunk/Source/WebCore/xml/XMLHttpRequestProgressEventThrottle.cpp	2012-04-17 13:44:43 UTC (rev 114373)
+++ trunk/Source/WebCore/xml/XMLHttpRequestProgressEventThrottle.cpp	2012-04-17 14:02:07 UTC (rev 114374)
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2010 Julien Chaffraix <[email protected]>
- * All right reserved.
+ * Copyright (C) 2010 Julien Chaffraix <[email protected]>  All right reserved.
+ * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -38,7 +38,8 @@
     : m_target(target)
     , m_loaded(0)
     , m_total(0)
-    , m_suspended(false)
+    , m_deferEvents(false)
+    , m_dispatchDeferredEventsTimer(this, &XMLHttpRequestProgressEventThrottle::dispatchDeferredEvents)
 {
     ASSERT(target);
 }
@@ -49,7 +50,12 @@
 
 void XMLHttpRequestProgressEventThrottle::dispatchProgressEvent(bool lengthComputable, unsigned long long loaded, unsigned long long total)
 {
-    ASSERT(!suspended());
+    if (m_deferEvents) {
+        // Only store the latest progress event while suspended.
+        m_deferredProgressEvent = XMLHttpRequestProgressEvent::create(eventNames().progressEvent, lengthComputable, loaded, total);
+        return;
+    }
+
     if (!isActive()) {
         // The timer is not active so the least frequent event for now is every byte.
         // Just go ahead and dispatch the event.
@@ -79,11 +85,15 @@
 
 void XMLHttpRequestProgressEventThrottle::dispatchEvent(PassRefPtr<Event> event)
 {
-    ASSERT(!suspended());
-    // We should not have any pending events from a previous resume.
-    ASSERT(!m_pausedEvent);
-
-    m_target->dispatchEvent(event);
+    ASSERT(event);
+    if (m_deferEvents) {
+        if (m_deferredEvents.size() > 1 && event->type() == eventNames().readystatechangeEvent && event->type() == m_deferredEvents.last()->type()) {
+            // Readystatechange events are state-less so avoid repeating two identical events in a row on resume.
+            return;
+        }
+        m_deferredEvents.append(event);
+    } else
+        m_target->dispatchEvent(event);
 }
 
 void XMLHttpRequestProgressEventThrottle::dispatchEventAndLoadEnd(PassRefPtr<Event> event)
@@ -96,6 +106,13 @@
 
 void XMLHttpRequestProgressEventThrottle::flushProgressEvent()
 {
+    if (m_deferEvents && m_deferredProgressEvent) {
+        // Move the progress event to the queue, to get it in the right order on resume.
+        m_deferredEvents.append(m_deferredProgressEvent);
+        m_deferredProgressEvent = 0;
+        return;
+    }
+
     if (!hasEventToDispatch())
         return;
 
@@ -109,21 +126,33 @@
     dispatchEvent(event);
 }
 
-void XMLHttpRequestProgressEventThrottle::dispatchPausedEvent()
+void XMLHttpRequestProgressEventThrottle::dispatchDeferredEvents(Timer<XMLHttpRequestProgressEventThrottle>* timer)
 {
-    ASSERT(!suspended());
-    if (!m_pausedEvent)
-        return;
+    ASSERT_UNUSED(timer, timer == &m_dispatchDeferredEventsTimer);
+    ASSERT(m_deferEvents);
+    m_deferEvents = false;
 
-    dispatchEvent(m_pausedEvent);
-    m_pausedEvent = 0;
+    // Take over the deferred events before dispatching them which can potentially add more.
+    Vector<RefPtr<Event> > deferredEvents;
+    m_deferredEvents.swap(deferredEvents);
+
+    RefPtr<Event> deferredProgressEvent = m_deferredProgressEvent;
+    m_deferredProgressEvent = 0;
+
+    Vector<RefPtr<Event> >::const_iterator it = deferredEvents.begin();
+    const Vector<RefPtr<Event> >::const_iterator end = deferredEvents.end();
+    for (; it != end; ++it)
+        dispatchEvent(*it);
+
+    // The progress event will be in the m_deferredEvents vector if the load was finished while suspended.
+    // If not, just send the most up-to-date progress on resume.
+    if (deferredProgressEvent)
+        dispatchEvent(deferredProgressEvent);
 }
 
 void XMLHttpRequestProgressEventThrottle::fired()
 {
     ASSERT(isActive());
-    ASSERT(!suspended());
-    ASSERT(!m_pausedEvent);
     if (!hasEventToDispatch()) {
         // No progress event was queued since the previous dispatch, we can safely stop the timer.
         stop();
@@ -142,13 +171,22 @@
 
 void XMLHttpRequestProgressEventThrottle::suspend()
 {
-    ASSERT(!m_pausedEvent);
+    // If re-suspended before deferred events have been dispatched, just stop the dispatch
+    // and continue the last suspend.
+    if (m_dispatchDeferredEventsTimer.isActive()) {
+        ASSERT(m_deferEvents);
+        m_dispatchDeferredEventsTimer.stop();
+        return;
+    }
+    ASSERT(!m_deferredProgressEvent);
+    ASSERT(m_deferredEvents.isEmpty());
+    ASSERT(!m_deferEvents);
 
-    m_suspended = true;
+    m_deferEvents = true;
     // If we have a progress event waiting to be dispatched,
-    // just queue it.
+    // just defer it.
     if (hasEventToDispatch()) {
-        m_pausedEvent = XMLHttpRequestProgressEvent::create(eventNames().progressEvent, m_lengthComputable, m_loaded, m_total);
+        m_deferredProgressEvent = XMLHttpRequestProgressEvent::create(eventNames().progressEvent, m_lengthComputable, m_loaded, m_total);
         m_total = 0;
         m_loaded = 0;
     }
@@ -160,8 +198,11 @@
     ASSERT(!m_loaded);
     ASSERT(!m_total);
 
-    m_suspended = false;
-    dispatchPausedEvent();
+    // Do not dispatch events inline here, since ScriptExecutionContext is iterating over
+    // the list of active DOM objects to resume them, and any activated JS event-handler
+    // could insert new active DOM objects to the list.
+    // m_deferEvents is kept true until all deferred events have been dispatched.
+    m_dispatchDeferredEventsTimer.startOneShot(0);
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/xml/XMLHttpRequestProgressEventThrottle.h (114373 => 114374)


--- trunk/Source/WebCore/xml/XMLHttpRequestProgressEventThrottle.h	2012-04-17 13:44:43 UTC (rev 114373)
+++ trunk/Source/WebCore/xml/XMLHttpRequestProgressEventThrottle.h	2012-04-17 14:02:07 UTC (rev 114374)
@@ -56,13 +56,11 @@
     void suspend();
     void resume();
 
-    bool suspended() const { return m_suspended; }
-
 private:
     static const double minimumProgressEventDispatchingIntervalInSeconds;
 
     virtual void fired();
-    void dispatchPausedEvent();
+    void dispatchDeferredEvents(Timer<XMLHttpRequestProgressEventThrottle>*);
     void flushProgressEvent();
 
     bool hasEventToDispatch() const;
@@ -74,8 +72,10 @@
     unsigned long long m_loaded;
     unsigned long long m_total;
 
-    bool m_suspended;
-    RefPtr<Event> m_pausedEvent;
+    bool m_deferEvents;
+    RefPtr<Event> m_deferredProgressEvent;
+    Vector<RefPtr<Event> > m_deferredEvents;
+    Timer<XMLHttpRequestProgressEventThrottle> m_dispatchDeferredEventsTimer;
 };
 
 } // namespace WebCore
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to