summaryrefslogtreecommitdiffstats
path: root/src/assistant/assistant/helpbrowsersupport.cpp
blob: 2109f906c4b9a57dcab6c4005fda95e51b5ebb66 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0

#include "helpbrowsersupport.h"
#include "helpenginewrapper.h"
#include "helpviewer.h"
#include "tracer.h"

#include <QtHelp/QHelpEngineCore>

#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>

#include <QtCore/QTimer>
#include <QtCore/QCoreApplication>
#include <QtCore/QUrl>
#include <QtCore/QDebug>

QT_BEGIN_NAMESPACE

// -- messages

static const char g_htmlPage[] = "<html><head><meta http-equiv=\"content-type\" content=\"text/html; "
    "charset=UTF-8\"><title>%1</title><style>body{padding: 3em 0em;background: #eeeeee;}"
    "hr{color: lightgray;width: 100%;}img{float: left;opacity: .8;}#box{background: white;border: 1px solid "
    "lightgray;width: 600px;padding: 60px;margin: auto;}h1{font-size: 130%;font-weight: bold;border-bottom: "
    "1px solid lightgray;margin-left: 48px;}h2{font-size: 100%;font-weight: normal;border-bottom: 1px solid "
    "lightgray;margin-left: 48px;}ul{font-size: 80%;padding-left: 48px;margin: 0;}#reloadButton{padding-left:"
    "48px;}</style></head><body><div id=\"box\"><h1>%2</h1><h2>%3</h2><h2><b>%4</b></h2></div></body></html>";

QString HelpBrowserSupport::msgError404()
{
    return QCoreApplication::translate("HelpViewer", "Error 404...");
}

QString HelpBrowserSupport::msgPageNotFound()
{
    return QCoreApplication::translate("HelpViewer", "The page could not be found");
}

QString HelpBrowserSupport::msgAllDocumentationSets()
{
     return QCoreApplication::translate("HelpViewer",
                                        "Please make sure that you have all "
                                        "documentation sets installed.");
}

QString HelpBrowserSupport::msgLoadError(const QUrl &url)
{
    return HelpViewer::tr("Error loading: %1").arg(url.toString());
}

QString HelpBrowserSupport::msgHtmlErrorPage(const QUrl &url)
{
    return QString::fromLatin1(g_htmlPage)
        .arg(HelpBrowserSupport::msgError404(), HelpBrowserSupport::msgPageNotFound(),
             HelpBrowserSupport::msgLoadError(url), HelpBrowserSupport::msgAllDocumentationSets());
}

// A QNetworkAccessManager implementing unhandled URL schema handling for WebKit-type browsers.

// -- HelpNetworkReply

class HelpNetworkReply : public QNetworkReply
{
public:
    HelpNetworkReply(const QNetworkRequest &request, const QByteArray &fileData,
        const QString &mimeType);

    void abort() override;

    qint64 bytesAvailable() const override
        { return data.size() + QNetworkReply::bytesAvailable(); }

protected:
    qint64 readData(char *data, qint64 maxlen) override;

private:
    QByteArray data;
    const qint64 origLen;
};

HelpNetworkReply::HelpNetworkReply(const QNetworkRequest &request,
        const QByteArray &fileData, const QString& mimeType)
    : data(fileData), origLen(fileData.size())
{
    TRACE_OBJ
    setRequest(request);
    setUrl(request.url());
    setOpenMode(QIODevice::ReadOnly);

    setHeader(QNetworkRequest::ContentTypeHeader, mimeType);
    setHeader(QNetworkRequest::ContentLengthHeader, QByteArray::number(origLen));
    QTimer::singleShot(0, this, &QNetworkReply::metaDataChanged);
    QTimer::singleShot(0, this, &QNetworkReply::readyRead);
    QTimer::singleShot(0, this, &QNetworkReply::finished);
}

void HelpNetworkReply::abort()
{
    TRACE_OBJ
}

qint64 HelpNetworkReply::readData(char *buffer, qint64 maxlen)
{
    TRACE_OBJ
    qint64 len = qMin(qint64(data.size()), maxlen);
    if (len) {
        memcpy(buffer, data.constData(), len);
        data.remove(0, len);
    }
    if (!data.size())
        QTimer::singleShot(0, this, &QNetworkReply::finished);
    return len;
}

// -- HelpRedirectNetworkReply

class HelpRedirectNetworkReply : public QNetworkReply
{
public:
    HelpRedirectNetworkReply(const QNetworkRequest &request, const QUrl &newUrl)
    {
        setRequest(request);
        setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 301);
        setAttribute(QNetworkRequest::RedirectionTargetAttribute, newUrl);

        QTimer::singleShot(0, this, &QNetworkReply::finished);
    }

protected:
    void abort() override { TRACE_OBJ }
    qint64 readData(char*, qint64) override { TRACE_OBJ return qint64(-1); }
};

// -- HelpNetworkAccessManager

class HelpNetworkAccessManager : public QNetworkAccessManager
{
public:
    HelpNetworkAccessManager(QObject *parent);

protected:
    QNetworkReply *createRequest(Operation op,
        const QNetworkRequest &request, QIODevice *outgoingData = nullptr) override;
};

HelpNetworkAccessManager::HelpNetworkAccessManager(QObject *parent)
    : QNetworkAccessManager(parent)
{
    TRACE_OBJ
}

QNetworkReply *HelpNetworkAccessManager::createRequest(Operation, const QNetworkRequest &request, QIODevice*)
{
    TRACE_OBJ

    QByteArray data;
    const QUrl url = request.url();
    QUrl redirectedUrl;
    switch (HelpBrowserSupport::resolveUrl(url, &redirectedUrl, &data)) {
    case HelpBrowserSupport::UrlRedirect:
        return new HelpRedirectNetworkReply(request, redirectedUrl);
    case HelpBrowserSupport::UrlLocalData: {
        const QString mimeType = HelpViewer::mimeFromUrl(url);
        return new HelpNetworkReply(request, data, mimeType);
    }
    case HelpBrowserSupport::UrlResolveError:
        break;
    }
    return new HelpNetworkReply(request, HelpBrowserSupport::msgHtmlErrorPage(request.url()).toUtf8(),
                                QStringLiteral("text/html"));
}

QByteArray HelpBrowserSupport::fileDataForLocalUrl(const QUrl &url)
{
    return HelpEngineWrapper::instance().fileData(url);
}

HelpBrowserSupport::ResolveUrlResult HelpBrowserSupport::resolveUrl(const QUrl &url,
                                                                    QUrl *targetUrlP,
                                                                    QByteArray *dataP)
{
    const HelpEngineWrapper &engine = HelpEngineWrapper::instance();

    const QUrl targetUrl = engine.findFile(url);
    if (!targetUrl.isValid())
        return UrlResolveError;

    if (targetUrl != url) {
        if (targetUrlP)
            *targetUrlP = targetUrl;
        return UrlRedirect;
    }

    if (dataP)
        *dataP = HelpBrowserSupport::fileDataForLocalUrl(targetUrl);
    return UrlLocalData;
}

QNetworkAccessManager *HelpBrowserSupport::createNetworkAccessManager(QObject *parent)
{
    return new HelpNetworkAccessManager(parent);
}

QT_END_NAMESPACE