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 8f06fd8ea08fbd26abf6668270c150b76d23b9b4..26a3a50564b26d6ab3c313ec94f7f68c53c997c8 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 4e15f612f99d490583ddec2ce62fc1ea57d423be..f23c2e632b75ac1fce0d143a395de55a91549c12 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 c9a7f406cb44e6bed4db9f30a330187bda7b36fd..51ca8b9d538888a674d3cfc72351e09632921803 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 50b32bc84bf3a4a128ce4c17df3c5f003e9e7f4d..e85d40e48982dcc63dcd06cc456630c384e5a267 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();
         }
     }
 }