WebCore: Geolocation PositionOptions does not use correct default values.
authordarin@apple.com <darin@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 13 Aug 2009 23:34:12 +0000 (23:34 +0000)
committerdarin@apple.com <darin@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 13 Aug 2009 23:34:12 +0000 (23:34 +0000)
https://bugs.webkit.org/show_bug.cgi?id=27254

Patch by Steve Block <steveblock@google.com> on 2009-08-13
Reviewed by Darin Adler.

Sets the correct default values for the properties of the PositionOptions object
passed to Geolocation methods. See http://www.w3.org/TR/geolocation-API/.

Also adds checking to throw an exception if arguments of the incorrect type are
passed to Geolocation methods. Adds a layout test to test this. This test should
pass on all platforms where Geolocation is implemented.

Test: fast/dom/Geolocation/argument-types.html

* bindings/js/JSGeolocationCustom.cpp:
(WebCore::createPositionCallback): Added. Creates the callback.
(WebCore::createPositionErrorCallback): Added. Creates the calback.
(WebCore::createPositionOptions): Added. Creates the PositionOptions object, setting defaults where required.
(WebCore::JSGeolocation::getCurrentPosition): Modified. Now uses above helper functions.
(WebCore::JSGeolocation::watchPosition): Modified. Now uses above helper functions.
* page/Geolocation.cpp:
(WebCore::Geolocation::GeoNotifier::GeoNotifier): Modified. Asserts that PositionOptions object is present.
(WebCore::Geolocation::GeoNotifier::startTimer): Modified. Only starts timer if timeout has been set.
* page/PositionOptions.h:
(WebCore::PositionOptions::create): Modified. Creates object with default values.
(WebCore::PositionOptions::timeout): Modified. Uses correct type.
(WebCore::PositionOptions::hasTimeout): Added. Determines whether a timeout has been set.
(WebCore::PositionOptions::setTimeout): Modified. Uses correct type.
(WebCore::PositionOptions::maximumAge): Modified. Uses correct type.
(WebCore::PositionOptions::setMaximumAge): Modified. Uses correct type.
(WebCore::PositionOptions::PositionOptions): Modified. Creates object with default values.

LayoutTests: Geolocation PositionOptions does not use correct default values.
https://bugs.webkit.org/show_bug.cgi?id=27254

Patch by Steve Block <steveblock@google.com> on 2009-08-13
Reviewed by Darin Adler.

Adds a layout test to check the acceptable types for arguments to Geolocation methods.

* fast/dom/Geolocation/resources/argument-types.js: Added. Tests the types of the arguments that can be passed to Geolocation methods.
* fast/dom/Geolocation/argument-types.html: Added. HTML wrapper for above JS test.
* fast/dom/Geolocation/argument-types-expected.txt: Added. Expected output for above test.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@47252 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/fast/dom/Geolocation/argument-types-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/Geolocation/argument-types.html [new file with mode: 0644]
LayoutTests/fast/dom/Geolocation/resources/argument-types.js [new file with mode: 0644]
WebCore/ChangeLog
WebCore/bindings/js/JSGeolocationCustom.cpp
WebCore/page/Geolocation.cpp
WebCore/page/PositionOptions.h

index 1151b41..9faf3a8 100644 (file)
@@ -1,3 +1,16 @@
+2009-08-13  Steve Block  <steveblock@google.com>
+
+        Reviewed by Darin Adler.
+
+        Geolocation PositionOptions does not use correct default values.
+        https://bugs.webkit.org/show_bug.cgi?id=27254
+
+        Adds a layout test to check the acceptable types for arguments to Geolocation methods.
+
+        * fast/dom/Geolocation/resources/argument-types.js: Added. Tests the types of the arguments that can be passed to Geolocation methods.
+        * fast/dom/Geolocation/argument-types.html: Added. HTML wrapper for above JS test.
+        * fast/dom/Geolocation/argument-types-expected.txt: Added. Expected output for above test.
+
 2009-08-13  Eric Seidel  <eric@webkit.org>
 
         Rubber-stamped by Mark Rowe.
