shadowAncestorNode() should be renamed to deprecatedShadowAncestorNode()
[WebKit-https.git] / Source / WebKit / qt / WebCoreSupport / QWebPageAdapter.cpp
1 /*
2  * Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  *
19  */
20
21 #include "config.h"
22 #include "QWebPageAdapter.h"
23
24 #include "CSSComputedStyleDeclaration.h"
25 #include "CSSParser.h"
26 #include "Chrome.h"
27 #include "ChromeClientQt.h"
28 #include "ClientRect.h"
29 #include "ContextMenu.h"
30 #include "ContextMenuClientQt.h"
31 #include "ContextMenuController.h"
32 #if ENABLE(DEVICE_ORIENTATION)
33 #include "DeviceMotionClientQt.h"
34 #include "DeviceOrientationClientMock.h"
35 #include "DeviceOrientationClientQt.h"
36 #endif
37 #include "DocumentLoader.h"
38 #include "DragClientQt.h"
39 #include "DragController.h"
40 #include "DragData.h"
41 #include "DragSession.h"
42 #include "EditorClientQt.h"
43 #include "FocusController.h"
44 #include "FrameView.h"
45 #if ENABLE(GEOLOCATION)
46 #include "GeolocationClientMock.h"
47 #include "GeolocationClientQt.h"
48 #include "GeolocationController.h"
49 #endif
50 #include "GeolocationPermissionClientQt.h"
51 #include "HTMLFrameOwnerElement.h"
52 #include "HTMLInputElement.h"
53 #include "HitTestResult.h"
54 #include "InitWebCoreQt.h"
55 #include "InspectorClientQt.h"
56 #include "InspectorController.h"
57 #include "InspectorServerQt.h"
58 #include "LocalizedStrings.h"
59 #include "MIMETypeRegistry.h"
60 #include "MemoryCache.h"
61 #include "NetworkingContext.h"
62 #include "NodeList.h"
63 #include "NotificationPresenterClientQt.h"
64 #include "PageGroup.h"
65 #include "Pasteboard.h"
66 #include "PlatformKeyboardEvent.h"
67 #include "PlatformTouchEvent.h"
68 #include "PlatformWheelEvent.h"
69 #include "PluginDatabase.h"
70 #include "PluginPackage.h"
71 #include "ProgressTracker.h"
72 #include "QWebFrameAdapter.h"
73 #include "RenderTextControl.h"
74 #include "SchemeRegistry.h"
75 #include "Scrollbar.h"
76 #include "ScrollbarTheme.h"
77 #include "Settings.h"
78 #include "UndoStepQt.h"
79 #include "UserAgentQt.h"
80 #include "WebEventConversion.h"
81 #include "WebKitVersion.h"
82 #include "WindowFeatures.h"
83 #include "qwebhistory_p.h"
84 #include "qwebpluginfactory.h"
85 #include "qwebsettings.h"
86 #include <Page.h>
87 #include <QBitArray>
88 #include <QGuiApplication>
89 #include <QMimeData>
90 #include <QMouseEvent>
91 #include <QNetworkAccessManager>
92 #include <QStyleHints>
93 #include <QTextCharFormat>
94 #include <QTouchEvent>
95 #include <QWheelEvent>
96
97 // from text/qfont.cpp
98 QT_BEGIN_NAMESPACE
99 extern Q_GUI_EXPORT int qt_defaultDpi();
100 QT_END_NAMESPACE
101
102 using namespace WebCore;
103
104 bool QWebPageAdapter::drtRun = false;
105
106 typedef QWebPageAdapter::MenuItemDescription MenuItem;
107
108 static inline DragOperation dropActionToDragOp(Qt::DropActions actions)
109 {
110     unsigned result = 0;
111     if (actions & Qt::CopyAction)
112         result |= DragOperationCopy;
113     // DragOperationgeneric represents InternetExplorer's equivalent of Move operation,
114     // hence it should be considered as "move"
115     if (actions & Qt::MoveAction)
116         result |= (DragOperationMove | DragOperationGeneric);
117     if (actions & Qt::LinkAction)
118         result |= DragOperationLink;
119     if (result == (DragOperationCopy | DragOperationMove | DragOperationGeneric | DragOperationLink))
120         result = DragOperationEvery;
121     return (DragOperation)result;
122 }
123
124 static inline Qt::DropAction dragOpToDropAction(unsigned actions)
125 {
126     Qt::DropAction result = Qt::IgnoreAction;
127     if (actions & DragOperationCopy)
128         result = Qt::CopyAction;
129     else if (actions & DragOperationMove)
130         result = Qt::MoveAction;
131     // DragOperationgeneric represents InternetExplorer's equivalent of Move operation,
132     // hence it should be considered as "move"
133     else if (actions & DragOperationGeneric)
134         result = Qt::MoveAction;
135     else if (actions & DragOperationLink)
136         result = Qt::LinkAction;
137     return result;
138 }
139
140 static WebCore::FrameLoadRequest frameLoadRequest(const QUrl &url, WebCore::Frame *frame)
141 {
142     return WebCore::FrameLoadRequest(frame->document()->securityOrigin(),
143         WebCore::ResourceRequest(url, frame->loader()->outgoingReferrer()));
144 }
145
146 static void openNewWindow(const QUrl& url, Frame* frame)
147 {
148     if (Page* oldPage = frame->page()) {
149         WindowFeatures features;
150         NavigationAction action;
151         FrameLoadRequest request = frameLoadRequest(url, frame);
152         if (Page* newPage = oldPage->chrome()->createWindow(frame, request, features, action)) {
153             newPage->mainFrame()->loader()->loadFrameRequest(request, false, false, 0, 0, MaybeSendReferrer);
154             newPage->chrome()->show();
155         }
156     }
157 }
158
159 QWebPageAdapter::QWebPageAdapter()
160     : settings(0)
161     , page(0)
162     , pluginFactory(0)
163     , forwardUnsupportedContent(false)
164     , insideOpenCall(false)
165     , clickCausedFocus(false)
166     , m_totalBytes(0)
167     , m_bytesReceived()
168     , networkManager(0)
169 {
170     WebCore::initializeWebCoreQt();
171 }
172
173 void QWebPageAdapter::initializeWebCorePage()
174 {
175 #if ENABLE(GEOLOCATION) || ENABLE(DEVICE_ORIENTATION)
176     bool useMock = QWebPageAdapter::drtRun;
177 #endif
178     Page::PageClients pageClients;
179     pageClients.chromeClient = new ChromeClientQt(this);
180     pageClients.contextMenuClient = new ContextMenuClientQt();
181     pageClients.editorClient = new EditorClientQt(this);
182     pageClients.dragClient = new DragClientQt(pageClients.chromeClient);
183     pageClients.inspectorClient = new InspectorClientQt(this);
184     page = new Page(pageClients);
185 #if ENABLE(GEOLOCATION)
186     if (useMock) {
187         // In case running in DumpRenderTree mode set the controller to mock provider.
188         GeolocationClientMock* mock = new GeolocationClientMock;
189         WebCore::provideGeolocationTo(page, mock);
190         mock->setController(WebCore::GeolocationController::from(page));
191     } else
192         WebCore::provideGeolocationTo(page, new GeolocationClientQt(this));
193 #endif
194 #if ENABLE(DEVICE_ORIENTATION)
195     if (useMock)
196         WebCore::provideDeviceOrientationTo(page, new DeviceOrientationClientMock);
197     else
198         WebCore::provideDeviceOrientationTo(page, new DeviceOrientationClientQt);
199     WebCore::provideDeviceMotionTo(page, new DeviceMotionClientQt);
200 #endif
201
202     // By default each page is put into their own unique page group, which affects popup windows
203     // and visited links. Page groups (per process only) is a feature making it possible to use
204     // separate settings for each group, so that for instance an integrated browser/email reader
205     // can use different settings for displaying HTML pages and HTML email. To make QtWebKit work
206     // as expected out of the box, we use a default group similar to what other ports are doing.
207     page->setGroupName("Default Group");
208
209     page->addLayoutMilestones(DidFirstVisuallyNonEmptyLayout);
210
211     settings = new QWebSettings(page->settings());
212
213 #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
214     WebCore::provideNotification(page, NotificationPresenterClientQt::notificationPresenter());
215 #endif
216
217     history.d = new QWebHistoryPrivate(static_cast<WebCore::BackForwardListImpl*>(page->backForwardList()));
218
219     PageGroup::setShouldTrackVisitedLinks(true);
220 }
221
222 QWebPageAdapter::~QWebPageAdapter()
223 {
224     delete page;
225     delete settings;
226
227 #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
228     NotificationPresenterClientQt::notificationPresenter()->removeClient();
229 #endif
230 }
231
232 void QWebPageAdapter::deletePage()
233 {
234     // Before we delete the page, detach the mainframe's loader
235     FrameLoader* loader = mainFrameAdapter()->frame->loader();
236     if (loader)
237         loader->detachFromParent();
238     delete page;
239     page = 0;
240 }
241
242 QWebPageAdapter* QWebPageAdapter::kit(Page* page)
243 {
244     return static_cast<ChromeClientQt*>(page->chrome()->client())->m_webPage;
245 }
246
247 ViewportArguments QWebPageAdapter::viewportArguments() const
248 {
249     return page ? page->viewportArguments() : WebCore::ViewportArguments();
250 }
251
252
253 void QWebPageAdapter::registerUndoStep(WTF::PassRefPtr<WebCore::UndoStep> step)
254 {
255     createUndoStep(QSharedPointer<UndoStepQt>(new UndoStepQt(step)));
256 }
257
258 void QWebPageAdapter::setNetworkAccessManager(QNetworkAccessManager *manager)
259 {
260     if (manager == networkManager)
261         return;
262     if (networkManager && networkManager->parent() == handle())
263         delete networkManager;
264     networkManager = manager;
265 }
266
267 QNetworkAccessManager* QWebPageAdapter::networkAccessManager()
268 {
269     if (!networkManager)
270         networkManager = new QNetworkAccessManager(handle());
271     return networkManager;
272 }
273
274 bool QWebPageAdapter::hasSelection() const
275 {
276     Frame* frame = page->focusController()->focusedOrMainFrame();
277     if (frame)
278         return (frame->selection()->selection().selectionType() != VisibleSelection::NoSelection);
279     return false;
280 }
281
282 QString QWebPageAdapter::selectedText() const
283 {
284     Frame* frame = page->focusController()->focusedOrMainFrame();
285     if (frame->selection()->selection().selectionType() == VisibleSelection::NoSelection)
286         return QString();
287     return frame->editor()->selectedText();
288 }
289
290 QString QWebPageAdapter::selectedHtml() const
291 {
292     return page->focusController()->focusedOrMainFrame()->editor()->selectedRange()->toHTML();
293 }
294
295 bool QWebPageAdapter::isContentEditable() const
296 {
297     return page->isEditable();
298 }
299
300 void QWebPageAdapter::setContentEditable(bool editable)
301 {
302     page->setEditable(editable);
303     page->setTabKeyCyclesThroughElements(!editable);
304
305     Frame* frame = mainFrameAdapter()->frame;
306     if (editable) {
307         frame->editor()->applyEditingStyleToBodyElement();
308         // FIXME: mac port calls this if there is no selectedDOMRange
309         // frame->setSelectionFromNone();
310     }
311
312 }
313
314 bool QWebPageAdapter::findText(const QString& subString, FindFlag options)
315 {
316     ::TextCaseSensitivity caseSensitivity = ::TextCaseInsensitive;
317     if (options & FindCaseSensitively)
318         caseSensitivity = ::TextCaseSensitive;
319
320     if (options & HighlightAllOccurrences) {
321         if (subString.isEmpty()) {
322             page->unmarkAllTextMatches();
323             return true;
324         }
325         return page->markAllMatchesForText(subString, caseSensitivity, true, 0);
326     }
327
328     if (subString.isEmpty()) {
329         page->mainFrame()->selection()->clear();
330         Frame* frame = page->mainFrame()->tree()->firstChild();
331         while (frame) {
332             frame->selection()->clear();
333             frame = frame->tree()->traverseNextWithWrap(false);
334         }
335     }
336     ::FindDirection direction = ::FindDirectionForward;
337     if (options & FindBackward)
338         direction = ::FindDirectionBackward;
339
340     const bool shouldWrap = options & FindWrapsAroundDocument;
341
342     return page->findString(subString, caseSensitivity, direction, shouldWrap);
343 }
344
345 void QWebPageAdapter::adjustPointForClicking(QMouseEvent* ev)
346 {
347     QtPlatformPlugin platformPlugin;
348     OwnPtr<QWebTouchModifier> touchModifier = platformPlugin.createTouchModifier();
349     if (!touchModifier)
350         return;
351
352     unsigned topPadding = touchModifier->hitTestPaddingForTouch(QWebTouchModifier::Up);
353     unsigned rightPadding = touchModifier->hitTestPaddingForTouch(QWebTouchModifier::Right);
354     unsigned bottomPadding = touchModifier->hitTestPaddingForTouch(QWebTouchModifier::Down);
355     unsigned leftPadding = touchModifier->hitTestPaddingForTouch(QWebTouchModifier::Left);
356
357     touchModifier = nullptr;
358
359     if (!topPadding && !rightPadding && !bottomPadding && !leftPadding)
360         return;
361
362     Document* startingDocument = page->mainFrame()->document();
363     if (!startingDocument)
364         return;
365
366     IntPoint originalPoint(ev->pos());
367     TouchAdjuster touchAdjuster(topPadding, rightPadding, bottomPadding, leftPadding);
368     IntPoint adjustedPoint = touchAdjuster.findCandidatePointForTouch(originalPoint, startingDocument);
369     if (adjustedPoint == IntPoint::zero())
370         return;
371     QMouseEvent* ret = new QMouseEvent(ev->type(), QPoint(adjustedPoint), ev->globalPos(), ev->button(), ev->buttons(), ev->modifiers());
372     delete ev;
373     ev = ret;
374 }
375
376 static bool hasMouseListener(Element* element)
377 {
378     ASSERT(element);
379     return element->hasEventListeners(eventNames().clickEvent)
380         || element->hasEventListeners(eventNames().mousedownEvent)
381         || element->hasEventListeners(eventNames().mouseupEvent);
382 }
383
384 static bool isClickableElement(Element* element, PassRefPtr<NodeList> prpList)
385 {
386     ASSERT(element);
387     RefPtr<NodeList> list = prpList;
388     bool isClickable = hasMouseListener(element);
389     if (!isClickable && list) {
390         Element* parent = element->parentElement();
391         unsigned count = list->length();
392         for (unsigned i = 0; i < count && parent; i++) {
393             if (list->item(i) != parent)
394                 continue;
395
396             isClickable = hasMouseListener(parent);
397             if (isClickable)
398                 break;
399
400             parent = parent->parentElement();
401         }
402     }
403
404     ExceptionCode ec = 0;
405     return isClickable
406         || element->webkitMatchesSelector("a,*:link,*:visited,*[role=button],button,input,select,label", ec)
407         || CSSComputedStyleDeclaration::create(element)->getPropertyValue(cssPropertyID("cursor")) == "pointer";
408 }
409
410 static bool isValidFrameOwner(Element* element)
411 {
412     ASSERT(element);
413     return element->isFrameOwnerElement() && static_cast<HTMLFrameOwnerElement*>(element)->contentFrame();
414 }
415
416 QWebPageAdapter::TouchAdjuster::TouchAdjuster(unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
417     : m_topPadding(topPadding)
418     , m_rightPadding(rightPadding)
419     , m_bottomPadding(bottomPadding)
420     , m_leftPadding(leftPadding)
421 {
422 }
423
424 IntPoint QWebPageAdapter::TouchAdjuster::findCandidatePointForTouch(const IntPoint& touchPoint, Document* document) const
425 {
426     if (!document)
427         return IntPoint();
428
429     int x = touchPoint.x();
430     int y = touchPoint.y();
431
432     RefPtr<NodeList> intersectedNodes = document->nodesFromRect(x, y, m_topPadding, m_rightPadding, m_bottomPadding, m_leftPadding, false /*ignoreClipping*/, false /*allowShadowContent*/);
433     if (!intersectedNodes)
434         return IntPoint();
435
436     Element* closestClickableElement = 0;
437     IntRect largestIntersectionRect;
438     FrameView* view = document->frame()->view();
439
440     // Touch rect in contents coordinates.
441     IntRect touchRect(HitTestLocation::rectForPoint(view->windowToContents(IntPoint(x, y)), m_topPadding, m_rightPadding, m_bottomPadding, m_leftPadding));
442
443     // Iterate over the list of nodes hit looking for the one whose bounding area
444     // has largest intersection with the touch area (point + padding).
445     for (unsigned i = 0; i < intersectedNodes->length(); i++) {
446         Node* currentNode = intersectedNodes->item(i);
447
448         Element* currentElement = currentNode->isElementNode() ? toElement(currentNode) : 0;
449         if (!currentElement || (!isClickableElement(currentElement, 0) && !isValidFrameOwner(currentElement)))
450             continue;
451
452         IntRect currentElementBoundingRect = currentElement->pixelSnappedBoundingBox();
453         currentElementBoundingRect.intersect(touchRect);
454
455         if (currentElementBoundingRect.isEmpty())
456             continue;
457
458         int currentIntersectionRectArea = currentElementBoundingRect.width() * currentElementBoundingRect.height();
459         int largestIntersectionRectArea = largestIntersectionRect.width() * largestIntersectionRect.height();
460         if (currentIntersectionRectArea > largestIntersectionRectArea) {
461             closestClickableElement = currentElement;
462             largestIntersectionRect = currentElementBoundingRect;
463         }
464     }
465
466     if (largestIntersectionRect.isEmpty())
467         return IntPoint();
468
469     // Handle the case when user taps a inner frame. It is done in three steps:
470     // 1) Transform the original touch point to the inner document coordinates;
471     // 1) Call nodesFromRect for the inner document in case;
472     // 3) Re-add the inner frame offset (location) before passing the new clicking
473     //    position to WebCore.
474     if (closestClickableElement->isFrameOwnerElement()) {
475         // Adjust client coordinates' origin to be top left of inner frame viewport.
476         RefPtr<ClientRect> rect = closestClickableElement->getBoundingClientRect();
477         IntPoint newTouchPoint = touchPoint;
478         IntSize offset =  IntSize(rect->left(), rect->top());
479         newTouchPoint -= offset;
480
481         HTMLFrameOwnerElement* owner = static_cast<HTMLFrameOwnerElement*>(closestClickableElement);
482         Document* childDocument = owner->contentFrame()->document();
483         return findCandidatePointForTouch(newTouchPoint, childDocument);
484     }
485     return view->contentsToWindow(largestIntersectionRect).center();
486 }
487
488 void QWebPageAdapter::mouseMoveEvent(QMouseEvent* ev)
489 {
490     WebCore::Frame* frame = mainFrameAdapter()->frame;
491     if (!frame->view())
492         return;
493
494     bool accepted = frame->eventHandler()->mouseMoved(convertMouseEvent(ev, 0));
495     ev->setAccepted(accepted);
496 }
497
498 void QWebPageAdapter::mousePressEvent(QMouseEvent* ev)
499 {
500     WebCore::Frame* frame = mainFrameAdapter()->frame;
501     if (!frame->view())
502         return;
503
504     RefPtr<WebCore::Node> oldNode;
505     Frame* focusedFrame = page->focusController()->focusedFrame();
506     if (Document* focusedDocument = focusedFrame ? focusedFrame->document() : 0)
507         oldNode = focusedDocument->focusedNode();
508
509     if (tripleClickTimer.isActive()
510         && (ev->pos() - tripleClick).manhattanLength() < qGuiApp->styleHints()->startDragDistance()) {
511         mouseTripleClickEvent(ev);
512         return;
513     }
514
515     bool accepted = false;
516     PlatformMouseEvent mev = convertMouseEvent(ev, 1);
517     // ignore the event if we can't map Qt's mouse buttons to WebCore::MouseButton
518     if (mev.button() != NoButton)
519         accepted = frame->eventHandler()->handleMousePressEvent(mev);
520     ev->setAccepted(accepted);
521
522     RefPtr<WebCore::Node> newNode;
523     focusedFrame = page->focusController()->focusedFrame();
524     if (Document* focusedDocument = focusedFrame ? focusedFrame->document() : 0)
525         newNode = focusedDocument->focusedNode();
526
527     if (newNode && oldNode != newNode)
528         clickCausedFocus = true;
529 }
530
531 void QWebPageAdapter::mouseDoubleClickEvent(QMouseEvent *ev)
532 {
533     WebCore::Frame* frame = mainFrameAdapter()->frame;
534     if (!frame->view())
535         return;
536
537     bool accepted = false;
538     PlatformMouseEvent mev = convertMouseEvent(ev, 2);
539     // ignore the event if we can't map Qt's mouse buttons to WebCore::MouseButton
540     if (mev.button() != NoButton)
541         accepted = frame->eventHandler()->handleMousePressEvent(mev);
542     ev->setAccepted(accepted);
543
544     tripleClickTimer.start(qGuiApp->styleHints()->mouseDoubleClickInterval(), handle());
545     tripleClick = QPointF(ev->pos()).toPoint();
546 }
547
548 void QWebPageAdapter::mouseTripleClickEvent(QMouseEvent *ev)
549 {
550     WebCore::Frame* frame = mainFrameAdapter()->frame;
551     if (!frame->view())
552         return;
553
554     bool accepted = false;
555     PlatformMouseEvent mev = convertMouseEvent(ev, 3);
556     // ignore the event if we can't map Qt's mouse buttons to WebCore::MouseButton
557     if (mev.button() != NoButton)
558         accepted = frame->eventHandler()->handleMousePressEvent(mev);
559     ev->setAccepted(accepted);
560 }
561
562 void QWebPageAdapter::mouseReleaseEvent(QMouseEvent *ev)
563 {
564     WebCore::Frame* frame = mainFrameAdapter()->frame;
565     if (!frame->view())
566         return;
567
568     bool accepted = false;
569     PlatformMouseEvent mev = convertMouseEvent(ev, 0);
570     // ignore the event if we can't map Qt's mouse buttons to WebCore::MouseButton
571     if (mev.button() != NoButton)
572         accepted = frame->eventHandler()->handleMouseReleaseEvent(mev);
573     ev->setAccepted(accepted);
574
575     handleSoftwareInputPanel(ev->button(), QPointF(ev->pos()).toPoint());
576 }
577
578 void QWebPageAdapter::handleSoftwareInputPanel(Qt::MouseButton button, const QPoint& pos)
579 {
580     Frame* frame = page->focusController()->focusedFrame();
581     if (!frame)
582         return;
583
584     if (client && client->inputMethodEnabled()
585         && frame->document()->focusedNode()
586             && button == Qt::LeftButton && qGuiApp->property("autoSipEnabled").toBool()) {
587         if (!clickCausedFocus || requestSoftwareInputPanel()) {
588             HitTestResult result = frame->eventHandler()->hitTestResultAtPoint(frame->view()->windowToContents(pos), false);
589             if (result.isContentEditable()) {
590                 QEvent event(QEvent::RequestSoftwareInputPanel);
591                 QGuiApplication::sendEvent(client->ownerWidget(), &event);
592             }
593         }
594     }
595
596     clickCausedFocus = false;
597 }
598
599 #ifndef QT_NO_WHEELEVENT
600 void QWebPageAdapter::wheelEvent(QWheelEvent *ev, int wheelScrollLines)
601 {
602     WebCore::Frame* frame = mainFrameAdapter()->frame;
603     if (!frame->view())
604         return;
605
606     PlatformWheelEvent pev = convertWheelEvent(ev, wheelScrollLines);
607     bool accepted = frame->eventHandler()->handleWheelEvent(pev);
608     ev->setAccepted(accepted);
609 }
610 #endif // QT_NO_WHEELEVENT
611
612 #ifndef QT_NO_DRAGANDDROP
613
614 Qt::DropAction QWebPageAdapter::dragEntered(const QMimeData *data, const QPoint &pos, Qt::DropActions possibleActions)
615 {
616     DragData dragData(data, pos, QCursor::pos(), dropActionToDragOp(possibleActions));
617     return dragOpToDropAction(page->dragController()->dragEntered(&dragData).operation);
618 }
619
620 void QWebPageAdapter::dragLeaveEvent()
621 {
622     DragData dragData(0, IntPoint(), QCursor::pos(), DragOperationNone);
623     page->dragController()->dragExited(&dragData);
624 }
625
626 Qt::DropAction QWebPageAdapter::dragUpdated(const QMimeData *data, const QPoint &pos, Qt::DropActions possibleActions)
627 {
628     DragData dragData(data, pos, QCursor::pos(), dropActionToDragOp(possibleActions));
629     return dragOpToDropAction(page->dragController()->dragUpdated(&dragData).operation);
630 }
631
632 bool QWebPageAdapter::performDrag(const QMimeData *data, const QPoint &pos, Qt::DropActions possibleActions)
633 {
634     DragData dragData(data, pos, QCursor::pos(), dropActionToDragOp(possibleActions));
635     return page->dragController()->performDrag(&dragData);
636 }
637
638 void QWebPageAdapter::inputMethodEvent(QInputMethodEvent *ev)
639 {
640     WebCore::Frame *frame = page->focusController()->focusedOrMainFrame();
641     WebCore::Editor *editor = frame->editor();
642
643     if (!editor->canEdit()) {
644         ev->ignore();
645         return;
646     }
647
648     Node* node = 0;
649     if (frame->selection()->rootEditableElement())
650         node = frame->selection()->rootEditableElement()->deprecatedShadowAncestorNode();
651
652     Vector<CompositionUnderline> underlines;
653     bool hasSelection = false;
654
655     for (int i = 0; i < ev->attributes().size(); ++i) {
656         const QInputMethodEvent::Attribute& a = ev->attributes().at(i);
657         switch (a.type) {
658         case QInputMethodEvent::TextFormat: {
659             QTextCharFormat textCharFormat = a.value.value<QTextFormat>().toCharFormat();
660             QColor qcolor = textCharFormat.underlineColor();
661             underlines.append(CompositionUnderline(qMin(a.start, (a.start + a.length)), qMax(a.start, (a.start + a.length)), Color(makeRGBA(qcolor.red(), qcolor.green(), qcolor.blue(), qcolor.alpha())), false));
662             break;
663         }
664         case QInputMethodEvent::Cursor: {
665             frame->selection()->setCaretVisible(a.length); // if length is 0 cursor is invisible
666             if (a.length > 0) {
667                 RenderObject* caretRenderer = frame->selection()->caretRenderer();
668                 if (caretRenderer) {
669                     QColor qcolor = a.value.value<QColor>();
670                     caretRenderer->style()->setColor(Color(makeRGBA(qcolor.red(), qcolor.green(), qcolor.blue(), qcolor.alpha())));
671                 }
672             }
673             break;
674         }
675         case QInputMethodEvent::Selection: {
676             hasSelection = true;
677             // A selection in the inputMethodEvent is always reflected in the visible text
678             if (node) {
679                 if (isHTMLTextFormControlElement(node))
680                     toHTMLTextFormControlElement(node)->setSelectionRange(qMin(a.start, (a.start + a.length)), qMax(a.start, (a.start + a.length)));
681             }
682
683             if (!ev->preeditString().isEmpty())
684                 editor->setComposition(ev->preeditString(), underlines, qMin(a.start, (a.start + a.length)), qMax(a.start, (a.start + a.length)));
685             else {
686                 // If we are in the middle of a composition, an empty pre-edit string and a selection of zero
687                 // cancels the current composition
688                 if (editor->hasComposition() && !(a.start + a.length))
689                     editor->setComposition(QString(), underlines, 0, 0);
690             }
691             break;
692         }
693         default:
694             break;
695         }
696     }
697
698     if (node && ev->replacementLength() > 0) {
699         int cursorPos = frame->selection()->extent().offsetInContainerNode();
700         int start = cursorPos + ev->replacementStart();
701         if (isHTMLTextFormControlElement(node))
702             toHTMLTextFormControlElement(node)->setSelectionRange(start, start + ev->replacementLength());
703         // Commit regardless of whether commitString is empty, to get rid of selection.
704         editor->confirmComposition(ev->commitString());
705     } else if (!ev->commitString().isEmpty()) {
706         if (editor->hasComposition())
707             editor->confirmComposition(ev->commitString());
708         else
709             editor->insertText(ev->commitString(), 0);
710     } else if (!hasSelection && !ev->preeditString().isEmpty())
711         editor->setComposition(ev->preeditString(), underlines, 0, 0);
712     else if (ev->preeditString().isEmpty() && editor->hasComposition())
713         editor->confirmComposition(String());
714
715     ev->accept();
716 }
717
718 QVariant QWebPageAdapter::inputMethodQuery(Qt::InputMethodQuery property) const
719 {
720     Frame* frame = page->focusController()->focusedFrame();
721     if (!frame)
722         return QVariant();
723
724     WebCore::Editor* editor = frame->editor();
725
726     RenderObject* renderer = 0;
727     RenderTextControl* renderTextControl = 0;
728
729     if (frame->selection()->rootEditableElement())
730         renderer = frame->selection()->rootEditableElement()->deprecatedShadowAncestorNode()->renderer();
731
732     if (renderer && renderer->isTextControl())
733         renderTextControl = toRenderTextControl(renderer);
734
735     switch (property) {
736     case Qt::ImMicroFocus: {
737         WebCore::FrameView* view = frame->view();
738         if (view && view->needsLayout()) {
739             // We can't access absoluteCaretBounds() while the view needs to layout.
740             return QVariant();
741         }
742         return QVariant(view->contentsToWindow(frame->selection()->absoluteCaretBounds()));
743     }
744     case Qt::ImFont: {
745         if (renderTextControl) {
746             RenderStyle* renderStyle = renderTextControl->style();
747             return QVariant(QFont(renderStyle->font().syntheticFont()));
748         }
749         return QVariant(QFont());
750     }
751     case Qt::ImCursorPosition: {
752         if (editor->hasComposition())
753             return QVariant(frame->selection()->end().offsetInContainerNode());
754         return QVariant(frame->selection()->extent().offsetInContainerNode());
755     }
756     case Qt::ImSurroundingText: {
757         if (renderTextControl && renderTextControl->textFormControlElement()) {
758             QString text = renderTextControl->textFormControlElement()->value();
759             RefPtr<Range> range = editor->compositionRange();
760             if (range)
761                 text.remove(range->startPosition().offsetInContainerNode(), TextIterator::rangeLength(range.get()));
762             return QVariant(text);
763         }
764         return QVariant();
765     }
766     case Qt::ImCurrentSelection: {
767         if (!editor->hasComposition() && renderTextControl && renderTextControl->textFormControlElement()) {
768             int start = frame->selection()->start().offsetInContainerNode();
769             int end = frame->selection()->end().offsetInContainerNode();
770             if (end > start)
771                 return QVariant(QString(renderTextControl->textFormControlElement()->value()).mid(start, end - start));
772         }
773         return QVariant();
774
775     }
776     case Qt::ImAnchorPosition: {
777         if (editor->hasComposition())
778             return QVariant(frame->selection()->start().offsetInContainerNode());
779         return QVariant(frame->selection()->base().offsetInContainerNode());
780     }
781     case Qt::ImMaximumTextLength: {
782         if (frame->selection()->isContentEditable()) {
783             if (frame->document() && frame->document()->focusedNode()) {
784                 if (frame->document()->focusedNode()->hasTagName(HTMLNames::inputTag)) {
785                     HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(frame->document()->focusedNode());
786                     return QVariant(inputElement->maxLength());
787                 }
788             }
789             return QVariant(HTMLInputElement::maximumLength);
790         }
791         return QVariant(0);
792     }
793     default:
794         return QVariant();
795     }
796 }
797
798 typedef struct {
799     const char* name;
800     double deferredRepaintDelay;
801     double initialDeferredRepaintDelayDuringLoading;
802     double maxDeferredRepaintDelayDuringLoading;
803     double deferredRepaintDelayIncrementDuringLoading;
804 } QRepaintThrottlingPreset;
805
806 void QWebPageAdapter::dynamicPropertyChangeEvent(QObject* obj, QDynamicPropertyChangeEvent* event)
807 {
808     if (event->propertyName() == "_q_viewMode") {
809         page->setViewMode(Page::stringToViewMode(obj->property("_q_viewMode").toString()));
810     } else if (event->propertyName() == "_q_HTMLTokenizerChunkSize") {
811         int chunkSize = obj->property("_q_HTMLTokenizerChunkSize").toInt();
812         page->setCustomHTMLTokenizerChunkSize(chunkSize);
813     } else if (event->propertyName() == "_q_HTMLTokenizerTimeDelay") {
814         double timeDelay = obj->property("_q_HTMLTokenizerTimeDelay").toDouble();
815         page->setCustomHTMLTokenizerTimeDelay(timeDelay);
816     } else if (event->propertyName() == "_q_RepaintThrottlingDeferredRepaintDelay") {
817         double p = obj->property("_q_RepaintThrottlingDeferredRepaintDelay").toDouble();
818         FrameView::setRepaintThrottlingDeferredRepaintDelay(p);
819     } else if (event->propertyName() == "_q_RepaintThrottlingnInitialDeferredRepaintDelayDuringLoading") {
820         double p = obj->property("_q_RepaintThrottlingnInitialDeferredRepaintDelayDuringLoading").toDouble();
821         FrameView::setRepaintThrottlingnInitialDeferredRepaintDelayDuringLoading(p);
822     } else if (event->propertyName() == "_q_RepaintThrottlingMaxDeferredRepaintDelayDuringLoading") {
823         double p = obj->property("_q_RepaintThrottlingMaxDeferredRepaintDelayDuringLoading").toDouble();
824         FrameView::setRepaintThrottlingMaxDeferredRepaintDelayDuringLoading(p);
825     } else if (event->propertyName() == "_q_RepaintThrottlingDeferredRepaintDelayIncrementDuringLoading") {
826         double p = obj->property("_q_RepaintThrottlingDeferredRepaintDelayIncrementDuringLoading").toDouble();
827         FrameView::setRepaintThrottlingDeferredRepaintDelayIncrementDuringLoading(p);
828     } else if (event->propertyName() == "_q_RepaintThrottlingPreset") {
829         static const QRepaintThrottlingPreset presets[] = {
830             {   "NoThrottling",     0,      0,      0,      0 },
831             {   "Legacy",       0.025,      0,    2.5,    0.5 },
832             {   "Minimal",       0.01,      0,      1,    0.2 },
833             {   "Medium",       0.025,      1,      5,    0.5 },
834             {   "Heavy",          0.1,      2,     10,      1 }
835         };
836
837         QString p = obj->property("_q_RepaintThrottlingPreset").toString();
838         for (size_t i = 0; i < sizeof(presets) / sizeof(presets[0]); i++) {
839             if (p == QLatin1String(presets[i].name)) {
840                 FrameView::setRepaintThrottlingDeferredRepaintDelay(presets[i].deferredRepaintDelay);
841                 FrameView::setRepaintThrottlingnInitialDeferredRepaintDelayDuringLoading(presets[i].initialDeferredRepaintDelayDuringLoading);
842                 FrameView::setRepaintThrottlingMaxDeferredRepaintDelayDuringLoading(presets[i].maxDeferredRepaintDelayDuringLoading);
843                 FrameView::setRepaintThrottlingDeferredRepaintDelayIncrementDuringLoading(presets[i].deferredRepaintDelayIncrementDuringLoading);
844                 break;
845             }
846         }
847     } else if (event->propertyName() == "_q_webInspectorServerPort") {
848 #if ENABLE(INSPECTOR)
849         QVariant port = obj->property("_q_webInspectorServerPort");
850         if (port.isValid()) {
851             InspectorServerQt* inspectorServer = InspectorServerQt::server();
852             inspectorServer->listen(port.toInt());
853         }
854 #endif
855     } else if (event->propertyName() == "_q_deadDecodedDataDeletionInterval") {
856         double interval = obj->property("_q_deadDecodedDataDeletionInterval").toDouble();
857         memoryCache()->setDeadDecodedDataDeletionInterval(interval);
858     }
859 }
860
861 #endif // QT_NO_DRAGANDDROP
862
863 #define MAP_ACTION_FROM_VALUE(Name, Value) \
864     case Value: return QWebPageAdapter::Name
865
866 static QWebPageAdapter::MenuAction adapterActionForContextMenuAction(WebCore::ContextMenuAction action)
867 {
868     switch (action) {
869         FOR_EACH_MAPPED_MENU_ACTION(MAP_ACTION_FROM_VALUE, SEMICOLON_SEPARATOR);
870 #if ENABLE(INSPECTOR)
871     case WebCore::ContextMenuItemTagInspectElement:
872         return QWebPageAdapter::InspectElement;
873 #endif
874     default:
875         break;
876     }
877     return QWebPageAdapter::NoAction;
878 }
879
880 QList<MenuItem> descriptionForPlatformMenu(const QList<ContextMenuItem>* items, Page* page)
881 {
882     QList<MenuItem> itemDescriptions;
883     if (!items)
884         return itemDescriptions;
885     for (int i = 0; i < items->count(); ++i) {
886         const ContextMenuItem &item = items->at(i);
887         MenuItem description;
888         switch (item.type()) {
889         case WebCore::CheckableActionType: /* fall through */
890         case WebCore::ActionType: {
891             QWebPageAdapter::MenuAction action = adapterActionForContextMenuAction(item.action());
892             if (action > QWebPageAdapter::NoAction) {
893                 description.type = MenuItem::Action;
894                 description.action = action;
895                 ContextMenuItem it(item);
896                 page->contextMenuController()->checkOrEnableIfNeeded(it);
897                 PlatformMenuItemDescription desc = it.releasePlatformDescription();
898                 if (desc.enabled)
899                     description.traits |= MenuItem::Enabled;
900                 if (item.type() == WebCore::CheckableActionType) {
901                     description.traits |= MenuItem::Checkable;
902                     if (desc.checked)
903                         description.traits |= MenuItem::Checked;
904                 }
905             }
906             break;
907         }
908         case WebCore::SeparatorType:
909             description.type = MenuItem::Separator;
910             break;
911         case WebCore::SubmenuType: {
912             description.type = MenuItem::SubMenu;
913             description.subMenu = descriptionForPlatformMenu(item.platformSubMenu(), page);
914             description.subMenuTitle = item.title();
915             // Don't append empty submenu descriptions.
916             if (description.subMenu.isEmpty())
917                 continue;
918         }
919         }
920         if (description.type > MenuItem::NoType)
921             itemDescriptions.append(description);
922     }
923     return itemDescriptions;
924 }
925
926 QWebHitTestResultPrivate* QWebPageAdapter::updatePositionDependentMenuActions(const QPoint& pos, QBitArray* visitedWebActions)
927 {
928     ASSERT(visitedWebActions);
929     WebCore::Frame* focusedFrame = page->focusController()->focusedOrMainFrame();
930     HitTestResult result = focusedFrame->eventHandler()->hitTestResultAtPoint(focusedFrame->view()->windowToContents(pos), /*allowShadowContent*/ false);
931     page->contextMenuController()->setHitTestResult(result);
932
933 #if ENABLE(INSPECTOR)
934     if (page->inspectorController()->enabled())
935         page->contextMenuController()->addInspectElementItem();
936 #endif
937
938     WebCore::ContextMenu* webcoreMenu = page->contextMenuController()->contextMenu();
939     QList<MenuItem> itemDescriptions;
940     if (client && webcoreMenu)
941         itemDescriptions = descriptionForPlatformMenu(webcoreMenu->platformDescription(), page);
942     createAndSetCurrentContextMenu(itemDescriptions, visitedWebActions);
943     if (result.scrollbar())
944         return 0;
945     return new QWebHitTestResultPrivate(result);
946 }
947
948 static void extractContentTypeFromHash(const HashSet<String>& types, QStringList* list)
949 {
950     if (!list)
951         return;
952
953     HashSet<String>::const_iterator endIt = types.end();
954     for (HashSet<String>::const_iterator it = types.begin(); it != endIt; ++it)
955         *list << *it;
956 }
957
958 static void extractContentTypeFromPluginVector(const Vector<PluginPackage*>& plugins, QStringList* list)
959 {
960     if (!list)
961         return;
962
963     for (unsigned i = 0; i < plugins.size(); ++i) {
964         MIMEToDescriptionsMap::const_iterator it = plugins[i]->mimeToDescriptions().begin();
965         MIMEToDescriptionsMap::const_iterator end = plugins[i]->mimeToDescriptions().end();
966         for (; it != end; ++it)
967             *list << it->key;
968     }
969 }
970
971 QStringList QWebPageAdapter::supportedContentTypes() const
972 {
973     QStringList mimeTypes;
974
975     extractContentTypeFromHash(MIMETypeRegistry::getSupportedImageMIMETypes(), &mimeTypes);
976     extractContentTypeFromHash(MIMETypeRegistry::getSupportedNonImageMIMETypes(), &mimeTypes);
977     if (page->settings() && page->settings()->arePluginsEnabled())
978         extractContentTypeFromPluginVector(PluginDatabase::installedPlugins()->plugins(), &mimeTypes);
979
980     return mimeTypes;
981 }
982
983 void QWebPageAdapter::_q_cleanupLeakMessages()
984 {
985 #ifndef NDEBUG
986     // Need this to make leak messages accurate.
987     memoryCache()->setCapacities(0, 0, 0);
988 #endif
989 }
990
991 void QWebPageAdapter::_q_onLoadProgressChanged(int)
992 {
993     m_totalBytes = page->progress()->totalPageAndResourceBytesToLoad();
994     m_bytesReceived = page->progress()->totalBytesReceived();
995 }
996
997 bool QWebPageAdapter::supportsContentType(const QString& mimeType) const
998 {
999     const String type = mimeType.toLower();
1000     if (MIMETypeRegistry::isSupportedImageMIMEType(type))
1001         return true;
1002
1003     if (MIMETypeRegistry::isSupportedNonImageMIMEType(type))
1004         return true;
1005
1006     if (page->settings() && page->settings()->arePluginsEnabled()
1007         && PluginDatabase::installedPlugins()->isMIMETypeRegistered(type))
1008         return true;
1009
1010     return false;
1011 }
1012
1013 void QWebPageAdapter::didShowInspector()
1014 {
1015 #if ENABLE(INSPECTOR)
1016     page->inspectorController()->show();
1017 #endif
1018 }
1019
1020 void QWebPageAdapter::didCloseInspector()
1021 {
1022 #if ENABLE(INSPECTOR)
1023     page->inspectorController()->close();
1024 #endif
1025 }
1026
1027 void QWebPageAdapter::updateActionInternal(QWebPageAdapter::MenuAction action, const char* commandName, bool* enabled, bool* checked)
1028 {
1029     WebCore::FrameLoader* loader = mainFrameAdapter()->frame->loader();
1030     WebCore::Editor* editor = page->focusController()->focusedOrMainFrame()->editor();
1031
1032     switch (action) {
1033     case QWebPageAdapter::Back:
1034         *enabled = page->canGoBackOrForward(-1);
1035         break;
1036     case QWebPageAdapter::Forward:
1037         *enabled = page->canGoBackOrForward(1);
1038         break;
1039     case QWebPageAdapter::Stop:
1040         *enabled = loader->isLoading();
1041         break;
1042     case QWebPageAdapter::Reload:
1043         *enabled = !loader->isLoading();
1044         break;
1045     case QWebPageAdapter::SetTextDirectionDefault:
1046     case QWebPageAdapter::SetTextDirectionLeftToRight:
1047     case QWebPageAdapter::SetTextDirectionRightToLeft:
1048         *enabled = editor->canEdit();
1049         *checked = false;
1050         break;
1051     default: {
1052
1053         // if it's an editor command, let its logic determine state
1054         if (commandName) {
1055             Editor::Command command = editor->command(commandName);
1056             *enabled = command.isEnabled();
1057             if (*enabled)
1058                 *checked = command.state() != FalseTriState;
1059             else
1060                 *checked = false;
1061         }
1062         break;
1063     }
1064     }
1065 }
1066
1067 void QWebPageAdapter::triggerAction(QWebPageAdapter::MenuAction action, QWebHitTestResultPrivate* hitTestResult, const char* commandName, bool endToEndReload)
1068 {
1069     Frame* frame = page->focusController()->focusedOrMainFrame();
1070     if (!frame)
1071         return;
1072     Editor* editor = frame->editor();
1073
1074     // Convenience
1075     QWebHitTestResultPrivate hitTest;
1076     if (!hitTestResult)
1077         hitTestResult = &hitTest;
1078
1079     switch (action) {
1080     case OpenLink:
1081         if (Frame* targetFrame = hitTestResult->webCoreFrame) {
1082             targetFrame->loader()->loadFrameRequest(frameLoadRequest(hitTestResult->linkUrl, targetFrame), /*lockHistory*/ false, /*lockBackForwardList*/ false, /*event*/ 0, /*FormState*/ 0, MaybeSendReferrer);
1083             break;
1084         }
1085         // fall through
1086     case OpenLinkInNewWindow:
1087         openNewWindow(hitTestResult->linkUrl, frame);
1088         break;
1089     case OpenLinkInThisWindow:
1090         frame->loader()->loadFrameRequest(frameLoadRequest(hitTestResult->linkUrl, frame), /*lockHistory*/ false, /*lockBackForwardList*/ false, /*event*/ 0, /*FormState*/ 0, MaybeSendReferrer);
1091         break;
1092     case OpenFrameInNewWindow: {
1093         KURL url = frame->loader()->documentLoader()->unreachableURL();
1094         if (url.isEmpty())
1095             url = frame->loader()->documentLoader()->url();
1096         openNewWindow(url, frame);
1097         break;
1098         }
1099     case CopyLinkToClipboard: {
1100 #if defined(Q_WS_X11)
1101         bool oldSelectionMode = Pasteboard::generalPasteboard()->isSelectionMode();
1102         Pasteboard::generalPasteboard()->setSelectionMode(true);
1103         editor->copyURL(hitTestResult->linkUrl, hitTestResult->linkText);
1104         Pasteboard::generalPasteboard()->setSelectionMode(oldSelectionMode);
1105 #endif
1106         editor->copyURL(hitTestResult->linkUrl, hitTestResult->linkText);
1107         break;
1108     }
1109     case OpenImageInNewWindow:
1110         openNewWindow(hitTestResult->imageUrl, frame);
1111         break;
1112     case DownloadImageToDisk:
1113         frame->loader()->client()->startDownload(WebCore::ResourceRequest(hitTestResult->imageUrl, frame->loader()->outgoingReferrer()));
1114         break;
1115     case DownloadLinkToDisk:
1116         frame->loader()->client()->startDownload(WebCore::ResourceRequest(hitTestResult->linkUrl, frame->loader()->outgoingReferrer()));
1117         break;
1118     case Back:
1119         page->goBack();
1120         break;
1121     case Forward:
1122         page->goForward();
1123         break;
1124     case Stop:
1125         mainFrameAdapter()->frame->loader()->stopForUserCancel();
1126         updateNavigationActions();
1127         break;
1128     case Reload:
1129         mainFrameAdapter()->frame->loader()->reload(endToEndReload);
1130         break;
1131
1132     case SetTextDirectionDefault:
1133         editor->setBaseWritingDirection(NaturalWritingDirection);
1134         break;
1135     case SetTextDirectionLeftToRight:
1136         editor->setBaseWritingDirection(LeftToRightWritingDirection);
1137         break;
1138     case SetTextDirectionRightToLeft:
1139         editor->setBaseWritingDirection(RightToLeftWritingDirection);
1140         break;
1141 #if ENABLE(INSPECTOR)
1142     case InspectElement: {
1143         ASSERT(hitTestResult != &hitTest);
1144         page->inspectorController()->inspect(hitTestResult->innerNonSharedNode);
1145         break;
1146     }
1147 #endif
1148     default:
1149         if (commandName)
1150             editor->command(commandName).execute();
1151         break;
1152     }
1153 }
1154
1155
1156 QString QWebPageAdapter::contextMenuItemTagForAction(QWebPageAdapter::MenuAction action, bool* checkable) const
1157 {
1158     ASSERT(checkable);
1159     switch (action) {
1160     case OpenLink:
1161         return contextMenuItemTagOpenLink();
1162     case OpenLinkInNewWindow:
1163         return contextMenuItemTagOpenLinkInNewWindow();
1164     case OpenFrameInNewWindow:
1165         return contextMenuItemTagOpenFrameInNewWindow();
1166     case OpenLinkInThisWindow:
1167         return contextMenuItemTagOpenLinkInThisWindow();
1168
1169     case DownloadLinkToDisk:
1170         return contextMenuItemTagDownloadLinkToDisk();
1171     case CopyLinkToClipboard:
1172         return contextMenuItemTagCopyLinkToClipboard();
1173
1174     case OpenImageInNewWindow:
1175         return contextMenuItemTagOpenImageInNewWindow();
1176     case DownloadImageToDisk:
1177         return contextMenuItemTagDownloadImageToDisk();
1178     case CopyImageToClipboard:
1179         return contextMenuItemTagCopyImageToClipboard();
1180     case CopyImageUrlToClipboard:
1181         return contextMenuItemTagCopyImageUrlToClipboard();
1182
1183     case Cut:
1184         return contextMenuItemTagCut();
1185     case Copy:
1186         return contextMenuItemTagCopy();
1187     case Paste:
1188         return contextMenuItemTagPaste();
1189     case SelectAll:
1190         return contextMenuItemTagSelectAll();
1191
1192     case Back:
1193         return contextMenuItemTagGoBack();
1194     case Forward:
1195         return contextMenuItemTagGoForward();
1196     case Reload:
1197         return contextMenuItemTagReload();
1198     case Stop:
1199         return contextMenuItemTagStop();
1200
1201     case SetTextDirectionDefault:
1202         return contextMenuItemTagDefaultDirection();
1203     case SetTextDirectionLeftToRight:
1204         *checkable = true;
1205         return contextMenuItemTagLeftToRight();
1206     case SetTextDirectionRightToLeft:
1207         *checkable = true;
1208         return contextMenuItemTagRightToLeft();
1209
1210     case ToggleBold:
1211         *checkable = true;
1212         return contextMenuItemTagBold();
1213     case ToggleItalic:
1214         *checkable = true;
1215         return contextMenuItemTagItalic();
1216     case ToggleUnderline:
1217         *checkable = true;
1218         return contextMenuItemTagUnderline();
1219
1220 #if ENABLE(INSPECTOR)
1221     case InspectElement:
1222         return contextMenuItemTagInspectElement();
1223 #endif
1224     default:
1225         ASSERT_NOT_REACHED();
1226         return QString();
1227     }
1228 }
1229
1230 #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
1231 void QWebPageAdapter::allowNotificationsForFrame(QWebFrameAdapter* frame)
1232 {
1233     NotificationPresenterClientQt::notificationPresenter()->allowNotificationForFrame(frame->frame);
1234 }
1235
1236 void QWebPageAdapter::addNotificationPresenterClient()
1237 {
1238     NotificationPresenterClientQt::notificationPresenter()->addClient();
1239 }
1240
1241 #ifndef QT_NO_SYSTEMTRAYICON
1242 bool QWebPageAdapter::hasSystemTrayIcon() const
1243 {
1244     return NotificationPresenterClientQt::notificationPresenter()->hasSystemTrayIcon();
1245 }
1246
1247 void QWebPageAdapter::setSystemTrayIcon(QObject *icon)
1248 {
1249     NotificationPresenterClientQt::notificationPresenter()->setSystemTrayIcon(icon);
1250 }
1251 #endif // QT_NO_SYSTEMTRAYICON
1252 #endif // ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
1253
1254 #if ENABLE(GEOLOCATION)
1255 void QWebPageAdapter::setGeolocationEnabledForFrame(QWebFrameAdapter* frame, bool on)
1256 {
1257     GeolocationPermissionClientQt::geolocationPermissionClient()->setPermission(frame, on);
1258 }
1259 #endif
1260
1261
1262 QString QWebPageAdapter::defaultUserAgentString()
1263 {
1264     return UserAgentQt::standardUserAgent("", WEBKIT_MAJOR_VERSION, WEBKIT_MINOR_VERSION);
1265 }
1266
1267 bool QWebPageAdapter::treatSchemeAsLocal(const QString& scheme)
1268 {
1269     return WebCore::SchemeRegistry::shouldTreatURLSchemeAsLocal(scheme);
1270 }
1271
1272 QObject* QWebPageAdapter::currentFrame() const
1273 {
1274     Frame* frame = page->focusController()->focusedOrMainFrame();
1275     return frame->loader()->networkingContext()->originatingObject();
1276 }
1277
1278 bool QWebPageAdapter::hasFocusedNode() const
1279 {
1280     bool hasFocus = false;
1281     Frame* frame = page->focusController()->focusedFrame();
1282     if (frame) {
1283         Document* document = frame->document();
1284         hasFocus = document && document->focusedNode();
1285     }
1286     return hasFocus;
1287 }
1288
1289 QWebPageAdapter::ViewportAttributes QWebPageAdapter::viewportAttributesForSize(const QSize &availableSize, const QSize &deviceSize) const
1290 {
1291     static const int desktopWidth = 980;
1292
1293     float devicePixelRatio = qt_defaultDpi() / WebCore::ViewportArguments::deprecatedTargetDPI;
1294
1295     WebCore::ViewportAttributes conf = WebCore::computeViewportAttributes(viewportArguments(), desktopWidth, deviceSize.width(), deviceSize.height(), devicePixelRatio, availableSize);
1296     WebCore::restrictMinimumScaleFactorToViewportSize(conf, availableSize, devicePixelRatio);
1297     WebCore::restrictScaleFactorToInitialScaleIfNotUserScalable(conf);
1298
1299     page->setDeviceScaleFactor(devicePixelRatio);
1300     QWebPageAdapter::ViewportAttributes result;
1301
1302     result.size = QSizeF(conf.layoutSize.width(), conf.layoutSize.height());
1303     result.initialScaleFactor = conf.initialScale;
1304     result.minimumScaleFactor = conf.minimumScale;
1305     result.maximumScaleFactor = conf.maximumScale;
1306     result.devicePixelRatio = devicePixelRatio;
1307     result.isUserScalable = static_cast<bool>(conf.userScalable);
1308
1309     return result;
1310 }
1311
1312
1313 bool QWebPageAdapter::handleKeyEvent(QKeyEvent *ev)
1314 {
1315     Frame* frame = page->focusController()->focusedOrMainFrame();
1316     return frame->eventHandler()->keyEvent(ev);
1317 }
1318
1319 bool QWebPageAdapter::handleScrolling(QKeyEvent *ev)
1320 {
1321     Frame* frame = page->focusController()->focusedOrMainFrame();
1322     WebCore::ScrollDirection direction;
1323     WebCore::ScrollGranularity granularity;
1324
1325 #ifndef QT_NO_SHORTCUT
1326     if (ev == QKeySequence::MoveToNextPage || (ev->key() == Qt::Key_Space && !(ev->modifiers() & Qt::ShiftModifier))) {
1327         granularity = WebCore::ScrollByPage;
1328         direction = WebCore::ScrollDown;
1329     } else if (ev == QKeySequence::MoveToPreviousPage || ((ev->key() == Qt::Key_Space) && (ev->modifiers() & Qt::ShiftModifier))) {
1330         granularity = WebCore::ScrollByPage;
1331         direction = WebCore::ScrollUp;
1332     } else
1333 #endif // QT_NO_SHORTCUT
1334     if ((ev->key() == Qt::Key_Up && ev->modifiers() & Qt::ControlModifier) || ev->key() == Qt::Key_Home) {
1335         granularity = WebCore::ScrollByDocument;
1336         direction = WebCore::ScrollUp;
1337     } else if ((ev->key() == Qt::Key_Down && ev->modifiers() & Qt::ControlModifier) || ev->key() == Qt::Key_End) {
1338         granularity = WebCore::ScrollByDocument;
1339         direction = WebCore::ScrollDown;
1340     } else {
1341         switch (ev->key()) {
1342         case Qt::Key_Up:
1343             granularity = WebCore::ScrollByLine;
1344             direction = WebCore::ScrollUp;
1345             break;
1346         case Qt::Key_Down:
1347             granularity = WebCore::ScrollByLine;
1348             direction = WebCore::ScrollDown;
1349             break;
1350         case Qt::Key_Left:
1351             granularity = WebCore::ScrollByLine;
1352             direction = WebCore::ScrollLeft;
1353             break;
1354         case Qt::Key_Right:
1355             granularity = WebCore::ScrollByLine;
1356             direction = WebCore::ScrollRight;
1357             break;
1358         default:
1359             return false;
1360         }
1361     }
1362
1363     return frame->eventHandler()->scrollRecursively(direction, granularity);
1364 }
1365
1366 void QWebPageAdapter::focusInEvent(QFocusEvent *)
1367 {
1368     FocusController* focusController = page->focusController();
1369     focusController->setActive(true);
1370     focusController->setFocused(true);
1371     if (!focusController->focusedFrame())
1372         focusController->setFocusedFrame(mainFrameAdapter()->frame);
1373 }
1374
1375 void QWebPageAdapter::focusOutEvent(QFocusEvent *)
1376 {
1377     // only set the focused frame inactive so that we stop painting the caret
1378     // and the focus frame. But don't tell the focus controller so that upon
1379     // focusInEvent() we can re-activate the frame.
1380     FocusController* focusController = page->focusController();
1381     // Call setFocused first so that window.onblur doesn't get called twice
1382     focusController->setFocused(false);
1383     focusController->setActive(false);
1384 }
1385
1386 bool QWebPageAdapter::handleShortcutOverrideEvent(QKeyEvent* event)
1387 {
1388     WebCore::Frame* frame = page->focusController()->focusedOrMainFrame();
1389     WebCore::Editor* editor = frame->editor();
1390     if (!editor->canEdit())
1391         return false;
1392     if (event->modifiers() == Qt::NoModifier
1393         || event->modifiers() == Qt::ShiftModifier
1394         || event->modifiers() == Qt::KeypadModifier) {
1395         if (event->key() < Qt::Key_Escape)
1396             event->accept();
1397         else {
1398             switch (event->key()) {
1399             case Qt::Key_Return:
1400             case Qt::Key_Enter:
1401             case Qt::Key_Delete:
1402             case Qt::Key_Home:
1403             case Qt::Key_End:
1404             case Qt::Key_Backspace:
1405             case Qt::Key_Left:
1406             case Qt::Key_Right:
1407             case Qt::Key_Up:
1408             case Qt::Key_Down:
1409             case Qt::Key_Tab:
1410                 event->accept();
1411             default:
1412                 break;
1413             }
1414         }
1415     }
1416     return true;
1417 }
1418
1419 bool QWebPageAdapter::touchEvent(QTouchEvent* event)
1420 {
1421 #if ENABLE(TOUCH_EVENTS)
1422     Frame* frame = mainFrameAdapter()->frame;
1423     if (!frame->view())
1424         return false;
1425
1426     // Always accept the QTouchEvent so that we'll receive also TouchUpdate and TouchEnd events
1427     event->setAccepted(true);
1428
1429     // Return whether the default action was cancelled in the JS event handler
1430     return frame->eventHandler()->handleTouchEvent(convertTouchEvent(event));
1431 #else
1432     event->ignore();
1433     return false;
1434 #endif
1435 }
1436
1437 bool QWebPageAdapter::swallowContextMenuEvent(QContextMenuEvent *event, QWebFrameAdapter *webFrame)
1438 {
1439     // Check the first and last enum values match at least, since we cast.
1440     ASSERT(int(QWebPageAdapter::ScrollUp) == int(WebCore::ScrollUp));
1441     ASSERT(int(QWebPageAdapter::ScrollRight) == int(WebCore::ScrollRight));
1442     ASSERT(int(QWebPageAdapter::ScrollByLine) == int(WebCore::ScrollByLine));
1443     ASSERT(int(QWebPageAdapter::ScrollByDocument) == int(WebCore::ScrollByDocument));
1444
1445     page->contextMenuController()->clearContextMenu();
1446
1447     if (webFrame) {
1448         Frame* frame = webFrame->frame;
1449         if (Scrollbar* scrollBar = frame->view()->scrollbarAtPoint(convertMouseEvent(event, 1).position())) {
1450             bool horizontal = (scrollBar->orientation() == HorizontalScrollbar);
1451             QWebPageAdapter::ScrollDirection direction = QWebPageAdapter::InvalidScrollDirection;
1452             QWebPageAdapter::ScrollGranularity granularity = QWebPageAdapter::InvalidScrollGranularity;
1453             bool scroll = handleScrollbarContextMenuEvent(event, horizontal, &direction, &granularity);
1454             if (!scroll)
1455                 return true;
1456             if (direction == QWebPageAdapter::InvalidScrollDirection || granularity == QWebPageAdapter::InvalidScrollGranularity) {
1457                 ScrollbarTheme* theme = scrollBar->theme();
1458                 // Set the pressed position to the middle of the thumb so that when we
1459                 // do move, the delta will be from the current pixel position of the
1460                 // thumb to the new position
1461                 int position = theme->trackPosition(scrollBar) + theme->thumbPosition(scrollBar) + theme->thumbLength(scrollBar) / 2;
1462                 scrollBar->setPressedPos(position);
1463                 const QPoint pos = scrollBar->convertFromContainingWindow(event->pos());
1464                 scrollBar->moveThumb(horizontal ? pos.x() : pos.y());
1465             } else
1466                 scrollBar->scrollableArea()->scroll(WebCore::ScrollDirection(direction), WebCore::ScrollGranularity(granularity));
1467             return true;
1468         }
1469     }
1470
1471     WebCore::Frame* focusedFrame = page->focusController()->focusedOrMainFrame();
1472     focusedFrame->eventHandler()->sendContextMenuEvent(convertMouseEvent(event, 1));
1473     ContextMenu* menu = page->contextMenuController()->contextMenu();
1474     // If the website defines its own handler then sendContextMenuEvent takes care of
1475     // calling/showing it and the context menu pointer will be zero. This is the case
1476     // on maps.google.com for example.
1477
1478     return !menu;
1479 }