[Qt] [WK2] Support customizing popup menus with QML
[WebKit-https.git] / Source / WebKit2 / UIProcess / qt / QtWebPageProxy.cpp
1 /*
2  * Copyright (C) 2010, 2011 Nokia Corporation 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 program 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 program; 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 "QtWebPageProxy.h"
23
24 #include <qdeclarativeengine.h>
25 #include <QtQuick/qquickcanvas.h>
26 #include "qquickwebview_p.h"
27 #include "qquickwebview_p_p.h"
28 #include "qquickwebpage_p.h"
29 #include "qwebdownloaditem_p.h"
30 #include "qwebdownloaditem_p_p.h"
31 #include "qwebpreferences_p.h"
32 #include "qwebpreferences_p_p.h"
33
34 #include "DownloadProxy.h"
35 #include "DrawingAreaProxyImpl.h"
36 #include "LayerTreeHostProxy.h"
37 #include "qwkhistory.h"
38 #include "qwkhistory_p.h"
39 #include "QtDownloadManager.h"
40 #include "QtPageClient.h"
41 #include "QtWebPageEventHandler.h"
42 #include "QtWebUndoCommand.h"
43 #include "WebBackForwardList.h"
44 #include "WebContext.h"
45 #include "WebContextMenuProxyQt.h"
46 #include "WebEditCommandProxy.h"
47 #include "WebPopupMenuProxyQt.h"
48 #include "WKStringQt.h"
49 #include "WKURLQt.h"
50 #include <QDrag>
51 #include <QMimeData>
52 #include <QUndoStack>
53 #include <WebCore/DragData.h>
54 #include <WebKit2/WKFrame.h>
55 #include <WebKit2/WKPageGroup.h>
56 #include <WebKit2/WKRetainPtr.h>
57
58 using namespace WebKit;
59 using namespace WebCore;
60
61 QtWebPageProxy::QtWebPageProxy(QQuickWebPage* qmlWebPage, QQuickWebView* qmlWebView, QtPageClient *pageClient, WKContextRef contextRef, WKPageGroupRef pageGroupRef)
62     : m_qmlWebPage(qmlWebPage)
63     , m_qmlWebView(qmlWebView)
64     , m_context(contextRef ? QtWebContext::create(toImpl(contextRef)) : QtWebContext::defaultContext())
65     , m_undoStack(adoptPtr(new QUndoStack(this)))
66     , m_navigatorQtObjectEnabled(false)
67 {
68     m_webPageProxy = m_context->createWebPage(pageClient, toImpl(pageGroupRef));
69     m_history = QWKHistoryPrivate::createHistory(this, m_webPageProxy->backForwardList());
70 }
71
72 void QtWebPageProxy::init(QtWebPageEventHandler* eventHandler)
73 {
74     m_eventHandler = eventHandler;
75     m_webPageProxy->initializeWebPage();
76 }
77
78 QtWebPageProxy::~QtWebPageProxy()
79 {
80     m_webPageProxy->close();
81     delete m_history;
82 }
83
84 void QtWebPageProxy::showContextMenu(QSharedPointer<QMenu> menu)
85 {
86     // Remove the active menu in case this function is called twice.
87     if (activeMenu)
88         activeMenu->hide();
89
90     if (menu->isEmpty())
91         return;
92
93     QWindow* window = m_qmlWebView->canvas();
94     if (!window)
95         return;
96
97     activeMenu = menu;
98
99     activeMenu->window()->winId(); // Ensure that the menu has a window
100     Q_ASSERT(activeMenu->window()->windowHandle());
101     activeMenu->window()->windowHandle()->setTransientParent(window);
102
103     QPoint menuPositionInScene = m_qmlWebView->mapToScene(menu->pos()).toPoint();
104     menu->exec(window->mapToGlobal(menuPositionInScene));
105     // The last function to get out of exec() clear the local copy.
106     if (activeMenu == menu)
107         activeMenu.clear();
108 }
109
110 void QtWebPageProxy::hideContextMenu()
111 {
112     if (activeMenu)
113         activeMenu->hide();
114 }
115
116 void QtWebPageProxy::setViewNeedsDisplay(const WebCore::IntRect&)
117 {
118     m_qmlWebPage->update();
119 }
120
121 WebCore::IntSize QtWebPageProxy::viewSize()
122 {
123     return WebCore::IntSize(m_qmlWebPage->width(), m_qmlWebPage->height());
124 }
125
126 bool QtWebPageProxy::isViewFocused()
127 {
128     return m_qmlWebView->hasFocus();
129 }
130
131 bool QtWebPageProxy::isViewVisible()
132 {
133     return m_qmlWebView->isVisible() && m_qmlWebPage->isVisible();
134 }
135
136 void QtWebPageProxy::pageDidRequestScroll(const IntPoint& pos)
137 {
138     m_qmlWebView->d_func()->scrollPositionRequested(pos);
139 }
140
141 void QtWebPageProxy::didChangeContentsSize(const IntSize& newSize)
142 {
143     m_qmlWebView->d_func()->didChangeContentsSize(newSize);
144 }
145
146 void QtWebPageProxy::didChangeViewportProperties(const WebCore::ViewportArguments& args)
147 {
148     m_qmlWebView->d_func()->didChangeViewportProperties(args);
149 }
150
151 void QtWebPageProxy::registerEditCommand(PassRefPtr<WebEditCommandProxy> command, WebPageProxy::UndoOrRedo undoOrRedo)
152 {
153     if (undoOrRedo == WebPageProxy::Undo) {
154         const QtWebUndoCommand* webUndoCommand = static_cast<const QtWebUndoCommand*>(m_undoStack->command(m_undoStack->index()));
155         if (webUndoCommand && webUndoCommand->inUndoRedo())
156             return;
157         m_undoStack->push(new QtWebUndoCommand(command));
158     }
159 }
160
161 void QtWebPageProxy::clearAllEditCommands()
162 {
163     m_undoStack->clear();
164 }
165
166 bool QtWebPageProxy::canUndoRedo(WebPageProxy::UndoOrRedo undoOrRedo)
167 {
168     if (undoOrRedo == WebPageProxy::Undo)
169         return m_undoStack->canUndo();
170     return m_undoStack->canRedo();
171 }
172
173 void QtWebPageProxy::executeUndoRedo(WebPageProxy::UndoOrRedo undoOrRedo)
174 {
175     if (undoOrRedo == WebPageProxy::Undo)
176         m_undoStack->undo();
177     else
178         m_undoStack->redo();
179 }
180
181 void QtWebPageProxy::selectionChanged(bool, bool, bool, bool)
182 {
183 }
184
185 PassRefPtr<WebPopupMenuProxy> QtWebPageProxy::createPopupMenuProxy(WebPageProxy*)
186 {
187     return WebPopupMenuProxyQt::create(m_webPageProxy.get(), m_qmlWebView);
188 }
189
190 WKPageRef QtWebPageProxy::pageRef() const
191 {
192     return toAPI(m_webPageProxy.get());;
193 }
194
195 void QtWebPageProxy::didReceiveMessageFromNavigatorQtObject(const String& message)
196 {
197     QVariantMap variantMap;
198     variantMap.insert(QLatin1String("data"), QString(message));
199     variantMap.insert(QLatin1String("origin"), url());
200     emit receivedMessageFromNavigatorQtObject(variantMap);
201 }
202
203 bool QtWebPageProxy::canGoBack() const
204 {
205     return m_webPageProxy->canGoBack();
206 }
207
208 void QtWebPageProxy::goBack()
209 {
210     m_webPageProxy->goBack();
211 }
212
213 bool QtWebPageProxy::canGoForward() const
214 {
215     return m_webPageProxy->canGoForward();
216 }
217
218 void QtWebPageProxy::goForward()
219 {
220     m_webPageProxy->goForward();
221 }
222
223 bool QtWebPageProxy::loading() const
224 {
225     RefPtr<WebKit::WebFrameProxy> mainFrame = m_webPageProxy->mainFrame();
226     return mainFrame && !(WebFrameProxy::LoadStateFinished == mainFrame->loadState());
227 }
228
229 void QtWebPageProxy::stop()
230 {
231     m_webPageProxy->stopLoading();
232 }
233
234 bool QtWebPageProxy::canReload() const
235 {
236     RefPtr<WebKit::WebFrameProxy> mainFrame = m_webPageProxy->mainFrame();
237     if (mainFrame)
238         return (WebFrameProxy::LoadStateFinished == mainFrame->loadState());
239     return m_webPageProxy->backForwardList()->currentItem();
240 }
241
242 void QtWebPageProxy::reload()
243 {
244     m_webPageProxy->reload(/* reloadFromOrigin */ true);
245 }
246
247 void QtWebPageProxy::updateNavigationState()
248 {
249     emit m_qmlWebView->navigationStateChanged();
250 }
251
252 void QtWebPageProxy::didRelaunchProcess()
253 {
254     updateNavigationState();
255     qWarning("WARNING: The web process has been successfully restarted.");
256     setDrawingAreaSize(viewSize());
257 }
258
259 void QtWebPageProxy::processDidCrash()
260 {
261     updateNavigationState();
262
263     ASSERT(m_eventHandler);
264     m_eventHandler->resetGestureRecognizers();
265
266     WebCore::KURL url(WebCore::ParsedURLString, m_webPageProxy->urlAtProcessExit());
267     qWarning("WARNING: The web process experienced a crash on '%s'.", qPrintable(QUrl(url).toString(QUrl::RemoveUserInfo)));
268 }
269
270 QWebPreferences* QtWebPageProxy::preferences() const
271 {
272     if (!m_preferences)
273         m_preferences = adoptPtr(QWebPreferencesPrivate::createPreferences(const_cast<QtWebPageProxy*>(this)));
274     return m_preferences.get();
275 }
276
277 void QtWebPageProxy::setCustomUserAgent(const QString& userAgent)
278 {
279     WKRetainPtr<WKStringRef> wkUserAgent(WKStringCreateWithQString(userAgent));
280     WKPageSetCustomUserAgent(pageRef(), wkUserAgent.get());
281 }
282
283 QString QtWebPageProxy::customUserAgent() const
284 {
285     return WKStringCopyQString(WKPageCopyCustomUserAgent(pageRef()));
286 }
287
288 void QtWebPageProxy::setNavigatorQtObjectEnabled(bool enabled)
289 {
290     ASSERT(enabled != m_navigatorQtObjectEnabled);
291     // FIXME: Currently we have to keep this information in both processes and the setting is asynchronous.
292     m_navigatorQtObjectEnabled = enabled;
293     m_context->setNavigatorQtObjectEnabled(m_webPageProxy.get(), enabled);
294 }
295
296 void QtWebPageProxy::postMessageToNavigatorQtObject(const QString& message)
297 {
298     m_context->postMessageToNavigatorQtObject(m_webPageProxy.get(), message);
299 }
300
301 void QtWebPageProxy::loadHTMLString(const QString& html, const QUrl& baseUrl)
302 {
303     WKRetainPtr<WKURLRef> wkUrl(WKURLCreateWithQUrl(baseUrl));
304     WKRetainPtr<WKStringRef> wkHtmlString(WKStringCreateWithQString(html));
305
306     WKPageLoadHTMLString(pageRef(), wkHtmlString.get(), wkUrl.get());
307 }
308
309 void QtWebPageProxy::load(const QUrl& url)
310 {
311     WKRetainPtr<WKURLRef> wkurl = adoptWK(WKURLCreateWithQUrl(url));
312     WKPageLoadURL(pageRef(), wkurl.get());
313 }
314
315 QUrl QtWebPageProxy::url() const
316 {
317     WKRetainPtr<WKFrameRef> frame = WKPageGetMainFrame(pageRef());
318     if (!frame)
319         return QUrl();
320     return WKURLCopyQUrl(adoptWK(WKFrameCopyURL(frame.get())).get());
321 }
322
323 QString QtWebPageProxy::title() const
324 {
325     return WKStringCopyQString(adoptWK(WKPageCopyTitle(toAPI(m_webPageProxy.get()))).get());
326 }
327
328 void QtWebPageProxy::setDrawingAreaSize(const QSize& size)
329 {
330     if (!m_webPageProxy->drawingArea())
331         return;
332
333     m_webPageProxy->drawingArea()->setSize(IntSize(size), IntSize());
334 }
335
336 qreal QtWebPageProxy::textZoomFactor() const
337 {
338     return WKPageGetTextZoomFactor(pageRef());
339 }
340
341 void QtWebPageProxy::setTextZoomFactor(qreal zoomFactor)
342 {
343     WKPageSetTextZoomFactor(pageRef(), zoomFactor);
344 }
345
346 qreal QtWebPageProxy::pageZoomFactor() const
347 {
348     return WKPageGetPageZoomFactor(pageRef());
349 }
350
351 void QtWebPageProxy::setPageZoomFactor(qreal zoomFactor)
352 {
353     WKPageSetPageZoomFactor(pageRef(), zoomFactor);
354 }
355
356 void QtWebPageProxy::setPageAndTextZoomFactors(qreal pageZoomFactor, qreal textZoomFactor)
357 {
358     WKPageSetPageAndTextZoomFactors(pageRef(), pageZoomFactor, textZoomFactor);
359 }
360
361 QWKHistory* QtWebPageProxy::history() const
362 {
363     return m_history;
364 }
365
366 void QtWebPageProxy::startDrag(const WebCore::DragData& dragData, PassRefPtr<ShareableBitmap> dragImage)
367 {
368     QImage dragQImage;
369     if (dragImage)
370         dragQImage = dragImage->createQImage();
371     else if (dragData.platformData() && dragData.platformData()->hasImage())
372         dragQImage = qvariant_cast<QImage>(dragData.platformData()->imageData());
373
374
375     DragOperation dragOperationMask = dragData.draggingSourceOperationMask();
376     QMimeData* mimeData = const_cast<QMimeData*>(dragData.platformData());
377     Qt::DropActions supportedDropActions = QtWebPageEventHandler::dragOperationToDropActions(dragOperationMask);
378
379     QPoint clientPosition;
380     QPoint globalPosition;
381     Qt::DropAction actualDropAction = Qt::IgnoreAction;
382
383     if (QWindow* window = m_qmlWebView->canvas()) {
384         QDrag* drag = new QDrag(window);
385         drag->setPixmap(QPixmap::fromImage(dragQImage));
386         drag->setMimeData(mimeData);
387         actualDropAction = drag->exec(supportedDropActions);
388         globalPosition = QCursor::pos();
389         clientPosition = window->mapFromGlobal(globalPosition);
390     }
391
392     m_webPageProxy->dragEnded(clientPosition, globalPosition, QtWebPageEventHandler::dropActionToDragOperation(actualDropAction));
393 }
394
395 void QtWebPageProxy::handleDownloadRequest(DownloadProxy* download)
396 {
397     // This function is responsible for hooking up a DownloadProxy to our API layer
398     // by creating a QWebDownloadItem. It will then wait for the QWebDownloadItem to be
399     // ready (filled with the ResourceResponse information) so we can pass it through to
400     // our WebViews.
401     QWebDownloadItem* downloadItem = new QWebDownloadItem();
402     downloadItem->d->downloadProxy = download;
403
404     connect(downloadItem->d, SIGNAL(receivedResponse(QWebDownloadItem*)), this, SLOT(didReceiveDownloadResponse(QWebDownloadItem*)));
405     m_context->downloadManager()->addDownload(download, downloadItem);
406 }
407
408 void QtWebPageProxy::didReceiveDownloadResponse(QWebDownloadItem* downloadItem)
409 {
410     // Now that our downloadItem has everything we need we can emit downloadRequested.
411     if (!downloadItem)
412         return;
413
414     QDeclarativeEngine::setObjectOwnership(downloadItem, QDeclarativeEngine::JavaScriptOwnership);
415     emit m_qmlWebView->experimental()->downloadRequested(downloadItem);
416 }
417
418 PassOwnPtr<DrawingAreaProxy> QtWebPageProxy::createDrawingAreaProxy()
419 {
420     return DrawingAreaProxyImpl::create(m_webPageProxy.get());
421 }
422
423 void QtWebPageProxy::renderToCurrentGLContext(const TransformationMatrix& transform, float opacity)
424 {
425     DrawingAreaProxy* drawingArea = m_webPageProxy->drawingArea();
426     if (drawingArea)
427         drawingArea->paintToCurrentGLContext(transform, opacity);
428 }
429
430 void QtWebPageProxy::purgeGLResources()
431 {
432     DrawingAreaProxy* drawingArea = m_webPageProxy->drawingArea();
433     if (drawingArea && drawingArea->layerTreeHostProxy())
434         drawingArea->layerTreeHostProxy()->purgeGLResources();
435 }
436
437 void QtWebPageProxy::setVisibleContentRectAndScale(const QRectF& visibleContentRect, float scale)
438 {
439     if (!m_webPageProxy->drawingArea())
440         return;
441
442     QRect alignedVisibleContentRect = visibleContentRect.toAlignedRect();
443     m_webPageProxy->drawingArea()->setVisibleContentsRectAndScale(alignedVisibleContentRect, scale);
444
445     // FIXME: Once we support suspend and resume, this should be delayed until the page is active if the page is suspended.
446     m_webPageProxy->setFixedVisibleContentRect(alignedVisibleContentRect);
447 }
448
449 void QtWebPageProxy::setVisibleContentRectTrajectoryVector(const QPointF& trajectoryVector)
450 {
451     if (!m_webPageProxy->drawingArea())
452         return;
453
454     m_webPageProxy->drawingArea()->setVisibleContentRectTrajectoryVector(trajectoryVector);
455 }
456
457 #include "moc_QtWebPageProxy.cpp"