StyleInjector chokes on startup

81 views
Skip to first unread message

Gordan Krešić

unread,
Jun 18, 2020, 2:25:48 PM6/18/20
to google-we...@googlegroups.com
Probably unrelated with StyleInjector but with Scheduler.scheduleFinally,
but I'll try to explain:

Imagine class that defines ClientBundle as follows:

public class Foo {

public static interface Resources extends ClientBundle {
static final Resources $ = GWT.create(Resources.class);
@Source("Foo.gss")
Foo.Style style();
}
static {
Resources.$.style().ensureInjected();
}

@CssResource.ImportedWithPrefix("Foo")
public static interface Style extends CssResource {
String foo();
}

// ...

}

This class is GUI element that gets initialized through Activities/Places
framework, if that makes any difference.

Problem is, given style is NOT injected on app startup, but IT GETS INJECTED
if a do something as simple as:

Scheduler.get().scheduleFixedDelay(() -> {
return false;
}, 0);

in Foo's contructor.

Since StyleInjector injects styles using:

Scheduler.get().scheduleFinally

it looks like Scheduler doesn't recognize switch from GWT to browser event
loop if scheduleFinally is called sufficiently early during app startup
(like in static initializer of a class).

GWT 2.9.

Like I said, I've found a workaround (albeit a *very* dirty one), but
shouldn't this be treated as a bug in GWT?

-gkresic.

Gordan Krešić

unread,
Jun 18, 2020, 2:33:06 PM6/18/20
to google-we...@googlegroups.com
On 18. 06. 2020. 20:25, Gordan Krešić wrote:
> Probably unrelated with StyleInjector but with Scheduler.scheduleFinally

Ok, I've put a most basic repro case to prove that this is a Scheduler issue:

public class Foo {

static {
Scheduler.get().scheduleFinally(() -> GWT.log("Finally!"));
}

}

Now initialite Foo on startup (in EntryPoint.onModuleLoad() for example),
but "Finally!" will be printed only *after* first event loop, like described:

Scheduler.get().scheduleFixedDelay(() -> {
return false;
}, 0);

-gkresic.

Thomas Broyer

unread,
Jun 19, 2020, 3:42:33 AM6/19/20
to GWT Users
Yes, we can probably consider that a bug in GWT.
I'd also call this pattern of doing real work in a static initializer a code smell: http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/
While still a flaw considering the above link, it's however a common practice to call `ensureInjected()` from the class constructor (ideally, you'd rather call it in a lifecycle method, such as Activity#start, or Widget#onAttach); and that would likely fix your issue here.

Gordan Krešić

unread,
Jun 21, 2020, 7:13:54 AM6/21/20
to google-we...@googlegroups.com
I'll take the blame for code design, no questions about that :)

Do you think it's worth filing a bug report or this is too much of an edge
case in a code that is going away soon anyway? Note that I didn't test
against org.gwtproject.core.client.Scheduler only
com.google.gwt.core.client.Scheduler (and they do differ, at least regarding
scheduleFinally).
> --
> You received this message because you are subscribed to the Google Groups
> "GWT Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to google-web-tool...@googlegroups.com
> <mailto:google-web-tool...@googlegroups.com>.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/google-web-toolkit/1ecd7663-8a5a-449e-aef5-4008d3735433o%40googlegroups.com
> <https://groups.google.com/d/msgid/google-web-toolkit/1ecd7663-8a5a-449e-aef5-4008d3735433o%40googlegroups.com?utm_medium=email&utm_source=footer>.

Colin Alworth

unread,
Jun 24, 2020, 1:16:17 PM6/24/20
to GWT Users
Can you clarify the browser event that you are working with? If it is something through JsInterop, then this is expected, since JSNI style calls into Java from JS require $entry, but jsinterop provides no such mechanism. If it is an event from a GWT Widget, then that would go through JSNI, and this should not happen.

As such, https://github.com/gwtproject/gwt-core has an updated Scheduler which is compatible with JsInterop calls - scheduleFinally is now implemented as a microtask, so that it will always precede any subsequent event, no matter how the event originates. The gwt-core project also contains an updated StyleInjector, and a corresponding gwt-resources module (not yet moved to github.com/gwtproject) will provide an updated ClientBundle implementation.

Note that as we're committed to maintaining legacy dev mode in the GWT 2.x branch, we cannot replace the com.google.gwt.core.client.Scheduler wiring with jsinterop.

