Bug 68721

Summary: [test case] a fixed (with css) text/html <object> "jumps" when scrolling
Product: [Applications] konqueror Reporter: Panther Wyvern <panther>
Component: khtml rendererAssignee: Konqueror Developers <konq-bugs>
Status: RESOLVED FIXED    
Severity: normal CC: zyzstar
Priority: NOR    
Version: unspecified   
Target Milestone: ---   
Platform: openSUSE   
OS: Linux   
Latest Commit: Version Fixed In:
Attachments: example of jumping elements
small html file loaded into main document via <object>
small html file loaded into main document via <object>
example of jumping elements

Description Panther Wyvern 2003-11-21 10:09:21 UTC
Version:            (using KDE KDE 3.1.1)
Installed from:    SuSE RPMs
OS:          Linux

I have a css section marked in the following way:

----------------------
#rmenu {
	position: absolute;
	right: 0px;
	top: 230px;
	bottom: 0px;
	width: 80px;
	text-align: center;
	color: #CCEA06;
	}

body> #rmenu {
	position: fixed;
	top: auto;
	}
----------------------

So you can see, the position of <div id="rmenu"> is fixed.  If I want to put a an html <object> in that <div> (such as rmenu.html), it is also fixed.  However, when the <object> is there and you scroll down the page, the object kind of hops.  It stays fixed, but it jiggles up and down, as opposed to if I put the information that's in rmenu.html inside the main document (when it doesn't hop, jiggle or jump) instead of using the <object> tag.

I will create an example if asked.
Comment 1 Pablo Pita Leira 2003-11-23 13:27:53 UTC
It would be good to have an example of the bug to reproduce or to see it it is fixed in the new versions. 
Comment 2 Panther Wyvern 2003-11-24 05:48:11 UTC
I just downloaded and installed the SuSE builds of KDE 3.1.4.  Konqueror acts the same.

I have slapped together a quick example and put it up here:

http://www.mylinuxisp.com/~panther/konqueror.html

All of the css attributes are in the main document.  I have four fixed elements - two are contained in the main document (one with image, one without) and two are inline object frames from smaller html files.  Scroll through the page and see how the object frames jump because of their fixed position while the other two do not.  A funny thing I just noticed is that if you scroll the page with the mouse wheel, arrow keys, scrollbar buttons or the scrollbar itself, the object frames jump while the paragraphs within the main document stay still.  However, if you scroll using PageUp/PageDown, the object frames remain still while the paragraphs within the main document jump.  Try it.
Comment 3 Stephan Kulow 2003-11-24 09:29:39 UTC
Can you perhaps reduce it a bit more and attach it here? KDE 3.2 will behave
the same :)
Comment 4 Panther Wyvern 2003-11-24 15:45:50 UTC
Created attachment 3378 [details]
example of jumping elements

This is the first time I'm attaching here... I hope I'm not screwing this up.
:)
Comment 5 Panther Wyvern 2003-11-24 15:47:43 UTC
Created attachment 3379 [details]
small html file loaded into main document via <object>
Comment 6 Panther Wyvern 2003-11-24 15:48:21 UTC
Created attachment 3380 [details]
small html file loaded into main document via <object>
Comment 7 Panther Wyvern 2003-11-24 15:55:45 UTC
Created attachment 3381 [details]
example of jumping elements

Okay, I messed up before but I *think* I have a better grasp on it now. :)
Comment 8 Pablo Pita Leira 2003-11-24 23:15:21 UTC
Well, I can confirm this with KDE CVS 3.1.93. When scrolling, the <object>s rendering is very unstable; they certainly jump a lot, and sometimes, even vanish. With Mozilla Firebird, the objects remain in their fixed position, and they are rendered in subwindows that also get scrollbars. 

