Update copyright headers
[qt:qt.git] / demos / browser / webview.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the demonstration applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "browserapplication.h"
43 #include "browsermainwindow.h"
44 #include "cookiejar.h"
45 #include "downloadmanager.h"
46 #include "networkaccessmanager.h"
47 #include "tabwidget.h"
48 #include "webview.h"
49
50 #include <QtGui/QClipboard>
51 #include <QtGui/QMenu>
52 #include <QtGui/QMessageBox>
53 #include <QtGui/QMouseEvent>
54
55 #include <QtWebKit/QWebHitTestResult>
56
57 #ifndef QT_NO_UITOOLS
58 #include <QtUiTools/QUiLoader>
59 #endif  //QT_NO_UITOOLS
60
61 #include <QtCore/QDebug>
62 #include <QtCore/QBuffer>
63
64 WebPage::WebPage(QObject *parent)
65     : QWebPage(parent)
66     , m_keyboardModifiers(Qt::NoModifier)
67     , m_pressedButtons(Qt::NoButton)
68     , m_openInNewTab(false)
69 {
70     setNetworkAccessManager(BrowserApplication::networkAccessManager());
71     connect(this, SIGNAL(unsupportedContent(QNetworkReply*)),
72             this, SLOT(handleUnsupportedContent(QNetworkReply*)));
73 }
74
75 BrowserMainWindow *WebPage::mainWindow()
76 {
77     QObject *w = this->parent();
78     while (w) {
79         if (BrowserMainWindow *mw = qobject_cast<BrowserMainWindow*>(w))
80             return mw;
81         w = w->parent();
82     }
83     return BrowserApplication::instance()->mainWindow();
84 }
85
86 bool WebPage::acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type)
87 {
88     // ctrl open in new tab
89     // ctrl-shift open in new tab and select
90     // ctrl-alt open in new window
91     if (type == QWebPage::NavigationTypeLinkClicked
92         && (m_keyboardModifiers & Qt::ControlModifier
93             || m_pressedButtons == Qt::MidButton)) {
94         bool newWindow = (m_keyboardModifiers & Qt::AltModifier);
95         WebView *webView;
96         if (newWindow) {
97             BrowserApplication::instance()->newMainWindow();
98             BrowserMainWindow *newMainWindow = BrowserApplication::instance()->mainWindow();
99             webView = newMainWindow->currentTab();
100             newMainWindow->raise();
101             newMainWindow->activateWindow();
102             webView->setFocus();
103         } else {
104             bool selectNewTab = (m_keyboardModifiers & Qt::ShiftModifier);
105             webView = mainWindow()->tabWidget()->newTab(selectNewTab);
106         }
107         webView->load(request);
108         m_keyboardModifiers = Qt::NoModifier;
109         m_pressedButtons = Qt::NoButton;
110         return false;
111     }
112     if (frame == mainFrame()) {
113         m_loadingUrl = request.url();
114         emit loadingUrl(m_loadingUrl);
115     }
116     return QWebPage::acceptNavigationRequest(frame, request, type);
117 }
118
119 QWebPage *WebPage::createWindow(QWebPage::WebWindowType type)
120 {
121     Q_UNUSED(type);
122     if (m_keyboardModifiers & Qt::ControlModifier || m_pressedButtons == Qt::MidButton)
123         m_openInNewTab = true;
124     if (m_openInNewTab) {
125         m_openInNewTab = false;
126         return mainWindow()->tabWidget()->newTab()->page();
127     }
128     BrowserApplication::instance()->newMainWindow();
129     BrowserMainWindow *mainWindow = BrowserApplication::instance()->mainWindow();
130     return mainWindow->currentTab()->page();
131 }
132
133 #if !defined(QT_NO_UITOOLS)
134 QObject *WebPage::createPlugin(const QString &classId, const QUrl &url, const QStringList &paramNames, const QStringList &paramValues)
135 {
136     Q_UNUSED(url);
137     Q_UNUSED(paramNames);
138     Q_UNUSED(paramValues);
139     QUiLoader loader;
140     return loader.createWidget(classId, view());
141 }
142 #endif // !defined(QT_NO_UITOOLS)
143
144 void WebPage::handleUnsupportedContent(QNetworkReply *reply)
145 {
146     QString errorString = reply->errorString();
147
148     if (m_loadingUrl != reply->url()) {
149         // sub resource of this page
150         qWarning() << "Resource" << reply->url().toEncoded() << "has unknown Content-Type, will be ignored.";
151         reply->deleteLater();
152         return;
153     }
154
155     if (reply->error() == QNetworkReply::NoError && !reply->header(QNetworkRequest::ContentTypeHeader).isValid()) {
156         errorString = "Unknown Content-Type";
157     }
158
159     QFile file(QLatin1String(":/notfound.html"));
160     bool isOpened = file.open(QIODevice::ReadOnly);
161     Q_ASSERT(isOpened);
162     Q_UNUSED(isOpened)
163
164     QString title = tr("Error loading page: %1").arg(reply->url().toString());
165     QString html = QString(QLatin1String(file.readAll()))
166                         .arg(title)
167                         .arg(errorString)
168                         .arg(reply->url().toString());
169
170     QBuffer imageBuffer;
171     imageBuffer.open(QBuffer::ReadWrite);
172     QIcon icon = view()->style()->standardIcon(QStyle::SP_MessageBoxWarning, 0, view());
173     QPixmap pixmap = icon.pixmap(QSize(32,32));
174     if (pixmap.save(&imageBuffer, "PNG")) {
175         html.replace(QLatin1String("IMAGE_BINARY_DATA_HERE"),
176                      QString(QLatin1String(imageBuffer.buffer().toBase64())));
177     }
178
179     QList<QWebFrame*> frames;
180     frames.append(mainFrame());
181     while (!frames.isEmpty()) {
182         QWebFrame *frame = frames.takeFirst();
183         if (frame->url() == reply->url()) {
184             frame->setHtml(html, reply->url());
185             return;
186         }
187         QList<QWebFrame *> children = frame->childFrames();
188         foreach(QWebFrame *frame, children)
189             frames.append(frame);
190     }
191     if (m_loadingUrl == reply->url()) {
192         mainFrame()->setHtml(html, reply->url());
193     }
194 }
195
196
197 WebView::WebView(QWidget* parent)
198     : QWebView(parent)
199     , m_progress(0)
200     , m_page(new WebPage(this))
201 {
202     setPage(m_page);
203     connect(page(), SIGNAL(statusBarMessage(QString)),
204             SLOT(setStatusBarText(QString)));
205     connect(this, SIGNAL(loadProgress(int)),
206             this, SLOT(setProgress(int)));
207     connect(this, SIGNAL(loadFinished(bool)),
208             this, SLOT(loadFinished()));
209     connect(page(), SIGNAL(loadingUrl(QUrl)),
210             this, SIGNAL(urlChanged(QUrl)));
211     connect(page(), SIGNAL(downloadRequested(QNetworkRequest)),
212             this, SLOT(downloadRequested(QNetworkRequest)));
213     page()->setForwardUnsupportedContent(true);
214
215 }
216
217 void WebView::contextMenuEvent(QContextMenuEvent *event)
218 {
219     QWebHitTestResult r = page()->mainFrame()->hitTestContent(event->pos());
220     if (!r.linkUrl().isEmpty()) {
221         QMenu menu(this);
222         menu.addAction(pageAction(QWebPage::OpenLinkInNewWindow));
223         menu.addAction(tr("Open in New Tab"), this, SLOT(openLinkInNewTab()));
224         menu.addSeparator();
225         menu.addAction(pageAction(QWebPage::DownloadLinkToDisk));
226         // Add link to bookmarks...
227         menu.addSeparator();
228         menu.addAction(pageAction(QWebPage::CopyLinkToClipboard));
229         if (page()->settings()->testAttribute(QWebSettings::DeveloperExtrasEnabled))
230             menu.addAction(pageAction(QWebPage::InspectElement));
231         menu.exec(mapToGlobal(event->pos()));
232         return;
233     }
234     QWebView::contextMenuEvent(event);
235 }
236
237 void WebView::wheelEvent(QWheelEvent *event)
238 {
239     if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
240         int numDegrees = event->delta() / 8;
241         int numSteps = numDegrees / 15;
242         setTextSizeMultiplier(textSizeMultiplier() + numSteps * 0.1);
243         event->accept();
244         return;
245     }
246     QWebView::wheelEvent(event);
247 }
248
249 void WebView::openLinkInNewTab()
250 {
251     m_page->m_openInNewTab = true;
252     pageAction(QWebPage::OpenLinkInNewWindow)->trigger();
253 }
254
255 void WebView::setProgress(int progress)
256 {
257     m_progress = progress;
258 }
259
260 void WebView::loadFinished()
261 {
262     if (100 != m_progress) {
263         qWarning() << "Received finished signal while progress is still:" << progress()
264                    << "Url:" << url();
265     }
266     m_progress = 0;
267 }
268
269 void WebView::loadUrl(const QUrl &url)
270 {
271     m_initialUrl = url;
272     load(url);
273 }
274
275 QString WebView::lastStatusBarText() const
276 {
277     return m_statusBarText;
278 }
279
280 QUrl WebView::url() const
281 {
282     QUrl url = QWebView::url();
283     if (!url.isEmpty())
284         return url;
285
286     return m_initialUrl;
287 }
288
289 void WebView::mousePressEvent(QMouseEvent *event)
290 {
291     m_page->m_pressedButtons = event->buttons();
292     m_page->m_keyboardModifiers = event->modifiers();
293     QWebView::mousePressEvent(event);
294 }
295
296 void WebView::mouseReleaseEvent(QMouseEvent *event)
297 {
298     QWebView::mouseReleaseEvent(event);
299     if (!event->isAccepted() && (m_page->m_pressedButtons & Qt::MidButton)) {
300         QUrl url(QApplication::clipboard()->text(QClipboard::Selection));
301         if (!url.isEmpty() && url.isValid() && !url.scheme().isEmpty()) {
302             setUrl(url);
303         }
304     }
305 }
306
307 void WebView::setStatusBarText(const QString &string)
308 {
309     m_statusBarText = string;
310 }
311
312 void WebView::downloadRequested(const QNetworkRequest &request)
313 {
314     BrowserApplication::downloadManager()->download(request);
315 }
316