Geolocation SPI that seems to run OK in bck2brwsr (post 0.9 version) GeoSPI
authorJaroslav Tulach <jtulach@netbeans.org>
Wed, 27 Aug 2014 17:28:30 +0200
branchGeoSPI
changeset 842ebb2428bb7e1
parent 841 e673fd71b81b
child 843 5ed774012807
Geolocation SPI that seems to run OK in bck2brwsr (post 0.9 version)
geo/src/main/java/net/java/html/geo/Position.java
geo/src/main/java/org/netbeans/html/geo/impl/JsG.java
geo/src/main/java/org/netbeans/html/geo/impl/JsGLProvider.java
geo/src/main/java/org/netbeans/html/geo/spi/CoordImpl.java
geo/src/main/java/org/netbeans/html/geo/spi/GLProvider.java
geo/src/test/java/net/java/html/geo/OnLocationTest.java
geo/src/test/java/org/netbeans/html/geo/spi/CoordImplTest.java
     1.1 --- a/geo/src/main/java/net/java/html/geo/Position.java	Tue Aug 26 18:38:36 2014 +0200
     1.2 +++ b/geo/src/main/java/net/java/html/geo/Position.java	Wed Aug 27 17:28:30 2014 +0200
     1.3 @@ -42,9 +42,15 @@
     1.4   */
     1.5  package net.java.html.geo;
     1.6  
     1.7 +import java.util.Collections;
     1.8 +import java.util.ServiceLoader;
     1.9  import java.util.logging.Level;
    1.10  import java.util.logging.Logger;
    1.11 -import org.netbeans.html.geo.impl.JsG;
    1.12 +import net.java.html.BrwsrCtx;
    1.13 +import org.netbeans.html.context.spi.Contexts;
    1.14 +import org.netbeans.html.geo.impl.JsGLProvider;
    1.15 +import org.netbeans.html.geo.spi.GLProvider;
    1.16 +import org.netbeans.html.geo.spi.GLProvider.Callback;
    1.17  
    1.18  /** Class that represents a geolocation position provided as a callback
    1.19   * to {@link Handle#onLocation(net.java.html.geo.Position)} method. The
    1.20 @@ -59,10 +65,9 @@
    1.21      private final long timestamp;
    1.22      private final Coordinates coords;
    1.23  
    1.24 -    Position(Object position) {
    1.25 -        Object obj = JsG.get(position, "timestamp");
    1.26 -        timestamp = obj instanceof Number ? ((Number)obj).longValue() : 0L;
    1.27 -        coords = new Coordinates(JsG.get(position, "coords"));
    1.28 +    public Position(long timestamp, Coordinates coords) {
    1.29 +        this.timestamp = timestamp;
    1.30 +        this.coords = coords;
    1.31      }
    1.32      
    1.33      /** The actual location of the position.
    1.34 @@ -83,26 +88,22 @@
    1.35       *  Mimics closely <a href="http://www.w3.org/TR/geolocation-API/">
    1.36       * W3C's Geolocation API</a>.
    1.37       */
    1.38 -    public static final class Coordinates {
    1.39 -        private final Object data;
    1.40 -
    1.41 -        Coordinates(Object data) {
    1.42 -            this.data = data;
    1.43 +    public static abstract class Coordinates {
    1.44 +        protected Coordinates() {
    1.45 +            if (!getClass().getName().equals("org.netbeans.html.geo.spi.CoordImpl")) {
    1.46 +                throw new IllegalStateException();
    1.47 +            }
    1.48          }
    1.49          
    1.50          /**
    1.51           * @return geographic coordinate specified in decimal degrees.
    1.52           */
    1.53 -        public double getLatitude() {
    1.54 -            return ((Number)JsG.get(data, "latitude")).doubleValue(); // NOI18N
    1.55 -        }
    1.56 +        public abstract double getLatitude();
    1.57  
    1.58          /**
    1.59           * @return geographic coordinate specified in decimal degrees.
    1.60           */
    1.61 -        public double getLongitude() {
    1.62 -            return ((Number)JsG.get(data, "longitude")).doubleValue(); // NOI18N
    1.63 -        } 
    1.64 +        public abstract double getLongitude();
    1.65  
    1.66          /**
    1.67           * The accuracy attribute denotes the accuracy level of the latitude 
    1.68 @@ -111,18 +112,14 @@
    1.69           * 
    1.70           * @return accuracy in meters
    1.71           */
    1.72 -        public double getAccuracy() {
    1.73 -            return ((Number)JsG.get(data, "accuracy")).doubleValue(); // NOI18N
    1.74 -        }
    1.75 +        public abstract double getAccuracy();
    1.76          
    1.77          /** Denotes the height of the position, specified in meters above the ellipsoid. 
    1.78           * If the implementation cannot provide altitude information, 
    1.79           * the value of this attribute must be null.
    1.80           * @return value in meters, may return null, if the information is not available 
    1.81           */
    1.82 -        public Double getAltitude() {
    1.83 -            return (Double)JsG.get(data, "altitude"); // NOI18N
    1.84 -        }
    1.85 +        public abstract Double getAltitude();
    1.86          
    1.87          /**  The altitude accuracy is specified in meters. 
    1.88           * If the implementation cannot provide altitude information, 
    1.89 @@ -130,19 +127,22 @@
    1.90           * must be a non-negative real number.
    1.91           * @return value in meters; may return null, if the information is not available 
    1.92           */
    1.93 -        public Double getAltitudeAccuracy() {
    1.94 -            return (Double)JsG.get(data, "altitudeAccuracy"); // NOI18N
    1.95 -        }
    1.96 +        public abstract Double getAltitudeAccuracy();
    1.97          
    1.98 -        /** @return may return null, if the information is not available */
    1.99 -        public Double getHeading() {
   1.100 -            return (Double)JsG.get(data, "heading"); // NOI18N
   1.101 -        }
   1.102 +        /** Denotes the direction of travel of the device and 
   1.103 +         * is specified in degrees, where 0° ≤ heading < 360°, 
   1.104 +         * counting clockwise relative to the true north. 
   1.105 +         * 
   1.106 +         * @return may return null, if the information is not available 
   1.107 +         */
   1.108 +        public abstract Double getHeading();
   1.109          
   1.110 -        /** @return may return null, if the information is not available */
   1.111 -        public Double getSpeed() {
   1.112 -            return (Double)JsG.get(data, "speed"); // NOI18N
   1.113 -        }
   1.114 +        /** Denotes the magnitude of the horizontal component of the 
   1.115 +         * device's current velocity and is specified in meters per second.
   1.116 +         * 
   1.117 +         * @return may return null, if the information is not available 
   1.118 +         */
   1.119 +        public abstract Double getSpeed();
   1.120      } // end of Coordinates
   1.121  
   1.122      /** Rather than subclassing this class directly consider using {@link OnLocation}
   1.123 @@ -155,7 +155,7 @@
   1.124          private boolean enableHighAccuracy;
   1.125          private long timeout;
   1.126          private long maximumAge;
   1.127 -        volatile JsH handle;
   1.128 +        volatile JsH<?> handle;
   1.129  
   1.130          /** Creates new instance of this handle.
   1.131           * 
   1.132 @@ -184,7 +184,12 @@
   1.133           * @return true, if one can call {@link #start}.
   1.134           */
   1.135          public final boolean isSupported() {
   1.136 -            return JsG.hasGeolocation();
   1.137 +            JsH<?> p = seekProviders(null, null);
   1.138 +            if (p != null) {
   1.139 +                p.stop();
   1.140 +                return true;
   1.141 +            }
   1.142 +            return false;
   1.143          }
   1.144  
   1.145          /** Turns on high accuracy mode as specified by the 
   1.146 @@ -224,38 +229,107 @@
   1.147              if (handle != null) {
   1.148                  return;
   1.149              }
   1.150 -            handle = new JsH();
   1.151 +            
   1.152 +            Exception[] problem = { null };
   1.153 +            StringBuilder sb = new StringBuilder();
   1.154 +            sb.append("[");
   1.155 +            JsH<?> h = seekProviders(sb, problem);
   1.156 +            sb.append("\n]");
   1.157              try {
   1.158 -                if (!isSupported()) {
   1.159 -                    throw new IllegalStateException("geolocation API not supported");
   1.160 +                if (problem[0] != null) {
   1.161 +                    onError(problem[0]);
   1.162 +                    return;
   1.163                  }
   1.164 -                handle.start();
   1.165 -            } catch (Exception ex) {
   1.166 +                if (h == null) {
   1.167 +                    onError(new IllegalStateException("geolocation API not supported. Among providers: " + sb));
   1.168 +                }
   1.169 +                synchronized (this) {
   1.170 +                    if (handle != null) {
   1.171 +                        onError(new IllegalStateException("Parallel request"));
   1.172 +                    }
   1.173 +                    handle = h;
   1.174 +                }
   1.175 +            } catch (Throwable thr) {
   1.176 +                LOG.log(Level.INFO, "Problems delivering onError report", thr);
   1.177 +            }
   1.178 +        }
   1.179 +
   1.180 +        private JsH<?> seekProviders(StringBuilder sb, Exception[] problem) {
   1.181 +            BrwsrCtx ctx = BrwsrCtx.findDefault(getClass());
   1.182 +            JsH<?> h = seekProviders(Contexts.find(ctx, GLProvider.class), null, sb, problem);
   1.183 +            if (h == null) {
   1.184 +                h = seekProviders(null, ServiceLoader.load(GLProvider.class), sb, problem);
   1.185 +            }
   1.186 +            if (h == null) {
   1.187 +                h = seekProviders(new JsGLProvider(), null, sb, problem);
   1.188 +            }
   1.189 +            return h;
   1.190 +        }
   1.191 +
   1.192 +        private JsH<?> seekProviders(
   1.193 +            GLProvider single, Iterable<GLProvider> set,
   1.194 +            StringBuilder sb, Exception[] problem
   1.195 +        ) {
   1.196 +            if (set == null) {
   1.197 +                if (single == null) {
   1.198 +                    return null;
   1.199 +                }
   1.200 +                set = Collections.singleton(single);
   1.201 +            }
   1.202 +            JsH<?> h = null;
   1.203 +            for (GLProvider<?,?> p : set) {
   1.204 +                if (sb != null) {
   1.205 +                    if (sb.length() > 1) {
   1.206 +                        sb.append(',');
   1.207 +                    }
   1.208 +                    sb.append("\n  ").append(p.getClass().getName());
   1.209 +                }
   1.210                  try {
   1.211 -                    onError(ex);
   1.212 -                } catch (Throwable thr) {
   1.213 -                    LOG.log(Level.INFO, "Problems delivering onError report", thr);
   1.214 +                    h = createHandle(p);
   1.215 +                } catch (Exception ex) {
   1.216 +                    LOG.log(Level.INFO, "Problems when starting " + p.getClass().getName(), ex);
   1.217 +                    if (problem != null && problem[0] == null) {
   1.218 +                        problem[0] = ex;
   1.219 +                    }
   1.220 +                }
   1.221 +                if (h != null) {
   1.222 +                    break;
   1.223                  }
   1.224              }
   1.225 +            return h;
   1.226          }
   1.227  
   1.228          /** Stops all pending requests. After this call no further callbacks
   1.229           * can be obtained. Does nothing if no query or watch was in progress.
   1.230           */
   1.231          public final void stop() {
   1.232 -            JsH h = handle;
   1.233 -            if (h == null) {
   1.234 -                return;
   1.235 +            JsH h;
   1.236 +            synchronized (this) {
   1.237 +                h = handle;
   1.238 +                if (h == null) {
   1.239 +                    return;
   1.240 +                }
   1.241 +                handle = null;
   1.242              }
   1.243 -            handle = null;
   1.244              h.stop();
   1.245          }
   1.246 +        
   1.247 +        private <Watch> JsH<Watch> createHandle(GLProvider<?,Watch> p) {
   1.248 +            JsH<Watch> temp = new JsH<Watch>(p);
   1.249 +            return temp.watch == null ? null : temp;
   1.250 +        }
   1.251  
   1.252 -        private final class JsH extends JsG {
   1.253 -            long watch;
   1.254 +        private final class JsH<Watch> extends Callback {
   1.255 +            private final GLProvider<?, Watch> provider;
   1.256 +            private final Watch watch;
   1.257 +            
   1.258 +            public JsH(GLProvider<?, Watch> p) {
   1.259 +                this.provider = p;
   1.260 +                this.watch = start(p, oneTime, enableHighAccuracy, timeout, maximumAge);
   1.261 +            }
   1.262              
   1.263              @Override
   1.264 -            public void onLocation(Object position) {
   1.265 +            public void onLocation(Position position) {
   1.266                  if (handle != this) {
   1.267                      return;
   1.268                  }
   1.269 @@ -263,14 +337,14 @@
   1.270                      stop();
   1.271                  }
   1.272                  try {
   1.273 -                    Handle.this.onLocation(new Position(position));
   1.274 +                    Handle.this.onLocation(position);
   1.275                  } catch (Throwable ex) {
   1.276                      LOG.log(Level.SEVERE, null, ex);
   1.277                  }
   1.278              }
   1.279  
   1.280              @Override
   1.281 -            public void onError(final String message, int code) {
   1.282 +            public void onError(Exception err) {
   1.283                  if (handle != this) {
   1.284                      return;
   1.285                  }
   1.286 @@ -278,24 +352,14 @@
   1.287                      stop();
   1.288                  }
   1.289                  try {
   1.290 -                    final Exception err = new Exception(message + " errno: " + code) {
   1.291 -                        @Override
   1.292 -                        public String getLocalizedMessage() {
   1.293 -                            return message;
   1.294 -                        }
   1.295 -                    };
   1.296                      Handle.this.onError(err);
   1.297                  } catch (Throwable ex) {
   1.298                      LOG.log(Level.SEVERE, null, ex);
   1.299                  }
   1.300              }
   1.301  
   1.302 -            final void start() {
   1.303 -                watch = start(oneTime, enableHighAccuracy, timeout, maximumAge);
   1.304 -            }
   1.305 -
   1.306              protected final void stop() {
   1.307 -                super.stop(watch);
   1.308 +                super.stop(provider, watch);
   1.309              }
   1.310          }
   1.311      }
     2.1 --- a/geo/src/main/java/org/netbeans/html/geo/impl/JsG.java	Tue Aug 26 18:38:36 2014 +0200
     2.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.3 @@ -1,107 +0,0 @@
     2.4 -/**
     2.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     2.6 - *
     2.7 - * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     2.8 - *
     2.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    2.10 - * Other names may be trademarks of their respective owners.
    2.11 - *
    2.12 - * The contents of this file are subject to the terms of either the GNU
    2.13 - * General Public License Version 2 only ("GPL") or the Common
    2.14 - * Development and Distribution License("CDDL") (collectively, the
    2.15 - * "License"). You may not use this file except in compliance with the
    2.16 - * License. You can obtain a copy of the License at
    2.17 - * http://www.netbeans.org/cddl-gplv2.html
    2.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    2.19 - * specific language governing permissions and limitations under the
    2.20 - * License.  When distributing the software, include this License Header
    2.21 - * Notice in each file and include the License file at
    2.22 - * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    2.23 - * particular file as subject to the "Classpath" exception as provided
    2.24 - * by Oracle in the GPL Version 2 section of the License file that
    2.25 - * accompanied this code. If applicable, add the following below the
    2.26 - * License Header, with the fields enclosed by brackets [] replaced by
    2.27 - * your own identifying information:
    2.28 - * "Portions Copyrighted [year] [name of copyright owner]"
    2.29 - *
    2.30 - * Contributor(s):
    2.31 - *
    2.32 - * The Original Software is NetBeans. The Initial Developer of the Original
    2.33 - * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    2.34 - *
    2.35 - * If you wish your version of this file to be governed by only the CDDL
    2.36 - * or only the GPL Version 2, indicate your decision by adding
    2.37 - * "[Contributor] elects to include this software in this distribution
    2.38 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
    2.39 - * single choice of license, a recipient has the option to distribute
    2.40 - * your version of this file under either the CDDL, the GPL Version 2 or
    2.41 - * to extend the choice of license to its licensees as provided above.
    2.42 - * However, if you add GPL Version 2 code and therefore, elected the GPL
    2.43 - * Version 2 license, then the option applies only if the new code is
    2.44 - * made subject to such option by the copyright holder.
    2.45 - */
    2.46 -package org.netbeans.html.geo.impl;
    2.47 -
    2.48 -import net.java.html.js.JavaScriptBody;
    2.49 -
    2.50 -/** Implementation class to deal with browser's <code>navigator.geolocation</code> 
    2.51 - * object.
    2.52 - *
    2.53 - * @author Jaroslav Tulach
    2.54 - */
    2.55 -public abstract class JsG {
    2.56 -    protected JsG() {
    2.57 -        if (!getClass().getName().equals("net.java.html.geo.Position$Handle$JsH")) {
    2.58 -            throw new IllegalStateException();
    2.59 -        }
    2.60 -    }
    2.61 -    
    2.62 -    public abstract void onLocation(Object position);
    2.63 -    public abstract void onError(String message, int code);
    2.64 -    
    2.65 -    @JavaScriptBody(args = {}, body = "return !!navigator.geolocation;")
    2.66 -    public static boolean hasGeolocation() {
    2.67 -        return false;
    2.68 -    }
    2.69 -
    2.70 -    @JavaScriptBody(
    2.71 -        args = { "onlyOnce", "enableHighAccuracy", "timeout", "maximumAge" }, 
    2.72 -        javacall = true, 
    2.73 -        body = 
    2.74 -        "var self = this;\n" +
    2.75 -        "var ok = function (position) {\n" +
    2.76 -        "  self.@org.netbeans.html.geo.impl.JsG::onLocation(Ljava/lang/Object;)(position);\n" +
    2.77 -        "};\n" +
    2.78 -        "var fail = function (error) {\n" +
    2.79 -        "  self.@org.netbeans.html.geo.impl.JsG::onError(Ljava/lang/String;I)(error.message, error.code);\n" +
    2.80 -        "};\n" +
    2.81 -        "var options = {};\n" +
    2.82 -        "options.enableHighAccuracy = enableHighAccuracy;\n" +
    2.83 -        "if (timeout >= 0) options.timeout = timeout;\n" +
    2.84 -        "if (maximumAge >= 0) options.maximumAge = maximumAge;\n" +
    2.85 -        "if (onlyOnce) {\n" +
    2.86 -        "  navigator.geolocation.getCurrentPosition(ok, fail, options);\n" +
    2.87 -        "  return 0;\n" +
    2.88 -        "} else {\n" +
    2.89 -        "  return navigator.geolocation.watchPosition(ok, fail, options);\n" +
    2.90 -        "}\n"
    2.91 -    )
    2.92 -    protected long start(
    2.93 -        boolean onlyOnce, 
    2.94 -        boolean enableHighAccuracy,
    2.95 -        long timeout,
    2.96 -        long maximumAge
    2.97 -    ) {
    2.98 -        return -1;
    2.99 -    }
   2.100 -    
   2.101 -    @JavaScriptBody(args = { "watch" }, body = "navigator.geolocation.clearWatch(watch);")
   2.102 -    protected void stop(long watch) {
   2.103 -    }
   2.104 -
   2.105 -    @JavaScriptBody(args = { "self", "property" }, body = "return self[property];")
   2.106 -    public static Object get(Object self, String property) {
   2.107 -        return null;
   2.108 -    }
   2.109 -
   2.110 -}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/geo/src/main/java/org/netbeans/html/geo/impl/JsGLProvider.java	Wed Aug 27 17:28:30 2014 +0200
     3.3 @@ -0,0 +1,154 @@
     3.4 +/**
     3.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3.6 + *
     3.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     3.8 + *
     3.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    3.10 + * Other names may be trademarks of their respective owners.
    3.11 + *
    3.12 + * The contents of this file are subject to the terms of either the GNU
    3.13 + * General Public License Version 2 only ("GPL") or the Common
    3.14 + * Development and Distribution License("CDDL") (collectively, the
    3.15 + * "License"). You may not use this file except in compliance with the
    3.16 + * License. You can obtain a copy of the License at
    3.17 + * http://www.netbeans.org/cddl-gplv2.html
    3.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    3.19 + * specific language governing permissions and limitations under the
    3.20 + * License.  When distributing the software, include this License Header
    3.21 + * Notice in each file and include the License file at
    3.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    3.23 + * particular file as subject to the "Classpath" exception as provided
    3.24 + * by Oracle in the GPL Version 2 section of the License file that
    3.25 + * accompanied this code. If applicable, add the following below the
    3.26 + * License Header, with the fields enclosed by brackets [] replaced by
    3.27 + * your own identifying information:
    3.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    3.29 + *
    3.30 + * Contributor(s):
    3.31 + *
    3.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    3.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    3.34 + *
    3.35 + * If you wish your version of this file to be governed by only the CDDL
    3.36 + * or only the GPL Version 2, indicate your decision by adding
    3.37 + * "[Contributor] elects to include this software in this distribution
    3.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    3.39 + * single choice of license, a recipient has the option to distribute
    3.40 + * your version of this file under either the CDDL, the GPL Version 2 or
    3.41 + * to extend the choice of license to its licensees as provided above.
    3.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    3.43 + * Version 2 license, then the option applies only if the new code is
    3.44 + * made subject to such option by the copyright holder.
    3.45 + */
    3.46 +package org.netbeans.html.geo.impl;
    3.47 +
    3.48 +import net.java.html.js.JavaScriptBody;
    3.49 +import org.netbeans.html.geo.spi.GLProvider;
    3.50 +
    3.51 +/** Implementation class to deal with browser's <code>navigator.geolocation</code> 
    3.52 + * object.
    3.53 + *
    3.54 + * @author Jaroslav Tulach
    3.55 + */
    3.56 +public final class JsGLProvider extends GLProvider<Object, Long> {
    3.57 +    public JsGLProvider() {
    3.58 +    }
    3.59 +    
    3.60 +    @JavaScriptBody(args = {}, body = "return !!navigator.geolocation;")
    3.61 +    private static boolean hasGeolocation() {
    3.62 +        return false;
    3.63 +    }
    3.64 +
    3.65 +    @JavaScriptBody(
    3.66 +        args = { "c", "onlyOnce", "enableHighAccuracy", "timeout", "maximumAge" }, 
    3.67 +        javacall = true, 
    3.68 +        body = 
    3.69 +        "var self = this;\n" +
    3.70 +        "var ok = function (position) {\n" +
    3.71 +        "  self.@org.netbeans.html.geo.impl.JsGLProvider::onLocation(Ljava/lang/Object;Ljava/lang/Object;)(c, position);\n" +
    3.72 +        "};\n" +
    3.73 +        "var fail = function (error) {\n" +
    3.74 +        "  self.@org.netbeans.html.geo.impl.JsGLProvider::onError(Ljava/lang/Object;Ljava/lang/String;I)(c, error.message, error.code);\n" +
    3.75 +        "};\n" +
    3.76 +        "var options = {};\n" +
    3.77 +        "options.enableHighAccuracy = enableHighAccuracy;\n" +
    3.78 +        "if (timeout >= 0) options.timeout = timeout;\n" +
    3.79 +        "if (maximumAge >= 0) options.maximumAge = maximumAge;\n" +
    3.80 +        "if (onlyOnce) {\n" +
    3.81 +        "  navigator.geolocation.getCurrentPosition(ok, fail, options);\n" +
    3.82 +        "  return 0;\n" +
    3.83 +        "} else {\n" +
    3.84 +        "  return navigator.geolocation.watchPosition(ok, fail, options);\n" +
    3.85 +        "}\n"
    3.86 +    )
    3.87 +    private long doStart(
    3.88 +        Callback c,
    3.89 +        boolean onlyOnce, 
    3.90 +        boolean enableHighAccuracy,
    3.91 +        long timeout,
    3.92 +        long maximumAge
    3.93 +    ) {
    3.94 +        return -1;
    3.95 +    }
    3.96 +    
    3.97 +    protected void stop(long watch) {
    3.98 +    }
    3.99 +
   3.100 +    @Override
   3.101 +    public Long start(Callback c, boolean oneTime, boolean enableHighAccuracy, long timeout, long maximumAge) {
   3.102 +        if (!hasGeolocation()) {
   3.103 +            return null;
   3.104 +        }
   3.105 +        return doStart(c, oneTime, enableHighAccuracy, timeout, maximumAge);
   3.106 +    }
   3.107 +    
   3.108 +    final void onLocation(Object c, Object p) {
   3.109 +        callback((Callback)c, timeStamp(p), p, null);
   3.110 +    }
   3.111 +    
   3.112 +    final void onError(Object c, final String msg, int code) {
   3.113 +        final Exception err = new Exception(msg + " errno: " + code) {
   3.114 +            @Override
   3.115 +            public String getLocalizedMessage() {
   3.116 +                return msg;
   3.117 +            }
   3.118 +        };
   3.119 +        callback((Callback)c, 0L, null, err);
   3.120 +    }
   3.121 +
   3.122 +    @Override
   3.123 +    @JavaScriptBody(args = {"watch"}, body = "navigator.geolocation.clearWatch(watch);")
   3.124 +    public native void stop(Long watch);
   3.125 +
   3.126 +    @JavaScriptBody(args = { "p" }, body = "return p.timestamp;")
   3.127 +    private static native long timeStamp(Object position);
   3.128 +
   3.129 +    @Override
   3.130 +    @JavaScriptBody(args = { "coords" }, body = "return coords.coords.latitude;")
   3.131 +    protected native double latitude(Object coords);
   3.132 +
   3.133 +    @Override
   3.134 +    @JavaScriptBody(args = { "coords" }, body = "return coords.coords.longitude;")
   3.135 +    protected native double longitude(Object coords);
   3.136 +
   3.137 +    @Override
   3.138 +    @JavaScriptBody(args = { "coords" }, body = "return coords.coords.accuracy;")
   3.139 +    protected native double accuracy(Object coords);
   3.140 +
   3.141 +    @Override
   3.142 +    @JavaScriptBody(args = {"coords"}, body = "return coords.coords.altitude ? coords.coords.altitude : null;")
   3.143 +    protected native Double altitude(Object coords);
   3.144 +
   3.145 +    @Override
   3.146 +    @JavaScriptBody(args = {"coords"}, body = "return coords.coords.altitudeAccuracy ? coords.coords.altitudeAccuracy : null;")
   3.147 +    protected native Double altitudeAccuracy(Object coords);
   3.148 +
   3.149 +    @Override
   3.150 +    @JavaScriptBody(args = {"coords"}, body = "return coords.coords.heading ? coords.coords.heading : null;")
   3.151 +    protected native Double heading(Object coords);
   3.152 +
   3.153 +    @Override
   3.154 +    @JavaScriptBody(args = {"coords"}, body = "return coords.coords.speed ? coords.coords.speed : null;")
   3.155 +    protected native Double speed(Object coords);
   3.156 +
   3.157 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/geo/src/main/java/org/netbeans/html/geo/spi/CoordImpl.java	Wed Aug 27 17:28:30 2014 +0200
     4.3 @@ -0,0 +1,87 @@
     4.4 +/**
     4.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     4.6 + *
     4.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     4.8 + *
     4.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    4.10 + * Other names may be trademarks of their respective owners.
    4.11 + *
    4.12 + * The contents of this file are subject to the terms of either the GNU
    4.13 + * General Public License Version 2 only ("GPL") or the Common
    4.14 + * Development and Distribution License("CDDL") (collectively, the
    4.15 + * "License"). You may not use this file except in compliance with the
    4.16 + * License. You can obtain a copy of the License at
    4.17 + * http://www.netbeans.org/cddl-gplv2.html
    4.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    4.19 + * specific language governing permissions and limitations under the
    4.20 + * License.  When distributing the software, include this License Header
    4.21 + * Notice in each file and include the License file at
    4.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    4.23 + * particular file as subject to the "Classpath" exception as provided
    4.24 + * by Oracle in the GPL Version 2 section of the License file that
    4.25 + * accompanied this code. If applicable, add the following below the
    4.26 + * License Header, with the fields enclosed by brackets [] replaced by
    4.27 + * your own identifying information:
    4.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    4.29 + *
    4.30 + * Contributor(s):
    4.31 + *
    4.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    4.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    4.34 + *
    4.35 + * If you wish your version of this file to be governed by only the CDDL
    4.36 + * or only the GPL Version 2, indicate your decision by adding
    4.37 + * "[Contributor] elects to include this software in this distribution
    4.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    4.39 + * single choice of license, a recipient has the option to distribute
    4.40 + * your version of this file under either the CDDL, the GPL Version 2 or
    4.41 + * to extend the choice of license to its licensees as provided above.
    4.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    4.43 + * Version 2 license, then the option applies only if the new code is
    4.44 + * made subject to such option by the copyright holder.
    4.45 + */
    4.46 +package org.netbeans.html.geo.spi;
    4.47 +
    4.48 +import net.java.html.geo.Position;
    4.49 +
    4.50 +/**
    4.51 + *
    4.52 + * @author Jaroslav Tulach
    4.53 + */
    4.54 +final class CoordImpl<Coords> extends Position.Coordinates {
    4.55 +    private final Coords data;
    4.56 +    private final GLProvider<Coords, ?> provider;
    4.57 +
    4.58 +    CoordImpl(Coords data, GLProvider<Coords, ?> p) {
    4.59 +        this.data = data;
    4.60 +        this.provider = p;
    4.61 +    }
    4.62 +
    4.63 +    @Override public double getLatitude() {
    4.64 +        return provider.latitude(data);
    4.65 +    }
    4.66 +
    4.67 +    @Override public double getLongitude() {
    4.68 +        return provider.longitude(data);
    4.69 +    }
    4.70 +
    4.71 +    @Override public double getAccuracy() {
    4.72 +        return provider.accuracy(data);
    4.73 +    }
    4.74 +
    4.75 +    @Override public Double getAltitude() {
    4.76 +        return provider.altitude(data);
    4.77 +    }
    4.78 +
    4.79 +    @Override public Double getAltitudeAccuracy() {
    4.80 +        return provider.altitudeAccuracy(data);
    4.81 +    }
    4.82 +
    4.83 +    @Override public Double getHeading() {
    4.84 +        return provider.heading(data);
    4.85 +    }
    4.86 +
    4.87 +    @Override public Double getSpeed() {
    4.88 +        return provider.speed(data);
    4.89 +    }
    4.90 +} // end of CoordImpl
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/geo/src/main/java/org/netbeans/html/geo/spi/GLProvider.java	Wed Aug 27 17:28:30 2014 +0200
     5.3 @@ -0,0 +1,99 @@
     5.4 +/**
     5.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     5.6 + *
     5.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     5.8 + *
     5.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    5.10 + * Other names may be trademarks of their respective owners.
    5.11 + *
    5.12 + * The contents of this file are subject to the terms of either the GNU
    5.13 + * General Public License Version 2 only ("GPL") or the Common
    5.14 + * Development and Distribution License("CDDL") (collectively, the
    5.15 + * "License"). You may not use this file except in compliance with the
    5.16 + * License. You can obtain a copy of the License at
    5.17 + * http://www.netbeans.org/cddl-gplv2.html
    5.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    5.19 + * specific language governing permissions and limitations under the
    5.20 + * License.  When distributing the software, include this License Header
    5.21 + * Notice in each file and include the License file at
    5.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    5.23 + * particular file as subject to the "Classpath" exception as provided
    5.24 + * by Oracle in the GPL Version 2 section of the License file that
    5.25 + * accompanied this code. If applicable, add the following below the
    5.26 + * License Header, with the fields enclosed by brackets [] replaced by
    5.27 + * your own identifying information:
    5.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    5.29 + *
    5.30 + * Contributor(s):
    5.31 + *
    5.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    5.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    5.34 + *
    5.35 + * If you wish your version of this file to be governed by only the CDDL
    5.36 + * or only the GPL Version 2, indicate your decision by adding
    5.37 + * "[Contributor] elects to include this software in this distribution
    5.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    5.39 + * single choice of license, a recipient has the option to distribute
    5.40 + * your version of this file under either the CDDL, the GPL Version 2 or
    5.41 + * to extend the choice of license to its licensees as provided above.
    5.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    5.43 + * Version 2 license, then the option applies only if the new code is
    5.44 + * made subject to such option by the copyright holder.
    5.45 + */
    5.46 +
    5.47 +package org.netbeans.html.geo.spi;
    5.48 +
    5.49 +import net.java.html.geo.Position;
    5.50 +
    5.51 +/**
    5.52 + *
    5.53 + * @author Jaroslav Tulach
    5.54 + */
    5.55 +public abstract class GLProvider<Coords,Watch> {
    5.56 +    protected abstract Watch start(
    5.57 +        Callback c, 
    5.58 +        boolean oneTime, boolean enableHighAccuracy, 
    5.59 +        long timeout, long maximumAge
    5.60 +    );
    5.61 +    protected abstract void stop(Watch watch);
    5.62 +    
    5.63 +    protected final void callback(
    5.64 +        Callback c,
    5.65 +        long timestamp, Coords position,
    5.66 +        Exception ex
    5.67 +    ) {
    5.68 +        if (ex == null) {
    5.69 +            c.onLocation(new Position(timestamp, new CoordImpl<Coords>(position, this)));
    5.70 +        } else {
    5.71 +            c.onError(ex);
    5.72 +        }
    5.73 +    }
    5.74 +    
    5.75 +    protected abstract double latitude(Coords coords);
    5.76 +    protected abstract double longitude(Coords coords);
    5.77 +    protected abstract double accuracy(Coords coords);
    5.78 +    protected abstract Double altitude(Coords coords);
    5.79 +    protected abstract Double altitudeAccuracy(Coords coords);
    5.80 +    protected abstract Double heading(Coords coords);
    5.81 +    protected abstract Double speed(Coords coords);
    5.82 +    
    5.83 +    
    5.84 +    public static abstract class Callback {
    5.85 +        protected Callback() {
    5.86 +            if (!getClass().getName().equals("net.java.html.geo.Position$Handle$JsH")) {
    5.87 +                throw new IllegalStateException();
    5.88 +            }
    5.89 +        }
    5.90 +        
    5.91 +        protected final <Watch> Watch start(GLProvider<?,Watch> p, boolean oneTime, boolean enableHighAccuracy, long timeout, long maximumAge) {
    5.92 +            return p.start(this, oneTime, enableHighAccuracy, timeout, maximumAge);
    5.93 +        }
    5.94 +        
    5.95 +        protected final <Watch> void stop(GLProvider<?,Watch> p, Watch w) {
    5.96 +            p.stop(w);
    5.97 +        }
    5.98 +        
    5.99 +        protected abstract void onLocation(Position p);
   5.100 +        protected abstract void onError(Exception ex);
   5.101 +    }
   5.102 +}
     6.1 --- a/geo/src/test/java/net/java/html/geo/OnLocationTest.java	Tue Aug 26 18:38:36 2014 +0200
     6.2 +++ b/geo/src/test/java/net/java/html/geo/OnLocationTest.java	Wed Aug 27 17:28:30 2014 +0200
     6.3 @@ -66,7 +66,7 @@
     6.4      @Test public void onLocationHandleCallback() throws Throwable {
     6.5          net.java.html.geo.Position.Handle h = OnLocationHandle.createQuery();
     6.6          cnt = 0;
     6.7 -        h.onLocation(new Position(null));
     6.8 +        h.onLocation(new Position(0L, null));
     6.9          assertEquals(cnt, 1, "The callback has been made");
    6.10      }
    6.11  
    6.12 @@ -104,7 +104,7 @@
    6.13      @Test public void onInstanceCallback() throws Throwable {
    6.14          OnLocationTest t = new OnLocationTest();
    6.15          net.java.html.geo.Position.Handle h = InstanceHandle.createWatch(t);
    6.16 -        h.onLocation(new Position(null));
    6.17 +        h.onLocation(new Position(0L, null));
    6.18          assertEquals(t.instCnt, 1, "One callback made");
    6.19      }
    6.20  
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/geo/src/test/java/org/netbeans/html/geo/spi/CoordImplTest.java	Wed Aug 27 17:28:30 2014 +0200
     7.3 @@ -0,0 +1,111 @@
     7.4 +/**
     7.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     7.6 + *
     7.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     7.8 + *
     7.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    7.10 + * Other names may be trademarks of their respective owners.
    7.11 + *
    7.12 + * The contents of this file are subject to the terms of either the GNU
    7.13 + * General Public License Version 2 only ("GPL") or the Common
    7.14 + * Development and Distribution License("CDDL") (collectively, the
    7.15 + * "License"). You may not use this file except in compliance with the
    7.16 + * License. You can obtain a copy of the License at
    7.17 + * http://www.netbeans.org/cddl-gplv2.html
    7.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    7.19 + * specific language governing permissions and limitations under the
    7.20 + * License.  When distributing the software, include this License Header
    7.21 + * Notice in each file and include the License file at
    7.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    7.23 + * particular file as subject to the "Classpath" exception as provided
    7.24 + * by Oracle in the GPL Version 2 section of the License file that
    7.25 + * accompanied this code. If applicable, add the following below the
    7.26 + * License Header, with the fields enclosed by brackets [] replaced by
    7.27 + * your own identifying information:
    7.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    7.29 + *
    7.30 + * Contributor(s):
    7.31 + *
    7.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    7.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    7.34 + *
    7.35 + * If you wish your version of this file to be governed by only the CDDL
    7.36 + * or only the GPL Version 2, indicate your decision by adding
    7.37 + * "[Contributor] elects to include this software in this distribution
    7.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    7.39 + * single choice of license, a recipient has the option to distribute
    7.40 + * your version of this file under either the CDDL, the GPL Version 2 or
    7.41 + * to extend the choice of license to its licensees as provided above.
    7.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    7.43 + * Version 2 license, then the option applies only if the new code is
    7.44 + * made subject to such option by the copyright holder.
    7.45 + */
    7.46 +
    7.47 +package org.netbeans.html.geo.spi;
    7.48 +
    7.49 +import static org.testng.Assert.*;
    7.50 +import org.testng.annotations.AfterClass;
    7.51 +import org.testng.annotations.AfterMethod;
    7.52 +import org.testng.annotations.BeforeClass;
    7.53 +import org.testng.annotations.BeforeMethod;
    7.54 +import org.testng.annotations.Test;
    7.55 +
    7.56 +/**
    7.57 + *
    7.58 + * @author Jaroslav Tulach
    7.59 + */
    7.60 +public class CoordImplTest extends GLProvider<Double, Object> {
    7.61 +    
    7.62 +    public CoordImplTest() {
    7.63 +    }
    7.64 +    @Test public void testGetLatitude() {
    7.65 +        CoordImpl<Double> c = new CoordImpl<Double>(50.5, this);
    7.66 +        assertEquals(c.getLatitude(), 50.5, 0.1, "Latitude returned as provided");
    7.67 +    }
    7.68 +
    7.69 +    @Override
    7.70 +    protected Object start(Callback c, boolean oneTime, boolean enableHighAccuracy, long timeout, long maximumAge) {
    7.71 +        throw new UnsupportedOperationException();
    7.72 +    }
    7.73 +
    7.74 +    @Override
    7.75 +    protected void stop(Object watch) {
    7.76 +        throw new UnsupportedOperationException();
    7.77 +    }
    7.78 +
    7.79 +    @Override
    7.80 +    protected double latitude(Double coords) {
    7.81 +        return coords;
    7.82 +    }
    7.83 +
    7.84 +    @Override
    7.85 +    protected double longitude(Double coords) {
    7.86 +        throw new UnsupportedOperationException();
    7.87 +    }
    7.88 +
    7.89 +    @Override
    7.90 +    protected double accuracy(Double coords) {
    7.91 +        throw new UnsupportedOperationException();
    7.92 +    }
    7.93 +
    7.94 +    @Override
    7.95 +    protected Double altitude(Double coords) {
    7.96 +        throw new UnsupportedOperationException();
    7.97 +    }
    7.98 +
    7.99 +    @Override
   7.100 +    protected Double altitudeAccuracy(Double coords) {
   7.101 +        throw new UnsupportedOperationException();
   7.102 +    }
   7.103 +
   7.104 +    @Override
   7.105 +    protected Double heading(Double coords) {
   7.106 +        throw new UnsupportedOperationException();
   7.107 +    }
   7.108 +
   7.109 +    @Override
   7.110 +    protected Double speed(Double coords) {
   7.111 +        throw new UnsupportedOperationException();
   7.112 +    }
   7.113 +    
   7.114 +}