1/*
2 * Copyright (C) 2010 Apple Inc. All rights reserved.
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
28#if ENABLE(DEVICE_ORIENTATION)
29
30#include "JSDeviceMotionEvent.h"
31
32#include "DeviceMotionData.h"
33#include "DeviceMotionEvent.h"
34#include <runtime/ObjectConstructor.h>
35
36using namespace JSC;
37
38namespace WebCore {
39
40static PassRefPtr<DeviceMotionData::Acceleration> readAccelerationArgument(JSValue value, ExecState* exec)
41{
42    if (value.isUndefinedOrNull())
43        return 0;
44
45    // Given the above test, this will always yield an object.
46    JSObject* object = value.toObject(exec);
47
48    JSValue xValue = object->get(exec, Identifier(exec, "x"));
49    if (exec->hadException())
50        return 0;
51    bool canProvideX = !xValue.isUndefinedOrNull();
52    double x = xValue.toNumber(exec);
53    if (exec->hadException())
54        return 0;
55
56    JSValue yValue = object->get(exec, Identifier(exec, "y"));
57    if (exec->hadException())
58        return 0;
59    bool canProvideY = !yValue.isUndefinedOrNull();
60    double y = yValue.toNumber(exec);
61    if (exec->hadException())
62        return 0;
63
64    JSValue zValue = object->get(exec, Identifier(exec, "z"));
65    if (exec->hadException())
66        return 0;
67    bool canProvideZ = !zValue.isUndefinedOrNull();
68    double z = zValue.toNumber(exec);
69    if (exec->hadException())
70        return 0;
71
72    if (!canProvideX && !canProvideY && !canProvideZ)
73        return 0;
74
75    return DeviceMotionData::Acceleration::create(canProvideX, x, canProvideY, y, canProvideZ, z);
76}
77
78static PassRefPtr<DeviceMotionData::RotationRate> readRotationRateArgument(JSValue value, ExecState* exec)
79{
80    if (value.isUndefinedOrNull())
81        return 0;
82
83    // Given the above test, this will always yield an object.
84    JSObject* object = value.toObject(exec);
85
86    JSValue alphaValue = object->get(exec, Identifier(exec, "alpha"));
87    if (exec->hadException())
88        return 0;
89    bool canProvideAlpha = !alphaValue.isUndefinedOrNull();
90    double alpha = alphaValue.toNumber(exec);
91    if (exec->hadException())
92        return 0;
93
94    JSValue betaValue = object->get(exec, Identifier(exec, "beta"));
95    if (exec->hadException())
96        return 0;
97    bool canProvideBeta = !betaValue.isUndefinedOrNull();
98    double beta = betaValue.toNumber(exec);
99    if (exec->hadException())
100        return 0;
101
102    JSValue gammaValue = object->get(exec, Identifier(exec, "gamma"));
103    if (exec->hadException())
104        return 0;
105    bool canProvideGamma = !gammaValue.isUndefinedOrNull();
106    double gamma = gammaValue.toNumber(exec);
107    if (exec->hadException())
108        return 0;
109
110    if (!canProvideAlpha && !canProvideBeta && !canProvideGamma)
111        return 0;
112
113    return DeviceMotionData::RotationRate::create(canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma);
114}
115
116static JSObject* createAccelerationObject(const DeviceMotionData::Acceleration* acceleration, ExecState* exec)
117{
118    JSObject* object = constructEmptyObject(exec);
119    object->putDirect(exec->vm(), Identifier(exec, "x"), acceleration->canProvideX() ? jsNumber(acceleration->x()) : jsNull());
120    object->putDirect(exec->vm(), Identifier(exec, "y"), acceleration->canProvideY() ? jsNumber(acceleration->y()) : jsNull());
121    object->putDirect(exec->vm(), Identifier(exec, "z"), acceleration->canProvideZ() ? jsNumber(acceleration->z()) : jsNull());
122    return object;
123}
124
125static JSObject* createRotationRateObject(const DeviceMotionData::RotationRate* rotationRate, ExecState* exec)
126{
127    JSObject* object = constructEmptyObject(exec);
128    object->putDirect(exec->vm(), Identifier(exec, "alpha"), rotationRate->canProvideAlpha() ? jsNumber(rotationRate->alpha()) : jsNull());
129    object->putDirect(exec->vm(), Identifier(exec, "beta"),  rotationRate->canProvideBeta()  ? jsNumber(rotationRate->beta())  : jsNull());
130    object->putDirect(exec->vm(), Identifier(exec, "gamma"), rotationRate->canProvideGamma() ? jsNumber(rotationRate->gamma()) : jsNull());
131    return object;
132}
133
134JSValue JSDeviceMotionEvent::acceleration(ExecState* exec) const
135{
136    DeviceMotionEvent* imp = static_cast<DeviceMotionEvent*>(impl());
137    if (!imp->deviceMotionData()->acceleration())
138        return jsNull();
139    return createAccelerationObject(imp->deviceMotionData()->acceleration(), exec);
140}
141
142JSValue JSDeviceMotionEvent::accelerationIncludingGravity(ExecState* exec) const
143{
144    DeviceMotionEvent* imp = static_cast<DeviceMotionEvent*>(impl());
145    if (!imp->deviceMotionData()->accelerationIncludingGravity())
146        return jsNull();
147    return createAccelerationObject(imp->deviceMotionData()->accelerationIncludingGravity(), exec);
148}
149
150JSValue JSDeviceMotionEvent::rotationRate(ExecState* exec) const
151{
152    DeviceMotionEvent* imp = static_cast<DeviceMotionEvent*>(impl());
153    if (!imp->deviceMotionData()->rotationRate())
154        return jsNull();
155    return createRotationRateObject(imp->deviceMotionData()->rotationRate(), exec);
156}
157
158JSValue JSDeviceMotionEvent::interval(ExecState*) const
159{
160    DeviceMotionEvent* imp = static_cast<DeviceMotionEvent*>(impl());
161    if (!imp->deviceMotionData()->canProvideInterval())
162        return jsNull();
163    return jsNumber(imp->deviceMotionData()->interval());
164}
165
166JSValue JSDeviceMotionEvent::initDeviceMotionEvent(ExecState* exec)
167{
168    const String type = exec->argument(0).toString(exec)->value(exec);
169    bool bubbles = exec->argument(1).toBoolean(exec);
170    bool cancelable = exec->argument(2).toBoolean(exec);
171
172    // If any of the parameters are null or undefined, mark them as not provided.
173    // Otherwise, use the standard JavaScript conversion.
174    RefPtr<DeviceMotionData::Acceleration> acceleration = readAccelerationArgument(exec->argument(3), exec);
175    if (exec->hadException())
176        return jsUndefined();
177
178    RefPtr<DeviceMotionData::Acceleration> accelerationIncludingGravity = readAccelerationArgument(exec->argument(4), exec);
179    if (exec->hadException())
180        return jsUndefined();
181
182    RefPtr<DeviceMotionData::RotationRate> rotationRate = readRotationRateArgument(exec->argument(5), exec);
183    if (exec->hadException())
184        return jsUndefined();
185
186    bool intervalProvided = !exec->argument(6).isUndefinedOrNull();
187    double interval = exec->argument(6).toNumber(exec);
188    RefPtr<DeviceMotionData> deviceMotionData = DeviceMotionData::create(acceleration, accelerationIncludingGravity, rotationRate, intervalProvided, interval);
189    DeviceMotionEvent* imp = static_cast<DeviceMotionEvent*>(impl());
190    imp->initDeviceMotionEvent(type, bubbles, cancelable, deviceMotionData.get());
191    return jsUndefined();
192}
193
194} // namespace WebCore
195
196#endif // ENABLE(DEVICE_ORIENTATION)
197