Skip to content

Commit 09e8854

Browse files
committed
[WebDriver][BiDi] Implement the browsingContext.getTree method
https://bugs.webkit.org/show_bug.cgi?id=283784 Reviewed by BJ Burg. Add support for the root and maxDepth parameter. The root selection is done by splitting the steps of selecting the navigables to traverse and actually traversing them. Also, fix the presence of some fields in the returned payload. In the current version of the spec, only the "parent" field is actually optional, while the other fields must be present, even if they're null. In order to support the latter, this commit also adds support for a new boolean field in object types declared in the automation protocol: "nullable". This field generates a "setNull..." method that sets the related key in the underlying objet with JSON::Value::null(). That way we can keep the original setter receiving the original types like Ref<...>. Originally, the generated inspector code also restricted the non-optional fields to be set at construction time, with the nested `Builder` class wrapping the generated type constructor. This commit also exposes the normal setters for non-optional-but-nullable fields, so we can set these fields later if needed. For example: auto obj = Protocol::Module::Object::create() .setRequiredField() .setNullChildren() // "children" is a "nullable" member .release(); ... obj.setChildren(childrenArray); This "nullable" tag contrasts with "optional" in the sense that with optional, the key might be missing from the final object. Note that this doesn't cover nullable command parameters nor support for processing of incoming frontend data with nullable fields, as we only need to support outbound data from the backend (i.e. command return values) for now. * Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_protocol_types_header.py: * Generate new setter models in Builder and actual Model classes. * Source/JavaScriptCore/inspector/scripts/codegen/models.py: * Add "nullable" property to type declarations. (Protocol.parse_type_member): (TypeMember.__init__): * Source/JavaScriptCore/inspector/scripts/tests/expected/type-declaration-object-type.json-result: * Rebaseline results. * Source/JavaScriptCore/inspector/scripts/tests/type-declaration-object-type.json: * Add testcase declaration. * Source/WebKit/UIProcess/Automation/BidiBrowsingContextAgent.cpp: (WebKit::BidiBrowsingContextAgent::getBrowsingContextID const): Helper added. (WebKit::BidiBrowsingContextAgent::getNavigableInfo): Added. (WebKit::BidiBrowsingContextAgent::getNextTree): Added. (WebKit::toOptionalUint): Added. (WebKit::BidiBrowsingContextAgent::getTree): Add support for maxDepth, root, and actually traverse the navigable trees. * Source/WebKit/UIProcess/Automation/BidiBrowsingContextAgent.h: Declare new methods and types. * Source/WebKit/UIProcess/Automation/WebAutomationSession.h: Make handleForWebFrame* methods public as the BrowsingContext agent uses it. * Source/WebKit/UIProcess/Automation/protocol/BidiBrowsingContext.json: Fix optional and nullable fields for getTree parameters. * WebDriverTests/TestExpectations.json: Gardening results with this patch. Canonical link: https://commits.webkit.org/295805@main
1 parent ad5cb20 commit 09e8854

9 files changed

Lines changed: 346 additions & 59 deletions

File tree

‎Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_protocol_types_header.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,8 @@ def _generate_class_for_object_declaration(self, type_declaration, domain):
266266
for type_member in required_members:
267267
lines.append(self._generate_builder_setter_for_member(type_member, domain))
268268
lines.append(Template(CppTemplates.ProtocolObjectBuilderDeclarationPostlude).substitute(None, **builder_args))
269+
for member in [member for member in required_members if member.is_nullable]:
270+
lines.append(self._generate_unchecked_setter_for_member(member, domain))
269271
for member in optional_members:
270272
lines.append(self._generate_unchecked_setter_for_member(member, domain))
271273

@@ -343,6 +345,14 @@ def _generate_builder_setter_for_member(self, type_member, domain):
343345
lines.append(' m_result->%(setter)s("%(memberKey)s"_s, %(memberValue)s);' % setter_args)
344346
lines.append(' return castState<%(camelName)sSet>();' % setter_args)
345347
lines.append(' }')
348+
if type_member.is_nullable:
349+
lines.append('')
350+
lines.append(' Builder<STATE | %(camelName)sSet>& set%(camelName)sIsNull()' % setter_args)
351+
lines.append(' {')
352+
lines.append(' static_assert(!(STATE & %(camelName)sSet), "property %(memberKey)s already set");' % setter_args)
353+
lines.append(' m_result->setValue("%(memberKey)s"_s, JSON::Value::null());' % setter_args)
354+
lines.append(' return castState<%(camelName)sSet>();' % setter_args)
355+
lines.append(' }')
346356
return '\n'.join(lines)
347357

