Skip to content

Commit 0b6bdc8

Browse files
Introduce C SPI WKPageEvaluateJavaScriptInMainFrame to avoid using WKSerializedScriptValueRef
https://bugs.webkit.org/show_bug.cgi?id=277522 rdar://133024450 Reviewed by Timothy Hatcher. WKPageRunJavaScriptInMainFrame returns the result as a SerializedScriptValue, which you need a JS context to do anything with. WKWebView.evaluateJavaScript returns the result as an NSNumber, NSString, NSDate, NSArray, NSDictionary, or NSNull. This does the WKTypeRef equivalent to make the C SPI look more like the public ObjC API. The next step is to stop using SerializedScriptValue and a temporary JS context just to send a result of one of these 5 types. * Source/WebKit/Shared/API/APISerializedScriptValue.cpp: Copied from Source/WebKit/UIProcess/API/Cocoa/APISerializedScriptValueCocoa.mm. (API::SharedJSContext::SharedJSContext): (API::SharedJSContext::ensureContext): (API::SharedJSContext::releaseContextIfNecessary): (API::sharedContext): (API::SerializedScriptValue::deserializeWK): * Source/WebKit/Shared/API/APISerializedScriptValue.h: * Source/WebKit/Sources.txt: * Source/WebKit/UIProcess/API/C/WKPage.cpp: (WKPageRunJavaScriptInMainFrame): (WKPageEvaluateJavaScriptInMainFrame): (callRunJavaScriptBlockAndRelease): Deleted. (WKPageRunJavaScriptInMainFrame_b): Deleted. * Source/WebKit/UIProcess/API/C/WKPage.h: * Source/WebKit/UIProcess/API/Cocoa/APISerializedScriptValueCocoa.mm: (API::SerializedScriptValue::deserialize): * Source/WebKit/UIProcess/API/Cocoa/WKUserContentController.mm: * Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm: (-[WKWebView _evaluateJavaScript:asAsyncFunction:withSourceURL:withArguments:forceUserGesture:inFrame:inWorld:completionHandler:]): * Source/WebKit/UIProcess/API/Cocoa/_WKInspectorExtension.mm: (-[_WKInspectorExtension evaluateScript:frameURL:contextSecurityOrigin:useContentScriptContext:completionHandler:]): (-[_WKInspectorExtension evaluateScript:inTabWithIdentifier:completionHandler:]): * Source/WebKit/WebKit.xcodeproj/project.pbxproj: * Source/WebKit/WebProcess/Extensions/API/Cocoa/WebExtensionAPIDevToolsInspectedWindowCocoa.mm: (WebKit::WebExtensionAPIDevToolsInspectedWindow::eval): Canonical link: https://commits.webkit.org/281812@main
1 parent fa8948c commit 0b6bdc8

33 files changed

Lines changed: 292 additions & 180 deletions

Source/WebKit/Shared/API/APIArray.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ Ref<Array> Array::create(Vector<RefPtr<Object>>&& elements)
4040
return adoptRef(*new Array(WTFMove(elements)));
4141
}
4242

