Skip to content

Commit c6483e9

Browse files
committed
[WPE] Web Inspector: implement platformSave
https://bugs.webkit.org/show_bug.cgi?id=306768 Reviewed by Adrian Perez de Castro. Extracted a common GIO-based implementation for WebKitGTK and WPE WebKit into WebInspectorUIProxyGLib. For WebKitGTK, the logic keeps using file chooser dialog to get an appropriate destination filename. For WPE, this uses G_USER_DIRECTORY_DOWNLOAD and falls back to the user's home directory if undefined, saving the file there with the first 8 elements of the contents hash to avoid accidental overwrites. * Source/WebKit/SourcesGTK.txt: * Source/WebKit/SourcesWPE.txt: * Source/WebKit/UIProcess/Inspector/glib/WebInspectorUIProxyGLib.cpp: Added. (WebKit::fileReplaceContentsCallback): (WebKit::platformSaveDataToFile): * Source/WebKit/UIProcess/Inspector/glib/WebInspectorUIProxyGLib.h: * Source/WebKit/UIProcess/Inspector/gtk/WebInspectorUIProxyGtk.cpp: (WebKit::WebInspectorUIProxy::platformSave): (WebKit::fileReplaceContentsCallback): Deleted. * Source/WebKit/UIProcess/Inspector/wpe/WebInspectorUIProxyWPE.cpp: (WebKit::computeContentHash): (WebKit::WebInspectorUIProxy::platformSave): * Source/WebKit/WebProcess/Inspector/wpe/WebInspectorUIWPE.cpp: (WebKit::WebInspectorUI::canSave): Canonical link: https://commits.webkit.org/306914@main
1 parent 5e09e03 commit c6483e9

File tree

7 files changed

+163
-25
lines changed

7 files changed

+163
-25
lines changed

‎Source/WebKit/SourcesGTK.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ UIProcess/geoclue/GeoclueGeolocationProvider.cpp
245245

246246
UIProcess/Inspector/glib/RemoteInspectorClient.cpp
247247
UIProcess/Inspector/glib/RemoteInspectorHTTPServer.cpp
248+
UIProcess/Inspector/glib/WebInspectorUIProxyGLib.cpp
248249

249250
UIProcess/Inspector/gtk/RemoteWebInspectorUIProxyGtk.cpp
250251
UIProcess/Inspector/gtk/WebInspectorUIProxyGtk.cpp

‎Source/WebKit/SourcesWPE.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ UIProcess/gstreamer/WebPageProxyGStreamer.cpp
256256

257257
UIProcess/Inspector/glib/RemoteInspectorClient.cpp
258258
UIProcess/Inspector/glib/RemoteInspectorHTTPServer.cpp
259+
UIProcess/Inspector/glib/WebInspectorUIProxyGLib.cpp
259260