348358
def _generate_unchecked_setter_for_member(self, type_member, domain):
@@ -369,6 +379,12 @@ def _generate_unchecked_setter_for_member(self, type_member, domain):
369379
lines.append(' {')
370380
lines.append(' JSON::ObjectBase::%(setter)s("%(memberKey)s"_s, %(memberValue)s);' % setter_args)
371381
lines.append(' }')
382+
if type_member.is_nullable:
383+
lines.append('')
384+
lines.append(' void set%(camelName)sIsNull()' % setter_args)
385+
lines.append(' {')
386+
lines.append(' JSON::ObjectBase::setValue("%(memberKey)s"_s, JSON::Value::null());' % setter_args)
387+
lines.append(' }')
372388
return '\n'.join(lines)
373389

374390
def _generate_forward_declarations_for_binding_traits(self, domains):

‎Source/JavaScriptCore/inspector/scripts/codegen/models.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ def parse_type_member(self, json):
455455
log.debug("parse type member %s" % json['name'])
456456

457457
type_ref = TypeReference(json.get('type'), json.get('$ref'), json.get('enum'), json.get('items'))
458-
return TypeMember(json['name'], type_ref, json.get('optional', False), json.get('description', ""))
458+
return TypeMember(json['name'], type_ref, json.get('optional', False), json.get('nullable', False), json.get('description', ""))
459459

460460
def parse_command(self, json, debuggable_types):
461461
check_for_required_properties(['name'], json, "command")
@@ -693,10 +693,11 @@ def resolve_type_references(self, protocol, domain):
693693

694694

695695
class TypeMember:
696-
def __init__(self, member_name, type_ref, is_optional, description):
696+
def __init__(self, member_name, type_ref, is_optional, is_nullable, description):
697697
self.member_name = member_name
698698
self.type_ref = type_ref
699699
self.is_optional = is_optional
700+
self.is_nullable = is_nullable
700701
self.description = description
701702

702703
if not isinstance(self.is_optional, bool):

‎Source/JavaScriptCore/inspector/scripts/tests/expected/type-declaration-object-type.json-result‎

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ class OptionalParameterBundle;
363363
class ParameterBundle;
364364
class ObjectWithPropertyNameConflicts;
365365
class DummyObject;
366+
class ObjectWithNullableProperties;
366367
enum class MouseButton;
367368
} // Database
368369

@@ -838,6 +839,90 @@ public:
838839
}
839840
};
840841

842+
/* An object with properties that can be null. */
843+
class ObjectWithNullableProperties final : public JSON::ObjectBase {
844+
public:
845+
enum {
846+
NoFieldsSet = 0,
847+
NullableObjectSet = 1 << 0,
848+
AllFieldsSet = (NullableObjectSet)
849+
};
850+
851+
template<int STATE>
852+
class Builder {
853+
private:
854+
RefPtr<JSON::Object> m_result;
855+
856+
template<int STEP> Builder<STATE | STEP>& castState()
857+
{
858+
return *reinterpret_cast<Builder<STATE | STEP>*>(this);
859+
}
860+
861+
Builder(Ref</*ObjectWithNullableProperties*/JSON::Object>&& object)
862+
: m_result(WTFMove(object))
863+
{
864+
static_assert(STATE == NoFieldsSet, "builder created in non init state");
865+
}
866+
friend class ObjectWithNullableProperties;
867+
public:
868+
869+
Builder<STATE | NullableObjectSet>& setNullableObject(Ref<Protocol::Database::DummyObject>&& in_nullableObject)
870+
{
871+
static_assert(!(STATE & NullableObjectSet), "property nullableObject already set");
872+
m_result->setObject("nullableObject"_s, WTFMove(in_nullableObject));
873+
return castState<NullableObjectSet>();
874+
}
875+
876+
Builder<STATE | NullableObjectSet>& setNullableObjectIsNull()
877+
{
878+
static_assert(!(STATE & NullableObjectSet), "property nullableObject already set");
879+
m_result->setValue("nullableObject"_s, JSON::Value::null());
880+
return castState<NullableObjectSet>();
881+
}
882+
883+
Ref<ObjectWithNullableProperties> release()
884+
{
885+
static_assert(STATE == AllFieldsSet, "result is not ready");
886+
static_assert(sizeof(ObjectWithNullableProperties) == sizeof(JSON::Object), "cannot cast");
887+
888+
Ref<JSON::Object> jsonResult = m_result.releaseNonNull();
889+
auto result = WTFMove(*reinterpret_cast<Ref<ObjectWithNullableProperties>*>(&jsonResult));
890+
return result;
891+
}
892+
};
893+
894+
/*
895+
* Synthetic constructor:
896+
* Ref<ObjectWithNullableProperties> result = ObjectWithNullableProperties::create()
897+
* .setNullableObject(...)
898+
* .release();
899+
*/
900+
static Builder<NoFieldsSet> create()
901+
{
902+
return Builder<NoFieldsSet>(JSON::Object::create());
903+
}
904+
905+
void setNullableObject(Ref<Protocol::Database::DummyObject>&& in_opt_nullableObject)
906+
{
907+
JSON::ObjectBase::setObject("nullableObject"_s, WTFMove(in_opt_nullableObject));
908+
}
909+
910+
void setNullableObjectIsNull()
911+
{
912+
JSON::ObjectBase::setValue("nullableObject"_s, JSON::Value::null());
913+
}
914+
915+
void setNullableOptionalObject(Ref<Protocol::Database::DummyObject>&& in_opt_nullableOptionalObject)
916+
{
917+
JSON::ObjectBase::setObject("nullableOptionalObject"_s, WTFMove(in_opt_nullableOptionalObject));
918+
}
919+
920+
void setNullableOptionalObjectIsNull()
921+
{
922+
JSON::ObjectBase::setValue("nullableOptionalObject"_s, JSON::Value::null());
923+
}
924+
};
925+
841926
} // Database
842927

