jaroslav@288
|
1 |
/**
|
jaroslav@358
|
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
jaroslav@288
|
3 |
*
|
jaroslav@551
|
4 |
* Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
|
jaroslav@288
|
5 |
*
|
jaroslav@358
|
6 |
* Oracle and Java are registered trademarks of Oracle and/or its affiliates.
|
jaroslav@358
|
7 |
* Other names may be trademarks of their respective owners.
|
jaroslav@288
|
8 |
*
|
jaroslav@358
|
9 |
* The contents of this file are subject to the terms of either the GNU
|
jaroslav@358
|
10 |
* General Public License Version 2 only ("GPL") or the Common
|
jaroslav@358
|
11 |
* Development and Distribution License("CDDL") (collectively, the
|
jaroslav@358
|
12 |
* "License"). You may not use this file except in compliance with the
|
jaroslav@358
|
13 |
* License. You can obtain a copy of the License at
|
jaroslav@358
|
14 |
* http://www.netbeans.org/cddl-gplv2.html
|
jaroslav@358
|
15 |
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
|
jaroslav@358
|
16 |
* specific language governing permissions and limitations under the
|
jaroslav@358
|
17 |
* License. When distributing the software, include this License Header
|
jaroslav@358
|
18 |
* Notice in each file and include the License file at
|
jaroslav@358
|
19 |
* nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
|
jaroslav@358
|
20 |
* particular file as subject to the "Classpath" exception as provided
|
jaroslav@358
|
21 |
* by Oracle in the GPL Version 2 section of the License file that
|
jaroslav@358
|
22 |
* accompanied this code. If applicable, add the following below the
|
jaroslav@358
|
23 |
* License Header, with the fields enclosed by brackets [] replaced by
|
jaroslav@358
|
24 |
* your own identifying information:
|
jaroslav@358
|
25 |
* "Portions Copyrighted [year] [name of copyright owner]"
|
jaroslav@358
|
26 |
*
|
jaroslav@358
|
27 |
* Contributor(s):
|
jaroslav@358
|
28 |
*
|
jaroslav@358
|
29 |
* The Original Software is NetBeans. The Initial Developer of the Original
|
jaroslav@551
|
30 |
* Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
|
jaroslav@358
|
31 |
*
|
jaroslav@358
|
32 |
* If you wish your version of this file to be governed by only the CDDL
|
jaroslav@358
|
33 |
* or only the GPL Version 2, indicate your decision by adding
|
jaroslav@358
|
34 |
* "[Contributor] elects to include this software in this distribution
|
jaroslav@358
|
35 |
* under the [CDDL or GPL Version 2] license." If you do not indicate a
|
jaroslav@358
|
36 |
* single choice of license, a recipient has the option to distribute
|
jaroslav@358
|
37 |
* your version of this file under either the CDDL, the GPL Version 2 or
|
jaroslav@358
|
38 |
* to extend the choice of license to its licensees as provided above.
|
jaroslav@358
|
39 |
* However, if you add GPL Version 2 code and therefore, elected the GPL
|
jaroslav@358
|
40 |
* Version 2 license, then the option applies only if the new code is
|
jaroslav@358
|
41 |
* made subject to such option by the copyright holder.
|
jaroslav@288
|
42 |
*/
|
jaroslav@288
|
43 |
package net.java.html.boot.fx;
|
jaroslav@288
|
44 |
|
jaroslav@288
|
45 |
import java.net.URL;
|
jtulach@656
|
46 |
import javafx.application.Platform;
|
jaroslav@288
|
47 |
import javafx.beans.value.ChangeListener;
|
jaroslav@288
|
48 |
import javafx.beans.value.ObservableValue;
|
jaroslav@288
|
49 |
import javafx.concurrent.Worker;
|
jaroslav@288
|
50 |
import javafx.scene.web.WebView;
|
jtulach@656
|
51 |
import net.java.html.BrwsrCtx;
|
jaroslav@288
|
52 |
import net.java.html.boot.BrowserBuilder;
|
jaroslav@290
|
53 |
import net.java.html.js.JavaScriptBody;
|
jaroslav@362
|
54 |
import org.netbeans.html.boot.fx.AbstractFXPresenter;
|
jtulach@886
|
55 |
import org.netbeans.html.context.spi.Contexts;
|
jtulach@886
|
56 |
import org.netbeans.html.context.spi.Contexts.Id;
|
jaroslav@288
|
57 |
|
jtulach@658
|
58 |
/** Utility methods to use {@link WebView} and {@link JavaScriptBody} code
|
jtulach@658
|
59 |
* in existing <em>JavaFX</em> applications.
|
jtulach@658
|
60 |
* This class is for those who want to instantiate their own {@link WebView},
|
jtulach@658
|
61 |
* configure it manually, embed it into own <em>JavaFX</em>
|
jtulach@658
|
62 |
* application and based on other events in the application
|
jtulach@658
|
63 |
* {@link #runInBrowser(javafx.scene.web.WebView, java.lang.Runnable) re-execute code}
|
jtulach@658
|
64 |
* inside of such {@link WebView}s.
|
jtulach@1006
|
65 |
* <p>
|
jtulach@658
|
66 |
* In case such detailed control is not necessary,
|
jtulach@1006
|
67 |
* consider using {@link BrowserBuilder}. Btw. when using the {@link BrowserBuilder}
|
jtulach@1006
|
68 |
* one can execute presenter in headless mode. Just specify: <code>
|
jtulach@1006
|
69 |
* {@link System}.{@link System#setProperty(java.lang.String, java.lang.String) setProperty}("fxpresenter.headless", "true");
|
jtulach@1006
|
70 |
* </code>
|
jtulach@1006
|
71 |
*
|
jaroslav@627
|
72 |
*
|
jtulach@655
|
73 |
* @author Jaroslav Tulach
|
jaroslav@290
|
74 |
* @since 0.6
|
jaroslav@288
|
75 |
*/
|
jaroslav@288
|
76 |
public final class FXBrowsers {
|
jaroslav@288
|
77 |
private FXBrowsers() {
|
jaroslav@288
|
78 |
}
|
jaroslav@288
|
79 |
|
jaroslav@542
|
80 |
/** Enables the Java/JavaScript bridge (that supports {@link JavaScriptBody} and co.)
|
jaroslav@290
|
81 |
* in the provided <code>webView</code>. This method returns
|
jaroslav@290
|
82 |
* immediately. Once the support is active, it calls back specified
|
jaroslav@290
|
83 |
* method in <code>onPageLoad</code> class - the class can possibly be
|
jaroslav@290
|
84 |
* loaded by a different classloader (to enable replacement of
|
jaroslav@290
|
85 |
* methods with {@link JavaScriptBody} annotations with executable
|
jaroslav@290
|
86 |
* versions). The method <code>methodName</code> needs to be <code>public</code>
|
jaroslav@290
|
87 |
* (in a public class), <code>static</code> and take either no parameters
|
jaroslav@290
|
88 |
* or an array of {@link String}s.
|
jtulach@658
|
89 |
* <p>
|
jtulach@658
|
90 |
* This method sets {@link WebView#getUserData()} and {@link #runInBrowser(javafx.scene.web.WebView, java.lang.Runnable)}
|
jtulach@658
|
91 |
* relies on the value. Please don't alter it.
|
jtulach@886
|
92 |
* <p>
|
jtulach@886
|
93 |
* Since introduction of {@link Id technology identifiers} the
|
jtulach@886
|
94 |
* provided <code>args</code> strings are also passed to the
|
jtulach@886
|
95 |
* {@link BrwsrCtx context} when it is being
|
jtulach@886
|
96 |
* {@link Contexts#newBuilder(java.lang.Object...) created}
|
jtulach@886
|
97 |
* and can influence the selection
|
jtulach@886
|
98 |
* of available technologies
|
jtulach@886
|
99 |
* (like {@link org.netbeans.html.json.spi.Technology},
|
jtulach@886
|
100 |
* {@link org.netbeans.html.json.spi.Transfer} or
|
jtulach@886
|
101 |
* {@link org.netbeans.html.json.spi.WSTransfer}).
|
jaroslav@290
|
102 |
*
|
jaroslav@290
|
103 |
* @param webView the instance of Web View to tweak
|
jaroslav@290
|
104 |
* @param url the URL of the HTML page to load into the view
|
jaroslav@290
|
105 |
* @param onPageLoad callback class with method <code>methodName</code>
|
jaroslav@290
|
106 |
* @param methodName the method to call when the page is loaded
|
jaroslav@290
|
107 |
* @param args arguments to pass to the <code>methodName</code> method
|
jaroslav@290
|
108 |
*/
|
jaroslav@288
|
109 |
public static void load(
|
jaroslav@288
|
110 |
final WebView webView, final URL url,
|
jaroslav@288
|
111 |
Class<?> onPageLoad, String methodName,
|
jaroslav@288
|
112 |
String... args
|
jaroslav@288
|
113 |
) {
|
jtulach@886
|
114 |
Object[] context = new Object[args.length + 1];
|
jtulach@886
|
115 |
System.arraycopy(args, 0, context, 1, args.length);
|
jtulach@886
|
116 |
context[0] = new Load(webView);
|
jtulach@886
|
117 |
BrowserBuilder.newBrowser(context).
|
jaroslav@288
|
118 |
loadPage(url.toExternalForm()).
|
jaroslav@288
|
119 |
loadClass(onPageLoad).
|
jaroslav@288
|
120 |
invoke(methodName, args).
|
jaroslav@288
|
121 |
showAndWait();
|
jaroslav@288
|
122 |
}
|
jtulach@656
|
123 |
|
jtulach@656
|
124 |
/** Enables the Java/JavaScript bridge (that supports {@link JavaScriptBody} and co.)
|
jtulach@656
|
125 |
* in the provided <code>webView</code>. This method returns
|
jtulach@656
|
126 |
* immediately. Once the support is active, it calls back specified
|
jtulach@656
|
127 |
* method in <code>onPageLoad</code>'s run method.
|
jtulach@656
|
128 |
* This is more convenient way to initialize the webview,
|
jtulach@656
|
129 |
* but it requires one to make sure
|
jtulach@656
|
130 |
* all {@link JavaScriptBody} methods has been post-processed during
|
jtulach@656
|
131 |
* compilation and there will be no need to instantiate new classloader.
|
jtulach@658
|
132 |
* <p>
|
jtulach@658
|
133 |
* This method sets {@link WebView#getUserData()} and {@link #runInBrowser(javafx.scene.web.WebView, java.lang.Runnable)}
|
jtulach@658
|
134 |
* relies on the value. Please don't alter it.
|
jtulach@656
|
135 |
*
|
jtulach@656
|
136 |
* @param webView the instance of Web View to tweak
|
jtulach@656
|
137 |
* @param url the URL of the HTML page to load into the view
|
jtulach@656
|
138 |
* @param onPageLoad callback to call when the page is ready
|
jtulach@656
|
139 |
* @since 0.8.1
|
jtulach@656
|
140 |
*/
|
jtulach@656
|
141 |
public static void load(
|
jtulach@656
|
142 |
WebView webView, final URL url, Runnable onPageLoad
|
jtulach@656
|
143 |
) {
|
jtulach@771
|
144 |
load(webView, url, onPageLoad, null);
|
jtulach@771
|
145 |
}
|
jtulach@771
|
146 |
|
jtulach@771
|
147 |
/** Enables the Java/JavaScript bridge (that supports {@link JavaScriptBody} and co.)
|
jtulach@771
|
148 |
* in the provided <code>webView</code>. This method returns
|
jtulach@889
|
149 |
* immediately. Once the support is active, it calls back {@link Runnable#run() run}
|
jtulach@889
|
150 |
* method in <code>onPageLoad</code>.
|
jtulach@771
|
151 |
* This is more convenient way to initialize the webview,
|
jtulach@771
|
152 |
* but it requires one to make sure
|
jtulach@771
|
153 |
* all {@link JavaScriptBody} methods has been post-processed during
|
jtulach@771
|
154 |
* compilation and there will be no need to instantiate new classloader.
|
jtulach@771
|
155 |
* <p>
|
jtulach@771
|
156 |
* This method sets {@link WebView#getUserData()} and {@link #runInBrowser(javafx.scene.web.WebView, java.lang.Runnable)}
|
jtulach@771
|
157 |
* relies on the value. Please don't alter it.
|
jtulach@771
|
158 |
*
|
jtulach@771
|
159 |
* @param webView the instance of Web View to tweak
|
jtulach@771
|
160 |
* @param url the URL of the HTML page to load into the view
|
jtulach@771
|
161 |
* @param onPageLoad callback to call when the page is ready
|
jtulach@771
|
162 |
* @param loader the loader to use when constructing initial {@link BrwsrCtx} or <code>null</code>
|
jtulach@771
|
163 |
* @since 0.9
|
jtulach@771
|
164 |
*/
|
jtulach@771
|
165 |
public static void load(
|
jtulach@771
|
166 |
WebView webView, final URL url, Runnable onPageLoad, ClassLoader loader
|
jtulach@771
|
167 |
) {
|
jtulach@889
|
168 |
load(webView, url, onPageLoad, loader, new Object[0]);
|
jtulach@889
|
169 |
}
|
jtulach@889
|
170 |
|
jtulach@889
|
171 |
/** Enables the Java/JavaScript bridge (that supports {@link JavaScriptBody} and co.)
|
jtulach@889
|
172 |
* in the provided <code>webView</code>. This method returns
|
jtulach@889
|
173 |
* immediately. Once the support is active, it calls back {@link Runnable#run() run}
|
jtulach@889
|
174 |
* method in <code>onPageLoad</code>.
|
jtulach@889
|
175 |
* This is more convenient way to initialize the webview,
|
jtulach@889
|
176 |
* but it requires one to make sure
|
jtulach@889
|
177 |
* all {@link JavaScriptBody} methods has been post-processed during
|
jtulach@889
|
178 |
* compilation and there will be no need to instantiate new classloader.
|
jtulach@889
|
179 |
* <p>
|
jtulach@889
|
180 |
* This method sets {@link WebView#getUserData()} and {@link #runInBrowser(javafx.scene.web.WebView, java.lang.Runnable)}
|
jtulach@889
|
181 |
* relies on the value. Please don't alter it.
|
jtulach@889
|
182 |
*
|
jtulach@889
|
183 |
* @param webView the instance of Web View to tweak
|
jtulach@889
|
184 |
* @param url the URL of the HTML page to load into the view
|
jtulach@889
|
185 |
* @param onPageLoad callback to call when the page is ready
|
jtulach@889
|
186 |
* @param loader the loader to use when constructing initial {@link BrwsrCtx} or <code>null</code>
|
jtulach@889
|
187 |
* @param context additonal configuration to pass to {@link BrowserBuilder#newBrowser(java.lang.Object...)}
|
jtulach@889
|
188 |
* and {@link Contexts#newBuilder(java.lang.Object...)} factory methods
|
jtulach@889
|
189 |
* @since 1.1
|
jtulach@889
|
190 |
*/
|
jtulach@889
|
191 |
public static void load(
|
jtulach@889
|
192 |
WebView webView, final URL url, Runnable onPageLoad, ClassLoader loader,
|
jtulach@889
|
193 |
Object... context
|
jtulach@889
|
194 |
) {
|
jtulach@889
|
195 |
Object[] newCtx = new Object[context.length + 1];
|
jtulach@889
|
196 |
System.arraycopy(context, 0, newCtx, 1, context.length);
|
jtulach@889
|
197 |
newCtx[0] = new Load(webView);
|
jtulach@889
|
198 |
BrowserBuilder.newBrowser(newCtx).
|
jtulach@656
|
199 |
loadPage(url.toExternalForm()).
|
jtulach@656
|
200 |
loadFinished(onPageLoad).
|
jtulach@771
|
201 |
classloader(loader).
|
jtulach@656
|
202 |
showAndWait();
|
jtulach@656
|
203 |
}
|
jtulach@656
|
204 |
|
jtulach@656
|
205 |
/** Executes a code inside of provided {@link WebView}. This method
|
jaroslav@752
|
206 |
* associates the {@link BrwsrCtx execution context} with provided browser,
|
jtulach@658
|
207 |
* so the {@link JavaScriptBody} annotations know where to execute
|
jtulach@658
|
208 |
* their JavaScript bodies.
|
jtulach@658
|
209 |
* The code is going to be executed synchronously
|
jtulach@656
|
210 |
* in case {@link Platform#isFxApplicationThread()} returns <code>true</code>.
|
jtulach@656
|
211 |
* Otherwise this method returns immediately and the code is executed
|
jtulach@656
|
212 |
* later via {@link Platform#runLater(java.lang.Runnable)}.
|
jtulach@658
|
213 |
* <p>
|
jtulach@658
|
214 |
* This method relies on {@link WebView#getUserData()} being properly
|
jtulach@658
|
215 |
* provided by the <code>load</code> methods in this class.
|
jtulach@656
|
216 |
*
|
jtulach@656
|
217 |
* @param webView the web view previously prepared by one of the <code>load</code>
|
jaroslav@752
|
218 |
* methods in this class
|
jtulach@656
|
219 |
* @param code the code to execute
|
jtulach@656
|
220 |
* @throws IllegalArgumentException if the web view was not properly
|
jtulach@656
|
221 |
* initialized
|
jtulach@656
|
222 |
* @see BrwsrCtx#execute(java.lang.Runnable)
|
jtulach@656
|
223 |
* @since 0.8.1
|
jtulach@656
|
224 |
*/
|
jtulach@656
|
225 |
public static void runInBrowser(WebView webView, Runnable code) {
|
jtulach@656
|
226 |
Object ud = webView.getUserData();
|
jtulach@656
|
227 |
if (!(ud instanceof Load)) {
|
jtulach@656
|
228 |
throw new IllegalArgumentException();
|
jtulach@656
|
229 |
}
|
jtulach@656
|
230 |
((Load)ud).execute(code);
|
jtulach@656
|
231 |
}
|
jtulach@656
|
232 |
|
jtulach@656
|
233 |
private static class Load extends AbstractFXPresenter {
|
jtulach@656
|
234 |
private final WebView webView;
|
jtulach@656
|
235 |
|
jtulach@656
|
236 |
public Load(WebView webView) {
|
jtulach@656
|
237 |
webView.setUserData(this);
|
jtulach@656
|
238 |
this.webView = webView;
|
jtulach@656
|
239 |
}
|
jtulach@656
|
240 |
|
jtulach@656
|
241 |
@Override
|
jtulach@656
|
242 |
protected void waitFinished() {
|
jtulach@656
|
243 |
// don't wait
|
jtulach@656
|
244 |
}
|
jtulach@656
|
245 |
|
jtulach@656
|
246 |
@Override
|
jtulach@656
|
247 |
protected WebView findView(final URL resource) {
|
jtulach@656
|
248 |
final Worker<Void> w = webView.getEngine().getLoadWorker();
|
jtulach@656
|
249 |
w.stateProperty().addListener(new ChangeListener<Worker.State>() {
|
jtulach@656
|
250 |
private String previous;
|
jtulach@656
|
251 |
|
jtulach@656
|
252 |
@Override
|
jtulach@656
|
253 |
public void changed(ObservableValue<? extends Worker.State> ov, Worker.State t, Worker.State newState) {
|
jtulach@656
|
254 |
if (newState.equals(Worker.State.SUCCEEDED)) {
|
jtulach@656
|
255 |
if (checkValid()) {
|
jtulach@656
|
256 |
onPageLoad();
|
jtulach@656
|
257 |
}
|
jtulach@656
|
258 |
}
|
jtulach@656
|
259 |
if (newState.equals(Worker.State.FAILED)) {
|
jtulach@656
|
260 |
checkValid();
|
jtulach@656
|
261 |
throw new IllegalStateException("Failed to load " + resource);
|
jtulach@656
|
262 |
}
|
jtulach@656
|
263 |
}
|
jtulach@656
|
264 |
|
jtulach@656
|
265 |
private boolean checkValid() {
|
jtulach@656
|
266 |
final String crnt = webView.getEngine().getLocation();
|
jtulach@656
|
267 |
if (previous != null && !previous.equals(crnt)) {
|
jtulach@656
|
268 |
w.stateProperty().removeListener(this);
|
jtulach@656
|
269 |
return false;
|
jtulach@656
|
270 |
}
|
jtulach@656
|
271 |
previous = crnt;
|
jtulach@656
|
272 |
return true;
|
jtulach@656
|
273 |
}
|
jtulach@656
|
274 |
});
|
jtulach@656
|
275 |
|
jtulach@656
|
276 |
return webView;
|
jtulach@656
|
277 |
}
|
jtulach@656
|
278 |
}
|
jtulach@656
|
279 |
|
jaroslav@288
|
280 |
}
|