2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
6 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7 * Other names may be trademarks of their respective owners.
9 * The contents of this file are subject to the terms of either the GNU
10 * General Public License Version 2 only ("GPL") or the Common
11 * Development and Distribution License("CDDL") (collectively, the
12 * "License"). You may not use this file except in compliance with the
13 * License. You can obtain a copy of the License at
14 * http://www.netbeans.org/cddl-gplv2.html
15 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16 * specific language governing permissions and limitations under the
17 * License. When distributing the software, include this License Header
18 * Notice in each file and include the License file at
19 * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
20 * particular file as subject to the "Classpath" exception as provided
21 * by Oracle in the GPL Version 2 section of the License file that
22 * accompanied this code. If applicable, add the following below the
23 * License Header, with the fields enclosed by brackets [] replaced by
24 * your own identifying information:
25 * "Portions Copyrighted [year] [name of copyright owner]"
29 * The Original Software is NetBeans. The Initial Developer of the Original
30 * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
32 * If you wish your version of this file to be governed by only the CDDL
33 * or only the GPL Version 2, indicate your decision by adding
34 * "[Contributor] elects to include this software in this distribution
35 * under the [CDDL or GPL Version 2] license." If you do not indicate a
36 * single choice of license, a recipient has the option to distribute
37 * your version of this file under either the CDDL, the GPL Version 2 or
38 * to extend the choice of license to its licensees as provided above.
39 * However, if you add GPL Version 2 code and therefore, elected the GPL
40 * Version 2 license, then the option applies only if the new code is
41 * made subject to such option by the copyright holder.
43 package org.netbeans.html.boot.fx;
46 import java.util.ResourceBundle;
47 import java.util.concurrent.CountDownLatch;
48 import java.util.concurrent.Executors;
49 import java.util.logging.Level;
50 import java.util.logging.Logger;
51 import java.util.prefs.Preferences;
52 import javafx.application.Application;
53 import javafx.application.Platform;
54 import javafx.beans.value.ChangeListener;
55 import javafx.beans.value.ObservableValue;
56 import javafx.concurrent.Worker;
57 import javafx.event.ActionEvent;
58 import javafx.event.EventHandler;
59 import javafx.geometry.Insets;
60 import javafx.geometry.Pos;
61 import javafx.geometry.Rectangle2D;
62 import javafx.scene.Scene;
63 import javafx.scene.control.Button;
64 import javafx.scene.control.TextField;
65 import javafx.scene.layout.BorderPane;
66 import javafx.scene.layout.HBox;
67 import javafx.scene.layout.VBox;
68 import javafx.scene.text.Text;
69 import javafx.scene.web.PromptData;
70 import javafx.scene.web.WebEvent;
71 import javafx.scene.web.WebView;
72 import javafx.stage.Modality;
73 import javafx.stage.Screen;
74 import javafx.stage.Stage;
75 import javafx.stage.Window;
76 import javafx.stage.WindowEvent;
77 import javafx.util.Callback;
79 /** This is an implementation class, to implement browser builder API. Just
80 * include this JAR on classpath and the browser builder API will find
81 * this implementation automatically.
83 public class FXBrwsr extends Application {
84 private static final Logger LOG = Logger.getLogger(FXBrwsr.class.getName());
85 private static FXBrwsr INSTANCE;
86 private static final CountDownLatch FINISHED = new CountDownLatch(1);
87 private BorderPane root;
89 public static synchronized WebView findWebView(final URL url, final FXPresenter onLoad) {
90 if (INSTANCE == null) {
91 final String callee = findCalleeClassName();
92 Executors.newFixedThreadPool(1).submit(new Runnable() {
96 FXBrwsr.launch(FXBrwsr.class, callee);
97 } catch (Throwable ex) {
100 FINISHED.countDown();
105 while (INSTANCE == null) {
107 FXBrwsr.class.wait();
108 } catch (InterruptedException ex) {
112 if (!Platform.isFxApplicationThread()) {
113 final WebView[] arr = {null};
114 final CountDownLatch waitForResult = new CountDownLatch(1);
115 Platform.runLater(new Runnable() {
118 arr[0] = INSTANCE.newView(url, onLoad);
119 waitForResult.countDown();
124 waitForResult.await();
126 } catch (InterruptedException ex) {
127 LOG.log(Level.INFO, null, ex);
132 return INSTANCE.newView(url, onLoad);
136 static synchronized Stage findStage() throws InterruptedException {
137 while (INSTANCE == null) {
138 FXBrwsr.class.wait();
140 return INSTANCE.stage;
146 public void start(Stage primaryStage) throws Exception {
147 BorderPane r = new BorderPane();
148 Object[] arr = findInitialSize(this.getParameters().getRaw().get(0));
149 Scene scene = new Scene(r, (Double)arr[2], (Double)arr[3]);
150 primaryStage.setScene(scene);
152 this.stage = primaryStage;
153 synchronized (FXBrwsr.class) {
155 FXBrwsr.class.notifyAll();
157 primaryStage.setX((Double)arr[0]);
158 primaryStage.setY((Double)arr[1]);
159 if (arr[4] != null) {
160 scene.getWindow().setOnCloseRequest((EventHandler<WindowEvent>) arr[4]);
162 if (Boolean.getBoolean("fxpresenter.headless")) {
168 static String findCalleeClassName() {
169 StackTraceElement[] frames = new Exception().getStackTrace();
170 for (StackTraceElement e : frames) {
171 String cn = e.getClassName();
172 if (cn.startsWith("org.netbeans.html.")) { // NOI18N
175 if (cn.startsWith("net.java.html.")) { // NOI18N
178 if (cn.startsWith("java.")) { // NOI18N
181 if (cn.startsWith("javafx.")) { // NOI18N
184 if (cn.startsWith("com.sun.")) { // NOI18N
189 return "org.netbeans.html"; // NOI18N
192 private static Object[] findInitialSize(String callee) {
193 final Preferences prefs = Preferences.userRoot().node(callee.replace('.', '/'));
194 Rectangle2D screen = Screen.getPrimary().getBounds();
195 double x = prefs.getDouble("x", screen.getWidth() * 0.05); // NOI18N
196 double y = prefs.getDouble("y", screen.getHeight() * 0.05); // NOI18N
197 double width = prefs.getDouble("width", screen.getWidth() * 0.9); // NOI18N
198 double height = prefs.getDouble("height", screen.getHeight() * 0.9); // NOI18N
201 x, y, width, height, null
204 if (!callee.equals("org.netbeans.html")) { // NOI18N
205 arr[4] = new EventHandler<WindowEvent>() {
207 public void handle(WindowEvent event) {
208 Window window = (Window) event.getSource();
209 prefs.putDouble("x", window.getX()); // NOI18N
210 prefs.putDouble("y", window.getY()); // NOI18N
211 prefs.putDouble("width", window.getWidth()); // NOI18N
212 prefs.putDouble("height", window.getHeight()); // NOI18N
220 private WebView newView(final URL url, final FXPresenter onLoad) {
221 final WebView view = new WebView();
222 view.setContextMenuEnabled(false);
223 view.getEngine().setOnAlert(new EventHandler<WebEvent<String>>() {
225 public void handle(WebEvent<String> t) {
226 final Stage dialogStage = new Stage();
227 dialogStage.initModality(Modality.WINDOW_MODAL);
228 dialogStage.initOwner(stage);
229 ResourceBundle r = ResourceBundle.getBundle("org/netbeans/html/boot/fx/Bundle"); // NOI18N
230 dialogStage.setTitle(r.getString("AlertTitle")); // NOI18N
231 final Button button = new Button(r.getString("AlertCloseButton")); // NOI18N
232 final Text text = new Text(t.getData());
233 VBox box = new VBox();
234 box.setAlignment(Pos.CENTER);
236 box.setPadding(new Insets(10));
237 box.getChildren().addAll(text, button);
238 dialogStage.setScene(new Scene(box));
239 button.setCancelButton(true);
240 button.setOnAction(new CloseDialogHandler(dialogStage, null));
241 dialogStage.centerOnScreen();
242 dialogStage.showAndWait();
245 view.getEngine().setConfirmHandler(new Callback<String, Boolean>() {
247 public Boolean call(String question) {
248 final Stage dialogStage = new Stage();
249 dialogStage.initModality(Modality.WINDOW_MODAL);
250 dialogStage.initOwner(stage);
251 ResourceBundle r = ResourceBundle.getBundle("org/netbeans/html/boot/fx/Bundle"); // NOI18N
252 dialogStage.setTitle(r.getString("ConfirmTitle")); // NOI18N
253 final Button ok = new Button(r.getString("ConfirmOKButton")); // NOI18N
254 final Button cancel = new Button(r.getString("ConfirmCancelButton")); // NOI18N
255 final Text text = new Text(question);
256 final Insets ins = new Insets(10);
257 final VBox box = new VBox();
258 box.setAlignment(Pos.CENTER);
261 final HBox buttons = new HBox(10);
262 buttons.getChildren().addAll(ok, cancel);
263 buttons.setAlignment(Pos.CENTER);
264 buttons.setPadding(ins);
265 box.getChildren().addAll(text, buttons);
266 dialogStage.setScene(new Scene(box));
267 ok.setCancelButton(false);
269 final boolean[] res = new boolean[1];
270 ok.setOnAction(new CloseDialogHandler(dialogStage, res));
271 cancel.setCancelButton(true);
272 cancel.setOnAction(new CloseDialogHandler(dialogStage, null));
273 dialogStage.centerOnScreen();
274 dialogStage.showAndWait();
278 view.getEngine().setPromptHandler(new Callback<PromptData, String>() {
280 public String call(PromptData prompt) {
281 final Stage dialogStage = new Stage();
282 dialogStage.initModality(Modality.WINDOW_MODAL);
283 dialogStage.initOwner(stage);
284 ResourceBundle r = ResourceBundle.getBundle("org/netbeans/html/boot/fx/Bundle"); // NOI18N
285 dialogStage.setTitle(r.getString("PromptTitle")); // NOI18N
286 final Button ok = new Button(r.getString("PromptOKButton")); // NOI18N
287 final Button cancel = new Button(r.getString("PromptCancelButton")); // NOI18N
288 final Text text = new Text(prompt.getMessage());
289 final TextField line = new TextField();
290 if (prompt.getDefaultValue() != null) {
291 line.setText(prompt.getDefaultValue());
293 final Insets ins = new Insets(10);
294 final VBox box = new VBox();
295 box.setAlignment(Pos.CENTER);
298 final HBox buttons = new HBox(10);
299 buttons.getChildren().addAll(ok, cancel);
300 buttons.setAlignment(Pos.CENTER);
301 buttons.setPadding(ins);
302 box.getChildren().addAll(text, line, buttons);
303 dialogStage.setScene(new Scene(box));
304 ok.setCancelButton(false);
306 final boolean[] res = new boolean[1];
307 ok.setOnAction(new CloseDialogHandler(dialogStage, res));
308 cancel.setCancelButton(true);
309 cancel.setOnAction(new CloseDialogHandler(dialogStage, null));
310 dialogStage.centerOnScreen();
311 dialogStage.showAndWait();
312 return res[0] ? line.getText() : null;
315 root.setCenter(view);
316 final Worker<Void> w = view.getEngine().getLoadWorker();
317 w.stateProperty().addListener(new ChangeListener<Worker.State>() {
318 private String previous;
321 public void changed(ObservableValue<? extends Worker.State> ov, Worker.State t, Worker.State newState) {
322 if (newState.equals(Worker.State.SUCCEEDED)) {
324 FXConsole.register(view.getEngine());
328 if (newState.equals(Worker.State.FAILED)) {
329 throw new IllegalStateException("Failed to load " + url);
332 private boolean checkValid() {
333 final String crnt = view.getEngine().getLocation();
334 if (previous != null && !previous.equals(crnt)) {
335 w.stateProperty().removeListener(this);
343 class Title implements ChangeListener<String> {
345 private String title;
352 public void changed(ObservableValue<? extends String> ov, String t, String t1) {
353 title = view.getEngine().getTitle();
355 stage.setTitle(title);
359 final Title x = new Title();
360 view.getEngine().titleProperty().addListener(x);
361 x.changed(null, null, null);
365 static void waitFinished() {
370 } catch (InterruptedException ex) {
371 LOG.log(Level.INFO, null, ex);
376 private static final class CloseDialogHandler implements EventHandler<ActionEvent> {
377 private final Stage dialogStage;
378 private final boolean[] res;
380 public CloseDialogHandler(Stage dialogStage, boolean[] res) {
381 this.dialogStage = dialogStage;
386 public void handle(ActionEvent t) {