Defer ScriptExecutionContext::Task's in Document when page loading is deferred.
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 7 Dec 2011 23:41:21 +0000 (23:41 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 7 Dec 2011 23:41:21 +0000 (23:41 +0000)
Schedule them with timer when page loading is resumed. The tasks will be performed
in the original order. This fixes the problem that database callbacks could be missed
when page loading was deferred.
https://bugs.webkit.org/show_bug.cgi?id=49401

Patch by Yong Li <yoli@rim.com> on 2011-12-07
Reviewed by Darin Adler.

Manual test added: ManualTests/database-callback-deferred.html.

* dom/Document.cpp:
(WebCore::Document::Document):
(WebCore::Document::didReceiveTask):
(WebCore::Document::postTask):
(WebCore::Document::pendingTasksTimerFired):
(WebCore::Document::suspendScheduledTasks):
(WebCore::Document::resumeScheduledTasks):
* dom/Document.h:
* page/PageGroupLoadDeferrer.cpp:
(WebCore::PageGroupLoadDeferrer::PageGroupLoadDeferrer):
(WebCore::PageGroupLoadDeferrer::~PageGroupLoadDeferrer):

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

ManualTests/database-callback-deferred.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/page/PageGroupLoadDeferrer.cpp

diff --git a/ManualTests/database-callback-deferred.html b/ManualTests/database-callback-deferred.html
new file mode 100644 (file)
index 0000000..cf3d03a
--- /dev/null
@@ -0,0 +1,17 @@
+<html>
+<script>
+function test() {
+    function transactionCallback()
+    {
+        document.getElementById("result").innerHTML = "Callback was called. Test passed";
+    }
+    var db = openDatabase("DatabaseCallbackDeferred", "1.0", "", 1);
+    db.transaction(function(tx) { tx.executeSql("CREATE TABLE IF NOT EXISTS CallbackDeferredTest (randomData)", []); }, transactionCallback, transactionCallback);
+
+    alert("Wait for a few seconds and close it");
+}
+</script>
+<body onload="test()">
+<p id="result">Wait...</p>
+</body>
+</html>
index 8f06fd8..26a3a50 100755 (executable)
@@ -1,3 +1,28 @@
+2011-12-07  Yong Li  <yoli@rim.com>
+
+        Defer ScriptExecutionContext::Task's in Document when page loading is deferred.
+        Schedule them with timer when page loading is resumed. The tasks will be performed
+        in the original order. This fixes the problem that database callbacks could be missed
+        when page loading was deferred.
+        https://bugs.webkit.org/show_bug.cgi?id=49401
+
+        Reviewed by Darin Adler.
+
+        Manual test added: ManualTests/database-callback-deferred.html. 
+
+        * dom/Document.cpp:
+        (WebCore::Document::Document):
+        (WebCore::Document::didReceiveTask):
+        (WebCore::Document::postTask):
+        (WebCore::Document::pendingTasksTimerFired):
+        (WebCore::Document::suspendScheduledTasks):
+        (WebCore::Document::resumeScheduledTasks):
+        * dom/Document.h:
+        * page/PageGroupLoadDeferrer.cpp:
+        (WebCore::PageGroupLoadDeferrer::PageGroupLoadDeferrer):
+        (WebCore::PageGroupLoadDeferrer::~PageGroupLoadDeferrer):
+
 2011-12-07  Andreas Kling  <kling@webkit.org>
 
         RenderObject::style(): Inline early-return condition.
index 4e15f61..f23c2e6 100644 (file)
@@ -7,6 +7,7 @@
  * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
  * Copyright (C) 2008, 2009, 2011 Google Inc. All rights reserved.
  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) Research In Motion Limited 2010-2011. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -429,6 +430,7 @@ Document::Document(Frame* frame, const KURL& url, bool isXHTML, bool isHTML)
     , m_writeRecursionIsTooDeep(false)
     , m_writeRecursionDepth(0)
     , m_wheelEventHandlerCount(0)
+    , m_pendingTasksTimer(this, &Document::pendingTasksTimerFired)
 {
     m_document = this;
 
@@ -4704,22 +4706,59 @@ public:
     OwnPtr<ScriptExecutionContext::Task> task;
 };
 