260261
UIProcess/Inspector/wpe/WebInspectorUIProxyWPE.cpp
261262

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright (C) 2026 Igalia S.L.
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions
6+
* are met:
7+
* 1. Redistributions of source code must retain the above copyright
8+
* notice, this list of conditions and the following disclaimer.
9+
* 2. Redistributions in binary form must reproduce the above copyright
10+
* notice, this list of conditions and the following disclaimer in the
11+
* documentation and/or other materials provided with the distribution.
12+
*
13+
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15+
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17+
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23+
* THE POSSIBILITY OF SUCH DAMAGE.
24+
*/
25+
26+
#include "config.h"
27+
#include "WebInspectorUIProxyGLib.h"
28+
29+
#include <wtf/glib/GUniquePtr.h>
30+
#include <wtf/glib/WTFGType.h>
31+
#include <wtf/text/Base64.h>
32+
33+
namespace WebKit {
34+
35+
struct PlatformSaveData {
36+
Vector<uint8_t> dataVector;
37+
CString dataString;
38+
};
39+
WEBKIT_DEFINE_ASYNC_DATA_STRUCT(PlatformSaveData)
40+
41+
using PlatformSaveDataPtr = std::unique_ptr<PlatformSaveData, decltype(&destroyPlatformSaveData)>;
42+
43+
static void fileReplaceContentsCallback(GObject* sourceObject, GAsyncResult* result, gpointer userData)
44+
{
45+
PlatformSaveDataPtr data(static_cast<PlatformSaveData*>(userData), destroyPlatformSaveData);
46+
GFile* file = G_FILE(sourceObject);
47+
48+
WTF::GUniqueOutPtr<GError> error;
49+
if (!g_file_replace_contents_finish(file, result, nullptr, &error.outPtr()))
50+
LOG_ERROR("Error replacing contents to file %s: %s", g_file_get_path(file), error->message);
51+
}
52+
53+
void platformSaveDataToFile(GRefPtr<GFile>&& file, const String& content, bool base64Encoded)
54+
{
55+
PlatformSaveDataPtr platformSaveData(createPlatformSaveData(), destroyPlatformSaveData);
56+
57+
if (base64Encoded) {
58+
auto decodedData = base64Decode(content);
59+
if (!decodedData)
60+
return;
61+
decodedData->shrinkToFit();
62+
platformSaveData->dataVector = WTF::move(*decodedData);
63+
} else
64+
platformSaveData->dataString = content.utf8();
65+
66+
const char* data = !platformSaveData->dataString.isNull() ? platformSaveData->dataString.data() : reinterpret_cast<const char*>(platformSaveData->dataVector.span().data());
67+
size_t dataLength = !platformSaveData->dataString.isNull() ? platformSaveData->dataString.length() : platformSaveData->dataVector.size();
68+
69+
g_file_replace_contents_async(file.get(), data, dataLength, nullptr, false,
70+
G_FILE_CREATE_NONE, nullptr, fileReplaceContentsCallback, platformSaveData.release());
71+
}
72+
73+
} // namespace WebKit
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright (C) 2026 Igalia S.L.
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions
6+
* are met:
7+
* 1. Redistributions of source code must retain the above copyright
8+
* notice, this list of conditions and the following disclaimer.
9+
* 2. Redistributions in binary form must reproduce the above copyright
10+
* notice, this list of conditions and the following disclaimer in the
11+
* documentation and/or other materials provided with the distribution.
12+
*
13+
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15+
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17+
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23+
* THE POSSIBILITY OF SUCH DAMAGE.
24+
*/
25+
26+
#pragma once
27+
28+
#include <gio/gio.h>
29+
#include <wtf/Forward.h>
30+
#include <wtf/glib/GRefPtr.h>
31+
32+
namespace WebKit {
33+
34+
void platformSaveDataToFile(GRefPtr<GFile>&&, const String& content, bool base64Encoded);
35+
36+
} // namespace WebKit

‎Source/WebKit/UIProcess/Inspector/gtk/WebInspectorUIProxyGtk.cpp‎

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "WebFramePolicyListenerProxy.h"
4141
#include "WebInspectorUIMessages.h"
4242
#include "WebInspectorUIProxyClient.h"
43+
#include "WebInspectorUIProxyGLib.h"
4344
#include "WebKitInspectorWindow.h"
4445
#include "WebKitWebViewBasePrivate.h"
4546
#include "WebOpenPanelResultListenerProxy.h"
@@ -529,12 +530,6 @@ void WebInspectorUIProxy::platformStartWindowDrag()
529530
notImplemented();
530531
}
531532

