
Recall what
MozSwing is . MozSwing is the only adequate (in my opinion) free and cross-platform solution for embedding a browser as a component of a swing. But, sadly enough to admit it, the project died at that stage when there were too many mistakes in it. These errors, as well as miscalculations in architecture, do not allow using this solution “as is” for your projects. With an overwhelming desire to fix it, I took up the job and did something for me.
(An article about Mozilla browser integration as a Swing component.)
Why is bad MozSwingNo, no, don’t think that I think MozSwing sucks, this is a huge job for which you need to thank you so much (my work is several times less), but MozSwing doesn’t like me and that's why:
- Support for xulrunner `only version 1.9 (which, by the way, does not support Russian letters in transit for windows-systems) when there is version 1.9.2.
- A huge number of brain-breaking static classes and methods, which is why MozSwing appears before us an indestructible rock. In order to change anything in it, you have to change the bytecode of the already loaded classes, because inheritance in the case of MozSwing does not work.
- The inability to explicitly override nsIWindowCreator (the class implementing this interface is creating windows for the browser), which makes it necessary to invent crutches to create browsers as tabs, since MozSwing prefers to open new pages in new windows. For example, this is how I replace nsIWindowCreator after initialization:
nsIWindowWatcher winWatcher = XPCOMUtils.getService( "@mozilla.org/embedcomp/window-watcher;1" , nsIWindowWatcher. class ); //$NON-NLS-1$
winWatcher.setWindowCreator(wndCreator);
* This source code was highlighted with Source Code Highlighter .
However, the error is potentially hidden here, because MozSwing loves static classes, methods, and internally refers to its nsIWindowCreator. - Some, let's say, very dubious solutions in the code (MozSwing source code: just in case a 3-window pool is created, so that you can take a window from this pool to embed a browser into it):
/**
* When mozilla does a callback to createChromeWindow()
* we need to create a swing window. But doing this on
* Swing thread using invokeAndWait sometimes ends
* with deadlock in AWT.
* Therefore we keep a list of precreated windows
* in case we will need them.
*/
private List <IMozillaWindow> precreatedWins = new LinkedList<IMozillaWindow>();
public void ensurePrecreatedWindows() {
ensurePrecreatedWindows(3);
}
public void ensurePrecreatedWindows( int winNum) {
assert !isMozillaThread(); //has to be called from swing
while (precreatedWins.size()<winNum) {
if (winFactory== null ) return ;
IMozillaWindow w = winFactory.create( false );
if (!(w instanceof Component)) return ;
// w is instance of something we can work with
precreatedWins.add(w);
Component c = (Component)w;
c.addNotify();
}
}
* This source code was highlighted with Source Code Highlighter .
The number three is especially scary. It kind of hints us that the three windows should be enough in principle ... Probably ... - To enable support for contextual commands (shortcut menu), some MozSwing'a methods have to be changed. For example:
@SuppressWarnings( "deprecation" )
public static void replaceChromeAdapterMethod() {
try {
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool. get ( "org.mozilla.browser.impl.ChromeAdapter" );
CtMethod ctMethod = ctClass.getMethod( "queryInterface" , "(Ljava/lang/String;)Lorg/mozilla/interfaces/nsISupports;" );
ctMethod.setBody( "{ return ru.redstonegroup.geo.gui.components.browser.impl.QueryInterfaceImpl.getInstance().queryInterface(this, $1); }" );
ctClass.toClass(QueryInterfaceImpl. class .getClassLoader());
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
* This source code was highlighted with Source Code Highlighter .
- Constantly drops Sun JVM on some linux systems (ubuntu, openSUSE).
- Confusion of sources (by the way, rather it is connected with the complexity of the XPCOM technology itself).
- No integration with maven.
- Difficult to integrate with IoC container.
- It is not possible to create a window without any ryushechek, they can only be hidden.
')
JbrowserOf course, I have no illusions - my decision is not ideal: since it is based on MozSwing, it took many of his illnesses. If you like, JBrowser is my rethinking of MozSwing ʻand much more suitable for real-world systems. Well, at least, I like my API many times more (although you may not like it at all).
The main problem when meeting MozSwing was that there was no single entry point in it - creating a browser was done as a simple component creation (something like new MozillaWindow, I'm sorry, I don’t remember exactly). Yes, it is in some sense convenient, as long as you do not need anything more than creating just a browser window, but how to configure the browser being created? One option is to inherit from MozSwing-components, climb inside and dig-dig-dig ...
Immediately I was not clear: how to change the proxy server settings for the browser? After some time, it turned out that there is a corresponding MozillaConfig class with a static setProxy method (or something like that). Oh my God, I would never have known if I hadn't opened the source. In general, for me it is all not obvious.
Therefore, JBrowser is a kind of opposite (currently not complete) MozSwing in terms of design. JBrowser has an entry point - the
BrowserManager interface. This is the topmost level of xulrunner and swingʻa integration. The class implementing the interface performs all the initialization, as it prepares the ground for further work. In addition to initialization, the implementing class is obliged to provide you on demand with a certain implementation of the
BrowserConfig interface, which allows you to adjust the policy of all browsers (enable / disable images, proxies, etc.) Just what I wanted.
In contrast to the upper level of integration, JBrowser has the lowest level of integration, the so-called “component browser”. Any browser component implements the
JBrowserComponent interface. This interface is a composite that combines the functionality of a Swing component and implements a
browser interface.
/**
* Swing // Browser embedded in swing component
* @author caiiiycuk
*/
public interface JBrowserComponent<T extends Component> extends DisplayableComponent, Browser, NativeBrowser {
/**
* @return See {@link java.awt.Component}
*/
T getComponent();
...
}
* This source code was highlighted with Source Code Highlighter .
There is
JBrowserCanvas - the basic implementation of this interface. This is nothing more than a Swing-Canvas component with a browser built into it. Other implementations of the browser component almost always wrap JBrowserCanvas (delegate calls to it). For example, another browser component
JBrowserFrame (browser and JFrame) does this.
Between these two opposites, there is another link that unites everything into a single whole - this is a factory-layer (factorial-layer). After creating the top integration layer based on BrowserManager, numerous factors of browser components can be created that implement the
ComponentFactory interface. Normally, when an application contains several such factors. Properly configured factor through its methods creates specific implementations of browser components. Suppose I use the following factors in my application: JFrameBrowserFactory (creates a browser as a new window), JTabbedBrowserFactory (creates a browser as a new tab). Thanks to this scheme, it becomes possible to easily solve the problem of customization of browser components you create.
So, here's the whole chain of working with JBrowser: create a BrowserManager (by the way, you can / should use the
builder for this), create at least one factor for your browser component and finally, create a browser using factor. This is the simplest way to work with JBrowser:
public class GettingStartedSnippet {
public static void main( String [] args) {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(( int ) (screenSize.getWidth() * 0.75f),
( int ) (screenSize.getHeight() * 0.75f));
frame.setLocationRelativeTo( null );
BrowserManager browserManager =
new JBrowserBuilder().buildBrowserManager();
JComponentFactory<Canvas> canvasFactory = browserManager.getComponentFactory(JBrowserCanvas. class );
JBrowserComponent<?> browser = canvasFactory.createBrowser();
frame.getContentPane().add(browser.getComponent());
frame.setVisible( true );
browser.setUrl( "http://code.google.com/p/jbrowser/" );
}
}
* This source code was highlighted with Source Code Highlighter .
Why factor? I think (maybe I'm wrong) that it is very convenient. For example, I register my factors in an IoC container and can easily access them from almost any part of the application, so I can at least make a button in the most recent menu that creates a new browser tab for me.
The advantage of this architecture is that you can rewrite any of these three levels, and still get a working system efficiently.
Features that were not in MozSwingIn addition to the complete processing of interfaces, there are some more features added:
- It became possible to easily get the favIcon of the open page (browser.getFavIcon ()).
- It became possible to easily build in the context menu.
- Full integration with maven. In this regard, JBrowser is easy to connect and easy to assemble for your target system (by changing the profile in pom.xml). At the moment, everything supported by MozSwing is supported - win, linux, solaris, mac.
Project links:
Project
JBrowser (stable)Project
with many examples (dev)How to get started quickly with
JBrowser under EclipseWhat could not be achievedUnfortunately, while Xulrunner 1.9.2 is not supported, however, there is a reserve, and the light at the end of the tunnel is visible.
JwebpaneI hope he will come out someday and will save the world. Really looking forward to this project.