mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 679966, part 2: Add mozVibrator() for "playing" a vibration pattern. r=bz
This commit is contained in:
parent
26afae03b7
commit
583f77dbbe
@ -71,6 +71,9 @@
|
||||
#include "BatteryManager.h"
|
||||
#include "SmsManager.h"
|
||||
#include "nsISmsService.h"
|
||||
#include "mozilla/Hal.h"
|
||||
#include "nsIWebNavigation.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
|
||||
// This should not be in the namespace.
|
||||
DOMCI_DATA(Navigator, mozilla::dom::Navigator)
|
||||
@ -79,7 +82,11 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
static const char sJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
|
||||
bool Navigator::sDoNotTrackEnabled = false;
|
||||
|
||||
static bool sDoNotTrackEnabled = false;
|
||||
static bool sVibratorEnabled = false;
|
||||
static PRUint32 sMaxVibrateMS = 0;
|
||||
static PRUint32 sMaxVibrateListLen = 0;
|
||||
|
||||
/* static */
|
||||
void
|
||||
@ -88,6 +95,12 @@ Navigator::Init()
|
||||
Preferences::AddBoolVarCache(&sDoNotTrackEnabled,
|
||||
"privacy.donottrackheader.enabled",
|
||||
false);
|
||||
Preferences::AddBoolVarCache(&sVibratorEnabled,
|
||||
"dom.vibrator.enabled", true);
|
||||
Preferences::AddUintVarCache(&sMaxVibrateMS,
|
||||
"dom.vibrator.max_vibrate_ms", 10000);
|
||||
Preferences::AddUintVarCache(&sMaxVibrateListLen,
|
||||
"dom.vibrator.max_vibrate_list_len", 128);
|
||||
}
|
||||
|
||||
Navigator::Navigator(nsPIDOMWindow* aWindow)
|
||||
@ -527,6 +540,176 @@ Navigator::HasDesktopNotificationSupport()
|
||||
return Preferences::GetBool("notification.feature.enabled", false);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class VibrateWindowListener : public nsIDOMEventListener
|
||||
{
|
||||
public:
|
||||
VibrateWindowListener(nsIDOMWindow *aWindow, nsIDOMDocument *aDocument)
|
||||
{
|
||||
mWindow = do_GetWeakReference(aWindow);
|
||||
mDocument = do_GetWeakReference(aDocument);
|
||||
|
||||
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(aDocument);
|
||||
NS_NAMED_LITERAL_STRING(visibilitychange, "mozvisibilitychange");
|
||||
target->AddSystemEventListener(visibilitychange,
|
||||
this, /* listener */
|
||||
true, /* use capture */
|
||||
false /* wants untrusted */);
|
||||
}
|
||||
|
||||
virtual ~VibrateWindowListener()
|
||||
{
|
||||
}
|
||||
|
||||
void RemoveListener();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
|
||||
private:
|
||||
nsWeakPtr mWindow;
|
||||
nsWeakPtr mDocument;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(VibrateWindowListener, nsIDOMEventListener)
|
||||
|
||||
nsRefPtr<VibrateWindowListener> gVibrateWindowListener;
|
||||
|
||||
NS_IMETHODIMP
|
||||
VibrateWindowListener::HandleEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
nsCOMPtr<nsIDOMEventTarget> target;
|
||||
aEvent->GetTarget(getter_AddRefs(target));
|
||||
nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(target);
|
||||
|
||||
bool hidden = true;
|
||||
if (doc) {
|
||||
doc->GetMozHidden(&hidden);
|
||||
}
|
||||
|
||||
if (hidden) {
|
||||
// It's important that we call CancelVibrate(), not Vibrate() with an
|
||||
// empty list, because Vibrate() will fail if we're no longer focused, but
|
||||
// CancelVibrate() will succeed, so long as nobody else has started a new
|
||||
// vibration pattern.
|
||||
nsCOMPtr<nsIDOMWindow> window = do_QueryReferent(mWindow);
|
||||
hal::CancelVibrate(window);
|
||||
RemoveListener();
|
||||
gVibrateWindowListener = NULL;
|
||||
// Careful: The line above might have deleted |this|!
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
VibrateWindowListener::RemoveListener()
|
||||
{
|
||||
nsCOMPtr<nsIDOMEventTarget> target = do_QueryReferent(mDocument);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
NS_NAMED_LITERAL_STRING(visibilitychange, "mozvisibilitychange");
|
||||
target->RemoveSystemEventListener(visibilitychange, this,
|
||||
true /* use capture */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a jsval into a vibration duration, checking that the duration is in
|
||||
* bounds (non-negative and not larger than sMaxVibrateMS).
|
||||
*
|
||||
* Returns true on success, false on failure.
|
||||
*/
|
||||
bool
|
||||
GetVibrationDurationFromJsval(const jsval& aJSVal, JSContext* cx,
|
||||
PRInt32 *aOut)
|
||||
{
|
||||
return JS_ValueToInt32(cx, aJSVal, aOut) &&
|
||||
*aOut >= 0 && static_cast<PRUint32>(*aOut) <= sMaxVibrateMS;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
NS_IMETHODIMP
|
||||
Navigator::MozVibrate(const jsval& aPattern, JSContext* cx)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mWindow);
|
||||
NS_ENSURE_TRUE(win, NS_OK);
|
||||
|
||||
nsIDOMDocument* domDoc = win->GetExtantDocument();
|
||||
NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE);
|
||||
|
||||
bool hidden = true;
|
||||
domDoc->GetMozHidden(&hidden);
|
||||
if (hidden) {
|
||||
// Hidden documents cannot start or stop a vibration.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoTArray<PRUint32, 8> pattern;
|
||||
|
||||
// null or undefined pattern is an error.
|
||||
if (JSVAL_IS_NULL(aPattern) || JSVAL_IS_VOID(aPattern)) {
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
}
|
||||
|
||||
if (JSVAL_IS_PRIMITIVE(aPattern)) {
|
||||
PRInt32 p;
|
||||
if (GetVibrationDurationFromJsval(aPattern, cx, &p)) {
|
||||
pattern.AppendElement(p);
|
||||
}
|
||||
else {
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
}
|
||||
}
|
||||
else {
|
||||
JSObject *obj = JSVAL_TO_OBJECT(aPattern);
|
||||
PRUint32 length;
|
||||
if (!JS_GetArrayLength(cx, obj, &length) || length > sMaxVibrateListLen) {
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
}
|
||||
pattern.SetLength(length);
|
||||
|
||||
for (PRUint32 i = 0; i < length; ++i) {
|
||||
jsval v;
|
||||
PRInt32 pv;
|
||||
if (JS_GetElement(cx, obj, i, &v) &&
|
||||
GetVibrationDurationFromJsval(v, cx, &pv)) {
|
||||
pattern[i] = pv;
|
||||
}
|
||||
else {
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The spec says we check sVibratorEnabled after we've done the sanity
|
||||
// checking on the pattern.
|
||||
if (!sVibratorEnabled) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Add a listener to cancel the vibration if the document becomes hidden,
|
||||
// and remove the old mozvisibility listener, if there was one.
|
||||
|
||||
if (!gVibrateWindowListener) {
|
||||
// If gVibrateWindowListener is null, this is the first time we've vibrated,
|
||||
// and we need to register a listener to clear gVibrateWindowListener on
|
||||
// shutdown.
|
||||
ClearOnShutdown(&gVibrateWindowListener);
|
||||
}
|
||||
else {
|
||||
gVibrateWindowListener->RemoveListener();
|
||||
}
|
||||
gVibrateWindowListener = new VibrateWindowListener(win, domDoc);
|
||||
|
||||
nsCOMPtr<nsIDOMWindow> domWindow =
|
||||
do_QueryInterface(static_cast<nsIDOMWindow*>(win));
|
||||
hal::Vibrate(pattern, domWindow);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
// Navigator::nsIDOMClientInformation
|
||||
//*****************************************************************************
|
||||
|
@ -107,8 +107,6 @@ private:
|
||||
bool IsSmsAllowed() const;
|
||||
bool IsSmsSupported() const;
|
||||
|
||||
static bool sDoNotTrackEnabled;
|
||||
|
||||
nsRefPtr<nsMimeTypeArray> mMimeTypes;
|
||||
nsRefPtr<nsPluginArray> mPlugins;
|
||||
nsRefPtr<nsGeolocation> mGeolocation;
|
||||
|
@ -39,7 +39,7 @@
|
||||
|
||||
#include "domstubs.idl"
|
||||
|
||||
[scriptable, uuid(B8EE0374-5F47-4ED0-B9B0-BDE3E6D81FF5)]
|
||||
[scriptable, uuid(a1ee08c1-0299-4908-a6ba-7cBc8da6531f)]
|
||||
interface nsIDOMNavigator : nsISupports
|
||||
{
|
||||
readonly attribute DOMString appCodeName;
|
||||
@ -61,5 +61,52 @@ interface nsIDOMNavigator : nsISupports
|
||||
readonly attribute DOMString doNotTrack;
|
||||
|
||||
boolean javaEnabled();
|
||||
};
|
||||
|
||||
/**
|
||||
* Pulse the device's vibrator, if it has one. If the device does not have a
|
||||
* vibrator, this function does nothing. If the window is hidden, this
|
||||
* function does nothing.
|
||||
*
|
||||
* mozVibrate takes one argument, which specifies either how long to vibrate
|
||||
* for or gives a pattern of vibrator-on/vibrator-off timings.
|
||||
*
|
||||
* If a vibration pattern is in effect when this function is called, this
|
||||
* call will overwrite the existing pattern, if this call successfully
|
||||
* completes.
|
||||
*
|
||||
* We handle the argument to mozVibrate as follows.
|
||||
*
|
||||
* - If the argument is undefined or null, we throw
|
||||
* NS_ERROR_DOM_NOT_SUPPORTED_ERR.
|
||||
*
|
||||
* - If the argument is 0, the empty list, or a list containing entirely 0s,
|
||||
* we cancel any outstanding vibration pattern; that is, we stop the device
|
||||
* from vibrating.
|
||||
*
|
||||
* - Otherwise, if the argument X is not a list, we treat it as though it's
|
||||
* the singleton list [X] and then proceed as below.
|
||||
*
|
||||
* - If the argument is a list (or if we wrapped it as a list above), then we
|
||||
* try to convert each element in the list to an integer, by first
|
||||
* converting it to a number and then rounding. If there is some element
|
||||
* that we can't convert to an integer, or if any of the integers are
|
||||
* negative, we throw NS_ERROR_DOM_NOT_SUPPORTED_ERR.
|
||||
*
|
||||
* This list of integers specifies a vibration pattern. Given a list of
|
||||
* numbers
|
||||
*
|
||||
* [a_1, b_1, a_2, b_2, ..., a_n]
|
||||
*
|
||||
* the device will vibrate for a_1 milliseconds, then be still for b_1
|
||||
* milliseconds, then vibrate for a_2 milliseconds, and so on.
|
||||
*
|
||||
* The list may contain an even or an odd number of elements, but if you
|
||||
* pass an even number of elements (that is, if your list ends with b_n
|
||||
* instead of a_n), the final element doesn't specify anything meaningful.
|
||||
*
|
||||
* We may throw NS_ERROR_DOM_NOT_SUPPORTED_ERR if the vibration pattern is
|
||||
* too long, or if any of its elements is too large.
|
||||
*/
|
||||
[implicit_jscontext]
|
||||
void mozVibrate(in jsval aPattern);
|
||||
};
|
||||
|
@ -72,6 +72,7 @@ _TEST_FILES = \
|
||||
test_windowedhistoryframes.html \
|
||||
test_focusrings.xul \
|
||||
file_moving_xhr.html \
|
||||
test_vibrator.html \
|
||||
$(NULL)
|
||||
|
||||
_CHROME_FILES = \
|
||||
|
99
dom/tests/mochitest/general/test_vibrator.html
Normal file
99
dom/tests/mochitest/general/test_vibrator.html
Normal file
@ -0,0 +1,99 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Vibrator</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Although we can't test that the vibrator works properly, we can test that
|
||||
navigator.mozVibrate throws an exception where appropriate. -->
|
||||
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
function expectFailure(param) {
|
||||
try {
|
||||
navigator.mozVibrate(param);
|
||||
}
|
||||
catch(e) {
|
||||
ok(true, 'mozVibrate(' + param + ') threw an expected exception.');
|
||||
return;
|
||||
}
|
||||
ok(false, 'mozVibrate(' + param + ') should have thrown an exception.');
|
||||
}
|
||||
|
||||
function expectSuccess(param) {
|
||||
try {
|
||||
navigator.mozVibrate(param);
|
||||
}
|
||||
catch(e) {
|
||||
ok(false, 'mozVibrate(' + param + ') threw an unexpected exception.');
|
||||
return;
|
||||
}
|
||||
ok(true, 'mozVibrate(' + param + ') did not throw an exception.');
|
||||
}
|
||||
|
||||
function testFailures() {
|
||||
expectFailure(null);
|
||||
expectFailure(undefined);
|
||||
expectFailure(-1);
|
||||
expectFailure('a');
|
||||
expectFailure([100, -1]);
|
||||
expectFailure([100, 'a']);
|
||||
|
||||
var maxVibrateMs = SpecialPowers.getIntPref('dom.vibrator.max_vibrate_ms');
|
||||
var maxVibrateListLen = SpecialPowers.getIntPref('dom.vibrator.max_vibrate_list_len');
|
||||
|
||||
// Make sure that these preferences are respected.
|
||||
expectFailure(maxVibrateMs + 1);
|
||||
expectFailure([maxVibrateMs + 1]);
|
||||
|
||||
var arr = [];
|
||||
for (var i = 0; i < maxVibrateListLen + 1; i++) {
|
||||
arr[i] = 0;
|
||||
}
|
||||
expectFailure(arr);
|
||||
}
|
||||
|
||||
function testSuccesses() {
|
||||
expectSuccess(0);
|
||||
expectSuccess([]);
|
||||
expectSuccess('1000');
|
||||
expectSuccess(1000);
|
||||
expectSuccess(1000.1);
|
||||
expectSuccess([0, 0, 0]);
|
||||
expectSuccess(['1000', 1000]);
|
||||
expectSuccess([1000, 1000]);
|
||||
expectSuccess([1000, 1000.1]);
|
||||
|
||||
// The following loop shouldn't cause us to crash. See bug 701716.
|
||||
for (var i = 0; i < 10000; i++) {
|
||||
navigator.mozVibrate([100, 100]);
|
||||
}
|
||||
ok(true, "Didn't crash after issuing a lot of vibrate() calls.");
|
||||
}
|
||||
|
||||
var origVibratorEnabled = SpecialPowers.getBoolPref('dom.vibrator.enabled');
|
||||
|
||||
// Test with the vibrator pref enabled.
|
||||
try {
|
||||
SpecialPowers.setBoolPref('dom.vibrator.enabled', true);
|
||||
testFailures();
|
||||
testSuccesses();
|
||||
|
||||
// Everything should be the same when the vibrator is disabled -- in
|
||||
// particular, a disabled vibrator shouldn't eat failures we'd otherwise
|
||||
// observe.
|
||||
SpecialPowers.setBoolPref('dom.vibrator.enabled', false);
|
||||
testFailures();
|
||||
testSuccesses();
|
||||
}
|
||||
finally {
|
||||
SpecialPowers.setBoolPref('dom.vibrator.enabled', origVibratorEnabled);
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -50,16 +50,28 @@ Vibrate(const nsTArray<uint32> &pattern, const WindowIdentifier &)
|
||||
// hal_sandbox::Vibrate, and hal_impl::Vibrate all must have the same
|
||||
// signature.
|
||||
|
||||
// Strangely enough, the Android Java API seems to treat vibrate([0]) as a
|
||||
// nop. But we want to treat vibrate([0]) like CancelVibrate! (Note that we
|
||||
// also need to treat vibrate([]) as a call to CancelVibrate.)
|
||||
bool allZero = true;
|
||||
for (uint32 i = 0; i < pattern.Length(); i++) {
|
||||
if (pattern[i] != 0) {
|
||||
allZero = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (allZero) {
|
||||
hal_impl::CancelVibrate(WindowIdentifier());
|
||||
return;
|
||||
}
|
||||
|
||||
AndroidBridge* b = AndroidBridge::Bridge();
|
||||
if (!b) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pattern.Length() == 0) {
|
||||
b->CancelVibrate();
|
||||
} else {
|
||||
b->Vibrate(pattern);
|
||||
}
|
||||
b->Vibrate(pattern);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -3394,6 +3394,10 @@ pref("dom.event.handling-user-input-time-limit", 1000);
|
||||
//3D Transforms
|
||||
pref("layout.3d-transforms.enabled", true);
|
||||
|
||||
pref("dom.vibrator.enabled", true);
|
||||
pref("dom.vibrator.max_vibrate_ms", 10000);
|
||||
pref("dom.vibrator.max_vibrate_list_len", 128);
|
||||
|
||||
// Battery API
|
||||
pref("dom.battery.enabled", true);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user