diff --git a/LayoutTests/fast/dom/Geolocation/argument-types-expected.txt b/LayoutTests/fast/dom/Geolocation/argument-types-expected.txt
new file mode 100644 (file)
index 0000000..052ecd2
--- /dev/null
@@ -0,0 +1,68 @@
+Tests the acceptable types for arguments to Geolocation methods.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS navigator.geolocation.getCurrentPosition() threw exception Error: TYPE_MISMATCH_ERR: DOM Exception 17.
+PASS navigator.geolocation.getCurrentPosition(undefined) threw exception Error: TYPE_MISMATCH_ERR: DOM Exception 17.
+PASS navigator.geolocation.getCurrentPosition(null) threw exception Error: TYPE_MISMATCH_ERR: DOM Exception 17.
+PASS navigator.geolocation.getCurrentPosition({}) threw exception Error: TYPE_MISMATCH_ERR: DOM Exception 17.
+PASS navigator.geolocation.getCurrentPosition(objectThrowingException) threw exception Error: TYPE_MISMATCH_ERR: DOM Exception 17.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(Math.abs) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(true) threw exception Error: TYPE_MISMATCH_ERR: DOM Exception 17.
+PASS navigator.geolocation.getCurrentPosition(42) threw exception Error: TYPE_MISMATCH_ERR: DOM Exception 17.
+PASS navigator.geolocation.getCurrentPosition("string") threw exception Error: TYPE_MISMATCH_ERR: DOM Exception 17.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, null) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, {}) threw exception Error: TYPE_MISMATCH_ERR: DOM Exception 17.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, objectThrowingException) threw exception Error: TYPE_MISMATCH_ERR: DOM Exception 17.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, emptyFunction) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, Math.abs) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, true) threw exception Error: TYPE_MISMATCH_ERR: DOM Exception 17.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, 42) threw exception Error: TYPE_MISMATCH_ERR: DOM Exception 17.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, "string") threw exception Error: TYPE_MISMATCH_ERR: DOM Exception 17.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, undefined) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, null) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, objectThrowingException) threw exception Error: enableHighAccuracy getter exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, emptyFunction) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, true) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, 42) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, "string") did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {dummyProperty:undefined}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {dummyProperty:null}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {dummyProperty:{}}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {dummyProperty:objectThrowingException}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {dummyProperty:emptyFunction}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {dummyProperty:true}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {dummyProperty:42}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {dummyProperty:"string"}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {enableHighAccuracy:undefined}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {enableHighAccuracy:null}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {enableHighAccuracy:{}}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {enableHighAccuracy:objectThrowingException}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {enableHighAccuracy:emptyFunction}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {enableHighAccuracy:true}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {enableHighAccuracy:42}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {enableHighAccuracy:"string"}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {maximumAge:undefined}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {maximumAge:null}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {maximumAge:{}}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {maximumAge:objectThrowingException}) threw exception Error: valueOf threw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {maximumAge:emptyFunction}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {maximumAge:true}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {maximumAge:42}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {maximumAge:"string"}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {timeout:undefined}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {timeout:null}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {timeout:{}}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {timeout:objectThrowingException}) threw exception Error: valueOf threw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {timeout:emptyFunction}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {timeout:true}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {timeout:42}) did not throw exception.
+PASS navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {timeout:"string"}) did not throw exception.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/Geolocation/argument-types.html b/LayoutTests/fast/dom/Geolocation/argument-types.html
new file mode 100644 (file)
index 0000000..8779bbf
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="../../js/resources/js-test-style.css">
+<script src="../../js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="resources/argument-types.js"></script>
+<script src="../../js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/dom/Geolocation/resources/argument-types.js b/LayoutTests/fast/dom/Geolocation/resources/argument-types.js
new file mode 100644 (file)
index 0000000..7f109c3
--- /dev/null
@@ -0,0 +1,104 @@
+description("Tests the acceptable types for arguments to Geolocation methods.");
+
+function shouldNotThrow(expression)
+{
+  try {
+    eval(expression);
+    testPassed(expression + " did not throw exception.");
+  } catch(e) {
+    testFailed(expression + " should not throw exception. Threw exception " + e);
+  }
+}
+
+function test(expression, expressionShouldThrow, expectedException) {
+    if (expressionShouldThrow) {
+        if (expectedException)
+            shouldThrow(expression, '(function() { return "' + expectedException + '"; })();');
+        else
+            shouldThrow(expression, '(function() { return "Error: TYPE_MISMATCH_ERR: DOM Exception 17"; })();');
+    } else {
+        shouldNotThrow(expression);
+    }
+}
+
+var emptyFunction = function() {};
+
+function ObjectThrowingException() {};
+ObjectThrowingException.prototype.valueOf = function() {
+    throw new Error('valueOf threw exception');
+}
+ObjectThrowingException.prototype.__defineGetter__("enableHighAccuracy", function() {
+    throw new Error('enableHighAccuracy getter exception');
+});
+var objectThrowingException = new ObjectThrowingException();
+
+
+test('navigator.geolocation.getCurrentPosition()', true);
+
+test('navigator.geolocation.getCurrentPosition(undefined)', true);
+test('navigator.geolocation.getCurrentPosition(null)', true);
+test('navigator.geolocation.getCurrentPosition({})', true);
+test('navigator.geolocation.getCurrentPosition(objectThrowingException)', true);
+test('navigator.geolocation.getCurrentPosition(emptyFunction)', false);
+test('navigator.geolocation.getCurrentPosition(Math.abs)', false);
+test('navigator.geolocation.getCurrentPosition(true)', true);
+test('navigator.geolocation.getCurrentPosition(42)', true);
+test('navigator.geolocation.getCurrentPosition("string")', true);
+
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined)', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, null)', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, {})', true);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, objectThrowingException)', true);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, emptyFunction)', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, Math.abs)', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, true)', true);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, 42)', true);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, "string")', true);
+
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, undefined)', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, null)', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, objectThrowingException)', true, 'Error: enableHighAccuracy getter exception');
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, emptyFunction)', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, true)', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, 42)', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, "string")', false);
+
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {dummyProperty:undefined})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {dummyProperty:null})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {dummyProperty:{}})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {dummyProperty:objectThrowingException})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {dummyProperty:emptyFunction})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {dummyProperty:true})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {dummyProperty:42})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {dummyProperty:"string"})', false);
+
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {enableHighAccuracy:undefined})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {enableHighAccuracy:null})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {enableHighAccuracy:{}})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {enableHighAccuracy:objectThrowingException})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {enableHighAccuracy:emptyFunction})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {enableHighAccuracy:true})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {enableHighAccuracy:42})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {enableHighAccuracy:"string"})', false);
+
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {maximumAge:undefined})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {maximumAge:null})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {maximumAge:{}})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {maximumAge:objectThrowingException})', true, 'Error: valueOf threw exception');
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {maximumAge:emptyFunction})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {maximumAge:true})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {maximumAge:42})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {maximumAge:"string"})', false);
+
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {timeout:undefined})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {timeout:null})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {timeout:{}})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {timeout:objectThrowingException})', true, 'Error: valueOf threw exception');
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {timeout:emptyFunction})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {timeout:true})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {timeout:42})', false);
+test('navigator.geolocation.getCurrentPosition(emptyFunction, undefined, {timeout:"string"})', false);
+
+var successfullyParsed = true;
+
index c8c3396..d82288e 100644 (file)
@@ -1,3 +1,37 @@
+2009-08-13  Steve Block  <steveblock@google.com>
+
+        Reviewed by Darin Adler.
+
+        Geolocation PositionOptions does not use correct default values.
+        https://bugs.webkit.org/show_bug.cgi?id=27254
+
+        Sets the correct default values for the properties of the PositionOptions object
+        passed to Geolocation methods. See http://www.w3.org/TR/geolocation-API/.
+
+        Also adds checking to throw an exception if arguments of the incorrect type are
+        passed to Geolocation methods. Adds a layout test to test this. This test should
+        pass on all platforms where Geolocation is implemented.
+
+        Test: fast/dom/Geolocation/argument-types.html
+
+        * bindings/js/JSGeolocationCustom.cpp:
+        (WebCore::createPositionCallback): Added. Creates the callback.
+        (WebCore::createPositionErrorCallback): Added. Creates the calback.
+        (WebCore::createPositionOptions): Added. Creates the PositionOptions object, setting defaults where required.
+        (WebCore::JSGeolocation::getCurrentPosition): Modified. Now uses above helper functions.
+        (WebCore::JSGeolocation::watchPosition): Modified. Now uses above helper functions.
+        * page/Geolocation.cpp:
+        (WebCore::Geolocation::GeoNotifier::GeoNotifier): Modified. Asserts that PositionOptions object is present.
+        (WebCore::Geolocation::GeoNotifier::startTimer): Modified. Only starts timer if timeout has been set.
+        * page/PositionOptions.h:
+        (WebCore::PositionOptions::create): Modified. Creates object with default values.
+        (WebCore::PositionOptions::timeout): Modified. Uses correct type.
+        (WebCore::PositionOptions::hasTimeout): Added. Determines whether a timeout has been set.
+        (WebCore::PositionOptions::setTimeout): Modified. Uses correct type.
+        (WebCore::PositionOptions::maximumAge): Modified. Uses correct type.
+        (WebCore::PositionOptions::setMaximumAge): Modified. Uses correct type.
+        (WebCore::PositionOptions::PositionOptions): Modified. Creates object with default values.
+
 2009-08-13  David Levin  <levin@chromium.org>
 
         Unreviewed chromium build fix.
