Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -922,7 +922,7 @@ public List<PendingJavaScriptInvocation> dumpPendingJavaScriptInvocations() {
return Collections.emptyList();
}

Map<Boolean, List<PendingJavaScriptInvocation>> partition = getPendingJavaScriptInvocations()
Map<Boolean, List<PendingJavaScriptInvocation>> partition = streamPendingJavaScriptInvocations()
.collect(Collectors.partitioningBy(
invocation -> invocation.getOwner().isVisible()));

Expand Down Expand Up @@ -993,14 +993,31 @@ private void checkInvocationQueueLock() {
}

/**
* Gets the pending javascript invocations added with
* {@link #addJavaScriptInvocation(PendingJavaScriptInvocation)} after last
* {@link #dumpPendingJavaScriptInvocations()}.
* Gets the pending JavaScript invocations added with
* {@link #addJavaScriptInvocation(PendingJavaScriptInvocation)} after the
* last {@link #dumpPendingJavaScriptInvocations()}, leaving them in the
* queue.
* <p>
* Unlike {@link #dumpPendingJavaScriptInvocations()}, this does not mark
* anything as sent, so it lets something that drives the client side
* without a browser read the queue, act on the calls it simulates and
* cancel them with {@link PendingJavaScriptInvocation#cancelExecution()},
* while every other invocation stays queued. Canceling reports a failure to
* an error handler registered on the result of the call.
* <p>
* The invocations owned by invisible nodes, which a dump retains instead of
* sending, are included; check
* {@link PendingJavaScriptInvocation#getOwner()} to tell them apart.
* Canceled invocations are not included. The list is an unmodifiable
* snapshot, so the queue can be changed while it is iterated.
*
* @return the pending javascript invocations, never <code>null</code>
* @return the pending JavaScript invocations, never <code>null</code>
*/
// Non-private for testing purposes
Stream<PendingJavaScriptInvocation> getPendingJavaScriptInvocations() {
public List<PendingJavaScriptInvocation> getPendingJavaScriptInvocations() {
return streamPendingJavaScriptInvocations().toList();
}

private Stream<PendingJavaScriptInvocation> streamPendingJavaScriptInvocations() {
session.checkHasLock();
return pendingJsInvocations.stream()
.filter(invocation -> !invocation.isCanceled());
Expand All @@ -1016,7 +1033,7 @@ Stream<PendingJavaScriptInvocation> getPendingJavaScriptInvocations() {
* @since 24.4
*/
public boolean containsPendingJavascript(String containsFilter) {
return getPendingJavaScriptInvocations().anyMatch(js -> js
return streamPendingJavaScriptInvocations().anyMatch(js -> js
.getInvocation().getExpression().contains(containsFilter));
}

Expand All @@ -1041,7 +1058,7 @@ public boolean containsPendingJavascript(String containsFilter) {
*/
public boolean containsPendingJsCall(Class<?> definitionType,
String methodName) {
return getPendingJavaScriptInvocations()
return streamPendingJavaScriptInvocations()
.map(js -> js.getInvocation().getJsCall())
.anyMatch(call -> call != null
&& call.definitionType() == definitionType
Expand Down Expand Up @@ -1501,11 +1518,11 @@ private void warnForUnavailableBundledDependencies(
// absence is not a problem
jsDeps.addAll(dependencies.getJavaScripts().stream().filter(
js -> !FrontendDependencyUrlResolver.isRuntimeJavaScript(js))
.map(JavaScript::value).collect(Collectors.toList()));
.map(JavaScript::value).toList());
jsDeps.addAll(dependencies.getJsModules().stream().map(JsModule::value)
.filter(src -> !FrontendDependencyUrlResolver
.isRuntimeDependencyUrl(src))
.collect(Collectors.toList()));
.toList());

if (!jsDeps.isEmpty()) {
maybeWarnAboutDependencies(componentClass, jsDeps);
Expand Down Expand Up @@ -1814,7 +1831,7 @@ public Router getRouter() {
* <code>false</code> otherwise
*/
public boolean isDirty() {
return getStateTree().isDirty() || getPendingJavaScriptInvocations()
return getStateTree().isDirty() || streamPendingJavaScriptInvocations()
.anyMatch(invocation -> invocation.getOwner().isVisible());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ void addDynamicImport_dynamicDependencyIsAvaialbleViaGetPendingSendToClient() {
}

private long countPendingInvocations() {
return ui.getInternals().getPendingJavaScriptInvocations().count();
return ui.getInternals().getPendingJavaScriptInvocations().size();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -403,13 +403,13 @@ void dumpPendingJavaScriptInvocations_ownerDetached_onlyItsInvocationsDiscarded(

internals.dumpPendingJavaScriptInvocations();

assertEquals(3, internals.getPendingJavaScriptInvocations().count(),
assertEquals(3, internals.getPendingJavaScriptInvocations().size(),
"Invocations of invisible components should be retained");

detachedNode.setParent(null);

assertEquals(List.of(otherInvocation),
internals.getPendingJavaScriptInvocations().toList(),
internals.getPendingJavaScriptInvocations(),
"Only the invocations of the detached owner should be discarded");
assertEquals(List.of(otherNode),
List.copyOf(invocationOwners(internals)),
Expand Down Expand Up @@ -446,7 +446,32 @@ void dumpPendingJavaScriptInvocations_invocationCanceled_pendingListPurged() {

invocation.cancelExecution();

assertEquals(0, internals.getPendingJavaScriptInvocations().count());
assertEquals(0, internals.getPendingJavaScriptInvocations().size());
}

@Test
void getPendingJavaScriptInvocations_oneCanceledWhileReading_othersStillDumped() {
StateNode node = new StateNode(ElementData.class);
internals.getStateTree().getRootNode()
.getFeature(ElementChildrenList.class).add(0, node);

PendingJavaScriptInvocation canceled = new PendingJavaScriptInvocation(
node, new UIInternals.JavaScriptInvocation("1"));
PendingJavaScriptInvocation kept = new PendingJavaScriptInvocation(node,
new UIInternals.JavaScriptInvocation("2"));
PendingJavaScriptInvocation queuedOnCancel = new PendingJavaScriptInvocation(
node, new UIInternals.JavaScriptInvocation("3"));
canceled.then(value -> {
}, error -> internals.addJavaScriptInvocation(queuedOnCancel));
internals.addJavaScriptInvocation(canceled);
internals.addJavaScriptInvocation(kept);

internals.getPendingJavaScriptInvocations().stream()
.filter(invocation -> invocation == canceled)
.forEach(PendingJavaScriptInvocation::cancelExecution);

assertEquals(List.of(kept, queuedOnCancel),
internals.dumpPendingJavaScriptInvocations());
}

@Test
Expand Down Expand Up @@ -711,11 +736,12 @@ void setTitle_titleAndPendingJsInvocationSetsCorrectTitle() {
internals.setTitle("new title");
assertEquals("new title", internals.getTitle());

assertEquals(1, internals.getPendingJavaScriptInvocations().count(),
assertEquals(1, internals.getPendingJavaScriptInvocations().size(),
"one pending JavaScript invocation should exist");

var pendingJavaScriptInvocation = internals
.getPendingJavaScriptInvocations().findFirst().orElse(null);
.getPendingJavaScriptInvocations().stream().findFirst()
.orElse(null);
assertNotNull(pendingJavaScriptInvocation,
"pendingJavaScriptInvocation should not be null");
assertEquals("new title", pendingJavaScriptInvocation.getInvocation()
Expand Down