Loading dom/base/Navigator.cpp +184 −1 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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 Loading @@ -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) Loading Loading @@ -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 //***************************************************************************** Loading dom/base/Navigator.h +0 −2 Original line number Diff line number Diff line Loading @@ -107,8 +107,6 @@ private: bool IsSmsAllowed() const; bool IsSmsSupported() const; static bool sDoNotTrackEnabled; nsRefPtr<nsMimeTypeArray> mMimeTypes; nsRefPtr<nsPluginArray> mPlugins; nsRefPtr<nsGeolocation> mGeolocation; Loading dom/interfaces/base/nsIDOMNavigator.idl +49 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); }; dom/tests/mochitest/general/Makefile.in +1 −0 Original line number Diff line number Diff line Loading @@ -72,6 +72,7 @@ _TEST_FILES = \ test_windowedhistoryframes.html \ test_focusrings.xul \ file_moving_xhr.html \ test_vibrator.html \ $(NULL) _CHROME_FILES = \ Loading dom/tests/mochitest/general/test_vibrator.html 0 → 100644 +99 −0 Original line number Diff line number Diff line <!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> Loading
dom/base/Navigator.cpp +184 −1 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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 Loading @@ -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) Loading Loading @@ -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 //***************************************************************************** Loading
dom/base/Navigator.h +0 −2 Original line number Diff line number Diff line Loading @@ -107,8 +107,6 @@ private: bool IsSmsAllowed() const; bool IsSmsSupported() const; static bool sDoNotTrackEnabled; nsRefPtr<nsMimeTypeArray> mMimeTypes; nsRefPtr<nsPluginArray> mPlugins; nsRefPtr<nsGeolocation> mGeolocation; Loading
dom/interfaces/base/nsIDOMNavigator.idl +49 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); };
dom/tests/mochitest/general/Makefile.in +1 −0 Original line number Diff line number Diff line Loading @@ -72,6 +72,7 @@ _TEST_FILES = \ test_windowedhistoryframes.html \ test_focusrings.xul \ file_moving_xhr.html \ test_vibrator.html \ $(NULL) _CHROME_FILES = \ Loading
dom/tests/mochitest/general/test_vibrator.html 0 → 100644 +99 −0 Original line number Diff line number Diff line <!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>