[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    KDE/kdelibs/khtml/ecma
From:       Maks Orlovich <maksim () kde ! org>
Date:       2008-01-26 21:06:35
Message-ID: 1201381595.656870.5781.nullmailer () svn ! kde ! org
[Download RAW message or body]

SVN commit 766887 by orlovich:

Forwardport:
Fix imprecision of timer events w/long-executing code.
It was counting all code as potential pause, while the intent is
to do it only for alerts and such. Hence, this makes the pausing explicit,
and also preventing reentrancy. Pauses are on for alerts & co
(and also used by the debugger).

Things are still off by ~1.5-2ms, though.

 M  +2 -0      debugger/debugwindow.cpp  
 M  +47 -13    kjs_window.cpp  
 M  +29 -1     kjs_window.h  


--- trunk/KDE/kdelibs/khtml/ecma/debugger/debugwindow.cpp #766886:766887
@@ -69,6 +69,7 @@
 #include <kjs/interpreter.h>
 #include <kjs/value.h>
 #include <kjs/context.h>
+#include <ecma/kjs_window.h>
 
 #include <QVBoxLayout>
 #include <QSplitter>
@@ -608,6 +609,7 @@
                 KStringHandler::rsqueeze(url, 80), lineNo, exceptionMsg);
 
     KJSErrorDialog dlg(this /*dlgParent*/, msg, true);
+    TimerPauser pause(exec); // don't let any timers fire while we're doing this!
     ++m_modalLevel;
     dlg.exec();
     --m_modalLevel;
--- trunk/KDE/kdelibs/khtml/ecma/kjs_window.cpp #766886:766887
@@ -1806,7 +1806,8 @@
   caption += "JavaScript"; // TODO: i18n
   // functions that work everywhere
   switch(id) {
-  case Window::Alert:
+  case Window::Alert: {
+    TimerPauser pause(exec);
     if (!widget->dialogsAllowed())
       return jsUndefined();
     if ( part && part->xmlDocImpl() )
@@ -1815,7 +1816,9 @@
       emit part->browserExtension()->requestFocus(part);
     KMessageBox::error(widget, Qt::convertFromPlainText(str, Qt::WhiteSpaceNormal), \
caption);  return jsUndefined();
-  case Window::Confirm:
+  }
+  case Window::Confirm: {
+    TimerPauser pause(exec);
     if (!widget->dialogsAllowed())
       return jsUndefined();
     if ( part && part->xmlDocImpl() )
@@ -1824,7 +1827,9 @@
       emit part->browserExtension()->requestFocus(part);
     return jsBoolean((KMessageBox::warningYesNo(widget, \
                Qt::convertFromPlainText(str), caption,
                                                 KStandardGuiItem::ok(), \
                KStandardGuiItem::cancel()) == KMessageBox::Yes));
-  case Window::Prompt:
+  }
+  case Window::Prompt: {
+    TimerPauser pause(exec);
 #ifndef KONQ_EMBEDDED
     if (!widget->dialogsAllowed())
       return jsUndefined();
@@ -1848,6 +1853,7 @@
 #else
     return jsUndefined();
 #endif
+  }
   case Window::GetComputedStyle:  {
        if ( !part || !part->xmlDocImpl() )
          return jsUndefined();
@@ -2203,7 +2209,7 @@
   else
       connect( parent->m_frame, SIGNAL( destroyed() ),
                this, SLOT( parentDestroyed() ) );
-  pausedTime = 0;
+  pauseLevel  = 0;
   lastTimerId = 0;
   currentlyDispatching = false;
 }
@@ -2223,11 +2229,40 @@
   scheduledActions.clear();
 }
 
