1/*
2 * Copyright 2009, The Android Open Source Project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *  * Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 *  * Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "V8Geolocation.h"
28
29#if ENABLE(GEOLOCATION)
30
31#include "Frame.h"
32#include "Geolocation.h"
33#include "V8Binding.h"
34#include "V8CustomPositionCallback.h"
35#include "V8CustomPositionErrorCallback.h"
36#include "V8Utilities.h"
37
38using namespace std;
39using namespace WTF;
40
41namespace WebCore {
42
43static PassRefPtr<PositionOptions> createPositionOptions(v8::Local<v8::Value> value, bool& succeeded)
44{
45    succeeded = true;
46
47    // Create default options.
48    RefPtr<PositionOptions> options = PositionOptions::create();
49
50    // Argument is optional (hence undefined is allowed), and null is allowed.
51    if (isUndefinedOrNull(value)) {
52        // Use default options.
53        return options.release();
54    }
55
56    // Given the above test, this will always yield an object.
57    v8::Local<v8::Object> object = value->ToObject();
58
59    // For all three properties, we apply the following ...
60    // - If the getter or the property's valueOf method throws an exception, we
61    //   quit so as not to risk overwriting the exception.
62    // - If the value is absent or undefined, we don't override the default.
63    v8::Local<v8::Value> enableHighAccuracyValue = object->Get(v8::String::New("enableHighAccuracy"));
64    if (enableHighAccuracyValue.IsEmpty()) {
65        succeeded = false;
66        return 0;
67    }
68    if (!enableHighAccuracyValue->IsUndefined()) {
69        v8::Local<v8::Boolean> enableHighAccuracyBoolean = enableHighAccuracyValue->ToBoolean();
70        if (enableHighAccuracyBoolean.IsEmpty()) {
71            succeeded = false;
72            return 0;
73        }
74        options->setEnableHighAccuracy(enableHighAccuracyBoolean->Value());
75    }
76
77    v8::Local<v8::Value> timeoutValue = object->Get(v8::String::New("timeout"));
78    if (timeoutValue.IsEmpty()) {
79        succeeded = false;
80        return 0;
81    }
82    if (!timeoutValue->IsUndefined()) {
83        v8::Local<v8::Number> timeoutNumber = timeoutValue->ToNumber();
84        if (timeoutNumber.IsEmpty()) {
85            succeeded = false;
86            return 0;
87        }
88        double timeoutDouble = timeoutNumber->Value();
89        // If the value is positive infinity, there's nothing to do.
90        if (!(isinf(timeoutDouble) && timeoutDouble > 0)) {
91            v8::Local<v8::Int32> timeoutInt32 = timeoutValue->ToInt32();
92            if (timeoutInt32.IsEmpty()) {
93                succeeded = false;
94                return 0;
95            }
96            // Wrap to int32 and force non-negative to match behavior of window.setTimeout.
97            options->setTimeout(max(0, timeoutInt32->Value()));
98        }
99    }
100
101    v8::Local<v8::Value> maximumAgeValue = object->Get(v8::String::New("maximumAge"));
102    if (maximumAgeValue.IsEmpty()) {
103        succeeded = false;
104        return 0;
105    }
106    if (!maximumAgeValue->IsUndefined()) {
107        v8::Local<v8::Number> maximumAgeNumber = maximumAgeValue->ToNumber();
108        if (maximumAgeNumber.IsEmpty()) {
109            succeeded = false;
110            return 0;
111        }
112        double maximumAgeDouble = maximumAgeNumber->Value();
113        if (isinf(maximumAgeDouble) && maximumAgeDouble > 0) {
114            // If the value is positive infinity, clear maximumAge.
115            options->clearMaximumAge();
116        } else {
117            v8::Local<v8::Int32> maximumAgeInt32 = maximumAgeValue->ToInt32();
118            if (maximumAgeInt32.IsEmpty()) {
119                succeeded = false;
120                return 0;
121            }
122            // Wrap to int32 and force non-negative to match behavior of window.setTimeout.
123            options->setMaximumAge(max(0, maximumAgeInt32->Value()));
124        }
125    }
126
127    return options.release();
128}
129
130v8::Handle<v8::Value> V8Geolocation::getCurrentPositionCallback(const v8::Arguments& args)
131{
132    INC_STATS("DOM.Geolocation.getCurrentPosition()");
133
134    bool succeeded = false;
135
136    RefPtr<PositionCallback> positionCallback = createFunctionOnlyCallback<V8CustomPositionCallback>(args[0], succeeded);
137    if (!succeeded)
138        return v8::Undefined();
139    ASSERT(positionCallback);
140
141    // Argument is optional (hence undefined is allowed), and null is allowed.
142    RefPtr<PositionErrorCallback> positionErrorCallback = createFunctionOnlyCallback<V8CustomPositionErrorCallback>(args[1], succeeded, CallbackAllowUndefined | CallbackAllowNull);
143    if (!succeeded)
144        return v8::Undefined();
145
146    RefPtr<PositionOptions> positionOptions = createPositionOptions(args[2], succeeded);
147    if (!succeeded)
148        return v8::Undefined();
149    ASSERT(positionOptions);
150
151    Geolocation* geolocation = V8Geolocation::toNative(args.Holder());
152    geolocation->getCurrentPosition(positionCallback.release(), positionErrorCallback.release(), positionOptions.release());
153    return v8::Undefined();
154}
155
156v8::Handle<v8::Value> V8Geolocation::watchPositionCallback(const v8::Arguments& args)
157{
158    INC_STATS("DOM.Geolocation.watchPosition()");
159
160    bool succeeded = false;
161
162    RefPtr<PositionCallback> positionCallback = createFunctionOnlyCallback<V8CustomPositionCallback>(args[0], succeeded);
163    if (!succeeded)
164        return v8::Undefined();
165    ASSERT(positionCallback);
166
167    // Argument is optional (hence undefined is allowed), and null is allowed.
168    RefPtr<PositionErrorCallback> positionErrorCallback = createFunctionOnlyCallback<V8CustomPositionErrorCallback>(args[1], succeeded, CallbackAllowUndefined | CallbackAllowNull);
169    if (!succeeded)
170        return v8::Undefined();
171
172    RefPtr<PositionOptions> positionOptions = createPositionOptions(args[2], succeeded);
173    if (!succeeded)
174        return v8::Undefined();
175    ASSERT(positionOptions);
176
177    Geolocation* geolocation = V8Geolocation::toNative(args.Holder());
178    int watchId = geolocation->watchPosition(positionCallback.release(), positionErrorCallback.release(), positionOptions.release());
179    return v8::Number::New(watchId);
180}
181
182} // namespace WebCore
183
184#endif // ENABLE(GEOLOCATION)
185