http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXBrwsr.java ---------------------------------------------------------------------- diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXBrwsr.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXBrwsr.java new file mode 100644 index 0000000..71c8547 --- /dev/null +++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXBrwsr.java @@ -0,0 +1,427 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package org.netbeans.html.boot.fx; + +import java.net.URL; +import java.util.ResourceBundle; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.prefs.Preferences; +import javafx.application.Application; +import javafx.application.Platform; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.concurrent.Worker; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.geometry.Rectangle2D; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.TextField; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; +import javafx.scene.web.PromptData; +import javafx.scene.web.WebEvent; +import javafx.scene.web.WebView; +import javafx.stage.Modality; +import javafx.stage.Screen; +import javafx.stage.Stage; +import javafx.stage.Window; +import javafx.stage.WindowEvent; +import javafx.util.Callback; + +/** This is an implementation class, to implement browser builder API. Just + * include this JAR on classpath and the browser builder API will find + * this implementation automatically. + */ +public class FXBrwsr extends Application { + private static final Logger LOG = Logger.getLogger(FXBrwsr.class.getName()); + private static FXBrwsr INSTANCE; + private static final CountDownLatch FINISHED = new CountDownLatch(1); + private BorderPane root; + + public static synchronized WebView findWebView(final URL url, final FXPresenter onLoad) { + if (INSTANCE == null) { + final String callee = findCalleeClassName(); + Executors.newFixedThreadPool(1).submit(new Runnable() { + @Override + public void run() { + if (!Platform.isFxApplicationThread()) { + try { + Platform.runLater(this); + } catch (IllegalStateException ex) { + try { + FXBrwsr.launch(FXBrwsr.class, callee); + } catch (Throwable t) { + t.printStackTrace(); + } finally { + FINISHED.countDown(); + } + } + } else { + FXBrwsr brwsr = new FXBrwsr(); + brwsr.start(new Stage(), callee); + INSTANCE = brwsr; + FINISHED.countDown(); + } + } + }); + } + while (INSTANCE == null) { + try { + FXBrwsr.class.wait(); + } catch (InterruptedException ex) { + // wait more + } + } + if (!Platform.isFxApplicationThread()) { + final WebView[] arr = {null}; + final CountDownLatch waitForResult = new CountDownLatch(1); + Platform.runLater(new Runnable() { + @Override + public void run() { + arr[0] = INSTANCE.newView(url, onLoad); + waitForResult.countDown(); + } + }); + for (;;) { + try { + waitForResult.await(); + break; + } catch (InterruptedException ex) { + LOG.log(Level.INFO, null, ex); + } + } + return arr[0]; + } else { + return INSTANCE.newView(url, onLoad); + } + } + + static synchronized Stage findStage() throws InterruptedException { + while (INSTANCE == null) { + FXBrwsr.class.wait(); + } + return INSTANCE.stage; + } + + private Stage stage; + + @Override + public void start(Stage primaryStage) throws Exception { + start(primaryStage, this.getParameters().getRaw().get(0)); + } + + final void start(Stage primaryStage, String callee) { + BorderPane r = new BorderPane(); + Object[] arr = findInitialSize(callee); + Scene scene = new Scene(r, (Double)arr[2], (Double)arr[3]); + primaryStage.setScene(scene); + this.root = r; + this.stage = primaryStage; + synchronized (FXBrwsr.class) { + INSTANCE = this; + FXBrwsr.class.notifyAll(); + } + primaryStage.setX((Double)arr[0]); + primaryStage.setY((Double)arr[1]); + if (arr[4] != null) { + scene.getWindow().setOnCloseRequest((EventHandler<WindowEvent>) arr[4]); + } + if (Boolean.getBoolean("fxpresenter.headless")) { + return; + } + primaryStage.show(); + } + + static String findCalleeClassName() { + StackTraceElement[] frames = new Exception().getStackTrace(); + for (StackTraceElement e : frames) { + String cn = e.getClassName(); + if (cn.startsWith("org.netbeans.html.")) { // NOI18N + continue; + } + if (cn.startsWith("net.java.html.")) { // NOI18N + continue; + } + if (cn.startsWith("java.")) { // NOI18N + continue; + } + if (cn.startsWith("javafx.")) { // NOI18N + continue; + } + if (cn.startsWith("com.sun.")) { // NOI18N + continue; + } + return cn; + } + return "org.netbeans.html"; // NOI18N + } + + private static Object[] findInitialSize(String callee) { + final Preferences prefs = Preferences.userRoot().node(callee.replace('.', '/')); + Rectangle2D screen = Screen.getPrimary().getBounds(); + double x = prefs.getDouble("x", screen.getWidth() * 0.05); // NOI18N + double y = prefs.getDouble("y", screen.getHeight() * 0.05); // NOI18N + double width = prefs.getDouble("width", screen.getWidth() * 0.9); // NOI18N + double height = prefs.getDouble("height", screen.getHeight() * 0.9); // NOI18N + + Object[] arr = { + x, y, width, height, null + }; + + if (!callee.equals("org.netbeans.html")) { // NOI18N + arr[4] = new EventHandler<WindowEvent>() { + @Override + public void handle(WindowEvent event) { + Window window = (Window) event.getSource(); + prefs.putDouble("x", window.getX()); // NOI18N + prefs.putDouble("y", window.getY()); // NOI18N + prefs.putDouble("width", window.getWidth()); // NOI18N + prefs.putDouble("height", window.getHeight()); // NOI18N + } + }; + } + + return arr; + } + + private WebView newView(final URL url, final FXPresenter onLoad) { + final WebView view = new WebView(); + view.setContextMenuEnabled(false); + Stage newStage; + BorderPane bp; + if (root == null) { + newStage = new Stage(); + newStage.initOwner(stage); + bp = new BorderPane(); + newStage.setScene(new Scene(bp)); + newStage.show(); + } else { + bp = root; + newStage = stage; + root = null; + } + + attachHandlers(view, newStage); + bp.setCenter(view); + final Worker<Void> w = view.getEngine().getLoadWorker(); + w.stateProperty().addListener(new ChangeListener<Worker.State>() { + private String previous; + + @Override + public void changed(ObservableValue<? extends Worker.State> ov, Worker.State t, Worker.State newState) { + if (newState.equals(Worker.State.SUCCEEDED)) { + if (checkValid()) { + FXConsole.register(view.getEngine()); + onLoad.onPageLoad(); + } + } + if (newState.equals(Worker.State.FAILED)) { + throw new IllegalStateException("Failed to load " + url); + } + } + private boolean checkValid() { + final String crnt = view.getEngine().getLocation(); + if (previous != null && !previous.equals(crnt)) { + w.stateProperty().removeListener(this); + return false; + } + previous = crnt; + return true; + } + + }); + class Title implements ChangeListener<String> { + + private String title; + + public Title() { + super(); + } + + @Override + public void changed(ObservableValue<? extends String> ov, String t, String t1) { + title = view.getEngine().getTitle(); + if (title != null) { + stage.setTitle(title); + } + } + } + final Title x = new Title(); + view.getEngine().titleProperty().addListener(x); + x.changed(null, null, null); + return view; + } + + private static void attachHandlers(final WebView view, final Stage owner) { + view.getEngine().setOnAlert(new EventHandler<WebEvent<String>>() { + @Override + public void handle(WebEvent<String> t) { + final Stage dialogStage = new Stage(); + dialogStage.initModality(Modality.WINDOW_MODAL); + dialogStage.initOwner(owner); + ResourceBundle r = ResourceBundle.getBundle("org/netbeans/html/boot/fx/Bundle"); // NOI18N + dialogStage.setTitle(r.getString("AlertTitle")); // NOI18N + final Button button = new Button(r.getString("AlertCloseButton")); // NOI18N + final Text text = new Text(t.getData()); + VBox box = new VBox(); + box.setAlignment(Pos.CENTER); + box.setSpacing(10); + box.setPadding(new Insets(10)); + box.getChildren().addAll(text, button); + dialogStage.setScene(new Scene(box)); + button.setCancelButton(true); + button.setOnAction(new CloseDialogHandler(dialogStage, null)); + dialogStage.centerOnScreen(); + dialogStage.showAndWait(); + } + }); + view.getEngine().setConfirmHandler(new Callback<String, Boolean>() { + @Override + public Boolean call(String question) { + final Stage dialogStage = new Stage(); + dialogStage.initModality(Modality.WINDOW_MODAL); + dialogStage.initOwner(owner); + ResourceBundle r = ResourceBundle.getBundle("org/netbeans/html/boot/fx/Bundle"); // NOI18N + dialogStage.setTitle(r.getString("ConfirmTitle")); // NOI18N + final Button ok = new Button(r.getString("ConfirmOKButton")); // NOI18N + final Button cancel = new Button(r.getString("ConfirmCancelButton")); // NOI18N + final Text text = new Text(question); + final Insets ins = new Insets(10); + final VBox box = new VBox(); + box.setAlignment(Pos.CENTER); + box.setSpacing(10); + box.setPadding(ins); + final HBox buttons = new HBox(10); + buttons.getChildren().addAll(ok, cancel); + buttons.setAlignment(Pos.CENTER); + buttons.setPadding(ins); + box.getChildren().addAll(text, buttons); + dialogStage.setScene(new Scene(box)); + ok.setCancelButton(false); + + final boolean[] res = new boolean[1]; + ok.setOnAction(new CloseDialogHandler(dialogStage, res)); + cancel.setCancelButton(true); + cancel.setOnAction(new CloseDialogHandler(dialogStage, null)); + dialogStage.centerOnScreen(); + dialogStage.showAndWait(); + return res[0]; + } + }); + view.getEngine().setPromptHandler(new Callback<PromptData, String>() { + @Override + public String call(PromptData prompt) { + final Stage dialogStage = new Stage(); + dialogStage.initModality(Modality.WINDOW_MODAL); + dialogStage.initOwner(owner); + ResourceBundle r = ResourceBundle.getBundle("org/netbeans/html/boot/fx/Bundle"); // NOI18N + dialogStage.setTitle(r.getString("PromptTitle")); // NOI18N + final Button ok = new Button(r.getString("PromptOKButton")); // NOI18N + final Button cancel = new Button(r.getString("PromptCancelButton")); // NOI18N + final Text text = new Text(prompt.getMessage()); + final TextField line = new TextField(); + if (prompt.getDefaultValue() != null) { + line.setText(prompt.getDefaultValue()); + } + final Insets ins = new Insets(10); + final VBox box = new VBox(); + box.setAlignment(Pos.CENTER); + box.setSpacing(10); + box.setPadding(ins); + final HBox buttons = new HBox(10); + buttons.getChildren().addAll(ok, cancel); + buttons.setAlignment(Pos.CENTER); + buttons.setPadding(ins); + box.getChildren().addAll(text, line, buttons); + dialogStage.setScene(new Scene(box)); + ok.setCancelButton(false); + + final boolean[] res = new boolean[1]; + ok.setOnAction(new CloseDialogHandler(dialogStage, res)); + cancel.setCancelButton(true); + cancel.setOnAction(new CloseDialogHandler(dialogStage, null)); + dialogStage.centerOnScreen(); + dialogStage.showAndWait(); + return res[0] ? line.getText() : null; + } + }); + } + + static void waitFinished() { + for (;;) { + try { + FINISHED.await(); + break; + } catch (InterruptedException ex) { + LOG.log(Level.INFO, null, ex); + } + } + } + + private static final class CloseDialogHandler implements EventHandler<ActionEvent> { + private final Stage dialogStage; + private final boolean[] res; + + public CloseDialogHandler(Stage dialogStage, boolean[] res) { + this.dialogStage = dialogStage; + this.res = res; + } + + @Override + public void handle(ActionEvent t) { + dialogStage.close(); + if (res != null) { + res[0] = true; + } + } + } + +}
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXConsole.java ---------------------------------------------------------------------- diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXConsole.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXConsole.java new file mode 100644 index 0000000..d176ede --- /dev/null +++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXConsole.java @@ -0,0 +1,84 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package org.netbeans.html.boot.fx; + +import java.util.logging.Level; +import java.util.logging.Logger; +import javafx.scene.web.WebEngine; +import netscape.javascript.JSObject; + +/** This is an implementation package - just + * include its JAR on classpath and use official browser builder API + * to access the functionality. + * <p> + * Redirects JavaScript's messages to Java's {@link Logger}. + * + * @author Jaroslav Tulach + */ +public final class FXConsole { + static final Logger LOG = Logger.getLogger(FXConsole.class.getName()); + + private FXConsole() { + } + + static void register(WebEngine eng) { + JSObject fn = (JSObject) eng.executeScript("" + + "(function(attr, l, c) {" + + " window.console[attr] = function(msg) { c.log(l, msg); };" + + "})" + ); + FXConsole c = new FXConsole(); + c.registerImpl(fn, "log", Level.INFO); + c.registerImpl(fn, "info", Level.INFO); + c.registerImpl(fn, "warn", Level.WARNING); + c.registerImpl(fn, "error", Level.SEVERE); + } + + private void registerImpl(JSObject eng, String attr, Level l) { + eng.call("call", null, attr, l, this); + } + + public void log(Level l, String msg) { + LOG.log(l, msg); + } +} http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXInspect.java ---------------------------------------------------------------------- diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXInspect.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXInspect.java new file mode 100644 index 0000000..2c8bc4f --- /dev/null +++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXInspect.java @@ -0,0 +1,134 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package org.netbeans.html.boot.fx; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.util.logging.Level; +import java.util.logging.Logger; +import javafx.application.Platform; +import javafx.scene.web.WebEngine; +import javafx.util.Callback; + +/** + * + * @author Jaroslav Tulach + */ +final class FXInspect implements Runnable { + static final Logger LOG = Logger.getLogger(FXInspect.class.getName()); + + + private final WebEngine engine; + private final ObjectInputStream input; + private Dbgr dbg; + + private FXInspect(WebEngine engine, int port) throws IOException { + this.engine = engine; + + Socket socket = new Socket(InetAddress.getByName(null), port); + ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); + this.input = new ObjectInputStream(socket.getInputStream()); + initializeDebugger(output); + } + + static boolean initialize(WebEngine engine) { + final int inspectPort = Integer.getInteger("netbeans.inspect.port", -1); // NOI18N + if (inspectPort != -1) { + try { + FXInspect inspector = new FXInspect(engine, inspectPort); + Thread t = new Thread(inspector, "FX<->NetBeans Inspector"); + t.start(); + return true; + } catch (IOException ex) { + LOG.log(Level.INFO, "Cannot connect to NetBeans IDE to port " + inspectPort, ex); // NOI18N + } + } + return false; + } + + private void initializeDebugger(final ObjectOutputStream output) { + Platform.runLater(new Runnable() { + @Override + public void run() { + dbg = new Dbgr(engine, new Callback<String,Void>() { + @Override + public Void call(String message) { + try { + byte[] bytes = message.getBytes(StandardCharsets.UTF_8); + output.writeInt(bytes.length); + output.write(bytes); + output.flush(); + } catch (IOException ioex) { + ioex.printStackTrace(); + } + return null; + } + }); + } + }); + } + + @Override + public void run() { + try { + while (true) { + int length = input.readInt(); + byte[] bytes = new byte[length]; + input.readFully(bytes); + final String message = new String(bytes, StandardCharsets.UTF_8); + Platform.runLater(new Runnable() { + @Override + public void run() { + dbg.sendMessage(message); + } + }); + } + } catch (IOException ex) { + LOG.log(Level.WARNING, null, ex); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXPresenter.java ---------------------------------------------------------------------- diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXPresenter.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXPresenter.java new file mode 100644 index 0000000..c54a1a1 --- /dev/null +++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXPresenter.java @@ -0,0 +1,88 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package org.netbeans.html.boot.fx; + +import java.io.File; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import javafx.scene.web.WebView; +import net.java.html.boot.BrowserBuilder; +import org.netbeans.html.boot.spi.Fn; +import org.openide.util.lookup.ServiceProvider; + +/** This is an implementation class, use {@link BrowserBuilder} API. Just + * include this JAR on classpath and the {@link BrowserBuilder} API will find + * this implementation automatically. + * + * @author Jaroslav Tulach + */ +@ServiceProvider(service = Fn.Presenter.class) +public final class FXPresenter extends AbstractFXPresenter { + static { + try { + try { + Class<?> c = Class.forName("javafx.application.Platform"); + // OK, on classpath + } catch (ClassNotFoundException classNotFoundException) { + Method m = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); + m.setAccessible(true); + File f = new File(System.getProperty("java.home"), "lib/jfxrt.jar"); + if (f.exists()) { + URL l = f.toURI().toURL(); + m.invoke(ClassLoader.getSystemClassLoader(), l); + } + } + } catch (Exception ex) { + throw new LinkageError("Can't add jfxrt.jar on the classpath", ex); + } + } + + protected void waitFinished() { + FXBrwsr.waitFinished(); + } + + protected WebView findView(final URL resource) { + return FXBrwsr.findWebView(resource, this); + } +} http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXToolbar.java ---------------------------------------------------------------------- diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXToolbar.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXToolbar.java new file mode 100644 index 0000000..20d4e10 --- /dev/null +++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXToolbar.java @@ -0,0 +1,437 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package org.netbeans.html.boot.fx; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.prefs.Preferences; +import javafx.beans.InvalidationListener; +import javafx.beans.Observable; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.scene.Group; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.CheckBox; +import javafx.scene.control.ComboBox; +import javafx.scene.control.Separator; +import javafx.scene.control.Toggle; +import javafx.scene.control.ToggleButton; +import javafx.scene.control.ToggleGroup; +import javafx.scene.control.ToolBar; +import javafx.scene.control.Tooltip; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.BorderPane; +import javafx.scene.web.WebEngine; +import javafx.scene.web.WebView; +import javafx.stage.Screen; +import javafx.stage.Stage; +import javafx.stage.Window; + +final class FXToolbar extends ToolBar { + private final ArrayList<ResizeBtn> resizeButtons; + private final WebView webView; + private final BorderPane container; + private final ToggleGroup resizeGroup = new ToggleGroup(); + private final ComboBox<String> comboZoom = new ComboBox<String>(); + private WatchDir watcher; + + FXToolbar(WebView wv, BorderPane container, boolean enableFirebug) { + this.webView = wv; + this.container = container; + + List<ResizeOption> options = ResizeOption.loadAll(); + options.add( 0, ResizeOption.SIZE_TO_FIT ); + resizeButtons = new ArrayList<ResizeBtn>( options.size() ); + + for( ResizeOption ro : options ) { + ResizeBtn button = new ResizeBtn(ro); + resizeButtons.add( button ); + resizeGroup.getToggles().add( button ); + getItems().add( button ); + } + resizeButtons.get( 0 ).setSelected( true ); + resizeGroup.selectedToggleProperty().addListener( new InvalidationListener() { + + @Override + public void invalidated( Observable o ) { + resize(); + } + }); + + getItems().add( new Separator() ); + + getItems().add( comboZoom ); + ArrayList<String> zoomModel = new ArrayList<String>( 6 ); + zoomModel.add( "200%" ); //NOI18N + zoomModel.add( "150%" ); //NOI18N + zoomModel.add( "100%" ); //NOI18N + zoomModel.add( "75%" ); //NOI18N + zoomModel.add( "50%" ); //NOI18N + comboZoom.setItems( FXCollections.observableList( zoomModel ) ); + comboZoom.setEditable( true ); + comboZoom.setValue( "100%" ); //NOI18N + comboZoom.valueProperty().addListener( new ChangeListener<String>() { + + @Override + public void changed( ObservableValue<? extends String> ov, String t, String t1 ) { + String newZoom = zoom( t1 ); + comboZoom.setValue( newZoom ); + } + }); + + getItems().add(new Separator()); + final CheckBox automatic = new CheckBox("Automatic"); + final Preferences prefs = Preferences.userNodeForPackage(FXToolbar.class); + final String ar = "automaticReload"; // NOI18N + automatic.setSelected(prefs.getBoolean(ar, true)); + getItems().add(automatic); + final Button reload = new Button("Reload"); + getItems().add(reload); + reload.setOnAction(new EventHandler<ActionEvent>() { + @Override + public void handle(ActionEvent event) { + webView.getEngine().reload(); + } + }); + automatic.setOnAction(new EventHandler<ActionEvent>() { + @Override + public void handle(ActionEvent event) { + prefs.putBoolean(ar, automatic.isSelected()); + listenOnChanges(automatic.isSelected()); + } + }); + webView.getEngine().locationProperty().addListener(new ChangeListener<String>() { + @Override + public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) { + listenOnChanges(automatic.isSelected()); + } + }); + if (enableFirebug){ + final Button firebug = new Button("Firebug"); + getItems().add(firebug); + firebug.setOnAction(new EventHandler<ActionEvent>() { + + @Override + public void handle(ActionEvent event) { + enableFirebug(webView.getEngine()); + firebug.setDisable(true); + } + });} + } + + private String zoom( String zoomFactor ) { + if( zoomFactor.trim().isEmpty() ) + return null; + + try { + zoomFactor = zoomFactor.replaceAll( "\\%", ""); //NOI18N + zoomFactor = zoomFactor.trim(); + double zoom = Double.parseDouble( zoomFactor ); + zoom = Math.abs( zoom )/100; + if( zoom <= 0.0 ) + return null; + webView.setScaleX(zoom); + webView.setScaleY(zoom); + webView.setScaleZ(zoom); + return (int)(100*zoom) + "%"; //NOI18N + } catch( NumberFormatException nfe ) { + //ignore + } + return null; + } + + private void resize() { + Toggle selection = resizeGroup.getSelectedToggle(); + if( selection instanceof ResizeBtn ) { + ResizeOption ro = ((ResizeBtn)selection).getResizeOption(); + if( ro == ResizeOption.SIZE_TO_FIT ) { + _autofit(); + } else { + _resize( ro.getWidth(), ro.getHeight() ); + } + } + + } + + private void _resize(final double width, final double height) { + Window window = container.getScene().getWindow(); + // size difference between root node and window depends on OS and Decorations + double diffY = window.getHeight() - container.getHeight(); + double diffX = window.getWidth() - container.getWidth(); + + webView.setMaxWidth(width); + webView.setMaxHeight(height); + webView.setMinWidth(width); + webView.setMinHeight(height); + javafx.geometry.Rectangle2D screenBounds = Screen.getPrimary().getBounds(); + double scaleX = screenBounds.getWidth() / ( width + diffX ); + double scaleY = screenBounds.getHeight() / ( height + diffY ); + // calculate scale factor if too big for device, the .1 adds some padding + double scale = Math.min(Math.min(scaleX, scaleY), 1.1) - .1; + webView.setScaleX(scale); + webView.setScaleY(scale); + container.getScene().setRoot(new Group()); + ((Stage)window).setScene(new Scene(container, width * scale, height * scale)); + } + + private void _autofit() { + if (container.getCenter() != webView) { + container.setCenter(webView); + } + webView.setMaxWidth( Integer.MAX_VALUE ); + webView.setMaxHeight( Integer.MAX_VALUE ); + webView.setMinWidth( -1 ); + webView.setMinHeight( -1 ); + webView.autosize(); + } + + /** + * Button to resize the browser window. + * Taken from NetBeans. Kept GPLwithCPEx license. + * Portions Copyright 2012 Oracle. + * + * @author S. Aubrecht + */ + static final class ResizeBtn extends ToggleButton { + + private final ResizeOption resizeOption; + + ResizeBtn(ResizeOption resizeOption) { + super(null, new ImageView(toImage(resizeOption))); + this.resizeOption = resizeOption; + setTooltip(new Tooltip(resizeOption.getToolTip())); + } + + ResizeOption getResizeOption() { + return resizeOption; + } + + static Image toImage(ResizeOption ro) { + if (ro == ResizeOption.SIZE_TO_FIT) { + return ResizeOption.Type.CUSTOM.getImage(); + } + return ro.getType().getImage(); + } + } + + /** + * Immutable value class describing a single button to resize web browser window. + * Taken from NetBeans. Kept GPLwithCPEx license. + * Portions Copyrighted 2012 Sun Microsystems, Inc. + * + * @author S. Aubrecht + */ + static final class ResizeOption { + + private final Type type; + private final String displayName; + private final int width; + private final int height; + private final boolean isDefault; + + enum Type { + DESKTOP("desktop.png"), + TABLET_PORTRAIT("tabletPortrait.png"), + TABLET_LANDSCAPE("tabletLandscape.png"), + SMARTPHONE_PORTRAIT("handheldPortrait.png"), + SMARTPHONE_LANDSCAPE("handheldLandscape.png"), + WIDESCREEN("widescreen.png"), + NETBOOK("netbook.png"), + CUSTOM("sizeToFit.png"); + + + private final String resource; + + private Type(String r) { + resource = r; + } + + public Image getImage() { + return new Image(Type.class.getResourceAsStream(resource)); + } + } + + private ResizeOption(Type type, String displayName, int width, int height, boolean showInToolbar, boolean isDefault) { + super(); + this.type = type; + this.displayName = displayName; + this.width = width; + this.height = height; + this.isDefault = isDefault; + } + + static List<ResizeOption> loadAll() { + List<ResizeOption> res = new ArrayList<ResizeOption>(10); + res.add(ResizeOption.create(ResizeOption.Type.DESKTOP, "Desktop", 1280, 1024, true, true)); + res.add(ResizeOption.create(ResizeOption.Type.TABLET_LANDSCAPE, "Tablet Landscape", 1024, 768, true, true)); + res.add(ResizeOption.create(ResizeOption.Type.TABLET_PORTRAIT, "Tablet Portrait", 768, 1024, true, true)); + res.add(ResizeOption.create(ResizeOption.Type.SMARTPHONE_LANDSCAPE, "Smartphone Landscape", 480, 320, true, true)); + res.add(ResizeOption.create(ResizeOption.Type.SMARTPHONE_PORTRAIT, "Smartphone Portrait", 320, 480, true, true)); + res.add(ResizeOption.create(ResizeOption.Type.WIDESCREEN, "Widescreen", 1680, 1050, false, true)); + res.add(ResizeOption.create(ResizeOption.Type.NETBOOK, "Netbook", 1024, 600, false, true)); + return res; + } + + /** + * Creates a new instance. + * @param type + * @param displayName Display name to show in tooltip, cannot be empty. + * @param width Screen width + * @param height Screen height + * @param showInToolbar True to show in web developer toolbar. + * @param isDefault True if this is a predefined option that cannot be removed. + * @return New instance. + */ + public static ResizeOption create(Type type, String displayName, int width, int height, boolean showInToolbar, boolean isDefault) { + if (width <= 0 || height <= 0) { + throw new IllegalArgumentException("Invalid screen dimensions: " + width + " x " + height); //NOI18N + } + return new ResizeOption(type, displayName, width, height, showInToolbar, isDefault); + } + /** + * An extra option to size the browser content to fit its window. + */ + public static final ResizeOption SIZE_TO_FIT = new ResizeOption(Type.CUSTOM, "Size To Fit", -1, -1, true, true); + + public String getDisplayName() { + return displayName; + } + + public Type getType() { + return type; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public boolean isDefault() { + return isDefault; + } + + @Override + public String toString() { + return displayName; + } + + public String getToolTip() { + if (width < 0 || height < 0) { + return displayName; + } + StringBuilder sb = new StringBuilder(); + sb.append(width); + sb.append(" x "); //NOI18N + sb.append(height); + sb.append(" ("); //NOI18N + sb.append(displayName); + sb.append(')'); //NOI18N + return sb.toString(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ResizeOption other = (ResizeOption) obj; + if (this.type != other.type) { + return false; + } + if ((this.displayName == null) ? (other.displayName != null) : !this.displayName.equals(other.displayName)) { + return false; + } + if (this.width != other.width) { + return false; + } + if (this.height != other.height) { + return false; + } + if (this.isDefault != other.isDefault) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 11 * hash + (this.type != null ? this.type.hashCode() : 0); + hash = 11 * hash + (this.displayName != null ? this.displayName.hashCode() : 0); + hash = 11 * hash + this.width; + hash = 11 * hash + this.height; + hash = 11 * hash + (this.isDefault ? 1 : 0); + return hash; + } + } + + private void listenOnChanges(boolean turnOn) { + try { + if (watcher != null) { + watcher.close(); + watcher = null; + } + final WebEngine eng = webView.getEngine(); + if (turnOn && eng.getLocation().startsWith("file:")) { // NOI18N + watcher = new WatchDir(eng); + } + } catch (Exception ex) { + FXInspect.LOG.log(Level.SEVERE, null, ex); + } + } + private static void enableFirebug(final WebEngine engine) { + engine.executeScript("if (!document.getElementById('FirebugLite')){E = document['createElement' + 'NS'] && document.documentElement.namespaceURI;E = E ? document['createElement' + 'NS'](E, 'script') : document['createElement']('script');E['setAttribute']('id', 'FirebugLite');E['setAttribute']('src', 'https://getfirebug.com/' + 'firebug-lite.js' + '#startOpened');E['setAttribute']('FirebugLite', '4');(document['getElementsByTagName']('head')[0] || document['getElementsByTagName']('body')[0]).appendChild(E);E = new Image;E['setAttribute']('src', 'https://getfirebug.com/' + '#startOpened');}"); + } +} http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/java/org/netbeans/html/boot/fx/WatchDir.java ---------------------------------------------------------------------- diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/WatchDir.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/WatchDir.java new file mode 100644 index 0000000..25cc5f2 --- /dev/null +++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/WatchDir.java @@ -0,0 +1,117 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package org.netbeans.html.boot.fx; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.ClosedWatchServiceException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardWatchEventKinds; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; +import java.util.logging.Level; +import java.util.logging.Logger; +import javafx.application.Platform; +import javafx.scene.web.WebEngine; + +/** + * + * @author Jaroslav Tulach + */ +final class WatchDir implements Runnable { + private final Path dir; + private final WatchKey key; + private final WatchService ws; + private final Thread watcher; + private final WebEngine engine; + + WatchDir(WebEngine eng) throws URISyntaxException, IOException { + dir = Paths.get(new URI(eng.getLocation())).getParent(); + engine = eng; + ws = dir.getFileSystem().newWatchService(); + key = dir.register(ws, + StandardWatchEventKinds.ENTRY_CREATE, + StandardWatchEventKinds.ENTRY_DELETE, + StandardWatchEventKinds.ENTRY_MODIFY + ); + watcher = new Thread(this, "Watching files in " + dir); + watcher.setDaemon(true); + watcher.setPriority(Thread.MIN_PRIORITY); + watcher.start(); + } + + public void close() throws IOException { + key.cancel(); + ws.close(); + watcher.interrupt(); + } + + @Override + public void run() { + if (Platform.isFxApplicationThread()) { + engine.reload(); + return; + } + try { + while (key.isValid()) { + WatchKey changed; + try { + changed = ws.take(); + if (changed != key || changed.pollEvents().isEmpty()) { + continue; + } + } catch (ClosedWatchServiceException ex) { + continue; + } + Platform.runLater(this); + if (!key.reset()) { + break; + } + } + } catch (InterruptedException ex) { + FXInspect.LOG.log(Level.SEVERE, null, ex); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/resources/org/netbeans/html/boot/fx/Bundle.properties ---------------------------------------------------------------------- diff --git a/boot-fx/src/main/resources/org/netbeans/html/boot/fx/Bundle.properties b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/Bundle.properties new file mode 100644 index 0000000..7ebe82d --- /dev/null +++ b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/Bundle.properties @@ -0,0 +1,54 @@ +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +# +# Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. +# +# Oracle and Java are registered trademarks of Oracle and/or its affiliates. +# Other names may be trademarks of their respective owners. +# +# The contents of this file are subject to the terms of either the GNU +# General Public License Version 2 only ("GPL") or the Common +# Development and Distribution License("CDDL") (collectively, the +# "License"). You may not use this file except in compliance with the +# License. You can obtain a copy of the License at +# http://www.netbeans.org/cddl-gplv2.html +# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the +# specific language governing permissions and limitations under the +# License. When distributing the software, include this License Header +# Notice in each file and include the License file at +# nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the GPL Version 2 section of the License file that +# accompanied this code. If applicable, add the following below the +# License Header, with the fields enclosed by brackets [] replaced by +# your own identifying information: +# "Portions Copyrighted [year] [name of copyright owner]" +# +# Contributor(s): +# +# The Original Software is NetBeans. The Initial Developer of the Original +# Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved. +# +# If you wish your version of this file to be governed by only the CDDL +# or only the GPL Version 2, indicate your decision by adding +# "[Contributor] elects to include this software in this distribution +# under the [CDDL or GPL Version 2] license." If you do not indicate a +# single choice of license, a recipient has the option to distribute +# your version of this file under either the CDDL, the GPL Version 2 or +# to extend the choice of license to its licensees as provided above. +# However, if you add GPL Version 2 code and therefore, elected the GPL +# Version 2 license, then the option applies only if the new code is +# made subject to such option by the copyright holder. +# + +AlertTitle=Warning +AlertCloseButton=Close + +ConfirmTitle=Question +ConfirmOKButton=OK +ConfirmCancelButton=Cancel + +PromptTitle=Question +PromptOKButton=OK +PromptCancelButton=Cancel + http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/resources/org/netbeans/html/boot/fx/desktop.png ---------------------------------------------------------------------- diff --git a/boot-fx/src/main/resources/org/netbeans/html/boot/fx/desktop.png b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/desktop.png new file mode 100644 index 0000000..b295e4b Binary files /dev/null and b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/desktop.png differ http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/resources/org/netbeans/html/boot/fx/handheldLandscape.png ---------------------------------------------------------------------- diff --git a/boot-fx/src/main/resources/org/netbeans/html/boot/fx/handheldLandscape.png b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/handheldLandscape.png new file mode 100644 index 0000000..ce5d64e Binary files /dev/null and b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/handheldLandscape.png differ http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/resources/org/netbeans/html/boot/fx/handheldPortrait.png ---------------------------------------------------------------------- diff --git a/boot-fx/src/main/resources/org/netbeans/html/boot/fx/handheldPortrait.png b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/handheldPortrait.png new file mode 100644 index 0000000..686bea8 Binary files /dev/null and b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/handheldPortrait.png differ http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/resources/org/netbeans/html/boot/fx/netbook.png ---------------------------------------------------------------------- diff --git a/boot-fx/src/main/resources/org/netbeans/html/boot/fx/netbook.png b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/netbook.png new file mode 100644 index 0000000..53838ba Binary files /dev/null and b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/netbook.png differ http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/resources/org/netbeans/html/boot/fx/selectionMode.png ---------------------------------------------------------------------- diff --git a/boot-fx/src/main/resources/org/netbeans/html/boot/fx/selectionMode.png b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/selectionMode.png new file mode 100644 index 0000000..d2e398c Binary files /dev/null and b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/selectionMode.png differ http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/resources/org/netbeans/html/boot/fx/sizeToFit.png ---------------------------------------------------------------------- diff --git a/boot-fx/src/main/resources/org/netbeans/html/boot/fx/sizeToFit.png b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/sizeToFit.png new file mode 100644 index 0000000..5fec5a4 Binary files /dev/null and b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/sizeToFit.png differ http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/resources/org/netbeans/html/boot/fx/tabletLandscape.png ---------------------------------------------------------------------- diff --git a/boot-fx/src/main/resources/org/netbeans/html/boot/fx/tabletLandscape.png b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/tabletLandscape.png new file mode 100644 index 0000000..89f7f61 Binary files /dev/null and b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/tabletLandscape.png differ http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/resources/org/netbeans/html/boot/fx/tabletPortrait.png ---------------------------------------------------------------------- diff --git a/boot-fx/src/main/resources/org/netbeans/html/boot/fx/tabletPortrait.png b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/tabletPortrait.png new file mode 100644 index 0000000..fb94771 Binary files /dev/null and b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/tabletPortrait.png differ http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/main/resources/org/netbeans/html/boot/fx/widescreen.png ---------------------------------------------------------------------- diff --git a/boot-fx/src/main/resources/org/netbeans/html/boot/fx/widescreen.png b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/widescreen.png new file mode 100644 index 0000000..933e01f Binary files /dev/null and b/boot-fx/src/main/resources/org/netbeans/html/boot/fx/widescreen.png differ http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersOnResourceTest.java ---------------------------------------------------------------------- diff --git a/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersOnResourceTest.java b/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersOnResourceTest.java new file mode 100644 index 0000000..1400ad8 --- /dev/null +++ b/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersOnResourceTest.java @@ -0,0 +1,209 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package net.java.html.boot.fx; + +import java.net.URL; +import java.util.concurrent.CountDownLatch; +import javafx.application.Application; +import javafx.application.Platform; +import javafx.scene.Scene; +import javafx.scene.layout.BorderPane; +import javafx.scene.web.WebView; +import javafx.stage.Stage; +import net.java.html.js.JavaScriptBody; +import net.java.html.js.JavaScriptResource; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNotSame; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * + * @author Jaroslav Tulach + */ +public class FXBrowsersOnResourceTest { + + public FXBrowsersOnResourceTest() { + } + + @BeforeClass public void initFX() throws Throwable { + new Thread("initFX") { + @Override + public void run() { + if (Platform.isFxApplicationThread()) { + new App().start(new Stage()); + } else { + try { + App.launch(App.class); + } catch (IllegalStateException ex) { + Platform.runLater(this); + } + } + } + }.start(); + App.CDL.await(); + } + + @Test + public void behaviorOfTwoWebViewsAtOnce() throws Throwable { + class R implements Runnable { + CountDownLatch DONE = new CountDownLatch(1); + Throwable t; + + @Override + public void run() { + try { + doTest(); + } catch (Throwable ex) { + t = ex; + } finally { + DONE.countDown(); + } + } + + private void doTest() throws Throwable { + URL u = FXBrowsersOnResourceTest.class.getResource("/org/netbeans/html/boot/fx/empty.html"); + assertNotNull(u, "URL found"); + FXBrowsers.load(App.getV1(), u, OnPages.class, "first"); + + } + } + R run = new R(); + Platform.runLater(run); + run.DONE.await(); + for (int i = 0; i < 100; i++) { + if (run.t != null) { + throw run.t; + } + if (System.getProperty("finalSecond") == null) { + Thread.sleep(100); + } + } + + + + assertEquals(Integer.getInteger("finalFirst"), Integer.valueOf(3), "Three times in view one"); + assertEquals(Integer.getInteger("finalSecond"), Integer.valueOf(2), "Two times in view one"); + } + + @JavaScriptResource("wnd.js") + public static class OnPages { + static Class<?> first; + static Object firstWindow; + + public static void first() { + first = OnPages.class; + firstWindow = window(); + assertNotNull(firstWindow, "First window found"); + + assertEquals(increment(), 1, "Now it is one"); + + URL u = FXBrowsersOnResourceTest.class.getResource("/org/netbeans/html/boot/fx/empty.html"); + assertNotNull(u, "URL found"); + FXBrowsers.load(App.getV2(), u, OnPages.class, "second", "Hello"); + + assertEquals(increment(), 2, "Now it is two and not influenced by second view"); + System.setProperty("finalFirst", "" + increment()); + } + + public static void second(String... args) { + assertEquals(args.length, 1, "One string argument"); + assertEquals(args[0], "Hello", "It is hello"); + assertEquals(first, OnPages.class, "Both views share the same classloader"); + + Object window = window(); + assertNotNull(window, "Some window found"); + assertNotNull(firstWindow, "First window is known"); + assertNotSame(firstWindow, window, "The window objects should be different"); + + assertEquals(increment(), 1, "Counting starts from zero"); + System.setProperty("finalSecond", "" + increment()); + } + + @JavaScriptBody(args = {}, body = "return wnd;") + private static native Object window(); + + @JavaScriptBody(args = {}, body = "" + + "if (wnd.cnt) return ++wnd.cnt;" + + "return wnd.cnt = 1;" + ) + private static native int increment(); + } + + public static class App extends Application { + static final CountDownLatch CDL = new CountDownLatch(1); + private static BorderPane pane; + + /** + * @return the v1 + */ + static WebView getV1() { + return (WebView)System.getProperties().get("v1"); + } + + /** + * @return the v2 + */ + static WebView getV2() { + return (WebView)System.getProperties().get("v2"); + } + + @Override + public void start(Stage stage) { + pane= new BorderPane(); + Scene scene = new Scene(pane, 800, 600); + stage.setScene(scene); + + System.getProperties().put("v1", new WebView()); + System.getProperties().put("v2", new WebView()); + + pane.setCenter(getV1()); + pane.setBottom(getV2()); + + CDL.countDown(); + } + + + } +} http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersTest.java ---------------------------------------------------------------------- diff --git a/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersTest.java b/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersTest.java new file mode 100644 index 0000000..2498c56 --- /dev/null +++ b/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersTest.java @@ -0,0 +1,253 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package net.java.html.boot.fx; + +import java.net.URL; +import java.util.concurrent.CountDownLatch; +import javafx.application.Application; +import javafx.application.Platform; +import javafx.scene.Scene; +import javafx.scene.layout.BorderPane; +import javafx.scene.web.WebView; +import javafx.stage.Stage; +import net.java.html.BrwsrCtx; +import net.java.html.js.JavaScriptBody; +import static org.testng.Assert.*; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * + * @author Jaroslav Tulach + */ +public class FXBrowsersTest { + private static CountDownLatch PROPERTY_SET; + + public FXBrowsersTest() { + } + + @BeforeClass public void initFX() throws Throwable { + new Thread("initFX") { + @Override + public void run() { + if (Platform.isFxApplicationThread()) { + new App().start(new Stage()); + } else { + try { + App.launch(App.class); + } catch (IllegalStateException ex) { + Platform.runLater(this); + } + } + } + }.start(); + App.CDL.await(); + } + + @JavaScriptBody(args = { }, body = "return true;") + static boolean inJS() { + return false; + } + + @Test + public void brwsrCtxExecute() throws Throwable { + assertFalse(inJS(), "We aren't in JS now"); + final CountDownLatch init = new CountDownLatch(1); + final BrwsrCtx[] ctx = { null }; + FXBrowsers.runInBrowser(App.getV1(), new Runnable() { + @Override + public void run() { + assertTrue(inJS(), "We are in JS context now"); + ctx[0] = BrwsrCtx.findDefault(FXBrowsersTest.class); + init.countDown(); + } + }); + init.await(); + + final CountDownLatch cdl = new CountDownLatch(1); + class R implements Runnable { + @Override + public void run() { + if (Platform.isFxApplicationThread()) { + assertTrue(inJS()); + cdl.countDown(); + } else { + ctx[0].execute(this); + } + } + } + new Thread(new R(), "Background thread").start(); + + cdl.await(); + } + + @Test + public void behaviorOfTwoWebViewsAtOnce() throws Throwable { + class R implements Runnable { + Throwable t; + + @Override + public void run() { + try { + doTest(); + } catch (Throwable ex) { + t = ex; + } + } + + private void doTest() throws Throwable { + URL u = FXBrowsersTest.class.getResource("/org/netbeans/html/boot/fx/empty.html"); + assertNotNull(u, "URL found"); + FXBrowsers.load(App.getV1(), u, OnPages.class, "first"); + } + } + R run = new R(); + PROPERTY_SET = new CountDownLatch(2); + Platform.runLater(run); + PROPERTY_SET.await(); + + assertEquals(Integer.getInteger("finalFirst"), Integer.valueOf(3), "Three times in view one"); + assertEquals(Integer.getInteger("finalSecond"), Integer.valueOf(2), "Two times in view one"); + + final CountDownLatch finish = new CountDownLatch(1); + final Object[] three = { 0 }; + assertFalse(Platform.isFxApplicationThread()); + FXBrowsers.runInBrowser(App.getV1(), new Runnable() { + @Override + public void run() { + assertTrue(Platform.isFxApplicationThread()); + three[0] = App.getV1().getEngine().executeScript("window.cntBrwsr"); + finish.countDown(); + } + }); + finish.await(); + + assertEquals(three[0], Integer.valueOf(3)); + } + + public static class OnPages { + static Class<?> first; + static Object firstWindow; + + public static void first() { + first = OnPages.class; + firstWindow = window(); + assertNotNull(firstWindow, "First window found"); + + assertEquals(increment(), 1, "Now it is one"); + + URL u = FXBrowsersTest.class.getResource("/org/netbeans/html/boot/fx/empty.html"); + assertNotNull(u, "URL found"); + FXBrowsers.load(App.getV2(), u, new Runnable() { + @Override + public void run() { + OnPages.second("Hello"); + } + }); + + assertEquals(increment(), 2, "Now it is two and not influenced by second view"); + System.setProperty("finalFirst", "" + increment()); + PROPERTY_SET.countDown(); + } + + public static void second(String... args) { + assertEquals(args.length, 1, "One string argument"); + assertEquals(args[0], "Hello", "It is hello"); + assertEquals(first, OnPages.class, "Both views share the same classloader"); + + Object window = window(); + assertNotNull(window, "Some window found"); + assertNotNull(firstWindow, "First window is known"); + assertNotSame(firstWindow, window, "The window objects should be different"); + + assertEquals(increment(), 1, "Counting starts from zero"); + System.setProperty("finalSecond", "" + increment()); + PROPERTY_SET.countDown(); + } + + @JavaScriptBody(args = {}, body = "return window;") + private static native Object window(); + + @JavaScriptBody(args = {}, body = "" + + "if (window.cntBrwsr) return ++window.cntBrwsr;" + + "return window.cntBrwsr = 1;" + ) + private static native int increment(); + } + + public static class App extends Application { + static final CountDownLatch CDL = new CountDownLatch(1); + private static BorderPane pane; + + /** + * @return the v1 + */ + static WebView getV1() { + return (WebView)System.getProperties().get("v1"); + } + + /** + * @return the v2 + */ + static WebView getV2() { + return (WebView)System.getProperties().get("v2"); + } + + @Override + public void start(Stage stage) { + pane= new BorderPane(); + Scene scene = new Scene(pane, 800, 600); + stage.setScene(scene); + + System.getProperties().put("v1", new WebView()); + System.getProperties().put("v2", new WebView()); + + pane.setCenter(getV1()); + pane.setBottom(getV2()); + + CDL.countDown(); + } + + + } +} http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-fx/src/test/java/org/netbeans/html/boot/fx/FXBrwsrTest.java ---------------------------------------------------------------------- diff --git a/boot-fx/src/test/java/org/netbeans/html/boot/fx/FXBrwsrTest.java b/boot-fx/src/test/java/org/netbeans/html/boot/fx/FXBrwsrTest.java new file mode 100644 index 0000000..1f52998 --- /dev/null +++ b/boot-fx/src/test/java/org/netbeans/html/boot/fx/FXBrwsrTest.java @@ -0,0 +1,84 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package org.netbeans.html.boot.fx; + +import org.sample.app.pkg.SampleApp; +import static org.testng.Assert.assertEquals; +import org.testng.annotations.Test; + +public class FXBrwsrTest { + + public FXBrwsrTest() { + } + + @Test + public void testFindCalleeClassName() throws InterruptedException { + String callee = invokeMain(); + assertEquals(callee, SampleApp.class.getName(), "Callee is found correctly"); + } + + synchronized static String invokeMain() throws InterruptedException { + new Thread("starting main") { + @Override + public void run() { + SampleApp.main(); + } + }.start(); + for (;;) { + String callee = System.getProperty("callee"); + if (callee != null) { + return callee; + } + FXBrwsrTest.class.wait(); + } + } + + + public static void computeCalleeClassName() { + String name = FXBrwsr.findCalleeClassName(); + System.setProperty("callee", name); + synchronized (FXBrwsrTest.class) { + FXBrwsrTest.class.notifyAll(); + } + } +}
