2010-09-05 Sheriff Bot <webkit.review.bot@gmail.com>
[WebKit-https.git] / WebKit / qt / WebCoreSupport / ChromeClientQt.cpp
1 /*
2  * Copyright (C) 2006 Zack Rusin <zack@kde.org>
3  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
4  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
5  *
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "config.h"
31 #include "ChromeClientQt.h"
32
33 #include "DatabaseTracker.h"
34 #include "FileChooser.h"
35 #include "Frame.h"
36 #include "FrameLoadRequest.h"
37 #include "FrameLoader.h"
38 #include "FrameLoaderClientQt.h"
39 #include "FrameView.h"
40 #include "Geolocation.h"
41 #if USE(ACCELERATED_COMPOSITING)
42 #include "GraphicsLayerQt.h"
43 #endif
44 #include "GeolocationPermissionClientQt.h"
45 #include "HitTestResult.h"
46 #include "Icon.h"
47 #include "NotImplemented.h"
48 #include "NotificationPresenterClientQt.h"
49 #include "PageClientQt.h"
50 #include "PopupMenuQt.h"
51 #if defined(Q_WS_MAEMO_5)
52 #include "QtMaemoWebPopup.h"
53 #else
54 #include "QtFallbackWebPopup.h"
55 #endif
56 #include "QWebPageClient.h"
57 #include "ScrollbarTheme.h"
58 #include "SearchPopupMenuQt.h"
59 #include "SecurityOrigin.h"
60 #include "ViewportArguments.h"
61 #include "WindowFeatures.h"
62
63 #include "qgraphicswebview.h"
64 #include "qwebframe_p.h"
65 #include "qwebpage.h"
66 #include "qwebpage_p.h"
67 #include "qwebsecurityorigin.h"
68 #include "qwebsecurityorigin_p.h"
69 #include "qwebview.h"
70 #include <qdebug.h>
71 #include <qeventloop.h>
72 #include <qtextdocument.h>
73 #include <qtooltip.h>
74
75 namespace WebCore {
76
77 ChromeClientQt::ChromeClientQt(QWebPage* webPage)
78     : m_webPage(webPage)
79     , m_eventLoop(0)
80 {
81     toolBarsVisible = statusBarVisible = menuBarVisible = true;
82 }
83
84 ChromeClientQt::~ChromeClientQt()
85 {
86     if (m_eventLoop)
87         m_eventLoop->exit();
88 }
89
90 void ChromeClientQt::setWindowRect(const FloatRect& rect)
91 {
92     if (!m_webPage)
93         return;
94     emit m_webPage->geometryChangeRequested(QRect(qRound(rect.x()), qRound(rect.y()),
95                             qRound(rect.width()), qRound(rect.height())));
96 }
97
98 /*!
99     windowRect represents the rect of the Window, including all interface elements
100     like toolbars/scrollbars etc. It is used by the viewport meta tag as well as
101     by the DOM Window object: outerHeight(), outerWidth(), screenX(), screenY().
102 */
103 FloatRect ChromeClientQt::windowRect()
104 {
105     if (!platformPageClient())
106         return FloatRect();
107     return platformPageClient()->windowRect();
108 }
109
110 FloatRect ChromeClientQt::pageRect()
111 {
112     if (!m_webPage)
113         return FloatRect();
114     return FloatRect(QRectF(QPointF(0, 0), m_webPage->viewportSize()));
115 }
116
117
118 float ChromeClientQt::scaleFactor()
119 {
120     notImplemented();
121     return 1;
122 }
123
124
125 void ChromeClientQt::focus()
126 {
127     if (!m_webPage)
128         return;
129     QWidget* view = m_webPage->view();
130     if (!view)
131         return;
132
133     view->setFocus();
134 }
135
136
137 void ChromeClientQt::unfocus()
138 {
139     if (!m_webPage)
140         return;
141     QWidget* view = m_webPage->view();
142     if (!view)
143         return;
144     view->clearFocus();
145 }
146
147 bool ChromeClientQt::canTakeFocus(FocusDirection)
148 {
149     // This is called when cycling through links/focusable objects and we
150     // reach the last focusable object. Then we want to claim that we can
151     // take the focus to avoid wrapping.
152     return true;
153 }
154
155 void ChromeClientQt::takeFocus(FocusDirection)
156 {
157     // don't do anything. This is only called when cycling to links/focusable objects,
158     // which in turn is called from focusNextPrevChild. We let focusNextPrevChild
159     // call QWidget::focusNextPrevChild accordingly, so there is no need to do anything
160     // here.
161 }
162
163
164 void ChromeClientQt::focusedNodeChanged(WebCore::Node*)
165 {
166 }
167
168
169 Page* ChromeClientQt::createWindow(Frame*, const FrameLoadRequest& request, const WindowFeatures& features)
170 {
171     QWebPage* newPage = m_webPage->createWindow(features.dialog ? QWebPage::WebModalDialog : QWebPage::WebBrowserWindow);
172     if (!newPage)
173         return 0;
174
175     // A call to QWebPage::mainFrame() implicitly creates the main frame.
176     // Make sure it exists, as WebCore expects it when returning from this call.
177     QWebFrame* mainFrame = newPage->mainFrame();
178
179     if (!request.isEmpty())
180         mainFrame->load(request.resourceRequest().url());
181     return newPage->d->page;
182 }
183
184 void ChromeClientQt::show()
185 {
186     if (!m_webPage)
187         return;
188     QWidget* view = m_webPage->view();
189     if (!view)
190         return;
191     view->window()->show();
192 }
193
194
195 bool ChromeClientQt::canRunModal()
196 {
197     return true;
198 }
199
200
201 void ChromeClientQt::runModal()
202 {
203     m_eventLoop = new QEventLoop();
204     QEventLoop* eventLoop = m_eventLoop;
205     m_eventLoop->exec();
206     delete eventLoop;
207 }
208
209
210 void ChromeClientQt::setToolbarsVisible(bool visible)
211 {
212     toolBarsVisible = visible;
213     emit m_webPage->toolBarVisibilityChangeRequested(visible);
214 }
215
216
217 bool ChromeClientQt::toolbarsVisible()
218 {
219     return toolBarsVisible;
220 }
221
222
223 void ChromeClientQt::setStatusbarVisible(bool visible)
224 {
225     emit m_webPage->statusBarVisibilityChangeRequested(visible);
226     statusBarVisible = visible;
227 }
228
229
230 bool ChromeClientQt::statusbarVisible()
231 {
232     return statusBarVisible;
233 }
234
235
236 void ChromeClientQt::setScrollbarsVisible(bool)
237 {
238     notImplemented();
239 }
240
241
242 bool ChromeClientQt::scrollbarsVisible()
243 {
244     notImplemented();
245     return true;
246 }
247
248
249 void ChromeClientQt::setMenubarVisible(bool visible)
250 {
251     menuBarVisible = visible;
252     emit m_webPage->menuBarVisibilityChangeRequested(visible);
253 }
254
255 bool ChromeClientQt::menubarVisible()
256 {
257     return menuBarVisible;
258 }
259
260 void ChromeClientQt::setResizable(bool)
261 {
262     notImplemented();
263 }
264
265 void ChromeClientQt::addMessageToConsole(MessageSource, MessageType, MessageLevel, const String& message,
266                                          unsigned int lineNumber, const String& sourceID)
267 {
268     QString x = message;
269     QString y = sourceID;
270     m_webPage->javaScriptConsoleMessage(x, lineNumber, y);
271 }
272
273 void ChromeClientQt::chromeDestroyed()
274 {
275     delete this;
276 }
277
278 bool ChromeClientQt::canRunBeforeUnloadConfirmPanel()
279 {
280     return true;
281 }
282
283 bool ChromeClientQt::runBeforeUnloadConfirmPanel(const String& message, Frame* frame)
284 {
285     return runJavaScriptConfirm(frame, message);
286 }
287
288 void ChromeClientQt::closeWindowSoon()
289 {
290     m_webPage->mainFrame()->d->frame->loader()->stopAllLoaders();
291     emit m_webPage->windowCloseRequested();
292 }
293
294 void ChromeClientQt::runJavaScriptAlert(Frame* f, const String& msg)
295 {
296     QString x = msg;
297     FrameLoaderClientQt* fl = static_cast<FrameLoaderClientQt*>(f->loader()->client());
298     m_webPage->javaScriptAlert(fl->webFrame(), x);
299 }
300
301 bool ChromeClientQt::runJavaScriptConfirm(Frame* f, const String& msg)
302 {
303     QString x = msg;
304     FrameLoaderClientQt* fl = static_cast<FrameLoaderClientQt*>(f->loader()->client());
305     return m_webPage->javaScriptConfirm(fl->webFrame(), x);
306 }
307
308 bool ChromeClientQt::runJavaScriptPrompt(Frame* f, const String& message, const String& defaultValue, String& result)
309 {
310     QString x = result;
311     FrameLoaderClientQt* fl = static_cast<FrameLoaderClientQt*>(f->loader()->client());
312     bool rc = m_webPage->javaScriptPrompt(fl->webFrame(), (QString)message, (QString)defaultValue, &x);
313
314     // Fix up a quirk in the QInputDialog class. If no input happened the string should be empty
315     // but it is null. See https://bugs.webkit.org/show_bug.cgi?id=30914.
316     if (rc && x.isNull())
317         result = String("");
318     else
319         result = x;
320
321     return rc;
322 }
323
324 void ChromeClientQt::setStatusbarText(const String& msg)
325 {
326     QString x = msg;
327     emit m_webPage->statusBarMessage(x);
328 }
329
330 bool ChromeClientQt::shouldInterruptJavaScript()
331 {
332     bool shouldInterrupt = false;
333     QMetaObject::invokeMethod(m_webPage, "shouldInterruptJavaScript", Qt::DirectConnection, Q_RETURN_ARG(bool, shouldInterrupt));
334     return shouldInterrupt;
335 }
336
337 bool ChromeClientQt::tabsToLinks() const
338 {
339     return m_webPage->settings()->testAttribute(QWebSettings::LinksIncludedInFocusChain);
340 }
341
342 IntRect ChromeClientQt::windowResizerRect() const
343 {
344 #if defined(Q_WS_MAC)
345     if (!m_webPage)
346         return IntRect();
347
348     QWebPageClient* pageClient = platformPageClient();
349     if (!pageClient)
350         return IntRect();
351
352     QWidget* ownerWidget = pageClient->ownerWidget();
353     if (!ownerWidget)
354         return IntRect();
355
356     QWidget* topLevelWidget = ownerWidget->window();
357     QRect topLevelGeometry(topLevelWidget->geometry());
358
359     // There's no API in Qt to query for the size of the resizer, so we assume
360     // it has the same width and height as the scrollbar thickness.
361     int scollbarThickness = ScrollbarTheme::nativeTheme()->scrollbarThickness();
362
363     // There's no API in Qt to query for the position of the resizer. Sometimes
364     // it's drawn by the system, and sometimes it's a QSizeGrip. For RTL locales
365     // it might even be on the lower left side of the window, but in WebKit we
366     // always draw scrollbars on the right hand side, so we assume this to be the
367     // location when computing the resize rect to reserve for WebKit.
368     QPoint resizeCornerTopLeft = ownerWidget->mapFrom(topLevelWidget,
369             QPoint(topLevelGeometry.width(), topLevelGeometry.height())
370             - QPoint(scollbarThickness, scollbarThickness));
371
372     QRect resizeCornerRect = QRect(resizeCornerTopLeft, QSize(scollbarThickness, scollbarThickness));
373     return resizeCornerRect.intersected(pageClient->geometryRelativeToOwnerWidget());
374 #else
375     return IntRect();
376 #endif
377 }
378
379 void ChromeClientQt::invalidateWindow(const IntRect&, bool)
380 {
381     notImplemented();
382 }
383
384 void ChromeClientQt::invalidateContentsAndWindow(const IntRect& windowRect, bool immediate)
385 {
386     // No double buffer, so only update the QWidget if content changed.
387     if (platformPageClient()) {
388         QRect rect(windowRect);
389         rect = rect.intersected(QRect(QPoint(0, 0), m_webPage->viewportSize()));
390         if (!rect.isEmpty())
391             platformPageClient()->update(rect);
392     }
393     emit m_webPage->repaintRequested(windowRect);
394
395     // FIXME: There is no "immediate" support for window painting.  This should be done always whenever the flag
396     // is set.
397 }
398
399 void ChromeClientQt::invalidateContentsForSlowScroll(const IntRect& windowRect, bool immediate)
400 {
401     invalidateContentsAndWindow(windowRect, immediate);
402 }
403
404 void ChromeClientQt::scroll(const IntSize& delta, const IntRect& scrollViewRect, const IntRect&)
405 {
406     if (platformPageClient())
407         platformPageClient()->scroll(delta.width(), delta.height(), scrollViewRect);
408     emit m_webPage->scrollRequested(delta.width(), delta.height(), scrollViewRect);
409 }
410
411 IntRect ChromeClientQt::windowToScreen(const IntRect& rect) const
412 {
413     QWebPageClient* pageClient = platformPageClient();
414     if (!pageClient)
415         return rect;
416
417     QWidget* ownerWidget = pageClient->ownerWidget();
418     if (!ownerWidget)
419        return rect;
420
421     QRect screenRect(rect);
422     screenRect.translate(ownerWidget->mapToGlobal(QPoint(0, 0)));
423
424     return screenRect;
425 }
426
427 IntPoint ChromeClientQt::screenToWindow(const IntPoint& point) const
428 {
429     QWebPageClient* pageClient = platformPageClient();
430     if (!pageClient)
431         return point;
432
433     QWidget* ownerWidget = pageClient->ownerWidget();
434     if (!ownerWidget)
435         return point;
436
437     return ownerWidget->mapFromGlobal(point);
438 }
439
440 PlatformPageClient ChromeClientQt::platformPageClient() const
441 {
442     return m_webPage->d->client;
443 }
444
445 void ChromeClientQt::contentsSizeChanged(Frame* frame, const IntSize& size) const
446 {
447     emit QWebFramePrivate::kit(frame)->contentsSizeChanged(size);
448 }
449
450 void ChromeClientQt::mouseDidMoveOverElement(const HitTestResult& result, unsigned)
451 {
452     TextDirection dir;
453     if (result.absoluteLinkURL() != lastHoverURL
454         || result.title(dir) != lastHoverTitle
455         || result.textContent() != lastHoverContent) {
456         lastHoverURL = result.absoluteLinkURL();
457         lastHoverTitle = result.title(dir);
458         lastHoverContent = result.textContent();
459         emit m_webPage->linkHovered(lastHoverURL.prettyURL(),
460                 lastHoverTitle, lastHoverContent);
461     }
462 }
463
464 void ChromeClientQt::setToolTip(const String &tip, TextDirection)
465 {
466 #ifndef QT_NO_TOOLTIP
467     QWidget* view = m_webPage->view();
468     if (!view)
469         return;
470
471     if (tip.isEmpty()) {
472         view->setToolTip(QString());
473         QToolTip::hideText();
474     } else {
475         QString dtip = QLatin1String("<p>") + Qt::escape(tip) + QLatin1String("</p>");
476         view->setToolTip(dtip);
477     }
478 #else
479     Q_UNUSED(tip);
480 #endif
481 }
482
483 void ChromeClientQt::print(Frame* frame)
484 {
485     emit m_webPage->printRequested(QWebFramePrivate::kit(frame));
486 }
487
488 #if ENABLE(DATABASE)
489 void ChromeClientQt::exceededDatabaseQuota(Frame* frame, const String& databaseName)
490 {
491     quint64 quota = QWebSettings::offlineStorageDefaultQuota();
492
493     if (!DatabaseTracker::tracker().hasEntryForOrigin(frame->document()->securityOrigin()))
494         DatabaseTracker::tracker().setQuota(frame->document()->securityOrigin(), quota);
495
496     emit m_webPage->databaseQuotaExceeded(QWebFramePrivate::kit(frame), databaseName);
497 }
498 #endif
499
500 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
501 void ChromeClientQt::reachedMaxAppCacheSize(int64_t)
502 {
503     // FIXME: Free some space.
504     notImplemented();
505 }
506
507 void ChromeClientQt::reachedApplicationCacheOriginQuota(SecurityOrigin*)
508 {
509     notImplemented();
510 }
511 #endif
512
513 #if ENABLE(NOTIFICATIONS)
514 NotificationPresenter* ChromeClientQt::notificationPresenter() const
515 {
516     return NotificationPresenterClientQt::notificationPresenter();
517 }
518 #endif
519
520 void ChromeClientQt::runOpenPanel(Frame* frame, PassRefPtr<FileChooser> prpFileChooser)
521 {
522     RefPtr<FileChooser> fileChooser = prpFileChooser;
523     bool supportMulti = m_webPage->supportsExtension(QWebPage::ChooseMultipleFilesExtension);
524
525     if (fileChooser->allowsMultipleFiles() && supportMulti) {
526         QWebPage::ChooseMultipleFilesExtensionOption option;
527         option.parentFrame = QWebFramePrivate::kit(frame);
528
529         if (!fileChooser->filenames().isEmpty())
530             for (unsigned i = 0; i < fileChooser->filenames().size(); ++i)
531                 option.suggestedFileNames += fileChooser->filenames()[i];
532
533         QWebPage::ChooseMultipleFilesExtensionReturn output;
534         m_webPage->extension(QWebPage::ChooseMultipleFilesExtension, &option, &output);
535
536         if (!output.fileNames.isEmpty()) {
537             Vector<String> names;
538             for (int i = 0; i < output.fileNames.count(); ++i)
539                 names.append(output.fileNames.at(i));
540             fileChooser->chooseFiles(names);
541         }
542     } else {
543         QString suggestedFile;
544         if (!fileChooser->filenames().isEmpty())
545             suggestedFile = fileChooser->filenames()[0];
546         QString file = m_webPage->chooseFile(QWebFramePrivate::kit(frame), suggestedFile);
547         if (!file.isEmpty())
548             fileChooser->chooseFile(file);
549     }
550 }
551
552 void ChromeClientQt::chooseIconForFiles(const Vector<String>& filenames, FileChooser* chooser)
553 {
554     chooser->iconLoaded(Icon::createIconForFiles(filenames));
555 }
556
557 void ChromeClientQt::setCursor(const Cursor& cursor)
558 {
559 #ifndef QT_NO_CURSOR
560     QWebPageClient* pageClient = platformPageClient();
561     if (!pageClient)
562         return;
563     pageClient->setCursor(*cursor.platformCursor());
564 #else
565     UNUSED_PARAM(cursor)
566 #endif
567 }
568
569 void ChromeClientQt::requestGeolocationPermissionForFrame(Frame* frame, Geolocation* geolocation)
570 {
571 #if ENABLE(GEOLOCATION)
572     QWebFrame* webFrame = QWebFramePrivate::kit(frame);
573     GeolocationPermissionClientQt::geolocationPermissionClient()->requestGeolocationPermissionForFrame(webFrame, geolocation);
574 #endif
575 }
576
577 void ChromeClientQt::cancelGeolocationPermissionRequestForFrame(Frame* frame, Geolocation* geolocation)
578 {
579 #if ENABLE(GEOLOCATION)
580     QWebFrame* webFrame = QWebFramePrivate::kit(frame);
581     GeolocationPermissionClientQt::geolocationPermissionClient()->cancelGeolocationPermissionRequestForFrame(webFrame, geolocation);
582 #endif
583 }
584
585 #if USE(ACCELERATED_COMPOSITING)
586 void ChromeClientQt::attachRootGraphicsLayer(Frame* frame, GraphicsLayer* graphicsLayer)
587 {
588     if (platformPageClient())
589         platformPageClient()->setRootGraphicsLayer(graphicsLayer ? graphicsLayer->nativeLayer() : 0);
590 }
591
592 void ChromeClientQt::setNeedsOneShotDrawingSynchronization()
593 {
594     // we want the layers to synchronize next time we update the screen anyway
595     if (platformPageClient())
596         platformPageClient()->markForSync(false);
597 }
598
599 void ChromeClientQt::scheduleCompositingLayerSync()
600 {
601     // we want the layers to synchronize ASAP
602     if (platformPageClient())
603         platformPageClient()->markForSync(true);
604 }
605
606 bool ChromeClientQt::allowsAcceleratedCompositing() const
607 {
608     return (platformPageClient() && platformPageClient()->allowsAcceleratedCompositing());
609 }
610
611 #endif
612
613 #if ENABLE(TILED_BACKING_STORE)
614 IntRect ChromeClientQt::visibleRectForTiledBackingStore() const
615 {
616     if (!platformPageClient() || !m_webPage)
617         return IntRect();
618
619     if (!platformPageClient()->viewResizesToContentsEnabled())
620         return QRect(m_webPage->mainFrame()->scrollPosition(), m_webPage->mainFrame()->geometry().size());
621
622     return enclosingIntRect(FloatRect(platformPageClient()->graphicsItemVisibleRect()));
623 }
624 #endif
625
626 QWebSelectMethod* ChromeClientQt::createSelectPopup() const
627 {
628     QWebSelectMethod* result = m_platformPlugin.createSelectInputMethod();
629     if (result)
630         return result;
631
632 #if defined(Q_WS_MAEMO_5)
633     return new QtMaemoWebPopup;
634 #elif !defined(QT_NO_COMBOBOX)
635     return new QtFallbackWebPopup(this);
636 #else
637     return 0;
638 #endif
639 }
640
641 void ChromeClientQt::didReceiveViewportArguments(Frame* frame, const ViewportArguments& arguments) const
642 {
643     if (m_webPage->mainFrame()->d->initialLayoutComplete)
644         return;
645
646     QSize viewportSize(arguments.width, arguments.height);
647     bool isUserScalable = arguments.userScalable == 1;
648
649     QWebPage::ViewportHints hints;
650     hints.m_isValid = true;
651     hints.m_size = viewportSize;
652     hints.m_initialScaleFactor = arguments.initialScale;
653     hints.m_minimumScaleFactor = arguments.minimumScale;
654     hints.m_maximumScaleFactor = arguments.maximumScale;
655     hints.m_targetDensityDpi = arguments.targetDensityDpi;
656     hints.m_isUserScalable = isUserScalable;
657
658     emit m_webPage->viewportChangeRequested(hints);
659 }
660
661 bool ChromeClientQt::selectItemWritingDirectionIsNatural()
662 {
663     return false;
664 }
665
666 PassRefPtr<PopupMenu> ChromeClientQt::createPopupMenu(PopupMenuClient* client) const
667 {
668     return adoptRef(new PopupMenuQt(client, this));
669 }
670
671 PassRefPtr<SearchPopupMenu> ChromeClientQt::createSearchPopupMenu(PopupMenuClient* client) const
672 {
673     return adoptRef(new SearchPopupMenuQt(createPopupMenu(client)));
674 }
675
676 } // namespace WebCore