1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Assistant of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
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 https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include "helpbrowsersupport.h" |
30 | #include "helpenginewrapper.h" |
31 | #include "helpviewer.h" |
32 | #include "tracer.h" |
33 | |
34 | #include <QtHelp/QHelpEngineCore> |
35 | |
36 | #include <QtNetwork/QNetworkAccessManager> |
37 | #include <QtNetwork/QNetworkReply> |
38 | #include <QtNetwork/QNetworkRequest> |
39 | |
40 | #include <QtCore/QTimer> |
41 | #include <QtCore/QCoreApplication> |
42 | #include <QtCore/QUrl> |
43 | #include <QtCore/QDebug> |
44 | |
45 | QT_BEGIN_NAMESPACE |
46 | |
47 | // -- messages |
48 | |
49 | static const char g_htmlPage[] = "<html><head><meta http-equiv=\"content-type\" content=\"text/html; " |
50 | "charset=UTF-8\"><title>%1</title><style>body{padding: 3em 0em;background: #eeeeee;}" |
51 | "hr{color: lightgray;width: 100%;}img{float: left;opacity: .8;}#box{background: white;border: 1px solid " |
52 | "lightgray;width: 600px;padding: 60px;margin: auto;}h1{font-size: 130%;font-weight: bold;border-bottom: " |
53 | "1px solid lightgray;margin-left: 48px;}h2{font-size: 100%;font-weight: normal;border-bottom: 1px solid " |
54 | "lightgray;margin-left: 48px;}ul{font-size: 80%;padding-left: 48px;margin: 0;}#reloadButton{padding-left:" |
55 | "48px;}</style></head><body><div id=\"box\"><h1>%2</h1><h2>%3</h2><h2><b>%4</b></h2></div></body></html>" ; |
56 | |
57 | QString HelpBrowserSupport::msgError404() |
58 | { |
59 | return QCoreApplication::translate(context: "HelpViewer" , key: "Error 404..." ); |
60 | } |
61 | |
62 | QString HelpBrowserSupport::msgPageNotFound() |
63 | { |
64 | return QCoreApplication::translate(context: "HelpViewer" , key: "The page could not be found" ); |
65 | } |
66 | |
67 | QString HelpBrowserSupport::msgAllDocumentationSets() |
68 | { |
69 | return QCoreApplication::translate(context: "HelpViewer" , |
70 | key: "Please make sure that you have all " |
71 | "documentation sets installed." ); |
72 | } |
73 | |
74 | QString HelpBrowserSupport::msgLoadError(const QUrl &url) |
75 | { |
76 | return HelpViewer::tr(s: "Error loading: %1" ).arg(a: url.toString()); |
77 | } |
78 | |
79 | QString HelpBrowserSupport::msgHtmlErrorPage(const QUrl &url) |
80 | { |
81 | return QString::fromLatin1(str: g_htmlPage) |
82 | .arg(args: HelpBrowserSupport::msgError404(), args: HelpBrowserSupport::msgPageNotFound(), |
83 | args: HelpBrowserSupport::msgLoadError(url), args: HelpBrowserSupport::msgAllDocumentationSets()); |
84 | } |
85 | |
86 | // A QNetworkAccessManager implementing unhandled URL schema handling for WebKit-type browsers. |
87 | |
88 | // -- HelpNetworkReply |
89 | |
90 | class HelpNetworkReply : public QNetworkReply |
91 | { |
92 | public: |
93 | HelpNetworkReply(const QNetworkRequest &request, const QByteArray &fileData, |
94 | const QString &mimeType); |
95 | |
96 | void abort() override; |
97 | |
98 | qint64 bytesAvailable() const override |
99 | { return data.length() + QNetworkReply::bytesAvailable(); } |
100 | |
101 | protected: |
102 | qint64 readData(char *data, qint64 maxlen) override; |
103 | |
104 | private: |
105 | QByteArray data; |
106 | const qint64 origLen; |
107 | }; |
108 | |
109 | HelpNetworkReply::HelpNetworkReply(const QNetworkRequest &request, |
110 | const QByteArray &fileData, const QString& mimeType) |
111 | : data(fileData), origLen(fileData.length()) |
112 | { |
113 | TRACE_OBJ |
114 | setRequest(request); |
115 | setUrl(request.url()); |
116 | setOpenMode(QIODevice::ReadOnly); |
117 | |
118 | setHeader(header: QNetworkRequest::ContentTypeHeader, value: mimeType); |
119 | setHeader(header: QNetworkRequest::ContentLengthHeader, value: QByteArray::number(origLen)); |
120 | QTimer::singleShot(interval: 0, receiver: this, slot: &QNetworkReply::metaDataChanged); |
121 | QTimer::singleShot(interval: 0, receiver: this, slot: &QNetworkReply::readyRead); |
122 | QTimer::singleShot(interval: 0, receiver: this, slot: &QNetworkReply::finished); |
123 | } |
124 | |
125 | void HelpNetworkReply::abort() |
126 | { |
127 | TRACE_OBJ |
128 | } |
129 | |
130 | qint64 HelpNetworkReply::readData(char *buffer, qint64 maxlen) |
131 | { |
132 | TRACE_OBJ |
133 | qint64 len = qMin(a: qint64(data.length()), b: maxlen); |
134 | if (len) { |
135 | memcpy(dest: buffer, src: data.constData(), n: len); |
136 | data.remove(index: 0, len); |
137 | } |
138 | if (!data.length()) |
139 | QTimer::singleShot(interval: 0, receiver: this, slot: &QNetworkReply::finished); |
140 | return len; |
141 | } |
142 | |
143 | // -- HelpRedirectNetworkReply |
144 | |
145 | class HelpRedirectNetworkReply : public QNetworkReply |
146 | { |
147 | public: |
148 | HelpRedirectNetworkReply(const QNetworkRequest &request, const QUrl &newUrl) |
149 | { |
150 | setRequest(request); |
151 | setAttribute(code: QNetworkRequest::HttpStatusCodeAttribute, value: 301); |
152 | setAttribute(code: QNetworkRequest::RedirectionTargetAttribute, value: newUrl); |
153 | |
154 | QTimer::singleShot(interval: 0, receiver: this, slot: &QNetworkReply::finished); |
155 | } |
156 | |
157 | protected: |
158 | void abort() override { TRACE_OBJ } |
159 | qint64 readData(char*, qint64) override { TRACE_OBJ return qint64(-1); } |
160 | }; |
161 | |
162 | // -- HelpNetworkAccessManager |
163 | |
164 | class HelpNetworkAccessManager : public QNetworkAccessManager |
165 | { |
166 | public: |
167 | HelpNetworkAccessManager(QObject *parent); |
168 | |
169 | protected: |
170 | QNetworkReply *createRequest(Operation op, |
171 | const QNetworkRequest &request, QIODevice *outgoingData = nullptr) override; |
172 | }; |
173 | |
174 | HelpNetworkAccessManager::HelpNetworkAccessManager(QObject *parent) |
175 | : QNetworkAccessManager(parent) |
176 | { |
177 | TRACE_OBJ |
178 | } |
179 | |
180 | QNetworkReply *HelpNetworkAccessManager::createRequest(Operation, const QNetworkRequest &request, QIODevice*) |
181 | { |
182 | TRACE_OBJ |
183 | |
184 | QByteArray data; |
185 | const QUrl url = request.url(); |
186 | QUrl redirectedUrl; |
187 | switch (HelpBrowserSupport::resolveUrl(url, targetUrl: &redirectedUrl, data: &data)) { |
188 | case HelpBrowserSupport::UrlRedirect: |
189 | return new HelpRedirectNetworkReply(request, redirectedUrl); |
190 | case HelpBrowserSupport::UrlLocalData: { |
191 | const QString mimeType = HelpViewer::mimeFromUrl(url); |
192 | return new HelpNetworkReply(request, data, mimeType); |
193 | } |
194 | case HelpBrowserSupport::UrlResolveError: |
195 | break; |
196 | } |
197 | return new HelpNetworkReply(request, HelpBrowserSupport::msgHtmlErrorPage(url: request.url()).toUtf8(), |
198 | QStringLiteral("text/html" )); |
199 | } |
200 | |
201 | QByteArray HelpBrowserSupport::fileDataForLocalUrl(const QUrl &url) |
202 | { |
203 | return HelpEngineWrapper::instance().fileData(url); |
204 | } |
205 | |
206 | HelpBrowserSupport::ResolveUrlResult HelpBrowserSupport::resolveUrl(const QUrl &url, |
207 | QUrl *targetUrlP, |
208 | QByteArray *dataP) |
209 | { |
210 | const HelpEngineWrapper &engine = HelpEngineWrapper::instance(); |
211 | |
212 | const QUrl targetUrl = engine.findFile(url); |
213 | if (!targetUrl.isValid()) |
214 | return UrlResolveError; |
215 | |
216 | if (targetUrl != url) { |
217 | if (targetUrlP) |
218 | *targetUrlP = targetUrl; |
219 | return UrlRedirect; |
220 | } |
221 | |
222 | if (dataP) |
223 | *dataP = HelpBrowserSupport::fileDataForLocalUrl(url: targetUrl); |
224 | return UrlLocalData; |
225 | } |
226 | |
227 | QNetworkAccessManager *HelpBrowserSupport::createNetworkAccessManager(QObject *parent) |
228 | { |
229 | return new HelpNetworkAccessManager(parent); |
230 | } |
231 | |
232 | QT_END_NAMESPACE |
233 | |