Using JCEF in IntelliJ Plugins

Having problems with building or using the JCEF Java binding? Ask your questions here.

Using JCEF in IntelliJ Plugins

Postby BFergerson » Sat May 25, 2019 10:02 pm

I would like to open by saying I'm not sure if this is a CEF/JCEF related issue or an IntelliJ issue, but it is my hope that this is a good place for figuring that out.

I'm trying to create an IntelliJ plugin using JCEF (version: 73.1.11.215) to display the UI. I'm able to get JCEF working on Windows and Linux when I run them as standalone applications.
It also works when being run in an IntelliJ plugin on Windows. The issue I'm having occurs when I try running JCEF as an IntelliJ plugin on my Ubuntu 18.04 OS.

This is the code I'm using to render the JFrame:
Code: Select all
CefSettings DEFAULT_SETTINGS = new CefSettings();
DEFAULT_SETTINGS.windowless_rendering_enabled = false;

CefApp cefApp = CefApp.getInstance(DEFAULT_SETTINGS);
CefClient client = cefApp.createClient();
CefBrowser browser = client.createBrowser("https://google.com", false, false);
Component browserUI = browser.getUIComponent();
JFrame mainFrame = new JFrame();
mainFrame.getContentPane().add(browserUI, BorderLayout.CENTER);
mainFrame.setSize(1000, 600);
mainFrame.setVisible(true);
browser.setFocus(true);
mainFrame.addWindowListener(new WindowAdapter() {
    @Override
    public void windowClosing(WindowEvent windowEvent) {
        mainFrame.dispose();
        cefApp.dispose();
    }
});


Running this code standalone shows:
Image

Running the same code as an IntelliJ plugin (by extending StartupActivity.runActivity) show this:
Image

I was able to track a discrepancy of their execution down to CefBrowserWr.createBrowserIfRequired. That method calls getNativeRef("CefBrowser") to determine if it should create a new browser or not and when running JCEF under IntelliJ it will always result in 0 which causes an infinite loop of it trying to create a browser.

Since the issue appeared to be in the native code, I downloaded and built the debug versions of CEF and JCEF and tracked another discrepancy between the successful and unsuccessful execution down to the unsuccessful one never calling CefBrowserHostImpl::CreateInternal (browser_host_impl.cc). It appears this method needs to execute for LifeSpanHandler::OnAfterCreated (life_span_handler.cpp) to be called which will set the necessary value for getNativeRef("CefBrowser") to return a valid reference and the infinite loop fixed.

This is where I have been stuck for a while. I'm able to see that CefBrowserHostImpl::CreateInternal is triggered by CefBrowserHost::CreateBrowser (browser_host_impl.cc) and I'm able to confirm that code gets executed during the unsuccessful run and even returns true, however the required function never executes. This should be getting executed because CefBrowserHost::CreateBrowser contains the following code which should trigger it:
Code: Select all
// Create the browser on the UI thread.
CreateBrowserHelper* helper = new CreateBrowserHelper(
    windowInfo, client, url, settings, request_context);
CEF_POST_TASK(CEF_UIT, base::BindOnce(CreateBrowserWithHelper, helper));


Because of this code, I started to think I was dealing with a threading issue. This was further strengthened by the fact that modifying Context::PreInitialize (context.cpp) and setting settings.multi_threaded_message_loop to false causes even the standalone JCEF application to exhibit the IntelliJ infinite create browser loop. This code also has a rather large comment mentioning "XInitThreads" but unfortunately, that wasn't a fix for me. I compiled the .so mentioned in the StackOverflow link and I called that during a breakpoint on Thread.run when IntelliJ itself is loading; the same issue.

Keeping a close eye on the threads which are used to trigger things I'm able to notice that on the standalone application CefMainDelegate::RunProcess is called by a thread called "CefUIThread" (stack trace), whereas when it's run under an IntelliJ plugin the thread is simply called "java" (stack trace).

It's because of all of these reasons that I believe I'm dealing with some kind of threading issue but it's not obvious to me what kind. Is anyone able to recommend better ways to debug this issue? I know CEF can work with IntelliJ because JxBrowser uses it and I was able to successfully evaluate it an IntelliJ plugin on Ubuntu. Using JCEF I almost have a functional open-source JxBrowser alternative (called Journey), at least for my needs. I just need to figure out what's going on here. I would really appreciate any pushes in the right direction, thanks.
BFergerson
Newbie
 
Posts: 3
Joined: Sat May 25, 2019 8:09 pm

Re: Using JCEF in IntelliJ Plugins

