package org.reldb.dbrowser.ui.html; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import org.eclipse.jface.util.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.browser.Browser; import org.eclipse.swt.browser.ProgressAdapter; import org.eclipse.swt.browser.ProgressEvent; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; public class BrowserNative implements HtmlBrowser { private Browser browser; private Style style; private StringBuffer text = new StringBuffer(); private static class Message { public String s; private boolean scroll = false; public Message(String s) {this.s = s;} public Message() {this.s = null;} public Message(boolean scroll) {this.scroll = scroll; this.s = null;} public boolean isNull() {return this.s == null;} public boolean isScroll() {return scroll;} } private BlockingQueue<Message> messageQueue = new LinkedBlockingQueue<Message>(); private static String cleanForJavascriptInsertion(String s) { return s.replace("\\", "\\\\").replace("'", "\\'").replace("\"", "\\\"").replace("\n", ""); } @Override public boolean createWidget(Composite parent) { if (Util.isMac()) style = new Style(-3); else style = new Style(0); try { browser = new Browser(parent, SWT.BORDER); browser.setJavascriptEnabled(true); browser.addProgressListener(new ProgressAdapter() { @Override public void completed(ProgressEvent event) { pumpQueue(); } }); } catch (Throwable t) { System.out.println("BrowserNative: Native browser not available: " + t); return false; } clear(); return true; } private boolean busy = false; private synchronized void pumpQueue() { busy = false; while (!messageQueue.isEmpty()) { Message message = messageQueue.poll(); if (message.isScroll()) scrollToBottom(); else if (message.isNull()) clear(); else appendHtml(message.s); } } private synchronized boolean enqueueClear() { if (busy) { try { messageQueue.put(new Message()); } catch (InterruptedException e) { } return true; } busy = true; return false; } private synchronized boolean enqueue(Message m) { if (busy) { try { messageQueue.put(m); } catch (InterruptedException e) { } return true; } return false; } @Override public void clear() { if (enqueueClear()) return; text = new StringBuffer(); browser.setText(style.getEmptyHTMLDocument()); } @Override public void appendHtml(String s) { if (enqueue(new Message(s))) return; browser.execute("document.getElementsByTagName('body')[0].innerHTML += '" + cleanForJavascriptInsertion(s) + "'"); text.append(s); } @Override public void scrollToBottom() { if (enqueue(new Message(true))) return; browser.execute("window.scrollTo(0, document.body.scrollHeight)"); } @Override public Control getWidget() { return browser; } @Override public String getText() { return style.getHTMLDocument(text.toString()); } @Override public String getSelectedText() { return null; } @Override public boolean isSelectedTextSupported() { return false; } @Override public Style getStyle() { return style; } @Override public void dispose() { browser.dispose(); } @Override public void setContent(String content) { text = new StringBuffer(content); browser.setText(style.getHTMLDocument(content)); } @Override public String getContent() { return text.toString(); } }