843928
namespace Test {
@@ -1390,6 +1475,7 @@ using namespace Inspector;
13901475
@class TestProtocolDatabaseParameterBundle;
13911476
@class TestProtocolDatabaseObjectWithPropertyNameConflicts;
13921477
@class TestProtocolDatabaseDummyObject;
1478+
@class TestProtocolDatabaseObjectWithNullableProperties;
13931479
@class TestProtocolTestParameterBundle;
13941480

13951481
typedef NS_ENUM(NSInteger, TestProtocolDatabaseMouseButton) {
@@ -1470,6 +1556,15 @@ __attribute__((visibility ("default")))
14701556
- (instancetype)initWithPayload:(NSDictionary<NSString *, id> *)payload;
14711557
- (instancetype)initWithProtocolObject:(RWIProtocolJSONObject *)jsonObject;
14721558
@end
1559+
1560+
__attribute__((visibility ("default")))
1561+
@interface TestProtocolDatabaseObjectWithNullableProperties : RWIProtocolJSONObject
1562+
- (instancetype)initWithPayload:(NSDictionary<NSString *, id> *)payload;
1563+
- (instancetype)initWithProtocolObject:(RWIProtocolJSONObject *)jsonObject;
1564+
- (instancetype)initWithNullableObject:(TestProtocolDatabaseDummyObject *)nullableObject;
1565+
/* required */ @property (nonatomic, retain) TestProtocolDatabaseDummyObject *nullableObject;
1566+
/* optional */ @property (nonatomic, retain) TestProtocolDatabaseDummyObject *nullableOptionalObject;
1567+
@end
14731568
__attribute__((visibility ("default")))
14741569
@interface TestProtocolTestParameterBundle : RWIProtocolJSONObject
14751570
- (instancetype)initWithPayload:(NSDictionary<NSString *, id> *)payload;
@@ -1722,6 +1817,7 @@ using namespace Inspector;
17221817
+ (void)_parseParameterBundle:(TestProtocolDatabaseParameterBundle **)outValue fromPayload:(id)payload;
17231818
+ (void)_parseObjectWithPropertyNameConflicts:(TestProtocolDatabaseObjectWithPropertyNameConflicts **)outValue fromPayload:(id)payload;
17241819
+ (void)_parseDummyObject:(TestProtocolDatabaseDummyObject **)outValue fromPayload:(id)payload;
1820+
+ (void)_parseObjectWithNullableProperties:(TestProtocolDatabaseObjectWithNullableProperties **)outValue fromPayload:(id)payload;
17251821

17261822
@end
17271823
@interface TestProtocolTypeConversions (TestDomain)
@@ -1776,6 +1872,12 @@ using namespace Inspector;
17761872
*outValue = [[TestProtocolDatabaseDummyObject alloc] initWithPayload:payload];
17771873
}
17781874

1875+
+ (void)_parseObjectWithNullableProperties:(TestProtocolDatabaseObjectWithNullableProperties **)outValue fromPayload:(id)payload
1876+
{
1877+
THROW_EXCEPTION_FOR_BAD_TYPE(payload, [NSDictionary class]);
1878+
*outValue = [[TestProtocolDatabaseObjectWithNullableProperties alloc] initWithPayload:payload];
1879+
}
1880+
17791881
@end
17801882
@implementation TestProtocolTypeConversions (TestDomain)
17811883

@@ -2323,6 +2425,68 @@ using namespace Inspector;
23232425

23242426
@end
23252427

2428+
@implementation TestProtocolDatabaseObjectWithNullableProperties
2429+
2430+
- (instancetype)initWithPayload:(nonnull NSDictionary<NSString *, id> *)payload
2431+
{
2432+
if (!(self = [super init]))
2433+
return nil;
2434+
2435+
THROW_EXCEPTION_FOR_REQUIRED_PROPERTY(payload[@"nullableObject"], @"nullableObject");
2436+
self.nullableObject = [[TestProtocolDatabaseDummyObject alloc] initWithPayload:payload[@"nullableObject"]];
2437+
2438+
self.nullableOptionalObject = [[TestProtocolDatabaseDummyObject alloc] initWithPayload:payload[@"nullableOptionalObject"]];
2439+
2440+
return self;
2441+
}
2442+
- (instancetype)initWithProtocolObject:(RWIProtocolJSONObject *)object
2443+
{
2444+
if (!(self = [super initWithJSONObject:[object toJSONObject].get()]))
2445+
return nil;
2446+
2447+
return self;
2448+
}
2449+
2450+
- (instancetype)initWithNullableObject:(TestProtocolDatabaseDummyObject *)nullableObject
2451+
{
2452+
if (!(self = [super init]))
2453+
return nil;
2454+
2455+
THROW_EXCEPTION_FOR_REQUIRED_PROPERTY(nullableObject, @"nullableObject");
2456+
2457+
self.nullableObject = nullableObject;
2458+
2459+
return self;
2460+
}
2461+
2462+
- (void)setNullableObject:(TestProtocolDatabaseDummyObject *)nullableObject
2463+
{
2464+
[super setObject:nullableObject forKey:@"nullableObject"];
2465+
}
2466+
2467+
- (TestProtocolDatabaseDummyObject *)nullableObject
2468+
{
2469+
RWIProtocolJSONObject *object = [super objectForKey:@"nullableObject"];
2470+
if (!object)
2471+
return nil;
2472+
return [[TestProtocolDatabaseDummyObject alloc] initWithJSONObject:[[super objectForKey:@"nullableObject"] toJSONObject].get()];
2473+
}
2474+
2475+
- (void)setNullableOptionalObject:(TestProtocolDatabaseDummyObject *)nullableOptionalObject
2476+
{
2477+
[super setObject:nullableOptionalObject forKey:@"nullableOptionalObject"];
2478+
}
2479+
2480+
- (TestProtocolDatabaseDummyObject *)nullableOptionalObject
2481+
{
2482+
RWIProtocolJSONObject *object = [super objectForKey:@"nullableOptionalObject"];
2483+
if (!object)
2484+
return nil;
2485+
return [[TestProtocolDatabaseDummyObject alloc] initWithJSONObject:[[super objectForKey:@"nullableOptionalObject"] toJSONObject].get()];
2486+
}
2487+
2488+
@end
2489+
23262490
@implementation TestProtocolTestParameterBundle
23272491

23282492
- (instancetype)initWithPayload:(nonnull NSDictionary<NSString *, id> *)payload

‎Source/JavaScriptCore/inspector/scripts/tests/type-declaration-object-type.json‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,15 @@
7474
"id": "DummyObject",
7575
"description": "An open object that doesn't have any predefined fields.",
7676
"type": "object"
77+
},
78+
{
79+
"id": "ObjectWithNullableProperties",
80+
"description": "An object with properties that can be null.",
81+
"type": "object",
82+
"properties": [
83+
{ "name": "nullableObject", "$ref": "DummyObject", "nullable": true },
84+
{ "name": "nullableOptionalObject", "$ref": "DummyObject", "nullable": true, "optional": true }
85+
]
7786
}
7887
]
7988
},

0 commit comments

Comments
 (0)