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

source code of qttools/src/assistant/assistant/helpbrowsersupport.cpp