Skip to content

Commit 89b70c7

Browse files
committed
Add asset notifications
This allows an app to determine which assets are currently loaded, and which are in flight.
1 parent e7d034b commit 89b70c7

File tree

11 files changed

+236
-0
lines changed

11 files changed

+236
-0
lines changed

Source/WebCore/PlatformQt.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ list(APPEND WebCore_SOURCES
197197
platform/qt/PlatformKeyboardEventQt.cpp
198198
platform/qt/PlatformScreenQt.cpp
199199
platform/qt/QStyleHelpers.cpp
200+
platform/qt/ResourceLoadTrackerQt.cpp
200201
platform/qt/RenderThemeQStyle.cpp
201202
platform/qt/RenderThemeQt.cpp
202203
platform/qt/RenderThemeQtMobile.cpp

Source/WebCore/loader/DocumentThreadableLoader.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,12 @@ void DocumentThreadableLoader::notifyFinished(CachedResource& resource, const Ne
474474
ASSERT(m_client);
475475
ASSERT_UNUSED(resource, &resource == m_resource);
476476

477+
#if PLATFORM(QT)
478+
LocalFrame* localFrame = document().frame();
479+
if (localFrame)
480+
localFrame->loader().client().dispatchDidFinishResourceLoad(resource);
481+
#endif
482+
477483
if (m_resource->errorOccurred())
478484
didFail(m_resource->resourceLoaderIdentifier(), m_resource->resourceError());
479485
else
@@ -595,6 +601,13 @@ void DocumentThreadableLoader::loadRequest(ResourceRequest&& request, SecurityCh
595601

596602
auto cachedResource = m_document->protectedCachedResourceLoader()->requestRawResource(WTFMove(newRequest));
597603
m_resource = cachedResource.value_or(nullptr);
604+
605+
#if PLATFORM(QT)
606+
LocalFrame* localFrame = document().frame();
607+
if (localFrame && m_resource.get())
608+
localFrame->loader().client().dispatchDidStartResourceLoad(*m_resource.get());
609+
#endif
610+
598611
if (CachedResourceHandle resource = m_resource)
599612
resource->addClient(*this);
600613
else

Source/WebCore/loader/ImageLoader.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,12 @@ void ImageLoader::notifyFinished(CachedResource& resource, const NetworkLoadMetr
469469
{
470470
LOG_WITH_STREAM(LazyLoading, stream << "ImageLoader " << this << " notifyFinished - hasPendingLoadEvent " << m_hasPendingLoadEvent);
471471

472+
#if PLATFORM(QT)
473+
LocalFrame* localFrame = element().document().frame();
474+
if (localFrame)
475+
localFrame->loader().client().dispatchDidFinishResourceLoad(resource);
476+
#endif
477+
472478
ASSERT(m_failedLoadURL.isEmpty());
473479
ASSERT_UNUSED(resource, &resource == m_image.get());
474480

@@ -690,6 +696,12 @@ void ImageLoader::dispatchPendingBeforeLoadEvent()
690696
return;
691697
if (!element().document().hasLivingRenderTree())
692698
return;
699+
#if PLATFORM(QT)
700+
LocalFrame* localFrame = element().document().frame();
701+
if (localFrame)
702+
localFrame->loader().client().dispatchDidStartResourceLoad(*m_image.get());
703+
#endif
704+
693705
m_hasPendingBeforeLoadEvent = false;
694706
if (!element().isConnected())
695707
return;

Source/WebCore/loader/LocalFrameLoaderClient.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@
4141
#include <wtf/WeakRef.h>
4242
#include <wtf/text/WTFString.h>
4343

44+
#if PLATFORM(QT)
45+
#include "CachedResource.h"
46+
#endif
47+
4448
#if ENABLE(APPLICATION_MANIFEST)
4549
#include "ApplicationManifest.h"
4650
#endif
@@ -160,6 +164,10 @@ class WEBCORE_EXPORT LocalFrameLoaderClient : public FrameLoaderClient {
160164
virtual void dispatchDidFinishLoading(DocumentLoader*, IsMainResourceLoad, ResourceLoaderIdentifier) = 0;
161165
virtual void dispatchDidFailLoading(DocumentLoader*, IsMainResourceLoad, ResourceLoaderIdentifier, const ResourceError&) = 0;
162166
virtual bool dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, const ResourceRequest&, const ResourceResponse&, int length) = 0;
167+
#if PLATFORM(QT)
168+
virtual void dispatchDidStartResourceLoad(const CachedResource&) { }
169+
virtual void dispatchDidFinishResourceLoad(const CachedResource&) { }
170+
#endif
163171

164172
virtual void dispatchDidDispatchOnloadEvents() = 0;
165173
virtual void dispatchDidReceiveServerRedirectForProvisionalLoad() = 0;

Source/WebCore/loader/cache/CachedResourceLoader.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
#include <wtf/text/MakeString.h>
9494
#include <wtf/text/WTFString.h>
9595

96+
9697
#if ENABLE(APPLICATION_MANIFEST)
9798
#include "CachedApplicationManifest.h"
9899
#endif

Source/WebKitLegacy/PlatformQt.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ set(QtWebKit_PUBLIC_FRAMEWORK_HEADERS
215215
qt/Api/qwebhistory.h
216216
qt/Api/qwebhistoryinterface.h
217217
qt/Api/qwebkitglobal.h
218+
qt/Api/qwebresourcetypes.h
218219
qt/Api/qwebkitplatformplugin.h
219220
qt/Api/qwebpluginfactory.h
220221
qt/Api/qwebscriptworld.h
@@ -238,6 +239,7 @@ ecm_generate_headers(
238239
QWebHistoryInterface
239240
QWebKitPlatformPlugin,QWebHapticFeedbackPlayer,QWebFullScreenVideoHandler,QWebNotificationData,QWebNotificationPresenter,QWebSelectData,QWebSelectMethod,QWebSpellChecker,QWebTouchModifier
240241
QWebPluginFactory
242+
QWebResourceTypes
241243
QWebSecurityOrigin
242244
QWebSettings
243245
COMMON_HEADER
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
Copyright (C) 2025 Michael Nutt <michael@nuttnet.net>
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 library 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 library; 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+
#ifndef QWEBRESOURCETYPES_H
21+
#define QWEBRESOURCETYPES_H
22+
23+
#include <QtWebKit/qwebkitglobal.h>
24+
#include <QtCore/QString>
25+
#include <QtCore/QUrl>
26+
27+
// These types are duplicated from WebCore to provide a clean public API
28+
// They have the same layout and field names so Qt signals/slots can pass them directly
29+
30+
struct QWEBKIT_EXPORT QtResourceTimingInfo {
31+
qint64 domainLookupMs = -1;
32+
qint64 connectMs = -1;
33+
qint64 sslMs = -1;
34+
qint64 requestMs = -1;
35+
qint64 responseMs = -1;
36+
qint64 totalMs = 0;
37+
};
38+
39+
struct QWEBKIT_EXPORT QtResourceRequestInfo {
40+
QString initiatorType;
41+
QString resourceType;
42+
};
43+
44+
#endif // QWEBRESOURCETYPES_H

Source/WebKitLegacy/qt/WebCoreSupport/FrameLoaderClientQt.cpp

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,11 @@ void FrameLoaderClientQt::setFrame(QWebFrameAdapter* webFrame, LocalFrame* frame
235235

236236
connect(this, SIGNAL(titleChanged(QString)),
237237
m_webFrame->handle(), SIGNAL(titleChanged(QString)));
238+
239+
connect(this, SIGNAL(resourceLoadStarted(const QUrl&, const QtResourceRequestInfo&, bool)),
240+
m_webFrame->pageAdapter->handle(), SIGNAL(resourceLoadStarted(const QUrl&, const QtResourceRequestInfo&, bool)));
241+
connect(this, SIGNAL(resourceLoadFinished(const QUrl, qint64, const QtResourceTimingInfo&, bool)),
242+
m_webFrame->pageAdapter->handle(), SIGNAL(resourceLoadFinished(const QUrl&, qint64, const QtResourceTimingInfo&, bool)));
238243
}
239244

240245
bool FrameLoaderClientQt::hasWebView() const
@@ -1392,6 +1397,116 @@ RefPtr<HistoryItem> FrameLoaderClientQt::createHistoryItemTree(bool clipAtTarget
13921397
return coreMainFrame->loader().history().createItemTree(*m_frame, clipAtTarget, itemID);
13931398
}
13941399

1400+
1401+
void FrameLoaderClientQt::dispatchDidStartResourceLoad(const WebCore::CachedResource& resource)
1402+
{
1403+
QUrl qurl(resource.url());
1404+
1405+
bool isCoalesced = resource.numberOfClients() == 0;
1406+
1407+
QtResourceRequestInfo requestInfo;
1408+
requestInfo.resourceType = resourceTypeToString(resource.type());
1409+
requestInfo.initiatorType = resource.initiatorType().string();
1410+
1411+
Q_EMIT resourceLoadStarted(qurl, requestInfo, isCoalesced);
1412+
}
1413+
1414+
void FrameLoaderClientQt::dispatchDidFinishResourceLoad(const WebCore::CachedResource& resource)
1415+
{
1416+
QUrl qurl(resource.url());
1417+
qint64 size = resource.encodedSize();
1418+
bool success = (resource.status() != WebCore::CachedResource::Status::LoadError &&
1419+
resource.status() != WebCore::CachedResource::Status::DecodeError);
1420+
1421+
// Create timing info from resource
1422+
QtResourceTimingInfo timing;
1423+
if (auto networkMetrics = const_cast<WebCore::CachedResource&>(resource).takeNetworkLoadMetrics()) {
1424+
timing = extractTimingInfo(*networkMetrics);
1425+
} else {
1426+
timing.totalMs = 0; // Fallback
1427+
}
1428+
1429+
Q_EMIT resourceLoadFinished(qurl, size, timing, success);
1430+
}
1431+
1432+
QString FrameLoaderClientQt::resourceTypeToString(WebCore::CachedResource::Type type) const
1433+
{
1434+
switch (type) {
1435+
case WebCore::CachedResource::Type::MainResource:
1436+
return QStringLiteral("document");
1437+
case WebCore::CachedResource::Type::ImageResource:
1438+
return QStringLiteral("image");
1439+
case WebCore::CachedResource::Type::CSSStyleSheet:
1440+
return QStringLiteral("stylesheet");
1441+
case WebCore::CachedResource::Type::Script:
1442+
return QStringLiteral("script");
1443+
case WebCore::CachedResource::Type::FontResource:
1444+
return QStringLiteral("font");
1445+
case WebCore::CachedResource::Type::SVGFontResource:
1446+
return QStringLiteral("svg-font");
1447+
case WebCore::CachedResource::Type::MediaResource:
1448+
return QStringLiteral("media");
1449+
case WebCore::CachedResource::Type::RawResource:
1450+
return QStringLiteral("xhr");
1451+
case WebCore::CachedResource::Type::Icon:
1452+
return QStringLiteral("icon");
1453+
case WebCore::CachedResource::Type::Beacon:
1454+
return QStringLiteral("beacon");
1455+
case WebCore::CachedResource::Type::Ping:
1456+
return QStringLiteral("ping");
1457+
#if ENABLE(XSLT)
1458+
case WebCore::CachedResource::Type::XSLStyleSheet:
1459+
return QStringLiteral("xsl");
1460+
#endif
1461+
case WebCore::CachedResource::Type::LinkPrefetch:
1462+
return QStringLiteral("prefetch");
1463+
#if ENABLE(VIDEO)
1464+
case WebCore::CachedResource::Type::TextTrackResource:
1465+
return QStringLiteral("track");
1466+
#endif
1467+
#if ENABLE(APPLICATION_MANIFEST)
1468+
case WebCore::CachedResource::Type::ApplicationManifest:
1469+
return QStringLiteral("manifest");
1470+
#endif
1471+
case WebCore::CachedResource::Type::SVGDocumentResource:
1472+
return QStringLiteral("svg");
1473+
}
1474+
return QStringLiteral("unknown");
1475+
}
1476+
1477+
QtResourceTimingInfo FrameLoaderClientQt::extractTimingInfo(const WebCore::NetworkLoadMetrics& metrics) const
1478+
{
1479+
QtResourceTimingInfo info;
1480+
1481+
// Extract detailed timing breakdown from NetworkLoadMetrics (W3C Resource Timing API)
1482+
if (metrics.domainLookupStart.secondsSinceEpoch().value() > 0 && metrics.domainLookupEnd.secondsSinceEpoch().value() > 0) {
1483+
info.domainLookupMs = (metrics.domainLookupEnd - metrics.domainLookupStart).millisecondsAs<qint64>();
1484+
}
1485+
1486+
if (metrics.connectStart.secondsSinceEpoch().value() > 0 && metrics.connectEnd.secondsSinceEpoch().value() > 0) {
1487+
info.connectMs = (metrics.connectEnd - metrics.connectStart).millisecondsAs<qint64>();
1488+
}
1489+
1490+
if (metrics.secureConnectionStart.secondsSinceEpoch().value() > 0 && metrics.connectEnd.secondsSinceEpoch().value() > 0) {
1491+
info.sslMs = (metrics.connectEnd - metrics.secureConnectionStart).millisecondsAs<qint64>();
1492+
}
1493+
1494+
if (metrics.requestStart.secondsSinceEpoch().value() > 0 && metrics.responseStart.secondsSinceEpoch().value() > 0) {
1495+
info.requestMs = (metrics.responseStart - metrics.requestStart).millisecondsAs<qint64>();
1496+
}
1497+
1498+
if (metrics.responseStart.secondsSinceEpoch().value() > 0 && metrics.responseEnd.secondsSinceEpoch().value() > 0) {
1499+
info.responseMs = (metrics.responseEnd - metrics.responseStart).millisecondsAs<qint64>();
1500+
}
1501+
1502+
// Calculate total timing
1503+
if (metrics.fetchStart.secondsSinceEpoch().value() > 0 && metrics.responseEnd.secondsSinceEpoch().value() > 0) {
1504+
info.totalMs = (metrics.responseEnd - metrics.fetchStart).millisecondsAs<qint64>();
1505+
}
1506+
1507+
return info;
1508+
}
1509+
13951510
}
13961511

13971512
#include "moc_FrameLoaderClientQt.cpp"

Source/WebKitLegacy/qt/WebCoreSupport/FrameLoaderClientQt.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,16 @@
3030
#ifndef FrameLoaderClientQt_h
3131
#define FrameLoaderClientQt_h
3232

33+
#include <WebCore/CachedResource.h>
3334
#include <WebCore/FormState.h>
3435
#include <WebCore/LocalFrameLoaderClient.h>
36+
#include <WebCore/NetworkLoadMetrics.h>
3537
#include <WebCore/ProcessSwapDisposition.h>
3638
#include <WebCore/ResourceResponse.h>
3739
#include <WebCore/ResourceError.h>
3840

3941
#include <QObject>
42+
#include "qwebresourcetypes.h"
4043
#include <QUrl>
4144
#include <wtf/URL.h>
4245

@@ -68,6 +71,8 @@ class FrameLoaderClientQt final : public QObject, public LocalFrameLoaderClient
6871
Q_SIGNALS:
6972
void titleChanged(const QString& title);
7073
void unsupportedContent(QNetworkReply*);
74+
void resourceLoadStarted(const QUrl& url, const QtResourceRequestInfo& requestInfo, bool fromCache);
75+
void resourceLoadFinished(const QUrl& url, qint64 size, const QtResourceTimingInfo& timing, bool success);
7176

7277
public:
7378
FrameLoaderClientQt(WebCore::FrameLoader&);
@@ -97,6 +102,8 @@ class FrameLoaderClientQt final : public QObject, public LocalFrameLoaderClient
97102
void dispatchDidFinishLoading(WebCore::DocumentLoader*, WebCore::IsMainResourceLoad, WebCore::ResourceLoaderIdentifier) override;
98103
void dispatchDidFailLoading(WebCore::DocumentLoader*, WebCore::IsMainResourceLoad, WebCore::ResourceLoaderIdentifier, const WebCore::ResourceError&) override;
99104
bool dispatchDidLoadResourceFromMemoryCache(WebCore::DocumentLoader*, const WebCore::ResourceRequest&, const WebCore::ResourceResponse&, int) override;
105+
void dispatchDidStartResourceLoad(const WebCore::CachedResource&) override;
106+
void dispatchDidFinishResourceLoad(const WebCore::CachedResource&) override;
100107

101108
void dispatchDidDispatchOnloadEvents() override;
102109
void dispatchDidReceiveServerRedirectForProvisionalLoad() override;
@@ -246,6 +253,10 @@ private Q_SLOTS:
246253
// QTFIXME: consider introducing some sort of flags for storing state
247254
bool m_isDisplayingErrorPage;
248255
bool m_shouldSuppressLoadStarted;
256+
257+
// Helper methods for resource tracking
258+
QString resourceTypeToString(WebCore::CachedResource::Type type) const;
259+
QtResourceTimingInfo extractTimingInfo(const WebCore::NetworkLoadMetrics& metrics) const;
249260
};
250261

251262
}

Source/WebKitLegacy/qt/WidgetApi/qwebpage.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3439,6 +3439,31 @@ bool QWebPage::recentlyAudible() const
34393439
\sa loadStarted(), ErrorPageExtension
34403440
*/
34413441

3442+
/*!
3443+
\fn void QWebPage::resourceLoadStarted(const QUrl& url, const QtResourceRequestInfo& requestInfo, bool fromCache)
3444+
3445+
This signal is emitted when a resource (image, stylesheet, script, etc.) begins loading.
3446+
\a url contains the URL of the resource being loaded.
3447+
\a requestInfo contains detailed information about the resource request including HTTP method,
3448+
headers, priority, and other request metadata.
3449+
\a fromCache indicates whether the resource is being loaded from memory cache.
3450+
3451+
\sa resourceLoadFinished(), QtResourceRequestInfo
3452+
*/
3453+
3454+
/*!
3455+
\fn void QWebPage::resourceLoadFinished(const QUrl& url, qint64 size, const QtResourceTimingInfo& timing, bool success)
3456+
3457+
This signal is emitted when a resource finishes loading (successfully or with error).
3458+
\a url contains the URL of the resource that finished loading.
3459+
\a size contains the actual size in bytes of the loaded resource.
3460+
\a timing contains detailed timing information about the resource load including
3461+
DNS lookup, connection, request/response times, and cache status.
3462+
\a success indicates whether the resource loaded successfully.
3463+
3464+
\sa resourceLoadStarted(), QtResourceTimingInfo
3465+
*/
3466+
34423467
/*!
34433468
\fn void QWebPage::linkHovered(const QString &link, const QString &title, const QString &textContent)
34443469

0 commit comments

Comments
 (0)