43+
Ref<Array> Array::createWithCapacity(size_t capacity)
44+
{
45+
auto array = create(Vector<RefPtr<Object>>());
46+
array->m_elements.reserveInitialCapacity(capacity);
47+
return array;
48+
}
49+
4350
Ref<Array> Array::createStringArray(const Vector<WTF::String>& strings)
4451
{
4552
auto elements = strings.map([](auto& string) -> RefPtr<Object> {

Source/WebKit/Shared/API/APIArray.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class Array final : public ObjectImpl<Object::Type::Array> {
5050

5151
public:
5252
static Ref<Array> create();
53+
static Ref<Array> createWithCapacity(size_t);
5354
static Ref<Array> create(Vector<RefPtr<Object>>&&);
5455
static Ref<Array> createStringArray(const Vector<WTF::String>&);
5556
static Ref<Array> createStringArray(const std::span<const WTF::String>);

Source/WebKit/Shared/API/APIDictionary.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ Ref<Dictionary> Dictionary::create()
3636
return create({ });
3737
}
3838

39+
Ref<Dictionary> Dictionary::createWithCapacity(size_t capacity)
40+
{
41+
auto dictionary = create();
42+
dictionary->m_map.reserveInitialCapacity(capacity);
43+
return dictionary;
44+
}
45+
3946
Ref<Dictionary> Dictionary::create(MapType&& map)
4047
{
4148
return adoptRef(*new Dictionary(WTFMove(map)));

Source/WebKit/Shared/API/APIDictionary.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class Dictionary final : public ObjectImpl<Object::Type::Dictionary> {
3939
using MapType = HashMap<WTF::String, RefPtr<Object>>;
4040

4141
static Ref<Dictionary> create();
42+
static Ref<Dictionary> createWithCapacity(size_t);
4243
static Ref<Dictionary> create(MapType&&);
4344

4445
virtual ~Dictionary();
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
* Copyright (C) 2024 Apple Inc. All rights reserved.
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. ``AS IS'' AND ANY
14+
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21+
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24+
*/
25+
26+
#include "config.h"
27+
#include "APISerializedScriptValue.h"
28+
29+
#include "WKMutableArray.h"
30+
#include "WKMutableDictionary.h"
31+
#include "WKNumber.h"
32+
#include "WKString.h"
33+
#include <JavaScriptCore/JSRemoteInspector.h>
34+
#include <JavaScriptCore/JSRetainPtr.h>
35+
36+
namespace API {
37+
38+
static constexpr auto SharedJSContextWKMaxIdleTime = 10_s;
39+
40+
class SharedJSContextWK {
41+
public:
42+
SharedJSContextWK()
43+
: m_timer(RunLoop::main(), this, &SharedJSContextWK::releaseContextIfNecessary)
44+
{
45+
}
46+
47+
JSRetainPtr<JSGlobalContextRef> ensureContext()
48+
{
49+
m_lastUseTime = MonotonicTime::now();
50+
if (!m_context) {
51+
bool inspectionPreviouslyFollowedInternalPolicies = JSRemoteInspectorGetInspectionFollowsInternalPolicies();
52+
JSRemoteInspectorSetInspectionFollowsInternalPolicies(false);
53+
54+
// FIXME: rdar://100738357 Remote Web Inspector: Remove use of JSRemoteInspectorGetInspectionEnabledByDefault
55+
// and JSRemoteInspectorSetInspectionEnabledByDefault once the default state is always false.
56+
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
57+
bool previous = JSRemoteInspectorGetInspectionEnabledByDefault();
58+
JSRemoteInspectorSetInspectionEnabledByDefault(false);
59+
m_context = adopt(JSGlobalContextCreate(nullptr));
60+
JSRemoteInspectorSetInspectionEnabledByDefault(previous);
61+
ALLOW_DEPRECATED_DECLARATIONS_END
62+
63+
JSRemoteInspectorSetInspectionFollowsInternalPolicies(inspectionPreviouslyFollowedInternalPolicies);
64+
65+
m_timer.startOneShot(SharedJSContextWKMaxIdleTime);
66+
}
67+
return m_context;
68+
}
69+
70+
void releaseContextIfNecessary()
71+
{
72+
auto idleTime = MonotonicTime::now() - m_lastUseTime;
73+
if (idleTime < SharedJSContextWKMaxIdleTime) {
74+
// We lazily restart the timer if needed every 10 seconds instead of doing so every time ensureContext()
75+
// is called, for performance reasons.
76+
m_timer.startOneShot(SharedJSContextWKMaxIdleTime - idleTime);
77+
return;
78+
}
79+
m_context.clear();
80+
}
81+
82+
private:
83+
JSRetainPtr<JSGlobalContextRef> m_context;
84+
RunLoop::Timer m_timer;
85+
MonotonicTime m_lastUseTime;
86+
};
87+
88+
static SharedJSContextWK& sharedContext()
89+
{
90+
static MainThreadNeverDestroyed<SharedJSContextWK> sharedContext;
91+
return sharedContext.get();
92+
}
93+
94+
static WKRetainPtr<WKTypeRef> valueToWKObject(JSContextRef context, JSValueRef value)
95+
{
96+
auto jsToWKString = [] (JSStringRef input) {
97+
size_t bufferSize = JSStringGetMaximumUTF8CStringSize(input);
98+
Vector<char> buffer(bufferSize);
99+
size_t utf8Length = JSStringGetUTF8CString(input, buffer.data(), bufferSize);
100+
ASSERT(buffer[utf8Length] == '\0');
101+
return adoptWK(WKStringCreateWithUTF8CStringWithLength(buffer.data(), utf8Length - 1));
102+
};
103+
104+
if (!JSValueIsObject(context, value)) {
105+
if (JSValueIsBoolean(context, value))
106+
return adoptWK(WKBooleanCreate(JSValueToBoolean(context, value)));
107+
if (JSValueIsNumber(context, value))
108+
return adoptWK(WKDoubleCreate(JSValueToNumber(context, value, nullptr)));
109+
if (JSValueIsString(context, value)) {
110+
JSStringRef jsString = JSValueToStringCopy(context, value, nullptr);
111+
WKRetainPtr result = jsToWKString(jsString);
112+
JSStringRelease(jsString);
113+
return result;
114+
}
115+
return nullptr;
116+
}
117+
118+
JSObjectRef object = JSValueToObject(context, value, nullptr);
119+
120+
if (JSValueIsArray(context, value)) {
121+
JSStringRef jsString = JSStringCreateWithUTF8CString("length");
122+
JSValueRef lengthPropertyName = JSValueMakeString(context, jsString);
123+
JSStringRelease(jsString);
124+
JSValueRef lengthValue = JSObjectGetPropertyForKey(context, object, lengthPropertyName, nullptr);
125+
double lengthDouble = JSValueToNumber(context, lengthValue, nullptr);
126+
if (lengthDouble < 0 || lengthDouble > static_cast<double>(std::numeric_limits<size_t>::max()))
127+
return nullptr;
128+
size_t length = lengthDouble;
129+
WKRetainPtr result = adoptWK(WKMutableArrayCreateWithCapacity(length));
130+
for (size_t i = 0; i < length; ++i)
131+
WKArrayAppendItem(result.get(), valueToWKObject(context, JSObjectGetPropertyAtIndex(context, object, i, nullptr)).get());
132+
return result;
133+
}
134+
135+
JSPropertyNameArrayRef names = JSObjectCopyPropertyNames(context, object);
136+
size_t length = JSPropertyNameArrayGetCount(names);
137+
auto result = adoptWK(WKMutableDictionaryCreateWithCapacity(length));
138+
for (size_t i = 0; i < length; i++) {
139+
JSStringRef jsKey = JSPropertyNameArrayGetNameAtIndex(names, i);
140+
WKRetainPtr key = jsToWKString(jsKey);
141+
WKRetainPtr value = valueToWKObject(context, JSObjectGetPropertyForKey(context, object, JSValueMakeString(context, jsKey), nullptr));
142+
WKDictionarySetItem(result.get(), key.get(), value.get());
143+
}
144+
JSPropertyNameArrayRelease(names);
145+
146+
return result;
147+
}
148+
149+
WKRetainPtr<WKTypeRef> SerializedScriptValue::deserializeWK(WebCore::SerializedScriptValue& serializedScriptValue)
150+
{
151+
ASSERT(RunLoop::isMain());
152+
JSRetainPtr context = sharedContext().ensureContext();
153+
ASSERT(context);
154+
155+
JSValueRef value = serializedScriptValue.deserialize(context.get(), nullptr);
156+
if (!value)
157+
return nullptr;
158+
159+
return valueToWKObject(context.get(), value);
160+
}
161+
162+
} // API

Source/WebKit/Shared/API/APISerializedScriptValue.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
#pragma once
2727

2828
#include "APIObject.h"
29-
29+
#include "WKRetainPtr.h"
3030
#include <WebCore/SerializedScriptValue.h>
3131
#include <wtf/RefPtr.h>
3232

@@ -38,6 +38,8 @@ typedef struct _JSCContext JSCContext;
3838
typedef struct _JSCValue JSCValue;
3939
#endif
4040

41+
typedef const void* WKTypeRef;
42+
4143
namespace API {
4244

4345
class SerializedScriptValue : public API::ObjectImpl<API::Object::Type::SerializedScriptValue> {
@@ -64,9 +66,11 @@ class SerializedScriptValue : public API::ObjectImpl<API::Object::Type::Serializ
6466
{
6567
return m_serializedScriptValue->deserialize(context, exception);
6668
}
67-
69+
70+
static WKRetainPtr<WKTypeRef> deserializeWK(WebCore::SerializedScriptValue&);
71+
6872
#if PLATFORM(COCOA) && defined(__OBJC__)
69-
static id deserialize(WebCore::SerializedScriptValue&, JSValueRef* exception);
73+
static id deserialize(WebCore::SerializedScriptValue&);
7074
static RefPtr<SerializedScriptValue> createFromNSObject(id);
7175
#endif
7276

Source/WebKit/Shared/API/c/WKMutableArray.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ WKMutableArrayRef WKMutableArrayCreate()
3434
return const_cast<WKMutableArrayRef>(WebKit::toAPI(&API::Array::create().leakRef()));
3535
}
3636

37+
WKMutableArrayRef WKMutableArrayCreateWithCapacity(size_t capacity)
38+
{
39+
return const_cast<WKMutableArrayRef>(WebKit::toAPI(&API::Array::create().leakRef()));
40+
}
41+
3742
void WKArrayAppendItem(WKMutableArrayRef arrayRef, WKTypeRef itemRef)
3843
{
3944
WebKit::toImpl(arrayRef)->elements().append(WebKit::toImpl(itemRef));

Source/WebKit/Shared/API/c/WKMutableArray.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ extern "C" {
3838
#endif
3939

4040
WK_EXPORT WKMutableArrayRef WKMutableArrayCreate(void);
41+
WK_EXPORT WKMutableArrayRef WKMutableArrayCreateWithCapacity(size_t capacity);
4142

4243
WK_EXPORT void WKArrayAppendItem(WKMutableArrayRef array, WKTypeRef item);
4344

Source/WebKit/Shared/API/c/WKMutableDictionary.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ WKMutableDictionaryRef WKMutableDictionaryCreate()
3434
return const_cast<WKMutableDictionaryRef>(WebKit::toAPI(&API::Dictionary::create().leakRef()));
3535
}
3636

37+
WKMutableDictionaryRef WKMutableDictionaryCreateWithCapacity(size_t capacity)
38+
{
39+
return const_cast<WKMutableDictionaryRef>(WebKit::toAPI(&API::Dictionary::createWithCapacity(capacity).leakRef()));
40+
}
41+
3742
bool WKDictionarySetItem(WKMutableDictionaryRef dictionaryRef, WKStringRef keyRef, WKTypeRef itemRef)
3843
{
3944
return WebKit::toImpl(dictionaryRef)->set(WebKit::toImpl(keyRef)->string(), WebKit::toImpl(itemRef));

Source/WebKit/Shared/API/c/WKMutableDictionary.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ extern "C" {
3737
#endif
3838

3939
WK_EXPORT WKMutableDictionaryRef WKMutableDictionaryCreate(void);
40+
WK_EXPORT WKMutableDictionaryRef WKMutableDictionaryCreateWithCapacity(size_t);
4041

4142
WK_EXPORT bool WKDictionarySetItem(WKMutableDictionaryRef dictionary, WKStringRef key, WKTypeRef item);
4243

0 commit comments

Comments
 (0)