Gordan Krešić

unread,
Jun 24, 2020, 6:09:43 PM6/24/20
to google-we...@googlegroups.com
I'm willing to provide more info, but TBH I'm not sure what are you asking.

You can see below code example that shows the problem. One "solution" that
triggers scheduleFinally to run is to invoke another scheduleFixedDelay -
that event is for sure not related to widgets, but I'm not sure if it is
related to JsInterop.

OTOH, I can confirm that handling mouse click on button widget DO triggers
scheduleFinally, too.

Like I've said, I've noticed new Sheduler (gwt-core) and all the differences
that it brings when compared to "old" Scheduler (gwt-client), but I didn't
test this new Scheduler.

So, "old" Scheduler obviously has a bug or at least very unexpected
behaviour. I agree that fixing this is not worth an effort if it's not a
very simple fix (which I assume it isn't), but I offered to document this
misbehaviour in form of bug report so that anyone else can at least find
this workaround if needed (I didn't found any reference to it so far).
> > email to google-web-tool...@googlegroups.com
> <mailto:google-web-toolkit%2Bunsu...@googlegroups.com>
> > <mailto:google-web-tool...@googlegroups.com
> <mailto:google-web-toolkit%2Bunsu...@googlegroups.com>>.
> <https://groups.google.com/d/msgid/google-web-toolkit/1ecd7663-8a5a-449e-aef5-4008d3735433o%40googlegroups.com?utm_medium=email&utm_source=footer
> <https://groups.google.com/d/msgid/google-web-toolkit/1ecd7663-8a5a-449e-aef5-4008d3735433o%40googlegroups.com?utm_medium=email&utm_source=footer>>.
>
>
> --
> You received this message because you are subscribed to the Google Groups
> "GWT Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to google-web-tool...@googlegroups.com
> <mailto:google-web-tool...@googlegroups.com>.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/google-web-toolkit/0d761c71-bb99-44b2-9581-9aee45cff40bo%40googlegroups.com
> <https://groups.google.com/d/msgid/google-web-toolkit/0d761c71-bb99-44b2-9581-9aee45cff40bo%40googlegroups.com?utm_medium=email&utm_source=footer>.

Colin Alworth

unread,
Jun 24, 2020, 9:27:05 PM6/24/20
to google-we...@googlegroups.com
Sorry, I phrased that poorly.

Old scheduler with "old events" (i.e. from JSNI that correctly uses $entry, buggy JSNI forgetting $entry doesnt count) will work properly, and old/new events with new scheduler will work too. The problem is in old scheduler with "new" events - anything using plain jsinterop and avoiding $entry on its way into JS from Java. There is no fix we can do in GWT for it, old Scheduler assumes always that Entry owns all chances for JS to call Java, and so correctly wires up GWT's side of the event loop as well as uncaught exception handling.

Another option than triggering a fixed delay is to decide when the finally should be invoked, and call SchedulerImpl.INSTANCE.flushFinallyCommands(), that will trigger them all to take place right away. This is what $entry does, after your event is entirely handled in Java.

As a workaround, it may be possible to extend com.google.gwt.core.client.SchedulerImpl and splice in the Promise wiring found in gwt-core. Three bad effects off the top of my head:
* scheduleEntry won't work (deprecated in gwt-core, there is no way to do this without requiring $entry or the like)
* legacy dev mode won't work (it _might_ be possible to work around this, but for this last issue...)
* any browser without Promise support won't work, or will have a still-wrong hack in it (without promises, you need another microtask implementation, and of the browsers that don't have Promise, most don't have MutationObserver either which is the go-to hack I've found. Are there other hacks?) - you may end up with the "finally" happening after the next event or after some other timer instead of before it.

Filing it sounds good - we can offer the tradeoffs, and if someone has the need and the bandwidth to resolve this problem fully, we probably can get most of the way there, provided they can accept the caveats and test heavily.

--
Colin Alworth
co...@colinalworth.com
> You received this message because you are subscribed to a topic in the
> Google Groups "GWT Users" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/google-web-toolkit/TyJAVyCkir4/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> google-web-tool...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/google-web-toolkit/060994ea-9eef-9ecb-fa96-a54cceeac3d1%40steatoda.com.
>
Reply all
Reply to author
Forward
0 new messages