Postby magreenblatt » Sun May 26, 2019 3:38 am

This could be related to https://bitbucket.org/chromiumembedded/cef/issues/2512. Can you call CefApp.startup() early enough? If not, you may need to use off-screen rendering.
magreenblatt
Site Admin
 
Posts: 9854
Joined: Fri May 29, 2009 6:57 pm

Re: Using JCEF in IntelliJ Plugins

Postby BFergerson » Mon May 27, 2019 9:36 pm

Thanks for the response. I tried the off-screen rendering and it seems to cause IntelliJ to instant crash.

The following code works
Code: Select all
mkdir jcef-test && cd jcef-test
wget https://github.com/CodeBrig/Journey/releases/download/0.1.1-online/journey-browser-0.1.1.jar
wget https://github.com/CodeBrig/Journey/releases/download/0.1.1-online/jcef-distrib-linux64.zip
unzip jcef-distrib-linux64.zip

export LD_LIBRARY_PATH=/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64:$(pwd)/linux64/bin/lib/linux64
java -cp "journey-browser-0.1.1.jar:linux64/bin/*" tests.detailed.MainFrame --off-screen-rendering-enabled

This properly shows Google with the JCEF detailed frame in off-screen rendering mode.

When I run this command (from the same terminal session) IntelliJ instantly crashes:
Code: Select all
git clone https://github.com/BFergerson/jcef-intellij-plugin-test plugin-test && cd plugin-test
./gradlew runIde

This project has the same code I posted before but now uses off-screen rendering (and also calls CefApp.startup()).


If I replace all the native files with their debug counterparts I get this output:
Code: Select all
initialize on Thread[AWT-EventQueue-1 2019.2#IC-192.4205.45 IDEA, eap:true, os:Linux 4.15.0-50-generic, java-version:Oracle Corporation 1.8.0_212-8u212-b03-0ubuntu1.18.04.1-b03,6,Idea Thread Group] with library path /tmp/journey-0.1.1
Received signal 11 SEGV_MAPERR 00000000000f
#0 0x7f149b604dc9 base::debug::CollectStackTrace()
#1 0x7f149b567fe3 base::debug::StackTrace::StackTrace()
#2 0x7f149b604951 base::debug::(anonymous namespace)::StackDumpSignalHandler()
#3 0x7f1656f17f20 <unknown>
#4 0x7f1642dce134 <unknown>
  r8: 000000000000005c  r9: 00007f1492df6028 r10: 0000000000000000 r11: 0000000000000000
 r12: 0000000000000000 r13: 00007f1492df5eb0 r14: 00007f1492df5da0 r15: 00007f147c036000
  di: 00007f147c036000  si: 0000000000000000  bp: 00000000f5cc2550  bx: 00007f163e2e2688
  dx: 00007f1656878678  ax: 0000000000000000  cx: 00007f147c0370a0  sp: 00007f1492df5dc0
  ip: 00007f1642dce134 efl: 0000000000010246 cgf: 002b000000000033 erf: 0000000000000004
 trp: 000000000000000e msk: 0000000000000004 cr2: 000000000000000f
[end of stack trace]
Calling _exit(1). Core file will not be generated.

initialize on Thread[AWT-EventQueue-1 2019.2#IC-192.4205.45 IDEA, eap:true, os:Linux 4.15.0-50-generic, java-version:Oracle Corporation 1.8.0_212-8u212-b03-0ubuntu1.18.04.1-b03,6,Idea Thread Group] with library path /tmp/journey-0.1.1
Received signal 11 SEGV_ACCERR 7f5f92ca0c80
#0 0x7f5dd6e04dc9 base::debug::CollectStackTrace()
#1 0x7f5dd6d67fe3 base::debug::StackTrace::StackTrace()
#2 0x7f5dd6e04951 base::debug::(anonymous namespace)::StackDumpSignalHandler()
#3 0x7f5f924bbf20 <unknown>
#4 0x7f5f9153234c <unknown>
#5 0x7f5f91534eb0 <unknown>
#6 0x7f5f918d1ab1 <unknown>
#7 0x7f5f918d2398 <unknown>
#8 0x7f5f918d292d <unknown>
#9 0x7f5f91579872 <unknown>
#10 0x7f5f9157b12b JVM_DefineClassWithSource
#11 0x7f5f8bddfa05 Java_java_lang_ClassLoader_defineClass1
#12 0x7f5f7c855b0b <unknown>
  r8: 00007f5ed43ac800  r9: 00007f5f797cc528 r10: 00007f5ed43ac800 r11: 0000000000000028
 r12: 00007f5f2b56a430 r13: 00007f5f00214df8 r14: 00007f5f2b56a3f0 r15: 00007f5e541dd540
  di: 00007f5ed43ac800  si: 00007f5f2b56a3f0  bp: 00007f5f2b56a3a0  bx: 00007f5ed43ac800
  dx: 00007f5f91e1c678  ax: 00007f5f92ca0c80  cx: 00007f5f91349870  sp: 00007f5f2b56a370
  ip: 00007f5f9153234c efl: 0000000000010202 cgf: 002b000000000033 erf: 0000000000000007
 trp: 000000000000000e msk: 0000000000000004 cr2: 00007f5f92ca0c80
[end of stack trace]
Calling _exit(1). Core file will not be generated.

While trying to capture the errors above the off-screen rendered mode actually worked a few times. I ran ./gradlew runIde around 10 times and twice it showed up correctly and didn't crash. Each of the other times was instant crash.

One of the times while closing the IDE (on a successful run) it spit out the following error:
Code: Select all
xception in thread "AWT-EventQueue-1 2019.2#IC-192.4205.45 IDEA, eap:true, os:Linux 4.15.0-50-generic, java-version:Oracle Corporation 1.8.0_212-8u212-b03-0ubuntu1.18.04.1-b03" java.lang.NullPointerExceptionrunIde
        at org.cef.browser.CefBrowserOsr.onPaint(CefBrowserOsr.java:242)
        at org.cef.CefClient.onPaint(CefClient.java:674)
        at org.cef.CefApp.N_DoMessageLoopWork(Native Method)
        at org.cef.CefApp.access$900(CefApp.java:24)
        at org.cef.CefApp$6.run(CefApp.java:496)
        at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
        at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
        at java.awt.EventQueue.access$500(EventQueue.java:97)
        at java.awt.EventQueue$3.run(EventQueue.java:709)
        at java.awt.EventQueue$3.run(EventQueue.java:703)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
        at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:749)
        at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:698)
        at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:386)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:205)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