index 493166c..4577a07 100644 (file)
@@ -20,7 +20,7 @@
  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "config.h"
 #include "JSCustomPositionErrorCallback.h"
 #include "JSDOMWindow.h"
 #include "PositionOptions.h"
+#include <runtime/InternalFunction.h>
 
 using namespace JSC;
+using namespace std;
 
 namespace WebCore {
 
-static PassRefPtr<PositionOptions> createPositionOptions(ExecState* exec, JSValue value)
+static PassRefPtr<PositionCallback> createPositionCallback(ExecState* exec, JSValue value)
 {
-    if (!value.isObject())
+    // The spec specifies 'FunctionOnly' for this object.
+    if (!value.isObject(&InternalFunction::info)) {
+        setDOMException(exec, TYPE_MISMATCH_ERR);
         return 0;
+    }
 
     JSObject* object = asObject(value);
+    Frame* frame = toJSDOMWindow(exec->lexicalGlobalObject())->impl()->frame();
+    return JSCustomPositionCallback::create(object, frame);
+}
 
-    JSValue enableHighAccuracyValue = object->get(exec, Identifier(exec, "enableHighAccuracy"));
-    if (exec->hadException())
+static PassRefPtr<PositionErrorCallback> createPositionErrorCallback(ExecState* exec, JSValue value)
+{
+    // Argument is optional (hence undefined is allowed), and null is allowed.
+    if (value.isUndefinedOrNull())
         return 0;
-    bool enableHighAccuracy = enableHighAccuracyValue.toBoolean(exec);
-    if (exec->hadException())
+
+    // The spec specifies 'FunctionOnly' for this object.
+    if (!value.isObject(&InternalFunction::info)) {
+        setDOMException(exec, TYPE_MISMATCH_ERR);
         return 0;
+    }
 
-    JSValue timeoutValue = object->get(exec, Identifier(exec, "timeout"));
+    JSObject* object = asObject(value);
+    Frame* frame = toJSDOMWindow(exec->lexicalGlobalObject())->impl()->frame();
+    return JSCustomPositionErrorCallback::create(object, frame);
+}
+
+static PassRefPtr<PositionOptions> createPositionOptions(ExecState* exec, JSValue value)
+{
+    // Create default options.
+    RefPtr<PositionOptions> options = PositionOptions::create();
+
+    // Argument is optional (hence undefined is allowed), and null is allowed.
+    if (value.isUndefinedOrNull()) {
+        // Use default options.
+        return options.release();
+    }
+
+    // Given the above test, this will always yield an object.
+    JSObject* object = value.toObject(exec);
+
+    // For all three properties, we apply the following ...
+    // - If the getter or the property's valueOf method throws an exception, we
+    //   quit so as not to risk overwriting the exception.
+    // - If the value is absent or undefined, we don't override the default.
+    JSValue enableHighAccuracyValue = object->get(exec, Identifier(exec, "enableHighAccuracy"));
     if (exec->hadException())
         return 0;
-    unsigned timeout = timeoutValue.toUInt32(exec);
+    if (!enableHighAccuracyValue.isUndefined()) {
+        options->setEnableHighAccuracy(enableHighAccuracyValue.toBoolean(exec));
+        if (exec->hadException())
+            return 0;
+    }
+
+    JSValue timeoutValue = object->get(exec, Identifier(exec, "timeout"));
     if (exec->hadException())
         return 0;
+    if (!timeoutValue.isUndefined()) {
+        // Wrap to int32 and force non-negative to match behavior of window.setTimeout.
+        options->setTimeout(max(0, timeoutValue.toInt32(exec)));
+        if (exec->hadException())
+            return 0;
+    }
 
     JSValue maximumAgeValue = object->get(exec, Identifier(exec, "maximumAge"));
     if (exec->hadException())
         return 0;
-    unsigned maximumAge = maximumAgeValue.toUInt32(exec);
-    if (exec->hadException())
-        return 0;
+    if (!maximumAgeValue.isUndefined()) {
+        // Wrap to int32 and force non-negative to match behavior of window.setTimeout.
+        options->setMaximumAge(max(0, maximumAgeValue.toInt32(exec)));
+        if (exec->hadException())
+            return 0;
+    }
 
-    return PositionOptions::create(enableHighAccuracy, timeout, maximumAge);
+    return options.release();
 }
 
 JSValue JSGeolocation::getCurrentPosition(ExecState* exec, const ArgList& args)
 {
     // Arguments: PositionCallback, (optional)PositionErrorCallback, (optional)PositionOptions
-    RefPtr<PositionCallback> positionCallback;
-    JSObject* object = args.at(0).getObject();
+
+    RefPtr<PositionCallback> positionCallback = createPositionCallback(exec, args.at(0));
     if (exec->hadException())
         return jsUndefined();
-    if (!object) {
-        setDOMException(exec, TYPE_MISMATCH_ERR);
+    ASSERT(positionCallback);
+
+    RefPtr<PositionErrorCallback> positionErrorCallback = createPositionErrorCallback(exec, args.at(1));
+    if (exec->hadException())
         return jsUndefined();
-    }
 
-    if (Frame* frame = toJSDOMWindow(exec->lexicalGlobalObject())->impl()->frame())
-        positionCallback = JSCustomPositionCallback::create(object, frame);
-    
-    RefPtr<PositionErrorCallback> positionErrorCallback;
-    if (!args.at(1).isUndefinedOrNull()) {
-        JSObject* object = args.at(1).getObject();
-        if (!object) {
-            setDOMException(exec, TYPE_MISMATCH_ERR);
-            return jsUndefined();
-        }
-
-        if (Frame* frame = toJSDOMWindow(exec->lexicalGlobalObject())->impl()->frame())
-            positionErrorCallback = JSCustomPositionErrorCallback::create(object, frame);
-    }
-    
-    RefPtr<PositionOptions> positionOptions;
-    if (!args.at(2).isUndefinedOrNull()) {
-        positionOptions = createPositionOptions(exec, args.at(2));
-        if (exec->hadException())
-            return jsUndefined();
-    }
+    RefPtr<PositionOptions> positionOptions = createPositionOptions(exec, args.at(2));
+    if (exec->hadException())
+        return jsUndefined();
+    ASSERT(positionOptions);
 
     m_impl->getCurrentPosition(positionCallback.release(), positionErrorCallback.release(), positionOptions.release());
-    
     return jsUndefined();
 }
 
 JSValue JSGeolocation::watchPosition(ExecState* exec, const ArgList& args)
 {
     // Arguments: PositionCallback, (optional)PositionErrorCallback, (optional)PositionOptions
-    RefPtr<PositionCallback> positionCallback;
-    JSObject* object = args.at(0).getObject();
+
+    RefPtr<PositionCallback> positionCallback = createPositionCallback(exec, args.at(0));
     if (exec->hadException())
         return jsUndefined();
-    if (!object) {
-        setDOMException(exec, TYPE_MISMATCH_ERR);
+    ASSERT(positionCallback);
+
+    RefPtr<PositionErrorCallback> positionErrorCallback = createPositionErrorCallback(exec, args.at(1));
+    if (exec->hadException())
         return jsUndefined();
-    }
-    
-    if (Frame* frame = toJSDOMWindow(exec->lexicalGlobalObject())->impl()->frame())
-        positionCallback = JSCustomPositionCallback::create(object, frame);
-    
-    RefPtr<PositionErrorCallback> positionErrorCallback;
-    if (!args.at(1).isUndefinedOrNull()) {
-        JSObject* object = args.at(1).getObject();
-        if (!object) {
-            setDOMException(exec, TYPE_MISMATCH_ERR);
-            return jsUndefined();
-        }
-        
-        if (Frame* frame = toJSDOMWindow(exec->lexicalGlobalObject())->impl()->frame())
-            positionErrorCallback = JSCustomPositionErrorCallback::create(object, frame);
-    }
-    
-    RefPtr<PositionOptions> positionOptions;
-    if (!args.at(2).isUndefinedOrNull()) {
-        positionOptions = createPositionOptions(exec, args.at(2));
-        if (exec->hadException())
-            return jsUndefined();
-    }
+
+    RefPtr<PositionOptions> positionOptions = createPositionOptions(exec, args.at(2));
+    if (exec->hadException())
+        return jsUndefined();
+    ASSERT(positionOptions);
 
     int watchID = m_impl->watchPosition(positionCallback.release(), positionErrorCallback.release(), positionOptions.release());
     return jsNumber(exec, watchID);
index f4d448d..3f7e079 100644 (file)
@@ -41,11 +41,15 @@ Geolocation::GeoNotifier::GeoNotifier(PassRefPtr<PositionCallback> successCallba
     , m_options(options)
     , m_timer(this, &Geolocation::GeoNotifier::timerFired)
 {
+    ASSERT(m_successCallback);
+    // If no options were supplied from JS, we should have created a default set
+    // of options in JSGeolocationCustom.cpp.
+    ASSERT(m_options);
 }
 
 void Geolocation::GeoNotifier::startTimer()
 {
-    if (m_errorCallback && m_options)
+    if (m_errorCallback && m_options->hasTimeout())
         m_timer.startOneShot(m_options->timeout() / 1000.0);
 }
 
index 10845d3..ed7074b 100644 (file)
@@ -33,26 +33,41 @@ namespace WebCore {
     
 class PositionOptions : public RefCounted<PositionOptions> {
 public:
-    static PassRefPtr<PositionOptions> create(bool highAccuracy, unsigned timeout, unsigned maximumAge) { return adoptRef(new PositionOptions(highAccuracy, timeout, maximumAge)); }
+    static PassRefPtr<PositionOptions> create() { return adoptRef(new PositionOptions()); }
 
     bool enableHighAccuracy() const { return m_highAccuracy; }
     void setEnableHighAccuracy(bool enable) { m_highAccuracy = enable; }
-    unsigned timeout() const { return m_timeout; }
-    void setTimeout(unsigned t) { m_timeout = t; }
-    unsigned maximumAge() const { return m_maximumAge; }
-    void setMaximumAge(unsigned a) { m_maximumAge = a; }
+    bool hasTimeout() const { return m_hasTimeout; }
+    int timeout() const
+    {
+        ASSERT(hasTimeout());
+        return m_timeout;
+    }
+    void setTimeout(int timeout)
+    {
+        ASSERT(timeout >= 0);
+        m_hasTimeout = true;
+        m_timeout = timeout;
+    }
+    int maximumAge() const { return m_maximumAge; }
+    void setMaximumAge(int age)
+    {
+        ASSERT(age >= 0);
+        m_maximumAge = age;
+    }
     
 private:
-    PositionOptions(bool highAccuracy, unsigned timeout, unsigned maximumAge)
-        : m_highAccuracy(highAccuracy)
-        , m_timeout(timeout)
-        , m_maximumAge(maximumAge)
+    PositionOptions()
+        : m_highAccuracy(false)
+        , m_hasTimeout(false)
+        , m_maximumAge(0)
     {
     }
     
     bool m_highAccuracy;
-    unsigned m_timeout;
-    unsigned m_maximumAge;
+    bool m_hasTimeout;
+    int m_timeout;
+    int m_maximumAge;
 };
     
 } // namespace WebCore