532-
static void fileReplaceContentsCallback(GObject* sourceObject, GAsyncResult* result, gpointer userData)
533-
{
534-
GFile* file = G_FILE(sourceObject);
535-
g_file_replace_contents_finish(file, result, nullptr, nullptr);
536-
}
537-
538533
void WebInspectorUIProxy::platformSave(Vector<WebCore::InspectorFrontendClient::SaveData>&& saveDatas, bool forceSaveAs)
539534
{
540535
ASSERT(saveDatas.size() == 1);
@@ -562,23 +557,8 @@ void WebInspectorUIProxy::platformSave(Vector<WebCore::InspectorFrontendClient::
562557
if (gtk_native_dialog_run(GTK_NATIVE_DIALOG(dialog.get())) != GTK_RESPONSE_ACCEPT)
563558
return;
564559

565-
Vector<uint8_t> dataVector;
566-
CString dataString;
567-
if (saveDatas[0].base64Encoded) {
568-
auto decodedData = base64Decode(saveDatas[0].content, { Base64DecodeOption::ValidatePadding });
569-
if (!decodedData)
570-
return;
571-
decodedData->shrinkToFit();
572-
dataVector = WTF::move(*decodedData);
573-
} else
574-
dataString = saveDatas[0].content.utf8();
575-
576-
const char* data = !dataString.isNull() ? dataString.data() : reinterpret_cast<const char*>(dataVector.span().data());
577-
size_t dataLength = !dataString.isNull() ? dataString.length() : dataVector.size();
578560
GRefPtr<GFile> file = adoptGRef(gtk_file_chooser_get_file(chooser));
579-
GUniquePtr<char> path(g_file_get_path(file.get()));
580-
g_file_replace_contents_async(file.get(), data, dataLength, nullptr, false,
581-
G_FILE_CREATE_REPLACE_DESTINATION, nullptr, fileReplaceContentsCallback, protect(inspectorPage()).get());
561+
platformSaveDataToFile(WTF::move(file), saveDatas[0].content, saveDatas[0].base64Encoded);
582562
}
583563

584564
void WebInspectorUIProxy::platformLoad(const String&, CompletionHandler<void(const String&)>&& completionHandler)

‎Source/WebKit/UIProcess/Inspector/wpe/WebInspectorUIProxyWPE.cpp‎

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "APIPageConfiguration.h"
3434
#include "WPEWebViewPlatform.h"
3535
#include "WebFramePolicyListenerProxy.h"
36+
#include "WebInspectorUIProxyGLib.h"
3637
#include "WebPageGroup.h"
3738
#include "WebPageProxy.h"
3839
#include "WebPreferences.h"
@@ -46,6 +47,7 @@
4647
#include <wtf/FileSystem.h>
4748
#include <wtf/glib/GRefPtr.h>
4849
#include <wtf/glib/GUniquePtr.h>
50+
#include <wtf/text/MakeString.h>
4951
#include <wtf/text/WTFString.h>
5052

5153
namespace WebKit {
@@ -230,9 +232,45 @@ void WebInspectorUIProxy::platformShowCertificate(const WebCore::CertificateInfo
230232
notImplemented();
231233
}
232234

233-
void WebInspectorUIProxy::platformSave(Vector<WebCore::InspectorFrontendClient::SaveData>&&, bool /* forceSaveAs */)
235+
static String computeContentHash(const String& content, bool base64Encoded)
234236
{
235-
notImplemented();
237+
GUniquePtr<char> digest;
238+
if (base64Encoded) {
239+
auto decoded = base64Decode(content);
240+
if (decoded)
241+
digest.reset(g_compute_checksum_for_data(G_CHECKSUM_SHA256, decoded->span().data(), decoded->size()));
242+
} else {
243+
CString utf8 = content.utf8();
244+
digest.reset(g_compute_checksum_for_string(G_CHECKSUM_SHA256, utf8.data(), utf8.length()));
245+
}
246+
247+
return String::fromUTF8(digest.get());
248+
}
249+
250+
void WebInspectorUIProxy::platformSave(Vector<WebCore::InspectorFrontendClient::SaveData>&& saveDatas, bool forceSaveAs)
251+
{
252+
ASSERT(saveDatas.size() == 1);
253+
UNUSED_PARAM(forceSaveAs);
254+
255+
// Some inspector views (Audits for instance) use a custom URI scheme, such
256+
// as web-inspector. So we can't rely on the URL being a valid file:/// URL
257+
// unfortunately.
258+
URL url { saveDatas[0].url };
259+
auto filename = url.path().substring(1).utf8();
260+
261+
const gchar* downloadsDir = g_get_user_special_dir(G_USER_DIRECTORY_DOWNLOAD);
262+
if (!downloadsDir) {
263+
// If we don't have XDG user dirs info, set just to HOME.
264+
downloadsDir = g_get_home_dir();
265+
}
266+
267+
auto hash = computeContentHash(saveDatas[0].content, saveDatas[0].base64Encoded);
268+
269+
auto updatedFilename = makeString(filename, "-"_s, hash.left(8)).utf8();
270+
271+
GRefPtr<GFile> file = adoptGRef(g_file_new_build_filename(downloadsDir, updatedFilename.data(), nullptr));
272+
273+
platformSaveDataToFile(WTF::move(file), saveDatas[0].content, saveDatas[0].base64Encoded);
236274
}
237275

238276
void WebInspectorUIProxy::platformLoad(const String&, CompletionHandler<void(const String&)>&& completionHandler)

‎Source/WebKit/WebProcess/Inspector/wpe/WebInspectorUIWPE.cpp‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,17 @@ void WebInspectorUI::didEstablishConnection()
3939
WTF::registerInspectorResourceIfNeeded();
4040
}
4141

42-
bool WebInspectorUI::canSave(InspectorFrontendClient::SaveMode)
42+
bool WebInspectorUI::canSave(InspectorFrontendClient::SaveMode saveMode)
4343
{
44+
switch (saveMode) {
45+
case InspectorFrontendClient::SaveMode::SingleFile:
46+
return true;
47+
48+
case InspectorFrontendClient::SaveMode::FileVariants:
49+
return false;
50+
}
51+
52+
ASSERT_NOT_REACHED();
4453
return false;
4554
}
4655

0 commit comments

Comments
 (0)