I'm not sure it's related to the crashing issue with IntelliJ though.

Could you tell me how I could debug this better to get what's causing it to crash? I can place a breakpoint at stack_trace_posix.cc?
When I do this I don't see how to output the exception. My background is Java. What I believe I'm looking for is an error with a stack trace equivalent.
BFergerson
Newbie
 
Posts: 3
Joined: Sat May 25, 2019 8:09 pm

Re: Using JCEF in IntelliJ Plugins

Postby mochalovilia » Tue Jun 11, 2019 3:56 am

Hi BFergerson,

I'm also trying to create an IntelliJ plugin using JCEF (cef_binary_73.1.11+ge6986dc+chromium-73.0.3683.75_linux64). Just like you I'm able to get JCEF working on Windows and Linux when I run them as standalone applications.
It also works when being run in an IntelliJ plugin on Windows. But doesn't work on IntelliJ 2018.03 plugin on Ubuntu 16 OS.

I have different error stack trace though:
Code: Select all
java.lang.UnsatisfiedLinkError: org.cef.CefApp.N_PreInitialize()Z
   at org.cef.CefApp.N_PreInitialize(Native Method)
   at org.cef.CefApp.access$000(CefApp.java:24)
   at org.cef.CefApp$1.run(CefApp.java:162)
   at org.cef.CefApp.<init>(CefApp.java:167)
   at org.cef.CefApp.getInstance(CefApp.java:220)
   at org.cef.CefApp.getInstance(CefApp.java:207)


I am quite new both to Java and C++ and not totally sure how to debug. BFergerson, if you find solution please update this post, please.
mochalovilia
Newbie
 
Posts: 1
Joined: Tue Jun 11, 2019 3:24 am

Re: Using JCEF in IntelliJ Plugins

Postby BFergerson » Sat Jul 13, 2019 2:27 pm

Hey mochalovilia,

Please try using the latest version of: https://github.com/CodeBrig/Journey
More specifically: https://github.com/CodeBrig/Journey/issues/3#issuecomment-511147185

In your post, you mention you're having an issue with IntelliJ 2018.03. I've tested this specific version with Journey and it works in an IntelliJ plugin on Linux.
Even though I didn't mention it I was using IntelliJ 2019 in my original post so it looks like my issues are specifically with IntelliJ 2019+ versions.

Let me know if that helps.
BFergerson
Newbie
 
Posts: 3
Joined: Sat May 25, 2019 8:09 pm


Return to JCEF Forum

Who is online

Users browsing this forum: No registered users and 2 guests