+void WindowQObject::pauseTimers()
+{
+    ++pauseLevel;
+    if (pauseLevel == 1)
+        pauseStart = DateTimeMS::now();
+}
+
+void WindowQObject::resumeTimers()
+{
+    --pauseLevel;
+    if (pauseLevel == 0) {
+        // Adjust all timers by the delay length, making sure there is a minimum
+        // margin from current time, however, so we don't go stampeding off if
+        // there is some unwanted recursion, etc.
+        DateTimeMS curTime          = DateTimeMS::now();
+        DateTimeMS earliestDispatch = curTime.addMSecs(5);
+        int delay = pauseStart.msecsTo(curTime);
+        foreach (ScheduledAction *action, scheduledActions) {
+            action->nextTime = action->nextTime.addMSecs(delay);
+            if (earliestDispatch > action->nextTime)
+                action->nextTime = earliestDispatch;
+        }
+
+        // Dispatch any timers that may have been ignored if ::timerEvent fell in \
the middle +        // of a pause..
+        timerEvent(0);
+    }
+}
+
 int WindowQObject::installTimeout(const Identifier &handler, int t, bool singleShot)
 {
   int id = ++lastTimerId;
   if (t < 10) t = 10;
-  DateTimeMS nextTime = DateTimeMS::now().addMSecs(-pausedTime + t);
+  DateTimeMS nextTime = DateTimeMS::now().addMSecs(t);
 
   ScheduledAction *action = new \
ScheduledAction(handler.qstring(),nextTime,t,singleShot,id);  \
scheduledActions.append(action); @@ -2243,7 +2278,7 @@
   int id = ++lastTimerId;
   if (t < 10) t = 10;
 
-  DateTimeMS nextTime = DateTimeMS::now().addMSecs(-pausedTime + t);
+  DateTimeMS nextTime = DateTimeMS::now().addMSecs(t);
   ScheduledAction *action = new \
ScheduledAction(objFunc,args,nextTime,t,singleShot,id);  \
scheduledActions.append(action);  setNextTimer();
@@ -2284,18 +2319,20 @@
   if (scheduledActions.isEmpty())
     return;
 
+  if (pauseLevel)
+    return;
+
   currentlyDispatching = true;
 
 
-  DateTimeMS currentActual = DateTimeMS::now();
-  DateTimeMS currentAdjusted = currentActual.addMSecs(-pausedTime);
+  DateTimeMS current = DateTimeMS::now();
 
   // Work out which actions are to be executed. We take a separate copy of
   // this list since the main one may be modified during action execution
   QList<ScheduledAction*> toExecute;
   foreach (ScheduledAction *action, scheduledActions)
   {
-    if (currentAdjusted >= action->nextTime)
+    if (current >= action->nextTime)
       toExecute.append(action);
   }
 
@@ -2324,8 +2361,6 @@
       action->nextTime = action->nextTime.addMSecs(action->interval);
   }
 
-  pausedTime += currentActual.msecsTo(DateTimeMS::now());
-
   currentlyDispatching = false;
 
   // Work out when next event is to occur
@@ -2408,8 +2443,7 @@
   }
 
 
-  DateTimeMS nextTimeActual = nextTime.addMSecs(pausedTime);
-  int nextInterval = DateTimeMS::now().msecsTo(nextTimeActual);
+  int nextInterval = DateTimeMS::now().msecsTo(nextTime);
   if (nextInterval < 0)
     nextInterval = 0;
   timerIds.append(startTimer(nextInterval));
--- trunk/KDE/kdelibs/khtml/ecma/kjs_window.h #766886:766887
@@ -271,6 +271,9 @@
     void clearTimeout(int timerId);
     void mark();
     bool hasTimers() const;
+
+    void pauseTimers();
+    void resumeTimers();
   public Q_SLOTS:
     void timeoutClose();
   protected Q_SLOTS:
@@ -282,12 +285,37 @@
   private:
     Window *parent;
     QList<ScheduledAction*> scheduledActions;
-    int pausedTime;
+
+    /**
+     We need to pause timers when alerts are up; so we keep track
+     of recursion of that and the delay at topmost level.
+     */
+    int pauseLevel;
+    DateTimeMS pauseStart;
+
     int lastTimerId;
     QList<int> timerIds;
     bool currentlyDispatching;
   };
 
+  /**
+   * Helper for pausing/resuming timers
+   */
+  class TimerPauser
+  {
+  public:
+    TimerPauser(ExecState* exec) {
+      win = Window::retrieveActive(exec);
+      win->winq->pauseTimers();
+    }
+
+    ~TimerPauser() {
+      win->winq->resumeTimers();
+    }
+  private:
+    Window* win;
+  };
+
   class Location : public JSObject {
   public:
     ~Location();


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic