Extending Eclipse: live help actions in the browser control

November 11th, 2008

The previous entry has talked about the implementation details of the Eclipse Help view, and how it is using an embedded HTTP server powered by Jetty to serve dynamic content from a number of sources. Once you have such a custom server in place, you can embed a Browser control in any view / editor and call its setUrl method to show the required content. The links in the browser control will be handled in the same way as in the usual browser control – relative links will be served by the same HTTP server, and absolute links can point to content on the intranet or even external internet pages. But what about the live help actions?

The org.eclipse.help.ILiveHelpAction interface defines an entry point from Eclipse Help view “back” into Eclipse itself. If you include the PLUGINS_ROOT/org.eclipse.help/livehelp.js in your HTML document, you can attach a javascript:liveAction handler on the links in your document. This handler gets three parameters:

  1. ID of the plugin that contains the class defined by the second parameter
  2. Fully qualified class name that must implement the ILiveHelpAction interface
  3. Possibly empty parameter passed to the setInitializationString of the live help action instance

When such a link is clicked, the setInitializationString() and run() methods of the specified class are called. How does this work, and how can this be extended to work outside the Help view in any embedded browser control?

Here is the flow of execution in Eclipse 3.4 (all of these details are internal and subject to change in the future versions).

  • The Help view is a collection of HTML pages. These are served from the org.eclipse.help.webapp plugin.
  • One of the pages has an IFrame with 0*0 dimension named liveHelpFrame.
  • The implementation of liveAction JavaScript method in the livehelp.js locates that frame and sets its location to the URL that encodes the three parameters (plugin ID, class name and argument string)
  • A special livehelp servlet registered on the Jetty server gets this request. This servlet is implemented in org.eclipse.help.internal.webapp.servlet.LiveHelpServlet class.
  • The doGet method checks that the embedded web server is running, and that the first two parameters are not null.
  • The helper BaseHelpSystem.runLiveHelp locates the plugin bundle and loads the specified class. An instance of this class is created (the implicit assumption is that there is a public no-arg constructor).
  • If specified, the argument string is passed to the setInitializationString of the created instance.
  • A daemon thread based on this instance is created and started. Note that if your live help action needs access to the UI layer (to display a dialog, for example), the logic in the run method needs to be wrapped in Display.runAsync call.

rent a car bulgaria
Note that the above flow does not mention the actual contents of the 0*0 IFrame being used in the process. The whole reason for this frame’s existence is to make a call to the live help servlet. The LiveHelpServlet.doGet does not make anything with the resp object.

As can be seen from this flow, including the livehelp.js and putting a javascript:liveAction on the HTML anchor is not enough. The implementation of the JavaScript liveAction method makes certain checks to ensure that the browser is the one from the Help view. As such, if you click on this link in a browser control placed in another view, it will not result in invoking the specified action.

Until this functionality is supported by Eclipse in a published way, we can mimic this flow in a custom view / editor. Here are the basic steps – assuming that you have a browser control and a Jetty server / servlet already configured.

  • Add two more servlets. The first will serve the “core” content, such as a custom livehelp.js, the initial blank content of the invisible IFrame and possibly a custom CSS. The second will handle the live help requests.
  • Modify your existing servlet (that serves the usual HTML pages) to inject two pieces into every HTML file that it serves.
    • The first piece is a HEAD tag to include the livehelp.js from the new core servlet. This piece can also include a link to the custom global CSS file.
    • The second piece is a 0*0 IFrame named liveHelpFrame with initial content set to a blank page served by the core servlet. It is very important to obey the cross-scripting limitations placed by the browsers to make sure that a javascript handler on the main content can change the location of this IFrame.
  • Implement the livehelp.js based on the core Eclipse script. You can simplify the implementation since you know exactly where to look for that IFrame. Get the URL of the window, strip away the trailing parts and replace them to point to your live help servlet. Add plugin ID, class name and the argument to the resulting URL to make sure that these are passed to the servlet. Finally, set the complete URL to be the location of the invisible IFrame. This will result in a call to the servlet, but leave the visible browser content unchanged.
  • The implementation of the core servlet is very simple, fetching the required content from the bundled files (just like the servlet that is serving the rest of your files).
  • The implementation of the live help servlet is the same as the core Eclipse one. Parse the parameters, make sure that the plugin ID and class name are present, load the class, create an instance of that class and then run its run method on a new daemon thread.

Congratulations – you have your own browser that is able to display a rich collection of interlinked documents and invoke actions defined in your plugin classes, interacting with other parts of your plugins / application.