LiveHTML WebKitDebugging protocol through CLI that supports --livehtml command
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Sat, 08 Jun 2013 12:09:10 +0200
changeset 1169c19ac78b940e
parent 1168 3e510c4e3768
child 1170 ebedd84fba80
child 1173 aa82f9de8e33
LiveHTML WebKitDebugging protocol through CLI that supports --livehtml command
launcher/fx/pom.xml
launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/FXBrwsrLauncher.java
launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/BrowserToolbar.java
launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/FXBrwsr.java
launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/WebDebug.java
     1.1 --- a/launcher/fx/pom.xml	Fri May 31 09:16:23 2013 +0200
     1.2 +++ b/launcher/fx/pom.xml	Sat Jun 08 12:09:10 2013 +0200
     1.3 @@ -54,5 +54,15 @@
     1.4        <scope>system</scope>
     1.5        <systemPath>${jfxrt.jar}</systemPath>
     1.6      </dependency>
     1.7 +    <dependency>
     1.8 +      <groupId>org.testng</groupId>
     1.9 +      <artifactId>testng</artifactId>
    1.10 +      <scope>test</scope>
    1.11 +    </dependency>
    1.12 +    <dependency>
    1.13 +      <groupId>org.netbeans.modules</groupId>
    1.14 +      <artifactId>org-netbeans-bootstrap</artifactId>
    1.15 +      <version>RELEASE73</version>
    1.16 +    </dependency>
    1.17    </dependencies>
    1.18  </project>
     2.1 --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/FXBrwsrLauncher.java	Fri May 31 09:16:23 2013 +0200
     2.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/FXBrwsrLauncher.java	Sat Jun 08 12:09:10 2013 +0200
     2.3 @@ -73,6 +73,10 @@
     2.4                          if (isDebugged()) {
     2.5                              params.add("--toolbar=true");
     2.6                              params.add("--firebug=true");
     2.7 +                            String ud = System.getProperty("netbeans.user");
     2.8 +                            if (ud != null) {
     2.9 +                                params.add("--userdir=" + ud);
    2.10 +                            }
    2.11                          }
    2.12                          FXBrwsr.launch(FXBrwsr.class, params.toArray(new String[params.size()]));
    2.13                          LOG.log(Level.INFO, "Launcher is back. Closing");
     3.1 --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/BrowserToolbar.java	Fri May 31 09:16:23 2013 +0200
     3.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/BrowserToolbar.java	Sat Jun 08 12:09:10 2013 +0200
     3.3 @@ -45,7 +45,7 @@
     3.4      private final ToggleGroup resizeGroup = new ToggleGroup();
     3.5      private final ComboBox<String> comboZoom = new ComboBox<String>();
     3.6      
     3.7 -    BrowserToolbar(WebView webView, Pane container, boolean useFirebug) {
     3.8 +    BrowserToolbar(WebView webView, Pane container, boolean useFirebug, final WebDebug webDebug) {
     3.9          this.webView = webView;
    3.10          this.container = container;
    3.11          
    3.12 @@ -105,20 +105,18 @@
    3.13              getItems().add(firebug);
    3.14          }
    3.15          
    3.16 -        /*
    3.17 -        final ToggleButton btnSelMode = new ToggleButton( null, new ImageView(
    3.18 -            new Image(BrowserToolbar.class.getResourceAsStream("selectionMode.png"))
    3.19 -        ));
    3.20 -        btnSelMode.setTooltip( new Tooltip( "Toggle selection mode" ) );
    3.21 -        btnSelMode.selectedProperty().addListener( new InvalidationListener() {
    3.22 -
    3.23 -            @Override
    3.24 -            public void invalidated( Observable o ) {
    3.25 -                toggleSelectionMode( btnSelMode.isSelected() );
    3.26 -            }
    3.27 -        });
    3.28 -        getItems().add( btnSelMode );
    3.29 -        */
    3.30 +        if (webDebug != null) {
    3.31 +            final ToggleButton btnSelMode = new ToggleButton(null, new ImageView(
    3.32 +                    new Image(BrowserToolbar.class.getResourceAsStream("selectionMode.png"))));
    3.33 +            btnSelMode.setTooltip(new Tooltip("Toggle selection mode"));
    3.34 +            btnSelMode.selectedProperty().addListener(new InvalidationListener() {
    3.35 +                @Override
    3.36 +                public void invalidated(Observable o) {
    3.37 +                    toggleSelectionMode(webDebug, btnSelMode.isSelected());
    3.38 +                }
    3.39 +            });
    3.40 +            getItems().add(btnSelMode);
    3.41 +        }
    3.42      }
    3.43  
    3.44      private String zoom( String zoomFactor ) {
    3.45 @@ -183,8 +181,9 @@
    3.46          webView.autosize();
    3.47      }
    3.48  
    3.49 -    private void toggleSelectionMode( boolean selMode ) {
    3.50 -        System.err.println( "selection mode: " + selMode );
    3.51 +    private void toggleSelectionMode(WebDebug dbg, boolean selMode) {
    3.52 +        // "inspect"
    3.53 +        dbg.call("{\"message\":\"selection_mode\",\"selectionMode\":" + selMode + "}");
    3.54      }
    3.55      
    3.56      final void toggleFireBug(boolean enable) {
     4.1 --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/FXBrwsr.java	Fri May 31 09:16:23 2013 +0200
     4.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/FXBrwsr.java	Sat Jun 08 12:09:10 2013 +0200
     4.3 @@ -27,20 +27,13 @@
     4.4  import javafx.beans.value.ObservableValue;
     4.5  import javafx.event.ActionEvent;
     4.6  import javafx.event.EventHandler;
     4.7 -import javafx.geometry.HPos;
     4.8  import javafx.geometry.Insets;
     4.9  import javafx.geometry.Pos;
    4.10 -import javafx.geometry.VPos;
    4.11 -import javafx.scene.Node;
    4.12  import javafx.scene.Scene;
    4.13  import javafx.scene.control.Button;
    4.14  import javafx.scene.control.ToolBar;
    4.15  import javafx.scene.layout.BorderPane;
    4.16 -import javafx.scene.layout.ColumnConstraints;
    4.17 -import javafx.scene.layout.GridPane;
    4.18  import javafx.scene.layout.HBox;
    4.19 -import javafx.scene.layout.Pane;
    4.20 -import javafx.scene.layout.Priority;
    4.21  import javafx.scene.layout.VBox;
    4.22  import javafx.scene.text.Text;
    4.23  import javafx.scene.web.WebEngine;
    4.24 @@ -58,11 +51,12 @@
    4.25   */
    4.26  public class FXBrwsr extends Application {
    4.27      private static final Logger LOG = Logger.getLogger(FXBrwsr.class.getName());
    4.28 -
    4.29 +    
    4.30      @Override
    4.31      public void start(Stage primaryStage) throws Exception {
    4.32          WebView view = new WebView();
    4.33 -        WebController wc = new WebController(view, getParameters().getUnnamed());
    4.34 +        final String nbUserDir = this.getParameters().getNamed().get("userdir"); // NOI18N
    4.35 +        WebController wc = new WebController(view, nbUserDir, getParameters().getUnnamed());
    4.36  
    4.37          final VBox vbox = new VBox();
    4.38          vbox.setAlignment( Pos.CENTER );
    4.39 @@ -79,7 +73,7 @@
    4.40          final boolean showToolbar = "true".equals(this.getParameters().getNamed().get("toolbar")); // NOI18N
    4.41          final boolean useFirebug = "true".equals(this.getParameters().getNamed().get("firebug")); // NOI18N
    4.42          if (showToolbar) {
    4.43 -            final ToolBar toolbar = new BrowserToolbar(view, vbox, useFirebug);
    4.44 +            final ToolBar toolbar = new BrowserToolbar(view, vbox, useFirebug, wc.dbg);
    4.45              root.setTop( toolbar );
    4.46          }
    4.47          root.setCenter(hbox);
    4.48 @@ -96,8 +90,11 @@
    4.49       */
    4.50      private static class WebController {
    4.51          private final JVMBridge bridge = new JVMBridge();
    4.52 +        private final WebDebug dbg;
    4.53 +        private final String ud;
    4.54  
    4.55 -        public WebController(WebView view, List<String> params) {
    4.56 +        public WebController(WebView view, String ud, List<String> params) {
    4.57 +            this.ud = ud;
    4.58              LOG.log(Level.INFO, "Initializing WebView with {0}", params);
    4.59              final WebEngine eng = view.getEngine();
    4.60              try {
    4.61 @@ -140,6 +137,15 @@
    4.62                      dialogStage.showAndWait();
    4.63                  }
    4.64              });
    4.65 +            WebDebug wd = null;
    4.66 +            try {
    4.67 +                if (ud != null) {
    4.68 +                    wd = WebDebug.create(eng.impl_getDebugger(), ud);
    4.69 +                }
    4.70 +            } catch (Exception ex) {
    4.71 +                LOG.log(Level.WARNING, null, ex);
    4.72 +            }
    4.73 +            this.dbg = wd;
    4.74          }
    4.75  
    4.76          boolean initBck2Brwsr(WebEngine webEngine) {
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/WebDebug.java	Sat Jun 08 12:09:10 2013 +0200
     5.3 @@ -0,0 +1,187 @@
     5.4 +/**
     5.5 + * Back 2 Browser Bytecode Translator
     5.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     5.7 + *
     5.8 + * This program is free software: you can redistribute it and/or modify
     5.9 + * it under the terms of the GNU General Public License as published by
    5.10 + * the Free Software Foundation, version 2 of the License.
    5.11 + *
    5.12 + * This program is distributed in the hope that it will be useful,
    5.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    5.15 + * GNU General Public License for more details.
    5.16 + *
    5.17 + * You should have received a copy of the GNU General Public License
    5.18 + * along with this program. Look for COPYING file in the top folder.
    5.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    5.20 + */
    5.21 +package org.apidesign.bck2brwsr.launcher.fximpl;
    5.22 +
    5.23 +import com.sun.javafx.scene.web.Debugger;
    5.24 +import java.io.File;
    5.25 +import java.io.IOException;
    5.26 +import java.io.InputStream;
    5.27 +import java.io.InterruptedIOException;
    5.28 +import java.io.OutputStream;
    5.29 +import java.io.UnsupportedEncodingException;
    5.30 +import java.lang.reflect.Method;
    5.31 +import java.util.concurrent.ArrayBlockingQueue;
    5.32 +import java.util.concurrent.Executors;
    5.33 +import java.util.concurrent.TimeUnit;
    5.34 +import java.util.concurrent.atomic.AtomicReference;
    5.35 +import java.util.logging.Level;
    5.36 +import java.util.logging.Logger;
    5.37 +import javafx.application.Platform;
    5.38 +import javafx.util.Callback;
    5.39 +
    5.40 +/** Simulates WebKit protocol over WebSockets.
    5.41 + *
    5.42 + * @author Jaroslav Tulach
    5.43 + */
    5.44 +abstract class WebDebug extends OutputStream 
    5.45 +implements Callback<String, Void>, Runnable {
    5.46 +    private static final Logger LOG = Logger.getLogger(WebDebug.class.getName());
    5.47 +
    5.48 +    private final Debugger debug;
    5.49 +    private final StringBuilder cmd = new StringBuilder();
    5.50 +    private final ToDbgInputStream toDbg;
    5.51 +    private final String ud;
    5.52 +    
    5.53 +    WebDebug(Debugger debug, String ud) throws Exception {
    5.54 +        this.debug = debug;
    5.55 +        this.ud = ud;
    5.56 +        toDbg = new ToDbgInputStream();
    5.57 +        debug.setEnabled(true);
    5.58 +        debug.setMessageCallback(this);
    5.59 +    }
    5.60 +    
    5.61 +    static WebDebug create(Debugger debug, String ud) throws Exception {
    5.62 +        WebDebug web = new WebDebug(debug, ud) {};
    5.63 +        
    5.64 +        Executors.newFixedThreadPool(1).execute(web);
    5.65 +        
    5.66 +        return web;
    5.67 +    }
    5.68 +
    5.69 +    @Override
    5.70 +    public void run() {
    5.71 +        try {
    5.72 +            String p = "/home/jarda/src/bck2brwsr/html~demo/twitter/src/main/resources/org/apidesign/html/demo/twitter/index.html";
    5.73 +            File f = new File(p);
    5.74 +            String[] args = {"--livehtml", f.getAbsolutePath()};
    5.75 +            File dir = f.getParentFile();
    5.76 +            cliHandler(args, toDbg, this, System.err, dir);
    5.77 +        } catch (Exception ex) {
    5.78 +            LOG.log(Level.SEVERE, null, ex);
    5.79 +        }
    5.80 +    }
    5.81 +
    5.82 +    @Override
    5.83 +    public void close() {
    5.84 +        try {
    5.85 +            toDbg.close();
    5.86 +        } catch (IOException ex) {
    5.87 +            LOG.log(Level.SEVERE, null, ex);
    5.88 +        }
    5.89 +    }
    5.90 +
    5.91 +    @Override
    5.92 +    public Void call(String p) {
    5.93 +        assert p.indexOf('\n') == -1 : "No new line: " + p;
    5.94 +        LOG.log(Level.INFO, "toDbgr: {0}", p);
    5.95 +        toDbg.pushMsg(p);
    5.96 +        return null;
    5.97 +    }
    5.98 +
    5.99 +    @Override
   5.100 +    public void write(int b) throws IOException {
   5.101 +        if (b == '\n') {
   5.102 +            final String msg = cmd.toString();
   5.103 +            Platform.runLater(new Runnable() {
   5.104 +                @Override
   5.105 +                public void run() {
   5.106 +                    LOG.log(Level.INFO, "toView: {0}", msg);
   5.107 +                    debug.sendMessage(msg);
   5.108 +                }
   5.109 +            });
   5.110 +            cmd.setLength(0);
   5.111 +        } else {
   5.112 +            if (cmd.length() > 100000) {
   5.113 +                LOG.log(Level.WARNING, "Too big:\n{0}", cmd);
   5.114 +            }
   5.115 +            cmd.append((char)b);
   5.116 +        }
   5.117 +    }   
   5.118 +
   5.119 +    private void cliHandler(
   5.120 +        String[] args, InputStream is, OutputStream os, OutputStream err, File dir
   5.121 +    ) {
   5.122 +        try {
   5.123 +            Class<?> main = Class.forName("org.netbeans.MainImpl");
   5.124 +            Method m = main.getDeclaredMethod("execute", String[].class, InputStream.class, 
   5.125 +                OutputStream.class, OutputStream.class, AtomicReference.class
   5.126 +            );
   5.127 +            m.setAccessible(true);
   5.128 +            System.setProperty("netbeans.user", ud);
   5.129 +            int ret = (Integer)m.invoke(null, args, is, os, err, null);
   5.130 +            LOG.log(Level.INFO, "Return value: {0}", ret);
   5.131 +        } catch (Exception ex) {
   5.132 +            LOG.log(Level.SEVERE, null, ex);
   5.133 +        } finally {
   5.134 +            LOG.info("Communication is over");
   5.135 +        }
   5.136 +        
   5.137 +    }
   5.138 +
   5.139 +    private class ToDbgInputStream extends InputStream {
   5.140 +        private byte[] current;
   5.141 +        private int currentPos;
   5.142 +        private final ArrayBlockingQueue<byte[]> pending = new ArrayBlockingQueue<byte[]>(64);
   5.143 +
   5.144 +        public ToDbgInputStream() {
   5.145 +        }
   5.146 +
   5.147 +        @Override
   5.148 +        public int read() throws IOException {
   5.149 +            return read(null, 0, 1);
   5.150 +        }
   5.151 +        
   5.152 +        @Override
   5.153 +        public int read(byte[] arr, int offset, int len) throws IOException {
   5.154 +            if (current == null || current.length <= currentPos) {
   5.155 +                for (;;) {
   5.156 +                    WebDebug.this.flush();
   5.157 +                    try {
   5.158 +                        current = pending.poll(100, TimeUnit.MILLISECONDS);
   5.159 +                        if (current == null) {
   5.160 +                            return 0;
   5.161 +                        }
   5.162 +                        break;
   5.163 +                    } catch (InterruptedException ex) {
   5.164 +                        throw (InterruptedIOException)new InterruptedIOException().initCause(ex);
   5.165 +                    }
   5.166 +                }
   5.167 +                LOG.info("Will return: " + new String(current));
   5.168 +                currentPos = 0;
   5.169 +            }
   5.170 +            int cnt = 0;
   5.171 +            while (len-- > 0 && currentPos < current.length) {
   5.172 +                final byte nextByte = current[currentPos++];
   5.173 +                if (arr == null) {
   5.174 +                    return nextByte;
   5.175 +                }
   5.176 +                arr[offset + cnt++] = nextByte;
   5.177 +            }
   5.178 +            LOG.log(Level.INFO, "read returns: {0}", new String(arr, offset, cnt));
   5.179 +            return cnt;
   5.180 +        }
   5.181 +
   5.182 +        private void pushMsg(String p) {
   5.183 +            try {
   5.184 +                pending.offer((p + '\n').getBytes("UTF-8"));
   5.185 +            } catch (UnsupportedEncodingException ex) {
   5.186 +                throw new IllegalStateException(ex);
   5.187 +            }
   5.188 +        }
   5.189 +    }
   5.190 +}