Just in case this could be a duplicate, this bug is also related to <object> rendering : http://bugs.kde.org/show_bug.cgi?id=61286.
Comment 9 Florian Ehrenthal 2004-09-15 23:31:18 UTC
i am experiencing the same problem.
position fixed and either a <object> or an <iframe> result in the described manner.
i am using kde 3.3 so this is still valid :(
Comment 10 Alexander Shaduri 2004-10-02 20:11:24 UTC
Same here with KDE 3.3.
I put up an example page at
http://members.lycos.co.uk/alexv6/test.html .
the iframe element there jumps a lot.
That would be torelable if it didn't vanish sometimes.
Comment 11 Florian Ehrenthal 2005-11-22 17:37:04 UTC
updating, still valid in kde 3.4.3
Comment 12 Thiago Macieira 2006-04-22 16:07:15 UTC
*** Bug 125910 has been marked as a duplicate of this bug. ***
Comment 13 Germain Garand 2007-02-05 15:18:43 UTC
SVN commit 630479 by ggarand:

A simple "page scaling" zoom à la Opera. 
For now, it replaces the font scaling action, as the method names used for it 
in KHTML part are better suited to a real zoom (e.g. setZoomFactor).
Need to reintroduce pure font-scaling under another name..

Also introduce an alternate, non-blitting painting mode for being able to do 
fixed positioning again.
QScrollArea's quite a bit on the terse side, so there was no such thing ;(
Should fix widget jittering issues as a bonus.


BUG: 42029, 68721



 M  +2 -2      css/cssstyleselector.cpp  
 M  +9 -4      khtml_part.cpp  
 M  +111 -18   khtmlview.cpp  
 M  +20 -1     khtmlview.h  
 M  +15 -8     rendering/render_canvas.cpp  
 M  +1 -1      rendering/render_replaced.cpp  
 M  +2 -0      xml/dom_nodeimpl.cpp  


--- trunk/KDE/kdelibs/khtml/css/cssstyleselector.cpp #630478:630479
@@ -749,7 +749,7 @@
     // FIXME: We only need to invalidate the fixed regions when scrolling.  It's total overkill to
     // prevent the entire view from blitting on a scroll.
     if (style->hasFixedBackgroundImage() && view)
-        view->useSlowRepaints();
+        view->setHasStaticBackground();
 }
 
 unsigned int CSSStyleSelector::addInlineDeclarations(DOM::ElementImpl* e,
@@ -2414,7 +2414,7 @@
             p = ABSOLUTE; break;
         case CSS_VAL_FIXED:
             {
-                view->useSlowRepaints();
+                view->setHasStaticBackground();
                 p = FIXED;
                 break;
             }
--- trunk/KDE/kdelibs/khtml/khtml_part.cpp #630478:630479
@@ -5754,11 +5754,16 @@
   if (d->m_zoomFactor == percent) return;
   d->m_zoomFactor = percent;
 
-  if(d->m_doc) {
+  if(d->m_view) {
       QApplication::setOverrideCursor( Qt::WaitCursor );
-    if (d->m_doc->styleSelector())
-      d->m_doc->styleSelector()->computeFontSizes(d->m_doc->paintDeviceMetrics(), d->m_zoomFactor);
-    d->m_doc->recalcStyle( NodeImpl::Force );
+
+// ### make the increasing/decreasing of font size a separate setting
+//
+//    if (d->m_doc->styleSelector())
+//      d->m_doc->styleSelector()->computeFontSizes(d->m_doc->paintDeviceMetrics(), d->m_zoomFactor);
+//    d->m_doc->recalcStyle( NodeImpl::Force );
+
+    d->m_view->setZoomLevel( d->m_zoomFactor );
     QApplication::restoreOverrideCursor();
   }
 
--- trunk/KDE/kdelibs/khtml/khtmlview.cpp #630478:630479
@@ -186,10 +186,11 @@
 	    oldUnderMouse->deref();
         oldUnderMouse = 0;
         linkPressed = false;
-        useSlowRepaints = false;
+        staticWidget = false;
 	tabMovePending = false;
 	lastTabbingDirection = true;
 	pseudoFocusNode = PFNone;
+	zoomLevel = 100;
 #ifndef KHTML_NO_SCROLLBARS
         //We don't turn off the toolbars here
 	//since if the user turns them
@@ -287,6 +288,8 @@
         scrollSuspended = false;
     }
 
+    bool haveZoom() const { return zoomLevel != 100; }
+
 #ifndef KHTML_NO_CARET
     /** this function returns an instance of the caret view context. If none
      * exists, it will be instantiated.
@@ -324,9 +327,10 @@
     Qt::ScrollBarPolicy hpolicy;
     bool prevScrollbarVisible:1;
     bool linkPressed:1;
-    bool useSlowRepaints:1;
+    bool staticWidget:1;
     bool ignoreWheelEvents:1;
 
+    int zoomLevel;
     int borderX, borderY;
     KSimpleConfig *formCompletions;
 
@@ -510,9 +514,13 @@
     QSize s = viewport()->size();
     resizeContents(s.width(), s.height());
 
-    // ### we'll enable redirection of khtmlview here
-    //     when event and painting issues have been thoroughly worked out
-    // m_kwp->setIsRedirected(true);
+    // ### we'll enable redirection of khtmlview
+    //     when event issues have been thoroughly worked out
+
+    bool redirect = false; // m_part->parentPart() && !isFrame() ...
+
+    m_kwp->setIsRedirected( redirect );
+    d->staticWidget = redirect;
 }
 
 void KHTMLView::clear()
@@ -640,6 +648,7 @@
 
 void KHTMLView::updateContents(int x, int y, int w, int h)
 {
+    applyTransforms(x, y, w, h);
     widget()->update(x, y, w, h);
 }
 
@@ -650,6 +659,7 @@
 
 void KHTMLView::repaintContents(int x, int y, int w, int h)
 {
+    applyTransforms(x, y, w, h);
     widget()->repaint(x, y, w, h);
 }
 
@@ -658,6 +668,42 @@
     repaintContents( r.x(), r.y(), r.width(), r.height() );
 }
 
+void KHTMLView::applyTransforms( int& x, int& y, int& w, int& h) const
+{
+     if (d->staticWidget) {
+        x -= contentsX();
+        y -= contentsY();
+    }
+    if (d->haveZoom()) {
+        const int z = d->zoomLevel;
+        x = x*z/100;
+        y = y*z/100;
+        w = w*z/100;
+        h = h*z/100;
+    }
+}
+
+void KHTMLView::revertTransforms( int& x, int& y, int& w, int& h) const
+{
+     if (d->staticWidget) {
+        x += contentsX();
+        y += contentsY();
+    }
+    if (d->haveZoom()) {
+        const int z = d->zoomLevel;
+        x = x*100/z;
+        y = y*100/z;
+        w = w*100/z;
+        h = h*100/z;
+    }
+}
+
+void KHTMLView::revertTransforms( int& x, int& y ) const
+{
+    int dummy = 0;
+    revertTransforms(x, y, dummy, dummy);
+}
+   
 void KHTMLView::resizeEvent (QResizeEvent* e)
 {
     int dw = e->oldSize().width() - e->size().width();
@@ -692,16 +738,30 @@
 
     QRect r = e->rect();
     QRect v(contentsX(), contentsY(), visibleWidth(), visibleHeight());
+    if (d->staticWidget) {
+        QPoint off(contentsX(),contentsY());
+        p.translate(-off);
+        r.translate(off);
+    }
 
     r = r.intersect(v);
     if (!r.isValid() || r.isEmpty()) return;
-    p.setClipRect(v);
+    
+    if (d->haveZoom()) {
+        p.scale( d->zoomLevel/100., d->zoomLevel/100.);
 
+        r.setX(r.x()*100/d->zoomLevel);
+        r.setY(r.y()*100/d->zoomLevel);
+        r.setWidth(r.width()*100/d->zoomLevel);
+        r.setHeight(r.height()*100/d->zoomLevel);
+    }
+    p.setClipRect(r);
+  
     int ex = r.x();
     int ey = r.y();
     int ew = r.width();
     int eh = r.height();
-    
+
     if(!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
         p.fillRect(ex, ey, ew, eh, palette().brush(QPalette::Active, QPalette::Base));
         return;
@@ -882,6 +942,23 @@
     QScrollArea::closeEvent( ev );
 }
 
+void KHTMLView::setZoomLevel(int percent)
+{
+    percent = percent < 20 ? 20 : (percent > 800 ? 800 : percent);
+    int oldpercent = d->zoomLevel;
+    d->zoomLevel = percent;
+    if (percent != oldpercent) {
+        if (d->layoutSchedulingEnabled)
+            layout();
+        widget()->update();
+    }
+}
+
+int KHTMLView::zoomLevel() const
+{
+    return d->zoomLevel;
+}
+
 //
 // Event Handling
 //
@@ -898,6 +975,7 @@
 
     int xm = _mouse->x();
     int ym = _mouse->y();
+    revertTransforms(xm, ym);
 
     // kDebug( 6000 ) << "mousePressEvent: viewport=("<<_mouse->x()-contentsX()<<"/"<<_mouse->y()-contentsY()<<"), contents=(" << xm << "/" << ym << ")\n";
 
@@ -1018,6 +1096,7 @@
 
     int xm = _mouse->x();
     int ym = _mouse->y();
+    revertTransforms(xm, ym);
 
     // kDebug( 6000 ) << "mouseDblClickEvent: x=" << xm << ", y=" << ym << endl;
 
@@ -1087,6 +1166,7 @@
 
     int xm = _mouse->x();
     int ym = _mouse->y();
+    revertTransforms(xm, ym);
 
     DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseMove );
     // Do not modify :hover/:active state while mouse is pressed.
@@ -1240,6 +1320,7 @@
 
     int xm = _mouse->x();
     int ym = _mouse->y();
+    revertTransforms(xm, ym);
 
     DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseRelease );
 
@@ -3001,10 +3082,11 @@
 }
 
 
-void KHTMLView::useSlowRepaints()
+void KHTMLView::setHasStaticBackground()
 {
-    d->useSlowRepaints = true;
-//    setStaticBackground(true); ### ?? FIXME
+    if (!d->staticWidget)
+        widget()->move(0,0);
+    d->staticWidget = true;
 }
 
 
@@ -3136,6 +3218,7 @@
     int exceptioncode = 0;
     int pageX = _mouse->x();
     int pageY = _mouse->y();
+    revertTransforms(pageX, pageY);
     int clientX = pageX - contentsX();
     int clientY = pageY - contentsY();
     int screenX = _mouse->globalX();
@@ -3164,7 +3247,7 @@
 
     // mouseout/mouseover
     if (setUnder && d->oldUnderMouse != targetNode) {
-        if (d->oldUnderMouse && d->oldUnderMouse->getDocument() != targetNode->getDocument()) {
+        if (d->oldUnderMouse && d->oldUnderMouse->getDocument() != m_part->xmlDocImpl()) {
             d->oldUnderMouse->deref();
             d->oldUnderMouse = 0;
         }
@@ -3436,16 +3519,29 @@
                      horizontalScrollBar()->maximum()-horizontalScrollBar()->value() : horizontalScrollBar()->value();
     d->contentsY = verticalScrollBar()->value();
 
-    if (d->useSlowRepaints) {
-        widget()->blockSignals( true );
-        widget()->move( widget()->pos().x() + dx, widget()->pos().y() +dy );
-        widget()->blockSignals( false );
+    if ( d->staticWidget ) {
         widget()->repaint();
         return;
     }
     QScrollArea::scrollContentsBy(dx, dy);
 }
 
+void KHTMLView::addChild(QWidget * child, int x, int y)
+{
+    if (!child) 
+        return;
+
+    if (child->parent() != widget())
+        child->setParent( widget() );
+    
+    // ### handle pseudo-zooming of non-redirected widgets (e.g. just resize'em)
+
+    if (!d->staticWidget)
+        child->move(x, y);
+    else
+        child->move(x-contentsX(), y-contentsY());
+}
+
 void KHTMLView::timerEvent ( QTimerEvent *e )
 {
 //    kDebug() << "timer event " << e->timerId() << endl;
@@ -3518,9 +3614,6 @@
 	}
     }
 
-//    setStaticBackground(d->useSlowRepaints); ?? ### FIXME
-
-//        kDebug() << "scheduled repaint "<< d->repaintTimerId  << endl;
     if (d->repaintTimerId)
         killTimer(d->repaintTimerId);
     d->repaintTimerId = 0;
--- trunk/KDE/kdelibs/khtml/khtmlview.h #630478:630479
@@ -98,6 +98,7 @@
     friend class DOM::HTMLFormElementImpl;
     friend class DOM::HTMLAnchorElementImpl;
     friend class DOM::HTMLInputElementImpl;
+    friend class DOM::NodeImpl;
     friend class DOM::DocumentImpl;
     friend class KHTMLPart;
     friend class khtml::RenderCanvas;
@@ -257,6 +258,8 @@
      */
     void updateContents( const QRect& r );
     void updateContents(int x, int y, int w, int h);
+    
+    void addChild(QWidget *child, int dx, int dy);
 
     /**
      * Requests an immediate repaint of the content area
@@ -264,6 +267,18 @@
      */
     void repaintContents( const QRect& r );
     void repaintContents(int x, int y, int w, int h);
+    
+    /**
+     * Apply a zoom level to the content area
+     * @param percent a zoom level expressed as a percentage
+     */
+    void setZoomLevel( int percent );
+    
+    /**
+     * Retrieve the current zoom level
+     *
+     */
+    int zoomLevel() const;
 
 public Q_SLOTS:
     /**
@@ -393,7 +408,10 @@
     QMap< DOM::ElementImpl*, QChar > buildFallbackAccessKeys() const;
     void displayAccessKeys( KHTMLView* caller, KHTMLView* origview, QVector< QChar >& taken, bool use_fallbacks );
 
-    void useSlowRepaints();
+    void setHasStaticBackground();
+    void applyTransforms( int& x, int& y, int& w, int& h) const;
+    void revertTransforms( int& x, int& y, int& w, int& h) const;
+    void revertTransforms( int& x, int& y ) const;
 
     void setIgnoreWheelEvents(bool e);
 
@@ -699,6 +717,7 @@
 
     void setWidgetVisible(::khtml::RenderWidget*, bool visible);
 
+
     int _width;
     int _height;
 
--- trunk/KDE/kdelibs/khtml/rendering/render_canvas.cpp #630478:630479
@@ -220,35 +220,42 @@
      // update our cached document size
     int hDocH = m_cachedDocHeight = docHeight();
     int hDocW = m_cachedDocWidth = docWidth();
+    
+    int zLevel = m_view? m_view->zoomLevel() : 100;
+    hDocW = hDocW*zLevel/100;
+    hDocH = hDocH*zLevel/100;
 
     if (!m_pagedMode && m_view) {
 
         bool vss = !m_view->verticalScrollBar()->isHidden();
         bool hss = !m_view->horizontalScrollBar()->isHidden();
         QSize s = m_view->maximumViewportSize();
-        if ( m_cachedDocWidth > s.width() )
+
+        int zoomedDocWidth = m_cachedDocWidth*zLevel/100;
+        int zoomedDocHeight = m_cachedDocHeight*zLevel/100;
+        if ( zoomedDocWidth > s.width() )
             s.setWidth( s.width()-m_view->verticalScrollBar()->sizeHint().width() );
-        if ( m_cachedDocHeight > s.height() )
+        if ( zoomedDocHeight > s.height() )
             s.setHeight( s.height()-m_view->horizontalScrollBar()->sizeHint().height() );
    
         // if we are about to show a scrollbar, and the document is sized to the viewport w or h,
         // then reserve the scrollbar space so that it doesn't trigger the _other_ scrollbar
 
         if (!vss && m_width - m_view->verticalScrollBar()->sizeHint().width() == s.width() &&
-            m_cachedDocWidth <= m_width)
-            hDocW = qMin( m_cachedDocWidth, s.width() );
+            zoomedDocWidth <= m_width)
+            hDocW = qMin( zoomedDocWidth, s.width() );
 
         if (!hss && m_height - m_view->horizontalScrollBar()->sizeHint().height() == s.height() &&
-            m_cachedDocHeight <= m_height)
-            hDocH = qMin( m_cachedDocHeight, s.height() );
+            zoomedDocHeight <= m_height)
+            hDocH = qMin( zoomedDocHeight, s.height() );
 
         // likewise, if a scrollbar is shown, and we have a cunning plan to turn it off,
         // think again if we are falling downright in the hysteresis zone
 
-        if (vss && s.width() > m_cachedDocWidth && m_cachedDocWidth > m_view->visibleWidth())
+        if (vss && s.width() > zoomedDocWidth && zoomedDocWidth > m_view->visibleWidth())
             hDocW = s.width()+1;
 
-        if (hss && s.height() > m_cachedDocHeight && m_cachedDocHeight > m_view->visibleHeight())
+        if (hss && s.height() > zoomedDocHeight && zoomedDocHeight > m_view->visibleHeight())
             hDocH = s.height()+1;
 
         m_view->resizeContents(hDocW, hDocH);
--- trunk/KDE/kdelibs/khtml/rendering/render_replaced.cpp #630478:630479
@@ -524,7 +524,7 @@
     }
     m_view->setWidgetVisible(this, true);
     if (!khtmlw)
-        m_widget->move( xPos, yPos );
+        m_view->addChild( m_widget, xPos, yPos );
     else
         m_widget->move( xPos, -500000 +yPos);
     m_widget->show();
--- trunk/KDE/kdelibs/khtml/xml/dom_nodeimpl.cpp #630478:630479
@@ -503,6 +503,8 @@
     int exceptioncode = 0;
     int pageX = _mouse->x();
     int pageY = _mouse->y();
+    if ( getDocument()->view() )
+        getDocument()->view()->revertTransforms( pageX, pageY );
     int clientX = pageX;
     int clientY = pageY;
     if ( getDocument()->view() )