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

List:       kde-commits
Subject:    branches/KDE/4.0/kdelibs/khtml/ecma
From:       Maks Orlovich <maksim () kde ! org>
Date:       2008-01-26 20:46:50
Message-ID: 1201380410.178254.5066.nullmailer () svn ! kde ! org
[Download RAW message or body]

SVN commit 766880 by orlovich:

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  +11 -9     debugger/debugwindow.cpp  
 M  +48 -14    kjs_window.cpp  
 M  +32 -4     kjs_window.h  


--- branches/KDE/4.0/kdelibs/khtml/ecma/debugger/debugwindow.cpp #766879:766880
@@ -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>
@@ -220,7 +221,7 @@
 
     menuBar()->insertItem("&Debug", debugMenu);
 */
-    // ### KDE4.1: proper debug menu. Don't want to 
+    // ### KDE4.1: proper debug menu. Don't want to
     // add strings right now.
 }
 
@@ -450,7 +451,7 @@
 
 void DebugWindow::clearInterpreter(KJS::Interpreter* interp)
 {
-    // We may get a clear when we weren't even attached, if the 
+    // We may get a clear when we weren't even attached, if the
     // interpreter gets created but nothing gets run in it.
     // Be careful not to insert a bogus null into contexts map then
     InterpreterContext* ctx = m_contexts.value(interp);
@@ -546,7 +547,7 @@
 
     // Since we purposefully bypass toString, we need to figure out
     // string serialization ourselves.
-    //### might be easier to export class info for ErrorInstance --- 
+    //### might be easier to export class info for ErrorInstance ---
 
     JSObject* valueObj = exceptionObj->getObject();
     JSValue*  protoObj = valueObj ? valueObj->prototype() : 0;
@@ -568,12 +569,12 @@
     {
         exception = true;
     }
-    
+
     if (!exception)
         return exceptionMsg;
-    
+
     // Clear exceptions temporarily so we can get/call a few things.
-    // We memorize the old exception first, of course. Note that 
+    // We memorize the old exception first, of course. Note that
     // This is not always the same as exceptionObj since we may be
     //  asked to translate a non-active exception
     JSValue* oldExceptionObj = exec->exception();
@@ -586,14 +587,14 @@
     {
         JSValue* lineValue = valueObj->get(exec, "line");
         JSValue* urlValue  = valueObj->get(exec, "sourceURL");
-        
+
         int      line = lineValue->toNumber(exec);
         QString  url  = urlValue->toString(exec).qstring();
         exceptionMsg = i18n("Parse error at %1 line %2", url, line - 1);
     }
     else
     {
-        // ### it's still not 100% safe to call toString here, even on 
+        // ### it's still not 100% safe to call toString here, even on
         // native exception objects, since someone might have changed the toString \
                property
         // of the exception prototype, but I'll punt on this case for now.
         exceptionMsg = exceptionObj->toString(exec).qstring();
@@ -629,6 +630,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;
@@ -766,7 +768,7 @@
 
     // Also, if we exit from an eval context, we probably want to
     // clear the corresponding document, unless it's open.
-    // We can not do it safely if there are any functions declared, 
+    // We can not do it safely if there are any functions declared,
     // however, since they can escape.
     if (exec->context()->codeType() == EvalCode)
     {
--- branches/KDE/4.0/kdelibs/khtml/ecma/kjs_window.cpp #766879:766880
@@ -516,7 +516,7 @@
 #ifdef KJS_VERBOSE
   kDebug(6070) << "Window("<<this<<")::getOwnPropertySlot " << \
propertyName.qstring();  #endif
-  
+
   // we want only limited operations on a closed window
   if (m_frame.isNull() || m_frame->m_part.isNull()) {
     const HashEntry* entry = Lookup::findEntry(&WindowTable, propertyName);
@@ -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));
--- branches/KDE/4.0/kdelibs/khtml/ecma/kjs_window.h #766879:766880
@@ -127,7 +127,7 @@
     Location *location() const;
     JSObject* frames( ExecState* exec ) const;
     JSEventListener *getJSEventListener(JSValue* val, bool html = false);
-    JSLazyEventListener *getJSLazyEventListener(const QString &code, const QString& \
sourceUrl, int lineNo,  +    JSLazyEventListener *getJSLazyEventListener(const \
                QString &code, const QString& sourceUrl, int lineNo,
                                                 const QString &name, DOM::NodeImpl* \
node);  void clear( ExecState *exec );
     virtual UString toString(ExecState *exec) const;
@@ -230,9 +230,9 @@
     DateTimeMS addMSecs(int s) const;
     bool operator >(const DateTimeMS &other) const;
     bool operator >=(const DateTimeMS &other) const;
-    
+
     int msecsTo(const DateTimeMS &other) const;
-    
+
     static DateTimeMS now();
   };
 
@@ -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