-static void performTask(void* ctx)
+void Document::didReceiveTask(void* untypedContext)
 {
     ASSERT(isMainThread());
 
-    PerformTaskContext* context = reinterpret_cast<PerformTaskContext*>(ctx);
+    OwnPtr<PerformTaskContext> context = adoptPtr(static_cast<PerformTaskContext*>(untypedContext));
     ASSERT(context);
 
-    if (Document* document = context->documentReference->document())
-        context->task->performTask(document);
+    Document* document = context->documentReference->document();
+    if (!document)
+        return;
+
+    Page* page = document->page();
+    if ((page && page->defersLoading()) || !document->m_pendingTasks.isEmpty()) {
+        document->m_pendingTasks.append(context->task.release());
+        return;
+    }
 
-    delete context;
+    context->task->performTask(document);
 }
 
 void Document::postTask(PassOwnPtr<Task> task)
 {
-    callOnMainThread(performTask, new PerformTaskContext(m_weakReference, task));
+    callOnMainThread(didReceiveTask, new PerformTaskContext(m_weakReference, task));
+}
+
+void Document::pendingTasksTimerFired(Timer<Document>*)
+{
+    while (!m_pendingTasks.isEmpty()) {
+        OwnPtr<Task> task = m_pendingTasks[0].release();
+        m_pendingTasks.remove(0);
+        task->performTask(this);
+    }
+}
+
+void Document::suspendScheduledTasks()
+{
+    suspendScriptedAnimationControllerCallbacks();
+    suspendActiveDOMObjects(ActiveDOMObject::WillShowDialog);
+    scriptRunner()->suspend();
+    m_pendingTasksTimer.stop();
+    if (m_parser)
+        m_parser->suspendScheduledTasks();
+}
+
+void Document::resumeScheduledTasks()
+{
+    if (m_parser)
+        m_parser->resumeScheduledTasks();
+    if (!m_pendingTasks.isEmpty())
+        m_pendingTasksTimer.startOneShot(0);
+    scriptRunner()->resume();
+    resumeActiveDOMObjects();
+    resumeScriptedAnimationControllerCallbacks();
 }
 
 void Document::suspendScriptedAnimationControllerCallbacks()
index c9a7f40..51ca8b9 100644 (file)
@@ -1114,7 +1114,7 @@ public:
     unsigned wheelEventHandlerCount() const { return m_wheelEventHandlerCount; }
     void didAddWheelEventHandler();
     void didRemoveWheelEventHandler();
-    
+
     bool visualUpdatesAllowed() const;
 
 #if ENABLE(MICRODATA)
@@ -1124,6 +1124,9 @@ public:
     
     bool isInDocumentWrite() { return m_writeRecursionDepth > 0; }
 
+    void suspendScheduledTasks();
+    void resumeScheduledTasks();
+
 protected:
     Document(Frame*, const KURL&, bool isXHTML, bool isHTML);
 
@@ -1171,6 +1174,10 @@ private:
 
     void loadEventDelayTimerFired(Timer<Document>*);
 
+    void pendingTasksTimerFired(Timer<Document>*);
+
+    static void didReceiveTask(void*);
+
 #if ENABLE(PAGE_VISIBILITY_API)
     PageVisibilityState visibilityState() const;
 #endif
@@ -1433,6 +1440,9 @@ private:
 #if ENABLE(REQUEST_ANIMATION_FRAME)
     OwnPtr<ScriptedAnimationController> m_scriptedAnimationController;
 #endif
+
+    Timer<Document> m_pendingTasksTimer;
+    Vector<OwnPtr<Task> > m_pendingTasks;
 };
 
 // Put these methods here, because they require the Document definition, but we really want to inline them.
index 50b32bc..e85d40e 100644 (file)
@@ -48,13 +48,8 @@ PageGroupLoadDeferrer::PageGroupLoadDeferrer(Page* page, bool deferSelf)
                 // windows or sheets, which is exactly when PageGroupLoadDeferrer is used.
                 // NOTE: if PageGroupLoadDeferrer is ever used for tasks other than showing a modal window or sheet,
                 // the constructor will need to take a ActiveDOMObject::ReasonForSuspension.
-                for (Frame* frame = otherPage->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
-                    frame->document()->suspendScriptedAnimationControllerCallbacks();
-                    frame->document()->suspendActiveDOMObjects(ActiveDOMObject::WillShowDialog);
-                    frame->document()->scriptRunner()->suspend();
-                    if (DocumentParser* parser = frame->document()->parser())
-                        parser->suspendScheduledTasks();
-                }
+                for (Frame* frame = otherPage->mainFrame(); frame; frame = frame->tree()->traverseNext())
+                    frame->document()->suspendScheduledTasks();
             }
         }
     }
@@ -71,13 +66,8 @@ PageGroupLoadDeferrer::~PageGroupLoadDeferrer()
         if (Page* page = m_deferredFrames[i]->page()) {
             page->setDefersLoading(false);
 
-            for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
-                frame->document()->resumeActiveDOMObjects();
-                frame->document()->resumeScriptedAnimationControllerCallbacks();
-                frame->document()->scriptRunner()->resume();
-                if (DocumentParser* parser = frame->document()->parser())
-                    parser->resumeScheduledTasks();
-            }
+            for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext())
+                frame->document()->resumeScheduledTasks();
         }
     }
 }