1 /* This file is part of the KDE project
2  *
3  * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
4  *                     1999 Lars Knoll <knoll@kde.org>
5  *                     1999 Antti Koivisto <koivisto@kde.org>
6  *                     2000-2004 Dirk Mueller <mueller@kde.org>
7  *                     2003 Leo Savernik <l.savernik@aon.at>
8  *                     2003-2008 Apple Computer, Inc.
9  *                     2008 Allan Sandfeld Jensen <kde@carewolf.com>
10  *                     2006-2008 Germain Garand <germain@ebooksfrance.org>
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Library General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public License
23  * along with this library; see the file COPYING.LIB.  If not, write to
24  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25  * Boston, MA 02110-1301, USA.
26  */
27 
28 #include "khtmlview.h"
29 
30 #include "khtml_part.h"
31 #include "khtml_events.h"
32 #include <config-khtml.h>
33 #if HAVE_X11
34 #include <qx11info_x11.h>
35 #endif
36 
37 #include "html/html_documentimpl.h"
38 #include "html/html_inlineimpl.h"
39 #include "html/html_formimpl.h"
40 #include "html/htmltokenizer.h"
41 #include "editing/editor.h"
42 #include "rendering/render_arena.h"
43 #include "rendering/render_canvas.h"
44 #include "rendering/render_frames.h"
45 #include "rendering/render_replaced.h"
46 #include "rendering/render_form.h"
47 #include "rendering/render_layer.h"
48 #include "rendering/render_line.h"
49 #include "rendering/render_table.h"
50 // removeme
51 #define protected public
52 #include "rendering/render_text.h"
53 #undef protected
54 #include "xml/dom2_eventsimpl.h"
55 #include "css/cssstyleselector.h"
56 #include "misc/loader.h"
57 #include "khtml_settings.h"
58 #include "khtml_printsettings.h"
59 
60 #include "khtmlpart_p.h"
61 
62 #include <kcursor.h>
63 #include "khtml_debug.h"
64 #include <kiconloader.h>
65 #include <knotification.h>
66 #include <kconfig.h>
67 #include <../khtml_version.h>
68 
69 #include <kstringhandler.h>
70 #include <kconfiggroup.h>
71 #include <ksharedconfig.h>
72 
73 #include <QBitmap>
74 #include <QDialog>
75 #include <QDialogButtonBox>
76 #include <QLabel>
77 #include <QKeyEvent>
78 #include <QObject>
79 #include <QPainter>
80 #include <QHash>
81 #include <QToolTip>
82 #include <QString>
83 #include <QTextDocument>
84 #include <QTimer>
85 #include <QAbstractEventDispatcher>
86 #include <QVector>
87 #include <QAbstractScrollArea>
88 #include <QPrinter>
89 #include <QPrintDialog>
90 #include <qstandardpaths.h>
91 
92 //#define DEBUG_FLICKER
93 
94 #include <limits.h>
95 #if HAVE_X11
96 #include <X11/Xlib.h>
97 #include <fixx11h.h>
98 #elif defined(Q_OS_WIN)
99 #include <windows.h>
100 #endif
101 
102 #if 0
103 namespace khtml
104 {
105 void dumpLineBoxes(RenderFlow *flow);
106 }
107 #endif
108 
109 using namespace DOM;
110 using namespace khtml;
111 
112 #ifndef NDEBUG
113 static const int sFirstLayoutDelay = 520;
114 static const int sParsingLayoutsInterval = 380;
115 static const int sLayoutAttemptDelay = 300;
116 #else
117 static const int sFirstLayoutDelay = 280;
118 static const int sParsingLayoutsInterval = 320;
119 static const int sLayoutAttemptDelay = 200;
120 #endif
121 static const int sLayoutAttemptIncrement = 20;
122 static const int sParsingLayoutsIncrement = 60;
123 
124 static const int sSmoothScrollTime = 128;
125 static const int sSmoothScrollTick = 16;
126 static const int sSmoothScrollMinStaticPixels = 320 * 200;
127 
128 static const int sMaxMissedDeadlines = 12;
129 static const int sWayTooMany = -1;
130 
131 class KHTMLViewPrivate
132 {
133     friend class KHTMLView;
134 public:
135 
136     enum PseudoFocusNodes {
137         PFNone,
138         PFTop,
139         PFBottom
140     };
141 
142     enum StaticBackgroundState {
143         SBNone = 0,
144         SBPartial,
145         SBFull
146     };
147 
148     enum CompletedState {
149         CSNone = 0,
150         CSFull,
151         CSActionPending
152     };
153 
KHTMLViewPrivate(KHTMLView * v)154     KHTMLViewPrivate(KHTMLView *v)
155         : underMouse(nullptr), underMouseNonShared(nullptr), oldUnderMouse(nullptr)
156     {
157         postponed_autorepeat = nullptr;
158         scrollingFromWheelTimerId = 0;
159         smoothScrollMode = KHTMLView::SSMWhenEfficient;
160 
161         reset();
162         vpolicy = Qt::ScrollBarAsNeeded;
163         hpolicy = Qt::ScrollBarAsNeeded;
164         formCompletions = nullptr;
165         prevScrollbarVisible = true;
166 
167         possibleTripleClick = false;
168         emitCompletedAfterRepaint = CSNone;
169         cursorIconWidget = nullptr;
170         cursorIconType   = KHTMLView::LINK_NORMAL;
171         m_mouseScrollTimer = nullptr;
172         m_mouseScrollIndicator = nullptr;
173         contentsX = 0;
174         contentsY = 0;
175         view = v;
176     }
~KHTMLViewPrivate()177     ~KHTMLViewPrivate()
178     {
179         delete formCompletions;
180         delete postponed_autorepeat;
181         if (underMouse) {
182             underMouse->deref();
183         }
184         if (underMouseNonShared) {
185             underMouseNonShared->deref();
186         }
187         if (oldUnderMouse) {
188             oldUnderMouse->deref();
189         }
190 
191         delete cursorIconWidget;
192         delete m_mouseScrollTimer;
193         delete m_mouseScrollIndicator;
194     }
reset()195     void reset()
196     {
197         if (underMouse) {
198             underMouse->deref();
199         }
200         underMouse = nullptr;
201         if (underMouseNonShared) {
202             underMouseNonShared->deref();
203         }
204         underMouseNonShared = nullptr;
205         if (oldUnderMouse) {
206             oldUnderMouse->deref();
207         }
208         oldUnderMouse = nullptr;
209         linkPressed = false;
210         staticWidget = SBNone;
211         fixedObjectsCount = 0;
212         staticObjectsCount = 0;
213         tabMovePending = false;
214         lastTabbingDirection = true;
215         pseudoFocusNode = PFNone;
216         zoomLevel = 100;
217 #ifndef KHTML_NO_SCROLLBARS
218         //We don't turn off the toolbars here
219         //since if the user turns them
220         //off, then chances are they want them turned
221         //off always - even after a reset.
222 #else
223         vpolicy = ScrollBarAlwaysOff;
224         hpolicy = ScrollBarAlwaysOff;
225 #endif
226         scrollBarMoved = false;
227         contentsMoving = false;
228         ignoreWheelEvents = false;
229         scrollingFromWheel = QPoint(-1, -1);
230         borderX = 30;
231         borderY = 30;
232         steps = 0;
233         dx = dy = 0;
234         paged = false;
235         clickX = -1;
236         clickY = -1;
237         clickCount = 0;
238         isDoubleClick = false;
239         scrollingSelf = false;
240         delete postponed_autorepeat;
241         postponed_autorepeat = nullptr;
242         layoutTimerId = 0;
243         repaintTimerId = 0;
244         scrollTimerId = 0;
245         scrollSuspended = false;
246         scrollSuspendPreActivate = false;
247         smoothScrolling = false;
248         smoothScrollModeIsDefault = true;
249         shouldSmoothScroll = false;
250         smoothScrollMissedDeadlines = 0;
251         hasFrameset = false;
252         complete = false;
253         firstLayoutPending = true;
254 #ifdef SPEED_DEBUG
255         firstRepaintPending = true;
256 #endif
257         needsFullRepaint = true;
258         dirtyLayout = false;
259         layoutSchedulingEnabled = true;
260         painting = false;
261         layoutCounter = 0;
262         layoutAttemptCounter = 0;
263         scheduledLayoutCounter = 0;
264         updateRegion = QRegion();
265         m_dialogsAllowed = true;
266         accessKeysActivated = false;
267         accessKeysPreActivate = false;
268 
269         // the view might have been built before the part it will be assigned to,
270         // so exceptionally, we need to directly ref/deref KHTMLGlobal to
271         // account for this transitory case.
272         KHTMLGlobal::ref();
273         accessKeysEnabled = KHTMLGlobal::defaultHTMLSettings()->accessKeysEnabled();
274         KHTMLGlobal::deref();
275 
276         emitCompletedAfterRepaint = CSNone;
277         m_mouseEventsTarget = nullptr;
278         m_clipHolder = nullptr;
279     }
newScrollTimer(QWidget * view,int tid)280     void newScrollTimer(QWidget *view, int tid)
281     {
282         //qCDebug(KHTML_LOG) << "newScrollTimer timer " << tid;
283         view->killTimer(scrollTimerId);
284         scrollTimerId = tid;
285         scrollSuspended = false;
286     }
287     enum ScrollDirection { ScrollLeft, ScrollRight, ScrollUp, ScrollDown };
288 
adjustScroller(QWidget * view,ScrollDirection direction,ScrollDirection oppositedir)289     void adjustScroller(QWidget *view, ScrollDirection direction, ScrollDirection oppositedir)
290     {
291         static const struct {
292             int msec, pixels;
293         } timings [] = {
294             {320, 1}, {224, 1}, {160, 1}, {112, 1}, {80, 1}, {56, 1}, {40, 1},
295             {28, 1}, {20, 1}, {20, 2}, {20, 3}, {20, 4}, {20, 6}, {20, 8}, {0, 0}
296         };
297         if (!scrollTimerId ||
298                 (static_cast<int>(scrollDirection) != direction &&
299                  (static_cast<int>(scrollDirection) != oppositedir || scrollSuspended))) {
300             scrollTiming = 6;
301             scrollBy = timings[scrollTiming].pixels;
302             scrollDirection = direction;
303             newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
304         } else if (scrollDirection == direction &&
305                    timings[scrollTiming + 1].msec && !scrollSuspended) {
306             scrollBy = timings[++scrollTiming].pixels;
307             newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
308         } else if (scrollDirection == oppositedir) {
309             if (scrollTiming) {
310                 scrollBy = timings[--scrollTiming].pixels;
311                 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec));
312             }
313         }
314         scrollSuspended = false;
315     }
316 
haveZoom() const317     bool haveZoom() const
318     {
319         return zoomLevel != 100;
320     }
321 
startScrolling()322     void startScrolling()
323     {
324         smoothScrolling = true;
325         smoothScrollTimer.start(sSmoothScrollTick);
326         shouldSmoothScroll = false;
327     }
328 
stopScrolling()329     void stopScrolling()
330     {
331         smoothScrollTimer.stop();
332         dx = dy = 0;
333         steps = 0;
334         updateContentsXY();
335         smoothScrolling = false;
336         shouldSmoothScroll = false;
337     }
338 
updateContentsXY()339     void updateContentsXY()
340     {
341         contentsX = QApplication::isRightToLeft() ?
342                     view->horizontalScrollBar()->maximum() - view->horizontalScrollBar()->value() : view->horizontalScrollBar()->value();
343         contentsY = view->verticalScrollBar()->value();
344     }
scrollAccessKeys(int dx,int dy)345     void scrollAccessKeys(int dx, int dy)
346     {
347         QList<QLabel *> wl = view->widget()->findChildren<QLabel *>("KHTMLAccessKey");
348         foreach (QLabel *w, wl) {
349             w->move(w->pos() + QPoint(dx, dy));
350         }
351     }
scrollExternalWidgets(int dx,int dy)352     void scrollExternalWidgets(int dx, int dy)
353     {
354         if (visibleWidgets.isEmpty()) {
355             return;
356         }
357 
358         QHashIterator<void *, QWidget *> it(visibleWidgets);
359         while (it.hasNext()) {
360             it.next();
361             it.value()->move(it.value()->pos() + QPoint(dx, dy));
362         }
363     }
364 
365     NodeImpl *underMouse;
366     NodeImpl *underMouseNonShared;
367     NodeImpl *oldUnderMouse;
368 
369     // Do not adjust bitfield enums sizes.
370     // They are oversized because they are signed on some platforms.
371     bool tabMovePending: 1;
372     bool lastTabbingDirection: 1;
373     PseudoFocusNodes pseudoFocusNode: 3;
374     bool scrollBarMoved: 1;
375     bool contentsMoving: 1;
376 
377     Qt::ScrollBarPolicy vpolicy;
378     Qt::ScrollBarPolicy hpolicy;
379     bool prevScrollbarVisible: 1;
380     bool linkPressed: 1;
381     bool ignoreWheelEvents: 1;
382     StaticBackgroundState staticWidget: 3;
383     int staticObjectsCount;
384     int fixedObjectsCount;
385 
386     int zoomLevel;
387     int borderX, borderY;
388     int dx, dy;
389     int steps;
390     KConfig *formCompletions;
391 
392     int clickX, clickY, clickCount;
393     bool isDoubleClick;
394 
395     bool paged;
396 
397     bool scrollingSelf;
398     int contentsX, contentsY;
399     int layoutTimerId;
400     QKeyEvent *postponed_autorepeat;
401 
402     int repaintTimerId;
403     int scrollTimerId;
404     int scrollTiming;
405     int scrollBy;
406     ScrollDirection scrollDirection     : 3;
407     bool scrollSuspended            : 1;
408     bool scrollSuspendPreActivate       : 1;
409     KHTMLView::SmoothScrollingMode smoothScrollMode : 3;
410     bool smoothScrolling                          : 1;
411     bool smoothScrollModeIsDefault                : 1;
412     bool shouldSmoothScroll                       : 1;
413     bool hasFrameset                              : 1;
414     bool complete               : 1;
415     bool firstLayoutPending         : 1;
416 #ifdef SPEED_DEBUG
417     bool firstRepaintPending                    : 1;
418 #endif
419     bool layoutSchedulingEnabled        : 1;
420     bool needsFullRepaint           : 1;
421     bool painting               : 1;
422     bool possibleTripleClick            : 1;
423     bool dirtyLayout                           : 1;
424     bool m_dialogsAllowed           : 1;
425     short smoothScrollMissedDeadlines;
426     int layoutCounter;
427     int layoutAttemptCounter;
428     int scheduledLayoutCounter;
429     QRegion updateRegion;
430     QTimer smoothScrollTimer;
431     QTime smoothScrollStopwatch;
432     QHash<void *, QWidget *> visibleWidgets;
433     bool accessKeysEnabled;
434     bool accessKeysActivated;
435     bool accessKeysPreActivate;
436     CompletedState emitCompletedAfterRepaint;
437 
438     QLabel               *cursorIconWidget;
439     KHTMLView::LinkCursor cursorIconType;
440 
441     // scrolling activated by MMB
442     short m_mouseScroll_byX;
443     short m_mouseScroll_byY;
444     QPoint scrollingFromWheel;
445     int scrollingFromWheelTimerId;
446     QTimer *m_mouseScrollTimer;
447     QWidget *m_mouseScrollIndicator;
448     QPointer<QWidget> m_mouseEventsTarget;
449     QStack<QRegion> *m_clipHolder;
450     KHTMLView *view;
451 };
452 
453 #ifndef QT_NO_TOOLTIP
454 
455 /** calculates the client-side image map rectangle for the given image element
456  * @param img image element
457  * @param scrollOfs scroll offset of viewport in content coordinates
458  * @param p position to be probed in viewport coordinates
459  * @param r returns the bounding rectangle in content coordinates
460  * @param s returns the title string
461  * @return true if an appropriate area was found -- only in this case r and
462  *  s are valid, false otherwise
463  */
findImageMapRect(HTMLImageElementImpl * img,const QPoint & scrollOfs,const QPoint & p,QRect & r,QString & s)464 static bool findImageMapRect(HTMLImageElementImpl *img, const QPoint &scrollOfs,
465                              const QPoint &p, QRect &r, QString &s)
466 {
467     HTMLMapElementImpl *map;
468     if (img && img->document()->isHTMLDocument() &&
469             (map = static_cast<HTMLDocumentImpl *>(img->document())->getMap(img->imageMap()))) {
470         RenderObject::NodeInfo info(true, false);
471         RenderObject *rend = img->renderer();
472         int ax, ay;
473         if (!rend || !rend->absolutePosition(ax, ay)) {
474             return false;
475         }
476         // we're a client side image map
477         bool inside = map->mapMouseEvent(p.x() - ax + scrollOfs.x(),
478                                          p.y() - ay + scrollOfs.y(), rend->contentWidth(),
479                                          rend->contentHeight(), info);
480         if (inside && info.URLElement()) {
481             HTMLAreaElementImpl *area = static_cast<HTMLAreaElementImpl *>(info.URLElement());
482             Q_ASSERT(area->id() == ID_AREA);
483             s = area->getAttribute(ATTR_TITLE).string();
484             QRegion reg = area->cachedRegion();
485             if (!s.isEmpty() && !reg.isEmpty()) {
486                 r = reg.boundingRect();
487                 r.translate(ax, ay);
488                 return true;
489             }
490         }
491     }
492     return false;
493 }
494 
event(QEvent * e)495 bool KHTMLView::event(QEvent *e)
496 {
497     switch (e->type()) {
498     case QEvent::ToolTip: {
499         QHelpEvent *he = static_cast<QHelpEvent *>(e);
500         QPoint     p   = he->pos();
501 
502         DOM::NodeImpl *node = d->underMouseNonShared;
503         QRect region;
504         while (node) {
505             if (node->isElementNode()) {
506                 DOM::ElementImpl *e = static_cast<DOM::ElementImpl *>(node);
507                 QRect r;
508                 QString s;
509                 bool found = false;
510                 // for images, check if it is part of a client-side image map,
511                 // and query the <area>s' title attributes, too
512                 if (e->id() == ID_IMG && !e->getAttribute(ATTR_USEMAP).isEmpty()) {
513                     found = findImageMapRect(static_cast<HTMLImageElementImpl *>(e),
514                                              viewportToContents(QPoint(0, 0)), p, r, s);
515                 }
516                 if (!found) {
517                     s = e->getAttribute(ATTR_TITLE).string().trimmed();
518                     r = node->getRect();
519                 }
520                 region |= QRect(contentsToViewport(r.topLeft()), r.size());
521                 if (!s.isEmpty()) {
522                     QToolTip::showText(he->globalPos(),
523                                        Qt::convertFromPlainText(s, Qt::WhiteSpaceNormal),
524                                        widget(), region);
525                     break;
526                 }
527             }
528             node = node->parentNode();
529         }
530         // Qt makes tooltip events happen nearly immediately when a preceding one was processed in the past few seconds.
531         // We don't want that feature to apply to web tootlips however, as it clashes with dhtml menus.
532         // So we'll just pretend we did not process that event.
533         return false;
534     }
535 
536     case QEvent::DragEnter:
537     case QEvent::DragMove:
538     case QEvent::DragLeave:
539     case QEvent::Drop:
540         // In Qt4, one needs to both call accept() on the DND event and return
541         // true on ::event for the candidate widget for the drop to be possible.
542         // Apps hosting us, such as konq, can do the former but not the later.
543         // We will do the second bit, as it's a no-op unless someone else explicitly
544         // accepts the event. We need to skip the scrollarea to do that,
545         // since it will just skip the events, both killing the drop, and
546         // not permitting us to forward it up the part hiearchy in our dragEnterEvent,
547         // etc. handlers
548         return QWidget::event(e);
549     case QEvent::StyleChange:
550     case QEvent::LayoutRequest: {
551         updateScrollBars();
552         return QAbstractScrollArea::event(e);
553     }
554     case QEvent::PaletteChange:
555         slotPaletteChanged();
556         return QScrollArea::event(e);
557     default:
558         return QScrollArea::event(e);
559     }
560 }
561 #endif
562 
KHTMLView(KHTMLPart * part,QWidget * parent)563 KHTMLView::KHTMLView(KHTMLPart *part, QWidget *parent)
564     : QScrollArea(parent), d(new KHTMLViewPrivate(this))
565 {
566     m_medium = "screen";
567 
568     m_part = part;
569 
570     QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
571     QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
572 
573     initWidget();
574     widget()->setMouseTracking(true);
575 }
576 
~KHTMLView()577 KHTMLView::~KHTMLView()
578 {
579     closeChildDialogs();
580     if (m_part) {
581         DOM::DocumentImpl *doc = m_part->xmlDocImpl();
582         if (doc) {
583             doc->detach();
584         }
585     }
586     delete d;
587 }
588 
setPart(KHTMLPart * part)589 void KHTMLView::setPart(KHTMLPart *part)
590 {
591     assert(part && !m_part);
592     m_part = part;
593 }
594 
initWidget()595 void KHTMLView::initWidget()
596 {
597     // Do not access the part here. It might not be fully constructed.
598 
599     setFrameStyle(QFrame::NoFrame);
600     setFocusPolicy(Qt::StrongFocus);
601     viewport()->setFocusProxy(this);
602 
603     _marginWidth = -1; // undefined
604     _marginHeight = -1;
605     _width = 0;
606     _height = 0;
607 
608     installEventFilter(this);
609 
610     setAcceptDrops(true);
611     if (!widget()) {
612         setWidget(new QWidget(this));
613     }
614     widget()->setAttribute(Qt::WA_NoSystemBackground);
615 
616     // Do *not* remove this attribute frivolously.
617     // You might not notice a change of behaviour in Debug builds
618     // but removing opaque events will make QWidget::scroll fail horribly
619     // in Release builds.
620     widget()->setAttribute(Qt::WA_OpaquePaintEvent);
621 
622     verticalScrollBar()->setCursor(Qt::ArrowCursor);
623     horizontalScrollBar()->setCursor(Qt::ArrowCursor);
624 
625     connect(&d->smoothScrollTimer, SIGNAL(timeout()), this, SLOT(scrollTick()));
626 }
627 
resizeContentsToViewport()628 void KHTMLView::resizeContentsToViewport()
629 {
630     QSize s = viewport()->size();
631     resizeContents(s.width(), s.height());
632 }
633 
634 // called by KHTMLPart::clear()
clear()635 void KHTMLView::clear()
636 {
637     if (d->accessKeysEnabled && d->accessKeysActivated) {
638         accessKeysTimeout();
639     }
640     viewport()->unsetCursor();
641     if (d->cursorIconWidget) {
642         d->cursorIconWidget->hide();
643     }
644     if (d->smoothScrolling) {
645         d->stopScrolling();
646     }
647     d->reset();
648     QAbstractEventDispatcher::instance()->unregisterTimers(this);
649     emit cleared();
650 
651     QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
652     QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
653     verticalScrollBar()->setEnabled(false);
654     horizontalScrollBar()->setEnabled(false);
655 
656 }
657 
hideEvent(QHideEvent * e)658 void KHTMLView::hideEvent(QHideEvent *e)
659 {
660     QScrollArea::hideEvent(e);
661 }
662 
showEvent(QShowEvent * e)663 void KHTMLView::showEvent(QShowEvent *e)
664 {
665     QScrollArea::showEvent(e);
666 }
667 
setMouseEventsTarget(QWidget * w)668 void KHTMLView::setMouseEventsTarget(QWidget *w)
669 {
670     d->m_mouseEventsTarget = w;
671 }
672 
mouseEventsTarget() const673 QWidget *KHTMLView::mouseEventsTarget() const
674 {
675     return d->m_mouseEventsTarget;
676 }
677 
setClipHolder(QStack<QRegion> * ch)678 void KHTMLView::setClipHolder(QStack<QRegion> *ch)
679 {
680     d->m_clipHolder = ch;
681 }
682 
clipHolder() const683 QStack<QRegion> *KHTMLView::clipHolder() const
684 {
685     return d->m_clipHolder;
686 }
687 
contentsWidth() const688 int KHTMLView::contentsWidth() const
689 {
690     return widget() ? widget()->width() : 0;
691 }
692 
contentsHeight() const693 int KHTMLView::contentsHeight() const
694 {
695     return widget() ? widget()->height() : 0;
696 }
697 
resizeContents(int w,int h)698 void KHTMLView::resizeContents(int w, int h)
699 {
700     if (!widget()) {
701         return;
702     }
703     widget()->resize(w, h);
704     if (!widget()->isVisible()) {
705         updateScrollBars();
706     }
707 }
708 
contentsX() const709 int KHTMLView::contentsX() const
710 {
711     return d->contentsX;
712 }
713 
contentsY() const714 int KHTMLView::contentsY() const
715 {
716     return d->contentsY;
717 }
718 
visibleWidth() const719 int KHTMLView::visibleWidth() const
720 {
721     if (m_kwp->isRedirected()) {
722         // our RenderWidget knows better
723         if (RenderWidget *rw = m_kwp->renderWidget()) {
724             int ret = rw->width() - rw->paddingLeft() - rw->paddingRight() - rw->borderLeft() - rw->borderRight();
725             if (verticalScrollBar()->isVisible()) {
726                 ret -= verticalScrollBar()->sizeHint().width();
727                 ret = qMax(0, ret);
728             }
729             return ret;
730         }
731     }
732     return viewport()->width();
733 }
734 
visibleHeight() const735 int KHTMLView::visibleHeight() const
736 {
737     if (m_kwp->isRedirected()) {
738         // our RenderWidget knows better
739         if (RenderWidget *rw = m_kwp->renderWidget()) {
740             int ret = rw->height() - rw->paddingBottom() - rw->paddingTop() - rw->borderTop() - rw->borderBottom();
741             if (horizontalScrollBar()->isVisible()) {
742                 ret -= horizontalScrollBar()->sizeHint().height();
743                 ret = qMax(0, ret);
744             }
745             return ret;
746         }
747     }
748     return viewport()->height();
749 }
750 
setContentsPos(int x,int y)751 void KHTMLView::setContentsPos(int x, int y)
752 {
753     horizontalScrollBar()->setValue(QApplication::isRightToLeft() ?
754                                     horizontalScrollBar()->maximum() - x : x);
755     verticalScrollBar()->setValue(y);
756 }
757 
scrollBy(int x,int y)758 void KHTMLView::scrollBy(int x, int y)
759 {
760     if (d->scrollTimerId) {
761         d->newScrollTimer(this, 0);
762     }
763     horizontalScrollBar()->setValue(horizontalScrollBar()->value() + x);
764     verticalScrollBar()->setValue(verticalScrollBar()->value() + y);
765 }
766 
contentsToViewport(const QPoint & p) const767 QPoint KHTMLView::contentsToViewport(const QPoint &p) const
768 {
769     return QPoint(p.x() - contentsX(), p.y() - contentsY());
770 }
771 
contentsToViewport(int x,int y,int & cx,int & cy) const772 void KHTMLView::contentsToViewport(int x, int y, int &cx, int &cy) const
773 {
774     QPoint p(x, y);
775     p = contentsToViewport(p);
776     cx = p.x();
777     cy = p.y();
778 }
779 
viewportToContents(const QPoint & p) const780 QPoint KHTMLView::viewportToContents(const QPoint &p) const
781 {
782     return QPoint(p.x() + contentsX(), p.y() + contentsY());
783 }
784 
viewportToContents(int x,int y,int & cx,int & cy) const785 void KHTMLView::viewportToContents(int x, int y, int &cx, int &cy) const
786 {
787     QPoint p(x, y);
788     p = viewportToContents(p);
789     cx = p.x();
790     cy = p.y();
791 }
792 
updateContents(int x,int y,int w,int h)793 void KHTMLView::updateContents(int x, int y, int w, int h)
794 {
795     applyTransforms(x, y, w, h);
796     if (m_kwp->isRedirected()) {
797         QPoint off = m_kwp->absolutePos();
798         KHTMLView *pview = m_part->parentPart()->view();
799         pview->updateContents(x + off.x(), y + off.y(), w, h);
800     } else {
801         widget()->update(x, y, w, h);
802     }
803 }
804 
updateContents(const QRect & r)805 void KHTMLView::updateContents(const QRect &r)
806 {
807     updateContents(r.x(), r.y(), r.width(), r.height());
808 }
809 
repaintContents(int x,int y,int w,int h)810 void KHTMLView::repaintContents(int x, int y, int w, int h)
811 {
812     applyTransforms(x, y, w, h);
813     if (m_kwp->isRedirected()) {
814         QPoint off = m_kwp->absolutePos();
815         KHTMLView *pview = m_part->parentPart()->view();
816         pview->repaintContents(x + off.x(), y + off.y(), w, h);
817     } else {
818         widget()->repaint(x, y, w, h);
819     }
820 }
821 
repaintContents(const QRect & r)822 void KHTMLView::repaintContents(const QRect &r)
823 {
824     repaintContents(r.x(), r.y(), r.width(), r.height());
825 }
826 
applyTransforms(int & x,int & y,int & w,int & h) const827 void KHTMLView::applyTransforms(int &x, int &y, int &w, int &h) const
828 {
829     if (d->haveZoom()) {
830         const int z = d->zoomLevel;
831         x = x * z / 100;
832         y = y * z / 100;
833         w = w * z / 100;
834         h = h * z / 100;
835     }
836     x -= contentsX();
837     y -= contentsY();
838 }
839 
revertTransforms(int & x,int & y,int & w,int & h) const840 void KHTMLView::revertTransforms(int &x, int &y, int &w, int &h) const
841 {
842     x += contentsX();
843     y += contentsY();
844     if (d->haveZoom()) {
845         const int z = d->zoomLevel;
846         x = x * 100 / z;
847         y = y * 100 / z;
848         w = w * 100 / z;
849         h = h * 100 / z;
850     }
851 }
852 
revertTransforms(int & x,int & y) const853 void KHTMLView::revertTransforms(int &x, int &y) const
854 {
855     int dummy = 0;
856     revertTransforms(x, y, dummy, dummy);
857 }
858 
resizeEvent(QResizeEvent *)859 void KHTMLView::resizeEvent(QResizeEvent * /*e*/)
860 {
861     updateScrollBars();
862 
863     // If we didn't load anything, make white area as big as the view
864     if (!m_part->xmlDocImpl()) {
865         resizeContentsToViewport();
866     }
867 
868     // Viewport-dependent media queries may cause us to need completely different style information.
869     if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->styleSelector()->affectedByViewportChange()) {
870         m_part->xmlDocImpl()->updateStyleSelector();
871     }
872 
873     if (d->layoutSchedulingEnabled) {
874         layout();
875     }
876 
877     QApplication::sendPostedEvents(viewport(), QEvent::Paint);
878 
879     if (m_part && m_part->xmlDocImpl()) {
880         if (m_part->parentPart()) {
881             // sub-frame : queue the resize event until our toplevel is done layouting
882             khtml::ChildFrame *cf = m_part->parentPart()->frame(m_part);
883             if (cf && !cf->m_partContainerElement.isNull()) {
884                 cf->m_partContainerElement.data()->postResizeEvent();
885             }
886         } else {
887             // toplevel : dispatch sub-frames'resize events before our own
888             HTMLPartContainerElementImpl::sendPostedResizeEvents();
889             m_part->xmlDocImpl()->dispatchWindowEvent(EventImpl::RESIZE_EVENT, false, false);
890         }
891     }
892 }
893 
paintEvent(QPaintEvent * e)894 void KHTMLView::paintEvent(QPaintEvent *e)
895 {
896     QRect r = e->rect();
897     QRect v(contentsX(), contentsY(), visibleWidth(), visibleHeight());
898     QPoint off(contentsX(), contentsY());
899     r.translate(off);
900     r = r.intersected(v);
901     if (!r.isValid() || r.isEmpty()) {
902         return;
903     }
904 
905     QPainter p(widget());
906     p.translate(-off);
907 
908     if (d->haveZoom()) {
909         p.scale(d->zoomLevel / 100., d->zoomLevel / 100.);
910 
911         r.setX(r.x() * 100 / d->zoomLevel);
912         r.setY(r.y() * 100 / d->zoomLevel);
913         r.setWidth(r.width() * 100 / d->zoomLevel);
914         r.setHeight(r.height() * 100 / d->zoomLevel);
915         r.adjust(-1, -1, 1, 1);
916     }
917     p.setClipRect(r);
918 
919     int ex = r.x();
920     int ey = r.y();
921     int ew = r.width();
922     int eh = r.height();
923 
924     if (!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
925         p.fillRect(ex, ey, ew, eh, palette().brush(QPalette::Active, QPalette::Base));
926         return;
927     } else if (d->complete && static_cast<RenderCanvas *>(m_part->xmlDocImpl()->renderer())->needsLayout()) {
928         // an external update request happens while we have a layout scheduled
929         unscheduleRelayout();
930         layout();
931     } else if (m_part->xmlDocImpl()->tokenizer()) {
932         m_part->xmlDocImpl()->tokenizer()->setNormalYieldDelay();
933     }
934 
935     if (d->painting) {
936         // qCDebug(KHTML_LOG) << "WARNING: paintEvent reentered! ";
937         return;
938     }
939     d->painting = true;
940 
941     m_part->xmlDocImpl()->renderer()->layer()->paint(&p, r);
942 
943     if (d->hasFrameset) {
944         NodeImpl *body = static_cast<HTMLDocumentImpl *>(m_part->xmlDocImpl())->body();
945         if (body && body->renderer() && body->id() == ID_FRAMESET) {
946             static_cast<RenderFrameSet *>(body->renderer())->paintFrameSetRules(&p, r);
947         } else {
948             d->hasFrameset = false;
949         }
950     }
951 
952     khtml::DrawContentsEvent event(&p, ex, ey, ew, eh);
953     QApplication::sendEvent(m_part, &event);
954 
955     if (d->contentsMoving && !d->smoothScrolling && widget()->underMouse()) {
956         QMouseEvent *tempEvent = new QMouseEvent(QEvent::MouseMove, widget()->mapFromGlobal(QCursor::pos()),
957                 Qt::NoButton, Qt::NoButton, Qt::NoModifier);
958         QApplication::postEvent(widget(), tempEvent);
959     }
960 #ifdef SPEED_DEBUG
961     if (d->firstRepaintPending && !m_part->parentPart()) {
962         qCDebug(KHTML_LOG) << "FIRST PAINT:" << m_part->d->m_parsetime.elapsed();
963     }
964     d->firstRepaintPending = false;
965 #endif
966     d->painting = false;
967 }
968 
setMarginWidth(int w)969 void KHTMLView::setMarginWidth(int w)
970 {
971     // make it update the rendering area when set
972     _marginWidth = w;
973 }
974 
setMarginHeight(int h)975 void KHTMLView::setMarginHeight(int h)
976 {
977     // make it update the rendering area when set
978     _marginHeight = h;
979 }
980 
layout()981 void KHTMLView::layout()
982 {
983     if (m_part && m_part->xmlDocImpl()) {
984         DOM::DocumentImpl *document = m_part->xmlDocImpl();
985 
986         khtml::RenderCanvas *canvas = static_cast<khtml::RenderCanvas *>(document->renderer());
987         if (!canvas) {
988             return;
989         }
990 
991         d->layoutSchedulingEnabled = false;
992         d->dirtyLayout = true;
993 
994         // the reference object for the overflow property on canvas
995         RenderObject *ref = nullptr;
996         RenderObject *root = document->documentElement() ? document->documentElement()->renderer() : nullptr;
997 
998         if (document->isHTMLDocument()) {
999             NodeImpl *body = static_cast<HTMLDocumentImpl *>(document)->body();
1000             if (body && body->renderer() && body->id() == ID_FRAMESET) {
1001                 QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1002                 QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1003                 body->renderer()->setNeedsLayout(true);
1004                 d->hasFrameset = true;
1005             } else if (root) { // only apply body's overflow to canvas if root has a visible overflow
1006                 ref = (!body || root->style()->hidesOverflow()) ? root : body->renderer();
1007             }
1008         } else {
1009             ref = root;
1010         }
1011         if (ref) {
1012             if (ref->style()->overflowX() == OHIDDEN) {
1013                 if (d->hpolicy == Qt::ScrollBarAsNeeded) {
1014                     QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1015                 }
1016             } else if (ref->style()->overflowX() == OSCROLL) {
1017                 if (d->hpolicy == Qt::ScrollBarAsNeeded) {
1018                     QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1019                 }
1020             } else if (horizontalScrollBarPolicy() != d->hpolicy) {
1021                 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy);
1022             }
1023             if (ref->style()->overflowY() == OHIDDEN) {
1024                 if (d->vpolicy == Qt::ScrollBarAsNeeded) {
1025                     QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1026                 }
1027             } else if (ref->style()->overflowY() == OSCROLL) {
1028                 if (d->vpolicy == Qt::ScrollBarAsNeeded) {
1029                     QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1030                 }
1031             } else if (verticalScrollBarPolicy() != d->vpolicy) {
1032                 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
1033             }
1034         }
1035         d->needsFullRepaint = d->firstLayoutPending;
1036         if (_height !=  visibleHeight() || _width != visibleWidth()) {
1037             ;
1038             d->needsFullRepaint = true;
1039             _height = visibleHeight();
1040             _width = visibleWidth();
1041         }
1042 
1043         canvas->layout();
1044 
1045         emit finishedLayout();
1046         if (d->firstLayoutPending) {
1047             // make sure firstLayoutPending is set to false now in case this layout
1048             // wasn't scheduled
1049             d->firstLayoutPending = false;
1050             verticalScrollBar()->setEnabled(true);
1051             horizontalScrollBar()->setEnabled(true);
1052         }
1053         d->layoutCounter++;
1054 
1055         if (d->accessKeysEnabled && d->accessKeysActivated) {
1056             emit hideAccessKeys();
1057             displayAccessKeys();
1058         }
1059     } else {
1060         _width = visibleWidth();
1061     }
1062 
1063     if (d->layoutTimerId) {
1064         killTimer(d->layoutTimerId);
1065     }
1066     d->layoutTimerId = 0;
1067     d->layoutSchedulingEnabled = true;
1068 }
1069 
closeChildDialogs()1070 void KHTMLView::closeChildDialogs()
1071 {
1072     QList<QDialog *> dlgs = findChildren<QDialog *>();
1073     foreach (QDialog *dlg, dlgs) {
1074         if (dlg->testAttribute(Qt::WA_ShowModal)) {
1075             // qCDebug(KHTML_LOG) << "closeChildDialogs: closing dialog " << dlg;
1076             // close() ends up calling QButton::animateClick, which isn't immediate
1077             // we need something the exits the event loop immediately (#49068)
1078             dlg->reject();
1079         }
1080     }
1081     d->m_dialogsAllowed = false;
1082 }
1083 
dialogsAllowed()1084 bool KHTMLView::dialogsAllowed()
1085 {
1086     bool allowed = d->m_dialogsAllowed;
1087     KHTMLPart *p = m_part->parentPart();
1088     if (p && p->view()) {
1089         allowed &= p->view()->dialogsAllowed();
1090     }
1091     return allowed;
1092 }
1093 
closeEvent(QCloseEvent * ev)1094 void KHTMLView::closeEvent(QCloseEvent *ev)
1095 {
1096     closeChildDialogs();
1097     QScrollArea::closeEvent(ev);
1098 }
1099 
setZoomLevel(int percent)1100 void KHTMLView::setZoomLevel(int percent)
1101 {
1102     percent = percent < 20 ? 20 : (percent > 800 ? 800 : percent);
1103     int oldpercent = d->zoomLevel;
1104     d->zoomLevel = percent;
1105     if (percent != oldpercent) {
1106         if (d->layoutSchedulingEnabled) {
1107             layout();
1108         }
1109         widget()->update();
1110     }
1111 }
1112 
zoomLevel() const1113 int KHTMLView::zoomLevel() const
1114 {
1115     return d->zoomLevel;
1116 }
1117 
setSmoothScrollingMode(SmoothScrollingMode m)1118 void KHTMLView::setSmoothScrollingMode(SmoothScrollingMode m)
1119 {
1120     d->smoothScrollMode = m;
1121     d->smoothScrollModeIsDefault = false;
1122     if (d->smoothScrolling && !m) {
1123         d->stopScrolling();
1124     }
1125 }
1126 
setSmoothScrollingModeDefault(SmoothScrollingMode m)1127 void KHTMLView::setSmoothScrollingModeDefault(SmoothScrollingMode m)
1128 {
1129     // check for manual override
1130     if (!d->smoothScrollModeIsDefault) {
1131         return;
1132     }
1133     d->smoothScrollMode = m;
1134     if (d->smoothScrolling && !m) {
1135         d->stopScrolling();
1136     }
1137 }
1138 
smoothScrollingMode() const1139 KHTMLView::SmoothScrollingMode KHTMLView::smoothScrollingMode() const
1140 {
1141     return d->smoothScrollMode;
1142 }
1143 
1144 //
1145 // Event Handling
1146 //
1147 /////////////////
1148 
mousePressEvent(QMouseEvent * _mouse)1149 void KHTMLView::mousePressEvent(QMouseEvent *_mouse)
1150 {
1151     if (!m_part->xmlDocImpl()) {
1152         return;
1153     }
1154     if (d->possibleTripleClick && (_mouse->button() & Qt::MouseButtonMask) == Qt::LeftButton) {
1155         mouseDoubleClickEvent(_mouse);   // it handles triple clicks too
1156         return;
1157     }
1158 
1159     int xm = _mouse->x();
1160     int ym = _mouse->y();
1161     revertTransforms(xm, ym);
1162 
1163     // qCDebug(KHTML_LOG) << "mousePressEvent: viewport=("<<_mouse->x()-contentsX()<<"/"<<_mouse->y()-contentsY()<<"), contents=(" << xm << "/" << ym << ")\n";
1164 
1165     d->isDoubleClick = false;
1166 
1167     DOM::NodeImpl::MouseEvent mev(_mouse->buttons(), DOM::NodeImpl::MousePress);
1168     m_part->xmlDocImpl()->prepareMouseEvent(false, xm, ym, &mev);
1169 
1170     //qCDebug(KHTML_LOG) << "innerNode="<<mev.innerNode.nodeName().string();
1171 
1172     if ((_mouse->button() == Qt::MidButton) &&
1173             !m_part->d->m_bOpenMiddleClick && !d->m_mouseScrollTimer &&
1174             mev.url.isNull() && (mev.innerNode.elementId() != ID_INPUT)) {
1175         QPoint point = mapFromGlobal(_mouse->globalPos());
1176 
1177         d->m_mouseScroll_byX = 0;
1178         d->m_mouseScroll_byY = 0;
1179 
1180         d->m_mouseScrollTimer = new QTimer(this);
1181         connect(d->m_mouseScrollTimer, SIGNAL(timeout()), this, SLOT(slotMouseScrollTimer()));
1182 
1183         if (!d->m_mouseScrollIndicator) {
1184             QPixmap pixmap(48, 48), icon;
1185             pixmap.fill(QColor(qRgba(127, 127, 127, 127)));
1186 
1187             QPainter p(&pixmap);
1188             QStyleOption option;
1189 
1190             option.rect.setRect(16, 0, 16, 16);
1191             QApplication::style()->drawPrimitive(QStyle::PE_IndicatorArrowUp, &option, &p);
1192             option.rect.setRect(0, 16, 16, 16);
1193             QApplication::style()->drawPrimitive(QStyle::PE_IndicatorArrowLeft, &option, &p);
1194             option.rect.setRect(16, 32, 16, 16);
1195             QApplication::style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &option, &p);
1196             option.rect.setRect(32, 16, 16, 16);
1197             QApplication::style()->drawPrimitive(QStyle::PE_IndicatorArrowRight, &option, &p);
1198             p.drawEllipse(23, 23, 2, 2);
1199 
1200             d->m_mouseScrollIndicator = new QWidget(this);
1201             d->m_mouseScrollIndicator->setFixedSize(48, 48);
1202             QPalette palette;
1203             palette.setBrush(d->m_mouseScrollIndicator->backgroundRole(), QBrush(pixmap));
1204             d->m_mouseScrollIndicator->setPalette(palette);
1205         }
1206         d->m_mouseScrollIndicator->move(point.x() - 24, point.y() - 24);
1207 
1208         bool hasHorBar = visibleWidth() < contentsWidth();
1209         bool hasVerBar = visibleHeight() < contentsHeight();
1210 
1211         KConfigGroup cg(KSharedConfig::openConfig(), "HTML Settings");
1212         if (cg.readEntry("ShowMouseScrollIndicator", true)) {
1213             d->m_mouseScrollIndicator->show();
1214             d->m_mouseScrollIndicator->unsetCursor();
1215 
1216             QBitmap mask = d->m_mouseScrollIndicator->palette().brush(d->m_mouseScrollIndicator->backgroundRole()).texture().createHeuristicMask(true);
1217 
1218             if (hasHorBar && !hasVerBar) {
1219                 QBitmap bm(16, 16);
1220                 bm.clear();
1221                 QPainter painter(&mask);
1222                 painter.drawPixmap(QRectF(16, 0, bm.width(), bm.height()), bm, bm.rect());
1223                 painter.drawPixmap(QRectF(16, 32, bm.width(), bm.height()), bm, bm.rect());
1224                 d->m_mouseScrollIndicator->setCursor(Qt::SizeHorCursor);
1225             } else if (!hasHorBar && hasVerBar) {
1226                 QBitmap bm(16, 16);
1227                 bm.clear();
1228                 QPainter painter(&mask);
1229                 painter.drawPixmap(QRectF(0, 16, bm.width(), bm.height()), bm, bm.rect());
1230                 painter.drawPixmap(QRectF(32, 16, bm.width(), bm.height()), bm, bm.rect());
1231                 d->m_mouseScrollIndicator->setCursor(Qt::SizeVerCursor);
1232             } else {
1233                 d->m_mouseScrollIndicator->setCursor(Qt::SizeAllCursor);
1234             }
1235 
1236             d->m_mouseScrollIndicator->setMask(mask);
1237         } else {
1238             if (hasHorBar && !hasVerBar) {
1239                 viewport()->setCursor(Qt::SizeHorCursor);
1240             } else if (!hasHorBar && hasVerBar) {
1241                 viewport()->setCursor(Qt::SizeVerCursor);
1242             } else {
1243                 viewport()->setCursor(Qt::SizeAllCursor);
1244             }
1245         }
1246 
1247         return;
1248     } else if (d->m_mouseScrollTimer) {
1249         delete d->m_mouseScrollTimer;
1250         d->m_mouseScrollTimer = nullptr;
1251 
1252         if (d->m_mouseScrollIndicator) {
1253             d->m_mouseScrollIndicator->hide();
1254         }
1255     }
1256 
1257     if (d->clickCount > 0 &&
1258             QPoint(d->clickX - xm, d->clickY - ym).manhattanLength() <= QApplication::startDragDistance()) {
1259         d->clickCount++;
1260     } else {
1261         d->clickCount = 1;
1262         d->clickX = xm;
1263         d->clickY = ym;
1264     }
1265 
1266     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT, mev.innerNode.handle(), mev.innerNonSharedNode.handle(), true,
1267                                            d->clickCount, _mouse, true, DOM::NodeImpl::MousePress);
1268 
1269     if (!swallowEvent) {
1270         emit m_part->nodeActivated(mev.innerNode);
1271 
1272         khtml::MousePressEvent event(_mouse, xm, ym, mev.url, mev.target, mev.innerNode);
1273         QApplication::sendEvent(m_part, &event);
1274         // we might be deleted after this
1275     }
1276 }
1277 
mouseDoubleClickEvent(QMouseEvent * _mouse)1278 void KHTMLView::mouseDoubleClickEvent(QMouseEvent *_mouse)
1279 {
1280     if (!m_part->xmlDocImpl()) {
1281         return;
1282     }
1283 
1284     int xm = _mouse->x();
1285     int ym = _mouse->y();
1286     revertTransforms(xm, ym);
1287 
1288     // qCDebug(KHTML_LOG) << "mouseDblClickEvent: x=" << xm << ", y=" << ym;
1289 
1290     d->isDoubleClick = true;
1291 
1292     DOM::NodeImpl::MouseEvent mev(_mouse->buttons(), DOM::NodeImpl::MouseDblClick);
1293     m_part->xmlDocImpl()->prepareMouseEvent(false, xm, ym, &mev);
1294 
1295     // We do the same thing as mousePressEvent() here, since the DOM does not treat
1296     // single and double-click events as separate (only the detail, i.e. number of clicks differs)
1297     if (d->clickCount > 0 &&
1298             QPoint(d->clickX - xm, d->clickY - ym).manhattanLength() <= QApplication::startDragDistance()) {
1299         d->clickCount++;
1300     } else { // shouldn't happen, if Qt has the same criterias for double clicks.
1301         d->clickCount = 1;
1302         d->clickX = xm;
1303         d->clickY = ym;
1304     }
1305     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT, mev.innerNode.handle(), mev.innerNonSharedNode.handle(), true,
1306                                            d->clickCount, _mouse, true, DOM::NodeImpl::MouseDblClick);
1307 
1308     if (!swallowEvent) {
1309         khtml::MouseDoubleClickEvent event(_mouse, xm, ym, mev.url, mev.target, mev.innerNode, d->clickCount);
1310         QApplication::sendEvent(m_part, &event);
1311     }
1312 
1313     d->possibleTripleClick = true;
1314     QTimer::singleShot(QApplication::doubleClickInterval(), this, SLOT(tripleClickTimeout()));
1315 }
1316 
tripleClickTimeout()1317 void KHTMLView::tripleClickTimeout()
1318 {
1319     d->possibleTripleClick = false;
1320     d->clickCount = 0;
1321 }
1322 
targetOpensNewWindow(KHTMLPart * part,QString target)1323 static bool targetOpensNewWindow(KHTMLPart *part, QString target)
1324 {
1325     if (!target.isEmpty() && (target.toLower() != "_top") &&
1326             (target.toLower() != "_self") && (target.toLower() != "_parent")) {
1327         if (target.toLower() == "_blank") {
1328             return true;
1329         } else {
1330             while (part->parentPart()) {
1331                 part = part->parentPart();
1332             }
1333             if (!part->frameExists(target)) {
1334                 return true;
1335             }
1336         }
1337     }
1338     return false;
1339 }
1340 
mouseMoveEvent(QMouseEvent * _mouse)1341 void KHTMLView::mouseMoveEvent(QMouseEvent *_mouse)
1342 {
1343     if (d->m_mouseScrollTimer) {
1344         QPoint point = mapFromGlobal(_mouse->globalPos());
1345 
1346         int deltaX = point.x() - d->m_mouseScrollIndicator->x() - 24;
1347         int deltaY = point.y() - d->m_mouseScrollIndicator->y() - 24;
1348 
1349         (deltaX > 0) ? d->m_mouseScroll_byX = 1 : d->m_mouseScroll_byX = -1;
1350         (deltaY > 0) ? d->m_mouseScroll_byY = 1 : d->m_mouseScroll_byY = -1;
1351 
1352         double adX = qAbs(deltaX) / 30.0;
1353         double adY = qAbs(deltaY) / 30.0;
1354 
1355         d->m_mouseScroll_byX = qMax(qMin(d->m_mouseScroll_byX * int(adX * adX), SHRT_MAX), SHRT_MIN);
1356         d->m_mouseScroll_byY = qMax(qMin(d->m_mouseScroll_byY * int(adY * adY), SHRT_MAX), SHRT_MIN);
1357 
1358         if (d->m_mouseScroll_byX == 0 && d->m_mouseScroll_byY == 0) {
1359             d->m_mouseScrollTimer->stop();
1360         } else if (!d->m_mouseScrollTimer->isActive()) {
1361             d->m_mouseScrollTimer->start(20);
1362         }
1363     }
1364 
1365     if (!m_part->xmlDocImpl()) {
1366         return;
1367     }
1368 
1369     int xm = _mouse->x();
1370     int ym = _mouse->y();
1371     revertTransforms(xm, ym);
1372 
1373     DOM::NodeImpl::MouseEvent mev(_mouse->buttons(), DOM::NodeImpl::MouseMove);
1374     // Do not modify :hover/:active state while mouse is pressed.
1375     m_part->xmlDocImpl()->prepareMouseEvent(_mouse->buttons() /*readonly ?*/, xm, ym, &mev);
1376 
1377     // qCDebug(KHTML_LOG) << "mouse move: " << _mouse->pos()
1378     //        << " button " << _mouse->button()
1379     //        << " state " << _mouse->state();
1380 
1381     DOM::NodeImpl *target = mev.innerNode.handle();
1382     DOM::NodeImpl *fn = m_part->xmlDocImpl()->focusNode();
1383 
1384     // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved)
1385     if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget()) {
1386         target = fn;
1387     }
1388 
1389     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEMOVE_EVENT, target, mev.innerNonSharedNode.handle(), false,
1390                                            0, _mouse, true, DOM::NodeImpl::MouseMove);
1391 
1392     if (d->clickCount > 0 &&
1393             QPoint(d->clickX - xm, d->clickY - ym).manhattanLength() > QApplication::startDragDistance()) {
1394         d->clickCount = 0;  // moving the mouse outside the threshold invalidates the click
1395     }
1396 
1397     khtml::RenderObject *r = target ? target->renderer() : nullptr;
1398     bool setCursor = true;
1399     bool forceDefault = false;
1400     if (r && r->isWidget()) {
1401         RenderWidget *rw = static_cast<RenderWidget *>(r);
1402         KHTMLWidget *kw = qobject_cast<KHTMLView *>(rw->widget()) ? dynamic_cast<KHTMLWidget *>(rw->widget()) : nullptr;
1403         if (kw && kw->m_kwp->isRedirected()) {
1404             setCursor = false;
1405         } else if (QLineEdit *le = qobject_cast<QLineEdit *>(rw->widget())) {
1406             QList<QWidget *> wl = le->findChildren<QWidget *>("KLineEditButton");
1407             // force arrow cursor above lineedit clear button
1408             foreach (QWidget *w, wl) {
1409                 if (w->underMouse()) {
1410                     forceDefault = true;
1411                     break;
1412                 }
1413             }
1414         } else if (QTextEdit *te = qobject_cast<QTextEdit *>(rw->widget())) {
1415             if (te->verticalScrollBar()->underMouse() || te->horizontalScrollBar()->underMouse()) {
1416                 forceDefault = true;
1417             }
1418         }
1419     }
1420     khtml::RenderStyle *style = (r && r->style()) ? r->style() : nullptr;
1421     QCursor c;
1422     LinkCursor linkCursor = LINK_NORMAL;
1423     switch (!forceDefault ? (style ? style->cursor() : CURSOR_AUTO) : CURSOR_DEFAULT) {
1424     case CURSOR_AUTO:
1425         if (r && r->isText() && ((m_part->d->m_bMousePressed && m_part->d->editor_context.m_beganSelectingText) ||
1426                                  !r->isPointInsideSelection(xm, ym, m_part->caret()))) {
1427             c = QCursor(Qt::IBeamCursor);
1428         }
1429         if (mev.url.length() && m_part->settings()->changeCursor()) {
1430             c = m_part->urlCursor();
1431             if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@') > 0) {
1432                 linkCursor = LINK_MAILTO;
1433             } else if (targetOpensNewWindow(m_part, mev.target.string())) {
1434                 linkCursor = LINK_NEWWINDOW;
1435             }
1436         }
1437 
1438         if (r && r->isFrameSet() && !static_cast<RenderFrameSet *>(r)->noResize()) {
1439             c = QCursor(static_cast<RenderFrameSet *>(r)->cursorShape());
1440         }
1441 
1442         break;
1443     case CURSOR_CROSS:
1444         c = QCursor(Qt::CrossCursor);
1445         break;
1446     case CURSOR_POINTER:
1447         c = m_part->urlCursor();
1448         if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@') > 0) {
1449             linkCursor = LINK_MAILTO;
1450         } else if (targetOpensNewWindow(m_part, mev.target.string())) {
1451             linkCursor = LINK_NEWWINDOW;
1452         }
1453         break;
1454     case CURSOR_PROGRESS:
1455         c = QCursor(Qt::BusyCursor); // working_cursor
1456         break;
1457     case CURSOR_MOVE:
1458     case CURSOR_ALL_SCROLL:
1459         c = QCursor(Qt::SizeAllCursor);
1460         break;
1461     case CURSOR_E_RESIZE:
1462     case CURSOR_W_RESIZE:
1463     case CURSOR_EW_RESIZE:
1464         c = QCursor(Qt::SizeHorCursor);
1465         break;
1466     case CURSOR_N_RESIZE:
1467     case CURSOR_S_RESIZE:
1468     case CURSOR_NS_RESIZE:
1469         c = QCursor(Qt::SizeVerCursor);
1470         break;
1471     case CURSOR_NE_RESIZE:
1472     case CURSOR_SW_RESIZE:
1473     case CURSOR_NESW_RESIZE:
1474         c = QCursor(Qt::SizeBDiagCursor);
1475         break;
1476     case CURSOR_NW_RESIZE:
1477     case CURSOR_SE_RESIZE:
1478     case CURSOR_NWSE_RESIZE:
1479         c = QCursor(Qt::SizeFDiagCursor);
1480         break;
1481     case CURSOR_TEXT:
1482         c = QCursor(Qt::IBeamCursor);
1483         break;
1484     case CURSOR_WAIT:
1485         c = QCursor(Qt::WaitCursor);
1486         break;
1487     case CURSOR_HELP:
1488         c = QCursor(Qt::WhatsThisCursor);
1489         break;
1490     case CURSOR_DEFAULT:
1491         break;
1492     case CURSOR_NONE:
1493     case CURSOR_NOT_ALLOWED:
1494         c = QCursor(Qt::ForbiddenCursor);
1495         break;
1496     case CURSOR_ROW_RESIZE:
1497         c = QCursor(Qt::SplitVCursor);
1498         break;
1499     case CURSOR_COL_RESIZE:
1500         c = QCursor(Qt::SplitHCursor);
1501         break;
1502     case CURSOR_VERTICAL_TEXT:
1503     case CURSOR_CONTEXT_MENU:
1504     case CURSOR_NO_DROP:
1505     case CURSOR_CELL:
1506     case CURSOR_COPY:
1507     case CURSOR_ALIAS:
1508         c = QCursor(Qt::ArrowCursor);
1509         break;
1510     }
1511 
1512     if (!setCursor && style && style->cursor() != CURSOR_AUTO) {
1513         setCursor = true;
1514     }
1515 
1516     QWidget *vp = viewport();
1517     for (KHTMLPart *p = m_part; p; p = p->parentPart())
1518         if (!p->parentPart()) {
1519             vp = p->view()->viewport();
1520         }
1521     if (setCursor && (vp->cursor().shape() != c.shape() || c.shape() == Qt::BitmapCursor)) {
1522         if (c.shape() == Qt::ArrowCursor) {
1523             for (KHTMLPart *p = m_part; p; p = p->parentPart()) {
1524                 p->view()->viewport()->unsetCursor();
1525             }
1526         } else {
1527             vp->setCursor(c);
1528         }
1529     }
1530 
1531     if (linkCursor != LINK_NORMAL && isVisible() && hasFocus()) {
1532 #if HAVE_X11
1533 
1534         if (!d->cursorIconWidget) {
1535 #if HAVE_X11
1536             d->cursorIconWidget = new QLabel(nullptr, Qt::X11BypassWindowManagerHint);
1537             XSetWindowAttributes attr;
1538             attr.save_under = True;
1539             XChangeWindowAttributes(QX11Info::display(), d->cursorIconWidget->winId(), CWSaveUnder, &attr);
1540 #else
1541             d->cursorIconWidget = new QLabel(NULL, NULL);
1542             //TODO
1543 #endif
1544         }
1545 
1546         // Update the pixmap if need be.
1547         if (linkCursor != d->cursorIconType) {
1548             d->cursorIconType = linkCursor;
1549             QString cursorIcon;
1550             switch (linkCursor) {
1551             case LINK_MAILTO:     cursorIcon = "mail-message-new"; break;
1552             case LINK_NEWWINDOW:  cursorIcon = "window-new";       break;
1553             default:              cursorIcon = "dialog-error";     break;
1554             }
1555 
1556             QPixmap icon_pixmap = KHTMLGlobal::iconLoader()->loadIcon(cursorIcon, KIconLoader::Small, 0, KIconLoader::DefaultState, QStringList(), nullptr, true);
1557 
1558             d->cursorIconWidget->resize(icon_pixmap.width(), icon_pixmap.height());
1559             d->cursorIconWidget->setMask(icon_pixmap.createMaskFromColor(Qt::transparent));
1560             d->cursorIconWidget->setPixmap(icon_pixmap);
1561             d->cursorIconWidget->update();
1562         }
1563 
1564         QPoint c_pos = QCursor::pos();
1565         d->cursorIconWidget->move(c_pos.x() + 15, c_pos.y() + 15);
1566 #if HAVE_X11
1567         XRaiseWindow(QX11Info::display(), d->cursorIconWidget->winId());
1568         QApplication::flush();
1569 #elif defined(Q_OS_WIN)
1570         SetWindowPos(d->cursorIconWidget->winId(), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
1571 #else
1572         //TODO?
1573 #endif
1574         d->cursorIconWidget->show();
1575 #endif
1576     } else if (d->cursorIconWidget) {
1577         d->cursorIconWidget->hide();
1578     }
1579 
1580     if (r && r->isWidget()) {
1581         _mouse->ignore();
1582     }
1583 
1584     if (!swallowEvent) {
1585         khtml::MouseMoveEvent event(_mouse, xm, ym, mev.url, mev.target, mev.innerNode);
1586         QApplication::sendEvent(m_part, &event);
1587     }
1588 }
1589 
mouseReleaseEvent(QMouseEvent * _mouse)1590 void KHTMLView::mouseReleaseEvent(QMouseEvent *_mouse)
1591 {
1592     bool swallowEvent = false;
1593 
1594     int xm = _mouse->x();
1595     int ym = _mouse->y();
1596     revertTransforms(xm, ym);
1597 
1598     DOM::NodeImpl::MouseEvent mev(_mouse->buttons(), DOM::NodeImpl::MouseRelease);
1599 
1600     if (m_part->xmlDocImpl()) {
1601         m_part->xmlDocImpl()->prepareMouseEvent(false, xm, ym, &mev);
1602 
1603         DOM::NodeImpl *target = mev.innerNode.handle();
1604         DOM::NodeImpl *fn = m_part->xmlDocImpl()->focusNode();
1605 
1606         // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved)
1607         if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget()) {
1608             target = fn;
1609         }
1610 
1611         swallowEvent = dispatchMouseEvent(EventImpl::MOUSEUP_EVENT, target, mev.innerNonSharedNode.handle(), true,
1612                                           d->clickCount, _mouse, false, DOM::NodeImpl::MouseRelease);
1613 
1614         // clear our sticky event target on any mouseRelease event
1615         if (d->m_mouseEventsTarget) {
1616             d->m_mouseEventsTarget = nullptr;
1617         }
1618 
1619         if (d->clickCount > 0 &&
1620                 QPoint(d->clickX - xm, d->clickY - ym).manhattanLength() <= QApplication::startDragDistance()) {
1621             QMouseEvent me(d->isDoubleClick ? QEvent::MouseButtonDblClick : QEvent::MouseButtonRelease,
1622                            _mouse->pos(), _mouse->button(), _mouse->buttons(), _mouse->modifiers());
1623             dispatchMouseEvent(EventImpl::CLICK_EVENT, mev.innerNode.handle(), mev.innerNonSharedNode.handle(), true,
1624                                d->clickCount, &me, true, DOM::NodeImpl::MouseRelease);
1625         }
1626 
1627         khtml::RenderObject *r = target ? target->renderer() : nullptr;
1628         if (r && r->isWidget()) {
1629             _mouse->ignore();
1630         }
1631     }
1632 
1633     if (!swallowEvent) {
1634         khtml::MouseReleaseEvent event(_mouse, xm, ym, mev.url, mev.target, mev.innerNode);
1635         QApplication::sendEvent(m_part, &event);
1636     }
1637 }
1638 
1639 // returns true if event should be swallowed
dispatchKeyEvent(QKeyEvent * _ke)1640 bool KHTMLView::dispatchKeyEvent(QKeyEvent *_ke)
1641 {
1642     if (!m_part->xmlDocImpl()) {
1643         return false;
1644     }
1645     // Pressing and releasing a key should generate keydown, keypress and keyup events
1646     // Holding it down should generated keydown, keypress (repeatedly) and keyup events
1647     // The problem here is that Qt generates two autorepeat events (keyrelease+keypress)
1648     // for autorepeating, while DOM wants only one autorepeat event (keypress), so one
1649     // of the Qt events shouldn't be passed to DOM, but it should be still filtered
1650     // out if DOM would filter the autorepeat event. Additional problem is that Qt keyrelease
1651     // events don't have text() set (Qt bug?), so DOM often would ignore the keypress event
1652     // if it was created using Qt keyrelease, but Qt autorepeat keyrelease comes
1653     // before Qt autorepeat keypress (i.e. problem whether to filter it out or not).
1654     // The solution is to filter out and postpone the Qt autorepeat keyrelease until
1655     // the following Qt keypress event comes. If DOM accepts the DOM keypress event,
1656     // the postponed event will be simply discarded. If not, it will be passed to keyPressEvent()
1657     // again, and here it will be ignored.
1658     //
1659     //  Qt:      Press      | Release(autorepeat) Press(autorepeat) etc. |   Release
1660     //  DOM:   Down + Press |      (nothing)           Press             |     Up
1661 
1662     // It's also possible to get only Releases. E.g. the release of alt-tab,
1663     // or when the keypresses get captured by an accel.
1664 
1665     if (_ke == d->postponed_autorepeat) { // replayed event
1666         return false;
1667     }
1668 
1669     if (_ke->type() == QEvent::KeyPress) {
1670         if (!_ke->isAutoRepeat()) {
1671             bool ret = dispatchKeyEventHelper(_ke, false);   // keydown
1672             // don't send keypress even if keydown was blocked, like IE (and unlike Mozilla)
1673             if (!ret && dispatchKeyEventHelper(_ke, true)) { // keypress
1674                 ret = true;
1675             }
1676             return ret;
1677         } else { // autorepeat
1678             bool ret = dispatchKeyEventHelper(_ke, true);   // keypress
1679             if (!ret && d->postponed_autorepeat) {
1680                 keyPressEvent(d->postponed_autorepeat);
1681             }
1682             delete d->postponed_autorepeat;
1683             d->postponed_autorepeat = nullptr;
1684             return ret;
1685         }
1686     } else { // QEvent::KeyRelease
1687         // Discard postponed "autorepeat key-release" events that didn't see
1688         // a keypress after them (e.g. due to QAccel)
1689         delete d->postponed_autorepeat;
1690         d->postponed_autorepeat = nullptr;
1691 
1692         if (!_ke->isAutoRepeat()) {
1693             return dispatchKeyEventHelper(_ke, false);   // keyup
1694         } else {
1695             d->postponed_autorepeat = new QKeyEvent(_ke->type(), _ke->key(), _ke->modifiers(),
1696                                                     _ke->text(), _ke->isAutoRepeat(), _ke->count());
1697             if (_ke->isAccepted()) {
1698                 d->postponed_autorepeat->accept();
1699             } else {
1700                 d->postponed_autorepeat->ignore();
1701             }
1702             return true;
1703         }
1704     }
1705 }
1706 
1707 // returns true if event should be swallowed
dispatchKeyEventHelper(QKeyEvent * _ke,bool keypress)1708 bool KHTMLView::dispatchKeyEventHelper(QKeyEvent *_ke, bool keypress)
1709 {
1710     DOM::NodeImpl *keyNode = m_part->xmlDocImpl()->focusNode();
1711     if (keyNode) {
1712         return keyNode->dispatchKeyEvent(_ke, keypress);
1713     } else { // no focused node, send to document
1714         return m_part->xmlDocImpl()->dispatchKeyEvent(_ke, keypress);
1715     }
1716 }
1717 
keyPressEvent(QKeyEvent * _ke)1718 void KHTMLView::keyPressEvent(QKeyEvent *_ke)
1719 {
1720     // If CTRL was hit, be prepared for access keys
1721     if (d->accessKeysEnabled && _ke->key() == Qt::Key_Control && !(_ke->modifiers() & ~Qt::ControlModifier) && !d->accessKeysActivated) {
1722         d->accessKeysPreActivate = true;
1723         _ke->accept();
1724         return;
1725     }
1726 
1727     if (_ke->key() == Qt::Key_Shift && !(_ke->modifiers() & ~Qt::ShiftModifier)) {
1728         d->scrollSuspendPreActivate = true;
1729     }
1730 
1731     // accesskey handling needs to be done before dispatching, otherwise e.g. lineedits
1732     // may eat the event
1733 
1734     if (d->accessKeysEnabled && d->accessKeysActivated) {
1735         int state = (_ke->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier));
1736         if (state == 0 || state == Qt::ShiftModifier) {
1737             if (_ke->key() != Qt::Key_Shift) {
1738                 accessKeysTimeout();
1739             }
1740             handleAccessKey(_ke);
1741             _ke->accept();
1742             return;
1743         }
1744         accessKeysTimeout();
1745         _ke->accept();
1746         return;
1747     }
1748 
1749     if (dispatchKeyEvent(_ke)) {
1750         // If either keydown or keypress was accepted by a widget, or canceled by JS, stop here.
1751         _ke->accept();
1752         return;
1753     }
1754 
1755     int offs = (viewport()->height() < 30) ? viewport()->height() : 30; // ### ??
1756     if (_ke->modifiers() & Qt::ShiftModifier)
1757         switch (_ke->key()) {
1758         case Qt::Key_Space:
1759             verticalScrollBar()->setValue(verticalScrollBar()->value() - viewport()->height() + offs);
1760             if (d->scrollSuspended) {
1761                 d->newScrollTimer(this, 0);
1762             }
1763             break;
1764 
1765         case Qt::Key_Down:
1766         case Qt::Key_J:
1767             d->adjustScroller(this, KHTMLViewPrivate::ScrollDown, KHTMLViewPrivate::ScrollUp);
1768             break;
1769 
1770         case Qt::Key_Up:
1771         case Qt::Key_K:
1772             d->adjustScroller(this, KHTMLViewPrivate::ScrollUp, KHTMLViewPrivate::ScrollDown);
1773             break;
1774 
1775         case Qt::Key_Left:
1776         case Qt::Key_H:
1777             d->adjustScroller(this, KHTMLViewPrivate::ScrollLeft, KHTMLViewPrivate::ScrollRight);
1778             break;
1779 
1780         case Qt::Key_Right:
1781         case Qt::Key_L:
1782             d->adjustScroller(this, KHTMLViewPrivate::ScrollRight, KHTMLViewPrivate::ScrollLeft);
1783             break;
1784         }
1785     else
1786         switch (_ke->key()) {
1787         case Qt::Key_Down:
1788         case Qt::Key_J:
1789             if (!d->scrollTimerId || d->scrollSuspended) {
1790                 verticalScrollBar()->setValue(verticalScrollBar()->value() + 10);
1791             }
1792             if (d->scrollTimerId) {
1793                 d->newScrollTimer(this, 0);
1794             }
1795             break;
1796 
1797         case Qt::Key_Space:
1798         case Qt::Key_PageDown:
1799             d->shouldSmoothScroll = true;
1800             verticalScrollBar()->setValue(verticalScrollBar()->value() + viewport()->height() - offs);
1801             if (d->scrollSuspended) {
1802                 d->newScrollTimer(this, 0);
1803             }
1804             break;
1805 
1806         case Qt::Key_Up:
1807         case Qt::Key_K:
1808             if (!d->scrollTimerId || d->scrollSuspended) {
1809                 verticalScrollBar()->setValue(verticalScrollBar()->value() - 10);
1810             }
1811             if (d->scrollTimerId) {
1812                 d->newScrollTimer(this, 0);
1813             }
1814             break;
1815 
1816         case Qt::Key_PageUp:
1817             d->shouldSmoothScroll = true;
1818             verticalScrollBar()->setValue(verticalScrollBar()->value() - viewport()->height() + offs);
1819             if (d->scrollSuspended) {
1820                 d->newScrollTimer(this, 0);
1821             }
1822             break;
1823         case Qt::Key_Right:
1824         case Qt::Key_L:
1825             if (!d->scrollTimerId || d->scrollSuspended) {
1826                 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 10);
1827             }
1828             if (d->scrollTimerId) {
1829                 d->newScrollTimer(this, 0);
1830             }
1831             break;
1832 
1833         case Qt::Key_Left:
1834         case Qt::Key_H:
1835             if (!d->scrollTimerId || d->scrollSuspended) {
1836                 horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 10);
1837             }
1838             if (d->scrollTimerId) {
1839                 d->newScrollTimer(this, 0);
1840             }
1841             break;
1842         case Qt::Key_Enter:
1843         case Qt::Key_Return:
1844             // ### FIXME:
1845             // or even better to HTMLAnchorElementImpl::event()
1846             if (m_part->xmlDocImpl()) {
1847                 NodeImpl *n = m_part->xmlDocImpl()->focusNode();
1848                 if (n) {
1849                     n->setActive();
1850                 }
1851             }
1852             break;
1853         case Qt::Key_Home:
1854             verticalScrollBar()->setValue(0);
1855             horizontalScrollBar()->setValue(0);
1856             if (d->scrollSuspended) {
1857                 d->newScrollTimer(this, 0);
1858             }
1859             break;
1860         case Qt::Key_End:
1861             verticalScrollBar()->setValue(contentsHeight() - visibleHeight());
1862             if (d->scrollSuspended) {
1863                 d->newScrollTimer(this, 0);
1864             }
1865             break;
1866         case Qt::Key_Shift:
1867             // what are you doing here?
1868             _ke->ignore();
1869             return;
1870         default:
1871             if (d->scrollTimerId) {
1872                 d->newScrollTimer(this, 0);
1873             }
1874             _ke->ignore();
1875             return;
1876         }
1877 
1878     _ke->accept();
1879 }
1880 
keyReleaseEvent(QKeyEvent * _ke)1881 void KHTMLView::keyReleaseEvent(QKeyEvent *_ke)
1882 {
1883     if (d->scrollSuspendPreActivate && _ke->key() != Qt::Key_Shift) {
1884         d->scrollSuspendPreActivate = false;
1885     }
1886     if (_ke->key() == Qt::Key_Shift && d->scrollSuspendPreActivate && !(_ke->modifiers() & Qt::ShiftModifier))
1887         if (d->scrollTimerId) {
1888             d->scrollSuspended = !d->scrollSuspended;
1889             if (d->scrollSuspended) {
1890                 d->stopScrolling();
1891             }
1892         }
1893 
1894     if (d->accessKeysEnabled) {
1895         if (d->accessKeysPreActivate && _ke->key() != Qt::Key_Control) {
1896             d->accessKeysPreActivate = false;
1897         }
1898         if (d->accessKeysPreActivate && !(_ke->modifiers() & Qt::ControlModifier)) {
1899             displayAccessKeys();
1900             m_part->setStatusBarText(i18n("Access Keys activated"), KHTMLPart::BarOverrideText);
1901             d->accessKeysActivated = true;
1902             d->accessKeysPreActivate = false;
1903             _ke->accept();
1904             return;
1905         } else if (d->accessKeysActivated) {
1906             accessKeysTimeout();
1907             _ke->accept();
1908             return;
1909         }
1910     }
1911 
1912     // Send keyup event
1913     if (dispatchKeyEvent(_ke)) {
1914         _ke->accept();
1915         return;
1916     }
1917 
1918     QScrollArea::keyReleaseEvent(_ke);
1919 }
1920 
focusNextPrevChild(bool next)1921 bool KHTMLView::focusNextPrevChild(bool next)
1922 {
1923     // Now try to find the next child
1924     if (m_part->xmlDocImpl() && focusNextPrevNode(next)) {
1925         //if (m_part->xmlDocImpl()->focusNode())
1926         // qCDebug(KHTML_LOG) << "focusNode.name: "
1927         //      << m_part->xmlDocImpl()->focusNode()->nodeName().string();
1928         return true; // focus node found
1929     }
1930 
1931     // If we get here, pass tabbing control up to the next/previous child in our parent
1932     d->pseudoFocusNode = KHTMLViewPrivate::PFNone;
1933     if (m_part->parentPart() && m_part->parentPart()->view()) {
1934         return m_part->parentPart()->view()->focusNextPrevChild(next);
1935     }
1936 
1937     return QWidget::focusNextPrevChild(next);
1938 }
1939 
doAutoScroll()1940 void KHTMLView::doAutoScroll()
1941 {
1942     QPoint pos = QCursor::pos();
1943     QPoint off;
1944     KHTMLView *v = m_kwp->isRedirected() ? m_kwp->rootViewPos(off) : this;
1945     pos = v->viewport()->mapFromGlobal(pos);
1946     pos -= off;
1947     int xm, ym;
1948     viewportToContents(pos.x(), pos.y(), xm, ym); // ###
1949 
1950     pos = QPoint(pos.x() - viewport()->x(), pos.y() - viewport()->y());
1951     if ((pos.y() < 0) || (pos.y() > visibleHeight()) ||
1952             (pos.x() < 0) || (pos.x() > visibleWidth())) {
1953         ensureVisible(xm, ym, 0, 5);
1954 
1955 #ifndef KHTML_NO_SELECTION
1956         // extend the selection while scrolling
1957         DOM::Node innerNode;
1958         if (m_part->isExtendingSelection()) {
1959             RenderObject::NodeInfo renderInfo(true/*readonly*/, false/*active*/);
1960             m_part->xmlDocImpl()->renderer()->layer()
1961             ->nodeAtPoint(renderInfo, xm, ym);
1962             innerNode = renderInfo.innerNode();
1963         }/*end if*/
1964 
1965         if (innerNode.handle() && innerNode.handle()->renderer()
1966                 && innerNode.handle()->renderer()->shouldSelect()) {
1967             m_part->extendSelectionTo(xm, ym, innerNode);
1968         }/*end if*/
1969 #endif // KHTML_NO_SELECTION
1970     }
1971 }
1972 
1973 // KHTML defines its own stacking order for any object and thus takes
1974 // control of widget painting whenever it can. This is called "redirection".
1975 //
1976 // Redirected widgets are placed off screen. When they are declared as a child of our view (ChildPolished event),
1977 // an event filter is installed, so as to catch any paint event and translate them as update() of the view's main widget.
1978 //
1979 // Painting also happens spontaneously within widgets. In this case, the widget would update() parts of itself.
1980 // While this ordinarily results in a paintEvent being schedduled, it is not the case with off screen widgets.
1981 // Thus update() is monitored by using the mechanism that deffers any update call happening during a paint event,
1982 // transforming it into a posted UpdateLater event. Hence the need to set Qt::WA_WState_InPaintEvent on redirected widgets.
1983 //
1984 // Once the UpdateLater event has been received, Qt::WA_WState_InPaintEvent is removed and the process continues
1985 // with the update of the corresponding rect on the view. That in turn will make our painting subsystem render()
1986 // the widget at the correct stacking position.
1987 //
1988 // For non-redirected (e.g. external) widgets, z-order is honoured through masking. cf.RenderLayer::updateWidgetMasks
1989 
handleWidget(QWidget * w,KHTMLView * view,bool recurse=true)1990 static void handleWidget(QWidget *w, KHTMLView *view, bool recurse = true)
1991 {
1992     if (w->isWindow()) {
1993         return;
1994     }
1995 
1996     if (!qobject_cast<QFrame *>(w)) {
1997         w->setAttribute(Qt::WA_NoSystemBackground);
1998     }
1999 
2000     w->setAttribute(Qt::WA_WState_InPaintEvent);
2001 
2002     if (!(w->objectName() == "KLineEditButton")) {
2003         w->setAttribute(Qt::WA_OpaquePaintEvent);
2004     }
2005 
2006     w->installEventFilter(view);
2007 
2008     if (!recurse) {
2009         return;
2010     }
2011     if (qobject_cast<KHTMLView *>(w)) {
2012         handleWidget(static_cast<KHTMLView *>(w)->widget(), view, false);
2013         handleWidget(static_cast<KHTMLView *>(w)->horizontalScrollBar(), view, false);
2014         handleWidget(static_cast<KHTMLView *>(w)->verticalScrollBar(), view, false);
2015         return;
2016     }
2017 
2018     QObjectList children = w->children();
2019     foreach (QObject *object, children) {
2020         QWidget *widget = qobject_cast<QWidget *>(object);
2021         if (widget) {
2022             handleWidget(widget, view);
2023         }
2024     }
2025 }
2026 
2027 class KHTMLBackingStoreHackWidget : public QWidget
2028 {
2029 public:
publicEvent(QEvent * e)2030     void publicEvent(QEvent *e)
2031     {
2032         QWidget::event(e);
2033     }
2034 };
2035 
viewportEvent(QEvent * e)2036 bool  KHTMLView::viewportEvent(QEvent *e)
2037 {
2038     switch (e->type()) {
2039     // those must not be dispatched to the specialized handlers
2040     // as widgetEvent() already took care of that
2041     case QEvent::MouseButtonPress:
2042     case QEvent::MouseButtonRelease:
2043     case QEvent::MouseButtonDblClick:
2044     case QEvent::MouseMove:
2045 #ifndef QT_NO_WHEELEVENT
2046     case QEvent::Wheel:
2047 #endif
2048     case QEvent::ContextMenu:
2049     case QEvent::DragEnter:
2050     case QEvent::DragMove:
2051     case QEvent::DragLeave:
2052     case QEvent::Drop:
2053         return false;
2054     default:
2055         break;
2056     }
2057     return QScrollArea::viewportEvent(e);
2058 }
2059 
setInPaintEventFlag(QWidget * w,bool b=true,bool recurse=true)2060 static void setInPaintEventFlag(QWidget *w, bool b = true, bool recurse = true)
2061 {
2062     w->setAttribute(Qt::WA_WState_InPaintEvent, b);
2063 
2064     if (!recurse) {
2065         return;
2066     }
2067     if (qobject_cast<KHTMLView *>(w)) {
2068         setInPaintEventFlag(static_cast<KHTMLView *>(w)->widget(), b, false);
2069         setInPaintEventFlag(static_cast<KHTMLView *>(w)->horizontalScrollBar(), b, false);
2070         setInPaintEventFlag(static_cast<KHTMLView *>(w)->verticalScrollBar(), b, false);
2071         return;
2072     }
2073 
2074     foreach (QObject *cw, w->children()) {
2075         if (cw->isWidgetType() && ! static_cast<QWidget *>(cw)->isWindow()
2076                 && !(static_cast<QWidget *>(cw)->windowModality() & Qt::ApplicationModal)) {
2077             setInPaintEventFlag(static_cast<QWidget *>(cw), b);
2078         }
2079     }
2080 }
2081 
eventFilter(QObject * o,QEvent * e)2082 bool KHTMLView::eventFilter(QObject *o, QEvent *e)
2083 {
2084     if (e->type() == QEvent::ShortcutOverride) {
2085         QKeyEvent *ke = (QKeyEvent *) e;
2086         if (m_part->isEditable() || m_part->isCaretMode()
2087                 || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()
2088                     && m_part->xmlDocImpl()->focusNode()->isContentEditable())) {
2089             if ((ke->modifiers() & Qt::ControlModifier) || (ke->modifiers() & Qt::ShiftModifier)) {
2090                 switch (ke->key()) {
2091                 case Qt::Key_Left:
2092                 case Qt::Key_Right:
2093                 case Qt::Key_Up:
2094                 case Qt::Key_Down:
2095                 case Qt::Key_Home:
2096                 case Qt::Key_End:
2097                     ke->accept();
2098                     return true;
2099                 default:
2100                     break;
2101                 }
2102             }
2103         }
2104     }
2105 
2106     if (e->type() == QEvent::Leave) {
2107         if (d->cursorIconWidget) {
2108             d->cursorIconWidget->hide();
2109         }
2110         m_part->resetHoverText();
2111     }
2112 
2113     QWidget *view = widget();
2114     if (o == view) {
2115         if (widgetEvent(e)) {
2116             return true;
2117         } else if (e->type() == QEvent::Resize) {
2118             updateScrollBars();
2119             return false;
2120         }
2121     } else if (o->isWidgetType()) {
2122         QWidget *v = static_cast<QWidget *>(o);
2123         QWidget *c = v;
2124         while (v && v != view) {
2125             c = v;
2126             v = v->parentWidget();
2127         }
2128         KHTMLWidget *k = dynamic_cast<KHTMLWidget *>(c);
2129         if (v && k && k->m_kwp->isRedirected()) {
2130             bool block = false;
2131             QWidget *w = static_cast<QWidget *>(o);
2132             switch (e->type()) {
2133             case QEvent::UpdateRequest: {
2134                 // implicitly call qt_syncBackingStore(w)
2135                 static_cast<KHTMLBackingStoreHackWidget *>(w)->publicEvent(e);
2136                 block = true;
2137                 break;
2138             }
2139             case QEvent::UpdateLater:
2140             // no break;
2141             case QEvent::Paint:
2142                 if (!allowWidgetPaintEvents) {
2143                     // eat the event. Like this we can control exactly when the widget
2144                     // gets repainted.
2145                     block = true;
2146                     int x = 0, y = 0;
2147                     QWidget *v = w;
2148                     while (v && v->parentWidget() != view) {
2149                         x += v->x();
2150                         y += v->y();
2151                         v = v->parentWidget();
2152                     }
2153 
2154                     QPoint ap = k->m_kwp->absolutePos();
2155                     x += ap.x();
2156                     y += ap.y();
2157                 }
2158                 break;
2159             case QEvent::MouseMove:
2160             case QEvent::MouseButtonPress:
2161             case QEvent::MouseButtonRelease:
2162             case QEvent::MouseButtonDblClick: {
2163 
2164                 if (0 && w->parentWidget() == view && !qobject_cast<QScrollBar *>(w) && !::qobject_cast<QScrollBar *>(w)) {
2165                     QMouseEvent *me = static_cast<QMouseEvent *>(e);
2166                     QPoint pt = w->mapTo(view, me->pos());
2167                     QMouseEvent me2(me->type(), pt, me->button(), me->buttons(), me->modifiers());
2168 
2169                     if (e->type() == QEvent::MouseMove) {
2170                         mouseMoveEvent(&me2);
2171                     } else if (e->type() == QEvent::MouseButtonPress) {
2172                         mousePressEvent(&me2);
2173                     } else if (e->type() == QEvent::MouseButtonRelease) {
2174                         mouseReleaseEvent(&me2);
2175                     } else {
2176                         mouseDoubleClickEvent(&me2);
2177                     }
2178                     block = true;
2179                 }
2180                 break;
2181             }
2182             case QEvent::KeyPress:
2183             case QEvent::KeyRelease:
2184                 if (w->parentWidget() == view && !qobject_cast<QScrollBar *>(w)) {
2185                     QKeyEvent *ke = static_cast<QKeyEvent *>(e);
2186                     if (e->type() == QEvent::KeyPress) {
2187                         keyPressEvent(ke);
2188                         ke->accept();
2189                     } else {
2190                         keyReleaseEvent(ke);
2191                         ke->accept();
2192                     }
2193                     block = true;
2194                 }
2195 
2196                 if (qobject_cast<KUrlRequester *>(w->parentWidget()) &&
2197                         e->type() == QEvent::KeyPress) {
2198                     // Since keypress events on the upload widget will
2199                     // be forwarded to the lineedit anyway,
2200                     // block the original copy at this level to prevent
2201                     // double-emissions of events it doesn't accept
2202                     e->ignore();
2203                     block = true;
2204                 }
2205 
2206                 break;
2207             case QEvent::FocusIn:
2208             case QEvent::FocusOut: {
2209                 QPoint dummy;
2210                 KHTMLView *root = m_kwp->rootViewPos(dummy);
2211                 if (!root) {
2212                     root = this;
2213                 }
2214                 block = static_cast<QFocusEvent *>(e)->reason() != Qt::MouseFocusReason ||  root->underMouse();
2215                 break;
2216             }
2217             default:
2218                 break;
2219             }
2220             if (block) {
2221                 //qDebug("eating event");
2222                 return true;
2223             }
2224         }
2225     }
2226 
2227 //    qCDebug(KHTML_LOG) <<"passing event on to sv event filter object=" << o->className() << " event=" << e->type();
2228     return QScrollArea::eventFilter(o, e);
2229 }
2230 
widgetEvent(QEvent * e)2231 bool KHTMLView::widgetEvent(QEvent *e)
2232 {
2233     switch (e->type()) {
2234     case QEvent::MouseButtonPress:
2235     case QEvent::MouseButtonRelease:
2236     case QEvent::MouseButtonDblClick:
2237     case QEvent::MouseMove:
2238     case QEvent::Paint:
2239 #ifndef QT_NO_WHEELEVENT
2240     case QEvent::Wheel:
2241 #endif
2242     case QEvent::ContextMenu:
2243     case QEvent::DragEnter:
2244     case QEvent::DragMove:
2245     case QEvent::DragLeave:
2246     case QEvent::Drop:
2247         return QFrame::event(e);
2248     case QEvent::ChildPolished: {
2249         // we need to install an event filter on all children of the widget() to
2250         // be able to get correct stacking of children within the document.
2251         QObject *c = static_cast<QChildEvent *>(e)->child();
2252         if (c->isWidgetType()) {
2253             QWidget *w = static_cast<QWidget *>(c);
2254             // don't install the event filter on toplevels
2255             if (!(w->windowFlags() & Qt::Window) && !(w->windowModality() & Qt::ApplicationModal)) {
2256                 KHTMLWidget *k = dynamic_cast<KHTMLWidget *>(w);
2257                 if (k && k->m_kwp->isRedirected()) {
2258                     w->unsetCursor();
2259                     handleWidget(w, this);
2260                 }
2261             }
2262         }
2263         break;
2264     }
2265     case QEvent::Move: {
2266         if (static_cast<QMoveEvent *>(e)->pos() != QPoint(0, 0)) {
2267             widget()->move(0, 0);
2268             updateScrollBars();
2269             return true;
2270         }
2271         break;
2272     }
2273     default:
2274         break;
2275     }
2276     return false;
2277 }
2278 
hasLayoutPending()2279 bool KHTMLView::hasLayoutPending()
2280 {
2281     return d->layoutTimerId && !d->firstLayoutPending;
2282 }
2283 
nodeUnderMouse() const2284 DOM::NodeImpl *KHTMLView::nodeUnderMouse() const
2285 {
2286     return d->underMouse;
2287 }
2288 
nonSharedNodeUnderMouse() const2289 DOM::NodeImpl *KHTMLView::nonSharedNodeUnderMouse() const
2290 {
2291     return d->underMouseNonShared;
2292 }
2293 
scrollTo(const QRect & bounds)2294 bool KHTMLView::scrollTo(const QRect &bounds)
2295 {
2296     d->scrollingSelf = true; // so scroll events get ignored
2297 
2298     int x, y, xe, ye;
2299     x = bounds.left();
2300     y = bounds.top();
2301     xe = bounds.right();
2302     ye = bounds.bottom();
2303 
2304     //qCDebug(KHTML_LOG)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y;
2305 
2306     int deltax;
2307     int deltay;
2308 
2309     int curHeight = visibleHeight();
2310     int curWidth = visibleWidth();
2311 
2312     if (ye - y > curHeight - d->borderY) {
2313         ye  = y + curHeight - d->borderY;
2314     }
2315 
2316     if (xe - x > curWidth - d->borderX) {
2317         xe = x + curWidth - d->borderX;
2318     }
2319 
2320     // is xpos of target left of the view's border?
2321     if (x < contentsX() + d->borderX) {
2322         deltax = x - contentsX() - d->borderX;
2323     }
2324     // is xpos of target right of the view's right border?
2325     else if (xe + d->borderX > contentsX() + curWidth) {
2326         deltax = xe + d->borderX - (contentsX() + curWidth);
2327     } else {
2328         deltax = 0;
2329     }
2330 
2331     // is ypos of target above upper border?
2332     if (y < contentsY() + d->borderY) {
2333         deltay = y - contentsY() - d->borderY;
2334     }
2335     // is ypos of target below lower border?
2336     else if (ye + d->borderY > contentsY() + curHeight) {
2337         deltay = ye + d->borderY - (contentsY() + curHeight);
2338     } else {
2339         deltay = 0;
2340     }
2341 
2342     int maxx = curWidth - d->borderX;
2343     int maxy = curHeight - d->borderY;
2344 
2345     int scrollX, scrollY;
2346 
2347     scrollX = deltax > 0 ? (deltax > maxx ? maxx : deltax) : deltax == 0 ? 0 : (deltax > -maxx ? deltax : -maxx);
2348     scrollY = deltay > 0 ? (deltay > maxy ? maxy : deltay) : deltay == 0 ? 0 : (deltay > -maxy ? deltay : -maxy);
2349 
2350     if (contentsX() + scrollX < 0) {
2351         scrollX = -contentsX();
2352     } else if (contentsWidth() - visibleWidth() - contentsX() < scrollX) {
2353         scrollX = contentsWidth() - visibleWidth() - contentsX();
2354     }
2355 
2356     if (contentsY() + scrollY < 0) {
2357         scrollY = -contentsY();
2358     } else if (contentsHeight() - visibleHeight() - contentsY() < scrollY) {
2359         scrollY = contentsHeight() - visibleHeight() - contentsY();
2360     }
2361 
2362     horizontalScrollBar()->setValue(horizontalScrollBar()->value() + scrollX);
2363     verticalScrollBar()->setValue(verticalScrollBar()->value() + scrollY);
2364 
2365     d->scrollingSelf = false;
2366 
2367     if ((abs(deltax) <= maxx) && (abs(deltay) <= maxy)) {
2368         return true;
2369     } else {
2370         return false;
2371     }
2372 
2373 }
2374 
focusNextPrevNode(bool next)2375 bool KHTMLView::focusNextPrevNode(bool next)
2376 {
2377     // Sets the focus node of the document to be the node after (or if
2378     // next is false, before) the current focus node.  Only nodes that
2379     // are selectable (i.e. for which isFocusable() returns true) are
2380     // taken into account, and the order used is that specified in the
2381     // HTML spec (see DocumentImpl::nextFocusNode() and
2382     // DocumentImpl::previousFocusNode() for details).
2383 
2384     DocumentImpl *doc = m_part->xmlDocImpl();
2385     NodeImpl *oldFocusNode = doc->focusNode();
2386 
2387     // See whether we're in the middle of a detach, or hiding of the
2388     // widget. In this case, we will just clear focus, being careful not to emit events
2389     // or update rendering. Doing this also prevents the code below from going bonkers with
2390     // oldFocusNode not actually being focusable, etc.
2391     if (oldFocusNode) {
2392         if ((oldFocusNode->renderer() && !oldFocusNode->renderer()->parent())
2393                 || !oldFocusNode->isTabFocusable()) {
2394             doc->quietResetFocus();
2395             return true;
2396         }
2397     }
2398 
2399 #if 1
2400     // If the user has scrolled the document, then instead of picking
2401     // the next focusable node in the document, use the first one that
2402     // is within the visible area (if possible).
2403     if (d->scrollBarMoved) {
2404         NodeImpl *toFocus;
2405         if (next) {
2406             toFocus = doc->nextFocusNode(oldFocusNode);
2407         } else {
2408             toFocus = doc->previousFocusNode(oldFocusNode);
2409         }
2410 
2411         if (!toFocus && oldFocusNode) {
2412             if (next) {
2413                 toFocus = doc->nextFocusNode(nullptr);
2414             } else {
2415                 toFocus = doc->previousFocusNode(nullptr);
2416             }
2417         }
2418 
2419         while (toFocus && toFocus != oldFocusNode) {
2420 
2421             QRect focusNodeRect = toFocus->getRect();
2422             if ((focusNodeRect.left() > contentsX()) && (focusNodeRect.right() < contentsX() + visibleWidth()) &&
2423                     (focusNodeRect.top() > contentsY()) && (focusNodeRect.bottom() < contentsY() + visibleHeight())) {
2424                 {
2425                     QRect r = toFocus->getRect();
2426                     ensureVisible(r.right(), r.bottom());
2427                     ensureVisible(r.left(), r.top());
2428                     d->scrollBarMoved = false;
2429                     d->tabMovePending = false;
2430                     d->lastTabbingDirection = next;
2431                     d->pseudoFocusNode = KHTMLViewPrivate::PFNone;
2432                     m_part->xmlDocImpl()->setFocusNode(toFocus);
2433                     Node guard(toFocus);
2434                     if (!toFocus->hasOneRef()) {
2435                         emit m_part->nodeActivated(Node(toFocus));
2436                     }
2437                     return true;
2438                 }
2439             }
2440             if (next) {
2441                 toFocus = doc->nextFocusNode(toFocus);
2442             } else {
2443                 toFocus = doc->previousFocusNode(toFocus);
2444             }
2445 
2446             if (!toFocus && oldFocusNode) {
2447                 if (next) {
2448                     toFocus = doc->nextFocusNode(nullptr);
2449                 } else {
2450                     toFocus = doc->previousFocusNode(nullptr);
2451                 }
2452             }
2453         }
2454 
2455         d->scrollBarMoved = false;
2456     }
2457 #endif
2458 
2459     if (!oldFocusNode && d->pseudoFocusNode == KHTMLViewPrivate::PFNone) {
2460         ensureVisible(contentsX(), next ? 0 : contentsHeight());
2461         d->scrollBarMoved = false;
2462         d->pseudoFocusNode = next ? KHTMLViewPrivate::PFTop : KHTMLViewPrivate::PFBottom;
2463         return true;
2464     }
2465 
2466     NodeImpl *newFocusNode = nullptr;
2467 
2468     if (d->tabMovePending && next != d->lastTabbingDirection) {
2469         //qCDebug(KHTML_LOG) << " tab move pending and tabbing direction changed!\n";
2470         newFocusNode = oldFocusNode;
2471     } else if (next) {
2472         if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFTop) {
2473             newFocusNode = doc->nextFocusNode(oldFocusNode);
2474         }
2475     } else {
2476         if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFBottom) {
2477             newFocusNode = doc->previousFocusNode(oldFocusNode);
2478         }
2479     }
2480 
2481     bool targetVisible = false;
2482     if (!newFocusNode) {
2483         if (next) {
2484             targetVisible = scrollTo(QRect(contentsX() + visibleWidth() / 2, contentsHeight() - d->borderY, 0, 0));
2485         } else {
2486             targetVisible = scrollTo(QRect(contentsX() + visibleWidth() / 2, d->borderY, 0, 0));
2487         }
2488     } else {
2489         // if it's an editable element, activate the caret
2490         if (!m_part->isCaretMode() && newFocusNode->isContentEditable()) {
2491             // qCDebug(KHTML_LOG) << "show caret! fn: " << newFocusNode->nodeName().string();
2492             m_part->clearCaretRectIfNeeded();
2493             m_part->d->editor_context.m_selection.moveTo(Position(newFocusNode, 0L));
2494             m_part->setCaretVisible(true);
2495         } else {
2496             m_part->setCaretVisible(false);
2497             // qCDebug(KHTML_LOG) << "hide caret! fn: " << newFocusNode->nodeName().string();
2498         }
2499         m_part->notifySelectionChanged();
2500 
2501         targetVisible = scrollTo(newFocusNode->getRect());
2502     }
2503 
2504     if (targetVisible) {
2505         //qCDebug(KHTML_LOG) << " target reached.\n";
2506         d->tabMovePending = false;
2507 
2508         m_part->xmlDocImpl()->setFocusNode(newFocusNode);
2509         if (newFocusNode) {
2510             Node guard(newFocusNode);
2511             if (!newFocusNode->hasOneRef()) {
2512                 emit m_part->nodeActivated(Node(newFocusNode));
2513             }
2514             return true;
2515         } else {
2516             d->pseudoFocusNode = next ? KHTMLViewPrivate::PFBottom : KHTMLViewPrivate::PFTop;
2517             return false;
2518         }
2519     } else {
2520         if (!d->tabMovePending) {
2521             d->lastTabbingDirection = next;
2522         }
2523         d->tabMovePending = true;
2524         return true;
2525     }
2526 }
2527 
displayAccessKeys()2528 void KHTMLView::displayAccessKeys()
2529 {
2530     QVector< QChar > taken;
2531     displayAccessKeys(nullptr, this, taken, false);
2532     displayAccessKeys(nullptr, this, taken, true);
2533 }
2534 
displayAccessKeys(KHTMLView * caller,KHTMLView * origview,QVector<QChar> & taken,bool use_fallbacks)2535 void KHTMLView::displayAccessKeys(KHTMLView *caller, KHTMLView *origview, QVector< QChar > &taken, bool use_fallbacks)
2536 {
2537     QMap< ElementImpl *, QChar > fallbacks;
2538     if (use_fallbacks) {
2539         fallbacks = buildFallbackAccessKeys();
2540     }
2541     for (NodeImpl *n = m_part->xmlDocImpl(); n != nullptr; n = n->traverseNextNode()) {
2542         if (n->isElementNode()) {
2543             ElementImpl *en = static_cast< ElementImpl * >(n);
2544             DOMString s = en->getAttribute(ATTR_ACCESSKEY);
2545             QString accesskey;
2546             if (s.length() == 1) {
2547                 QChar a = s.string()[ 0 ].toUpper();
2548                 if (qFind(taken.begin(), taken.end(), a) == taken.end()) { // !contains
2549                     accesskey = a;
2550                 }
2551             }
2552             if (accesskey.isNull() && fallbacks.contains(en)) {
2553                 QChar a = fallbacks[ en ].toUpper();
2554                 if (qFind(taken.begin(), taken.end(), a) == taken.end()) { // !contains
2555                     accesskey = QString("<qt><i>") + a + "</i></qt>";
2556                 }
2557             }
2558             if (!accesskey.isNull()) {
2559                 QRect rec = en->getRect();
2560                 QLabel *lab = new QLabel(accesskey, widget());
2561                 lab->setAttribute(Qt::WA_DeleteOnClose);
2562                 lab->setObjectName("KHTMLAccessKey");
2563                 connect(origview, SIGNAL(hideAccessKeys()), lab, SLOT(close()));
2564                 connect(this, SIGNAL(repaintAccessKeys()), lab, SLOT(repaint()));
2565                 lab->setPalette(QToolTip::palette());
2566                 lab->setLineWidth(2);
2567                 lab->setFrameStyle(QFrame::Box | QFrame::Plain);
2568                 lab->setContentsMargins(3, 3, 3, 3);
2569                 lab->adjustSize();
2570                 lab->setParent(widget());
2571                 lab->setAutoFillBackground(true);
2572                 lab->move(
2573                     qMin(rec.left() + rec.width() / 2 - contentsX(), contentsWidth() - lab->width()),
2574                     qMin(rec.top() + rec.height() / 2 - contentsY(), contentsHeight() - lab->height()));
2575                 lab->show();
2576                 taken.append(accesskey[ 0 ]);
2577             }
2578         }
2579     }
2580     if (use_fallbacks) {
2581         return;
2582     }
2583 
2584     QList<KParts::ReadOnlyPart *> frames = m_part->frames();
2585     foreach (KParts::ReadOnlyPart *cur, frames) {
2586         if (!qobject_cast<KHTMLPart *>(cur)) {
2587             continue;
2588         }
2589         KHTMLPart *part = static_cast< KHTMLPart * >(cur);
2590         if (part->view() && part->view() != caller) {
2591             part->view()->displayAccessKeys(this, origview, taken, use_fallbacks);
2592         }
2593     }
2594 
2595     // pass up to the parent
2596     if (m_part->parentPart() && m_part->parentPart()->view()
2597             && m_part->parentPart()->view() != caller) {
2598         m_part->parentPart()->view()->displayAccessKeys(this, origview, taken, use_fallbacks);
2599     }
2600 }
2601 
isScrollingFromMouseWheel() const2602 bool KHTMLView::isScrollingFromMouseWheel() const
2603 {
2604     return d->scrollingFromWheel != QPoint(-1, -1);
2605 }
2606 
accessKeysTimeout()2607 void KHTMLView::accessKeysTimeout()
2608 {
2609     d->accessKeysActivated = false;
2610     d->accessKeysPreActivate = false;
2611     m_part->setStatusBarText(QString(), KHTMLPart::BarOverrideText);
2612     emit hideAccessKeys();
2613 }
2614 
2615 // Handling of the HTML accesskey attribute.
handleAccessKey(const QKeyEvent * ev)2616 bool KHTMLView::handleAccessKey(const QKeyEvent *ev)
2617 {
2618 // Qt interprets the keyevent also with the modifiers, and ev->text() matches that,
2619 // but this code must act as if the modifiers weren't pressed
2620     QChar c;
2621     if (ev->key() >= Qt::Key_A && ev->key() <= Qt::Key_Z) {
2622         c = 'A' + ev->key() - Qt::Key_A;
2623     } else if (ev->key() >= Qt::Key_0 && ev->key() <= Qt::Key_9) {
2624         c = '0' + ev->key() - Qt::Key_0;
2625     } else {
2626         // TODO fake XKeyEvent and XLookupString ?
2627         // This below seems to work e.g. for eacute though.
2628         if (ev->text().length() == 1) {
2629             c = ev->text()[ 0 ];
2630         }
2631     }
2632     if (c.isNull()) {
2633         return false;
2634     }
2635     return focusNodeWithAccessKey(c);
2636 }
2637 
focusNodeWithAccessKey(QChar c,KHTMLView * caller)2638 bool KHTMLView::focusNodeWithAccessKey(QChar c, KHTMLView *caller)
2639 {
2640     DocumentImpl *doc = m_part->xmlDocImpl();
2641     if (!doc) {
2642         return false;
2643     }
2644     ElementImpl *node = doc->findAccessKeyElement(c);
2645     if (!node) {
2646         QList<KParts::ReadOnlyPart *> frames = m_part->frames();
2647         foreach (KParts::ReadOnlyPart *cur, frames) {
2648             if (!qobject_cast<KHTMLPart *>(cur)) {
2649                 continue;
2650             }
2651             KHTMLPart *part = static_cast< KHTMLPart * >(cur);
2652             if (part->view() && part->view() != caller
2653                     && part->view()->focusNodeWithAccessKey(c, this)) {
2654                 return true;
2655             }
2656         }
2657         // pass up to the parent
2658         if (m_part->parentPart() && m_part->parentPart()->view()
2659                 && m_part->parentPart()->view() != caller
2660                 && m_part->parentPart()->view()->focusNodeWithAccessKey(c, this)) {
2661             return true;
2662         }
2663         if (caller == nullptr) {  // the active frame (where the accesskey was pressed)
2664             const QMap< ElementImpl *, QChar > fallbacks = buildFallbackAccessKeys();
2665             for (QMap< ElementImpl *, QChar >::ConstIterator it = fallbacks.begin();
2666                     it != fallbacks.end();
2667                     ++it)
2668                 if (*it == c) {
2669                     node = it.key();
2670                     break;
2671                 }
2672         }
2673         if (node == nullptr) {
2674             return false;
2675         }
2676     }
2677 
2678     // Scroll the view as necessary to ensure that the new focus node is visible
2679 
2680     QRect r = node->getRect();
2681     ensureVisible(r.right(), r.bottom());
2682     ensureVisible(r.left(), r.top());
2683 
2684     Node guard(node);
2685     if (node->isFocusable()) {
2686         if (node->id() == ID_LABEL) {
2687             // if Accesskey is a label, give focus to the label's referrer.
2688             node = static_cast<ElementImpl *>(static_cast< HTMLLabelElementImpl * >(node)->getFormElement());
2689             if (!node) {
2690                 return true;
2691             }
2692             guard = node;
2693         }
2694         // Set focus node on the document
2695         m_part->xmlDocImpl()->setFocusNode(node);
2696 
2697         if (node != nullptr && node->hasOneRef()) { // deleted, only held by guard
2698             return true;
2699         }
2700         emit m_part->nodeActivated(Node(node));
2701         if (node != nullptr && node->hasOneRef()) {
2702             return true;
2703         }
2704     }
2705 
2706     switch (node->id()) {
2707     case ID_A:
2708         static_cast< HTMLAnchorElementImpl * >(node)->click();
2709         break;
2710     case ID_INPUT:
2711         static_cast< HTMLInputElementImpl * >(node)->click();
2712         break;
2713     case ID_BUTTON:
2714         static_cast< HTMLButtonElementImpl * >(node)->click();
2715         break;
2716     case ID_AREA:
2717         static_cast< HTMLAreaElementImpl * >(node)->click();
2718         break;
2719     case ID_TEXTAREA:
2720         break; // just focusing it is enough
2721     case ID_LEGEND:
2722         // TODO
2723         break;
2724     }
2725     return true;
2726 }
2727 
getElementText(NodeImpl * start,bool after)2728 static QString getElementText(NodeImpl *start, bool after)
2729 {
2730     QString ret;             // nextSibling(), to go after e.g. </select>
2731     for (NodeImpl *n = after ? start->nextSibling() : start->traversePreviousNode();
2732             n != nullptr;
2733             n = after ? n->traverseNextNode() : n->traversePreviousNode()) {
2734         if (n->isTextNode()) {
2735             if (after) {
2736                 ret += static_cast< TextImpl * >(n)->toString().string();
2737             } else {
2738                 ret.prepend(static_cast< TextImpl * >(n)->toString().string());
2739             }
2740         } else {
2741             switch (n->id()) {
2742             case ID_A:
2743             case ID_FONT:
2744             case ID_TT:
2745             case ID_U:
2746             case ID_B:
2747             case ID_I:
2748             case ID_S:
2749             case ID_STRIKE:
2750             case ID_BIG:
2751             case ID_SMALL:
2752             case ID_EM:
2753             case ID_STRONG:
2754             case ID_DFN:
2755             case ID_CODE:
2756             case ID_SAMP:
2757             case ID_KBD:
2758             case ID_VAR:
2759             case ID_CITE:
2760             case ID_ABBR:
2761             case ID_ACRONYM:
2762             case ID_SUB:
2763             case ID_SUP:
2764             case ID_SPAN:
2765             case ID_NOBR:
2766             case ID_WBR:
2767                 break;
2768             case ID_TD:
2769                 if (ret.trimmed().isEmpty()) {
2770                     break;
2771                 }
2772             // fall through
2773             default:
2774                 return ret.simplified();
2775             }
2776         }
2777     }
2778     return ret.simplified();
2779 }
2780 
buildLabels(NodeImpl * start)2781 static QMap< NodeImpl *, QString > buildLabels(NodeImpl *start)
2782 {
2783     QMap< NodeImpl *, QString > ret;
2784     for (NodeImpl *n = start;
2785             n != nullptr;
2786             n = n->traverseNextNode()) {
2787         if (n->id() == ID_LABEL) {
2788             HTMLLabelElementImpl *label = static_cast< HTMLLabelElementImpl * >(n);
2789             NodeImpl *labelfor = label->getFormElement();
2790             if (labelfor) {
2791                 ret[ labelfor ] = label->innerText().string().simplified();
2792             }
2793         }
2794     }
2795     return ret;
2796 }
2797 
2798 namespace khtml
2799 {
2800 struct AccessKeyData {
2801     ElementImpl *element;
2802     QString text;
2803     QString url;
2804     int priority; // 10(highest) - 0(lowest)
2805 };
2806 }
2807 
buildFallbackAccessKeys() const2808 QMap< ElementImpl *, QChar > KHTMLView::buildFallbackAccessKeys() const
2809 {
2810     // build a list of all possible candidate elements that could use an accesskey
2811     QLinkedList< AccessKeyData > data; // Note: this has to be a list type that keep iterators valid
2812     // when other entries are removed
2813     QMap< NodeImpl *, QString > labels = buildLabels(m_part->xmlDocImpl());
2814     QMap< QString, QChar > hrefs;
2815 
2816     for (NodeImpl *n = m_part->xmlDocImpl();
2817             n != nullptr;
2818             n = n->traverseNextNode()) {
2819         if (n->isElementNode()) {
2820             ElementImpl *element = static_cast< ElementImpl * >(n);
2821             if (element->renderer() == nullptr) {
2822                 continue;    // not visible
2823             }
2824             QString text;
2825             QString url;
2826             int priority = 0;
2827             bool ignore = false;
2828             bool text_after = false;
2829             bool text_before = false;
2830             switch (element->id()) {
2831             case ID_A:
2832                 url = element->getAttribute(ATTR_HREF).trimSpaces().string();
2833                 if (url.isEmpty()) { // doesn't have href, it's only an anchor
2834                     continue;
2835                 }
2836                 text = static_cast< HTMLElementImpl * >(element)->innerText().string().simplified();
2837                 priority = 2;
2838                 break;
2839             case ID_INPUT: {
2840                 HTMLInputElementImpl *in = static_cast< HTMLInputElementImpl * >(element);
2841                 switch (in->inputType()) {
2842                 case HTMLInputElementImpl::SUBMIT:
2843                     text = in->value().string();
2844                     if (text.isEmpty()) {
2845                         text = i18n("Submit");
2846                     }
2847                     priority = 7;
2848                     break;
2849                 case HTMLInputElementImpl::IMAGE:
2850                     text = in->altText().string();
2851                     priority = 7;
2852                     break;
2853                 case HTMLInputElementImpl::BUTTON:
2854                     text = in->value().string();
2855                     priority = 5;
2856                     break;
2857                 case HTMLInputElementImpl::RESET:
2858                     text = in->value().string();
2859                     if (text.isEmpty()) {
2860                         text = i18n("Reset");
2861                     }
2862                     priority = 5;
2863                     break;
2864                 case HTMLInputElementImpl::HIDDEN:
2865                     ignore = true;
2866                     break;
2867                 case HTMLInputElementImpl::CHECKBOX:
2868                 case HTMLInputElementImpl::RADIO:
2869                     text_after = true;
2870                     priority = 5;
2871                     break;
2872                 case HTMLInputElementImpl::TEXT:
2873                 case HTMLInputElementImpl::PASSWORD:
2874                 case HTMLInputElementImpl::FILE:
2875                     text_before = true;
2876                     priority = 5;
2877                     break;
2878                 default:
2879                     priority = 5;
2880                     break;
2881                 }
2882                 break;
2883             }
2884             case ID_BUTTON:
2885                 text = static_cast< HTMLElementImpl * >(element)->innerText().string().simplified();
2886                 switch (static_cast< HTMLButtonElementImpl * >(element)->buttonType()) {
2887                 case HTMLButtonElementImpl::SUBMIT:
2888                     if (text.isEmpty()) {
2889                         text = i18n("Submit");
2890                     }
2891                     priority = 7;
2892                     break;
2893                 case HTMLButtonElementImpl::RESET:
2894                     if (text.isEmpty()) {
2895                         text = i18n("Reset");
2896                     }
2897                     priority = 5;
2898                     break;
2899                 default:
2900                     priority = 5;
2901                     break;
2902                 }
2903                 break;
2904             case ID_SELECT: // these don't have accesskey attribute, but quick access may be handy
2905                 text_before = true;
2906                 text_after = true;
2907                 priority = 5;
2908                 break;
2909             case ID_FRAME:
2910                 ignore = true;
2911                 break;
2912             default:
2913                 ignore = !element->isFocusable();
2914                 priority = 2;
2915                 break;
2916             }
2917             if (ignore) {
2918                 continue;
2919             }
2920 
2921             // build map of manually assigned accesskeys and their targets
2922             DOMString akey = element->getAttribute(ATTR_ACCESSKEY);
2923             if (akey.length() == 1) {
2924                 hrefs[url] = akey.string()[ 0 ].toUpper();
2925                 continue; // has accesskey set, ignore
2926             }
2927             if (text.isNull() && labels.contains(element)) {
2928                 text = labels[ element ];
2929             }
2930             if (text.isNull() && text_before) {
2931                 text = getElementText(element, false);
2932             }
2933             if (text.isNull() && text_after) {
2934                 text = getElementText(element, true);
2935             }
2936             text = text.trimmed();
2937             // increase priority of items which have explicitly specified accesskeys in the config
2938             const QList< QPair< QString, QChar > > priorities
2939                 = m_part->settings()->fallbackAccessKeysAssignments();
2940             for (QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin();
2941                     it != priorities.end();
2942                     ++it) {
2943                 if (text == (*it).first) {
2944                     priority = 10;
2945                 }
2946             }
2947             AccessKeyData tmp = { element, text, url, priority };
2948             data.append(tmp);
2949         }
2950     }
2951 
2952     QList< QChar > keys;
2953     for (char c = 'A'; c <= 'Z'; ++c) {
2954         keys << c;
2955     }
2956     for (char c = '0'; c <= '9'; ++c) {
2957         keys << c;
2958     }
2959     for (NodeImpl *n = m_part->xmlDocImpl();
2960             n != nullptr;
2961             n = n->traverseNextNode()) {
2962         if (n->isElementNode()) {
2963             ElementImpl *en = static_cast< ElementImpl * >(n);
2964             DOMString s = en->getAttribute(ATTR_ACCESSKEY);
2965             if (s.length() == 1) {
2966                 QChar c = s.string()[ 0 ].toUpper();
2967                 keys.removeAll(c);   // remove manually assigned accesskeys
2968             }
2969         }
2970     }
2971 
2972     QMap< ElementImpl *, QChar > ret;
2973     for (int priority = 10; priority >= 0; --priority) {
2974         for (QLinkedList< AccessKeyData >::Iterator it = data.begin();
2975                 it != data.end();
2976             ) {
2977             if ((*it).priority != priority) {
2978                 ++it;
2979                 continue;
2980             }
2981             if (keys.isEmpty()) {
2982                 break;
2983             }
2984             QString text = (*it).text;
2985             QChar key;
2986             const QString url = (*it).url;
2987             // an identical link already has an accesskey assigned
2988             if (hrefs.contains(url)) {
2989                 it = data.erase(it);
2990                 continue;
2991             }
2992             if (!text.isEmpty()) {
2993                 const QList< QPair< QString, QChar > > priorities
2994                     = m_part->settings()->fallbackAccessKeysAssignments();
2995                 for (QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin();
2996                         it != priorities.end();
2997                         ++it)
2998                     if (text == (*it).first && keys.contains((*it).second)) {
2999                         key = (*it).second;
3000                         break;
3001                     }
3002             }
3003             // try first to select the first character as the accesskey,
3004             // then first character of the following words,
3005             // and then simply the first free character
3006             if (key.isNull() && !text.isEmpty()) {
3007                 const QStringList words = text.split(' ');
3008                 for (QStringList::ConstIterator it = words.begin();
3009                         it != words.end();
3010                         ++it) {
3011                     if (keys.contains((*it)[ 0 ].toUpper())) {
3012                         key = (*it)[ 0 ].toUpper();
3013                         break;
3014                     }
3015                 }
3016             }
3017             if (key.isNull() && !text.isEmpty()) {
3018                 for (int i = 0; i < text.length(); ++i) {
3019                     if (keys.contains(text[ i ].toUpper())) {
3020                         key = text[ i ].toUpper();
3021                         break;
3022                     }
3023                 }
3024             }
3025             if (key.isNull()) {
3026                 key = keys.front();
3027             }
3028             ret[(*it).element ] = key;
3029             keys.removeAll(key);
3030             it = data.erase(it);
3031             // assign the same accesskey also to other elements pointing to the same url
3032             if (!url.isEmpty() && !url.startsWith("javascript:", Qt::CaseInsensitive)) {
3033                 for (QLinkedList< AccessKeyData >::Iterator it2 = data.begin();
3034                         it2 != data.end();
3035                     ) {
3036                     if ((*it2).url == url) {
3037                         ret[(*it2).element ] = key;
3038                         if (it == it2) {
3039                             ++it;
3040                         }
3041                         it2 = data.erase(it2);
3042                     } else {
3043                         ++it2;
3044                     }
3045                 }
3046             }
3047         }
3048     }
3049     return ret;
3050 }
3051 
setMediaType(const QString & medium)3052 void KHTMLView::setMediaType(const QString &medium)
3053 {
3054     m_medium = medium;
3055 }
3056 
mediaType() const3057 QString KHTMLView::mediaType() const
3058 {
3059     return m_medium;
3060 }
3061 
pagedMode() const3062 bool KHTMLView::pagedMode() const
3063 {
3064     return d->paged;
3065 }
3066 
setWidgetVisible(RenderWidget * w,bool vis)3067 void KHTMLView::setWidgetVisible(RenderWidget *w, bool vis)
3068 {
3069     if (vis) {
3070         d->visibleWidgets.insert(w, w->widget());
3071     } else {
3072         d->visibleWidgets.remove(w);
3073     }
3074 }
3075 
needsFullRepaint() const3076 bool KHTMLView::needsFullRepaint() const
3077 {
3078     return d->needsFullRepaint;
3079 }
3080 
3081 namespace
3082 {
3083 class QPointerDeleter
3084 {
3085 public:
QPointerDeleter(QObject * o)3086     explicit QPointerDeleter(QObject *o) : obj(o) {}
~QPointerDeleter()3087     ~QPointerDeleter()
3088     {
3089         delete obj;
3090     }
3091 private:
3092     const QPointer<QObject> obj;
3093 };
3094 }
3095 
print(bool quick)3096 void KHTMLView::print(bool quick)
3097 {
3098     QPrinter printer;
3099     print(&printer, quick);
3100 }
3101 
print(QPrinter * _printer,bool quick)3102 void KHTMLView::print(QPrinter *_printer, bool quick)
3103 {
3104     if (!m_part->xmlDocImpl()) {
3105         return;
3106     }
3107     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
3108     if (!root) {
3109         return;
3110     }
3111 
3112     QPrinter &printer = *_printer;
3113     QPointer<QPrintDialog> dialog(new QPrintDialog(&printer, this));
3114     QPointer<KHTMLPrintSettings> printSettings(new KHTMLPrintSettings(dialog)); //XXX: doesn't save settings between prints like this
3115     dialog->setOptionTabs(QList<QWidget *>() << printSettings.data());
3116 
3117     const QPointerDeleter dialogDeleter(dialog);
3118 
3119     QString docname = m_part->xmlDocImpl()->URL().toDisplayString();
3120     if (!docname.isEmpty()) {
3121         docname = KStringHandler::csqueeze(docname, 80);
3122     }
3123 
3124     if (quick || (dialog->exec() && dialog)) { /*'this' and thus dialog might have been deleted while exec()!*/
3125         viewport()->setCursor(Qt::WaitCursor);   // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs
3126         // set up KPrinter
3127         printer.setFullPage(false);
3128         printer.setCreator(QString("KDE %1.%2.%3 HTML Library").arg(KHTML_VERSION_MAJOR).arg(KHTML_VERSION_MINOR).arg(KHTML_VERSION_PATCH));
3129         printer.setDocName(docname);
3130 
3131         QPainter *p = new QPainter;
3132         p->begin(&printer);
3133         khtml::setPrintPainter(p);
3134 
3135         m_part->xmlDocImpl()->setPaintDevice(&printer);
3136         QString oldMediaType = mediaType();
3137         setMediaType("print");
3138         // We ignore margin settings for html and body when printing
3139         // and use the default margins from the print-system
3140         // (In Qt 3.0.x the default margins are hardcoded in Qt)
3141         m_part->xmlDocImpl()->setPrintStyleSheet(printSettings->printFriendly() ?
3142                 "* { background-image: none !important;"
3143                 "    background-color: white !important;"
3144                 "    color: black !important; }"
3145                 "body { margin: 0px !important; }"
3146                 "html { margin: 0px !important; }" :
3147                 "body { margin: 0px !important; }"
3148                 "html { margin: 0px !important; }"
3149                                                 );
3150 
3151         // qCDebug(KHTML_LOG) << "printing: physical page width = " << printer.width()
3152         //              << " height = " << printer.height();
3153         root->setStaticMode(true);
3154         root->setPagedMode(true);
3155         root->setWidth(printer.width());
3156 //         root->setHeight(printer.height());
3157         root->setPageTop(0);
3158         root->setPageBottom(0);
3159         d->paged = true;
3160 
3161         m_part->xmlDocImpl()->styleSelector()->computeFontSizes(printer.logicalDpiY(), 100);
3162         m_part->xmlDocImpl()->updateStyleSelector();
3163         root->setPrintImages(printSettings->printImages());
3164         root->makePageBreakAvoidBlocks();
3165 
3166         root->setNeedsLayoutAndMinMaxRecalc();
3167         root->layout();
3168 
3169         // check sizes ask for action.. (scale or clip)
3170 
3171         bool printHeader = printSettings->printHeader();
3172 
3173         int headerHeight = 0;
3174         QFont headerFont("Sans Serif", 8);
3175 
3176         QString headerLeft = QDate::currentDate().toString(Qt::DefaultLocaleShortDate);
3177         QString headerMid = docname;
3178         QString headerRight;
3179 
3180         if (printHeader) {
3181             p->setFont(headerFont);
3182             headerHeight = (p->fontMetrics().lineSpacing() * 3) / 2;
3183         }
3184 
3185         // ok. now print the pages.
3186         // qCDebug(KHTML_LOG) << "printing: html page width = " << root->docWidth()
3187         //              << " height = " << root->docHeight();
3188         // qCDebug(KHTML_LOG) << "printing: margins left = " << printer.pageRect().left() - printer.paperRect().left()
3189         //              << " top = " << printer.pageRect().top() - printer.paperRect().top();
3190         // qCDebug(KHTML_LOG) << "printing: paper width = " << printer.width()
3191         //              << " height = " << printer.height();
3192         // if the width is too large to fit on the paper we just scale
3193         // the whole thing.
3194         int pageWidth = printer.width();
3195         int pageHeight = printer.height();
3196         p->setClipRect(0, 0, pageWidth, pageHeight);
3197 
3198         pageHeight -= headerHeight;
3199 
3200 #ifndef QT_NO_TRANSFORMATIONS
3201         bool scalePage = false;
3202         double scale = 0.0;
3203         if (root->docWidth() > printer.width()) {
3204             scalePage = true;
3205             scale = ((double) printer.width()) / ((double) root->docWidth());
3206             pageHeight = (int)(pageHeight / scale);
3207             pageWidth = (int)(pageWidth / scale);
3208             headerHeight = (int)(headerHeight / scale);
3209         }
3210 #endif
3211         // qCDebug(KHTML_LOG) << "printing: scaled html width = " << pageWidth
3212         //              << " height = " << pageHeight;
3213 
3214         root->setHeight(pageHeight);
3215         root->setPageBottom(pageHeight);
3216         root->setNeedsLayout(true);
3217         root->layoutIfNeeded();
3218 //         m_part->slotDebugRenderTree();
3219 
3220         // Squeeze header to make it it on the page.
3221         if (printHeader) {
3222             int available_width = printer.width() - 10 -
3223                                   2 * qMax(p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerLeft).width(),
3224                                            p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerRight).width());
3225             if (available_width < 150) {
3226                 available_width = 150;
3227             }
3228             int mid_width;
3229             int squeeze = 120;
3230             do {
3231                 headerMid = KStringHandler::csqueeze(docname, squeeze);
3232                 mid_width = p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerMid).width();
3233                 squeeze -= 10;
3234             } while (mid_width > available_width);
3235         }
3236 
3237         int top = 0;
3238         int bottom = 0;
3239         int page = 1;
3240         while (top < root->docHeight()) {
3241             if (top > 0) {
3242                 printer.newPage();
3243             }
3244 #ifndef QT_NO_TRANSFORMATIONS
3245             if (scalePage) {
3246                 p->scale(scale, scale);
3247             }
3248 #endif
3249             p->save();
3250             p->setClipRect(0, 0, pageWidth, headerHeight);
3251             if (printHeader) {
3252                 int dy = p->fontMetrics().lineSpacing();
3253                 p->setPen(Qt::black);
3254                 p->setFont(headerFont);
3255 
3256                 headerRight = QString("#%1").arg(page);
3257 
3258                 p->drawText(0, 0, printer.width(), dy, Qt::AlignLeft, headerLeft);
3259                 p->drawText(0, 0, printer.width(), dy, Qt::AlignHCenter, headerMid);
3260                 p->drawText(0, 0, printer.width(), dy, Qt::AlignRight, headerRight);
3261             }
3262 
3263             p->restore();
3264             p->translate(0, headerHeight - top);
3265 
3266             bottom = top + pageHeight;
3267 
3268             root->setPageTop(top);
3269             root->setPageBottom(bottom);
3270             root->setPageNumber(page);
3271 
3272             root->layer()->paint(p, QRect(0, top, pageWidth, pageHeight));
3273             // qCDebug(KHTML_LOG) << "printed: page " << page <<" bottom At = " << bottom;
3274 
3275             top = bottom;
3276             p->resetTransform();
3277             page++;
3278         }
3279 
3280         p->end();
3281         delete p;
3282 
3283         // and now reset the layout to the usual one...
3284         root->setPagedMode(false);
3285         root->setStaticMode(false);
3286         d->paged = false;
3287         khtml::setPrintPainter(nullptr);
3288         setMediaType(oldMediaType);
3289         m_part->xmlDocImpl()->setPaintDevice(this);
3290         m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->logicalDpiY(), m_part->fontScaleFactor());
3291         m_part->xmlDocImpl()->updateStyleSelector();
3292         viewport()->unsetCursor();
3293     }
3294 }
3295 
slotPaletteChanged()3296 void KHTMLView::slotPaletteChanged()
3297 {
3298     if (!m_part->xmlDocImpl()) {
3299         return;
3300     }
3301     DOM::DocumentImpl *document = m_part->xmlDocImpl();
3302     if (!document->isHTMLDocument()) {
3303         return;
3304     }
3305     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer());
3306     if (!root) {
3307         return;
3308     }
3309     root->style()->resetPalette();
3310     NodeImpl *body = static_cast<HTMLDocumentImpl *>(document)->body();
3311     if (!body) {
3312         return;
3313     }
3314     body->setChanged(true);
3315     body->recalcStyle(NodeImpl::Force);
3316 }
3317 
paint(QPainter * p,const QRect & rc,int yOff,bool * more)3318 void KHTMLView::paint(QPainter *p, const QRect &rc, int yOff, bool *more)
3319 {
3320     if (!m_part->xmlDocImpl()) {
3321         return;
3322     }
3323     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
3324     if (!root) {
3325         return;
3326     }
3327 #ifdef SPEED_DEBUG
3328     d->firstRepaintPending = false;
3329 #endif
3330 
3331     QPaintDevice *opd = m_part->xmlDocImpl()->paintDevice();
3332     m_part->xmlDocImpl()->setPaintDevice(p->device());
3333     root->setPagedMode(true);
3334     root->setStaticMode(true);
3335     root->setWidth(rc.width());
3336 
3337     // save()
3338     QRegion creg = p->clipRegion();
3339     QTransform t = p->worldTransform();
3340     QRect w = p->window();
3341     QRect v = p->viewport();
3342     bool vte = p->viewTransformEnabled();
3343     bool wme = p->worldMatrixEnabled();
3344 
3345     p->setClipRect(rc);
3346     p->translate(rc.left(), rc.top());
3347     double scale = ((double) rc.width() / (double) root->docWidth());
3348     int height = (int)((double) rc.height() / scale);
3349 #ifndef QT_NO_TRANSFORMATIONS
3350     p->scale(scale, scale);
3351 #endif
3352     root->setPageTop(yOff);
3353     root->setPageBottom(yOff + height);
3354 
3355     root->layer()->paint(p, QRect(0, yOff, root->docWidth(), height));
3356     if (more) {
3357         *more = yOff + height < root->docHeight();
3358     }
3359 
3360     // restore()
3361     p->setWorldTransform(t);
3362     p->setWindow(w);
3363     p->setViewport(v);
3364     p->setViewTransformEnabled(vte);
3365     p->setWorldMatrixEnabled(wme);
3366     if (!creg.isEmpty()) {
3367         p->setClipRegion(creg);
3368     } else {
3369         p->setClipRegion(QRegion(), Qt::NoClip);
3370     }
3371 
3372     root->setPagedMode(false);
3373     root->setStaticMode(false);
3374     m_part->xmlDocImpl()->setPaintDevice(opd);
3375 }
3376 
render(QPainter * p,const QRect & r,const QPoint & off)3377 void KHTMLView::render(QPainter *p, const QRect &r, const QPoint &off)
3378 {
3379 #ifdef SPEED_DEBUG
3380     d->firstRepaintPending = false;
3381 #endif
3382     QRect clip(off.x() + r.x(), off.y() + r.y(), r.width(), r.height());
3383     if (!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
3384         p->fillRect(clip, palette().brush(QPalette::Active, QPalette::Base));
3385         return;
3386     }
3387     QPaintDevice *opd = m_part->xmlDocImpl()->paintDevice();
3388     m_part->xmlDocImpl()->setPaintDevice(p->device());
3389 
3390     // save()
3391     QRegion creg = p->clipRegion();
3392     QTransform t = p->worldTransform();
3393     QRect w = p->window();
3394     QRect v = p->viewport();
3395     bool vte = p->viewTransformEnabled();
3396     bool wme = p->worldMatrixEnabled();
3397 
3398     p->setClipRect(clip);
3399     QRect rect = r.translated(contentsX(), contentsY());
3400     p->translate(off.x() - contentsX(), off.y() - contentsY());
3401 
3402     m_part->xmlDocImpl()->renderer()->layer()->paint(p, rect);
3403 
3404     // restore()
3405     p->setWorldTransform(t);
3406     p->setWindow(w);
3407     p->setViewport(v);
3408     p->setViewTransformEnabled(vte);
3409     p->setWorldMatrixEnabled(wme);
3410     if (!creg.isEmpty()) {
3411         p->setClipRegion(creg);
3412     } else {
3413         p->setClipRegion(QRegion(), Qt::NoClip);
3414     }
3415 
3416     m_part->xmlDocImpl()->setPaintDevice(opd);
3417 }
3418 
setHasStaticBackground(bool partial)3419 void KHTMLView::setHasStaticBackground(bool partial)
3420 {
3421     // full static iframe is irreversible for now
3422     if (d->staticWidget == KHTMLViewPrivate::SBFull && m_kwp->isRedirected()) {
3423         return;
3424     }
3425 
3426     d->staticWidget = partial ?
3427                       KHTMLViewPrivate::SBPartial : KHTMLViewPrivate::SBFull;
3428 }
3429 
setHasNormalBackground()3430 void KHTMLView::setHasNormalBackground()
3431 {
3432     // full static iframe is irreversible for now
3433     if (d->staticWidget == KHTMLViewPrivate::SBFull && m_kwp->isRedirected()) {
3434         return;
3435     }
3436 
3437     d->staticWidget = KHTMLViewPrivate::SBNone;
3438 }
3439 
addStaticObject(bool fixed)3440 void KHTMLView::addStaticObject(bool fixed)
3441 {
3442     if (fixed) {
3443         d->fixedObjectsCount++;
3444     } else {
3445         d->staticObjectsCount++;
3446     }
3447 
3448     setHasStaticBackground(true /*partial*/);
3449 }
3450 
removeStaticObject(bool fixed)3451 void KHTMLView::removeStaticObject(bool fixed)
3452 {
3453     if (fixed) {
3454         d->fixedObjectsCount--;
3455     } else {
3456         d->staticObjectsCount--;
3457     }
3458 
3459     assert(d->fixedObjectsCount >= 0 && d->staticObjectsCount >= 0);
3460 
3461     if (!d->staticObjectsCount && !d->fixedObjectsCount) {
3462         setHasNormalBackground();
3463     } else {
3464         setHasStaticBackground(true /*partial*/);
3465     }
3466 }
3467 
setVerticalScrollBarPolicy(Qt::ScrollBarPolicy policy)3468 void KHTMLView::setVerticalScrollBarPolicy(Qt::ScrollBarPolicy policy)
3469 {
3470 #ifndef KHTML_NO_SCROLLBARS
3471     d->vpolicy = policy;
3472     QScrollArea::setVerticalScrollBarPolicy(policy);
3473 #else
3474     Q_UNUSED(policy);
3475 #endif
3476 }
3477 
setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy policy)3478 void KHTMLView::setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy policy)
3479 {
3480 #ifndef KHTML_NO_SCROLLBARS
3481     d->hpolicy = policy;
3482     QScrollArea::setHorizontalScrollBarPolicy(policy);
3483 #else
3484     Q_UNUSED(policy);
3485 #endif
3486 }
3487 
restoreScrollBar()3488 void KHTMLView::restoreScrollBar()
3489 {
3490     int ow = visibleWidth();
3491     QScrollArea::setVerticalScrollBarPolicy(d->vpolicy);
3492     if (visibleWidth() != ow) {
3493         layout();
3494     }
3495     d->prevScrollbarVisible = verticalScrollBar()->isVisible();
3496 }
3497 
formCompletionItems(const QString & name) const3498 QStringList KHTMLView::formCompletionItems(const QString &name) const
3499 {
3500     if (!m_part->settings()->isFormCompletionEnabled()) {
3501         return QStringList();
3502     }
3503     if (!d->formCompletions) {
3504         d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions");
3505     }
3506     return d->formCompletions->group("").readEntry(name, QStringList());
3507 }
3508 
clearCompletionHistory(const QString & name)3509 void KHTMLView::clearCompletionHistory(const QString &name)
3510 {
3511     if (!d->formCompletions) {
3512         d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions");
3513     }
3514     d->formCompletions->group("").writeEntry(name, "");
3515     d->formCompletions->sync();
3516 }
3517 
addFormCompletionItem(const QString & name,const QString & value)3518 void KHTMLView::addFormCompletionItem(const QString &name, const QString &value)
3519 {
3520     if (!m_part->settings()->isFormCompletionEnabled()) {
3521         return;
3522     }
3523     // don't store values that are all numbers or just numbers with
3524     // dashes or spaces as those are likely credit card numbers or
3525     // something similar
3526     bool cc_number(true);
3527     for (int i = 0; i < value.length(); ++i) {
3528         QChar c(value[i]);
3529         if (!c.isNumber() && c != '-' && !c.isSpace()) {
3530             cc_number = false;
3531             break;
3532         }
3533     }
3534     if (cc_number) {
3535         return;
3536     }
3537     QStringList items = formCompletionItems(name);
3538     if (!items.contains(value)) {
3539         items.prepend(value);
3540     }
3541     while ((int)items.count() > m_part->settings()->maxFormCompletionItems()) {
3542         items.erase(items.isEmpty() ? items.end() : --items.end());
3543     }
3544     d->formCompletions->group("").writeEntry(name, items);
3545 }
3546 
addNonPasswordStorableSite(const QString & host)3547 void KHTMLView::addNonPasswordStorableSite(const QString &host)
3548 {
3549     if (!d->formCompletions) {
3550         d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions");
3551     }
3552 
3553     KConfigGroup cg(d->formCompletions, "NonPasswordStorableSites");
3554     QStringList sites = cg.readEntry("Sites", QStringList());
3555     sites.append(host);
3556     cg.writeEntry("Sites", sites);
3557     cg.sync();
3558 }
3559 
delNonPasswordStorableSite(const QString & host)3560 void KHTMLView::delNonPasswordStorableSite(const QString &host)
3561 {
3562     if (!d->formCompletions) {
3563         d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions");
3564     }
3565 
3566     KConfigGroup cg(d->formCompletions, "NonPasswordStorableSites");
3567     QStringList sites = cg.readEntry("Sites", QStringList());
3568     sites.removeOne(host);
3569     cg.writeEntry("Sites", sites);
3570     cg.sync();
3571 }
3572 
nonPasswordStorableSite(const QString & host) const3573 bool KHTMLView::nonPasswordStorableSite(const QString &host) const
3574 {
3575     if (!d->formCompletions) {
3576         d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions");
3577     }
3578     QStringList sites =  d->formCompletions->group("NonPasswordStorableSites").readEntry("Sites", QStringList());
3579     return (sites.indexOf(host) != -1);
3580 }
3581 
3582 // returns true if event should be swallowed
dispatchMouseEvent(int eventId,DOM::NodeImpl * targetNode,DOM::NodeImpl * targetNodeNonShared,bool cancelable,int detail,QMouseEvent * _mouse,bool setUnder,int mouseEventType,int orient)3583 bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode,
3584                                    DOM::NodeImpl *targetNodeNonShared, bool cancelable,
3585                                    int detail, QMouseEvent *_mouse, bool setUnder,
3586                                    int mouseEventType, int orient)
3587 {
3588     // if the target node is a text node, dispatch on the parent node - rdar://4196646 (and #76948)
3589     if (targetNode && targetNode->isTextNode()) {
3590         targetNode = targetNode->parentNode();
3591     }
3592 
3593     if (d->underMouse) {
3594         d->underMouse->deref();
3595     }
3596     d->underMouse = targetNode;
3597     if (d->underMouse) {
3598         d->underMouse->ref();
3599     }
3600 
3601     if (d->underMouseNonShared) {
3602         d->underMouseNonShared->deref();
3603     }
3604     d->underMouseNonShared = targetNodeNonShared;
3605     if (d->underMouseNonShared) {
3606         d->underMouseNonShared->ref();
3607     }
3608 
3609     bool isWheelEvent = (mouseEventType == DOM::NodeImpl::MouseWheel);
3610 
3611     int exceptioncode = 0;
3612     int pageX = _mouse->x();
3613     int pageY = _mouse->y();
3614     revertTransforms(pageX, pageY);
3615     int clientX = pageX - contentsX();
3616     int clientY = pageY - contentsY();
3617     int screenX = _mouse->globalX();
3618     int screenY = _mouse->globalY();
3619     int button = -1;
3620     switch (_mouse->button()) {
3621     case Qt::LeftButton:
3622         button = 0;
3623         break;
3624     case Qt::MidButton:
3625         button = 1;
3626         break;
3627     case Qt::RightButton:
3628         button = 2;
3629         break;
3630     default:
3631         break;
3632     }
3633     if (d->accessKeysEnabled && d->accessKeysPreActivate && button != -1) {
3634         d->accessKeysPreActivate = false;
3635     }
3636 
3637     bool ctrlKey = (_mouse->modifiers() & Qt::ControlModifier);
3638     bool altKey = (_mouse->modifiers() & Qt::AltModifier);
3639     bool shiftKey = (_mouse->modifiers() & Qt::ShiftModifier);
3640     bool metaKey = (_mouse->modifiers() & Qt::MetaModifier);
3641 
3642     // mouseout/mouseover
3643     if (setUnder && d->oldUnderMouse != targetNode) {
3644         if (d->oldUnderMouse && d->oldUnderMouse->document() != m_part->xmlDocImpl()) {
3645             d->oldUnderMouse->deref();
3646             d->oldUnderMouse = nullptr;
3647         }
3648         // send mouseout event to the old node
3649         if (d->oldUnderMouse) {
3650             // send mouseout event to the old node
3651             MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOUT_EVENT,
3652                                                     true, true, m_part->xmlDocImpl()->defaultView(),
3653                                                     0, screenX, screenY, clientX, clientY, pageX, pageY,
3654                                                     ctrlKey, altKey, shiftKey, metaKey,
3655                                                     button, targetNode);
3656             me->ref();
3657             d->oldUnderMouse->dispatchEvent(me, exceptioncode, true);
3658             me->deref();
3659         }
3660         // send mouseover event to the new node
3661         if (targetNode) {
3662             MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOVER_EVENT,
3663                                                     true, true, m_part->xmlDocImpl()->defaultView(),
3664                                                     0, screenX, screenY, clientX, clientY, pageX, pageY,
3665                                                     ctrlKey, altKey, shiftKey, metaKey,
3666                                                     button, d->oldUnderMouse);
3667 
3668             me->ref();
3669             targetNode->dispatchEvent(me, exceptioncode, true);
3670             me->deref();
3671         }
3672         if (d->oldUnderMouse) {
3673             d->oldUnderMouse->deref();
3674         }
3675         d->oldUnderMouse = targetNode;
3676         if (d->oldUnderMouse) {
3677             d->oldUnderMouse->ref();
3678         }
3679     }
3680 
3681     bool swallowEvent = false;
3682 
3683     if (targetNode) {
3684         // if the target node is a disabled widget, we don't want any full-blown mouse events
3685         if (targetNode->isGenericFormElement()
3686                 && static_cast<HTMLGenericFormElementImpl *>(targetNode)->disabled()) {
3687             return true;
3688         }
3689 
3690         // send the actual event
3691         bool dblclick = (eventId == EventImpl::CLICK_EVENT &&
3692                          _mouse->type() == QEvent::MouseButtonDblClick);
3693         MouseEventImpl *me = new MouseEventImpl(static_cast<EventImpl::EventId>(eventId),
3694                                                 true, cancelable, m_part->xmlDocImpl()->defaultView(),
3695                                                 detail, screenX, screenY, clientX, clientY, pageX, pageY,
3696                                                 ctrlKey, altKey, shiftKey, metaKey,
3697                                                 button, nullptr, isWheelEvent ? nullptr : _mouse, dblclick,
3698                                                 isWheelEvent ? static_cast<MouseEventImpl::Orientation>(orient) : MouseEventImpl::ONone);
3699         me->ref();
3700         if (!d->m_mouseEventsTarget && RenderLayer::gScrollBar && eventId == EventImpl::MOUSEDOWN_EVENT)
3701             // button is pressed inside a layer scrollbar, so make it the target for future mousemove events until released
3702         {
3703             d->m_mouseEventsTarget = RenderLayer::gScrollBar;
3704         }
3705         if (d->m_mouseEventsTarget && qobject_cast<QScrollBar *>(d->m_mouseEventsTarget) &&
3706                 dynamic_cast<KHTMLWidget *>(static_cast<QWidget *>(d->m_mouseEventsTarget))) {
3707             // we have a sticky mouse event target and it is a layer's scrollbar. Forward events manually.
3708             // ### should use the dom
3709             KHTMLWidget *w = dynamic_cast<KHTMLWidget *>(static_cast<QWidget *>(d->m_mouseEventsTarget));
3710             QPoint p = w->m_kwp->absolutePos();
3711             QMouseEvent fw(_mouse->type(), QPoint(pageX, pageY) - p, _mouse->button(), _mouse->buttons(), _mouse->modifiers());
3712             static_cast<RenderWidget::EventPropagator *>(static_cast<QWidget *>(d->m_mouseEventsTarget))->sendEvent(&fw);
3713             if (_mouse->type() == QMouseEvent::MouseButtonPress && _mouse->button() == Qt::RightButton) {
3714                 QContextMenuEvent cme(QContextMenuEvent::Mouse, p);
3715                 static_cast<RenderWidget::EventPropagator *>(static_cast<QWidget *>(d->m_mouseEventsTarget))->sendEvent(&cme);
3716                 d->m_mouseEventsTarget = nullptr;
3717             }
3718             swallowEvent = true;
3719         } else {
3720             targetNode->dispatchEvent(me, exceptioncode, true);
3721             bool defaultHandled = me->defaultHandled();
3722             if (defaultHandled || me->defaultPrevented()) {
3723                 swallowEvent = true;
3724             }
3725         }
3726         if (eventId == EventImpl::MOUSEDOWN_EVENT && !me->defaultPrevented()) {
3727             // Focus should be shifted on mouse down, not on a click.  -dwh
3728             // Blur current focus node when a link/button is clicked; this
3729             // is expected by some sites that rely on onChange handlers running
3730             // from form fields before the button click is processed.
3731             DOM::NodeImpl *nodeImpl = targetNode;
3732             for (; nodeImpl && !nodeImpl->isFocusable(); nodeImpl = nodeImpl->parentNode()) {
3733             }
3734             if (nodeImpl && nodeImpl->isMouseFocusable()) {
3735                 m_part->xmlDocImpl()->setFocusNode(nodeImpl);
3736             } else if (!nodeImpl || !nodeImpl->focused()) {
3737                 m_part->xmlDocImpl()->setFocusNode(nullptr);
3738             }
3739         }
3740         me->deref();
3741     }
3742 
3743     return swallowEvent;
3744 }
3745 
setIgnoreWheelEvents(bool e)3746 void KHTMLView::setIgnoreWheelEvents(bool e)
3747 {
3748     d->ignoreWheelEvents = e;
3749 }
3750 
3751 #ifndef QT_NO_WHEELEVENT
3752 
wheelEvent(QWheelEvent * e)3753 void KHTMLView::wheelEvent(QWheelEvent *e)
3754 {
3755     // check if we should reset the state of the indicator describing if
3756     // we are currently scrolling the view as a result of wheel events
3757     if (d->scrollingFromWheel != QPoint(-1, -1) && d->scrollingFromWheel != QCursor::pos()) {
3758         d->scrollingFromWheel = d->scrollingFromWheelTimerId ? QCursor::pos() : QPoint(-1, -1);
3759     }
3760 
3761     if (d->accessKeysEnabled && d->accessKeysPreActivate) {
3762         d->accessKeysPreActivate = false;
3763     }
3764 
3765     if ((e->modifiers() & Qt::ControlModifier) == Qt::ControlModifier) {
3766         emit zoomView(- e->delta());
3767         e->accept();
3768     } else if (d->firstLayoutPending) {
3769         e->accept();
3770     } else if (!m_kwp->isRedirected() &&
3771                ((e->orientation() == Qt::Vertical &&
3772                  ((d->ignoreWheelEvents && !verticalScrollBar()->isVisible())
3773                   || (e->delta() > 0 && contentsY() <= 0)
3774                   || (e->delta() < 0 && contentsY() >= contentsHeight() - visibleHeight())))
3775                 ||
3776                 (e->orientation() == Qt::Horizontal &&
3777                  ((d->ignoreWheelEvents && !horizontalScrollBar()->isVisible())
3778                   || (e->delta() > 0 && contentsX() <= 0)
3779                   || (e->delta() < 0 && contentsX() >= contentsWidth() - visibleWidth()))))
3780                && m_part->parentPart()) {
3781         if (m_part->parentPart()->view()) {
3782             m_part->parentPart()->view()->wheelEvent(e);
3783         }
3784         e->ignore();
3785     } else {
3786         int xm = e->x();
3787         int ym = e->y();
3788         revertTransforms(xm, ym);
3789 
3790         DOM::NodeImpl::MouseEvent mev(e->buttons(), DOM::NodeImpl::MouseWheel);
3791         m_part->xmlDocImpl()->prepareMouseEvent(false, xm, ym, &mev);
3792 
3793         MouseEventImpl::Orientation o = MouseEventImpl::OVertical;
3794         if (e->orientation() == Qt::Horizontal) {
3795             o = MouseEventImpl::OHorizontal;
3796         }
3797 
3798         QMouseEvent _mouse(QEvent::MouseMove, e->pos(), Qt::NoButton, e->buttons(), e->modifiers());
3799         bool swallow = dispatchMouseEvent(EventImpl::KHTML_MOUSEWHEEL_EVENT, mev.innerNode.handle(), mev.innerNonSharedNode.handle(),
3800                                           true, -e->delta() / 40, &_mouse, true, DOM::NodeImpl::MouseWheel, o);
3801 
3802         if (swallow) {
3803             return;
3804         }
3805 
3806         d->scrollBarMoved = true;
3807         d->scrollingFromWheel = QCursor::pos();
3808         if (d->smoothScrollMode != SSMDisabled) {
3809             d->shouldSmoothScroll = true;
3810         }
3811         if (d->scrollingFromWheelTimerId) {
3812             killTimer(d->scrollingFromWheelTimerId);
3813         }
3814         d->scrollingFromWheelTimerId = startTimer(400);
3815 
3816         if (m_part->parentPart()) {
3817             // don't propagate if we are a sub-frame and our scrollbars are already at end of range
3818             bool h = (static_cast<QWheelEvent *>(e)->orientation() == Qt::Horizontal);
3819             bool d = (static_cast<QWheelEvent *>(e)->delta() < 0);
3820             QScrollBar *hsb = horizontalScrollBar();
3821             QScrollBar *vsb = verticalScrollBar();
3822             if ((h && ((d && hsb->value() == hsb->maximum()) || (!d && hsb->value() == hsb->minimum()))) ||
3823                     (!h && ((d && vsb->value() == vsb->maximum()) || (!d && vsb->value() == vsb->minimum())))) {
3824                 e->accept();
3825                 return;
3826             }
3827         }
3828         QScrollArea::wheelEvent(e);
3829     }
3830 
3831 }
3832 #endif
3833 
dragEnterEvent(QDragEnterEvent * ev)3834 void KHTMLView::dragEnterEvent(QDragEnterEvent *ev)
3835 {
3836     // Still overridden for BC reasons only...
3837     QScrollArea::dragEnterEvent(ev);
3838 }
3839 
dropEvent(QDropEvent * ev)3840 void KHTMLView::dropEvent(QDropEvent *ev)
3841 {
3842     // Still overridden for BC reasons only...
3843     QScrollArea::dropEvent(ev);
3844 }
3845 
focusInEvent(QFocusEvent * e)3846 void KHTMLView::focusInEvent(QFocusEvent *e)
3847 {
3848     DOM::NodeImpl *fn = m_part->xmlDocImpl() ? m_part->xmlDocImpl()->focusNode() : nullptr;
3849     if (fn && fn->renderer() && fn->renderer()->isWidget() &&
3850             (e->reason() != Qt::MouseFocusReason) &&
3851             static_cast<khtml::RenderWidget *>(fn->renderer())->widget()) {
3852         static_cast<khtml::RenderWidget *>(fn->renderer())->widget()->setFocus();
3853     }
3854     m_part->setSelectionVisible();
3855     QScrollArea::focusInEvent(e);
3856 }
3857 
focusOutEvent(QFocusEvent * e)3858 void KHTMLView::focusOutEvent(QFocusEvent *e)
3859 {
3860     if (m_part) {
3861         m_part->stopAutoScroll();
3862         m_part->setSelectionVisible(false);
3863     }
3864 
3865     if (d->cursorIconWidget) {
3866         d->cursorIconWidget->hide();
3867     }
3868 
3869     QScrollArea::focusOutEvent(e);
3870 }
3871 
scrollContentsBy(int dx,int dy)3872 void KHTMLView::scrollContentsBy(int dx, int dy)
3873 {
3874     if (!dx && !dy) {
3875         return;
3876     }
3877 
3878     if (!d->firstLayoutPending && !d->complete && m_part->xmlDocImpl() &&
3879             d->layoutSchedulingEnabled) {
3880         // contents scroll while we are not complete: we need to check our layout *now*
3881         khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
3882         if (root && root->needsLayout()) {
3883             unscheduleRelayout();
3884             layout();
3885         }
3886     }
3887 
3888     if (d->shouldSmoothScroll && d->smoothScrollMode != SSMDisabled && m_part->xmlDocImpl() &&
3889             m_part->xmlDocImpl()->renderer() && (d->smoothScrollMode != SSMWhenEfficient || d->smoothScrollMissedDeadlines != sWayTooMany)) {
3890 
3891         bool doSmoothScroll = (!d->staticWidget || d->smoothScrollMode == SSMEnabled);
3892 
3893         int numStaticPixels = 0;
3894         QRegion r = static_cast<RenderCanvas *>(m_part->xmlDocImpl()->renderer())->staticRegion();
3895 
3896         // only do smooth scrolling if static region is relatively small
3897         if (!doSmoothScroll && d->staticWidget == KHTMLViewPrivate::SBPartial && r.rects().size() <= 10) {
3898             foreach (const QRect &rr, r.rects()) {
3899                 numStaticPixels += rr.width() * rr.height();
3900             }
3901             if ((numStaticPixels < sSmoothScrollMinStaticPixels) || (numStaticPixels * 8 < visibleWidth()*visibleHeight())) {
3902                 doSmoothScroll = true;
3903             }
3904         }
3905         if (doSmoothScroll) {
3906             setupSmoothScrolling(dx, dy);
3907             return;
3908         }
3909     }
3910 
3911     if (underMouse() && QToolTip::isVisible()) {
3912         QToolTip::hideText();
3913     }
3914 
3915     if (!d->scrollingSelf) {
3916         d->scrollBarMoved = true;
3917         d->contentsMoving = true;
3918         // ensure quick reset of contentsMoving flag
3919         scheduleRepaint(0, 0, 0, 0);
3920     }
3921 
3922     if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->documentElement()) {
3923         m_part->xmlDocImpl()->documentElement()->dispatchHTMLEvent(EventImpl::SCROLL_EVENT, true, false);
3924     }
3925 
3926     if (QApplication::isRightToLeft()) {
3927         dx = -dx;
3928     }
3929 
3930     if (!d->smoothScrolling) {
3931         d->updateContentsXY();
3932     } else {
3933         d->contentsX -= dx;
3934         d->contentsY -= dy;
3935     }
3936     if (widget()->pos() != QPoint(0, 0)) {
3937         // qCDebug(KHTML_LOG) << "Static widget wasn't positioned at (0,0). This should NOT happen. Please report this event to developers.";
3938         widget()->move(0, 0);
3939     }
3940 
3941     QWidget *w = widget();
3942     QPoint off;
3943     if (m_kwp->isRedirected()) {
3944         // This is a redirected sub frame. Translate to root view context
3945         KHTMLView *v = m_kwp->rootViewPos(off);
3946         if (v) {
3947             w = v->widget();
3948         }
3949         off = viewport()->mapTo(this, off);
3950     }
3951 
3952     if (d->staticWidget) {
3953 
3954         // now remove from view the external widgets that must have completely
3955         // disappeared after dx/dy scroll delta is effective
3956         if (!d->visibleWidgets.isEmpty()) {
3957             checkExternalWidgetsPosition();
3958         }
3959 
3960         if (d->staticWidget == KHTMLViewPrivate::SBPartial
3961                 && m_part->xmlDocImpl() && m_part->xmlDocImpl()->renderer()) {
3962             // static objects might be selectively repainted, like stones in flowing water
3963             QRegion r = static_cast<RenderCanvas *>(m_part->xmlDocImpl()->renderer())->staticRegion();
3964             r.translate(-contentsX(), -contentsY());
3965             QVector<QRect> ar = r.rects();
3966 
3967             for (int i = 0; i < ar.size(); ++i) {
3968                 widget()->update(ar[i]);
3969             }
3970             r = QRegion(QRect(0, 0, visibleWidth(), visibleHeight())) - r;
3971             ar = r.rects();
3972             for (int i = 0; i < ar.size(); ++i) {
3973                 w->scroll(dx, dy, ar[i].translated(off));
3974             }
3975             d->scrollExternalWidgets(dx, dy);
3976         } else {
3977             // we can't avoid a full update
3978             widget()->update();
3979         }
3980         if (d->accessKeysActivated) {
3981             d->scrollAccessKeys(dx, dy);
3982         }
3983 
3984         return;
3985     }
3986 
3987     if (m_kwp->isRedirected()) {
3988         const QRect rect(off.x(), off.y(), visibleWidth() * d->zoomLevel / 100, visibleHeight() * d->zoomLevel / 100);
3989         w->scroll(dx, dy, rect);
3990         if (d->zoomLevel != 100) {
3991             w->update(rect); // without this update we are getting bad rendering when an iframe is zoomed in
3992         }
3993     }  else {
3994         widget()->scroll(dx, dy, widget()->rect() & viewport()->rect());
3995     }
3996 
3997     d->scrollExternalWidgets(dx, dy);
3998     if (d->accessKeysActivated) {
3999         d->scrollAccessKeys(dx, dy);
4000     }
4001 }
4002 
setupSmoothScrolling(int dx,int dy)4003 void KHTMLView::setupSmoothScrolling(int dx, int dy)
4004 {
4005     // old or minimum speed
4006     int ddx = qMax(d->steps ? abs(d->dx) / d->steps : 0, 3);
4007     int ddy = qMax(d->steps ? abs(d->dy) / d->steps : 0, 3);
4008 
4009     // full scroll is remaining scroll plus new scroll
4010     d->dx = d->dx + dx;
4011     d->dy = d->dy + dy;
4012 
4013     if (d->dx == 0 && d->dy == 0) {
4014         d->stopScrolling();
4015         return;
4016     }
4017 
4018     d->steps = (sSmoothScrollTime - 1) / sSmoothScrollTick + 1;
4019 
4020     if (qMax(abs(d->dx), abs(d->dy)) / d->steps < qMax(ddx, ddy)) {
4021         // Don't move slower than average 4px/step in minimum one direction
4022         // This means fewer than normal steps
4023         d->steps = qMax((abs(d->dx) + ddx - 1) / ddx, (abs(d->dy) + ddy - 1) / ddy);
4024         if (d->steps < 1) {
4025             d->steps = 1;
4026         }
4027     }
4028 
4029     d->smoothScrollStopwatch.start();
4030     if (!d->smoothScrolling) {
4031         d->startScrolling();
4032         scrollTick();
4033     }
4034 }
4035 
scrollTick()4036 void KHTMLView::scrollTick()
4037 {
4038     if (d->dx == 0 && d->dy == 0) {
4039         d->stopScrolling();
4040         return;
4041     }
4042 
4043     if (d->steps < 1) {
4044         d->steps = 1;
4045     }
4046     int takesteps = d->smoothScrollStopwatch.restart() / sSmoothScrollTick;
4047     int scroll_x = 0;
4048     int scroll_y = 0;
4049     if (takesteps < 1) {
4050         takesteps = 1;
4051     }
4052     if (takesteps > d->steps) {
4053         takesteps = d->steps;
4054     }
4055     for (int i = 0; i < takesteps; i++) {
4056         int ddx = (d->dx / (d->steps + 1)) * 2;
4057         int ddy = (d->dy / (d->steps + 1)) * 2;
4058 
4059         // limit step to requested scrolling distance
4060         if (abs(ddx) > abs(d->dx)) {
4061             ddx = d->dx;
4062         }
4063         if (abs(ddy) > abs(d->dy)) {
4064             ddy = d->dy;
4065         }
4066 
4067         // update remaining scroll
4068         d->dx -= ddx;
4069         d->dy -= ddy;
4070         scroll_x += ddx;
4071         scroll_y += ddy;
4072         d->steps--;
4073     }
4074 
4075     d->shouldSmoothScroll = false;
4076     scrollContentsBy(scroll_x, scroll_y);
4077 
4078     if (takesteps < 2) {
4079         d->smoothScrollMissedDeadlines = 0;
4080     } else {
4081         if (d->smoothScrollMissedDeadlines != sWayTooMany &&
4082                 (!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->parsing())) {
4083             d->smoothScrollMissedDeadlines++;
4084             if (d->smoothScrollMissedDeadlines >= sMaxMissedDeadlines) {
4085                 // we missed many deadlines in a row!
4086                 // time to signal we had enough..
4087                 d->smoothScrollMissedDeadlines = sWayTooMany;
4088             }
4089         }
4090     }
4091 }
4092 
addChild(QWidget * child,int x,int y)4093 void KHTMLView::addChild(QWidget *child, int x, int y)
4094 {
4095     if (!child) {
4096         return;
4097     }
4098 
4099     if (child->parent() != widget()) {
4100         child->setParent(widget());
4101     }
4102 
4103     // ### handle pseudo-zooming of non-redirected widgets (e.g. just resize'em)
4104 
4105     child->move(x - contentsX(), y - contentsY());
4106 }
4107 
timerEvent(QTimerEvent * e)4108 void KHTMLView::timerEvent(QTimerEvent *e)
4109 {
4110 //    qCDebug(KHTML_LOG) << "timer event " << e->timerId();
4111     if (e->timerId() == d->scrollTimerId) {
4112         if (d->scrollSuspended) {
4113             return;
4114         }
4115         switch (d->scrollDirection) {
4116         case KHTMLViewPrivate::ScrollDown:
4117             if (contentsY() + visibleHeight() >= contentsHeight()) {
4118                 d->newScrollTimer(this, 0);
4119             } else {
4120                 verticalScrollBar()->setValue(verticalScrollBar()->value() + d->scrollBy);
4121             }
4122             break;
4123         case KHTMLViewPrivate::ScrollUp:
4124             if (contentsY() <= 0) {
4125                 d->newScrollTimer(this, 0);
4126             } else {
4127                 verticalScrollBar()->setValue(verticalScrollBar()->value() - d->scrollBy);
4128             }
4129             break;
4130         case KHTMLViewPrivate::ScrollRight:
4131             if (contentsX() + visibleWidth() >= contentsWidth()) {
4132                 d->newScrollTimer(this, 0);
4133             } else {
4134                 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + d->scrollBy);
4135             }
4136             break;
4137         case KHTMLViewPrivate::ScrollLeft:
4138             if (contentsX() <= 0) {
4139                 d->newScrollTimer(this, 0);
4140             } else {
4141                 horizontalScrollBar()->setValue(horizontalScrollBar()->value() - d->scrollBy);
4142             }
4143             break;
4144         }
4145         return;
4146     } else if (e->timerId() == d->scrollingFromWheelTimerId) {
4147         killTimer(d->scrollingFromWheelTimerId);
4148         d->scrollingFromWheelTimerId = 0;
4149     } else if (e->timerId() == d->layoutTimerId) {
4150         if (d->firstLayoutPending && d->layoutAttemptCounter < 4
4151                 && (!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->readyForLayout())) {
4152             d->layoutAttemptCounter++;
4153             killTimer(d->layoutTimerId);
4154             d->layoutTimerId = 0;
4155             scheduleRelayout();
4156             return;
4157         }
4158         layout();
4159         d->scheduledLayoutCounter++;
4160         if (d->firstLayoutPending) {
4161             d->firstLayoutPending = false;
4162             verticalScrollBar()->setEnabled(true);
4163             horizontalScrollBar()->setEnabled(true);
4164         }
4165     }
4166 
4167     d->contentsMoving = false;
4168     if (m_part->xmlDocImpl()) {
4169         DOM::DocumentImpl *document = m_part->xmlDocImpl();
4170         khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer());
4171 
4172         if (root && root->needsLayout()) {
4173             if (d->repaintTimerId) {
4174                 killTimer(d->repaintTimerId);
4175             }
4176             d->repaintTimerId = 0;
4177             scheduleRelayout();
4178             return;
4179         }
4180     }
4181 
4182     if (d->repaintTimerId) {
4183         killTimer(d->repaintTimerId);
4184     }
4185     d->repaintTimerId = 0;
4186 
4187     QRect updateRegion;
4188     const QVector<QRect> rects = d->updateRegion.rects();
4189 
4190     d->updateRegion = QRegion();
4191 
4192     if (rects.size()) {
4193         updateRegion = rects[0];
4194     }
4195 
4196     for (int i = 1; i < rects.size(); ++i) {
4197         QRect newRegion = updateRegion.united(rects[i]);
4198         if (2 * newRegion.height() > 3 * updateRegion.height()) {
4199             repaintContents(updateRegion);
4200             updateRegion = rects[i];
4201         } else {
4202             updateRegion = newRegion;
4203         }
4204     }
4205 
4206     if (!updateRegion.isNull()) {
4207         repaintContents(updateRegion);
4208     }
4209 
4210     // As widgets can only be accurately positioned during painting, every layout might
4211     // dissociate a widget from its RenderWidget. E.g: if a RenderWidget was visible before layout, but the layout
4212     // pushed it out of the viewport, it will not be repainted, and consequently it's associated widget won't be repositioned.
4213     // Thus we need to check each supposedly 'visible' widget at the end of layout, and remove it in case it's no more in sight.
4214 
4215     if (d->dirtyLayout && !d->visibleWidgets.isEmpty()) {
4216         checkExternalWidgetsPosition();
4217     }
4218 
4219     d->dirtyLayout = false;
4220 
4221     emit repaintAccessKeys();
4222     if (d->emitCompletedAfterRepaint) {
4223         bool full = d->emitCompletedAfterRepaint == KHTMLViewPrivate::CSFull;
4224         d->emitCompletedAfterRepaint = KHTMLViewPrivate::CSNone;
4225         if (full) {
4226             emit m_part->completed();
4227         } else {
4228             emit m_part->completed(true);
4229         }
4230     }
4231 }
4232 
checkExternalWidgetsPosition()4233 void KHTMLView::checkExternalWidgetsPosition()
4234 {
4235     QWidget *w;
4236     QRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
4237     QList<RenderWidget *> toRemove;
4238     QHashIterator<void *, QWidget *> it(d->visibleWidgets);
4239     while (it.hasNext()) {
4240         int xp = 0, yp = 0;
4241         it.next();
4242         RenderWidget *rw = static_cast<RenderWidget *>(it.key());
4243         if (!rw->absolutePosition(xp, yp) ||
4244                 !visibleRect.intersects(QRect(xp, yp, it.value()->width(), it.value()->height()))) {
4245             toRemove.append(rw);
4246         }
4247     }
4248     foreach (RenderWidget *r, toRemove)
4249         if ((w = d->visibleWidgets.take(r))) {
4250             w->move(0, -500000);
4251         }
4252 }
4253 
scheduleRelayout(khtml::RenderObject *)4254 void KHTMLView::scheduleRelayout(khtml::RenderObject * /*clippedObj*/)
4255 {
4256     if (!d->layoutSchedulingEnabled || d->layoutTimerId) {
4257         return;
4258     }
4259 
4260     int time = 0;
4261     if (d->firstLayoutPending) {
4262         // Any repaint happening while we have no content blanks the viewport ("white flash").
4263         // Hence the need to delay the first layout as much as we can.
4264         // Only if the document gets stuck for too long in incomplete state will we allow the blanking.
4265         time = d->layoutAttemptCounter ?
4266                sLayoutAttemptDelay + sLayoutAttemptIncrement * d->layoutAttemptCounter : sFirstLayoutDelay;
4267     } else if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing()) {
4268         // Delay between successive layouts in parsing mode.
4269         // Increment reflects the decaying importance of visual feedback over time.
4270         time = qMin(2000, sParsingLayoutsInterval + d->scheduledLayoutCounter * sParsingLayoutsIncrement);
4271     }
4272     d->layoutTimerId = startTimer(time);
4273 }
4274 
unscheduleRelayout()4275 void KHTMLView::unscheduleRelayout()
4276 {
4277     if (!d->layoutTimerId) {
4278         return;
4279     }
4280 
4281     killTimer(d->layoutTimerId);
4282     d->layoutTimerId = 0;
4283 }
4284 
unscheduleRepaint()4285 void KHTMLView::unscheduleRepaint()
4286 {
4287     if (!d->repaintTimerId) {
4288         return;
4289     }
4290 
4291     killTimer(d->repaintTimerId);
4292     d->repaintTimerId = 0;
4293 }
4294 
scheduleRepaint(int x,int y,int w,int h,bool asap)4295 void KHTMLView::scheduleRepaint(int x, int y, int w, int h, bool asap)
4296 {
4297     bool parsing = !m_part->xmlDocImpl() || m_part->xmlDocImpl()->parsing();
4298 
4299 //     qCDebug(KHTML_LOG) << "parsing " << parsing;
4300 //     qCDebug(KHTML_LOG) << "complete " << d->complete;
4301 
4302     int time = parsing && !d->firstLayoutPending ? 150 : (!asap ? (!d->complete ? 80 : 20) : 0);
4303 
4304 #ifdef DEBUG_FLICKER
4305     QPainter p;
4306     p.begin(viewport());
4307 
4308     int vx, vy;
4309     contentsToViewport(x, y, vx, vy);
4310     p.fillRect(vx, vy, w, h, Qt::red);
4311     p.end();
4312 #endif
4313 
4314     d->updateRegion = d->updateRegion.united(QRect(x, y, w, h));
4315 
4316     if (asap && !parsing) {
4317         unscheduleRepaint();
4318     }
4319 
4320     if (!d->repaintTimerId) {
4321         d->repaintTimerId = startTimer(time);
4322     }
4323 
4324 //     qCDebug(KHTML_LOG) << "starting timer " << time;
4325 }
4326 
complete(bool pendingAction)4327 void KHTMLView::complete(bool pendingAction)
4328 {
4329 //     qCDebug(KHTML_LOG) << "KHTMLView::complete()";
4330 
4331     d->complete = true;
4332 
4333     // is there a relayout pending?
4334     if (d->layoutTimerId) {
4335 //         qCDebug(KHTML_LOG) << "requesting relayout now";
4336         // do it now
4337         killTimer(d->layoutTimerId);
4338         d->layoutTimerId = startTimer(0);
4339         d->emitCompletedAfterRepaint = pendingAction ?
4340                                        KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
4341     }
4342 
4343     // is there a repaint pending?
4344     if (d->repaintTimerId) {
4345 //         qCDebug(KHTML_LOG) << "requesting repaint now";
4346         // do it now
4347         killTimer(d->repaintTimerId);
4348         d->repaintTimerId = startTimer(0);
4349         d->emitCompletedAfterRepaint = pendingAction ?
4350                                        KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull;
4351     }
4352 
4353     if (!d->emitCompletedAfterRepaint) {
4354         if (!pendingAction) {
4355             emit m_part->completed();
4356         } else {
4357             emit m_part->completed(true);
4358         }
4359     }
4360 
4361 }
4362 
updateScrollBars()4363 void KHTMLView::updateScrollBars()
4364 {
4365     const QWidget *view = widget();
4366     if (!view) {
4367         return;
4368     }
4369 
4370     QSize p = viewport()->size();
4371     QSize m = maximumViewportSize();
4372 
4373     if (m.expandedTo(view->size()) == m) {
4374         p = m;    // no scroll bars needed
4375     }
4376 
4377     QSize v = view->size();
4378     horizontalScrollBar()->setRange(0, v.width() - p.width());
4379     horizontalScrollBar()->setPageStep(p.width());
4380     verticalScrollBar()->setRange(0, v.height() - p.height());
4381     verticalScrollBar()->setPageStep(p.height());
4382     if (!d->smoothScrolling) {
4383         d->updateContentsXY();
4384     }
4385 }
4386 
slotMouseScrollTimer()4387 void KHTMLView::slotMouseScrollTimer()
4388 {
4389     horizontalScrollBar()->setValue(horizontalScrollBar()->value() + d->m_mouseScroll_byX);
4390     verticalScrollBar()->setValue(verticalScrollBar()->value() + d->m_mouseScroll_byY);
4391 }
4392 
positionOfLineBoundary(const DOM::Position & pos,bool toEnd)4393 static DOM::Position positionOfLineBoundary(const DOM::Position &pos, bool toEnd)
4394 {
4395     Selection sel = pos;
4396     sel.expandUsingGranularity(Selection::LINE);
4397     return toEnd ? sel.end() : sel.start();
4398 }
4399 
positionOfLineBegin(const DOM::Position & pos)4400 inline static DOM::Position positionOfLineBegin(const DOM::Position &pos)
4401 {
4402     return positionOfLineBoundary(pos, false);
4403 }
4404 
positionOfLineEnd(const DOM::Position & pos)4405 inline static DOM::Position positionOfLineEnd(const DOM::Position &pos)
4406 {
4407     return positionOfLineBoundary(pos, true);
4408 }
4409 
caretKeyPressEvent(QKeyEvent * _ke)4410 bool KHTMLView::caretKeyPressEvent(QKeyEvent *_ke)
4411 {
4412     EditorContext *ec = &m_part->d->editor_context;
4413     Selection &caret = ec->m_selection;
4414     Position old_pos = caret.caretPos();
4415     Position pos = old_pos;
4416     bool recalcXPos = true;
4417     bool handled = true;
4418 
4419     bool ctrl = _ke->modifiers() & Qt::ControlModifier;
4420     bool shift = _ke->modifiers() & Qt::ShiftModifier;
4421 
4422     switch (_ke->key()) {
4423 
4424     // -- Navigational keys
4425     case Qt::Key_Down:
4426         pos = old_pos.nextLinePosition(caret.xPosForVerticalArrowNavigation(Selection::EXTENT));
4427         recalcXPos = false;
4428         break;
4429 
4430     case Qt::Key_Up:
4431         pos = old_pos.previousLinePosition(caret.xPosForVerticalArrowNavigation(Selection::EXTENT));
4432         recalcXPos = false;
4433         break;
4434 
4435     case Qt::Key_Left:
4436         pos = ctrl ? old_pos.previousWordPosition() : old_pos.previousCharacterPosition();
4437         break;
4438 
4439     case Qt::Key_Right:
4440         pos = ctrl ? old_pos.nextWordPosition() : old_pos.nextCharacterPosition();
4441         break;
4442 
4443     case Qt::Key_PageDown:
4444 //       moveCaretNextPage(); ###
4445         break;
4446 
4447     case Qt::Key_PageUp:
4448 //       moveCaretPrevPage(); ###
4449         break;
4450 
4451     case Qt::Key_Home:
4452         if (ctrl)
4453             /*moveCaretToDocumentBoundary(false)*/; // ###
4454         else {
4455             pos = positionOfLineBegin(old_pos);
4456         }
4457         break;
4458 
4459     case Qt::Key_End:
4460         if (ctrl)
4461             /*moveCaretToDocumentBoundary(true)*/; // ###
4462         else {
4463             pos = positionOfLineEnd(old_pos);
4464         }
4465         break;
4466 
4467     default:
4468         handled = false;
4469 
4470     }/*end switch*/
4471 
4472     if (pos != old_pos) {
4473         m_part->clearCaretRectIfNeeded();
4474 
4475         caret.moveTo(shift ? caret.nonCaretPos() : pos, pos);
4476         int old_x = caret.xPosForVerticalArrowNavigation(Selection::CARETPOS);
4477 
4478         m_part->selectionLayoutChanged();
4479 
4480         // restore old x-position to prevent recalculation
4481         if (!recalcXPos) {
4482             m_part->d->editor_context.m_xPosForVerticalArrowNavigation = old_x;
4483         }
4484 
4485         m_part->emitCaretPositionChanged(pos);
4486         // ### check when to emit it
4487         m_part->notifySelectionChanged();
4488 
4489     }
4490 
4491     if (handled) {
4492         _ke->accept();
4493     }
4494     return handled;
4495 }
4496 
4497 #undef DEBUG_CARETMODE
4498