summaryrefslogtreecommitdiffstats
path: root/widget/tests
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /widget/tests
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'widget/tests')
-rw-r--r--widget/tests/TestAppShellSteadyState.cpp503
-rw-r--r--widget/tests/TestChromeMargin.cpp155
-rw-r--r--widget/tests/bug586713_window.xul50
-rw-r--r--widget/tests/chrome.ini100
-rw-r--r--widget/tests/chrome_context_menus_win.xul101
-rw-r--r--widget/tests/empty_window.xul4
-rw-r--r--widget/tests/mochitest.ini12
-rw-r--r--widget/tests/moz.build15
-rw-r--r--widget/tests/native_menus_window.xul285
-rw-r--r--widget/tests/native_mouse_mac_window.xul773
-rw-r--r--widget/tests/standalone_native_menu_window.xul334
-rw-r--r--widget/tests/taskbar_previews.xul127
-rw-r--r--widget/tests/test_assign_event_data.html748
-rw-r--r--widget/tests/test_bug1123480.xul79
-rw-r--r--widget/tests/test_bug1151186.html43
-rw-r--r--widget/tests/test_bug343416.xul202
-rw-r--r--widget/tests/test_bug413277.html36
-rw-r--r--widget/tests/test_bug428405.xul167
-rw-r--r--widget/tests/test_bug429954.xul44
-rw-r--r--widget/tests/test_bug444800.xul102
-rw-r--r--widget/tests/test_bug466599.xul109
-rw-r--r--widget/tests/test_bug478536.xul34
-rw-r--r--widget/tests/test_bug485118.xul71
-rw-r--r--widget/tests/test_bug517396.xul56
-rw-r--r--widget/tests/test_bug522217.xul36
-rw-r--r--widget/tests/test_bug538242.xul55
-rw-r--r--widget/tests/test_bug565392.html70
-rw-r--r--widget/tests/test_bug586713.xul30
-rw-r--r--widget/tests/test_bug593307.xul46
-rw-r--r--widget/tests/test_bug596600.xul177
-rw-r--r--widget/tests/test_bug673301.xul40
-rw-r--r--widget/tests/test_bug760802.xul91
-rw-r--r--widget/tests/test_chrome_context_menus_win.xul27
-rw-r--r--widget/tests/test_clipboard.xul80
-rw-r--r--widget/tests/test_composition_text_querycontent.xul34
-rw-r--r--widget/tests/test_imestate.html1529
-rw-r--r--widget/tests/test_input_events_on_deactive_window.xul236
-rw-r--r--widget/tests/test_key_event_counts.xul90
-rw-r--r--widget/tests/test_keycodes.xul4361
-rw-r--r--widget/tests/test_mouse_scroll.xul28
-rw-r--r--widget/tests/test_native_key_bindings_mac.html343
-rw-r--r--widget/tests/test_native_menus.xul30
-rw-r--r--widget/tests/test_native_mouse_mac.xul30
-rw-r--r--widget/tests/test_panel_mouse_coords.xul83
-rw-r--r--widget/tests/test_picker_no_crash.html36
-rw-r--r--widget/tests/test_platform_colors.xul107
-rw-r--r--widget/tests/test_plugin_input_event.html74
-rw-r--r--widget/tests/test_plugin_scroll_consistency.html61
-rw-r--r--widget/tests/test_position_on_resize.xul94
-rw-r--r--widget/tests/test_secure_input.html148
-rw-r--r--widget/tests/test_sizemode_events.xul105
-rw-r--r--widget/tests/test_standalone_native_menu.xul30
-rw-r--r--widget/tests/test_system_status_bar.xul57
-rw-r--r--widget/tests/test_taskbar_progress.xul126
-rw-r--r--widget/tests/test_wheeltransaction.xul28
-rw-r--r--widget/tests/unit/test_macwebapputils.js44
-rw-r--r--widget/tests/unit/test_taskbar_jumplistitems.js261
-rw-r--r--widget/tests/unit/xpcshell.ini7
-rw-r--r--widget/tests/utils.js27
-rw-r--r--widget/tests/window_bug429954.xul45
-rw-r--r--widget/tests/window_bug478536.xul215
-rw-r--r--widget/tests/window_bug522217.xul72
-rw-r--r--widget/tests/window_bug538242.xul3
-rw-r--r--widget/tests/window_bug593307_centerscreen.xul27
-rw-r--r--widget/tests/window_bug593307_offscreen.xul34
-rw-r--r--widget/tests/window_composition_text_querycontent.xul6992
-rw-r--r--widget/tests/window_imestate_iframes.html380
-rw-r--r--widget/tests/window_mouse_scroll_win.html1531
-rw-r--r--widget/tests/window_picker_no_crash_child.html10
-rw-r--r--widget/tests/window_state_windows.xul87
-rw-r--r--widget/tests/window_wheeltransaction.xul1560
71 files changed, 23727 insertions, 0 deletions
diff --git a/widget/tests/TestAppShellSteadyState.cpp b/widget/tests/TestAppShellSteadyState.cpp
new file mode 100644
index 000000000..03888fc0a
--- /dev/null
+++ b/widget/tests/TestAppShellSteadyState.cpp
@@ -0,0 +1,503 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "TestHarness.h"
+
+#include "nsIAppShell.h"
+#include "nsIAppShellService.h"
+#include "nsIDocument.h"
+#include "nsIDOMEvent.h"
+#include "nsIDOMEventListener.h"
+#include "nsIDOMEventTarget.h"
+#include "nsIDOMWindow.h"
+#include "nsIDOMWindowUtils.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIRunnable.h"
+#include "nsIURI.h"
+#include "nsIWebBrowserChrome.h"
+#include "nsIXULWindow.h"
+
+#include "nsAppShellCID.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsNetUtil.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Attributes.h"
+
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+using namespace mozilla;
+
+typedef void (*TestFunc)(nsIAppShell*);
+
+bool gStableStateEventHasRun = false;
+
+class ExitAppShellRunnable : public Runnable
+{
+ nsCOMPtr<nsIAppShell> mAppShell;
+
+public:
+ explicit ExitAppShellRunnable(nsIAppShell* aAppShell)
+ : mAppShell(aAppShell)
+ { }
+
+ NS_IMETHOD
+ Run() override
+ {
+ return mAppShell->Exit();
+ }
+};
+
+class StableStateRunnable : public Runnable
+{
+public:
+ NS_IMETHOD
+ Run() override
+ {
+ if (gStableStateEventHasRun) {
+ fail("StableStateRunnable already ran");
+ }
+ gStableStateEventHasRun = true;
+ return NS_OK;
+ }
+};
+
+class CheckStableStateRunnable : public Runnable
+{
+ bool mShouldHaveRun;
+
+public:
+ explicit CheckStableStateRunnable(bool aShouldHaveRun)
+ : mShouldHaveRun(aShouldHaveRun)
+ { }
+
+ NS_IMETHOD
+ Run() override
+ {
+ if (mShouldHaveRun == gStableStateEventHasRun) {
+ passed("StableStateRunnable state correct (%s)",
+ mShouldHaveRun ? "true" : "false");
+ } else {
+ fail("StableStateRunnable ran at wrong time");
+ }
+ return NS_OK;
+ }
+};
+
+class ScheduleStableStateRunnable : public CheckStableStateRunnable
+{
+protected:
+ nsCOMPtr<nsIAppShell> mAppShell;
+
+public:
+ explicit ScheduleStableStateRunnable(nsIAppShell* aAppShell)
+ : CheckStableStateRunnable(false), mAppShell(aAppShell)
+ { }
+
+ NS_IMETHOD
+ Run() override
+ {
+ CheckStableStateRunnable::Run();
+
+ nsCOMPtr<nsIRunnable> runnable = new StableStateRunnable();
+ nsresult rv = mAppShell->RunBeforeNextEvent(runnable);
+ if (NS_FAILED(rv)) {
+ fail("RunBeforeNextEvent returned failure code %u", rv);
+ }
+
+ return rv;
+ }
+};
+
+class NextTestRunnable : public Runnable
+{
+ nsCOMPtr<nsIAppShell> mAppShell;
+
+public:
+ explicit NextTestRunnable(nsIAppShell* aAppShell)
+ : mAppShell(aAppShell)
+ { }
+
+ NS_IMETHOD Run();
+};
+
+class ScheduleNestedStableStateRunnable : public ScheduleStableStateRunnable
+{
+public:
+ explicit ScheduleNestedStableStateRunnable(nsIAppShell* aAppShell)
+ : ScheduleStableStateRunnable(aAppShell)
+ { }
+
+ NS_IMETHOD
+ Run() override
+ {
+ ScheduleStableStateRunnable::Run();
+
+ nsCOMPtr<nsIRunnable> runnable = new CheckStableStateRunnable(false);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch check runnable");
+ }
+
+ if (NS_FAILED(NS_ProcessPendingEvents(nullptr))) {
+ fail("Failed to process all pending events");
+ }
+
+ runnable = new CheckStableStateRunnable(true);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch check runnable");
+ }
+
+ runnable = new NextTestRunnable(mAppShell);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch next test runnable");
+ }
+
+ return NS_OK;
+ }
+};
+
+class EventListener final : public nsIDOMEventListener
+{
+ nsCOMPtr<nsIAppShell> mAppShell;
+
+ static nsIDOMWindowUtils* sWindowUtils;
+ static nsIAppShell* sAppShell;
+
+ ~EventListener() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ explicit EventListener(nsIAppShell* aAppShell)
+ : mAppShell(aAppShell)
+ { }
+
+ NS_IMETHOD
+ HandleEvent(nsIDOMEvent* aEvent) override
+ {
+ nsString type;
+ if (NS_FAILED(aEvent->GetType(type))) {
+ fail("Failed to get event type");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (type.EqualsLiteral("load")) {
+ passed("Got load event");
+
+ nsCOMPtr<nsIDOMEventTarget> target;
+ if (NS_FAILED(aEvent->GetTarget(getter_AddRefs(target)))) {
+ fail("Failed to get event type");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIDocument> document = do_QueryInterface(target);
+ if (!document) {
+ fail("Failed to QI to nsIDocument!");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsPIDOMWindow> window = document->GetWindow();
+ if (!window) {
+ fail("Failed to get window from document!");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window);
+ if (!utils) {
+ fail("Failed to get DOMWindowUtils!");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!ScheduleTimer(utils)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+ }
+
+ if (type.EqualsLiteral("keypress")) {
+ passed("Got keypress event");
+
+ nsCOMPtr<nsIRunnable> runnable = new StableStateRunnable();
+ nsresult rv = mAppShell->RunBeforeNextEvent(runnable);
+ if (NS_FAILED(rv)) {
+ fail("RunBeforeNextEvent returned failure code %u", rv);
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+ }
+
+ fail("Got an unexpected event: %s", NS_ConvertUTF16toUTF8(type).get());
+ return NS_OK;
+ }
+
+#ifdef XP_WIN
+ static VOID CALLBACK
+ TimerCallback(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
+ {
+ if (sWindowUtils) {
+ nsCOMPtr<nsIDOMWindowUtils> utils = dont_AddRef(sWindowUtils);
+ sWindowUtils = nullptr;
+
+ if (gStableStateEventHasRun) {
+ fail("StableStateRunnable ran at wrong time");
+ } else {
+ passed("StableStateRunnable state correct (false)");
+ }
+
+ int32_t layout = 0x409; // US
+ int32_t keyCode = 0x41; // VK_A
+ NS_NAMED_LITERAL_STRING(a, "a");
+
+ if (NS_FAILED(utils->SendNativeKeyEvent(layout, keyCode, 0, a, a, nullptr))) {
+ fail("Failed to synthesize native event");
+ }
+
+ return;
+ }
+
+ KillTimer(nullptr, idEvent);
+
+ nsCOMPtr<nsIAppShell> appShell = dont_AddRef(sAppShell);
+
+ if (!gStableStateEventHasRun) {
+ fail("StableStateRunnable didn't run yet");
+ } else {
+ passed("StableStateRunnable state correct (true)");
+ }
+
+ nsCOMPtr<nsIRunnable> runnable = new NextTestRunnable(appShell);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch next test runnable");
+ }
+
+ }
+#endif
+
+ bool
+ ScheduleTimer(nsIDOMWindowUtils* aWindowUtils)
+ {
+#ifdef XP_WIN
+ UINT_PTR timerId = SetTimer(nullptr, 0, 1000, (TIMERPROC)TimerCallback);
+ if (!timerId) {
+ fail("SetTimer failed!");
+ return false;
+ }
+
+ nsCOMPtr<nsIDOMWindowUtils> utils = aWindowUtils;
+ utils.forget(&sWindowUtils);
+
+ nsCOMPtr<nsIAppShell> appShell = mAppShell;
+ appShell.forget(&sAppShell);
+
+ return true;
+#else
+ return false;
+#endif
+ }
+};
+
+nsIDOMWindowUtils* EventListener::sWindowUtils = nullptr;
+nsIAppShell* EventListener::sAppShell = nullptr;
+
+NS_IMPL_ISUPPORTS(EventListener, nsIDOMEventListener)
+
+already_AddRefed<nsIAppShell>
+GetAppShell()
+{
+ static const char* platforms[] = {
+ "android", "mac", "gonk", "gtk", "qt", "win"
+ };
+
+ NS_NAMED_LITERAL_CSTRING(contractPrefix, "@mozilla.org/widget/appshell/");
+ NS_NAMED_LITERAL_CSTRING(contractSuffix, ";1");
+
+ for (size_t index = 0; index < ArrayLength(platforms); index++) {
+ nsAutoCString contractID(contractPrefix);
+ contractID.AppendASCII(platforms[index]);
+ contractID.Append(contractSuffix);
+
+ nsCOMPtr<nsIAppShell> appShell = do_GetService(contractID.get());
+ if (appShell) {
+ return appShell.forget();
+ }
+ }
+
+ return nullptr;
+}
+
+void
+Test1(nsIAppShell* aAppShell)
+{
+ // Schedule stable state runnable to be run before next event.
+
+ nsCOMPtr<nsIRunnable> runnable = new StableStateRunnable();
+ if (NS_FAILED(aAppShell->RunBeforeNextEvent(runnable))) {
+ fail("RunBeforeNextEvent failed");
+ }
+
+ runnable = new CheckStableStateRunnable(true);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch check runnable");
+ }
+
+ runnable = new NextTestRunnable(aAppShell);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch next test runnable");
+ }
+}
+
+void
+Test2(nsIAppShell* aAppShell)
+{
+ // Schedule stable state runnable to be run before next event from another
+ // runnable.
+
+ nsCOMPtr<nsIRunnable> runnable = new ScheduleStableStateRunnable(aAppShell);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch schedule runnable");
+ }
+
+ runnable = new CheckStableStateRunnable(true);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch check runnable");
+ }
+
+ runnable = new NextTestRunnable(aAppShell);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch next test runnable");
+ }
+}
+
+void
+Test3(nsIAppShell* aAppShell)
+{
+ // Schedule steadystate runnable to be run before next event with nested loop.
+
+ nsCOMPtr<nsIRunnable> runnable =
+ new ScheduleNestedStableStateRunnable(aAppShell);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch schedule runnable");
+ }
+}
+
+bool
+Test4Internal(nsIAppShell* aAppShell)
+{
+#ifndef XP_WIN
+ // Not sure how to test on other platforms.
+ return false;
+#else
+ nsCOMPtr<nsIAppShellService> appService =
+ do_GetService(NS_APPSHELLSERVICE_CONTRACTID);
+ if (!appService) {
+ fail("Failed to get appshell service!");
+ return false;
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), "about:", nullptr))) {
+ fail("Failed to create new uri");
+ return false;
+ }
+
+ uint32_t flags = nsIWebBrowserChrome::CHROME_DEFAULT;
+
+ nsCOMPtr<nsIXULWindow> xulWindow;
+ if (NS_FAILED(appService->CreateTopLevelWindow(nullptr, uri, flags, 100, 100, nullptr,
+ getter_AddRefs(xulWindow)))) {
+ fail("Failed to create new window");
+ return false;
+ }
+
+ nsCOMPtr<nsIDOMWindow> window = do_GetInterface(xulWindow);
+ if (!window) {
+ fail("Can't get dom window!");
+ return false;
+ }
+
+ nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(window);
+ if (!target) {
+ fail("Can't QI to nsIDOMEventTarget!");
+ return false;
+ }
+
+ nsCOMPtr<nsIDOMEventListener> listener = new EventListener(aAppShell);
+ if (NS_FAILED(target->AddEventListener(NS_LITERAL_STRING("keypress"),
+ listener, false, false)) ||
+ NS_FAILED(target->AddEventListener(NS_LITERAL_STRING("load"), listener,
+ false, false))) {
+ fail("Can't add event listeners!");
+ return false;
+ }
+
+ return true;
+#endif
+}
+
+void
+Test4(nsIAppShell* aAppShell)
+{
+ if (!Test4Internal(aAppShell)) {
+ nsCOMPtr<nsIRunnable> runnable = new NextTestRunnable(aAppShell);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch next test runnable");
+ }
+ }
+}
+
+const TestFunc gTests[] = {
+ Test1, Test2, Test3, Test4
+};
+
+size_t gTestIndex = 0;
+
+NS_IMETHODIMP
+NextTestRunnable::Run()
+{
+ if (gTestIndex > 0) {
+ passed("Finished test %u", gTestIndex);
+ }
+
+ gStableStateEventHasRun = false;
+
+ if (gTestIndex < ArrayLength(gTests)) {
+ gTests[gTestIndex++](mAppShell);
+ }
+ else {
+ nsCOMPtr<nsIRunnable> exitRunnable = new ExitAppShellRunnable(mAppShell);
+
+ nsresult rv = NS_DispatchToCurrentThread(exitRunnable);
+ if (NS_FAILED(rv)) {
+ fail("Failed to dispatch exit runnable!");
+ }
+ }
+
+ return NS_OK;
+}
+
+int main(int argc, char** argv)
+{
+ ScopedLogging log;
+ ScopedXPCOM xpcom("TestAppShellSteadyState");
+
+ if (!xpcom.failed()) {
+ nsCOMPtr<nsIAppShell> appShell = GetAppShell();
+ if (!appShell) {
+ fail("Couldn't get appshell!");
+ } else {
+ nsCOMPtr<nsIRunnable> runnable = new NextTestRunnable(appShell);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch next test runnable");
+ } else if (NS_FAILED(appShell->Run())) {
+ fail("Failed to run appshell");
+ }
+ }
+ }
+
+ return gFailCount != 0;
+}
diff --git a/widget/tests/TestChromeMargin.cpp b/widget/tests/TestChromeMargin.cpp
new file mode 100644
index 000000000..22330f467
--- /dev/null
+++ b/widget/tests/TestChromeMargin.cpp
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* This tests the margin parsing functionality in nsAttrValue.cpp, which
+ * is accessible via nsContentUtils, and is used in setting chromemargins
+ * to widget windows. It's located here due to linking issues in the
+ * content directory.
+ */
+
+/* This test no longer compiles now that we've removed nsIContentUtils (bug
+ * 647273). We need to be internal code in order to include nsContentUtils.h,
+ * but defining MOZILLA_INTERNAL_API is not enough to make us internal.
+ */
+
+#include "TestHarness.h"
+
+#ifndef MOZILLA_INTERNAL_API
+// some of the includes make use of internal string types
+#define nsAString_h___
+#define nsString_h___
+#define nsStringFwd_h___
+#define nsReadableUtils_h___
+class nsACString;
+class nsAString;
+class nsAFlatString;
+class nsAFlatCString;
+class nsAdoptingString;
+class nsAdoptingCString;
+class nsXPIDLString;
+template<class T> class nsReadingIterator;
+#endif
+
+#include "nscore.h"
+#include "nsContentUtils.h"
+
+#ifndef MOZILLA_INTERNAL_API
+#undef nsString_h___
+#undef nsAString_h___
+#undef nsReadableUtils_h___
+#endif
+
+struct DATA {
+ bool shouldfail;
+ const char* margins;
+ int top;
+ int right;
+ int bottom;
+ int left;
+};
+
+const bool SHOULD_FAIL = true;
+const int SHOULD_PASS = false;
+
+const DATA Data[] = {
+ { SHOULD_FAIL, "", 1, 2, 3, 4 },
+ { SHOULD_FAIL, "1,0,0,0", 1, 2, 3, 4 },
+ { SHOULD_FAIL, "1,2,0,0", 1, 2, 3, 4 },
+ { SHOULD_FAIL, "1,2,3,0", 1, 2, 3, 4 },
+ { SHOULD_FAIL, "4,3,2,1", 1, 2, 3, 4 },
+ { SHOULD_FAIL, "azsasdasd", 0, 0, 0, 0 },
+ { SHOULD_FAIL, ",azsasdasd", 0, 0, 0, 0 },
+ { SHOULD_FAIL, " ", 1, 2, 3, 4 },
+ { SHOULD_FAIL, "azsdfsdfsdfsdfsdfsasdasd,asdasdasdasdasdasd,asdadasdasd,asdasdasdasd", 0, 0, 0, 0 },
+ { SHOULD_FAIL, "as,as,as,as", 0, 0, 0, 0 },
+ { SHOULD_FAIL, "0,0,0", 0, 0, 0, 0 },
+ { SHOULD_FAIL, "0,0", 0, 0, 0, 0 },
+ { SHOULD_FAIL, "4.6,1,1,1", 0, 0, 0, 0 },
+ { SHOULD_FAIL, ",,,,", 0, 0, 0, 0 },
+ { SHOULD_FAIL, "1, , , ,", 0, 0, 0, 0 },
+ { SHOULD_FAIL, "1, , ,", 0, 0, 0, 0 },
+ { SHOULD_FAIL, "@!@%^&^*()", 1, 2, 3, 4 },
+ { SHOULD_PASS, "4,3,2,1", 4, 3, 2, 1 },
+ { SHOULD_PASS, "-4,-3,-2,-1", -4, -3, -2, -1 },
+ { SHOULD_PASS, "10000,3,2,1", 10000, 3, 2, 1 },
+ { SHOULD_PASS, "4 , 3 , 2 , 1", 4, 3, 2, 1 },
+ { SHOULD_PASS, "4, 3 ,2,1", 4, 3, 2, 1 },
+ { SHOULD_FAIL, "4,3,2,10000000000000 --", 4, 3, 2, 10000000000000 },
+ { SHOULD_PASS, "4,3,2,1000", 4, 3, 2, 1000 },
+ { SHOULD_PASS, "2147483647,3,2,1000", 2147483647, 3, 2, 1000 },
+ { SHOULD_PASS, "2147483647,2147483647,2147483647,2147483647", 2147483647, 2147483647, 2147483647, 2147483647 },
+ { SHOULD_PASS, "-2147483647,3,2,1000", -2147483647, 3, 2, 1000 },
+ { SHOULD_FAIL, "2147483648,3,2,1000", 1, 3, 2, 1000 },
+ { 0, nullptr, 0, 0, 0, 0 }
+};
+
+void DoAttrValueTest()
+{
+ int idx = -1;
+ bool didFail = false;
+ while (Data[++idx].margins) {
+ nsAutoString str;
+ str.AssignLiteral(Data[idx].margins);
+ nsIntMargin values(99,99,99,99);
+ bool result = nsContentUtils::ParseIntMarginValue(str, values);
+
+ // if the parse fails
+ if (!result) {
+ if (Data[idx].shouldfail)
+ continue;
+ fail(Data[idx].margins);
+ didFail = true;
+ printf("*1\n");
+ continue;
+ }
+
+ if (Data[idx].shouldfail) {
+ if (Data[idx].top == values.top &&
+ Data[idx].right == values.right &&
+ Data[idx].bottom == values.bottom &&
+ Data[idx].left == values.left) {
+ // not likely
+ fail(Data[idx].margins);
+ didFail = true;
+ printf("*2\n");
+ continue;
+ }
+ // good failure, parse failed and that's what we expected.
+ continue;
+ }
+#if 0
+ printf("%d==%d %d==%d %d==%d %d==%d\n",
+ Data[idx].top, values.top,
+ Data[idx].right, values.right,
+ Data[idx].bottom, values.bottom,
+ Data[idx].left, values.left);
+#endif
+ if (Data[idx].top == values.top &&
+ Data[idx].right == values.right &&
+ Data[idx].bottom == values.bottom &&
+ Data[idx].left == values.left) {
+ // good parse results
+ continue;
+ }
+ else {
+ fail(Data[idx].margins);
+ didFail = true;
+ printf("*3\n");
+ continue;
+ }
+ }
+
+ if (!didFail)
+ passed("nsAttrValue margin parsing tests passed.");
+}
+
+int main(int argc, char** argv)
+{
+ ScopedXPCOM xpcom("");
+ if (xpcom.failed())
+ return 1;
+ DoAttrValueTest();
+ return 0;
+}
diff --git a/widget/tests/bug586713_window.xul b/widget/tests/bug586713_window.xul
new file mode 100644
index 000000000..78397afad
--- /dev/null
+++ b/widget/tests/bug586713_window.xul
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="bug586713_window"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="300"
+ height="300"
+ onload="onLoad();"
+ title="Bug 586713 Test">
+
+ <menubar id="nativemenubar">
+ <menu id="foo" label="Foo">
+ <menupopup>
+ <menuitem label="FooItem0"/>
+ </menupopup>
+ </menu>
+ </menubar>
+
+ <script type="application/javascript"><![CDATA[
+ function ok(condition, message) {
+ window.opener.wrappedJSObject.SimpleTest.ok(condition, message);
+ }
+
+ function onTestsFinished() {
+ window.close();
+ window.opener.wrappedJSObject.SimpleTest.finish();
+ }
+
+ var fooCallCount = 0;
+ function foo() {
+ fooCallCount++;
+ let instruction = document.createProcessingInstruction("xml-stylesheet", 'href="chrome://foo.css" type="text/css"');
+ document.insertBefore(instruction, document.documentElement);
+ if (fooCallCount == 2) {
+ ok(true, "If we got here we didn't crash, excellent.");
+ onTestsFinished();
+ }
+ }
+
+ function onLoad() {
+ foo();
+ setTimeout(() => foo(), 0);
+ }
+ ]]></script>
+</window>
diff --git a/widget/tests/chrome.ini b/widget/tests/chrome.ini
new file mode 100644
index 000000000..00d0d57a9
--- /dev/null
+++ b/widget/tests/chrome.ini
@@ -0,0 +1,100 @@
+[DEFAULT]
+skip-if = os == 'android'
+support-files =
+ empty_window.xul
+ utils.js
+
+[test_bug343416.xul]
+skip-if = debug
+[test_bug429954.xul]
+support-files = window_bug429954.xul
+[test_bug444800.xul]
+subsuite = clipboard
+[test_bug478536.xul]
+skip-if = true # Bug 561929
+support-files = window_bug478536.xul
+[test_bug517396.xul]
+[test_bug538242.xul]
+support-files = window_bug538242.xul
+[test_bug593307.xul]
+support-files = window_bug593307_offscreen.xul window_bug593307_centerscreen.xul
+[test_bug1151186.html]
+skip-if = os == 'linux' && debug #Bug 1176038
+[test_keycodes.xul]
+[test_wheeltransaction.xul]
+support-files = window_wheeltransaction.xul
+[test_imestate.html]
+support-files = window_imestate_iframes.html
+[test_plugin_scroll_consistency.html]
+[test_composition_text_querycontent.xul]
+support-files = window_composition_text_querycontent.xul
+[test_input_events_on_deactive_window.xul]
+[test_position_on_resize.xul]
+[test_sizemode_events.xul]
+[test_taskbar_progress.xul]
+skip-if = toolkit != "cocoa" && toolkit != "windows"
+[test_bug760802.xul]
+[test_clipboard.xul]
+subsuite = clipboard
+[test_panel_mouse_coords.xul]
+skip-if = toolkit == "windows" # bug 1009955
+
+# Cocoa
+[test_native_menus.xul]
+skip-if = toolkit != "cocoa"
+support-files = native_menus_window.xul
+[test_native_mouse_mac.xul]
+skip-if = toolkit != "cocoa" || os_version == '10.10' # 10.10: bug 1137575
+support-files = native_mouse_mac_window.xul
+[test_bug413277.html]
+skip-if = toolkit != "cocoa"
+[test_bug428405.xul]
+skip-if = toolkit != "cocoa"
+[test_bug466599.xul]
+subsuite = clipboard
+skip-if = toolkit != "cocoa"
+[test_bug485118.xul]
+skip-if = toolkit != "cocoa"
+[test_bug522217.xul]
+tags = fullscreen
+skip-if = toolkit != "cocoa"
+support-files = window_bug522217.xul
+[test_platform_colors.xul]
+#skip-if = toolkit != "cocoa"
+skip-if = true # Bug 1207190
+[test_standalone_native_menu.xul]
+skip-if = toolkit != "cocoa"
+support-files = standalone_native_menu_window.xul
+[test_bug586713.xul]
+skip-if = toolkit != "cocoa"
+support-files = bug586713_window.xul
+[test_key_event_counts.xul]
+skip-if = toolkit != "cocoa"
+[test_bug596600.xul]
+skip-if = toolkit != "cocoa"
+[test_bug673301.xul]
+subsuite = clipboard
+skip-if = toolkit != "cocoa"
+[test_secure_input.html]
+skip-if = toolkit != "cocoa"
+[test_native_key_bindings_mac.html]
+skip-if = toolkit != "cocoa"
+[test_system_status_bar.xul]
+skip-if = toolkit != "cocoa"
+
+# Windows
+# taskbar_previews.xul
+# window_state_windows.xul
+[test_chrome_context_menus_win.xul]
+skip-if = toolkit != "windows"
+support-files = chrome_context_menus_win.xul
+[test_plugin_input_event.html]
+skip-if = toolkit != "windows"
+[test_mouse_scroll.xul]
+skip-if = toolkit != "windows"
+support-files = window_mouse_scroll_win.html
+
+# Privacy relevant
+[test_bug1123480.xul]
+subsuite = clipboard
+
diff --git a/widget/tests/chrome_context_menus_win.xul b/widget/tests/chrome_context_menus_win.xul
new file mode 100644
index 000000000..9a4590747
--- /dev/null
+++ b/widget/tests/chrome_context_menus_win.xul
@@ -0,0 +1,101 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="ChromeContextMenuTest"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="300"
+ height="300"
+ title="Chrome Context Menu Test w/Plugin Focus">
+
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+<popupset>
+ <menupopup id="testmenu" onpopupshown="menuDisplayed()">
+ <menuitem label="One"/>
+ <menuitem label="Two"/>
+ <menuitem label="Three"/>
+ </menupopup>
+</popupset>
+
+<toolbox>
+ <toolbar id="nav-toolbar" style="height:30px" context="testmenu">
+ </toolbar>
+</toolbox>
+
+<script type="application/javascript"><![CDATA[
+
+function ok(condition, message) {
+ window.opener.wrappedJSObject.SimpleTest.ok(condition, message);
+}
+
+function onTestsFinished() {
+ window.close();
+ window.opener.wrappedJSObject.SimpleTest.finish();
+}
+
+function openContextMenuFor(element) {
+
+ var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIDOMWindowUtils);
+
+ var tbX = (window.mozInnerScreenX + 10) * utils.screenPixelsPerCSSPixel;
+ var tbY = (window.mozInnerScreenY + 10) * utils.screenPixelsPerCSSPixel;
+
+ // See nsWidnow's SynthesizeNativeMouseEvent & SendInput on msdn
+ var MOUSEEVENTF_RIGHTDOWN = 0x0008;
+ var MOUSEEVENTF_RIGHTUP = 0x0010;
+
+ utils.sendNativeMouseEvent(tbX, tbY,
+ MOUSEEVENTF_RIGHTDOWN,
+ 0, element);
+ utils.sendNativeMouseEvent(tbX, tbY,
+ MOUSEEVENTF_RIGHTUP,
+ 0, element);
+}
+
+var tid = 0;
+
+function onFocus() {
+ var _delayedOnLoad = function() {
+ var plugin = document.getElementById("plugin");
+ var toolbar = document.getElementById("nav-toolbar");
+
+ plugin.focus();
+
+ tid = setTimeout("menuTimeout()", 5000);
+
+ openContextMenuFor(toolbar);
+ }
+ setTimeout(_delayedOnLoad, 3000);
+}
+
+function menuTimeout() {
+ ok(false, "Right-click chrome menu did not display with focus on a plugin.");
+ onTestsFinished();
+}
+
+function menuDisplayed() {
+ clearTimeout(tid);
+ ok(true, "Right-click chrome menu displayed despite plugin having focus.");
+ onTestsFinished();
+}
+
+window.opener.wrappedJSObject.SimpleTest.waitForFocus(onFocus, window);
+
+
+]]></script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <br/>
+ <embed id="plugin" type="application/x-test" width="50" height="50"></embed>
+</body>
+
+</window>
diff --git a/widget/tests/empty_window.xul b/widget/tests/empty_window.xul
new file mode 100644
index 000000000..f0e01761d
--- /dev/null
+++ b/widget/tests/empty_window.xul
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<window title="Empty window"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/>
diff --git a/widget/tests/mochitest.ini b/widget/tests/mochitest.ini
new file mode 100644
index 000000000..bf7bfe689
--- /dev/null
+++ b/widget/tests/mochitest.ini
@@ -0,0 +1,12 @@
+[DEFAULT]
+support-files = utils.js
+
+[test_assign_event_data.html]
+subsuite = clipboard
+skip-if = toolkit == "cocoa" # Mac: Bug 933303
+[test_bug565392.html]
+subsuite = clipboard
+skip-if = toolkit != "windows" || e10s # Bug 1267406
+[test_picker_no_crash.html]
+skip-if = toolkit != "windows" || e10s # Bug 1267491
+support-files = window_picker_no_crash_child.html
diff --git a/widget/tests/moz.build b/widget/tests/moz.build
new file mode 100644
index 000000000..750202b48
--- /dev/null
+++ b/widget/tests/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
+MOCHITEST_MANIFESTS += ['mochitest.ini']
+MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
+
+# if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+#
+# Test disabled because it requires the internal API. Re-enabling this test
+# is bug 652123.
+# CPP_UNIT_TESTS += ['TestChromeMargin']
diff --git a/widget/tests/native_menus_window.xul b/widget/tests/native_menus_window.xul
new file mode 100644
index 000000000..6e614d017
--- /dev/null
+++ b/widget/tests/native_menus_window.xul
@@ -0,0 +1,285 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="NativeMenuWindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="300"
+ height="300"
+ onload="onLoad();"
+ title="Native Menu Test">
+
+ <command id="cmd_FooItem0" oncommand="executedCommandID = 'cmd_FooItem0';"/>
+ <command id="cmd_FooItem1" oncommand="executedCommandID = 'cmd_FooItem1';"/>
+ <command id="cmd_BarItem0" oncommand="executedCommandID = 'cmd_BarItem0';"/>
+ <command id="cmd_BarItem1" oncommand="executedCommandID = 'cmd_BarItem1';"/>
+ <command id="cmd_NewItem0" oncommand="executedCommandID = 'cmd_NewItem0';"/>
+ <command id="cmd_NewItem1" oncommand="executedCommandID = 'cmd_NewItem1';"/>
+ <command id="cmd_NewItem2" oncommand="executedCommandID = 'cmd_NewItem2';"/>
+ <command id="cmd_NewItem3" oncommand="executedCommandID = 'cmd_NewItem3';"/>
+ <command id="cmd_NewItem4" oncommand="executedCommandID = 'cmd_NewItem4';"/>
+ <command id="cmd_NewItem5" oncommand="executedCommandID = 'cmd_NewItem5';"/>
+
+ <!-- We do not modify any menus or menu items defined here in testing. These
+ serve as a baseline structure for us to test after other modifications.
+ We add children to the menubar defined here and test by modifying those
+ children. -->
+ <menubar id="nativemenubar">
+ <menu id="foo" label="Foo">
+ <menupopup>
+ <menuitem label="FooItem0" command="cmd_FooItem0"/>
+ <menuitem label="FooItem1" command="cmd_FooItem1"/>
+ <menuseparator/>
+ <menu label="Bar">
+ <menupopup>
+ <menuitem label="BarItem0" command="cmd_BarItem0"/>
+ <menuitem label="BarItem1" command="cmd_BarItem1"/>
+ </menupopup>
+ </menu>
+ </menupopup>
+ </menu>
+ </menubar>
+
+ <script type="application/javascript"><![CDATA[
+
+ function ok(condition, message) {
+ window.opener.wrappedJSObject.SimpleTest.ok(condition, message);
+ }
+
+ function onTestsFinished() {
+ window.close();
+ window.opener.wrappedJSObject.SimpleTest.finish();
+ }
+
+ // Force a menu to update itself. All of the menus parents will be updated
+ // as well. An empty string will force a complete menu system reload.
+ function forceUpdateNativeMenuAt(location) {
+ var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIDOMWindowUtils);
+ try {
+ utils.forceUpdateNativeMenuAt(location);
+ }
+ catch (e) {
+ dump(e + "\n");
+ }
+ }
+
+ var executedCommandID = "";
+
+ function testItem(location, targetID) {
+ var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIDOMWindowUtils);
+ var correctCommandHandler = false;
+ try {
+ utils.activateNativeMenuItemAt(location);
+ correctCommandHandler = executedCommandID == targetID;
+ }
+ catch (e) {
+ dump(e + "\n");
+ }
+ finally {
+ executedCommandID = "";
+ return correctCommandHandler;
+ }
+ }
+
+ function createXULMenuPopup() {
+ const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ var item = document.createElementNS(XUL_NS, "menupopup");
+ return item;
+ }
+
+ function createXULMenu(aLabel) {
+ const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ var item = document.createElementNS(XUL_NS, "menu");
+ item.setAttribute("label", aLabel);
+ return item;
+ }
+
+ function createXULMenuItem(aLabel, aCommandId) {
+ const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ var item = document.createElementNS(XUL_NS, "menuitem");
+ item.setAttribute("label", aLabel);
+ item.setAttribute("command", aCommandId);
+ return item;
+ }
+
+ function runBaseMenuTests() {
+ forceUpdateNativeMenuAt("0|3");
+ return testItem("0|0", "cmd_FooItem0") &&
+ testItem("0|1", "cmd_FooItem1") &&
+ testItem("0|3|0", "cmd_BarItem0") &&
+ testItem("0|3|1", "cmd_BarItem1");
+ }
+
+ function onLoad() {
+ var _delayedOnLoad = function() {
+ // First let's run the base menu tests.
+ ok(runBaseMenuTests());
+
+ // Set up some nodes that we'll use.
+ var menubarNode = document.getElementById("nativemenubar");
+ var newMenu0 = createXULMenu("NewMenu0");
+ var newMenu1 = createXULMenu("NewMenu1");
+ var newMenuPopup0 = createXULMenuPopup();
+ var newMenuPopup1 = createXULMenuPopup();
+ var newMenuItem0 = createXULMenuItem("NewMenuItem0", "cmd_NewItem0");
+ var newMenuItem1 = createXULMenuItem("NewMenuItem1", "cmd_NewItem1");
+ var newMenuItem2 = createXULMenuItem("NewMenuItem2", "cmd_NewItem2");
+ var newMenuItem3 = createXULMenuItem("NewMenuItem3", "cmd_NewItem3");
+ var newMenuItem4 = createXULMenuItem("NewMenuItem4", "cmd_NewItem4");
+ var newMenuItem5 = createXULMenuItem("NewMenuItem5", "cmd_NewItem5");
+
+ // Create another submenu with hierarchy via DOM manipulation.
+ // ******************
+ // * Foo * NewMenu0 * <- Menu bar
+ // ******************
+ // ****************
+ // * NewMenuItem0 * <- NewMenu0 submenu
+ // ****************
+ // * NewMenuItem1 *
+ // ****************
+ // * NewMenuItem2 *
+ // *******************************
+ // * NewMenu1 > * NewMenuItem3 * <- NewMenu1 submenu
+ // *******************************
+ // * NewMenuItem4 *
+ // ****************
+ // * NewMenuItem5 *
+ // ****************
+ newMenu0.appendChild(newMenuPopup0);
+ newMenuPopup0.appendChild(newMenuItem0);
+ newMenuPopup0.appendChild(newMenuItem1);
+ newMenuPopup0.appendChild(newMenuItem2);
+ newMenuPopup0.appendChild(newMenu1);
+ newMenu1.appendChild(newMenuPopup1);
+ newMenuPopup1.appendChild(newMenuItem3);
+ newMenuPopup1.appendChild(newMenuItem4);
+ newMenuPopup1.appendChild(newMenuItem5);
+ //XXX - we have to append the menu to the top-level of the menu bar
+ // only after constructing it. If we append before construction, it is
+ // invalid because it has no children and we don't validate it if we add
+ // children later.
+ menubarNode.appendChild(newMenu0);
+ forceUpdateNativeMenuAt("1|3");
+ // Run basic tests again.
+ ok(runBaseMenuTests());
+
+ // Error strings.
+ var sa = "Command handler(s) should have activated";
+ var sna = "Command handler(s) should not have activated";
+
+ // Test middle items.
+ ok(testItem("1|1", "cmd_NewItem1"), sa);
+ ok(testItem("1|3|1", "cmd_NewItem4"), sa);
+
+ // Hide newMenu0.
+ newMenu0.setAttribute("hidden", "true");
+ ok(runBaseMenuTests(), sa); // the base menu should still be unhidden
+ ok(!testItem("1|0", ""), sna);
+ ok(!testItem("1|1", ""), sna);
+ ok(!testItem("1|2", ""), sna);
+ ok(!testItem("1|3|0", ""), sna);
+ ok(!testItem("1|3|1", ""), sna);
+ ok(!testItem("1|3|2", ""), sna);
+
+ // Show newMenu0.
+ newMenu0.setAttribute("hidden", "false");
+ forceUpdateNativeMenuAt("1|3");
+ ok(runBaseMenuTests(), sa);
+ ok(testItem("1|0", "cmd_NewItem0"), sa);
+ ok(testItem("1|1", "cmd_NewItem1"), sa);
+ ok(testItem("1|2", "cmd_NewItem2"), sa);
+ ok(testItem("1|3|0", "cmd_NewItem3"), sa);
+ ok(testItem("1|3|1", "cmd_NewItem4"), sa);
+ ok(testItem("1|3|2", "cmd_NewItem5"), sa);
+
+ // Hide items.
+ newMenuItem1.setAttribute("hidden", "true");
+ newMenuItem4.setAttribute("hidden", "true");
+ forceUpdateNativeMenuAt("1|2");
+ ok(runBaseMenuTests(), sa);
+ ok(testItem("1|0", "cmd_NewItem0"), sa);
+ ok(testItem("1|1", "cmd_NewItem2"), sa);
+ ok(!testItem("1|2", ""), sna);
+ ok(testItem("1|2|0", "cmd_NewItem3"), sa);
+ ok(testItem("1|2|1", "cmd_NewItem5"), sa);
+ ok(!testItem("1|2|2", ""), sna);
+
+ // Show items.
+ newMenuItem1.setAttribute("hidden", "false");
+ newMenuItem4.setAttribute("hidden", "false");
+ forceUpdateNativeMenuAt("1|3");
+ ok(runBaseMenuTests(), sa);
+ ok(testItem("1|0", "cmd_NewItem0"), sa);
+ ok(testItem("1|1", "cmd_NewItem1"), sa);
+ ok(testItem("1|2", "cmd_NewItem2"), sa);
+ ok(testItem("1|3|0", "cmd_NewItem3"), sa);
+ ok(testItem("1|3|1", "cmd_NewItem4"), sa);
+ ok(testItem("1|3|2", "cmd_NewItem5"), sa);
+
+ // At this point in the tests the state of the menus has been returned
+ // to the originally diagramed state.
+
+ // Test command disabling
+ var cmd_NewItem0 = document.getElementById("cmd_NewItem0");
+ ok(testItem("1|0", "cmd_NewItem0"), sa);
+ cmd_NewItem0.setAttribute("disabled", "true");
+ ok(!testItem("1|0", "cmd_NewItem0"), sna);
+ cmd_NewItem0.removeAttribute("disabled");
+ ok(testItem("1|0", "cmd_NewItem0"), sa);
+
+ // Remove menu.
+ menubarNode.removeChild(newMenu0);
+ ok(runBaseMenuTests(), sa);
+ ok(!testItem("1|0", ""), sna);
+ ok(!testItem("1|1", ""), sna);
+ ok(!testItem("1|2", ""), sna);
+ ok(!testItem("1|3|0", ""), sna);
+ ok(!testItem("1|3|1", ""), sna);
+ ok(!testItem("1|3|2", ""), sna);
+ // return state to original diagramed state
+ menubarNode.appendChild(newMenu0);
+
+ // Test for bug 447042, make sure that adding a menu node with no children
+ // to the menu bar and then adding another menu node with children works.
+ // Menus with no children don't get their native menu items shown and that
+ // caused internal arrays to get out of sync and an append crashed.
+ var tmpMenu0 = createXULMenu("tmpMenu0");
+ menubarNode.removeChild(newMenu0);
+ menubarNode.appendChild(tmpMenu0);
+ menubarNode.appendChild(newMenu0);
+ forceUpdateNativeMenuAt("1|3");
+ ok(runBaseMenuTests());
+ ok(testItem("1|0", "cmd_NewItem0"), sa);
+ ok(testItem("1|1", "cmd_NewItem1"), sa);
+ ok(testItem("1|2", "cmd_NewItem2"), sa);
+ ok(testItem("1|3|0", "cmd_NewItem3"), sa);
+ ok(testItem("1|3|1", "cmd_NewItem4"), sa);
+ ok(testItem("1|3|2", "cmd_NewItem5"), sa);
+ // return state to original diagramed state
+ menubarNode.removeChild(tmpMenu0);
+ delete tmpMenu0;
+
+ // This test is basically a crash test for bug 433858.
+ newMenuItem1.setAttribute("hidden", "true");
+ newMenuItem2.setAttribute("hidden", "true");
+ newMenu1.setAttribute("hidden", "true");
+ forceUpdateNativeMenuAt("1");
+ newMenuItem1.setAttribute("hidden", "false");
+ newMenuItem2.setAttribute("hidden", "false");
+ newMenu1.setAttribute("hidden", "false");
+ forceUpdateNativeMenuAt("1");
+
+ onTestsFinished();
+ }
+
+ setTimeout(_delayedOnLoad, 1000);
+ }
+
+ ]]></script>
+</window>
diff --git a/widget/tests/native_mouse_mac_window.xul b/widget/tests/native_mouse_mac_window.xul
new file mode 100644
index 000000000..8680c3b1a
--- /dev/null
+++ b/widget/tests/native_mouse_mac_window.xul
@@ -0,0 +1,773 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="NativeMenuWindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ width="600"
+ height="600"
+ title="Native Mouse Event Test"
+ orient="vertical">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <box height="200" id="box"/>
+ <menupopup id="popup" width="250" height="50"/>
+ <panel id="panel" width="250" height="50" noautohide="true"/>
+
+ <script type="application/javascript"><![CDATA[
+
+ function ok(condition, message) {
+ window.opener.wrappedJSObject.SimpleTest.ok(condition, message);
+ }
+
+ function is(a, b, message) {
+ window.opener.wrappedJSObject.SimpleTest.is(a, b, message);
+ }
+
+ function todo(condition, message) {
+ window.opener.wrappedJSObject.SimpleTest.todo(condition, message);
+ }
+
+ function todo_is(a, b, message) {
+ window.opener.wrappedJSObject.SimpleTest.todo_is(a, b, message);
+ }
+
+ function onTestsFinished() {
+ clearTimeout(gAfterLoopExecution);
+ observe(window, eventMonitor, false);
+ observe(gRightWindow, eventMonitor, false);
+ observe(gPopup, eventMonitor, false);
+ gRightWindow.close();
+ var openerSimpleTest = window.opener.wrappedJSObject.SimpleTest;
+ window.close();
+ openerSimpleTest.finish();
+ }
+
+ const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ const xulWin = 'data:application/vnd.mozilla.xul+xml,<?xml version="1.0"?><?xml-stylesheet href="chrome://global/skin" type="text/css"?><window xmlns="' + XUL_NS + '"/>';
+
+ const NSLeftMouseDown = 1,
+ NSLeftMouseUp = 2,
+ NSRightMouseDown = 3,
+ NSRightMouseUp = 4,
+ NSMouseMoved = 5,
+ NSLeftMouseDragged = 6,
+ NSRightMouseDragged = 7,
+ NSMouseEntered = 8,
+ NSMouseExited = 9,
+ NSKeyDown = 10,
+ NSKeyUp = 11,
+ NSFlagsChanged = 12,
+ NSAppKitDefined = 13,
+ NSSystemDefined = 14,
+ NSApplicationDefined = 15,
+ NSPeriodic = 16,
+ NSCursorUpdate = 17,
+ NSScrollWheel = 22,
+ NSTabletPoint = 23,
+ NSTabletProximity = 24,
+ NSOtherMouseDown = 25,
+ NSOtherMouseUp = 26,
+ NSOtherMouseDragged = 27,
+ NSEventTypeGesture = 29,
+ NSEventTypeMagnify = 30,
+ NSEventTypeSwipe = 31,
+ NSEventTypeRotate = 18,
+ NSEventTypeBeginGesture = 19,
+ NSEventTypeEndGesture = 20;
+
+ const NSAlphaShiftKeyMask = 1 << 16,
+ NSShiftKeyMask = 1 << 17,
+ NSControlKeyMask = 1 << 18,
+ NSAlternateKeyMask = 1 << 19,
+ NSCommandKeyMask = 1 << 20,
+ NSNumericPadKeyMask = 1 << 21,
+ NSHelpKeyMask = 1 << 22,
+ NSFunctionKeyMask = 1 << 23;
+
+ const gDebug = false;
+
+ function printDebug(msg) { if (gDebug) dump(msg); }
+
+ var gExpectedEvents = [];
+ var gRightWindow = null, gPopup = null;
+ var gCurrentMouseX = 0, gCurrentMouseY = 0;
+ var gAfterLoopExecution = 0;
+
+ function testMouse(x, y, msg, elem, win, exp, flags, callback) {
+ clearExpectedEvents();
+ var syntheticEvent = null;
+ exp.forEach(function (expEv) {
+ expEv.screenX = x;
+ expEv.screenY = y;
+ if (expEv.synthetic) {
+ is(syntheticEvent, null,
+ "Can't handle two synthetic events in a single testMouse call");
+ syntheticEvent = expEv;
+ }
+ gExpectedEvents.push(expEv);
+ });
+ printDebug("sending event: " + x + ", " + y + " (" + msg + ")\n");
+ gCurrentMouseX = x;
+ gCurrentMouseY = y;
+ var utils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIDOMWindowUtils);
+ var callbackFunc = function() {
+ clearExpectedEvents();
+ callback();
+ }
+ if (syntheticEvent) {
+ // Set up this listener before we sendNativeMouseEvent, just
+ // in case that synchronously calls us.
+ eventListenOnce(syntheticEvent.target, syntheticEvent.type,
+ // Trigger callbackFunc async, so we're not assuming
+ // anything about how our listener gets ordered with
+ // others.
+ function () { SimpleTest.executeSoon(callbackFunc) });
+ }
+ utils.sendNativeMouseEvent(x, y, msg, flags || 0, elem);
+ if (!syntheticEvent) {
+ gAfterLoopExecution = setTimeout(callbackFunc, 0);
+ }
+ }
+
+ function eventListenOnce(elem, name, callback) {
+ elem.addEventListener(name, function(e) {
+ elem.removeEventListener(name, arguments.callee, false);
+ callback(e);
+ }, false);
+ }
+
+ function focusAndThen(win, callback) {
+ eventListenOnce(win, "focus", callback);
+ printDebug("focusing a window\n");
+ win.focus();
+ }
+
+ function eventToString(e) {
+ return JSON.stringify({
+ type: e.type, target: e.target.nodeName, screenX: e.screenX, screenY: e.screenY
+ });
+ }
+
+ function clearExpectedEvents() {
+ while (gExpectedEvents.length > 0) {
+ var expectedEvent = gExpectedEvents.shift();
+ var errFun = expectedEvent.shouldFireButDoesnt ? todo : ok;
+ errFun(false, "Didn't receive expected event: " + eventToString(expectedEvent));
+ }
+ }
+
+ var gEventNum = 0;
+
+ function eventMonitor(e) {
+ printDebug("got event: " + eventToString(e) + "\n");
+ processEvent(e);
+ }
+
+ function processEvent(e) {
+ if (e.screenX != gCurrentMouseX || e.screenY != gCurrentMouseY) {
+ todo(false, "Oh no! Received a stray event from a confused tracking area. Aborting test.");
+ onTestsFinished();
+ return;
+ }
+ var expectedEvent = gExpectedEvents.shift();
+ if (!expectedEvent) {
+ ok(false, "received event I didn't expect: " + eventToString(e));
+ return;
+ }
+ if (e.type != expectedEvent.type) {
+ // Didn't get expectedEvent.
+ var errFun = expectedEvent.shouldFireButDoesnt ? todo : ok;
+ errFun(false, "Didn't receive expected event: " + eventToString(expectedEvent));
+ return processEvent(e);
+ }
+ gEventNum++;
+ is(e.screenX, expectedEvent.screenX, gEventNum + " | wrong X coord for event " + eventToString(e));
+ is(e.screenY, expectedEvent.screenY, gEventNum + " | wrong Y coord for event " + eventToString(e));
+ is(e.target, expectedEvent.target, gEventNum + " | wrong target for event " + eventToString(e));
+ if (expectedEvent.firesButShouldnt) {
+ todo(false, gEventNum + " | Got an event that should not have fired: " + eventToString(e));
+ }
+ }
+
+ function observe(elem, fun, add) {
+ var addOrRemove = add ? "addEventListener" : "removeEventListener";
+ elem[addOrRemove]("mousemove", fun, false);
+ elem[addOrRemove]("mouseover", fun, false);
+ elem[addOrRemove]("mouseout", fun, false);
+ elem[addOrRemove]("mousedown", fun, false);
+ elem[addOrRemove]("mouseup", fun, false);
+ elem[addOrRemove]("click", fun, false);
+ }
+
+ function start() {
+ window.resizeTo(200, 200);
+ window.moveTo(50, 50);
+ gRightWindow = open(xulWin, '', 'chrome,screenX=300,screenY=50,width=200,height=200');
+ eventListenOnce(gRightWindow, "focus", function () {
+ focusAndThen(window, runTests);
+ });
+ gPopup = document.getElementById("popup");
+ }
+
+ function runTests() {
+ observe(window, eventMonitor, true);
+ observe(gRightWindow, eventMonitor, true);
+ var left = window, right = gRightWindow;
+ var leftElem = document.getElementById("box");
+ var rightElem = gRightWindow.document.documentElement;
+ var panel = document.getElementById("panel");
+ var tooltip = (function createTooltipInRightWindow() {
+ var _tooltip = right.document.createElementNS(XUL_NS, "tooltip");
+ _tooltip.setAttribute("id", "tip");
+ _tooltip.setAttribute("width", "80");
+ _tooltip.setAttribute("height", "20");
+ right.document.documentElement.appendChild(_tooltip);
+ return _tooltip;
+ })();
+ var tests = [
+
+ // Part 1: Disallow click-through
+
+ function blockClickThrough(callback) {
+ document.documentElement.setAttribute("clickthrough", "never");
+ gRightWindow.document.documentElement.setAttribute("clickthrough", "never");
+ callback();
+ },
+ // Enter the left window, which is focused.
+ [150, 150, NSMouseMoved, null, left, [
+ { type: "mouseover", target: leftElem },
+ { type: "mousemove", target: leftElem }
+ ]],
+ // Test that moving inside the window fires mousemove events.
+ [170, 150, NSMouseMoved, null, left, [
+ { type: "mousemove", target: leftElem },
+ ]],
+ // Leaving the window should fire a mouseout event...
+ [170, 20, NSMouseMoved, null, left, [
+ { type: "mouseout", target: leftElem },
+ ]],
+ // ... and entering a mouseover event.
+ [170, 120, NSMouseMoved, null, left, [
+ { type: "mouseover", target: leftElem },
+ { type: "mousemove", target: leftElem },
+ ]],
+ // Move over the right window, which is inactive.
+ // Inactive windows shouldn't respond to mousemove events when clickthrough="never",
+ // so we should only get a mouseout event, no mouseover event.
+ [400, 150, NSMouseMoved, null, right, [
+ { type: "mouseout", target: leftElem },
+ ]],
+ // Left-clicking while holding Cmd and middle clicking should work even
+ // on inactive windows, but without making them active.
+ [400, 150, NSLeftMouseDown, null, right, [
+ { type: "mousedown", target: rightElem },
+ ], NSCommandKeyMask],
+ [400, 150, NSLeftMouseUp, null, right, [
+ { type: "mouseup", target: rightElem },
+ { type: "click", target: rightElem },
+ ], NSCommandKeyMask],
+ [400, 150, NSOtherMouseDown, null, right, [
+ { type: "mousedown", target: rightElem },
+ ]],
+ [400, 150, NSOtherMouseUp, null, right, [
+ { type: "mouseup", target: rightElem },
+ { type: "click", target: rightElem },
+ ]],
+ // Clicking an inactive window should make it active and fire a mouseover
+ // event.
+ [400, 150, NSLeftMouseDown, null, right, [
+ { type: "mouseover", target: rightElem, synthetic: true },
+ ]],
+ [400, 150, NSLeftMouseUp, null, right, [
+ ]],
+ // Now it's focused, so we should get a mousedown event when clicking.
+ [400, 150, NSLeftMouseDown, null, right, [
+ { type: "mousedown", target: rightElem },
+ ]],
+ // Let's drag to the right without letting the button go.
+ [410, 150, NSLeftMouseDragged, null, right, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ // Let go of the mouse.
+ [410, 150, NSLeftMouseUp, null, right, [
+ { type: "mouseup", target: rightElem },
+ { type: "click", target: rightElem },
+ ]],
+ // Move the mouse back over the left window, which is inactive.
+ [150, 170, NSMouseMoved, null, left, [
+ { type: "mouseout", target: rightElem },
+ ]],
+ // Now we're being sneaky. The left window is inactive, but *right*-clicks to it
+ // should still get through. Test that.
+ // Ideally we'd be bracketing that event with over and out events, too, but it
+ // probably doesn't matter too much.
+ [150, 170, NSRightMouseDown, null, left, [
+ { type: "mouseover", target: leftElem, shouldFireButDoesnt: true },
+ { type: "mousedown", target: leftElem },
+ { type: "mouseout", target: leftElem, shouldFireButDoesnt: true },
+ ]],
+ // Let go of the mouse.
+ [150, 170, NSRightMouseUp, null, left, [
+ { type: "mouseover", target: leftElem, shouldFireButDoesnt: true },
+ { type: "mouseup", target: leftElem },
+ { type: "click", target: leftElem },
+ { type: "mouseout", target: leftElem, shouldFireButDoesnt: true },
+ ]],
+ // Right clicking hasn't focused it, so the window is still inactive.
+ // Let's focus it; this time without the mouse, for variaton's sake.
+ // Still, mouseout and mouseover events should fire.
+ function raiseLeftWindow(callback) {
+ clearExpectedEvents();
+ gExpectedEvents.push({ screenX: 150, screenY: 170, type: "mouseover", target: leftElem });
+ // We have to be a bit careful here. The synthetic mouse event may
+ // not fire for a bit after we focus the left window.
+ eventListenOnce(leftElem, "mouseover", function() {
+ // Trigger callback async, so we're not assuming
+ // anything about how our listener gets ordered with others.
+ SimpleTest.executeSoon(callback);
+ });
+ printDebug("focusing left window");
+ left.focus();
+ },
+ // It's active, so it should respond to mousemove events now.
+ [150, 170, NSMouseMoved, null, left, [
+ { type: "mousemove", target: leftElem },
+ ]],
+ // This was boring... let's introduce a popup. It will overlap both the left
+ // and the right window.
+ function openPopupInLeftWindow(callback) {
+ eventListenOnce(gPopup, "popupshown", callback);
+ gPopup.openPopupAtScreen(150, 50, true);
+ },
+ // Move the mouse over the popup.
+ [200, 80, NSMouseMoved, gPopup, left, [
+ { type: "mouseout", target: leftElem },
+ { type: "mouseover", target: gPopup },
+ { type: "mousemove", target: gPopup },
+ ]],
+ // Move the mouse back over the left window outside the popup.
+ [160, 170, NSMouseMoved, null, left, [
+ { type: "mouseout", target: gPopup },
+ { type: "mouseover", target: leftElem },
+ { type: "mousemove", target: leftElem },
+ ]],
+ // Back over the popup...
+ [190, 80, NSMouseMoved, gPopup, left, [
+ { type: "mouseout", target: leftElem },
+ { type: "mouseover", target: gPopup },
+ { type: "mousemove", target: gPopup },
+ ]],
+ // ...and over into the right window.
+ // It's inactive, so it shouldn't get mouseover events yet.
+ [400, 170, NSMouseMoved, null, right, [
+ { type: "mouseout", target: gPopup },
+ ]],
+ // Again, no mouse events please, even though a popup is open. (bug 425556)
+ [400, 180, NSMouseMoved, null, right, [
+ ]],
+ // Activate the right window with a click.
+ // This will close the popup and make the mouse enter the right window.
+ [400, 180, NSLeftMouseDown, null, right, [
+ { type: "mouseover", target: rightElem, synthetic: true },
+ ]],
+ [400, 180, NSLeftMouseUp, null, right, [
+ ]],
+ function verifyPopupClosed2(callback) {
+ is(gPopup.popupBoxObject.popupState, "closed", "popup should have closed when clicking");
+ callback();
+ },
+ // Now the right window is active; click it again, just for fun.
+ [400, 180, NSLeftMouseDown, null, right, [
+ { type: "mousedown", target: rightElem },
+ ]],
+ [400, 180, NSLeftMouseUp, null, right, [
+ { type: "mouseup", target: rightElem },
+ { type: "click", target: rightElem },
+ ]],
+
+ // Time for our next trick: a tooltip!
+ // Install the tooltip, but don't show it yet.
+ function setTooltip(callback) {
+ rightElem.setAttribute("tooltip", "tip");
+ gExpectedEvents.push({ screenX: 410, screenY: 180, type: "mousemove", target: rightElem });
+ eventListenOnce(rightElem, "popupshown", callback);
+ gCurrentMouseX = 410;
+ gCurrentMouseY = 180;
+ var utils = right.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIDOMWindowUtils);
+ utils.sendNativeMouseEvent(410, 180, NSMouseMoved, 0, null);
+ },
+ // Now the tooltip is visible.
+ // Move the mouse a little to the right.
+ [411, 180, NSMouseMoved, null, right, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ // Move another pixel.
+ [412, 180, NSMouseMoved, null, right, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ // Move up and click to make the tooltip go away.
+ [412, 80, NSMouseMoved, null, right, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ [412, 80, NSLeftMouseDown, null, right, [
+ { type: "mousedown", target: rightElem },
+ ]],
+ [412, 80, NSLeftMouseUp, null, right, [
+ { type: "mouseup", target: rightElem },
+ { type: "click", target: rightElem },
+ ]],
+ // OK, next round. Open a panel in the left window, which is inactive.
+ function openPanel(callback) {
+ eventListenOnce(panel, "popupshown", callback);
+ panel.openPopupAtScreen(150, 150, false);
+ },
+ // The panel is parented, so it will be z-ordered over its parent but
+ // under the active window.
+ // Now we move the mouse over the part where the panel rect intersects the
+ // right window's rect. Since the panel is under the window, all the events
+ // should target the right window.
+ [390, 170, NSMouseMoved, null, right, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ [390, 171, NSMouseMoved, null, right, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ [391, 171, NSMouseMoved, null, right, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ // Now move off the right window, so that the mouse is directly over the
+ // panel.
+ [260, 170, NSMouseMoved, panel, left, [
+ { type: "mouseout", target: rightElem },
+ ]],
+ [260, 171, NSMouseMoved, panel, left, [
+ ]],
+ [261, 171, NSMouseMoved, panel, left, [
+ ]],
+ // Let's be evil and click it.
+ [261, 171, NSLeftMouseDown, panel, left, [
+ ]],
+ [261, 171, NSLeftMouseUp, panel, left, [
+ ]],
+ // This didn't focus the window, unfortunately, so let's do it ourselves.
+ function raiseLeftWindowTakeTwo(callback) {
+ focusAndThen(left, callback);
+ },
+ // Now mouse events should get through to the panel (which is now over the
+ // right window).
+ [387, 170, NSMouseMoved, panel, left, [
+ { type: "mouseover", target: panel },
+ { type: "mousemove", target: panel },
+ ]],
+ [387, 171, NSMouseMoved, panel, left, [
+ { type: "mousemove", target: panel },
+ ]],
+ [388, 171, NSMouseMoved, panel, left, [
+ { type: "mousemove", target: panel },
+ ]],
+ // Click the panel.
+ [388, 171, NSLeftMouseDown, panel, left, [
+ { type: "mousedown", target: panel }
+ ]],
+ [388, 171, NSLeftMouseUp, panel, left, [
+ { type: "mouseup", target: panel },
+ { type: "click", target: panel },
+ ]],
+
+ // Last test for this part: Hit testing in the Canyon of Nowhere -
+ // the pixel row directly south of the panel, over the left window.
+ // Before bug 515003 we wrongly thought the mouse wasn't over any window.
+ [173, 200, NSMouseMoved, null, left, [
+ { type: "mouseout", target: panel },
+ { type: "mouseover", target: leftElem },
+ { type: "mousemove", target: leftElem },
+ ]],
+ [173, 201, NSMouseMoved, null, left, [
+ { type: "mousemove", target: leftElem },
+ ]],
+
+ // Part 2: Allow click-through
+
+ function hideThatPanel(callback) {
+ eventListenOnce(panel, "popuphidden", callback);
+ panel.hidePopup();
+ },
+ function unblockClickThrough(callback) {
+ document.documentElement.removeAttribute("clickthrough");
+ gRightWindow.document.documentElement.removeAttribute("clickthrough");
+ callback();
+ },
+ // Enter the left window, which is focused.
+ [150, 150, NSMouseMoved, null, left, [
+ { type: "mousemove", target: leftElem }
+ ]],
+ // Test that moving inside the window fires mousemove events.
+ [170, 150, NSMouseMoved, null, left, [
+ { type: "mousemove", target: leftElem },
+ ]],
+ // Leaving the window should fire a mouseout event...
+ [170, 20, NSMouseMoved, null, left, [
+ { type: "mouseout", target: leftElem },
+ ]],
+ // ... and entering a mouseover event.
+ [170, 120, NSMouseMoved, null, left, [
+ { type: "mouseover", target: leftElem },
+ { type: "mousemove", target: leftElem },
+ ]],
+ // Move over the right window, which is inactive but still accepts
+ // mouse events.
+ [400, 150, NSMouseMoved, null, right, [
+ { type: "mouseout", target: leftElem },
+ { type: "mouseover", target: rightElem },
+ { type: "mousemove", target: rightElem },
+ ]],
+ // Left-clicking while holding Cmd and middle clicking should work
+ // on inactive windows, but without making them active.
+ [400, 150, NSLeftMouseDown, null, right, [
+ { type: "mousedown", target: rightElem },
+ ], NSCommandKeyMask],
+ [400, 150, NSLeftMouseUp, null, right, [
+ { type: "mouseup", target: rightElem },
+ { type: "click", target: rightElem },
+ ], NSCommandKeyMask],
+ [400, 150, NSOtherMouseDown, null, right, [
+ { type: "mousedown", target: rightElem },
+ ]],
+ [400, 150, NSOtherMouseUp, null, right, [
+ { type: "mouseup", target: rightElem },
+ { type: "click", target: rightElem },
+ ]],
+ // Clicking an inactive window should make it active
+ [400, 150, NSLeftMouseDown, null, right, [
+ { type: "mousedown", target: rightElem },
+ ]],
+ [400, 150, NSLeftMouseUp, null, right, [
+ { type: "mouseup", target: rightElem },
+ { type: "click", target: rightElem },
+ ]],
+ // Now it's focused.
+ [401, 150, NSLeftMouseDown, null, right, [
+ { type: "mousedown", target: rightElem },
+ ]],
+ // Let's drag to the right without letting the button go.
+ [410, 150, NSLeftMouseDragged, null, right, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ // Let go of the mouse.
+ [410, 150, NSLeftMouseUp, null, right, [
+ { type: "mouseup", target: rightElem },
+ { type: "click", target: rightElem },
+ ]],
+ // Move the mouse back over the left window, which is inactive.
+ [150, 170, NSMouseMoved, null, left, [
+ { type: "mouseout", target: rightElem },
+ { type: "mouseover", target: leftElem },
+ { type: "mousemove", target: leftElem },
+ ]],
+ // Right-click it.
+ [150, 170, NSRightMouseDown, null, left, [
+ { type: "mousedown", target: leftElem },
+ ]],
+ // Let go of the mouse.
+ [150, 170, NSRightMouseUp, null, left, [
+ { type: "mouseup", target: leftElem },
+ { type: "click", target: leftElem },
+ ]],
+ // Right clicking hasn't focused it, so the window is still inactive.
+ // Let's focus it; this time without the mouse, for variaton's sake.
+ function raiseLeftWindow(callback) {
+ clearExpectedEvents();
+ focusAndThen(left, function () { SimpleTest.executeSoon(callback); });
+ },
+ // It's active and should still respond to mousemove events.
+ [150, 170, NSMouseMoved, null, left, [
+ { type: "mousemove", target: leftElem },
+ ]],
+
+ // This was boring... let's introduce a popup. It will overlap both the left
+ // and the right window.
+ function openPopupInLeftWindow(callback) {
+ eventListenOnce(gPopup, "popupshown", callback);
+ gPopup.openPopupAtScreen(150, 50, true);
+ },
+ // Move the mouse over the popup.
+ [200, 80, NSMouseMoved, gPopup, left, [
+ { type: "mouseout", target: leftElem },
+ { type: "mouseover", target: gPopup },
+ { type: "mousemove", target: gPopup },
+ ]],
+ // Move the mouse back over the left window outside the popup.
+ [160, 170, NSMouseMoved, null, left, [
+ { type: "mouseout", target: gPopup },
+ { type: "mouseover", target: leftElem },
+ { type: "mousemove", target: leftElem },
+ ]],
+ // Back over the popup...
+ [190, 80, NSMouseMoved, gPopup, left, [
+ { type: "mouseout", target: leftElem },
+ { type: "mouseover", target: gPopup },
+ { type: "mousemove", target: gPopup },
+ ]],
+ // ...and over into the right window.
+ [400, 170, NSMouseMoved, null, right, [
+ { type: "mouseout", target: gPopup },
+ { type: "mouseover", target: rightElem },
+ { type: "mousemove", target: rightElem },
+ ]],
+ [400, 180, NSMouseMoved, null, right, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ // Activate the right window with a click.
+ [400, 180, NSLeftMouseDown, null, right, [
+ { type: "mousedown", target: rightElem },
+ ]],
+ [400, 180, NSLeftMouseUp, null, right, [
+ { type: "mouseup", target: rightElem },
+ { type: "click", target: rightElem },
+ ]],
+ function verifyPopupClosed2(callback) {
+ is(gPopup.popupBoxObject.popupState, "closed", "popup should have closed when clicking");
+ callback();
+ },
+ // Now the right window is active; click it again, just for fun.
+ [400, 180, NSLeftMouseDown, null, right, [
+ { type: "mousedown", target: rightElem },
+ ]],
+ [400, 180, NSLeftMouseUp, null, right, [
+ { type: "mouseup", target: rightElem },
+ { type: "click", target: rightElem },
+ ]],
+
+ // Time for our next trick: a tooltip!
+ // Install the tooltip, but don't show it yet.
+ function setTooltip2(callback) {
+ rightElem.setAttribute("tooltip", "tip");
+ gExpectedEvents.push({ screenX: 410, screenY: 180, type: "mousemove", target: rightElem });
+ eventListenOnce(rightElem, "popupshown", callback);
+ gCurrentMouseX = 410;
+ gCurrentMouseY = 180;
+ var utils = right.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIDOMWindowUtils);
+ utils.sendNativeMouseEvent(410, 180, NSMouseMoved, 0, null);
+ },
+ // Now the tooltip is visible.
+ // Move the mouse a little to the right.
+ [411, 180, NSMouseMoved, null, right, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ // Move another pixel.
+ [412, 180, NSMouseMoved, null, right, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ // Move up and click to make the tooltip go away.
+ [412, 80, NSMouseMoved, null, right, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ [412, 80, NSLeftMouseDown, null, right, [
+ { type: "mousedown", target: rightElem },
+ ]],
+ [412, 80, NSLeftMouseUp, null, right, [
+ { type: "mouseup", target: rightElem },
+ { type: "click", target: rightElem },
+ ]],
+ // OK, next round. Open a panel in the left window, which is inactive.
+ function openPanel2(callback) {
+ eventListenOnce(panel, "popupshown", callback);
+ panel.openPopupAtScreen(150, 150, false);
+ },
+ // The panel is parented, so it will be z-ordered over its parent but
+ // under the active window.
+ // Now we move the mouse over the part where the panel rect intersects the
+ // right window's rect. Since the panel is under the window, all the events
+ // should target the right window.
+ [390, 170, NSMouseMoved, null, right, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ [390, 171, NSMouseMoved, null, right, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ [391, 171, NSMouseMoved, null, right, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ // Now move off the right window, so that the mouse is directly over the
+ // panel.
+ [260, 170, NSMouseMoved, panel, left, [
+ { type: "mouseout", target: rightElem },
+ { type: "mouseover", target: panel },
+ { type: "mousemove", target: panel },
+ ]],
+ [260, 171, NSMouseMoved, panel, left, [
+ { type: "mousemove", target: panel },
+ ]],
+ [261, 171, NSMouseMoved, panel, left, [
+ { type: "mousemove", target: panel },
+ ]],
+ // Let's be evil and click it.
+ [261, 171, NSLeftMouseDown, panel, left, [
+ { type: "mousedown", target: panel },
+ ]],
+ [261, 171, NSLeftMouseUp, panel, left, [
+ { type: "mouseup", target: panel },
+ { type: "click", target: panel },
+ ]],
+ // This didn't focus the window, unfortunately, so let's do it ourselves.
+ function raiseLeftWindowTakeTwo(callback) {
+ focusAndThen(left, callback);
+ },
+ [387, 170, NSMouseMoved, panel, left, [
+ { type: "mousemove", target: panel },
+ ]],
+ [387, 171, NSMouseMoved, panel, left, [
+ { type: "mousemove", target: panel },
+ ]],
+ [388, 171, NSMouseMoved, panel, left, [
+ { type: "mousemove", target: panel },
+ ]],
+ // Click the panel.
+ [388, 171, NSLeftMouseDown, panel, left, [
+ { type: "mousedown", target: panel }
+ ]],
+ [388, 171, NSLeftMouseUp, panel, left, [
+ { type: "mouseup", target: panel },
+ { type: "click", target: panel },
+ ]],
+
+ // Last test for today: Hit testing in the Canyon of Nowhere -
+ // the pixel row directly south of the panel, over the left window.
+ // Before bug 515003 we wrongly thought the mouse wasn't over any window.
+ [173, 200, NSMouseMoved, null, left, [
+ { type: "mouseout", target: panel },
+ { type: "mouseover", target: leftElem },
+ { type: "mousemove", target: leftElem },
+ ]],
+ [173, 201, NSMouseMoved, null, left, [
+ { type: "mousemove", target: leftElem },
+ ]],
+ ];
+ function runNextTest() {
+ if (!tests.length)
+ return onTestsFinished();
+
+ var test = tests.shift();
+ if (typeof test == "function")
+ return test(runNextTest);
+
+ var [x, y, msg, elem, win, exp, flags] = test;
+ testMouse(x, y, msg, elem, win, exp, flags, runNextTest);
+ }
+ runNextTest();
+ }
+
+ SimpleTest.waitForFocus(start);
+
+ ]]></script>
+</window>
diff --git a/widget/tests/standalone_native_menu_window.xul b/widget/tests/standalone_native_menu_window.xul
new file mode 100644
index 000000000..6783a66e6
--- /dev/null
+++ b/widget/tests/standalone_native_menu_window.xul
@@ -0,0 +1,334 @@
+<?xml version="1.0"?>
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="StandaloneNativeMenuWindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="300"
+ height="300"
+ onload="onLoad();"
+ title="nsIStandaloneNativeMenu Test">
+
+ <command id="cmd_FooItem0" oncommand="executedCommandID = 'cmd_FooItem0';"/>
+ <command id="cmd_FooItem1" oncommand="executedCommandID = 'cmd_FooItem1';"/>
+ <command id="cmd_BarItem0" oncommand="executedCommandID = 'cmd_BarItem0';"/>
+ <command id="cmd_BarItem1" oncommand="executedCommandID = 'cmd_BarItem1';"/>
+ <command id="cmd_NewItem0" oncommand="executedCommandID = 'cmd_NewItem0';"/>
+ <command id="cmd_NewItem1" oncommand="executedCommandID = 'cmd_NewItem1';"/>
+ <command id="cmd_NewItem2" oncommand="executedCommandID = 'cmd_NewItem2';"/>
+ <command id="cmd_NewItem3" oncommand="executedCommandID = 'cmd_NewItem3';"/>
+ <command id="cmd_NewItem4" oncommand="executedCommandID = 'cmd_NewItem4';"/>
+ <command id="cmd_NewItem5" oncommand="executedCommandID = 'cmd_NewItem5';"/>
+
+ <!-- We do not modify any menus or menu items defined here in testing. These
+ serve as a baseline structure for us to test after other modifications.
+ We add children to the menubar defined here and test by modifying those
+ children. -->
+ <popupset>
+ <menupopup id="standalonenativemenu">
+ <menu id="foo" label="Foo">
+ <menupopup>
+ <menuitem label="FooItem0" command="cmd_FooItem0"/>
+ <menuitem label="FooItem1" command="cmd_FooItem1"/>
+ <menuseparator/>
+ <menu label="Bar">
+ <menupopup>
+ <menuitem label="BarItem0" command="cmd_BarItem0"/>
+ <menuitem label="BarItem1" command="cmd_BarItem1"/>
+ </menupopup>
+ </menu>
+ </menupopup>
+ </menu>
+ </menupopup>
+ </popupset>
+
+ <script type="application/javascript"><![CDATA[
+
+ function ok(condition, message) {
+ window.opener.wrappedJSObject.SimpleTest.ok(condition, message);
+ }
+
+ function todo(condition, message) {
+ window.opener.wrappedJSObject.SimpleTest.todo(condition, message);
+ }
+
+ function onTestsFinished() {
+ window.close();
+ window.opener.wrappedJSObject.SimpleTest.finish();
+ }
+
+ var executedCommandID = "";
+
+ function testItem(menu, location, targetID) {
+ var correctCommandHandler = false;
+ try {
+ menu.menuWillOpen();
+ menu.activateNativeMenuItemAt(location);
+ correctCommandHandler = executedCommandID == targetID;
+ }
+ catch (e) {
+ dump(e + "\n");
+ }
+ finally {
+ executedCommandID = "";
+ return correctCommandHandler;
+ }
+ }
+
+ var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+ function createXULMenuPopup() {
+ return document.createElementNS(XUL_NS, "menupopup");
+ }
+
+ function createXULMenu(aLabel) {
+ var item = document.createElementNS(XUL_NS, "menu");
+ item.setAttribute("label", aLabel);
+ return item;
+ }
+
+ function createXULMenuItem(aLabel, aCommandId) {
+ var item = document.createElementNS(XUL_NS, "menuitem");
+ item.setAttribute("label", aLabel);
+ item.setAttribute("command", aCommandId);
+ return item;
+ }
+
+ function runBaseMenuTests(menu) {
+ menu.forceUpdateNativeMenuAt("0|3");
+ return testItem(menu, "0|0", "cmd_FooItem0") &&
+ testItem(menu, "0|1", "cmd_FooItem1") &&
+ testItem(menu, "0|3|0", "cmd_BarItem0") &&
+ testItem(menu, "0|3|1", "cmd_BarItem1");
+ }
+
+ function createStandaloneNativeMenu(menuNode) {
+ try {
+ let Cc = Components.classes;
+ let Ci = Components.interfaces;
+ let menu = Cc["@mozilla.org/widget/standalonenativemenu;1"].createInstance(Ci.nsIStandaloneNativeMenu);
+ menu.init(menuNode);
+ return menu;
+ } catch (e) {
+ ok(false, "Failed creating nsIStandaloneNativeMenu instance");
+ throw e;
+ }
+ }
+
+ function runDetachedMenuTests(addMenupopupBeforeCreatingSNM) {
+ let menu = createXULMenu("Detached menu");
+ menu.setAttribute("image", 'data:image/svg+xml,<svg%20xmlns="http://www.w3.org/2000/svg"%20width="32"%20height="32"><circle%20cx="16"%20cy="16"%20r="16"/></svg>');
+ let menupopup = createXULMenuPopup();
+
+ let popupShowingFired = false;
+ let itemActivated = false;
+
+ menupopup.addEventListener("popupshowing", function (e) {
+ popupShowingFired = true;
+
+ let menuitem = document.createElementNS(XUL_NS, "menuitem");
+ menuitem.setAttribute("label", "detached menu item");
+ menuitem.addEventListener("command", function (e) {
+ itemActivated = true;
+ })
+ menupopup.appendChild(menuitem);
+ })
+
+ // It shouldn't make a difference whether the menupopup is added to the
+ // menu element before or after we create the nsIStandaloneNativeMenu
+ // instance with it. We test both orders by calling this function twice
+ // with different values for addMenupopupBeforeCreatingSNM.
+
+ var menuSNM = null; // the nsIStandaloneNativeMenu object for "menu"
+ if (addMenupopupBeforeCreatingSNM) {
+ menu.appendChild(menupopup);
+ menuSNM = createStandaloneNativeMenu(menu);
+ } else {
+ menuSNM = createStandaloneNativeMenu(menu);
+ menu.appendChild(menupopup);
+ }
+
+ try {
+ ok(!popupShowingFired, "popupshowing shouldn't have fired before our call to menuWillOpen()");
+ menuSNM.menuWillOpen();
+ ok(popupShowingFired, "calling menuWillOpen() should have notified our popupshowing listener");
+
+ ok(!itemActivated, "our dynamically-added menuitem shouldn't have been activated yet");
+ menuSNM.activateNativeMenuItemAt("0");
+ ok(itemActivated, "the new menu item should have been activated now");
+ } catch (ex) {
+ ok(false, "dynamic menu test failed: " + ex);
+ }
+ }
+
+ function onLoad() {
+ var _delayedOnLoad = function() {
+ try {
+
+ var menuNode = document.getElementById("standalonenativemenu");
+ var menu = createStandaloneNativeMenu(menuNode);
+
+ // First let's run the base menu tests.
+ ok(runBaseMenuTests(menu), "base tests #1");
+
+ // Set up some nodes that we'll use.
+ var newMenu0 = createXULMenu("NewMenu0");
+ var newMenu1 = createXULMenu("NewMenu1");
+ var newMenuPopup0 = createXULMenuPopup();
+ var newMenuPopup1 = createXULMenuPopup();
+ var newMenuItem0 = createXULMenuItem("NewMenuItem0", "cmd_NewItem0");
+ var newMenuItem1 = createXULMenuItem("NewMenuItem1", "cmd_NewItem1");
+ var newMenuItem2 = createXULMenuItem("NewMenuItem2", "cmd_NewItem2");
+ var newMenuItem3 = createXULMenuItem("NewMenuItem3", "cmd_NewItem3");
+ var newMenuItem4 = createXULMenuItem("NewMenuItem4", "cmd_NewItem4");
+ var newMenuItem5 = createXULMenuItem("NewMenuItem5", "cmd_NewItem5");
+
+ // Create another submenu with hierarchy via DOM manipulation.
+ // ******************
+ // * Foo * NewMenu0 * <- Menu bar
+ // ******************
+ // ****************
+ // * NewMenuItem0 * <- NewMenu0 submenu
+ // ****************
+ // * NewMenuItem1 *
+ // ****************
+ // * NewMenuItem2 *
+ // *******************************
+ // * NewMenu1 > * NewMenuItem3 * <- NewMenu1 submenu
+ // *******************************
+ // * NewMenuItem4 *
+ // ****************
+ // * NewMenuItem5 *
+ // ****************
+ newMenu0.appendChild(newMenuPopup0);
+ newMenuPopup0.appendChild(newMenuItem0);
+ newMenuPopup0.appendChild(newMenuItem1);
+ newMenuPopup0.appendChild(newMenuItem2);
+ newMenuPopup0.appendChild(newMenu1);
+ newMenu1.appendChild(newMenuPopup1);
+ newMenuPopup1.appendChild(newMenuItem3);
+ newMenuPopup1.appendChild(newMenuItem4);
+ newMenuPopup1.appendChild(newMenuItem5);
+ //XXX - we have to append the menu to the top-level of the menu bar
+ // only after constructing it. If we append before construction, it is
+ // invalid because it has no children and we don't validate it if we add
+ // children later.
+ menuNode.appendChild(newMenu0);
+ menu.forceUpdateNativeMenuAt("1|3");
+ // Run basic tests again.
+ ok(runBaseMenuTests(menu), "base tests #2");
+
+ // Error strings.
+ var sa = "Command handler(s) should have activated";
+ var sna = "Command handler(s) should not have activated";
+
+ // Test middle items.
+ ok(testItem(menu, "1|1", "cmd_NewItem1"), "#1:" + sa);
+ ok(testItem(menu, "1|3|1", "cmd_NewItem4"), "#2:" + sa);
+
+ // Hide newMenu0.
+ newMenu0.setAttribute("hidden", "true");
+ ok(runBaseMenuTests(menu), "base tests #3: " + sa); // the base menu should still be unhidden
+ ok(!testItem(menu, "1|0", ""), "#3:" + sna);
+ ok(!testItem(menu, "1|1", ""), "#4:" + sna);
+ ok(!testItem(menu, "1|2", ""), "#5:" + sna);
+ ok(!testItem(menu, "1|3|0", ""), "#6:" + sna);
+ ok(!testItem(menu, "1|3|1", ""), "#7:" + sna);
+ ok(!testItem(menu, "1|3|2", ""), "#8:" + sna);
+
+ // Show newMenu0.
+ newMenu0.setAttribute("hidden", "false");
+ menu.forceUpdateNativeMenuAt("1|3");
+ ok(runBaseMenuTests(menu), "base tests #4:" + sa);
+ ok(testItem(menu, "1|0", "cmd_NewItem0"), "#9:" + sa);
+ ok(testItem(menu, "1|1", "cmd_NewItem1"), "#10:" + sa);
+ ok(testItem(menu, "1|2", "cmd_NewItem2"), "#11:" + sa);
+ ok(testItem(menu, "1|3|0", "cmd_NewItem3"), "#12:" + sa);
+ ok(testItem(menu, "1|3|1", "cmd_NewItem4"), "#13:" + sa);
+ ok(testItem(menu, "1|3|2", "cmd_NewItem5"), "#14:" + sa);
+
+ // Hide items.
+ newMenuItem1.setAttribute("hidden", "true");
+ newMenuItem4.setAttribute("hidden", "true");
+ menu.forceUpdateNativeMenuAt("1|2");
+ ok(runBaseMenuTests(menu), "base tests #5:" + sa);
+ ok(testItem(menu, "1|0", "cmd_NewItem0"), "#15:" + sa);
+ ok(testItem(menu, "1|1", "cmd_NewItem2"), "#16:" + sa);
+ ok(!testItem(menu, "1|2", ""), "#17:" + sna);
+ ok(testItem(menu, "1|2|0", "cmd_NewItem3"), "#18:" + sa);
+ ok(testItem(menu, "1|2|1", "cmd_NewItem5"), "#19:" + sa);
+ ok(!testItem(menu, "1|2|2", ""), "#20:" + sna);
+
+ // Show items.
+ newMenuItem1.setAttribute("hidden", "false");
+ newMenuItem4.setAttribute("hidden", "false");
+ //forceUpdateNativeMenuAt("1|3");
+ ok(runBaseMenuTests(menu), "base tests #6:" + sa);
+ ok(testItem(menu, "1|0", "cmd_NewItem0"), "#21:" + sa);
+ ok(testItem(menu, "1|1", "cmd_NewItem1"), "#22:" + sa);
+ ok(testItem(menu, "1|2", "cmd_NewItem2"), "#23:" + sa);
+ ok(testItem(menu, "1|3|0", "cmd_NewItem3"), "#24:" + sa);
+ ok(testItem(menu, "1|3|1", "cmd_NewItem4"), "#25:" + sa);
+ ok(testItem(menu, "1|3|2", "cmd_NewItem5"), "#26:" + sa);
+
+ // At this point in the tests the state of the menus has been returned
+ // to the originally diagramed state.
+
+ // Remove menu.
+ menuNode.removeChild(newMenu0);
+ ok(runBaseMenuTests(menu), "base tests #7:" + sa);
+ ok(!testItem(menu, "1|0", ""), "#27:" + sna);
+ ok(!testItem(menu, "1|1", ""), "#28:" + sna);
+ ok(!testItem(menu, "1|2", ""), "#29:" + sna);
+ ok(!testItem(menu, "1|3|0", ""), "#30:" + sna);
+ ok(!testItem(menu, "1|3|1", ""), "#31:" + sna);
+ ok(!testItem(menu, "1|3|2", ""), "#32:" + sna);
+ // return state to original diagramed state
+ menuNode.appendChild(newMenu0);
+
+ // Test for bug 447042, make sure that adding a menu node with no children
+ // to the menu bar and then adding another menu node with children works.
+ // Menus with no children don't get their native menu items shown and that
+ // caused internal arrays to get out of sync and an append crashed.
+ var tmpMenu0 = createXULMenu("tmpMenu0");
+ menuNode.removeChild(newMenu0);
+ menuNode.appendChild(tmpMenu0);
+ menuNode.appendChild(newMenu0);
+ menu.forceUpdateNativeMenuAt("1|3");
+ //todo(runBaseMenuTests(menu), "base tests #8");
+ todo(testItem(menu, "1|0", "cmd_NewItem0"), "#33:" +sa);
+ todo(testItem(menu, "1|1", "cmd_NewItem1"), "#34:" +sa);
+ todo(testItem(menu, "1|2", "cmd_NewItem2"), "#35:" +sa);
+ todo(testItem(menu, "1|3|0", "cmd_NewItem3"), "#36:" +sa);
+ todo(testItem(menu, "1|3|1", "cmd_NewItem4"), "#37:" +sa);
+ todo(testItem(menu, "1|3|2", "cmd_NewItem5"), "#38:" +sa);
+ // return state to original diagramed state
+ menuNode.removeChild(tmpMenu0);
+ delete tmpMenu0;
+
+ // This test is basically a crash test for bug 433858.
+ newMenuItem1.setAttribute("hidden", "true");
+ newMenuItem2.setAttribute("hidden", "true");
+ newMenu1.setAttribute("hidden", "true");
+ menu.forceUpdateNativeMenuAt("1");
+ newMenuItem1.setAttribute("hidden", "false");
+ newMenuItem2.setAttribute("hidden", "false");
+ newMenu1.setAttribute("hidden", "false");
+ menu.forceUpdateNativeMenuAt("1");
+
+ // Run tests where the menu nodes are not in the document's node tree.
+ runDetachedMenuTests(false);
+ runDetachedMenuTests(true);
+
+ } catch (e) {
+ ok(false, "Caught an exception: " + e);
+ } finally {
+ onTestsFinished();
+ }
+ }
+
+ setTimeout(_delayedOnLoad, 1000);
+ }
+
+ ]]></script>
+</window>
diff --git a/widget/tests/taskbar_previews.xul b/widget/tests/taskbar_previews.xul
new file mode 100644
index 000000000..2f294d187
--- /dev/null
+++ b/widget/tests/taskbar_previews.xul
@@ -0,0 +1,127 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Taskbar Previews Test"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="loaded();">
+
+ <title>Previews - yeah!</title>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script class="testbody" type="application/javascript">
+ <![CDATA[
+ let Cc = Components.classes;
+ let Ci = Components.interfaces;
+ let Cu = Components.utils;
+ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+ let taskbar = Cc["@mozilla.org/windows-taskbar;1"].getService(Ci.nsIWinTaskbar);
+
+ function IsWin7OrHigher() {
+ try {
+ var sysInfo = Cc["@mozilla.org/system-info;1"].
+ getService(Ci.nsIPropertyBag2);
+ var ver = parseFloat(sysInfo.getProperty("version"));
+ if (ver >= 6.1)
+ return true;
+ } catch (ex) { }
+ return false;
+ }
+ isnot(taskbar, null, "Taskbar service is defined");
+ is(taskbar.available, IsWin7OrHigher(), "Expected availability of taskbar");
+
+ SimpleTest.waitForExplicitFinish();
+
+ function stdPreviewSuite(p) {
+ p.visible = !p.visible;
+ p.visible = !p.visible;
+ p.visible = true;
+ p.invalidate();
+ p.visible = false;
+ }
+
+ function loaded()
+ {
+ if (!taskbar.available)
+ SimpleTest.finish();
+ let controller = {
+ width: 400,
+ height: 400,
+ thumbnailAspectRatio: 1.0,
+ drawThumbnail: function () { return false; },
+ drawPreview: function () { return false; },
+ get wrappedJSObject() { return this; }
+ }
+ // HACK from mconnor:
+ var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
+ let win = wm.getMostRecentWindow("navigator:browser");
+ let docShell = win.gBrowser.docShell;
+
+ let winPreview = taskbar.getTaskbarWindowPreview(docShell);
+ isnot(winPreview, null, "Window preview is not null");
+ winPreview.controller = controller;
+ let button = winPreview.getButton(0);
+ isnot(button, null, "Could get button at valid index");
+ try {
+ winPreview.getButton(-1);
+ ok(false, "Got button at negative index");
+ } catch (ex) {}
+ try {
+ winPreview.getButton(Ci.nsITaskbarWindowPreview.NUM_TOOLBAR_BUTTONS);
+ ok(false, "Got button at index that is too large");
+ } catch (ex) {}
+ button.image = null;
+ stdPreviewSuite(winPreview);
+ // Let's not perma-hide this window from the taskbar
+ winPreview.visible = true;
+
+ let tabP = taskbar.createTaskbarTabPreview(docShell, controller);
+ isnot(tabP, null, "Tab preview is not null");
+ is(tabP.controller.wrappedJSObject, controller, "Controllers match");
+ is(tabP.icon, null, "Default icon is null (windows default)");
+ tabP.icon = null;
+ tabP.move(null);
+ try {
+ tabP.move(tabP);
+ ok(false, "Moved a preview next to itself!");
+ } catch (ex) {}
+ stdPreviewSuite(tabP);
+
+ let tabP2 = taskbar.createTaskbarTabPreview(docShell, controller);
+ tabP.visible = true;
+ tabP2.visible = true;
+
+ isnot(tabP2, null, "2nd Tab preview is not null");
+ isnot(tabP,tabP2, "Tab previews are different");
+ tabP.active = true;
+ ok(tabP.active && !tabP2.active, "Only one tab is active (part 1)");
+ tabP2.active = true;
+ ok(!tabP.active && tabP2.active, "Only one tab is active (part 2)");
+ tabP.active = true;
+ ok(tabP.active && !tabP2.active, "Only one tab is active (part 3)");
+ tabP.active = false;
+ ok(!tabP.active && !tabP2.active, "Neither tab is active");
+ is(winPreview.active, false, "Window preview is not active");
+ tabP.active = true;
+ winPreview.active = true;
+ ok(winPreview.active && !tabP.active, "Tab preview takes activation from window");
+ tabP.active = true;
+ ok(tabP.active && !winPreview.active, "Tab preview takes activation from window");
+
+ tabP.visible = false;
+ tabP2.visible = false;
+
+ SimpleTest.finish();
+ }
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ </body>
+
+</window>
diff --git a/widget/tests/test_assign_event_data.html b/widget/tests/test_assign_event_data.html
new file mode 100644
index 000000000..39f31cafe
--- /dev/null
+++ b/widget/tests/test_assign_event_data.html
@@ -0,0 +1,748 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Testing ns*Event::Assign*EventData()</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/NativeKeyCodes.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+ <style>
+ #a {
+ background-color: transparent;
+ transition: background-color 0.1s linear;
+ }
+ #a:focus {
+ background-color: red;
+ }
+ .slidin {
+ border: green 1px solid;
+ width: 10px;
+ height: 10px;
+ animation-name: slidein;
+ animation-duration: 1s;
+ }
+ @keyframes slidein {
+ from {
+ margin-left: 100%;
+ }
+ to {
+ margin-left: 0;
+ }
+ }
+ #pointer-target {
+ border: 1px dashed red;
+ background: yellow;
+ margin: 0px 10px;
+ padding: 0px 10px;
+ }
+ #scrollable-div {
+ background: green;
+ overflow: auto;
+ width: 30px;
+ height: 30px;
+ }
+ #scrolled-div {
+ background: magenta;
+ width: 10px;
+ height: 10px;
+ }
+ #form {
+ background: silver;
+ padding: 0px 10px;
+ }
+ #animated-div {
+ background: cyan;
+ padding: 0px 10px;
+ }
+ </style>
+</head>
+<body>
+<div id="display">
+ <input id="input-text">
+ <button id="button">button</button>
+ <a id="a" href="about:blank">hyper link</a>
+ <span id="pointer-target">span</span>
+ <div id="scrollable-div"><div id="scrolled-div"></div></div>
+ <form id="form">form</form>
+ <div id="animated-div">&nbsp;</div>
+</div>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+
+<script class="testbody" type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.expectAssertions(0, 34);
+
+const kIsMac = (navigator.platform.indexOf("Mac") == 0);
+const kIsWin = (navigator.platform.indexOf("Win") == 0);
+
+var gEvent = null;
+var gCopiedEvent = [];
+var gCallback = null;
+var gCallPreventDefault = false;
+
+function onEvent(aEvent)
+{
+ if (gCallPreventDefault) {
+ aEvent.preventDefault();
+ }
+ gEvent = aEvent;
+ for (var attr in aEvent) {
+ if (!attr.match(/^[A-Z0-9_]+$/) && // ignore const attributes
+ attr != "multipleActionsPrevented" && // multipleActionsPrevented isn't defined in any DOM event specs.
+ typeof(aEvent[attr]) != "function") {
+ gCopiedEvent.push({ name: attr, value: aEvent[attr]});
+ }
+ }
+ setTimeout(gCallback, 0);
+}
+
+const kTests = [
+ { description: "InternalScrollPortEvent (overflow, vertical)",
+ targetID: "scrollable-div", eventType: "overflow",
+ dispatchEvent: function () {
+ document.getElementById("scrolled-div").style.height = "500px";
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [],
+ },
+ { description: "InternalScrollPortEvent (overflow, horizontal)",
+ targetID: "scrollable-div", eventType: "overflow",
+ dispatchEvent: function () {
+ document.getElementById("scrolled-div").style.width = "500px";
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [],
+ },
+ { description: "InternalScrollAreaEvent (MozScrolledAreaChanged, spreading)",
+ target: function () { return document; }, eventType: "MozScrolledAreaChanged",
+ dispatchEvent: function () {
+ document.getElementById("scrollable-div").style.width = "50000px";
+ document.getElementById("scrollable-div").style.height = "50000px";
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [],
+ },
+ { description: "InternalScrollAreaEvent (MozScrolledAreaChanged, shrinking)",
+ target: function () { return document; }, eventType: "MozScrolledAreaChanged",
+ dispatchEvent: function () {
+ document.getElementById("scrollable-div").style.width = "30px";
+ document.getElementById("scrollable-div").style.height = "30px";
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [],
+ },
+ { description: "WidgetKeyboardEvent (keydown of 'a' key without modifiers)",
+ targetID: "input-text", eventType: "keydown",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).value = "";
+ document.getElementById(this.targetID).focus();
+ synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, kIsWin ? WIN_VK_A : MAC_VK_ANSI_A,
+ {}, "a", "a");
+ },
+ canRun: function () {
+ return (kIsMac || kIsWin);
+ },
+ todoMismatch: [],
+ },
+ { description: "WidgetKeyboardEvent (keyup of 'a' key without modifiers)",
+ targetID: "input-text", eventType: "keydown",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).value = "";
+ document.getElementById(this.targetID).focus();
+ synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, kIsWin ? WIN_VK_A : MAC_VK_ANSI_A,
+ {}, "a", "a");
+ },
+ canRun: function () {
+ return (kIsMac || kIsWin);
+ },
+ todoMismatch: [],
+ },
+ { description: "WidgetKeyboardEvent (keypress of 'b' key with Shift)",
+ targetID: "input-text", eventType: "keypress",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).value = "";
+ document.getElementById(this.targetID).focus();
+ synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, kIsWin ? WIN_VK_B : MAC_VK_ANSI_B,
+ { shiftKey: true }, "B", "B");
+ },
+ canRun: function () {
+ return (kIsMac || kIsWin);
+ },
+ todoMismatch: [],
+ },
+ { description: "WidgetKeyboardEvent (keypress of 'c' key with Accel)",
+ targetID: "input-text", eventType: "keypress",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).value = "";
+ document.getElementById(this.targetID).focus();
+ synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, kIsWin ? WIN_VK_C : MAC_VK_ANSI_C,
+ { accelKey: true }, kIsWin ? "\u0003" : "c", "c");
+ },
+ canRun: function () {
+ return (kIsMac || kIsWin);
+ },
+ todoMismatch: [],
+ },
+ { description: "WidgetKeyboardEvent (keyup during composition)",
+ targetID: "input-text", eventType: "keyup",
+ dispatchEvent: function () {
+ setAndObserveCompositionPref(true, () => {
+ document.getElementById(this.targetID).value = "";
+ document.getElementById(this.targetID).focus();
+ synthesizeCompositionChange({ "composition":
+ { "string": "\u306D",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 },
+ "key": { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A },
+ });
+ synthesizeComposition({ type: "compositioncommitasis" });
+ setAndObserveCompositionPref(null, runNextTest);
+ });
+ return true;
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [ ],
+ },
+ { description: "WidgetKeyboardEvent (keydown during composition)",
+ targetID: "input-text", eventType: "keydown",
+ dispatchEvent: function () {
+ setAndObserveCompositionPref(true, () => {
+ document.getElementById(this.targetID).value = "";
+ document.getElementById(this.targetID).focus();
+ synthesizeCompositionChange({ "composition":
+ { "string": "\u306D",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+ synthesizeComposition({ type: "compositioncommitasis",
+ key: { key: "KEY_Enter", code: "Enter" } });
+ setAndObserveCompositionPref(null, runNextTest);
+ });
+ return true;
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [ ],
+ },
+ { description: "WidgetMouseEvent (mousedown of left button without modifier)",
+ targetID: "button", eventType: "mousedown",
+ dispatchEvent: function () {
+ synthesizeMouseAtCenter(document.getElementById(this.targetID),
+ { button: 0 });
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [],
+ },
+ { description: "WidgetMouseEvent (click of middle button with Shift)",
+ // XXX I'm not sure why middle click event isn't fired on button element.
+ targetID: "input-text", eventType: "click",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).value = "";
+ synthesizeMouseAtCenter(document.getElementById(this.targetID),
+ { button: 1, shiftKey: true, pressure: 0.5 });
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [],
+ },
+ { description: "WidgetMouseEvent (mouseup of right button with Alt)",
+ targetID: "button", eventType: "mouseup",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).value = "";
+ synthesizeMouseAtCenter(document.getElementById(this.targetID),
+ { button: 2, altKey: true });
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [],
+ },
+ { description: "WidgetDragEvent",
+ targetID: "input-text", eventType: "dragstart",
+ dispatchEvent: function () {
+ return;
+ },
+ canRun: function () {
+ todo(false, "WidgetDragEvent isn't tested");
+ return false;
+ },
+ todoMismatch: [],
+ },
+ { description: "WidgetTextEvent (text)",
+ targetID: "input-text", eventType: "text",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).value = "";
+ document.getElementById(this.targetID).focus();
+ synthesizeComposition({ type: "compositioncommit", data: "\u306D" });
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [ ],
+ },
+ { description: "WidgetCompositionEvent (compositionupdate)",
+ targetID: "input-text", eventType: "compositionupdate",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).value = "";
+ document.getElementById(this.targetID).focus();
+ synthesizeComposition({ type: "compositioncommit", data: "\u30E9\u30FC\u30E1\u30F3" });
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [ ],
+ },
+ { description: "InternalEditorInputEvent (input at key input)",
+ targetID: "input-text", eventType: "input",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).value = "";
+ document.getElementById(this.targetID).focus();
+ synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, kIsWin ? WIN_VK_B : MAC_VK_ANSI_B,
+ { shiftKey: true }, "B", "B");
+ },
+ canRun: function () {
+ return (kIsMac || kIsWin);
+ },
+ todoMismatch: [],
+ },
+ { description: "InternalEditorInputEvent (input at composing)",
+ targetID: "input-text", eventType: "input",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).value = "";
+ document.getElementById(this.targetID).focus();
+ synthesizeCompositionChange({ "composition":
+ { "string": "\u30E9\u30FC\u30E1\u30F3",
+ "clauses":
+ [
+ { "length": 4, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 4, "length": 0 }
+ });
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [ ],
+ },
+ { description: "InternalEditorInputEvent (input at committing)",
+ targetID: "input-text", eventType: "input",
+ dispatchEvent: function () {
+ synthesizeComposition({ type: "compositioncommitasis" });
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [ ],
+ },
+ { description: "WidgetMouseScrollEvent (DOMMouseScroll, vertical)",
+ targetID: "input-text", eventType: "DOMMouseScroll",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).value = "";
+ synthesizeWheel(document.getElementById(this.targetID), 3, 4,
+ { deltaY: 30, lineOrPageDeltaY: 2 });
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [ ],
+ },
+ { description: "WidgetMouseScrollEvent (DOMMouseScroll, horizontal)",
+ targetID: "input-text", eventType: "DOMMouseScroll",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).value = "";
+ synthesizeWheel(document.getElementById(this.targetID), 4, 5,
+ { deltaX: 30, lineOrPageDeltaX: 2, shiftKey: true });
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [ ],
+ },
+ { description: "WidgetMouseScrollEvent (MozMousePixelScroll, vertical)",
+ targetID: "input-text", eventType: "MozMousePixelScroll",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).value = "";
+ synthesizeWheel(document.getElementById(this.targetID), 3, 4,
+ { deltaY: 20, lineOrPageDeltaY: 1, altKey: true });
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [ ],
+ },
+ { description: "WidgetMouseScrollEvent (MozMousePixelScroll, horizontal)",
+ targetID: "input-text", eventType: "MozMousePixelScroll",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).value = "";
+ synthesizeWheel(document.getElementById(this.targetID), 4, 5,
+ { deltaX: 20, lineOrPageDeltaX: 1, ctrlKey: true });
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [ ],
+ },
+ { description: "WidgetWheelEvent (wheel, vertical)",
+ targetID: "input-text", eventType: "wheel",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).value = "";
+ synthesizeWheel(document.getElementById(this.targetID), 3, 4,
+ { deltaY: 20, lineOrPageDeltaY: 1, altKey: true });
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [ ],
+ },
+ { description: "WidgetWheelEvent (wheel, horizontal)",
+ targetID: "input-text", eventType: "wheel",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).value = "";
+ synthesizeWheel(document.getElementById(this.targetID), 4, 5,
+ { deltaX: 20, lineOrPageDeltaX: 1, ctrlKey: true });
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [ ],
+ },
+ { description: "WidgetWheelEvent (wheel, both)",
+ targetID: "input-text", eventType: "wheel",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).value = "";
+ synthesizeWheel(document.getElementById(this.targetID), 4, 5,
+ { deltaX: 20, deltaY: 10,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 });
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [ ],
+ },
+ { description: "WidgetTouchEvent (touchstart)",
+ target: function () { return document; }, eventType: "touchstart",
+ dispatchEvent: function () {
+ synthesizeTouchAtPoint(1, 2, { id: 10, rx: 4, ry: 3, angle: 0, force: 1, shiftKey: true});
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [ ],
+ },
+ { description: "WidgetTouchEvent (touchend)",
+ target: function () { return document; }, eventType: "touchend",
+ dispatchEvent: function () {
+ synthesizeTouchAtPoint(4, 6, { id: 5, rx: 1, ry: 2, angle: 0.5, force: 0.8, ctrlKey: true});
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [ ],
+ },
+ { description: "InternalFormEvent (reset)",
+ targetID: "form", eventType: "reset",
+ dispatchEvent: function () {
+ document.getElementById("form").reset();
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [ ],
+ },
+ { description: "WidgetCommandEvent",
+ targetID: "input-text", eventType: "",
+ dispatchEvent: function () {
+ return;
+ },
+ canRun: function () {
+ todo(false, "WidgetCommandEvent isn't tested");
+ return false;
+ },
+ todoMismatch: [],
+ },
+ { description: "InternalClipboardEvent (copy)",
+ targetID: "input-text", eventType: "copy",
+ dispatchEvent: function () {
+ document.getElementById("input-text").value = "go to clipboard!";
+ document.getElementById("input-text").focus();
+ document.getElementById("input-text").select();
+ synthesizeKey("c", { accelKey: true });
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [ ],
+ },
+ { description: "InternalUIEvent (DOMActivate)",
+ targetID: "button", eventType: "DOMActivate",
+ dispatchEvent: function () {
+ synthesizeMouseAtCenter(document.getElementById(this.targetID),
+ { button: 0, shiftKey: true });
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [],
+ },
+ { description: "InternalFocusEvent (focus)",
+ targetID: "input-text", eventType: "focus",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).focus();
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [],
+ },
+ { description: "InternalFocusEvent (blur)",
+ targetID: "input-text", eventType: "blur",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).blur();
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [],
+ },
+ { description: "WidgetSimpleGestureEvent",
+ targetID: "", eventType: "",
+ dispatchEvent: function () {
+ return;
+ },
+ canRun: function () {
+ // Simple gesture event may be handled before it comes content.
+ // So, we cannot test it in this test.
+ todo(false, "WidgetSimpleGestureEvent isn't tested");
+ return false;
+ },
+ todoMismatch: [],
+ },
+ { description: "InternalTransitionEvent (transitionend)",
+ targetID: "a", eventType: "transitionend",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).focus();
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [],
+ },
+ { description: "InternalAnimationEvent (animationend)",
+ targetID: "animated-div", eventType: "animationend",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).className = "slidin";
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [],
+ },
+ { description: "InternalMutationEvent (DOMAttrModified)",
+ targetID: "animated-div", eventType: "DOMAttrModified",
+ dispatchEvent: function () {
+ document.getElementById(this.targetID).setAttribute("x-data", "foo");
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [],
+ },
+ { description: "InternalMutationEvent (DOMNodeInserted)",
+ targetID: "animated-div", eventType: "DOMNodeInserted",
+ dispatchEvent: function () {
+ var div = document.createElement("div");
+ div.id = "inserted-div";
+ document.getElementById("animated-div").appendChild(div);
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [],
+ },
+ { description: "InternalMutationEvent (DOMNodeRemoved)",
+ targetID: "animated-div", eventType: "DOMNodeRemoved",
+ dispatchEvent: function () {
+ document.getElementById("animated-div").removeChild(document.getElementById("inserted-div"));
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [],
+ },
+ { description: "PointerEvent (pointerdown)",
+ targetID: "pointer-target", eventType: "pointerdown",
+ dispatchEvent: function () {
+ var elem = document.getElementById(this.targetID);
+ var rect = elem.getBoundingClientRect();
+ synthesizePointer(elem, rect.width/2, rect.height/2,
+ { type: this.eventType, button: 1, clickCount: 1, inputSource: 2, pressure: 0.25, isPrimary: true });
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [],
+ },
+ { description: "PointerEvent (pointerup)",
+ targetID: "pointer-target", eventType: "pointerup",
+ dispatchEvent: function () {
+ var elem = document.getElementById(this.targetID);
+ var rect = elem.getBoundingClientRect();
+ synthesizePointer(elem, rect.width/2, rect.height/2,
+ { type: this.eventType, button: -1, ctrlKey: true, shiftKey: true, altKey: true, isSynthesized: false });
+ },
+ canRun: function () {
+ return true;
+ },
+ todoMismatch: [],
+ },
+];
+
+/**
+ * Sets or clears dom.keyboardevent.dispatch_during_composition and calls the
+ * given callback when the change is observed.
+ *
+ * @param aValue
+ * Pass null to clear the pref. Otherwise pass a bool.
+ * @param aCallback
+ * Called when the pref change is observed.
+ */
+function setAndObserveCompositionPref(aValue, aCallback) {
+ let pref = "dom.keyboardevent.dispatch_during_composition";
+ let branch = SpecialPowers.Cc["@mozilla.org/preferences-service;1"].
+ getService(SpecialPowers.Ci.nsIPrefService).
+ getBranch(pref);
+ let obs = SpecialPowers.wrapCallback(function () {
+ branch.removeObserver("", obs);
+ // Make sure the code under test sees the change first, so executeSoon().
+ SimpleTest.executeSoon(aCallback);
+ });
+ branch.addObserver("", obs, false);
+ if (aValue === null) {
+ SpecialPowers.clearUserPref(pref);
+ } else {
+ SpecialPowers.setBoolPref(pref, aValue);
+ }
+}
+
+function doTest(aTest)
+{
+ if (!aTest.canRun()) {
+ SimpleTest.executeSoon(runNextTest);
+ return;
+ }
+ gEvent = null;
+ gCopiedEvent = [];
+ var target = aTest.target ? aTest.target() : document.getElementById(aTest.targetID);
+ target.addEventListener(aTest.eventType, onEvent, true);
+ gCallback = function () {
+ var description = aTest.description + " (gCallPreventDefault=" + gCallPreventDefault + ")";
+ target.removeEventListener(aTest.eventType, onEvent, true);
+ ok(gEvent !== null, description + ": failed to get duplicated event");
+ ok(gCopiedEvent.length > 0, description + ": count of attribute of the event must be larger than 0");
+ for (var i = 0; i < gCopiedEvent.length; ++i) {
+ var name = gCopiedEvent[i].name;
+ if (name == "rangeOffset") {
+ todo(false, description + ": " + name + " attribute value is never reset (" + gEvent[name] + ")");
+ } else if (name == "eventPhase") {
+ is(gEvent[name], 0, description + ": mismatch with fixed value (" + name + ")");
+ } else if (name == "rangeParent" || name == "currentTarget") {
+ is(gEvent[name], null, description + ": mismatch with fixed value (" + name + ")");
+ } else if (aTest.todoMismatch.indexOf(name) >= 0) {
+ todo_is(gEvent[name], gCopiedEvent[i].value, description + ": mismatch (" + name + ")");
+ } else if (name == "offsetX" || name == "offsetY") {
+ // do nothing; these are defined to return different values during event dispatch
+ // vs not during event dispatch
+ } else {
+ is(gEvent[name], gCopiedEvent[i].value, description + ": mismatch (" + name + ")");
+ }
+ }
+ if (!testWillCallRunNextTest) {
+ runNextTest();
+ }
+ };
+ var testWillCallRunNextTest = aTest.dispatchEvent();
+}
+
+var gIndex = -1;
+function runNextTest()
+{
+ if (++gIndex == kTests.length) {
+ if (gCallPreventDefault) {
+ finish();
+ return;
+ }
+ // Test with a call of preventDefault() of the events.
+ gCallPreventDefault = true;
+ gIndex = -1;
+ // Restoring the initial state of all elements.
+ document.getElementById("scrollable-div").style.height = "30px";
+ document.getElementById("scrollable-div").style.width = "30px";
+ document.getElementById("scrolled-div").style.height = "10px";
+ document.getElementById("scrolled-div").style.width = "10px";
+ document.getElementById("input-text").value = "";
+ document.getElementById("animated-div").className = "";
+ document.getElementById("animated-div").removeAttribute("x-data");
+ if (document.activeElement) {
+ document.activeElement.blur();
+ }
+ window.requestAnimationFrame(function () {
+ setTimeout(runNextTest, 0);
+ });
+ return;
+ }
+ doTest(kTests[gIndex]);
+}
+
+function init()
+{
+ SpecialPowers.pushPrefEnv({"set":[["middlemouse.contentLoadURL", false],
+ ["middlemouse.paste", false],
+ ["general.autoScroll", false],
+ ["mousewheel.default.action", 0],
+ ["mousewheel.default.action.override_x", -1],
+ ["mousewheel.with_shift.action", 0],
+ ["mousewheel.with_shift.action.override_x", -1],
+ ["mousewheel.with_control.action", 0],
+ ["mousewheel.with_control.action.override_x", -1],
+ ["mousewheel.with_alt.action", 0],
+ ["mousewheel.with_alt.action.override_x", -1],
+ ["mousewheel.with_meta.action", 0],
+ ["mousewheel.with_meta.action.override_x", -1]]}, runNextTest);
+}
+
+function finish()
+{
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForFocus(init);
+
+</script>
+</body>
diff --git a/widget/tests/test_bug1123480.xul b/widget/tests/test_bug1123480.xul
new file mode 100644
index 000000000..56ce0ed10
--- /dev/null
+++ b/widget/tests/test_bug1123480.xul
@@ -0,0 +1,79 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1123480
+-->
+<window title="Mozilla Bug 1123480"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="RunTest();">
+ <title>nsTransferable PBM Overflow Selection Test</title>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+ // Boilerplate constructs
+ var SmallDataset = 100000; // Hundred thousand bytes
+
+ // Create 1 Mo of sample garbage text
+ var Ipsum = ""; // Select text from this
+ for (var Iter = 0; Iter < SmallDataset; Iter++) {
+ Ipsum += Math.random().toString(36) + ' ';
+ }
+
+ function RunTest() {
+ // Construct a nsIFile object for access to file methods
+ Components.utils.import("resource://gre/modules/FileUtils.jsm");
+ var clipboardFile = FileUtils.getFile("TmpD", ["clipboardcache"]);
+
+ // Sanitize environment
+ if (clipboardFile.exists()) {
+ clipboardFile.remove(false);
+ }
+ ok(!clipboardFile.exists(), "failed to presanitize the environment");
+
+ // Overflow a nsTransferable region by using the clipboard helper
+ const gClipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper);
+ gClipboardHelper.copyString(Ipsum);
+
+ // Disabled private browsing mode should cache large selections to disk
+ ok(clipboardFile.exists(), "correctly saved memory by caching to disk");
+
+ // Sanitize environment again
+ if (clipboardFile.exists()) {
+ clipboardFile.remove(false);
+ }
+ ok(!clipboardFile.exists(), "failed to postsanitize the environment");
+
+ // Repeat procedure of plain text selection with private browsing enabled
+ Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+ var Winpriv = window.open("about:blank", "_blank", "chrome, width=500, height=200, private");
+ ok(Winpriv, "failed to open private window");
+ ok(PrivateBrowsingUtils.isContentWindowPrivate(Winpriv), "correctly used a private window context");
+
+ // Select plaintext in private channel
+ Components.utils.import('resource://gre/modules/Services.jsm');
+ const nsTransferable = Components.Constructor("@mozilla.org/widget/transferable;1", "nsITransferable");
+ const nsSupportsString = Components.Constructor("@mozilla.org/supports-string;1", "nsISupportsString");
+ var Loadctx = PrivateBrowsingUtils.privacyContextFromWindow(Winpriv);
+ var Transfer = nsTransferable();
+ var Suppstr = nsSupportsString();
+ Suppstr.data = Ipsum;
+ Transfer.init(Loadctx);
+ Transfer.addDataFlavor("text/plain");
+ Transfer.setTransferData("text/plain", Suppstr, Ipsum.length);
+ Services.clipboard.setData(Transfer, null, Services.clipboard.kGlobalClipboard);
+
+ // Enabled private browsing mode should not cache any selection to disk
+ ok(!clipboardFile.exists(), "did not violate private browsing mode");
+ }
+ ]]>
+ </script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1123480"
+ target="_blank">Mozilla Bug 1123480</a>
+ </body>
+</window>
diff --git a/widget/tests/test_bug1151186.html b/widget/tests/test_bug1151186.html
new file mode 100644
index 000000000..391e53d78
--- /dev/null
+++ b/widget/tests/test_bug1151186.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1151186
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1151186</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1151186 **/
+ SimpleTest.waitForExplicitFinish();
+
+ document.addEventListener("focus", () => {
+ document.getElementById("editor").focus();
+ SimpleTest.executeSoon(runTests);
+ });
+
+ function runTests()
+ {
+ is(document.activeElement, document.getElementById("editor"),
+ "The div element should be focused");
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ is(utils.IMEStatus, utils.IME_STATUS_ENABLED,
+ "IME should be enabled");
+ SimpleTest.finish();
+ }
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1151186">Mozilla Bug 1151186</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<div id="editor" contenteditable="true"></div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/widget/tests/test_bug343416.xul b/widget/tests/test_bug343416.xul
new file mode 100644
index 000000000..1c31bdf54
--- /dev/null
+++ b/widget/tests/test_bug343416.xul
@@ -0,0 +1,202 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=343416
+-->
+<window title="Mozilla Bug 343416"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=343416">Mozilla Bug 343416</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 343416 **/
+SimpleTest.waitForExplicitFinish();
+
+// Observer:
+var idleObserver =
+{
+ QueryInterface: function _qi(iid)
+ {
+ if (iid.equals(Components.interfaces.nsISupports) ||
+ iid.equals(Components.interfaces.nsIObserver))
+ {
+ return this;
+ }
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+ observe: function _observe(subject, topic, data)
+ {
+ if (topic != "idle")
+ return;
+
+ var diff = Math.abs(data - newIdleSeconds * 1000);
+
+// ok (diff < 5000, "The idle time should have increased by roughly 6 seconds, " +
+// "as that's when we told this listener to fire.");
+// if (diff >= 5000)
+// alert(data + " " + newIdleSeconds);
+
+ // Attempt to get to the nsIIdleService
+ var subjectOK = false;
+ try {
+ var idleService = subject.QueryInterface(nsIIdleService);
+ subjectOK = true;
+ }
+ catch (ex)
+ {}
+ ok(subjectOK, "The subject of the notification should be the " +
+ "nsIIdleService.");
+
+ // Attempt to remove ourselves.
+ var removedObserver = false;
+ try {
+ idleService.removeIdleObserver(this, newIdleSeconds);
+ removedObserver = true;
+ }
+ catch (ex)
+ {}
+ ok(removedObserver, "We should be able to remove our observer here.");
+ finishedListenerOK = true;
+ if (finishedTimeoutOK)
+ {
+ clearTimeout(testBailout);
+ finishThisTest();
+ }
+ }
+};
+
+
+const nsIIdleService = Components.interfaces.nsIIdleService;
+const nsIISCID = "@mozilla.org/widget/idleservice;1";
+var idleService = null;
+try
+{
+ idleService = Components.classes[nsIISCID].getService(nsIIdleService);
+}
+catch (ex)
+{}
+
+ok(idleService, "nsIIdleService should exist and be implemented on all tier 1 platforms.");
+
+var idleTime = null;
+var gotIdleTime = false;
+try
+{
+ idleTime = idleService.idleTime;
+ gotIdleTime = true;
+}
+catch (ex)
+{}
+
+ok (gotIdleTime, "Getting the idle time should not fail " +
+ "in normal circumstances on any tier 1 platform.");
+
+// Now we set up a timeout to sanity-test the idleTime after 5 seconds
+setTimeout(testIdleTime, 5000);
+var startTimeStamp = Date.now();
+
+// Now we add the listener:
+var newIdleSeconds = Math.floor(idleTime / 1000) + 6;
+var addedObserver = false;
+try
+{
+ idleService.addIdleObserver(idleObserver, newIdleSeconds);
+ addedObserver = true;
+}
+catch (ex)
+{}
+
+ok(addedObserver, "The nsIIdleService should allow us to add an observer.");
+
+addedObserver = false;
+try
+{
+ idleService.addIdleObserver(idleObserver, newIdleSeconds);
+ addedObserver = true;
+}
+catch (ex)
+{}
+
+ok(addedObserver, "The nsIIdleService should allow us to add the same observer again.");
+
+var removedObserver = false;
+try
+{
+ idleService.removeIdleObserver(idleObserver, newIdleSeconds);
+ removedObserver = true;
+}
+catch (ex)
+{}
+
+ok(removedObserver, "The nsIIdleService should allow us to remove the observer just once.");
+
+function testIdleTime()
+{
+ var gotIdleTime = false
+ try
+ {
+ var newIdleTime = idleService.idleTime;
+ gotIdleTime = true
+ }
+ catch (ex)
+ {}
+ ok(gotIdleTime, "Getting the idle time should not fail " +
+ "in normal circumstances on any tier 1 platform.");
+ // Get the time difference, remove the approx. 5 seconds that we've waited,
+ // should be very close to 0 left.
+ var timeDiff = Math.abs((newIdleTime - idleTime) -
+ (Date.now() - startTimeStamp));
+
+ var timePassed = Date.now() - startTimeStamp;
+ var idleTimeDiff = newIdleTime - idleTime;
+ // 1.5 second leniency.
+ ok(timeDiff < 1500, "The idle time should have increased by roughly the " +
+ "amount of time it took for the timeout to fire. " +
+ "You didn't touch the mouse or keyboard during the " +
+ "test did you?");
+ finishedTimeoutOK = true;
+}
+
+// make sure we still exit when the listener and/or setTimeout don't fire:
+var testBailout = setTimeout(finishThisTest, 12000);
+var finishedTimeoutOK = false, finishedListenerOK = false;
+function finishThisTest()
+{
+ ok(finishedTimeoutOK, "We set a timeout and it should have fired by now.");
+ ok(finishedListenerOK, "We added a listener and it should have been called by now.");
+ if (!finishedListenerOK)
+ {
+ var removedListener = false;
+ try
+ {
+ idleService.removeIdleObserver(idleObserver, newIdleSeconds);
+ removedListener = true;
+ }
+ catch (ex)
+ {}
+
+ ok(removedListener, "We added a listener and we should be able to remove it.");
+ }
+ // Done:
+ SimpleTest.finish();
+}
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/test_bug413277.html b/widget/tests/test_bug413277.html
new file mode 100644
index 000000000..dfb1ac8d4
--- /dev/null
+++ b/widget/tests/test_bug413277.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=413277
+-->
+<head>
+ <title>Test for Bug 413277</title>
+ <script type="text/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=413277">Mozilla Bug 413277</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript">
+ var atts='width=100, height=100, top=100, screenY=100';
+ atts += ', left=100, screenX=100, toolbar=no';
+ atts += ', location=no, directories=no, status=no';
+ atts += ', menubar=no, scrollbars=no, resizable=no';
+ var newWindow = window.open('_blank', 'win_name', atts);
+
+ newWindow.resizeBy(1000000, 1000000);
+ SimpleTest.is(newWindow.outerWidth, newWindow.screen.availWidth, true);
+ SimpleTest.is(newWindow.outerHeight, newWindow.screen.availHeight, true);
+ SimpleTest.is(newWindow.screenY, newWindow.screen.availTop, true);
+ SimpleTest.is(newWindow.screenX, newWindow.screen.availLeft, true);
+
+ newWindow.close();
+</script>
+</pre>
+</body>
diff --git a/widget/tests/test_bug428405.xul b/widget/tests/test_bug428405.xul
new file mode 100644
index 000000000..365480468
--- /dev/null
+++ b/widget/tests/test_bug428405.xul
@@ -0,0 +1,167 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window id="window1" title="Test Bug 428405"
+ onload="setGlobals(); loadFirstTab();"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/NativeKeyCodes.js"/>
+
+ <tabbox id="tabbox" flex="100%">
+ <tabs>
+ <tab label="Tab 1"/>
+ <tab label="Tab 2"/>
+ </tabs>
+ <tabpanels flex="100%">
+ <browser onload="configureFirstTab();" id="tab1browser" flex="100%"/>
+ <browser onload="configureSecondTab();" id="tab2browser" flex="100%"/>
+ </tabpanels>
+ </tabbox>
+
+ <script type="application/javascript"><![CDATA[
+
+ SimpleTest.waitForExplicitFinish();
+
+ var gCmdOptYReceived = false;
+
+ // Look for a cmd-opt-y event.
+ function onKeyPress(aEvent) {
+ gCmdOptYReceived = false;
+ if (String.fromCharCode(aEvent.charCode) != 'y')
+ return;
+ if (aEvent.ctrlKey || aEvent.shiftKey || !aEvent.metaKey || !aEvent.altKey)
+ return;
+ gCmdOptYReceived = true;
+ }
+
+ function setGlobals() {
+ var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
+ getService(Components.interfaces.nsIWindowMediator);
+ gChromeWindow = wm.getMostRecentWindow("navigator:browser");
+ // For some reason, a global <key> element's oncommand handler only gets
+ // invoked if the focus is outside both of the <browser> elements
+ // (tab1browser and tab2browser). So, to make sure we can see a
+ // cmd-opt-y event in window1 (if one is available), regardless of where
+ // the focus is in this window, we need to add a "keypress" event
+ // listener to gChromeWindow, and then check (in onKeyPress()) to see if
+ // it's a cmd-opt-y event.
+ gChromeWindow.addEventListener("keypress", onKeyPress, false);
+ }
+
+ // 1) Start loading first tab.
+ // 6) Start reloading first tab.
+ function loadFirstTab() {
+ var browser = document.getElementById("tab1browser");
+ browser.loadURI("data:text/html;charset=utf-8,<body><h2>First Tab</h2><p><input type='submit' value='Button' id='button1'/></body>", null, null);
+ }
+
+ function configureFirstTab() {
+ try {
+ var button = document.getElementById("tab1browser").contentDocument.getElementById("button1");
+ button.addEventListener("click", onFirstTabButtonClicked, false);
+ button.focus();
+ if (document.getElementById("tabbox").selectedIndex == 0) {
+ // 2) When first tab has finished loading (while first tab is
+ // focused), hit Return to trigger the action of first tab's
+ // button.
+ synthesizeNativeReturnKey();
+ } else {
+ // 7) When first tab has finished reloading (while second tab is
+ // focused), start loading second tab.
+ loadSecondTab();
+ }
+ } catch(e) {
+ }
+ }
+
+ // 8) Start loading second tab.
+ function loadSecondTab() {
+ var browser = document.getElementById("tab2browser");
+ browser.loadURI("data:text/html;charset=utf-8,<body><h2>Second Tab</h2><p><input type='submit' value='Button' id='button1'/></body>", null, null);
+ }
+
+ function configureSecondTab() {
+ try {
+ var button = document.getElementById("tab2browser").contentDocument.getElementById("button1");
+ button.addEventListener("click", onSecondTabButtonClicked, false);
+ button.focus();
+ if (document.getElementById("tabbox").selectedIndex == 1) {
+ // 9) When second tab has finished loading (while second tab is
+ // focused), hit Return to trigger action of second tab's
+ // button.
+ synthesizeNativeReturnKey();
+ }
+ } catch(e) {
+ }
+ }
+
+ // 3) First tab's button clicked.
+ function onFirstTabButtonClicked() {
+ switchToSecondTabAndReloadFirst();
+ }
+
+ // 10) Second tab's button clicked.
+ function onSecondTabButtonClicked() {
+ switchToFirstTab();
+ }
+
+ function switchToSecondTabAndReloadFirst() {
+ // 4) Switch to second tab.
+ document.getElementById("tabbox").selectedIndex = 1;
+ // 5) Start reloading first tab (while second tab is focused).
+ loadFirstTab();
+ }
+
+ function switchToFirstTab() {
+ // 11) Switch back to first tab.
+ document.getElementById("tabbox").selectedIndex = 0;
+ doCmdY();
+ }
+
+ function doCmdY() {
+ // 12) Back in first tab, try cmd-y.
+ gCmdOptYReceived = false;
+ if (!synthesizeNativeCmdOptY(finishTest)) {
+ ok(false, "Failed to synthesize native key");
+ finishTest();
+ }
+ }
+
+ function finishTest() {
+ // 13) Check result.
+ is(gCmdOptYReceived, true);
+
+ SimpleTest.finish();
+ }
+
+ // synthesizeNativeReturnKey() and synthesizeNativeCmdOptY() are needed
+ // because their synthesizeKey() counterparts don't work properly -- the
+ // latter make this test succeed when it should fail.
+
+ // The 'aNativeKeyCode', 'aCharacters' and 'aUnmodifiedCharacters'
+ // parameters used below (in synthesizeNativeReturnKey() and
+ // synthesizeNativeCmdOptY()) were confirmed accurate using the
+ // DebugEventsPlugin v1.01 from bmo bug 441880.
+
+ function synthesizeNativeReturnKey() {
+ synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, MAC_VK_Return, {}, "\u000a", "\u000a");
+ }
+
+ function synthesizeNativeCmdOptY(aCallback) {
+ return synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_Y, {metaKey:1, altKey:1}, "y", "y", aCallback);
+ }
+
+ ]]></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ </body>
+
+</window>
diff --git a/widget/tests/test_bug429954.xul b/widget/tests/test_bug429954.xul
new file mode 100644
index 000000000..9b617ed4d
--- /dev/null
+++ b/widget/tests/test_bug429954.xul
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=429954
+-->
+<window title="Mozilla Bug 429954"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(function () {
+ var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
+ getService(Components.interfaces.nsIWindowMediator);
+ var win = wm.getMostRecentWindow("navigator:browser");
+ win.maximize();
+ var maxX = win.screenX, maxY = win.screenY;
+ var maxWidth = win.outerWidth, maxHeight = win.outerHeight;
+ win.restore();
+
+ window.open("window_bug429954.xul", "_blank",
+ "chrome,resizable,width=" + maxWidth + ",height=" + maxHeight +
+ "screenX=" + maxX + "screenY=" + maxY);
+});
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/test_bug444800.xul b/widget/tests/test_bug444800.xul
new file mode 100644
index 000000000..14d572baa
--- /dev/null
+++ b/widget/tests/test_bug444800.xul
@@ -0,0 +1,102 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin"
+ type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=444800
+-->
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Mozilla Bug 444800" onload="initAndRunTests()">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=444800"
+ target="_blank">Mozilla Bug 444800</a>
+ <p/>
+ <img id="bitmapImage" src="data:image/bmp;base64,Qk2KAwAAAAAAAIoAAAB8AAAADwAAABAAAAABABgAAAAAAAADAAASCwAAEgsAAAAAAAAAAAAAAAD%2FAAD%2FAAD%2FAAAAAAAA%2FwEAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F0vf%2FAABc8tKY%2F%2F%2F%2FyNfq3Mi9%2F%2F%2F70vf%2FAABP8s2R%2F%2F%2F%2F%2F%2F%2F%2FAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2Fzff%2FAABB8s2R5f%2F%2FAAB5LgAA%2F%2B7Czff%2FAABB7s2R%2F%2F%2F%2F%2F%2F%2F%2FAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2Fzff%2FAABB99KRpdz%2FAAAAAAAA4Ktm0vv%2FAABB7s2R%2F%2F%2F%2F%2F%2F%2F%2FAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2Fzff%2FAABB7teYQZHNkS4AebfImAAA1%2FfyAABP7s2R%2F%2F%2F%2F%2F%2F%2F%2FAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2Fzff%2FAABByMiYAAB5159P0v%2F%2FAABBwtKrAABc7s2R%2F%2F%2F%2F%2F%2F%2F%2FAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2Fzff%2FAABPcIJwAAAA%2B%2BW3%2F%2F%2F%2FAHC3gnBBAABP7s2R%2F%2F%2F%2F%2F%2F%2F%2FAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2Fzff%2FAABcAAAAmE8A%2F%2F%2Fy%2F%2F%2F%2Fn9LyAAAAAAAA7s2Y%2F%2F%2F%2F%2F%2F%2F%2FAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FzfL%2FAABcAAAA4LFw%2F%2F%2F%2F%2F%2F%2F%2F4P%2F%2FAAB5AAAA7s2R%2F%2F%2F%2F%2F%2F%2F%2FAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F0vf%2FAABmXAAA%2F%2B7I%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FXJ%2FSAAAA8s2Y%2F%2F%2F%2F%2F%2F%2F%2FAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAA"/>
+ <p/>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <script class="testbody" type="application/javascript">
+ <![CDATA[
+const knsIClipboard = Components.interfaces.nsIClipboard;
+
+function copyImageToClipboard()
+{
+ var tmpNode = document.popupNode;
+ document.popupNode = document.getElementById("bitmapImage");
+
+ const kCmd = "cmd_copyImageContents";
+ var controller = top.document.commandDispatcher
+ .getControllerForCommand(kCmd);
+ ok((controller && controller.isCommandEnabled(kCmd)), "have copy command");
+ controller.doCommand(kCmd);
+
+ document.popupNode = tmpNode;
+}
+
+function getLoadContext() {
+ const Ci = Components.interfaces;
+ return window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsILoadContext);
+}
+
+function runImageClipboardTests(aCBSvc, aImageType)
+{
+ // Verify that hasDataMatchingFlavors() is working correctly.
+ var typeArray = [ aImageType ];
+ var hasImage = aCBSvc.hasDataMatchingFlavors(typeArray, typeArray.length,
+ knsIClipboard.kGlobalClipboard);
+ ok(hasImage, aImageType + " - hasDataMatchingFlavors()");
+
+ // Verify that getData() is working correctly.
+ var xfer = Components.classes["@mozilla.org/widget/transferable;1"]
+ .createInstance(Components.interfaces.nsITransferable);
+ xfer.init(getLoadContext());
+ xfer.addDataFlavor(aImageType);
+ aCBSvc.getData(xfer, knsIClipboard.kGlobalClipboard);
+
+ var typeObj = {}, dataObj = {}, lenObj = {};
+ xfer.getAnyTransferData(typeObj, dataObj, lenObj);
+ var gotValue = (null != dataObj.value);
+ ok(gotValue, aImageType + " - getData() returned a value");
+ if (gotValue)
+ {
+ const knsIInputStream = Components.interfaces.nsIInputStream;
+ var imgStream = dataObj.value.QueryInterface(knsIInputStream);
+ ok((null != imgStream), aImageType + " - got an nsIInputStream");
+ var bytesAvailable = imgStream.available();
+ ok((bytesAvailable > 10), aImageType + " - got some data");
+ }
+}
+
+function initAndRunTests()
+{
+ SimpleTest.waitForExplicitFinish();
+
+ copyImageToClipboard();
+
+ var cbSvc = Components.classes["@mozilla.org/widget/clipboard;1"]
+ .getService(knsIClipboard);
+
+ // Work around a problem on Windows where clipboard is not ready after copy.
+ setTimeout(function() { runTests(cbSvc); }, 0);
+}
+
+function runTests(aCBSvc)
+{
+ runImageClipboardTests(aCBSvc, "image/png");
+ runImageClipboardTests(aCBSvc, "image/jpg");
+ runImageClipboardTests(aCBSvc, "image/jpeg");
+
+ SimpleTest.finish();
+}
+
+]]>
+</script>
+</window>
diff --git a/widget/tests/test_bug466599.xul b/widget/tests/test_bug466599.xul
new file mode 100644
index 000000000..a70f47add
--- /dev/null
+++ b/widget/tests/test_bug466599.xul
@@ -0,0 +1,109 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=466599
+-->
+<window title="Mozilla Bug 466599"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="initAndRunTests()">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ </body>
+
+ <!-- test code goes here -->
+ <script class="testbody" type="application/javascript">
+ <![CDATA[
+
+ /** Test for Bug 466599 **/
+
+function getLoadContext() {
+ const Ci = Components.interfaces;
+ return window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsILoadContext);
+}
+
+function copyToClipboard(txt)
+{
+ var clipid = Components.interfaces.nsIClipboard;
+ var clip =
+ Components.classes['@mozilla.org/widget/clipboard;1'].createInstance(clipid);
+ if (!clip)
+ return false;
+ var trans =
+ Components.classes['@mozilla.org/widget/transferable;1'].createInstance(Components.interfaces.nsITransferable);
+ if (!trans)
+ return false;
+ trans.init(getLoadContext());
+ trans.addDataFlavor('text/html');
+ var str =
+ Components.classes['@mozilla.org/supports-string;1'].createInstance(Components.interfaces.nsISupportsString);
+ var copytext = txt;
+ str.data = copytext;
+ trans.setTransferData("text/html",str,copytext.length*2);
+ if (!clip)
+ return false;
+ clip.setData(trans,null,clipid.kGlobalClipboard);
+ return true;
+}
+
+function readFromClipboard()
+{
+ var clipid = Components.interfaces.nsIClipboard;
+ var clip =
+ Components.classes['@mozilla.org/widget/clipboard;1'].createInstance(clipid);
+ if (!clip)
+ return;
+ var trans =
+ Components.classes['@mozilla.org/widget/transferable;1'].createInstance(Components.interfaces.nsITransferable);
+ if (!trans)
+ return;
+ trans.init(getLoadContext());
+ trans.addDataFlavor('text/html');
+ clip.getData(trans,clipid.kGlobalClipboard);
+ var str = new Object();
+ var strLength = new Object();
+ trans.getTransferData("text/html",str,strLength);
+ if (str)
+ str = str.value.QueryInterface(Components.interfaces.nsISupportsString);
+ if (str)
+ pastetext = str.data.substring(0,strLength.value / 2);
+ return pastetext;
+}
+
+function encodeHtmlEntities(s)
+{
+ var result = '';
+ for (var i = 0; i < s.length; i++) {
+ var c = s.charAt(i);
+ result += {'<':'&lt;', '>':'&gt;', '&':'&amp;', '"':'&quot;'}[c] || c;
+ }
+ return result;
+}
+
+function initAndRunTests()
+{
+ var source = '<p>Lorem ipsum</p>';
+ var expect = new RegExp('<html>.*charset=utf-8.*' + source + '.*</html>', 'im');
+
+ var result = copyToClipboard(source);
+ ok(result, "copied HTML data to system pasteboard");
+
+ result = readFromClipboard();
+ ok(expect.test(result), "data on system pasteboard is wrapped with charset metadata");
+
+ $("display").innerHTML =
+ '<em>source:</em> <pre>' + encodeHtmlEntities(source) + '</pre><br/>' +
+ '<em>result:</em> <pre>' + encodeHtmlEntities(result) + '</pre>';
+}
+
+ ]]>
+ </script>
+</window>
diff --git a/widget/tests/test_bug478536.xul b/widget/tests/test_bug478536.xul
new file mode 100644
index 000000000..e83ff6032
--- /dev/null
+++ b/widget/tests/test_bug478536.xul
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=478536
+-->
+<window title="Mozilla Bug 478536"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <title>Test for Bug 478536</title>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+window.open("window_bug478536.xul", "_blank",
+ "chrome,width=600,height=600");
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/test_bug485118.xul b/widget/tests/test_bug485118.xul
new file mode 100644
index 000000000..bbd5daed9
--- /dev/null
+++ b/widget/tests/test_bug485118.xul
@@ -0,0 +1,71 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=485118
+-->
+<window title="Mozilla Bug 485118"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<hbox height="300">
+ <vbox width="300">
+ <scrollbar orient="horizontal"
+ maxpos="10000"
+ pageincrement="1"
+ id="horizontal"/>
+ <scrollbar orient="horizontal"
+ maxpos="10000"
+ pageincrement="1"
+ style="-moz-appearance: scrollbar-small;"
+ id="horizontalSmall"/>
+ <hbox flex="1">
+ <scrollbar orient="vertical"
+ maxpos="10000"
+ pageincrement="1"
+ id="vertical"/>
+ <scrollbar orient="vertical"
+ maxpos="10000"
+ pageincrement="1"
+ style="-moz-appearance: scrollbar-small;"
+ id="verticalSmall"/>
+ <spacer flex="1"/>
+ </hbox>
+ </vbox>
+</hbox>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+ ["horizontal", "vertical"].forEach(function (orient) {
+ ["", "Small"].forEach(function (size) {
+ var elem = document.getElementById(orient + size);
+ var thumbRect = document.getAnonymousElementByAttribute(elem, 'sbattr', 'scrollbar-thumb').getBoundingClientRect();
+ var sizeToCheck = orient == "horizontal" ? "width" : "height";
+ // var expectedSize = size == "Small" ? 19 : 26;
+ var expectedSize = 26;
+ is(thumbRect[sizeToCheck], expectedSize, size + " scrollbar has wrong minimum " + sizeToCheck);
+ });
+ });
+ SimpleTest.finish();
+}
+window.addEventListener("load", runTest, false);
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/test_bug517396.xul b/widget/tests/test_bug517396.xul
new file mode 100644
index 000000000..18a1b8f59
--- /dev/null
+++ b/widget/tests/test_bug517396.xul
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=517396
+-->
+<window title="Mozilla Bug 517396"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(function () {
+ // this test fails on Linux, bug 526236
+ if (navigator.platform.indexOf("Lin") != -1) {
+ ok(true, "disabled on Linux");
+ SimpleTest.finish();
+ return;
+ }
+
+ var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
+ getService(Components.interfaces.nsIWindowMediator);
+ var win = wm.getMostRecentWindow("navigator:browser");
+ var oldWidth = win.outerWidth, oldHeight = win.outerHeight;
+ win.maximize();
+ var newWidth = win.outerWidth, newHeight = win.outerHeight;
+ win.moveBy(10, 0);
+ var sizeShouldHaveChanged = !navigator.platform.match(/Mac/);
+ var compFunc = sizeShouldHaveChanged ? isnot : is;
+ var not = sizeShouldHaveChanged ? "" : "not ";
+ compFunc(win.outerWidth, newWidth, "moving a maximized window should " + not + "have changed its width");
+ compFunc(win.outerHeight, newHeight, "moving a maximized window should " + not + "have changed its height");
+ win.restore();
+ is(win.outerWidth, oldWidth, "restored window has wrong width");
+ is(win.outerHeight, oldHeight, "restored window has wrong height");
+ SimpleTest.finish();
+});
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/test_bug522217.xul b/widget/tests/test_bug522217.xul
new file mode 100644
index 000000000..22aa1a061
--- /dev/null
+++ b/widget/tests/test_bug522217.xul
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=522217
+-->
+<window title="Mozilla Bug 522217"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(function () {
+ window.open("window_bug522217.xul", "_blank",
+ "chrome,resizable,width=400,height=300");
+});
+
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/test_bug538242.xul b/widget/tests/test_bug538242.xul
new file mode 100644
index 000000000..9ebab5259
--- /dev/null
+++ b/widget/tests/test_bug538242.xul
@@ -0,0 +1,55 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=538242
+-->
+<window title="Mozilla Bug 538242"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+if (navigator.platform.startsWith("Win")) {
+ SimpleTest.expectAssertions(0, 1);
+}
+
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(function () {
+ if (navigator.platform.indexOf("Lin") != -1) {
+ ok(true, "This test is disabled on Linux because it expects moving windows to be synchronous which is not guaranteed on Linux.");
+ SimpleTest.finish();
+ return;
+ }
+
+ var win = window.open("window_bug538242.xul", "_blank",
+ "chrome=1,width=400,height=300,left=100,top=100");
+ SimpleTest.waitForFocus(function () {
+ is(win.screenX, 100, "window should open at 100, 100");
+ is(win.screenY, 100, "window should open at 100, 100");
+ var [oldX, oldY] = [win.screenX, win.screenY];
+ win.moveTo(0, 0);
+ isnot(win.screenX, oldX, "window should have moved to a point near 0, 0");
+ isnot(win.screenY, oldY, "window should have moved to a point near 0, 0");
+ win.close();
+ SimpleTest.finish();
+ }, win);
+});
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/test_bug565392.html b/widget/tests/test_bug565392.html
new file mode 100644
index 000000000..da6999ec6
--- /dev/null
+++ b/widget/tests/test_bug565392.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=565392
+-->
+<head>
+ <title>Test for Bug 565392</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=565392">Mozilla Bug 565392</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 565392 **/
+
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ const Cc = SpecialPowers.Cc;
+ const Ci = SpecialPowers.Ci;
+ var ds = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties);
+ var dir1 = ds.get("ProfD", Ci.nsIFile);
+ var clipboard = Cc["@mozilla.org/widget/clipboard;1"]
+ .getService(Ci.nsIClipboard);
+
+ function getLoadContext() {
+ return SpecialPowers.wrap(window).QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsILoadContext);
+ }
+
+ function getTransferableFile(file) {
+ var transferable = Cc['@mozilla.org/widget/transferable;1']
+ .createInstance(Ci.nsITransferable);
+ transferable.init(getLoadContext());
+ transferable.setTransferData("application/x-moz-file", file, 0);
+ return transferable;
+ }
+
+ function setClipboardData(transferable) {
+ clipboard.setData(transferable, null, 1);
+ }
+
+ function getClipboardData(mime) {
+ var transferable = Cc['@mozilla.org/widget/transferable;1']
+ .createInstance(Ci.nsITransferable);
+ transferable.init(getLoadContext());
+ transferable.addDataFlavor(mime);
+ clipboard.getData(transferable, 1);
+ var data = {};
+ transferable.getTransferData(mime, data, {}) ;
+ return data;
+ }
+
+setClipboardData(getTransferableFile(dir1))
+is(clipboard.hasDataMatchingFlavors(["application/x-moz-file"], 1,1), true);
+var data = getClipboardData("application/x-moz-file");
+var file = data.value.QueryInterface(Ci.nsIFile);
+ok(file.isDirectory(), true);
+is(file.target, dir1.target, true);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/widget/tests/test_bug586713.xul b/widget/tests/test_bug586713.xul
new file mode 100644
index 000000000..e91eb7931
--- /dev/null
+++ b/widget/tests/test_bug586713.xul
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Native menu system tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+window.open("bug586713_window.xul", "bug586713_window",
+ "chrome,width=600,height=600");
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/test_bug593307.xul b/widget/tests/test_bug593307.xul
new file mode 100644
index 000000000..0b6e4d4a1
--- /dev/null
+++ b/widget/tests/test_bug593307.xul
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=593307
+-->
+<window title="Mozilla Bug 593307"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function finish() {
+ offscreenWindow.close();
+ SimpleTest.finish();
+}
+
+var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIWebNavigation)
+ .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
+ .rootTreeItem
+ .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIDOMWindow);
+
+var offscreenWindow = mainWindow.openDialog("window_bug593307_offscreen.xul", "",
+ "dialog=no,chrome,width=200,height=200,screenX=-3000,screenY=-3000",
+ SimpleTest, finish);
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/test_bug596600.xul b/widget/tests/test_bug596600.xul
new file mode 100644
index 000000000..0468f7d4d
--- /dev/null
+++ b/widget/tests/test_bug596600.xul
@@ -0,0 +1,177 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Native mouse event tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+const NSMouseMoved = 5;
+
+var gLeftWindow, gRightWindow, gIFrame;
+var gExpectedEvents = [];
+
+function moveMouseTo(x, y, andThen) {
+ var utils = gLeftWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIDOMWindowUtils);
+ utils.sendNativeMouseEvent(x, y, NSMouseMoved, 0, gLeftWindow.documentElement);
+ SimpleTest.executeSoon(andThen);
+}
+
+function openWindows() {
+ gLeftWindow = open('empty_window.xul', '_blank', 'chrome,screenX=50,screenY=50,width=200,height=200');
+ SimpleTest.waitForFocus(function () {
+ gRightWindow = open('empty_window.xul', '', 'chrome,screenX=300,screenY=50,width=200,height=200');
+ SimpleTest.waitForFocus(attachIFrameToRightWindow, gRightWindow);
+ }, gLeftWindow);
+}
+
+function attachIFrameToRightWindow() {
+ gIFrame = gLeftWindow.document.createElementNS(XUL_NS, "iframe");
+ gIFrame.setAttribute("type", "content");
+ gIFrame.setAttribute("clickthrough", "never");
+ gIFrame.setAttribute("src", "data:text/html,<!DOCTYPE html>Content page");
+ gIFrame.style.width = "100px";
+ gIFrame.style.height = "100px";
+ gIFrame.style.margin = "50px";
+ gLeftWindow.document.documentElement.appendChild(gIFrame);
+ gIFrame.contentWindow.addEventListener("load", function (e) {
+ gIFrame.removeEventListener("load", arguments.callee, false);
+ test1();
+ }, false);
+}
+
+function test1() {
+ // gRightWindow is active, gLeftWindow is inactive.
+ moveMouseTo(0, 0, function () {
+ var expectMouseOver = false, expectMouseOut = false;
+ function mouseOverListener(e) {
+ ok(expectMouseOver, "Got expected mouseover at " + e.screenX + ", " + e.screenY);
+ expectMouseOver = false;
+ }
+ function mouseOutListener(e) {
+ ok(expectMouseOut, "Got expected mouseout at " + e.screenX + ", " + e.screenY);
+ expectMouseOut = false;
+ }
+ gLeftWindow.addEventListener("mouseover", mouseOverListener, false);
+ gLeftWindow.addEventListener("mouseout", mouseOutListener, false);
+
+ // Move into the left window
+ expectMouseOver = true;
+ moveMouseTo(80, 80, function () {
+ ok(!expectMouseOver, "Should have got mouseover event");
+
+ // Move over the iframe, which has clickthrough="never".
+ expectMouseOut = true;
+ moveMouseTo(150, 150, function () {
+ ok (!expectMouseOut, "Should have got mouseout event");
+ gLeftWindow.removeEventListener("mouseover", mouseOverListener, false);
+ gLeftWindow.removeEventListener("mouseout", mouseOutListener, false);
+ test2();
+ });
+ });
+ });
+}
+
+function test2() {
+ // Make the iframe cover the whole window.
+ gIFrame.style.margin = "0";
+ gIFrame.style.width = gIFrame.style.height = "200px";
+
+ // Add a box to the iframe at the left edge.
+ var doc = gIFrame.contentDocument;
+ var box = doc.createElement("div");
+ box.setAttribute("id", "box");
+ box.style.position = "absolute";
+ box.style.left = "0";
+ box.style.top = "50px";
+ box.style.width = "100px";
+ box.style.height = "100px";
+ box.style.backgroundColor = "green";
+ doc.body.appendChild(box);
+
+ ok(!box.matches(":hover"), "Box shouldn't be hovered (since the mouse isn't over it and since it's in a non-clickthrough iframe in a background window)");
+
+ // A function to waitForFocus and then wait for synthetic mouse
+ // events to happen. Note that those happen off the refresh driver,
+ // and happen after animation frame requests.
+ function changeFocusAndAwaitSyntheticMouse(callback, winToFocus,
+ elementToWatchForMouseEventOn) {
+ function mouseWatcher() {
+ elementToWatchForMouseEventOn.removeEventListener("mouseover",
+ mouseWatcher,
+ false);
+ elementToWatchForMouseEventOn.removeEventListener("mouseout",
+ mouseWatcher,
+ false);
+ SimpleTest.executeSoon(callback);
+ }
+ elementToWatchForMouseEventOn.addEventListener("mouseover",
+ mouseWatcher,
+ false);
+ elementToWatchForMouseEventOn.addEventListener("mouseout",
+ mouseWatcher,
+ false);
+ // Just pass a dummy function to waitForFocus; the mouseout/over listener
+ // will actually handle things for us.
+ SimpleTest.waitForFocus(function() {}, winToFocus);
+ }
+
+ // Move the mouse over the box.
+ moveMouseTo(100, 150, function () {
+ ok(!box.matches(":hover"), "Box shouldn't be hovered (since it's in a non-clickthrough iframe in a background window)");
+ // Activate the left window.
+ changeFocusAndAwaitSyntheticMouse(function () {
+ ok(gIFrame.matches(":hover"), "iframe should be hovered");
+ ok(box.matches(":hover"), "Box should be hovered");
+ // De-activate the window (by activating the right window).
+ changeFocusAndAwaitSyntheticMouse(function () {
+ ok(!gIFrame.matches(":hover"), "iframe shouldn't be hovered");
+ ok(!box.matches(":hover"), "Box shouldn't be hovered");
+ // Re-activate it.
+ changeFocusAndAwaitSyntheticMouse(function () {
+ ok(gIFrame.matches(":hover"), "iframe should be hovered");
+ ok(box.matches(":hover"), "Box should be hovered");
+ // Unhover box and iframe by moving the mouse outside the window.
+ moveMouseTo(0, 150, function () {
+ const isOSXSnowLeopard = navigator.userAgent.indexOf("Mac OS X 10.6") != -1;
+ if (!isOSXSnowLeopard) {
+ ok(!gIFrame.matches(":hover"), "iframe shouldn't be hovered");
+ ok(!box.matches(":hover"), "box shouldn't be hovered");
+ }
+ finalize();
+ });
+ }, gLeftWindow, box);
+ }, gRightWindow, box);
+ }, gLeftWindow, box);
+ });
+}
+
+function finalize() {
+ gRightWindow.close();
+ gLeftWindow.close();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(openWindows);
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/test_bug673301.xul b/widget/tests/test_bug673301.xul
new file mode 100644
index 000000000..a5736b86f
--- /dev/null
+++ b/widget/tests/test_bug673301.xul
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"/>
+<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<p id="display"></p>
+<div id="content" style="display: none"/>
+</body>
+
+<script type="application/javascript">
+function getLoadContext() {
+ const Ci = Components.interfaces;
+ return window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsILoadContext);
+}
+
+var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"]
+ .getService(Components.interfaces.nsIClipboard);
+
+var transferable = Components.classes['@mozilla.org/widget/transferable;1']
+ .createInstance(Components.interfaces.nsITransferable);
+transferable.init(getLoadContext());
+
+transferable.addDataFlavor("text/unicode");
+transferable.setTransferData("text/unicode", document, 4);
+
+clipboard.setData(transferable, null, Components.interfaces.nsIClipboard.kGlobalClipboard);
+
+transferable.setTransferData("text/unicode", null, 0);
+
+SimpleTest.ok(true, "Didn't crash setting non-text data for text/unicode type");
+</script>
+</window>
diff --git a/widget/tests/test_bug760802.xul b/widget/tests/test_bug760802.xul
new file mode 100644
index 000000000..c79be785e
--- /dev/null
+++ b/widget/tests/test_bug760802.xul
@@ -0,0 +1,91 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=760802
+-->
+<window title="Mozilla Bug 760802"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=760802"
+ target="_blank">Mozilla Bug 760802</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"/>
+ <iframe id="iframe_not_editable" width="300" height="150"
+ src="data:text/html,&lt;html&gt;&lt;body&gt;&lt;/body&gt;&lt;/html&gt;"/><br/>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+SimpleTest.waitForExplicitFinish();
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+function getBaseWindowInterface(win) {
+ return win.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem)
+ .treeOwner
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .nsIBaseWindow;
+}
+
+function getBaseWindowInterfaceFromDocShell(win) {
+ return win.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell)
+ .QueryInterface(Ci.nsIBaseWindow);
+}
+
+function shouldThrowException(fun, exception) {
+ try {
+ fun.call();
+ return false;
+ } catch (e) {
+ $("display").innerHTML += "<br/>OK thrown: "+e.message;
+ return (e instanceof Components.Exception &&
+ e.result === exception)
+ }
+}
+function doesntThrowException(fun) {
+ return !shouldThrowException(fun);
+}
+
+var baseWindow = getBaseWindowInterface(this);
+var nativeHandle = baseWindow.nativeHandle;
+$("display").innerHTML = "found nativeHandle for this window: "+nativeHandle;
+
+var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
+var win = wm.getMostRecentWindow("navigator:browser");
+var docShell = getBaseWindowInterfaceFromDocShell(win);
+
+ok(
+ shouldThrowException(function(){docShell.nativeHandle;},
+ Components.results.NS_ERROR_NOT_IMPLEMENTED),
+ "nativeHandle should not be implemented for nsDocShell"
+);
+
+ok(typeof(nativeHandle) === "string", "nativeHandle should be a string");
+ok(nativeHandle.match(/^0x[0-9a-f]+$/), "nativeHandle should have a memory address format");
+
+var iWin = document.getElementById("iframe_not_editable").contentWindow;
+is(getBaseWindowInterface(iWin).nativeHandle, nativeHandle,
+ "the nativeHandle of an iframe should be its parent's nativeHandle");
+
+var dialog = win.openDialog("data:text/plain,this is an active window.", "_blank",
+ "chrome,dialog=yes,width=100,height=100");
+
+isnot(getBaseWindowInterface(dialog).nativeHandle, "",
+ "the nativeHandle of a dialog should not be empty");
+
+dialog.close();
+
+todo(false, "the nativeHandle of a window without a mainWidget should be empty"); // how to build a window without a mainWidget ?
+
+SimpleTest.finish();
+ ]]></script>
+</window>
diff --git a/widget/tests/test_chrome_context_menus_win.xul b/widget/tests/test_chrome_context_menus_win.xul
new file mode 100644
index 000000000..575e7743d
--- /dev/null
+++ b/widget/tests/test_chrome_context_menus_win.xul
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+<script type="application/javascript" src="utils.js"></script>
+<script>
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ SimpleTest.waitForExplicitFinish();
+
+ var w = window.open('chrome_context_menus_win.xul', '_blank', 'chrome,resizable=yes,width=600,height=600');
+
+ function done()
+ {
+ w.close();
+ SimpleTest.finish();
+ }
+</script>
+<body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;" />
+</window>
diff --git a/widget/tests/test_clipboard.xul b/widget/tests/test_clipboard.xul
new file mode 100644
index 000000000..19a55714d
--- /dev/null
+++ b/widget/tests/test_clipboard.xul
@@ -0,0 +1,80 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=948065
+-->
+<window title="Mozilla Bug 948065"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="initAndRunTests()">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ </body>
+
+ <!-- test code goes here -->
+ <script class="testbody" type="application/javascript">
+ <![CDATA[
+
+ /** Test for Bug 948065 **/
+
+ const Cc = Components.classes;
+ const Ci = Components.interfaces;
+
+ const kIsMac = navigator.platform.indexOf("Mac") == 0;
+
+ function getLoadContext() {
+ return window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsILoadContext);
+ }
+
+ // Get clipboard data to paste.
+ function paste(clipboard) {
+ let trans = Cc['@mozilla.org/widget/transferable;1']
+ .createInstance(Ci.nsITransferable);
+ trans.init(getLoadContext());
+ trans.addDataFlavor("text/unicode");
+ clipboard.getData(trans, Ci.nsIClipboard.kGlobalClipboard);
+ let str = {};
+ let length = {};
+ try {
+ trans.getTransferData('text/unicode', str, length);
+ } catch (e) {
+ str = '';
+ }
+ if (str) {
+ str = str.value.QueryInterface(Ci.nsISupportsString);
+ if (str) {
+ str = str.data.substring(0, length.value / 2);
+ }
+ }
+ return str;
+ }
+
+ function initAndRunTests() {
+ let clipboard = Cc['@mozilla.org/widget/clipboard;1']
+ .getService(Ci.nsIClipboard);
+
+ // Test copy.
+ const data = "random number: " + Math.random();
+ let helper = Cc['@mozilla.org/widget/clipboardhelper;1']
+ .getService(Ci.nsIClipboardHelper);
+ helper.copyString(data);
+ is(paste(clipboard), data, 'Data was successfully copied.');
+
+ // Test emptyClipboard, disabled for OSX because bug 666254
+ if (!kIsMac) {
+ clipboard.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard);
+ is(paste(clipboard), '', 'Data was successfully cleared.');
+ }
+ }
+
+ ]]>
+ </script>
+</window>
diff --git a/widget/tests/test_composition_text_querycontent.xul b/widget/tests/test_composition_text_querycontent.xul
new file mode 100644
index 000000000..e48a1b14a
--- /dev/null
+++ b/widget/tests/test_composition_text_querycontent.xul
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Testing composition, text and query content events"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+// If setting selection with eSetSelection event whose range is larger than
+// the actual range, hits "Can only call this on frames that have been reflowed:
+// '!(GetStateBits() & NS_FRAME_FIRST_REFLOW) || (GetParent()->GetStateBits() &
+// NS_FRAME_TOO_DEEP_IN_FRAME_TREE)'" in nsTextFrame.cpp.
+// Strangely, this doesn't occur with RDP on Windows.
+SimpleTest.expectAssertions(0, 3);
+SimpleTest.waitForExplicitFinish();
+window.open("window_composition_text_querycontent.xul", "_blank",
+ "chrome,width=600,height=600");
+
+]]>
+</script>
+</window>
diff --git a/widget/tests/test_imestate.html b/widget/tests/test_imestate.html
new file mode 100644
index 000000000..fe5a3cce2
--- /dev/null
+++ b/widget/tests/test_imestate.html
@@ -0,0 +1,1529 @@
+<html style="ime-mode: disabled;">
+<head>
+ <title>Test for IME state controling</title>
+ <script type="text/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="utils.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body onload="setTimeout(runTests, 0);" style="ime-mode: disabled;">
+<script>
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+</script>
+<div id="display" style="ime-mode: disabled;">
+ <!-- input elements -->
+ <input type="text" id="text"/><br/>
+ <input type="text" id="text_readonly" readonly="readonly"/><br/>
+ <input type="password" id="password"/><br/>
+ <input type="password" id="password_readonly" readonly="readonly"/><br/>
+ <input type="checkbox" id="checkbox"/><br/>
+ <input type="radio" id="radio"/><br/>
+ <input type="submit" id="submit"/><br/>
+ <input type="reset" id="reset"/><br/>
+ <input type="file" id="file"/><br/>
+ <input type="button" id="ibutton"/><br/>
+ <input type="image" id="image" alt="image"/><br/>
+
+ <!-- html5 input elements -->
+ <input type="url" id="url"/><br/>
+ <input type="email" id="email"/><br/>
+ <input type="search" id="search"/><br/>
+ <input type="tel" id="tel"/><br/>
+ <input type="number" id="number"/><br/>
+
+ <!-- form controls -->
+ <button id="button">button</button><br/>
+ <textarea id="textarea">textarea</textarea><br/>
+ <textarea id="textarea_readonly" readonly="readonly">textarea[readonly]</textarea><br/>
+ <select id="select">
+ <option value="option" selected="selected"/>
+ </select><br/>
+ <select id="select_multiple" multiple="multiple">
+ <option value="option" selected="selected"/>
+ </select><br/>
+ <isindex id="isindex" prompt="isindex"/><br/>
+
+ <!-- a element -->
+ <a id="a_href" href="about:blank">a[href]</a><br/>
+
+ <!-- ime-mode test -->
+ <input type="text" id="ime_mode_auto" style="ime-mode: auto;"/><br/>
+ <input type="text" id="ime_mode_normal" style="ime-mode: normal;"/><br/>
+ <input type="text" id="ime_mode_active" style="ime-mode: active;"/><br/>
+ <input type="text" id="ime_mode_inactive" style="ime-mode: inactive;"/><br/>
+ <input type="text" id="ime_mode_disabled" style="ime-mode: disabled;"/><br/>
+
+ <input type="text" id="ime_mode_auto_url" style="ime-mode: auto;"/><br/>
+ <input type="text" id="ime_mode_normal_url" style="ime-mode: normal;"/><br/>
+ <input type="text" id="ime_mode_active_url" style="ime-mode: active;"/><br/>
+ <input type="text" id="ime_mode_inactive_url" style="ime-mode: inactive;"/><br/>
+ <input type="text" id="ime_mode_disabled_url" style="ime-mode: disabled;"/><br/>
+
+ <input type="text" id="ime_mode_auto_email" style="ime-mode: auto;"/><br/>
+ <input type="text" id="ime_mode_normal_email" style="ime-mode: normal;"/><br/>
+ <input type="text" id="ime_mode_active_email" style="ime-mode: active;"/><br/>
+ <input type="text" id="ime_mode_inactive_email" style="ime-mode: inactive;"/><br/>
+ <input type="text" id="ime_mode_disabled_email" style="ime-mode: disabled;"/><br/>
+
+ <input type="text" id="ime_mode_auto_search" style="ime-mode: auto;"/><br/>
+ <input type="text" id="ime_mode_normal_search" style="ime-mode: normal;"/><br/>
+ <input type="text" id="ime_mode_active_search" style="ime-mode: active;"/><br/>
+ <input type="text" id="ime_mode_inactive_search" style="ime-mode: inactive;"/><br/>
+ <input type="text" id="ime_mode_disabled_search" style="ime-mode: disabled;"/><br/>
+
+ <input type="text" id="ime_mode_auto_tel" style="ime-mode: auto;"/><br/>
+ <input type="text" id="ime_mode_normal_tel" style="ime-mode: normal;"/><br/>
+ <input type="text" id="ime_mode_active_tel" style="ime-mode: active;"/><br/>
+ <input type="text" id="ime_mode_inactive_tel" style="ime-mode: inactive;"/><br/>
+ <input type="text" id="ime_mode_disabled_tel" style="ime-mode: disabled;"/><br/>
+
+ <input type="text" id="ime_mode_auto_number" style="ime-mode: auto;"/><br/>
+ <input type="text" id="ime_mode_normal_number" style="ime-mode: normal;"/><br/>
+ <input type="text" id="ime_mode_active_number" style="ime-mode: active;"/><br/>
+ <input type="text" id="ime_mode_inactive_number" style="ime-mode: inactive;"/><br/>
+ <input type="text" id="ime_mode_disabled_number" style="ime-mode: disabled;"/><br/>
+
+ <input type="password" id="ime_mode_auto_p" style="ime-mode: auto;"/><br/>
+ <input type="password" id="ime_mode_normal_p" style="ime-mode: normal;"/><br/>
+ <input type="password" id="ime_mode_active_p" style="ime-mode: active;"/><br/>
+ <input type="password" id="ime_mode_inactive_p" style="ime-mode: inactive;"/><br/>
+ <input type="password" id="ime_mode_disabled_p" style="ime-mode: disabled;"/><br/>
+ <textarea id="ime_mode_auto_t" style="ime-mode: auto;">textarea</textarea><br/>
+ <textarea id="ime_mode_normal_t" style="ime-mode: normal;">textarea</textarea><br/>
+ <textarea id="ime_mode_active_t" style="ime-mode: active;">textarea</textarea><br/>
+ <textarea id="ime_mode_inactive_t" style="ime-mode: inactive;">textarea</textarea><br/>
+ <textarea id="ime_mode_disabled_t" style="ime-mode: disabled;">textarea</textarea><br/>
+
+ <!-- plugin -->
+ <object type="application/x-test" id="plugin"></object><br/>
+
+ <!-- contenteditable editor -->
+ <div id="contenteditableEditor" contenteditable="true"></div>
+
+ <!-- designMode editor -->
+ <iframe id="designModeEditor"
+ onload="document.getElementById('designModeEditor').contentDocument.designMode = 'on';"
+ src="data:text/html,<html><body></body></html>"></iframe><br/>
+</div>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+
+<script class="testbody" type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+function hitEventLoop(aFunc, aTimes)
+{
+ if (--aTimes) {
+ setTimeout(hitEventLoop, 0, aFunc, aTimes);
+ } else {
+ setTimeout(aFunc, 20);
+ }
+}
+
+var gUtils = window.
+ QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIDOMWindowUtils);
+var gFM = Components.classes["@mozilla.org/focus-manager;1"].
+ getService(Components.interfaces.nsIFocusManager);
+const kIMEEnabledSupported = navigator.platform.indexOf("Mac") == 0 ||
+ navigator.platform.indexOf("Win") == 0 ||
+ navigator.platform.indexOf("Linux") == 0;
+
+// We support to control IME open state on Windows and Mac actually. However,
+// we cannot test it on Mac if the current keyboard layout is not CJK. And also
+// we cannot test it on Win32 if the system didn't be installed IME. So,
+// currently we should not run the open state testing.
+const kIMEOpenSupported = false;
+
+function runBasicTest(aIsEditable, aInDesignMode, aDescription)
+{
+ var onIMEFocusBlurHandler = null;
+ var TIPCallback = function(aTIP, aNotification) {
+ switch (aNotification.type) {
+ case "request-to-commit":
+ aTIP.commitComposition();
+ break;
+ case "request-to-cancel":
+ aTIP.cancelComposition();
+ break;
+ case "notify-focus":
+ case "notify-blur":
+ if (onIMEFocusBlurHandler) {
+ onIMEFocusBlurHandler(aNotification);
+ }
+ break;
+ }
+ return true;
+ };
+
+ var TIP = Components.classes["@mozilla.org/text-input-processor;1"]
+ .createInstance(Components.interfaces.nsITextInputProcessor);
+ if (!TIP.beginInputTransactionForTests(window, TIPCallback)) {
+ ok(false, "runBasicTest(): failed to begin input transaction");
+ return;
+ }
+
+ function test(aTest)
+ {
+ function moveFocus(aTest, aFocusEventHandler)
+ {
+ if (aInDesignMode) {
+ if (document.activeElement) {
+ document.activeElement.blur();
+ }
+ } else if (aIsEditable) {
+ document.getElementById("display").focus();
+ } else if (aTest.expectedEnabled == gUtils.IME_STATUS_ENABLED) {
+ document.getElementById("password").focus();
+ } else {
+ document.getElementById("text").focus();
+ }
+ var previousFocusedElement = gFM.focusedElement;
+ var element = document.getElementById(aTest.id);
+ var focusEventTarget = element;
+ var subDocument = null;
+ if (element.contentDocument) {
+ focusEventTarget = element.contentDocument;
+ subDocument = element.contentDocument;
+ element = element.contentDocument.documentElement;
+ }
+
+ focusEventTarget.addEventListener("focus", aFocusEventHandler, true);
+ onIMEFocusBlurHandler = aFocusEventHandler;
+
+ element.focus();
+
+ focusEventTarget.removeEventListener("focus", aFocusEventHandler, true);
+ onIMEFocusBlurHandler = null;
+
+ var focusedElement = gFM.focusedElement;
+ if (focusedElement) {
+ var bindingParent = document.getBindingParent(focusedElement);
+ if (bindingParent) {
+ focusedElement = bindingParent;
+ }
+ }
+ if (aTest.focusable) {
+ is(focusedElement, element,
+ aDescription + ": " + aTest.description + ", focus didn't move");
+ return (element == focusedElement);
+ }
+ is(focusedElement, previousFocusedElement,
+ aDescription + ": " + aTest.description + ", focus moved as unexpected");
+ return (previousFocusedElement == focusedElement);
+ }
+
+ function testOpened(aTest, aOpened)
+ {
+ document.getElementById("text").focus();
+ gUtils.IMEIsOpen = aOpened;
+ if (!moveFocus(aTest)) {
+ return;
+ }
+ var message = aDescription + ": " + aTest.description +
+ ", wrong opened state";
+ is(gUtils.IMEIsOpen,
+ aTest.changeOpened ? aTest.expectedOpened : aOpened, message);
+ }
+
+ // IME Enabled state testing
+ var enabled = gUtils.IME_STATUS_ENABLED;
+ if (kIMEEnabledSupported) {
+ var focusEventCount = 0;
+ var IMEReceivesFocus = 0;
+ var IMEReceivesBlur = 0;
+ var IMEHasFocus = false;
+
+ function onFocus(aEvent)
+ {
+ switch (aEvent.type) {
+ case "focus":
+ focusEventCount++;
+ is(gUtils.IMEStatus, aTest.expectedEnabled,
+ aDescription + ": " + aTest.description + ", wrong enabled state at focus event");
+ break;
+ case "notify-focus":
+ IMEReceivesFocus++;
+ IMEHasFocus = true;
+ is(gUtils.IMEStatus, aTest.expectedEnabled,
+ aDescription + ": " + aTest.description +
+ ", IME should receive a focus notification after IME state is updated");
+ break;
+ case "notify-blur":
+ IMEReceivesBlur++;
+ IMEHasFocus = false;
+ var changingStatus = !(aIsEditable && aTest.expectedEnabled == gUtils.IME_STATUS_ENABLED);
+ if (aTest.toDesignModeEditor) {
+ is(gUtils.IME_STATUS_ENABLED, aTest.expectedEnabled,
+ aDescription + ": " + aTest.description +
+ ", IME should receive a blur notification after IME state is updated");
+ } else if (changingStatus) {
+ isnot(gUtils.IMEStatus, aTest.expectedEnabled,
+ aDescription + ": " + aTest.description +
+ ", IME should receive a blur notification before IME state is updated");
+ } else {
+ is(gUtils.IMEStatus, aTest.expectedEnabled,
+ aDescription + ": " + aTest.description +
+ ", IME should receive a blur notification and its context has expected IME state if the state isn't being changed");
+ }
+ break;
+ }
+ }
+
+ if (!moveFocus(aTest, onFocus)) {
+ return;
+ }
+
+ if (aTest.focusable) {
+ if (!aTest.focusEventNotFired) {
+ ok(focusEventCount > 0,
+ aDescription + ": " + aTest.description + ", focus event is never fired");
+ if (aTest.expectedEnabled == gUtils.IME_STATUS_ENABLED || aTest.expectedEnabled == gUtils.IME_STATUS_PASSWORD) {
+ ok(IMEReceivesFocus > 0,
+ aDescription + ": " + aTest.description + ", IME should receive a focus notification");
+ if (aInDesignMode && !aTest.toDesignModeEditor) {
+ is(IMEReceivesBlur, 0,
+ aDescription + ": " + aTest.description +
+ ", IME shouldn't receive a blur notification in designMode since focus isn't moved from another editor");
+ } else {
+ ok(IMEReceivesBlur > 0,
+ aDescription + ": " + aTest.description +
+ ", IME should receive a blur notification for the previous focused editor");
+ }
+ ok(IMEHasFocus,
+ aDescription + ": " + aTest.description +
+ ", IME should have focus right now");
+ } else {
+ is(IMEReceivesFocus, 0,
+ aDescription + ": " + aTest.description +
+ ", IME shouldn't receive a focus notification");
+ ok(IMEReceivesBlur > 0,
+ aDescription + ": " + aTest.description +
+ ", IME should receive a blur notification");
+ ok(!IMEHasFocus,
+ aDescription + ": " + aTest.description +
+ ", IME shouldn't have focus right now");
+ }
+ } else {
+ todo(focusEventCount > 0,
+ aDescription + ": " + aTest.description + ", focus event should be fired");
+ }
+ } else {
+ is(IMEReceivesFocus, 0,
+ aDescription + ": " + aTest.description +
+ ", IME shouldn't receive a focus notification at testing non-focusable element");
+ is(IMEReceivesBlur, 0,
+ aDescription + ": " + aTest.description +
+ ", IME shouldn't receive a blur notification at testing non-focusable element");
+ }
+
+ enabled = gUtils.IMEStatus;
+ inputtype = gUtils.focusedInputType;
+ is(enabled, aTest.expectedEnabled,
+ aDescription + ": " + aTest.description + ", wrong enabled state");
+ if (aTest.expectedType && !aInDesignMode) {
+ is(inputtype, aTest.expectedType,
+ aDescription + ": " + aTest.description + ", wrong input type");
+ } else if (aInDesignMode) {
+ is(inputtype, "",
+ aDescription + ": " + aTest.description + ", wrong input type")
+ }
+ }
+
+ if (!kIMEOpenSupported || enabled != gUtils.IME_STATUS_ENABLED ||
+ aTest.expectedEnabled != gUtils.IME_STATUS_ENABLED) {
+ return;
+ }
+
+ // IME Open state testing
+ testOpened(aTest, false);
+ testOpened(aTest, true);
+ }
+
+ if (kIMEEnabledSupported) {
+ // make sure there is an active element
+ document.getElementById("text").focus();
+ document.activeElement.blur();
+ is(gUtils.IMEStatus,
+ aInDesignMode ? gUtils.IME_STATUS_ENABLED : gUtils.IME_STATUS_DISABLED,
+ aDescription + ": unexpected enabled state when no element has focus");
+ }
+
+ // Form controls except text editable elements are "disable" in normal
+ // condition, however, if they are editable, they are "enabled".
+ // XXX Probably there are some bugs: If the form controls editable, they
+ // shouldn't be focusable.
+ const kEnabledStateOnNonEditableElement =
+ (aInDesignMode || aIsEditable) ? gUtils.IME_STATUS_ENABLED :
+ gUtils.IME_STATUS_DISABLED;
+ const kEnabledStateOnPasswordField =
+ aInDesignMode ? gUtils.IME_STATUS_ENABLED : gUtils.IME_STATUS_PASSWORD;
+ const kEnabledStateOnReadonlyField =
+ aInDesignMode ? gUtils.IME_STATUS_ENABLED : gUtils.IME_STATUS_DISABLED;
+ const kTests = [
+ { id: "text",
+ description: "input[type=text]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ expectedType: "text" },
+ { id: "text_readonly",
+ description: "input[type=text][readonly]",
+ focusable: !aInDesignMode,
+ expectedEnabled: kEnabledStateOnReadonlyField },
+ { id: "password",
+ description: "input[type=password]",
+ focusable: !aInDesignMode,
+ expectedEnabled: kEnabledStateOnPasswordField,
+ expectedType: "password" },
+ { id: "password_readonly",
+ description: "input[type=password][readonly]",
+ focusable: !aInDesignMode,
+ expectedEnabled: kEnabledStateOnReadonlyField },
+ { id: "checkbox",
+ description: "input[type=checkbox]",
+ focusable: !aInDesignMode,
+ focusEventNotFired: aIsEditable && !aInDesignMode,
+ expectedEnabled: kEnabledStateOnNonEditableElement },
+ { id: "radio",
+ description: "input[type=radio]",
+ focusable: !aInDesignMode,
+ focusEventNotFired: aIsEditable && !aInDesignMode,
+ expectedEnabled: kEnabledStateOnNonEditableElement },
+ { id: "submit",
+ description: "input[type=submit]",
+ focusable: !aInDesignMode,
+ expectedEnabled: kEnabledStateOnNonEditableElement },
+ { id: "reset",
+ description: "input[type=reset]",
+ focusable: !aInDesignMode,
+ expectedEnabled: kEnabledStateOnNonEditableElement },
+ { id: "file",
+ description: "input[type=file]",
+ focusable: !aInDesignMode,
+ focusEventNotFired: aIsEditable && !aInDesignMode,
+ expectedEnabled: kEnabledStateOnNonEditableElement },
+ { id: "button",
+ description: "input[type=button]",
+ focusable: !aInDesignMode,
+ expectedEnabled: kEnabledStateOnNonEditableElement },
+ { id: "image",
+ description: "input[type=image]",
+ focusable: !aInDesignMode,
+ expectedEnabled: kEnabledStateOnNonEditableElement },
+ { id: "url",
+ description: "input[type=url]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ expectedType: "url" },
+ { id: "email",
+ description: "input[type=email]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ expectedType: "email" },
+ { id: "search",
+ description: "input[type=search]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ expectedType: "search" },
+ { id: "tel",
+ description: "input[type=tel]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ expectedType: "tel" },
+ { id: "number",
+ description: "input[type=number]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ expectedType: "number" },
+
+ // form controls
+ { id: "button",
+ description: "button",
+ focusable: !aInDesignMode,
+ expectedEnabled: kEnabledStateOnNonEditableElement },
+ { id: "textarea",
+ description: "textarea",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED },
+ { id: "textarea_readonly",
+ description: "textarea[readonly]",
+ focusable: !aInDesignMode,
+ expectedEnabled: kEnabledStateOnReadonlyField },
+ { id: "select",
+ description: "select (dropdown list)",
+ focusable: !aInDesignMode,
+ focusEventNotFired: aIsEditable && !aInDesignMode,
+ expectedEnabled: kEnabledStateOnNonEditableElement },
+ { id: "select_multiple",
+ description: "select (list box)",
+ focusable: !aInDesignMode,
+ focusEventNotFired: aIsEditable && !aInDesignMode,
+ expectedEnabled: kEnabledStateOnNonEditableElement },
+
+ // a element
+ { id: "a_href",
+ description: "a[href]",
+ focusable: !aIsEditable && !aInDesignMode,
+ expectedEnabled: kEnabledStateOnNonEditableElement },
+
+ // ime-mode
+ { id: "ime_mode_auto",
+ description: "input[type=text][style=\"ime-mode: auto;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED },
+ { id: "ime_mode_normal",
+ description: "input[type=text][style=\"ime-mode: normal;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED },
+ { id: "ime_mode_active",
+ description: "input[type=text][style=\"ime-mode: active;\"]",
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ focusable: !aInDesignMode,
+ changeOpened: true, expectedOpened: true },
+ { id: "ime_mode_inactive",
+ description: "input[type=text][style=\"ime-mode: inactive;\"]",
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ focusable: !aInDesignMode,
+ changeOpened: true, expectedOpened: false },
+ { id: "ime_mode_disabled",
+ description: "input[type=text][style=\"ime-mode: disabled;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: kEnabledStateOnPasswordField },
+
+ { id: "ime_mode_auto_url",
+ description: "input[type=url][style=\"ime-mode: auto;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED },
+ { id: "ime_mode_normal_url",
+ description: "input[type=url][style=\"ime-mode: normal;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED },
+ { id: "ime_mode_active_url",
+ description: "input[type=url][style=\"ime-mode: active;\"]",
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ focusable: !aInDesignMode,
+ changeOpened: true, expectedOpened: true },
+ { id: "ime_mode_inactive_url",
+ description: "input[type=url][style=\"ime-mode: inactive;\"]",
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ focusable: !aInDesignMode,
+ changeOpened: true, expectedOpened: false },
+ { id: "ime_mode_disabled_url",
+ description: "input[type=url][style=\"ime-mode: disabled;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: kEnabledStateOnPasswordField },
+
+ { id: "ime_mode_auto_email",
+ description: "input[type=email][style=\"ime-mode: auto;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED },
+ { id: "ime_mode_normal_email",
+ description: "input[type=email][style=\"ime-mode: normal;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED },
+ { id: "ime_mode_active_email",
+ description: "input[type=email][style=\"ime-mode: active;\"]",
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ focusable: !aInDesignMode,
+ changeOpened: true, expectedOpened: true },
+ { id: "ime_mode_inactive_email",
+ description: "input[type=email][style=\"ime-mode: inactive;\"]",
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ focusable: !aInDesignMode,
+ changeOpened: true, expectedOpened: false },
+ { id: "ime_mode_disabled_email",
+ description: "input[type=email][style=\"ime-mode: disabled;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: kEnabledStateOnPasswordField },
+
+ { id: "ime_mode_auto_search",
+ description: "input[type=search][style=\"ime-mode: auto;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED },
+ { id: "ime_mode_normal_search",
+ description: "input[type=search][style=\"ime-mode: normal;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED },
+ { id: "ime_mode_active_search",
+ description: "input[type=search][style=\"ime-mode: active;\"]",
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ focusable: !aInDesignMode,
+ changeOpened: true, expectedOpened: true },
+ { id: "ime_mode_inactive_search",
+ description: "input[type=search][style=\"ime-mode: inactive;\"]",
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ focusable: !aInDesignMode,
+ changeOpened: true, expectedOpened: false },
+ { id: "ime_mode_disabled_search",
+ description: "input[type=search][style=\"ime-mode: disabled;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: kEnabledStateOnPasswordField },
+
+ { id: "ime_mode_auto_tel",
+ description: "input[type=tel][style=\"ime-mode: auto;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED },
+ { id: "ime_mode_normal_tel",
+ description: "input[type=tel][style=\"ime-mode: normal;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED },
+ { id: "ime_mode_active_tel",
+ description: "input[type=tel][style=\"ime-mode: active;\"]",
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ focusable: !aInDesignMode,
+ changeOpened: true, expectedOpened: true },
+ { id: "ime_mode_inactive_tel",
+ description: "input[type=tel][style=\"ime-mode: inactive;\"]",
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ focusable: !aInDesignMode,
+ changeOpened: true, expectedOpened: false },
+ { id: "ime_mode_disabled_tel",
+ description: "input[type=tel][style=\"ime-mode: disabled;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: kEnabledStateOnPasswordField },
+
+ { id: "ime_mode_auto_number",
+ description: "input[type=number][style=\"ime-mode: auto;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED },
+ { id: "ime_mode_normal_number",
+ description: "input[type=number][style=\"ime-mode: normal;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED },
+ { id: "ime_mode_active_number",
+ description: "input[type=number][style=\"ime-mode: active;\"]",
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ focusable: !aInDesignMode,
+ changeOpened: true, expectedOpened: true },
+ { id: "ime_mode_inactive_number",
+ description: "input[type=number][style=\"ime-mode: inactive;\"]",
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ focusable: !aInDesignMode,
+ changeOpened: true, expectedOpened: false },
+ { id: "ime_mode_disabled_number",
+ description: "input[type=number][style=\"ime-mode: disabled;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: kEnabledStateOnPasswordField },
+
+ { id: "ime_mode_auto_p",
+ description: "input[type=password][style=\"ime-mode: auto;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: kEnabledStateOnPasswordField },
+ { id: "ime_mode_normal_p",
+ description: "input[type=password][style=\"ime-mode: normal;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED },
+ { id: "ime_mode_active_p",
+ description: "input[type=password][style=\"ime-mode: active;\"]",
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ focusable: !aInDesignMode,
+ changeOpened: true, expectedOpened: true },
+ { id: "ime_mode_inactive_p",
+ description: "input[type=password][style=\"ime-mode: inactive;\"]",
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ focusable: !aInDesignMode,
+ changeOpened: true, expectedOpened: false },
+ { id: "ime_mode_disabled_p",
+ description: "input[type=password][style=\"ime-mode: disabled;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: kEnabledStateOnPasswordField },
+ { id: "ime_mode_auto",
+ description: "textarea[style=\"ime-mode: auto;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED },
+ { id: "ime_mode_normal",
+ description: "textarea[style=\"ime-mode: normal;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED },
+ { id: "ime_mode_active",
+ description: "textarea[style=\"ime-mode: active;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ changeOpened: true, expectedOpened: true },
+ { id: "ime_mode_inactive",
+ description: "textarea[style=\"ime-mode: inactive;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED,
+ changeOpened: true, expectedOpened: false },
+ { id: "ime_mode_disabled",
+ description: "textarea[style=\"ime-mode: disabled;\"]",
+ focusable: !aInDesignMode,
+ expectedEnabled: kEnabledStateOnPasswordField },
+
+ // HTML editors
+ { id: "contenteditableEditor",
+ description: "div[contenteditable=\"true\"]",
+ focusable: !aIsEditable && !aInDesignMode,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED },
+ { id: "designModeEditor",
+ description: "designMode editor",
+ focusable: true,
+ toDesignModeEditor: true,
+ expectedEnabled: gUtils.IME_STATUS_ENABLED },
+ ];
+
+ for (var i = 0; i < kTests.length; i++) {
+ test(kTests[i]);
+ }
+}
+
+function runPluginTest()
+{
+ if (!kIMEEnabledSupported) {
+ return;
+ }
+
+ if (navigator.platform.indexOf("Mac") == 0) {
+ // XXX on mac, currently, this test isn't passed because it doesn't return
+ // IME_STATUS_PLUGIN by its bug.
+ return;
+ }
+
+ var plugin = document.getElementById("plugin");
+
+ document.activeElement.blur();
+ is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
+ "runPluginTest: unexpected enabled state when no element has focus");
+
+ plugin.focus();
+ is(gUtils.IMEStatus, gUtils.IME_STATUS_PLUGIN,
+ "runPluginTest: unexpected enabled state when plugin has focus");
+
+ plugin.blur();
+ is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
+ "runPluginTest: unexpected enabled state when plugin has focus");
+
+ plugin.focus();
+ is(gUtils.IMEStatus, gUtils.IME_STATUS_PLUGIN,
+ "runPluginTest: unexpected enabled state when plugin has focus #2");
+
+ var parent = plugin.parentNode;
+ parent.removeChild(plugin);
+ is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
+ "runPluginTest: unexpected enabled state when plugin is removed from tree");
+
+ document.getElementById("text").focus();
+ is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
+ "runPluginTest: unexpected enabled state when input[type=text] has focus");
+}
+
+function runTypeChangingTest()
+{
+ if (!kIMEEnabledSupported)
+ return;
+
+ const kInputControls = [
+ { id: "text",
+ type: "text", expected: gUtils.IME_STATUS_ENABLED,
+ description: "[type=\"text\"]" },
+ { id: "text_readonly",
+ type: "text", expected: gUtils.IME_STATUS_DISABLED, isReadonly: true,
+ description: "[type=\"text\"][readonly]" },
+ { id: "password",
+ type: "password", expected: gUtils.IME_STATUS_PASSWORD,
+ description: "[type=\"password\"]" },
+ { id: "password_readonly",
+ type: "password", expected: gUtils.IME_STATUS_DISABLED, isReadonly: true,
+ description: "[type=\"password\"][readonly]" },
+ { id: "checkbox",
+ type: "checkbox", expected: gUtils.IME_STATUS_DISABLED,
+ description: "[type=\"checkbox\"]" },
+ { id: "radio",
+ type: "radio", expected: gUtils.IME_STATUS_DISABLED,
+ description: "[type=\"radio\"]" },
+ { id: "submit",
+ type: "submit", expected: gUtils.IME_STATUS_DISABLED,
+ description: "[type=\"submit\"]" },
+ { id: "reset",
+ type: "reset", expected: gUtils.IME_STATUS_DISABLED,
+ description: "[type=\"reset\"]" },
+ { id: "file",
+ type: "file", expected: gUtils.IME_STATUS_DISABLED,
+ description: "[type=\"file\"]" },
+ { id: "ibutton",
+ type: "button", expected: gUtils.IME_STATUS_DISABLED,
+ description: "[type=\"button\"]" },
+ { id: "image",
+ type: "image", expected: gUtils.IME_STATUS_DISABLED,
+ description: "[type=\"image\"]" },
+ { id: "url",
+ type: "url", expected: gUtils.IME_STATUS_ENABLED,
+ description: "[type=\"url\"]" },
+ { id: "email",
+ type: "email", expected: gUtils.IME_STATUS_ENABLED,
+ description: "[type=\"email\"]" },
+ { id: "search",
+ type: "search", expected: gUtils.IME_STATUS_ENABLED,
+ description: "[type=\"search\"]" },
+ { id: "tel",
+ type: "tel", expected: gUtils.IME_STATUS_ENABLED,
+ description: "[type=\"tel\"]" },
+ { id: "number",
+ type: "number", expected: gUtils.IME_STATUS_ENABLED,
+ description: "[type=\"number\"]" },
+ { id: "ime_mode_auto",
+ type: "text", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"text\"][ime-mode: auto;]" },
+ { id: "ime_mode_normal",
+ type: "text", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"text\"][ime-mode: normal;]" },
+ { id: "ime_mode_active",
+ type: "text", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"text\"][ime-mode: active;]" },
+ { id: "ime_mode_inactive",
+ type: "text", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"text\"][ime-mode: inactive;]" },
+ { id: "ime_mode_disabled",
+ type: "text", expected: gUtils.IME_STATUS_PASSWORD, imeMode: true,
+ description: "[type=\"text\"][ime-mode: disabled;]" },
+
+ { id: "ime_mode_auto_url",
+ type: "url", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"url\"][ime-mode: auto;]" },
+ { id: "ime_mode_normal_url",
+ type: "url", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"url\"][ime-mode: normal;]" },
+ { id: "ime_mode_active_url",
+ type: "url", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"url\"][ime-mode: active;]" },
+ { id: "ime_mode_inactive_url",
+ type: "url", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"url\"][ime-mode: inactive;]" },
+ { id: "ime_mode_disabled_url",
+ type: "url", expected: gUtils.IME_STATUS_PASSWORD, imeMode: true,
+ description: "[type=\"url\"][ime-mode: disabled;]" },
+
+ { id: "ime_mode_auto_email",
+ type: "email", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"email\"][ime-mode: auto;]" },
+ { id: "ime_mode_normal_email",
+ type: "email", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"email\"][ime-mode: normal;]" },
+ { id: "ime_mode_active_email",
+ type: "email", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"email\"][ime-mode: active;]" },
+ { id: "ime_mode_inactive_email",
+ type: "email", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"email\"][ime-mode: inactive;]" },
+ { id: "ime_mode_disabled_email",
+ type: "email", expected: gUtils.IME_STATUS_PASSWORD, imeMode: true,
+ description: "[type=\"email\"][ime-mode: disabled;]" },
+
+ { id: "ime_mode_auto_search",
+ type: "search", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"search\"][ime-mode: auto;]" },
+ { id: "ime_mode_normal_search",
+ type: "search", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"search\"][ime-mode: normal;]" },
+ { id: "ime_mode_active_search",
+ type: "search", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"search\"][ime-mode: active;]" },
+ { id: "ime_mode_inactive_search",
+ type: "search", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"search\"][ime-mode: inactive;]" },
+ { id: "ime_mode_disabled_search",
+ type: "search", expected: gUtils.IME_STATUS_PASSWORD, imeMode: true,
+ description: "[type=\"search\"][ime-mode: disabled;]" },
+
+ { id: "ime_mode_auto_tel",
+ type: "tel", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"tel\"][ime-mode: auto;]" },
+ { id: "ime_mode_normal_tel",
+ type: "tel", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"tel\"][ime-mode: normal;]" },
+ { id: "ime_mode_active_tel",
+ type: "tel", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"tel\"][ime-mode: active;]" },
+ { id: "ime_mode_inactive_tel",
+ type: "tel", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"tel\"][ime-mode: inactive;]" },
+ { id: "ime_mode_disabled_tel",
+ type: "tel", expected: gUtils.IME_STATUS_PASSWORD, imeMode: true,
+ description: "[type=\"tel\"][ime-mode: disabled;]" },
+
+ { id: "ime_mode_auto_number",
+ type: "number", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"number\"][ime-mode: auto;]" },
+ { id: "ime_mode_normal_number",
+ type: "number", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"number\"][ime-mode: normal;]" },
+ { id: "ime_mode_active_number",
+ type: "number", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"number\"][ime-mode: active;]" },
+ { id: "ime_mode_inactive_number",
+ type: "number", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"number\"][ime-mode: inactive;]" },
+ { id: "ime_mode_disabled_number",
+ type: "number", expected: gUtils.IME_STATUS_PASSWORD, imeMode: true,
+ description: "[type=\"number\"][ime-mode: disabled;]" },
+
+ { id: "ime_mode_auto_p",
+ type: "password", expected: gUtils.IME_STATUS_PASSWORD, imeMode: true,
+ description: "[type=\"password\"][ime-mode: auto;]" },
+ { id: "ime_mode_normal_p",
+ type: "password", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"password\"][ime-mode: normal;]" },
+ { id: "ime_mode_active_p",
+ type: "password", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"password\"][ime-mode: active;]" },
+ { id: "ime_mode_inactive_p",
+ type: "password", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
+ description: "[type=\"password\"][ime-mode: inactive;]" },
+ { id: "ime_mode_disabled_p",
+ type: "password", expected: gUtils.IME_STATUS_PASSWORD, imeMode: true,
+ description: "[type=\"password\"][ime-mode: disabled;]" }
+ ];
+
+ const kInputTypes = [
+ { type: "", expected: gUtils.IME_STATUS_ENABLED },
+ { type: "text", expected: gUtils.IME_STATUS_ENABLED },
+ { type: "password", expected: gUtils.IME_STATUS_PASSWORD },
+ { type: "checkbox", expected: gUtils.IME_STATUS_DISABLED },
+ { type: "radio", expected: gUtils.IME_STATUS_DISABLED },
+ { type: "submit", expected: gUtils.IME_STATUS_DISABLED },
+ { type: "reset", expected: gUtils.IME_STATUS_DISABLED },
+ { type: "file", expected: gUtils.IME_STATUS_DISABLED },
+ { type: "button", expected: gUtils.IME_STATUS_DISABLED },
+ { type: "image", expected: gUtils.IME_STATUS_DISABLED },
+ { type: "url", expected: gUtils.IME_STATUS_ENABLED },
+ { type: "email", expected: gUtils.IME_STATUS_ENABLED },
+ { type: "search", expected: gUtils.IME_STATUS_ENABLED },
+ { type: "tel", expected: gUtils.IME_STATUS_ENABLED },
+ { type: "number", expected: gUtils.IME_STATUS_ENABLED }
+ ];
+
+ function getExpectedIMEEnabled(aNewType, aInputControl)
+ {
+ if (aNewType.expected == gUtils.IME_STATUS_DISABLED ||
+ aInputControl.isReadonly)
+ return gUtils.IME_STATUS_DISABLED;
+ return aInputControl.imeMode ? aInputControl.expected : aNewType.expected;
+ }
+
+ const kOpenedState = [ true, false ];
+
+ for (var i = 0; i < kOpenedState.length; i++) {
+ const kOpened = kOpenedState[i];
+ for (var j = 0; j < kInputControls.length; j++) {
+ const kInput = kInputControls[j];
+ var e = document.getElementById(kInput.id);
+ e.focus();
+ for (var k = 0; k < kInputTypes.length; k++) {
+ const kType = kInputTypes[k];
+ var typeChangingDescription =
+ "\"" + e.getAttribute("type") + "\" to \"" + kInput.type + "\"";
+ e.setAttribute("type", kInput.type);
+ is(gUtils.IMEStatus, kInput.expected,
+ "type attr changing test (IMEStatus): " + typeChangingDescription +
+ " (" + kInput.description + ")");
+ is(gUtils.focusedInputType, kInput.type,
+ "type attr changing test (type): " + typeChangingDescription +
+ " (" + kInput.description + ")");
+
+ const kTestOpenState = kIMEOpenSupported &&
+ gUtils.IMEStatus == gUtils.IME_STATUS_ENABLED &&
+ getExpectedIMEEnabled(kType, kInput) == gUtils.IME_STATUS_ENABLED;
+ if (kTestOpenState) {
+ gUtils.IMEIsOpen = kOpened;
+ }
+
+ typeChangingDescription =
+ "\"" + e.getAttribute("type") + "\" to \"" + kType.type + "\"";
+ if (kType.type != "")
+ e.setAttribute("type", kType.type);
+ else
+ e.removeAttribute("type");
+
+ is(gUtils.IMEStatus, getExpectedIMEEnabled(kType, kInput),
+ "type attr changing test (IMEStatus): " + typeChangingDescription +
+ " (" + kInput.description + ")");
+ is(gUtils.focusedInputType, kType.type,
+ "type attr changing test (type): " + typeChangingDescription +
+ " (" + kInput.description + ")");
+ if (kTestOpenState && gUtils.IMEStatus == gUtils.IME_STATUS_ENABLED) {
+ is(gUtils.IMEIsOpen, kOpened,
+ "type attr changing test (open state is changed): " +
+ typeChangingDescription + " (" + kInput.description + ")");
+ }
+ }
+ // reset the type to default
+ e.setAttribute("type", kInput.type);
+ }
+ if (!kIMEOpenSupported)
+ break;
+ }
+}
+
+function runReadonlyChangingTest()
+{
+ if (!kIMEEnabledSupported)
+ return;
+
+ const kInputControls = [
+ { id: "text",
+ type: "text", expected: gUtils.IME_STATUS_ENABLED },
+ { id: "password",
+ type: "password", expected: gUtils.IME_STATUS_PASSWORD },
+ { id: "url",
+ type: "url", expected: gUtils.IME_STATUS_ENABLED },
+ { id: "email",
+ type: "email", expected: gUtils.IME_STATUS_ENABLED },
+ { id: "search",
+ type: "search", expected: gUtils.IME_STATUS_ENABLED },
+ { id: "tel",
+ type: "tel", expected: gUtils.IME_STATUS_ENABLED },
+ { id: "number",
+ type: "number", expected: gUtils.IME_STATUS_ENABLED },
+ { id: "textarea",
+ type: "textarea", expected: gUtils.IME_STATUS_ENABLED }
+ ];
+ const kOpenedState = [ true, false ];
+
+ for (var i = 0; i < kOpenedState.length; i++) {
+ const kOpened = kOpenedState[i];
+ for (var j = 0; j < kInputControls.length; j++) {
+ const kInput = kInputControls[j];
+ var e = document.getElementById(kInput.id);
+ e.focus();
+ if (kIMEOpenSupported && gUtils.IMEStatus == gUtils.IME_STATUS_ENABLED) {
+ gUtils.IMEIsOpen = kOpened;
+ }
+ e.setAttribute("readonly", "readonly");
+ is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
+ "readonly attr setting test: type=" + kInput.type);
+ e.removeAttribute("readonly");
+ is(gUtils.IMEStatus, kInput.expected,
+ "readonly attr removing test: type=" + kInput.type);
+ if (kIMEOpenSupported && gUtils.IMEStatus == gUtils.IME_STATUS_ENABLED) {
+ is(gUtils.IMEIsOpen, kOpened,
+ "readonly attr removing test (open state is changed): type=" +
+ kInput.type);
+ }
+ }
+ if (!kIMEOpenSupported)
+ break;
+ }
+}
+
+function runComplexContenteditableTests()
+{
+ if (!kIMEEnabledSupported) {
+ return;
+ }
+
+ var description = "runReadonlyChangingOnContenteditable: ";
+
+ var container = document.getElementById("display");
+ var button = document.getElementById("button");
+
+ // the editor has focus directly.
+ container.setAttribute("contenteditable", "true");
+ container.focus();
+
+ is(gFM.focusedElement, container,
+ description + "The editor doesn't get focus");
+ is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
+ description + "IME isn't enabled on HTML editor");
+ const kReadonly =
+ Components.interfaces.nsIPlaintextEditor.eEditorReadonlyMask;
+ var editor =
+ window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIWebNavigation).
+ QueryInterface(Components.interfaces.nsIDocShell).editor;
+ var flags = editor.flags;
+ editor.flags = flags | kReadonly;
+ is(gFM.focusedElement, container,
+ description + "The editor loses focus by flag change");
+ is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
+ description + "IME is still enabled on readonly HTML editor");
+ editor.flags = flags;
+ is(gFM.focusedElement, container,
+ description + "The editor loses focus by flag change #2");
+ is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
+ description + "IME is still disabled, the editor isn't readonly now");
+ container.removeAttribute("contenteditable");
+ todo_is(gFM.focusedElement, null,
+ description + "The container still has focus, the editor has been no editable");
+ todo_is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
+ description + "IME is still enabled on the editor, the editor has been no editable");
+
+ // a button which is in the editor has focus
+ button.focus();
+ is(gFM.focusedElement, button,
+ description + "The button doesn't get focus");
+ is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
+ description + "IME is enabled on the button");
+ container.setAttribute("contenteditable", "true");
+ is(gFM.focusedElement, button,
+ description + "The button loses focus, the container is editable now");
+ todo_is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
+ description + "IME is still disabled on the button, the container is editable now");
+ editor =
+ window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIWebNavigation).
+ QueryInterface(Components.interfaces.nsIDocShell).editor;
+ flags = editor.flags;
+ editor.flags = flags | kReadonly;
+ is(gFM.focusedElement, button,
+ description + "The button loses focus by changing editor flags");
+ is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
+ description + "IME is still enabled on the button, the container is readonly now");
+ editor.flags = flags;
+ is(gFM.focusedElement, button,
+ description + "The button loses focus by changing editor flags #2");
+ is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
+ description + "IME is still disabled on the button, the container isn't readonly now");
+ container.removeAttribute("contenteditable");
+ is(gFM.focusedElement, button,
+ description + "The button loses focus, the container has been no editable");
+ todo_is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
+ description + "IME is still enabled on the button, the container has been no editable");
+
+ description = "testOnIndependentEditor: ";
+ function testOnIndependentEditor(aEditor, aEditorDescription)
+ {
+ var isReadonly = aEditor.readOnly;
+ var expectedState =
+ aEditor.readOnly ? gUtils.IME_STATUS_DISABLED : gUtils.IME_STATUS_ENABLED;
+ var unexpectedStateDescription =
+ expectedState != gUtils.IME_STATUS_ENABLED ? "enabled" : "disabled";
+ aEditor.focus();
+ is(gFM.focusedElement, aEditor,
+ description + "The " + aEditorDescription + " doesn't get focus");
+ is(gUtils.IMEStatus, expectedState,
+ description + "IME is " + unexpectedStateDescription +
+ " on the " + aEditorDescription);
+ container.setAttribute("contenteditable", "true");
+ is(gFM.focusedElement, aEditor,
+ description + "The " + aEditorDescription +
+ " loses focus, the container is editable now");
+ is(gUtils.IMEStatus, expectedState,
+ description + "IME becomes " + unexpectedStateDescription +
+ " on the " + aEditorDescription + ", the container is editable now");
+ editor =
+ window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIWebNavigation).
+ QueryInterface(Components.interfaces.nsIDocShell).editor;
+ flags = editor.flags;
+ editor.flags = flags | kReadonly;
+ is(gFM.focusedElement, aEditor,
+ description + "The " + aEditorDescription +
+ " loses focus by changing editor flags");
+ is(gUtils.IMEStatus, expectedState,
+ description + "IME becomes " + unexpectedStateDescription + " on the " +
+ aEditorDescription + ", the container is readonly now");
+ editor.flags = flags;
+ is(gFM.focusedElement, aEditor,
+ description + "The " + aEditorDescription +
+ " loses focus by changing editor flags #2");
+ is(gUtils.IMEStatus, expectedState,
+ description + "IME becomes " + unexpectedStateDescription + " on the " +
+ aEditorDescription + ", the container isn't readonly now");
+ container.removeAttribute("contenteditable");
+ is(gFM.focusedElement, aEditor,
+ description + "The " + aEditorDescription +
+ " loses focus, the container has been no editable");
+ is(gUtils.IMEStatus, expectedState,
+ description + "IME becomes " + unexpectedStateDescription + " on the " +
+ aEditorDescription + ", the container has been no editable");
+ }
+
+ // a textarea which is in the editor has focus
+ testOnIndependentEditor(document.getElementById("textarea"),
+ "textarea");
+ // a readonly textarea which is in the editor has focus
+ testOnIndependentEditor(document.getElementById("textarea_readonly"),
+ "textarea[readonly]");
+ // an input field which is in the editor has focus
+ testOnIndependentEditor(document.getElementById("text"),
+ "input[type=\"text\"]");
+ // a readonly input field which is in the editor has focus
+ testOnIndependentEditor(document.getElementById("text_readonly"),
+ "input[type=\"text\"][readonly]");
+
+ description = "testOnOutsideOfEditor: ";
+ function testOnOutsideOfEditor(aFocusNode, aFocusNodeDescription, aEditor)
+ {
+ if (aFocusNode) {
+ aFocusNode.focus();
+ is(gFM.focusedElement, aFocusNode,
+ description + "The " + aFocusNodeDescription + " doesn't get focus");
+ } else {
+ if (document.activeElement) {
+ document.activeElement.blur();
+ }
+ is(gFM.focusedElement, null,
+ description + "Unexpected element has focus");
+ }
+ var expectedState =
+ aFocusNode ? gUtils.IMEStatus : gUtils.IME_STATUS_DISABLED;
+ var unexpectedStateDescription =
+ expectedState != gUtils.IME_STATUS_ENABLED ? "enabled" : "disabled";
+
+ aEditor.setAttribute("contenteditable", "true");
+ is(gFM.focusedElement, aFocusNode,
+ description + "The " + aFocusNodeDescription +
+ " loses focus, a HTML editor is editable now");
+ is(gUtils.IMEStatus, expectedState,
+ description + "IME becomes " + unexpectedStateDescription +
+ " on the " + aFocusNodeDescription +
+ ", the HTML editor is editable now");
+ editor =
+ window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIWebNavigation).
+ QueryInterface(Components.interfaces.nsIDocShell).editor;
+ flags = editor.flags;
+ editor.flags = flags | kReadonly;
+ is(gFM.focusedElement, aFocusNode,
+ description + aFocusNodeDescription +
+ " loses focus by changing HTML editor flags");
+ is(gUtils.IMEStatus, expectedState,
+ description + "IME becomes " + unexpectedStateDescription + " on " +
+ aFocusNodeDescription + ", the HTML editor is readonly now");
+ editor.flags = flags;
+ is(gFM.focusedElement, aFocusNode,
+ description + aFocusNodeDescription +
+ " loses focus by changing HTML editor flags #2");
+ is(gUtils.IMEStatus, expectedState,
+ description + "IME becomes " + unexpectedStateDescription + " on " +
+ aFocusNodeDescription + ", the HTML editor isn't readonly now");
+ container.removeAttribute("contenteditable");
+ is(gFM.focusedElement, aFocusNode,
+ description + aFocusNodeDescription +
+ " loses focus, the HTML editor has been no editable");
+ is(gUtils.IMEStatus, expectedState,
+ description + "IME becomes " + unexpectedStateDescription + " on " +
+ aFocusNodeDescription + ", the HTML editor has been no editable");
+ }
+
+ var div = document.getElementById("contenteditableEditor");
+ // a textarea which is outside of the editor has focus
+ testOnOutsideOfEditor(document.getElementById("textarea"), "textarea", div);
+ // a readonly textarea which is outside of the editor has focus
+ testOnOutsideOfEditor(document.getElementById("textarea_readonly"),
+ "textarea[readonly]", div);
+ // an input field which is outside of the editor has focus
+ testOnOutsideOfEditor(document.getElementById("text"),
+ "input[type=\"text\"]", div);
+ // a readonly input field which outside of the editor has focus
+ testOnOutsideOfEditor(document.getElementById("text_readonly"),
+ "input[type=\"text\"][readonly]", div);
+ // a readonly input field which outside of the editor has focus
+ testOnOutsideOfEditor(document.getElementById("button"), "button", div);
+ // nobody has focus.
+ testOnOutsideOfEditor(null, "nobody", div);
+}
+
+function runEditorFlagChangeTests()
+{
+ if (!kIMEEnabledSupported) {
+ return;
+ }
+
+ var description = "runEditorFlagChangeTests: ";
+
+ var container = document.getElementById("display");
+
+ // Reset selection from previous tests.
+ window.getSelection().collapse(container, 0);
+
+ // the editor has focus directly.
+ container.setAttribute("contenteditable", "true");
+ container.focus();
+
+ is(gFM.focusedElement, container,
+ description + "The editor doesn't get focus");
+ is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
+ description + "IME isn't enabled on HTML editor");
+ const kIMEStateChangeFlags =
+ Components.interfaces.nsIPlaintextEditor.eEditorPasswordMask |
+ Components.interfaces.nsIPlaintextEditor.eEditorReadonlyMask |
+ Components.interfaces.nsIPlaintextEditor.eEditorDisabledMask;
+ var editor =
+ window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIWebNavigation).
+ QueryInterface(Components.interfaces.nsIDocShell).editor;
+ var editorIMESupport =
+ editor.QueryInterface(Components.interfaces.nsIEditorIMESupport);
+ var flags = editor.flags;
+
+ // input characters
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3078\u3093\u3057\u3093",
+ "clauses":
+ [
+ { "length": 4, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 4, "length": 0 }
+ });
+
+ editor.flags &= ~kIMEStateChangeFlags;
+ ok(editorIMESupport.composing,
+ description + "#1 IME composition was committed unexpectedly");
+ is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
+ description + "#1 IME isn't enabled on HTML editor");
+
+ editor.flags |= ~kIMEStateChangeFlags;
+ ok(editorIMESupport.composing,
+ description + "#2 IME composition was committed unexpectedly");
+ is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
+ description + "#2 IME isn't enabled on HTML editor");
+
+ editor.flags = flags;
+ ok(editorIMESupport.composing,
+ description + "#3 IME composition was committed unexpectedly");
+ is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
+ description + "#3 IME isn't enabled on HTML editor");
+
+ // cancel the composition
+ synthesizeComposition({ type: "compositioncommit", data: "" });
+
+ container.removeAttribute("contenteditable");
+}
+
+function runEditableSubframeTests()
+{
+ window.open("window_imestate_iframes.html", "_blank",
+ "width=600,height=600");
+}
+
+function runTestPasswordFieldOnDialog()
+{
+ if (!kIMEEnabledSupported) {
+ return;
+ }
+
+ if (document.activeElement) {
+ document.activeElement.blur();
+ }
+
+ var dialog;
+
+ function WindowObserver()
+ {
+ Components.classes["@mozilla.org/observer-service;1"].
+ getService(Components.interfaces.nsIObserverService).
+ addObserver(this, "domwindowopened", false);
+ }
+
+ WindowObserver.prototype = {
+ QueryInterface: function (iid)
+ {
+ if (iid.equals(Components.interfaces.nsIObserver) ||
+ iid.equals(Components.interfaces.nsISupports)) {
+ return this;
+ }
+ },
+
+ observe: function (subject, topic, data)
+ {
+ if (topic === "domwindowopened") {
+ ok(true, "dialog window is created");
+ dialog = subject.QueryInterface(Components.interfaces.nsIDOMWindow);
+ dialog.addEventListener("load", onPasswordDialogLoad, false);
+ }
+ }
+ };
+
+ var observer = new WindowObserver();
+ var arg1 = new Object(), arg2 = new Object();
+ Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
+ getService(Components.interfaces.nsIPromptService).
+ promptPassword(window, "title", "text", arg1, "msg", arg2);
+
+ ok(true, "password dialog was closed");
+
+ Components.classes["@mozilla.org/observer-service;1"].
+ getService(Components.interfaces.nsIObserverService).
+ removeObserver(observer, "domwindowopened");
+
+ var passwordField;
+
+ function onPasswordDialogLoad()
+ {
+ ok(true, "onPasswordDialogLoad is called");
+ dialog.removeEventListener("load", onPasswordDialogLoad, false);
+ passwordField = dialog.document.getElementById("password1Textbox");
+ passwordField.addEventListener("focus", onPasswordFieldFocus, false);
+ }
+
+ function onPasswordFieldFocus()
+ {
+ ok(true, "onPasswordFieldFocus is called");
+ passwordField.removeEventListener("focus", onPasswordFieldFocus, false);
+ var utils = dialog.
+ QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIDOMWindowUtils);
+ is(utils.IMEStatus, utils.IME_STATUS_PASSWORD,
+ "IME isn't disabled on a password field of password dialog");
+ synthesizeKey("VK_ESCAPE", { }, dialog);
+ }
+}
+
+// Bug 580388 and bug 808287
+function runEditorReframeTests(aCallback)
+{
+ if (document.activeElement) {
+ document.activeElement.blur();
+ }
+
+ var IMEFocus = 0;
+ var IMEBlur = 0;
+ var IMEHasFocus = false;
+ var TIPCallback = function(aTIP, aNotification) {
+ switch (aNotification.type) {
+ case "request-to-commit":
+ aTIP.commitComposition();
+ break;
+ case "request-to-cancel":
+ aTIP.cancelComposition();
+ break;
+ case "notify-focus":
+ IMEFocus++;
+ IMEHasFocus = true;
+ break;
+ case "notify-blur":
+ IMEBlur++;
+ IMEHasFocus = false;
+ break;
+ }
+ return true;
+ };
+
+ var TIP = Components.classes["@mozilla.org/text-input-processor;1"]
+ .createInstance(Components.interfaces.nsITextInputProcessor);
+ if (!TIP.beginInputTransactionForTests(window, TIPCallback)) {
+ ok(false, "runEditorReframeTests(): failed to begin input transaction");
+ return;
+ }
+
+ var input = document.getElementById("text");
+ input.focus();
+
+ is(IMEFocus, 1, "runEditorReframeTests(): IME should receive a focus notification by a call of <input>.focus()");
+ is(IMEBlur, 0, "runEditorReframeTests(): IME shouldn't receive a blur notification by a call of <input>.focus()");
+ ok(IMEHasFocus, "runEditorReframeTests(): IME should have focus because <input>.focus() is called");
+
+ IMEFocus = IMEBlur = 0;
+
+ input.style.overflow = "visible";
+
+ var onInput = function (aEvent) {
+ aEvent.target.style.overflow = "hidden";
+ }
+ input.addEventListener("input", onInput, true);
+
+ var AKey = new KeyboardEvent("", { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A });
+ TIP.keydown(AKey);
+ TIP.keyup(AKey);
+
+ hitEventLoop(function () {
+ is(IMEFocus, 0, "runEditorReframeTests(): IME shouldn't receive a focus notification during reframing");
+ is(IMEBlur, 0, "runEditorReframeTests(): IME shouldn't receive a blur notification during reframing");
+ ok(IMEHasFocus, "runEditorReframeTests(): IME must have focus even after reframing");
+
+ var onFocus = function(aEvent) {
+ // Perform a style change and query during focus to trigger reframing
+ input.style.overflow = "visible";
+ synthesizeQuerySelectedText();
+ };
+ input.addEventListener("focus", onFocus);
+ IMEFocus = IMEBlur = 0;
+
+ input.blur();
+ input.focus();
+ TIP.keydown(AKey);
+ TIP.keyup(AKey);
+
+ hitEventLoop(function() {
+ is(IMEFocus, 1, "runEditorReframeTests(): IME should receive a focus notification at focus but shouldn't receive it during reframing");
+ is(IMEBlur, 1, "runEditorReframeTests(): IME should receive a blur notification at blur but shouldn't receive it during reframing");
+ ok(IMEHasFocus, "runEditorReframeTests(): IME sould have focus after reframing during focus");
+
+ input.removeEventListener("input", onInput, true);
+ input.removeEventListener("focus", onFocus);
+
+ input.style.overflow = "visible";
+ input.value = "";
+
+ TIP = null;
+
+ hitEventLoop(aCallback, 20);
+ }, 20);
+ }, 20);
+}
+
+function runTests()
+{
+ if (!kIMEEnabledSupported && !kIMEOpenSupported)
+ return;
+
+ // test for normal contents.
+ runBasicTest(false, false, "Testing of normal contents");
+
+ // test for plugin contents
+ runPluginTest();
+
+ var container = document.getElementById("display");
+ // test for contentEditable="true"
+ container.setAttribute("contenteditable", "true");
+ runBasicTest(true, false, "Testing [contentEditable=\"true\"]");
+
+ // test for contentEditable="false"
+ container.setAttribute("contenteditable", "false");
+ runBasicTest(false, false, "Testing [contentEditable=\"false\"]");
+
+ // test for removing contentEditable
+ container.setAttribute("contenteditable", "true");
+ container.removeAttribute("contenteditable");
+ runBasicTest(false, false, "Testing after contentEditable to be removed");
+
+ // test designMode
+ document.designMode = "on";
+ runBasicTest(true, true, "Testing designMode=\"on\"");
+ document.designMode = "off";
+ document.getElementById("text").focus();
+ runBasicTest(false, false, "Testing designMode=\"off\"");
+
+ // changing input[type] values
+ // XXX currently, type attribute changing doesn't work fine. bug 559728.
+ // runTypeChangingTest();
+
+ // changing readonly attribute
+ runReadonlyChangingTest();
+
+ // complex contenteditable editor's tests
+ runComplexContenteditableTests();
+
+ // test whether the IME state and composition are not changed unexpectedly
+ runEditorFlagChangeTests();
+
+ // test password field on dialog
+ // XXX temporary disable against failure
+ //runTestPasswordFieldOnDialog();
+
+ // Asynchronous tests
+ runEditorReframeTests(function () {
+ // This will call onFinish(), so, this test must be the last.
+ runEditableSubframeTests();
+ });
+}
+
+function onFinish()
+{
+ SimpleTest.finish();
+}
+
+</script>
+</body>
+
+</html>
diff --git a/widget/tests/test_input_events_on_deactive_window.xul b/widget/tests/test_input_events_on_deactive_window.xul
new file mode 100644
index 000000000..a85646266
--- /dev/null
+++ b/widget/tests/test_input_events_on_deactive_window.xul
@@ -0,0 +1,236 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Testing composition, text and query content events"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<div id="content" style="display: none">
+</div>
+<p id="display">
+ <textarea id="textarea"></textarea>
+</p>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests, window);
+
+var fm = Components.classes["@mozilla.org/focus-manager;1"].
+ getService(Components.interfaces.nsIFocusManager);
+var textarea = document.getElementById("textarea");
+var otherWindow;
+var timer;
+
+function runTests()
+{
+ textarea.focus();
+ is(fm.focusedElement, textarea, "we're deactive");
+ if (fm.focusedElement != textarea) {
+ SimpleTest.finish();
+ return;
+ }
+
+ otherWindow =
+ window.open("data:text/plain,this is an active window.", "_blank",
+ "chrome,width=100,height=100");
+ ok(otherWindow, "failed to open other window");
+ if (!otherWindow) {
+ SimpleTest.finish();
+ return;
+ }
+
+ SimpleTest.waitForFocus(startTests, otherWindow);
+ otherWindow.focus();
+}
+
+function startTests()
+{
+ clearTimeout(timer);
+ isnot(fm.focusedWindow, window, "we're not deactive");
+ if (fm.focusedWindow == window) {
+ otherWindow.close();
+ SimpleTest.finish();
+ return;
+ }
+
+ var keydownHandled, keypressHandled, keyupHandled, compositionstartHandled,
+ compositionendHandled, compositionupdateHandled, inputHandled;
+
+ function clear()
+ {
+ keydownHandled = false;
+ keypressHandled = false;
+ keyupHandled = false;
+ compositionstartHandled = false;
+ compositionendHandled = false;
+ compositionupdateHandled = false;
+ inputHandled = false;
+ }
+
+ function onEvent(aEvent)
+ {
+ if (aEvent.type == "keydown") {
+ keydownHandled = true;
+ } else if (aEvent.type == "keypress") {
+ keypressHandled = true;
+ } else if (aEvent.type == "keyup") {
+ keyupHandled = true;
+ } else if (aEvent.type == "compositionstart") {
+ compositionstartHandled = true;
+ } else if (aEvent.type == "compositionend") {
+ compositionendHandled = true;
+ } else if (aEvent.type == "compositionupdate") {
+ compositionupdateHandled = true;
+ } else if (aEvent.type == "input") {
+ inputHandled = true;
+ } else {
+ ok(false, "handled unknown event: " + aEvent.type);
+ }
+ }
+
+ textarea.addEventListener("keydown", onEvent, false);
+ textarea.addEventListener("keypress", onEvent, false);
+ textarea.addEventListener("keyup", onEvent, false);
+ textarea.addEventListener("compositionstart", onEvent, false);
+ textarea.addEventListener("compositionend", onEvent, false);
+ textarea.addEventListener("compositionupdate", onEvent, false);
+ textarea.addEventListener("input", onEvent, false);
+
+ startTestsInternal();
+
+ function startTestsInternal()
+ {
+ // key events
+ function checkKeyEvents(aKeydown, aKeypress, aKeyup, aInput, aDescription)
+ {
+ is(keydownHandled, aKeydown,
+ "keydown event is (not) handled: " + aDescription);
+ is(keypressHandled, aKeypress,
+ "keypress event is (not) handled: " + aDescription);
+ is(keyupHandled, aKeyup,
+ "keyup event is (not) handled: " + aDescription);
+ is(inputHandled, aInput,
+ "input event is (not) handled: " + aDescription);
+ }
+
+ function checkCompositionEvents(aStart, aEnd, aUpdate, aInput, aDescription)
+ {
+ is(compositionstartHandled, aStart,
+ "compositionstart event is (not) handled: " + aDescription);
+ is(compositionendHandled, aEnd,
+ "compositionend event is (not) handled: " + aDescription);
+ is(compositionupdateHandled, aUpdate,
+ "compositionupdate event is (not) handled: " + aDescription);
+ is(inputHandled, aInput,
+ "input event is (not) handled: " + aDescription);
+ }
+
+ clear();
+ synthesizeKey("a", { type: "keydown" });
+ checkKeyEvents(true, true, false, true, "a keydown and a keypress");
+ is(textarea.value, "a", "textarea value isn't 'a'");
+ clear();
+ synthesizeKey("a", { type: "keyup" });
+ checkKeyEvents(false, false, true, false, "a keyup");
+ clear();
+ synthesizeKey("VK_BACK_SPACE", {});
+ checkKeyEvents(true, true, true, true, "VK_BACK_SPACE key events");
+ is(textarea.value, "", "textarea value isn't empty");
+
+ // IME events
+ clear();
+ // input first character
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+ checkCompositionEvents(true, false, true, true, "starting to compose");
+ var queryText = synthesizeQueryTextContent(0, 100);
+ ok(queryText, "query text event result is null");
+ if (!queryText) {
+ return;
+ }
+ ok(queryText.succeeded, "query text event failed");
+ if (!queryText.succeeded) {
+ return;
+ }
+ is(queryText.text, "\u3089", "composing text is incorrect");
+ var querySelectedText = synthesizeQuerySelectedText();
+ ok(querySelectedText, "query selected text event result is null");
+ if (!querySelectedText) {
+ return;
+ }
+ ok(querySelectedText.succeeded, "query selected text event failed");
+ if (!querySelectedText.succeeded) {
+ return;
+ }
+ is(querySelectedText.offset, 1,
+ "query selected text event returns wrong offset");
+ is(querySelectedText.text, "",
+ "query selected text event returns wrong selected text");
+ clear();
+ // commit composition
+ synthesizeComposition({ type: "compositioncommitasis" });
+ checkCompositionEvents(false, true, false, true, "commit composition as is");
+ queryText = synthesizeQueryTextContent(0, 100);
+ ok(queryText, "query text event result is null after commit");
+ if (!queryText) {
+ return;
+ }
+ ok(queryText.succeeded, "query text event failed after commit");
+ if (!queryText.succeeded) {
+ return;
+ }
+ is(queryText.text, "\u3089", "composing text is incorrect after commit");
+ querySelectedText = synthesizeQuerySelectedText();
+ ok(querySelectedText,
+ "query selected text event result is null after commit");
+ if (!querySelectedText) {
+ return;
+ }
+ ok(querySelectedText.succeeded,
+ "query selected text event failed after commit");
+ if (!querySelectedText.succeeded) {
+ return;
+ }
+ is(querySelectedText.offset, 1,
+ "query selected text event returns wrong offset after commit");
+ is(querySelectedText.text, "",
+ "query selected text event returns wrong selected text after commit");
+ clear();
+ }
+
+ textarea.removeEventListener("keydown", onEvent, false);
+ textarea.removeEventListener("keypress", onEvent, false);
+ textarea.removeEventListener("keyup", onEvent, false);
+ textarea.removeEventListener("compositionstart", onEvent, false);
+ textarea.removeEventListener("compositionupdate", onEvent, false);
+ textarea.removeEventListener("compositionend", onEvent, false);
+ textarea.removeEventListener("input", onEvent, false);
+
+ otherWindow.close();
+
+ SimpleTest.finish();
+}
+
+
+]]>
+</script>
+</window>
diff --git a/widget/tests/test_key_event_counts.xul b/widget/tests/test_key_event_counts.xul
new file mode 100644
index 000000000..4dd4b83da
--- /dev/null
+++ b/widget/tests/test_key_event_counts.xul
@@ -0,0 +1,90 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<!-- We've had issues on Mac OS X where native key events either don't get processed
+ or they get processed twice. This test tests some of those scenarios. -->
+
+<window id="window1" title="Test Key Event Counts" onload="runTest()"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/NativeKeyCodes.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ </body>
+
+ <script type="application/javascript"><![CDATA[
+ var gKeyPressEventCount = 0;
+ var gKeyDownEventCound = 0;
+
+ function onKeyDown(e)
+ {
+ gKeyDownEventCount++;
+ }
+
+ function onKeyPress(e)
+ {
+ gKeyPressEventCount++;
+ e.preventDefault();
+ }
+
+ function* testBody()
+ {
+ window.addEventListener("keydown", onKeyDown, false);
+ window.addEventListener("keypress", onKeyPress, false);
+
+ // Test ctrl-tab
+ gKeyDownEventCount = 0;
+ gKeyPressEventCount = 0;
+ yield synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, MAC_VK_Tab, {ctrlKey:1}, "\t", "\t", continueTest);
+ is(gKeyDownEventCount, 1);
+ is(gKeyPressEventCount, 0, "ctrl-tab should be consumed by tabbox of tabbrowser at keydown");
+
+ // Test cmd+shift+a
+ gKeyDownEventCount = 0;
+ gKeyPressEventCount = 0;
+ yield synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_A, {metaKey:1, shiftKey:1}, "a", "A", continueTest);
+ is(gKeyDownEventCount, 1);
+ is(gKeyPressEventCount, 1);
+
+ // Test cmd-;
+ gKeyDownEventCount = 0;
+ gKeyPressEventCount = 0;
+ yield synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_Semicolon, {metaKey:1}, ";", ";", continueTest);
+ is(gKeyDownEventCount, 1);
+ is(gKeyPressEventCount, 1);
+
+ window.removeEventListener("keydown", onKeyDown, false);
+ window.removeEventListener("keypress", onKeyPress, false);
+ }
+
+ var gTestContinuation = null;
+
+ function continueTest()
+ {
+ if (!gTestContinuation) {
+ gTestContinuation = testBody();
+ }
+ var ret = gTestContinuation.next();
+ if (ret.done) {
+ SimpleTest.finish();
+ } else {
+ is(ret.value, true, "Key synthesized successfully");
+ }
+ }
+
+ function runTest()
+ {
+ SimpleTest.waitForExplicitFinish();
+ continueTest();
+ }
+ ]]></script>
+
+</window>
diff --git a/widget/tests/test_keycodes.xul b/widget/tests/test_keycodes.xul
new file mode 100644
index 000000000..3ec460ecb
--- /dev/null
+++ b/widget/tests/test_keycodes.xul
@@ -0,0 +1,4361 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Key event tests"
+ onload="runTest()"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/NativeKeyCodes.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
+
+<commandset>
+ <command id="expectedCommand" oncommand="this.activeCount++" disabled="true"/>
+ <command id="unexpectedCommand" oncommand="this.activeCount++" disabled="true"/>
+ <command id="expectedReservedCommand" oncommand="this.activeCount++" reserved="true" disabled="true"/>
+</commandset>
+<keyset>
+ <key id="unshiftedKey" key=";" modifiers="accel" command="unexpectedCommand"/>
+ <key id="shiftedKey" key=":" modifiers="accel" command="unexpectedCommand"/>
+ <key id="commandOptionF" key='f' modifiers="accel,alt" command="unexpectedCommand"/>
+ <key id="question" key='?' modifiers="accel" command="unexpectedCommand"/>
+ <key id="unshiftedX" key="x" modifiers="accel" command="unexpectedCommand"/>
+ <key id="shiftedX" key="X" modifiers="accel,shift" command="unexpectedCommand"/>
+ <key id="unshiftedPlus" key="+" modifiers="accel" command="unexpectedCommand"/>
+ <key id="reservedUnshiftedKey" key="'" modifiers="accel" command="unexpectedCommand"/>
+ <key id="reservedShiftedKey" key='"' modifiers="accel" command="unexpectedCommand"/>
+</keyset>
+
+<browser id="browser" type="content" src="data:text/html;charset=utf-8,&lt;button id='content_button'&gt;button&lt;/button&gt;" width="200" height="32"/>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<p id="display">
+ <!-- for some reason, if we don't have 'accesskey' here, adding it dynamically later
+ doesn't work! -->
+ <button id="button" accesskey="z">Hello</button>
+ <input type="text" id="textbox" value=""/>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+const IS_MAC = navigator.platform.indexOf("Mac") == 0;
+const IS_WIN = navigator.platform.indexOf("Win") == 0;
+const OS_VERSION =
+ IS_WIN ? parseFloat(Components.classes["@mozilla.org/system-info;1"]
+ .getService(Components.interfaces.nsIPropertyBag2)
+ .getProperty("version")) : 0;
+const WIN7 = 6.1;
+const WIN8 = 6.2;
+
+function isModifierKeyEvent(aEvent)
+{
+ switch (aEvent.key) {
+ case "Alt":
+ case "AltGraph":
+ case "CapsLock":
+ case "Control":
+ case "Fn":
+ case "FnLock":
+ case "Hyper":
+ case "Meta":
+ case "NumLock":
+ case "ScrollLock":
+ case "Shift":
+ case "Super":
+ case "Symbol":
+ case "SymbolLock":
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * Firefox infobar UI can have access keys which conflict with this test. Really
+ * stupid workaround until we can move this test into its own chrome window.
+ */
+function clearInfobars()
+{
+ var Ci = Components.interfaces;
+
+ var browser = window.top.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell).chromeEventHandler;
+ var chromeWin = browser.ownerDocument.defaultView;
+ var nb = chromeWin.gBrowser.getNotificationBox(browser);
+ for (let n of nb.allNotifications) {
+ nb.removeNotification(n, true);
+ }
+}
+
+function eventToString(aEvent)
+{
+ var name = aEvent.layout.name + " keyCode=" +
+ aEvent.keyCode + " (0x" + aEvent.keyCode.toString(16).toUpperCase() +
+ ") chars='" + aEvent.chars + "'";
+ if (typeof aEvent.unmodifiedChars === "string") {
+ name += " unmodifiedChars='" + aEvent.unmodifiedChars + "'";
+ }
+ if (aEvent.modifiers.shiftKey) {
+ name += " [Shift]";
+ }
+ if (aEvent.modifiers.shiftRightKey) {
+ name += " [Right Shift]";
+ }
+ if (aEvent.modifiers.ctrlKey) {
+ name += " [Ctrl]";
+ }
+ if (aEvent.modifiers.ctrlRightKey) {
+ name += " [Right Ctrl]";
+ }
+ if (aEvent.modifiers.altKey) {
+ name += " [Alt]";
+ }
+ if (aEvent.modifiers.altRightKey) {
+ name += " [Right Alt]";
+ }
+ if (aEvent.modifiers.metaKey) {
+ name += " [Command]";
+ }
+ if (aEvent.modifiers.metaRightKey) {
+ name += " [Right Command]";
+ }
+
+ return name;
+}
+
+function getPhase(aDOMEvent)
+{
+ switch (aDOMEvent.eventPhase) {
+ case aDOMEvent.None:
+ return "none";
+ case aDOMEvent.CAPTURING_PHASE:
+ return "capture";
+ case aDOMEvent.AT_TARGET:
+ return "target";
+ case aDOMEvent.BUBBLING_PHASE:
+ return "bubble";
+ default:
+ return "";
+ }
+}
+
+function eventTargetToString(aEventTarget)
+{
+ if (aEventTarget.navigator) {
+ return "window";
+ }
+ switch (aEventTarget.nodeType) {
+ case Node.ELEMENT_NODE:
+ return "element (" + aEventTarget.tagName + ")";
+ case Node.DOCUMENT_NODE:
+ return "document";
+ default:
+ return "";
+ }
+}
+
+function synthesizeKey(aEvent, aFocusElementId, aCallback)
+{
+ if (aFocusElementId.startsWith("content_")) {
+ var browser = document.getElementById("browser");
+ browser.contentDocument.getElementById(aFocusElementId).focus();
+ } else {
+ document.getElementById(aFocusElementId).focus();
+ }
+
+ return synthesizeNativeKey(aEvent.layout, aEvent.keyCode,
+ aEvent.modifiers,
+ aEvent.chars, aEvent.unmodifiedChars,
+ aCallback);
+}
+
+// Test the charcodes and modifiers being delivered to keypress handlers and
+// also keydown/keyup events too.
+function* runKeyEventTests()
+{
+ const nsIDOMKeyEvent = Components.interfaces.nsIDOMKeyEvent;
+ var eventList, keyDownFlags, keyUpFlags, testingEvent, expectedDOMKeyCode;
+ const kShiftFlag = 0x1;
+ const kCtrlFlag = 0x2;
+ const kAltFlag = 0x4;
+ const kMetaFlag = 0x8;
+ const kNumLockFlag = 0x10;
+ const kCapsLockFlag = 0x20;
+
+ function onKeyEvent(e)
+ {
+ function removeFlag(e, aFlag)
+ {
+ if (e.type == "keydown") {
+ var oldValue = keyDownFlags;
+ keyDownFlags &= ~aFlag;
+ return oldValue != keyDownFlags;
+ } else if (e.type == "keyup") {
+ var oldValue = keyUpFlags;
+ keyUpFlags &= ~aFlag;
+ return oldValue != keyUpFlags;
+ }
+ return false;
+ }
+
+ function isStateChangingModifierKeyEvent(e)
+ {
+ var flags = 0;
+ if (e.type == "keydown") {
+ flags = keyDownFlags ^ keyUpFlags;
+ } else if (e.type == "keyup") {
+ flags = keyUpFlags;
+ }
+ switch (e.keyCode) {
+ case e.DOM_VK_SHIFT:
+ is(e.ctrlKey, (flags & kCtrlFlag) != 0, name + ", Ctrl of Shift " + e.type + " event mismatch");
+ is(e.metaKey, (flags & kMetaFlag) != 0, name + ", Command of Shift " + e.type + " event mismatch");
+ is(e.altKey, (flags & kAltFlag) != 0, name + ", Alt of Shift " + e.type + " event mismatch");
+ is(e.shiftKey, e.type == "keydown", name + ", Shift of Shift " + e.type + " event mismatch");
+ return (testingEvent.modifiers.shiftKey || testingEvent.modifiers.shiftRightKey) &&
+ removeFlag(e, kShiftFlag) && expectedDOMKeyCode != e.keyCode;
+ case e.DOM_VK_CONTROL:
+ is(e.ctrlKey, e.type == "keydown", name + ", Ctrl of Ctrl " + e.type + " event mismatch");
+ is(e.metaKey, (flags & kMetaFlag) != 0, name + ", Command of Ctrl " + e.type + " event mismatch");
+ is(e.altKey, (flags & kAltFlag) != 0, name + ", Alt of Ctrl " + e.type + " event mismatch");
+ is(e.shiftKey, (flags & kShiftFlag) != 0, name + ", Shift of Ctrl " + e.type + " event mismatch");
+ return (testingEvent.modifiers.ctrlKey || testingEvent.modifiers.ctrlRightKey || (IS_WIN && testingEvent.modifiers.altGrKey)) &&
+ removeFlag(e, kCtrlFlag) && expectedDOMKeyCode != e.keyCode;
+ case e.DOM_VK_ALT:
+ is(e.ctrlKey, (flags & kCtrlFlag) != 0, name + ", Ctrl of Alt " + e.type + " event mismatch");
+ is(e.metaKey, (flags & kMetaFlag) != 0, name + ", Command of Alt " + e.type + " event mismatch");
+ is(e.altKey, e.type == "keydown", name + ", Alt of Alt " + e.type + " event mismatch");
+ is(e.shiftKey, (flags & kShiftFlag) != 0, name + ", Shift of Alt " + e.type + " event mismatch");
+ return (testingEvent.modifiers.altKey || testingEvent.modifiers.altRightKey || (IS_WIN && testingEvent.modifiers.altGrKey)) &&
+ removeFlag(e, kAltFlag) && expectedDOMKeyCode != e.keyCode;
+ case e.DOM_VK_META:
+ is(e.ctrlKey, (flags & kCtrlFlag) != 0, name + ", Ctrl of Command " + e.type + " evnet mismatch");
+ is(e.metaKey, e.type == "keydown", name + ", Command of Command " + e.type + " evnet mismatch");
+ is(e.altKey, (flags & kAltFlag) != 0, name + ", Alt of Command " + e.type + " evnet mismatch");
+ is(e.shiftKey, (flags & kShiftFlag) != 0, name + ", Shift of Command " + e.type + " evnet mismatch");
+ return (testingEvent.modifiers.metaKey || testingEvent.modifiers.metaRightKey) &&
+ removeFlag(e, kMetaFlag) && expectedDOMKeyCode != e.keyCode;
+ case e.DOM_VK_NUM_LOCK:
+ is(e.ctrlKey, (flags & kCtrlFlag) != 0, name + ", Ctrl of NumLock " + e.type + " event mismatch");
+ is(e.metaKey, (flags & kMetaFlag) != 0, name + ", Command of NumLock " + e.type + " event mismatch");
+ is(e.altKey, (flags & kAltFlag) != 0, name + ", Alt of NumLock " + e.type + " event mismatch");
+ is(e.shiftKey, (flags & kShiftFlag) != 0, name + ", Shift of NumLock " + e.type + " event mismatch");
+ return (testingEvent.modifiers.numLockKey || testingEvent.modifiers.numericKeyPadKey) &&
+ removeFlag(e, kNumLockFlag) && expectedDOMKeyCode != e.keyCode;
+ case e.DOM_VK_CAPS_LOCK:
+ is(e.ctrlKey, (flags & kCtrlFlag) != 0, name + ", Ctrl of CapsLock " + e.type + " event mismatch");
+ is(e.metaKey, (flags & kMetaFlag) != 0, name + ", Command of CapsLock " + e.type + " event mismatch");
+ is(e.altKey, (flags & kAltFlag) != 0, name + ", Alt of CapsLock " + e.type + " event mismatch");
+ is(e.shiftKey, (flags & kShiftFlag) != 0, name + ", Shift of CapsLock " + e.type + " event mismatch");
+ return testingEvent.modifiers.capsLockKey &&
+ removeFlag(e, kCapsLockFlag) && expectedDOMKeyCode != e.keyCode;
+ }
+ return false;
+ }
+
+ // Ignore the state changing key events which is fired by the testing event.
+ if (!isStateChangingModifierKeyEvent(e))
+ eventList.push(e);
+ }
+
+ function consumer(aEvent)
+ {
+ aEvent.preventDefault();
+ }
+
+ const SHOULD_DELIVER_NONE = 0x0;
+ const SHOULD_DELIVER_KEYDOWN = 0x1;
+ const SHOULD_DELIVER_KEYPRESS = 0x2;
+ const SHOULD_DELIVER_KEYUP = 0x4;
+ const SHOULD_DELIVER_ALL = SHOULD_DELIVER_KEYDOWN |
+ SHOULD_DELIVER_KEYPRESS |
+ SHOULD_DELIVER_KEYUP;
+ const SHOULD_DELIVER_KEYDOWN_KEYUP = SHOULD_DELIVER_KEYDOWN |
+ SHOULD_DELIVER_KEYUP;
+ const SHOULD_DELIVER_KEYDOWN_KEYPRESS = SHOULD_DELIVER_KEYDOWN |
+ SHOULD_DELIVER_KEYPRESS;
+
+ // The first parameter is the complete input event. The second parameter is
+ // what to test against. The third parameter is which key events should be
+ // delived for the event.
+ // @param aExpectedKeyValues Can be string or array of string.
+ // If all keyboard events have same key value,
+ // specify it as string. Otherwise, specify
+ // each key value in array.
+ function testKey(aEvent, aExpectedKeyValues, aExpectedCodeValue,
+ aExpectedGeckoKeyCode, aExpectGeckoChar,
+ aShouldDelivedEvent, aExpectLocation)
+ {
+ ok(aExpectedGeckoKeyCode != undefined, "keycode is undefined");
+ eventList = [];
+
+ // The modifier key events which are fired for state changing are har to
+ // test. We should ignore them for now.
+ keyDownFlags = keyUpFlags = 0;
+ if (!IS_MAC) {
+ // On Mac, nsChildView doesn't generate modifier keydown/keyup events for
+ // state changing for synthesizeNativeKeyEvent.
+ if (aEvent.modifiers.shiftKey || aEvent.modifiers.shiftRightKey) {
+ keyDownFlags |= kShiftFlag;
+ }
+ if (aEvent.modifiers.ctrlKey || aEvent.modifiers.ctrlRightKey ||
+ (IS_WIN && aEvent.modifiers.altGrKey)) {
+ keyDownFlags |= kCtrlFlag;
+ }
+ if (aEvent.modifiers.altKey || aEvent.modifiers.altRightKey ||
+ (IS_WIN && aEvent.modifiers.altGrKey)) {
+ keyDownFlags |= kAltFlag;
+ }
+ if (aEvent.modifiers.metaKey || aEvent.modifiers.metaRightKey) {
+ keyDownFlags |= kMetaFlag;
+ }
+ if (aEvent.modifiers.numLockKey || aEvent.modifiers.numericKeyPadKey) {
+ keyDownFlags |= kNumLockFlag;
+ }
+ if (aEvent.modifiers.capsLockKey) {
+ keyDownFlags |= kCapsLockFlag;
+ }
+ keyUpFlags = keyDownFlags;
+ }
+
+ testingEvent = aEvent;
+ expectedDOMKeyCode = aExpectedGeckoKeyCode;
+
+ var name = eventToString(aEvent);
+ ok(true, "Starting: " + name);
+
+ return synthesizeKey(aEvent, "button", function() {
+
+ var expectEventTypeList = [];
+ if (aShouldDelivedEvent & SHOULD_DELIVER_KEYDOWN)
+ expectEventTypeList.push("keydown");
+ if (aShouldDelivedEvent & SHOULD_DELIVER_KEYPRESS) {
+ expectEventTypeList.push("keypress");
+ for (var i = 1; i < aExpectGeckoChar.length; i++) {
+ expectEventTypeList.push("keypress");
+ }
+ }
+ if (aShouldDelivedEvent & SHOULD_DELIVER_KEYUP)
+ expectEventTypeList.push("keyup");
+ is(eventList.length, expectEventTypeList.length, name + ", wrong number of key events");
+
+ var longerLength = Math.max(eventList.length, expectEventTypeList.length);
+ var keypressCount = 0;
+ for (var i = 0; i < longerLength; i++) {
+ var firedEventType = i < eventList.length ? eventList[i].type : "";
+ var expectEventType = i < expectEventTypeList.length ? expectEventTypeList[i] : "";
+ if (firedEventType != "")
+ is(firedEventType, expectEventType, name + ", " + expectEventType + " should be fired");
+ else
+ is(firedEventType, expectEventType, name + ", a needed event is not fired");
+
+ if (firedEventType != "") {
+ var e = eventList[i];
+ if (e.type == "keypress") {
+ var isCtrlExpected =
+ !!(aEvent.modifiers.ctrlKey || aEvent.modifiers.ctrlRightKey) && !aEvent.isInputtingCharacters;
+ var isAltExpected =
+ !!(aEvent.modifiers.altKey || aEvent.modifiers.altRightKey) && !aEvent.isInputtingCharacters;
+ if (IS_WIN && (aEvent.modifiers.altGrKey || isCtrlExpected && isAltExpected)) {
+ isCtrlExpected = isAltExpected = (aEvent.chars == "");
+ }
+ is(e.ctrlKey, isCtrlExpected, name + ", Ctrl mismatch");
+ is(e.metaKey, !!(aEvent.modifiers.metaKey || aEvent.modifiers.metaRightKey), name + ", Command mismatch");
+ is(e.altKey, isAltExpected, name + ", Alt mismatch");
+ is(e.shiftKey, !!(aEvent.modifiers.shiftKey || aEvent.modifiers.shiftRightKey), name + ", Shift mismatch");
+ }
+
+ var expectedKeyValue =
+ typeof aExpectedKeyValues === "string" ? aExpectedKeyValues :
+ i < aExpectedKeyValues.length ? aExpectedKeyValues[i] :
+ undefined;
+ is(e.key, expectedKeyValue, name + ", wrong key value");
+ is(e.code, aExpectedCodeValue, name + ", wrong code value");
+
+ if (aExpectGeckoChar.length > 0 && e.type == "keypress") {
+ is(e.charCode, aExpectGeckoChar.charCodeAt(keypressCount++), name + ", charcode");
+ if (aExpectedGeckoKeyCode >= 0) {
+ if (aExpectGeckoChar) {
+ is(e.keyCode, 0, name + ", wrong keycode");
+ } else {
+ is(e.keyCode, aExpectedGeckoKeyCode, name + ", wrong keycode");
+ }
+ }
+ } else {
+ is(e.charCode, 0, name + ", no charcode");
+ if (aExpectedGeckoKeyCode >= 0) {
+ is(e.keyCode, aExpectedGeckoKeyCode, name + ", wrong keycode");
+ }
+ }
+ is(e.location, aExpectLocation, name + ", wrong location");
+ }
+ }
+
+ continueTest();
+ });
+ }
+
+ // These tests have to be per-plaform.
+ document.addEventListener("keydown", onKeyEvent, false);
+ document.addEventListener("keypress", onKeyEvent, false);
+ document.addEventListener("keyup", onKeyEvent, false);
+ // Prevent almost all shortcut key handlers.
+ SpecialPowers.addSystemEventListener(document, "keypress", consumer, true);
+
+ function cleanup()
+ {
+ document.removeEventListener("keydown", onKeyEvent, false);
+ document.removeEventListener("keypress", onKeyEvent, false);
+ document.removeEventListener("keyup", onKeyEvent, false);
+ SpecialPowers.removeSystemEventListener(document, "keypress", consumer, true);
+ }
+
+ function testKeysOnMac()
+ {
+ // On Mac, you can produce event records for any desired keyboard input
+ // by running with NSPR_LOG_MODULES=TextInputHandlerWidgets:5 and typing
+ // into the browser. We will dump the key event fields to the console
+ // (Find TextInputHandler::HandleKeyDownEvent or
+ // TextInputHandler::HandleKeyUpEvent in the log). Use the International system
+ // preferences widget to enable other keyboard layouts and select them from the
+ // input menu to see what keyboard events they generate.
+ // Note that it's possible to send bogus key events here, e.g.
+ // {keyCode:0, chars:"z", unmodifiedChars:"P"} --- sendNativeKeyEvent
+ // makes no attempt to verify that the keyCode matches the characters. So only
+ // test key event records that you saw Cocoa send.
+
+ // Ctrl keys
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers:{ctrlKey:1}, chars:"\u0001", unmodifiedChars:"a"},
+ "a", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0001", unmodifiedChars:"A"},
+ "A", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "A", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Alt keys
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers:{altKey:1}, chars:"\u00e5", unmodifiedChars:"a"},
+ "\u00e5", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u00e5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers:{altKey:1, shiftKey:1}, chars:"\u00c5", unmodifiedChars:"A"},
+ "\u00c5", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u00c5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Command keys
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers:{metaKey:1}, chars:"a", unmodifiedChars:"a"},
+ "a", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // Shift-cmd gives us the shifted character
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers:{metaKey:1, shiftKey:1}, chars:"a", unmodifiedChars:"A"},
+ "a", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "A", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // Ctrl-cmd gives us the unshifted character
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers:{metaKey:1, ctrlKey:1}, chars:"\u0001", unmodifiedChars:"a"},
+ "a", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // Alt-cmd gives us the shifted character
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers:{metaKey:1, altKey:1}, chars:"\u00e5", unmodifiedChars:"a"},
+ "\u00e5", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u00e5", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers:{metaKey:1, altKey:1, shiftKey:1}, chars:"\u00c5", unmodifiedChars:"a"},
+ "\u00c5", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u00c5", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Greek ctrl keys produce Latin charcodes
+ yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:MAC_VK_ANSI_A,
+ modifiers:{ctrlKey:1}, chars:"\u0001", unmodifiedChars:"\u03b1"},
+ "\u03b1", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:MAC_VK_ANSI_A,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0001", unmodifiedChars:"\u0391"},
+ "\u0391", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "A", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Greek command keys
+ yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:MAC_VK_ANSI_A,
+ modifiers:{metaKey:1}, chars:"a", unmodifiedChars:"\u03b1"},
+ "a", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // Shift-cmd gives us the shifted character
+ yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:MAC_VK_ANSI_A,
+ modifiers:{metaKey:1, shiftKey:1}, chars:"a", unmodifiedChars:"\u0391"},
+ "a", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "A", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Ctrl-cmd gives us the unshifted character
+ yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:MAC_VK_ANSI_A,
+ modifiers:{metaKey:1, ctrlKey:1}, chars:"\u0001", unmodifiedChars:"\u03b1"},
+ "\u03b1", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // Alt-cmd gives us the shifted character
+ yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:MAC_VK_ANSI_A,
+ modifiers:{metaKey:1, altKey:1}, chars:"\u00a8", unmodifiedChars:"\u03b1"},
+ "\u00a8", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u00a8", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:MAC_VK_ANSI_A,
+ modifiers:{metaKey:1, altKey:1, shiftKey:1}, chars:"\u00b9", unmodifiedChars:"\u0391"},
+ "\u00b9", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u00b9", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // German
+ yield testKey({layout:KEYBOARD_LAYOUT_GERMAN, keyCode:MAC_VK_ANSI_A,
+ modifiers: {}, chars:"a", unmodifiedChars:"a"},
+ "a", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_GERMAN, keyCode:MAC_VK_ANSI_LeftBracket,
+ modifiers: {}, chars:"\u00fc", unmodifiedChars:"\u00fc"},
+ "\u00fc", "BracketLeft", 0, "\u00fc", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_GERMAN, keyCode:MAC_VK_ANSI_Minus,
+ modifiers: {}, chars:"\u00df", unmodifiedChars:"\u00df"},
+ "\u00df", "Minus", nsIDOMKeyEvent.DOM_VK_QUESTION_MARK, "\u00df", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_GERMAN, keyCode:MAC_VK_ANSI_Minus,
+ modifiers:{shiftKey:1}, chars:"?", unmodifiedChars:"?"},
+ "?", "Minus", nsIDOMKeyEvent.DOM_VK_QUESTION_MARK, "?", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // Note that Shift+SS is '?' but Cmd+Shift+SS is '/' on German layout.
+ // Therefore, when Cmd key is pressed, the SS key's keycode is changed.
+ yield testKey({layout:KEYBOARD_LAYOUT_GERMAN, keyCode:MAC_VK_ANSI_Minus,
+ modifiers:{metaKey:1}, chars:"\u00df", unmodifiedChars:"\u00df"},
+ "\u00df", "Minus", nsIDOMKeyEvent.DOM_VK_SLASH, "\u00df", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_GERMAN, keyCode:MAC_VK_ANSI_Minus,
+ modifiers:{metaKey:1, shiftKey:1}, chars:"/", unmodifiedChars:"?"},
+ "/", "Minus", nsIDOMKeyEvent.DOM_VK_SLASH, "?", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Caps Lock key event
+ // XXX keyup event of Caps Lock key is not fired.
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_CapsLock,
+ modifiers:{capsLockKey:1}, chars:"", unmodifiedChars:""},
+ "CapsLock", "CapsLock", nsIDOMKeyEvent.DOM_VK_CAPS_LOCK, "", SHOULD_DELIVER_KEYDOWN, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_CapsLock,
+ modifiers:{capsLockKey:0}, chars:"", unmodifiedChars:""},
+ "CapsLock", "CapsLock", nsIDOMKeyEvent.DOM_VK_CAPS_LOCK, "", SHOULD_DELIVER_KEYDOWN, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Shift/RightShift key event
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_Shift,
+ modifiers:{shiftKey:1}, chars:"", unmodifiedChars:""},
+ "Shift", "ShiftLeft", nsIDOMKeyEvent.DOM_VK_SHIFT, "", SHOULD_DELIVER_KEYDOWN, KeyboardEvent.DOM_KEY_LOCATION_LEFT);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_Shift,
+ modifiers:{shiftKey:0}, chars:"", unmodifiedChars:""},
+ "Shift", "ShiftLeft", nsIDOMKeyEvent.DOM_VK_SHIFT, "", SHOULD_DELIVER_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_LEFT);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_RightShift,
+ modifiers:{shiftRightKey:1}, chars:"", unmodifiedChars:""},
+ "Shift", "ShiftRight", nsIDOMKeyEvent.DOM_VK_SHIFT, "", SHOULD_DELIVER_KEYDOWN, KeyboardEvent.DOM_KEY_LOCATION_RIGHT);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_RightShift,
+ modifiers:{shiftRightKey:0}, chars:"", unmodifiedChars:""},
+ "Shift", "ShiftRight", nsIDOMKeyEvent.DOM_VK_SHIFT, "", SHOULD_DELIVER_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_RIGHT);
+
+ // Control/RightControl key event
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_Control,
+ modifiers:{ctrlKey:1}, chars:"", unmodifiedChars:""},
+ "Control", "ControlLeft", nsIDOMKeyEvent.DOM_VK_CONTROL, "", SHOULD_DELIVER_KEYDOWN, KeyboardEvent.DOM_KEY_LOCATION_LEFT);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_Control,
+ modifiers:{ctrlKey:0}, chars:"", unmodifiedChars:""},
+ "Control", "ControlLeft", nsIDOMKeyEvent.DOM_VK_CONTROL, "", SHOULD_DELIVER_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_LEFT);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_RightControl,
+ modifiers:{ctrlRightKey:1}, chars:"", unmodifiedChars:""},
+ "Control", "ControlRight", nsIDOMKeyEvent.DOM_VK_CONTROL, "", SHOULD_DELIVER_KEYDOWN, KeyboardEvent.DOM_KEY_LOCATION_RIGHT);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_RightControl,
+ modifiers:{ctrlRightKey:0}, chars:"", unmodifiedChars:""},
+ "Control", "ControlRight", nsIDOMKeyEvent.DOM_VK_CONTROL, "", SHOULD_DELIVER_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_RIGHT);
+
+ // Option/RightOption key event
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_Option,
+ modifiers:{altKey:1}, chars:"", unmodifiedChars:""},
+ "Alt", "AltLeft", nsIDOMKeyEvent.DOM_VK_ALT, "", SHOULD_DELIVER_KEYDOWN, KeyboardEvent.DOM_KEY_LOCATION_LEFT);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_Option,
+ modifiers:{altKey:0}, chars:"", unmodifiedChars:""},
+ "Alt", "AltLeft", nsIDOMKeyEvent.DOM_VK_ALT, "", SHOULD_DELIVER_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_LEFT);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_RightOption,
+ modifiers:{altRightKey:1}, chars:"", unmodifiedChars:""},
+ "Alt", "AltRight", nsIDOMKeyEvent.DOM_VK_ALT, "", SHOULD_DELIVER_KEYDOWN, KeyboardEvent.DOM_KEY_LOCATION_RIGHT);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_RightOption,
+ modifiers:{altRightKey:0}, chars:"", unmodifiedChars:""},
+ "Alt", "AltRight", nsIDOMKeyEvent.DOM_VK_ALT, "", SHOULD_DELIVER_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_RIGHT);
+
+ // Command/RightCommand key event
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_Command,
+ modifiers:{metaKey:1}, chars:"", unmodifiedChars:""},
+ "Meta", "OSLeft", nsIDOMKeyEvent.DOM_VK_META, "", SHOULD_DELIVER_KEYDOWN, KeyboardEvent.DOM_KEY_LOCATION_LEFT);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_Command,
+ modifiers:{metaKey:0}, chars:"", unmodifiedChars:""},
+ "Meta", "OSLeft", nsIDOMKeyEvent.DOM_VK_META, "", SHOULD_DELIVER_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_LEFT);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_RightCommand,
+ modifiers:{metaRightKey:1}, chars:"", unmodifiedChars:""},
+ "Meta", "OSRight", nsIDOMKeyEvent.DOM_VK_META, "", SHOULD_DELIVER_KEYDOWN, KeyboardEvent.DOM_KEY_LOCATION_RIGHT);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_RightCommand,
+ modifiers:{metaRightKey:0}, chars:"", unmodifiedChars:""},
+ "Meta", "OSRight", nsIDOMKeyEvent.DOM_VK_META, "", SHOULD_DELIVER_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_RIGHT);
+
+ // all keys on keyboard (keyCode test)
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_Tab,
+ modifiers: {}, chars:"\t", unmodifiedChars:"\t"},
+ "Tab", "Tab", nsIDOMKeyEvent.DOM_VK_TAB, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadClear,
+ modifiers: {}, chars:"\uF739", unmodifiedChars:"\uF739"},
+ "Clear", "NumLock", nsIDOMKeyEvent.DOM_VK_CLEAR, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_Return,
+ modifiers: {}, chars:"\u000D", unmodifiedChars:"\u000D"},
+ "Enter", "Enter", nsIDOMKeyEvent.DOM_VK_RETURN, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_PC_Pause,
+ modifiers: {}, chars:"\uF712", unmodifiedChars:"\uF712"},
+ "F15", "F15", nsIDOMKeyEvent.DOM_VK_PAUSE, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_Escape,
+ modifiers: {}, chars:"\u001B", unmodifiedChars:"\u001B"},
+ "Escape", "Escape", nsIDOMKeyEvent.DOM_VK_ESCAPE, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_Space,
+ modifiers: {}, chars:" ", unmodifiedChars:" "},
+ " ", "Space", nsIDOMKeyEvent.DOM_VK_SPACE, " ", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_PageUp,
+ modifiers: {}, chars:"\uF72C", unmodifiedChars:"\uF72C"},
+ "PageUp", "PageUp", nsIDOMKeyEvent.DOM_VK_PAGE_UP, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_PageDown,
+ modifiers: {}, chars:"\uF72D", unmodifiedChars:"\uF72D"},
+ "PageDown", "PageDown", nsIDOMKeyEvent.DOM_VK_PAGE_DOWN, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_End,
+ modifiers: {}, chars:"\uF72B", unmodifiedChars:"\uF72B"},
+ "End", "End", nsIDOMKeyEvent.DOM_VK_END, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_Home,
+ modifiers: {}, chars:"\uF729", unmodifiedChars:"\uF729"},
+ "Home", "Home", nsIDOMKeyEvent.DOM_VK_HOME, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_LeftArrow,
+ modifiers: {}, chars:"\uF702", unmodifiedChars:"\uF702"},
+ "ArrowLeft", "ArrowLeft", nsIDOMKeyEvent.DOM_VK_LEFT, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_UpArrow,
+ modifiers: {}, chars:"\uF700", unmodifiedChars:"\uF700"},
+ "ArrowUp", "ArrowUp", nsIDOMKeyEvent.DOM_VK_UP, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_RightArrow,
+ modifiers: {}, chars:"\uF703", unmodifiedChars:"\uF703"},
+ "ArrowRight", "ArrowRight", nsIDOMKeyEvent.DOM_VK_RIGHT, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_DownArrow,
+ modifiers: {}, chars:"\uF701", unmodifiedChars:"\uF701"},
+ "ArrowDown", "ArrowDown", nsIDOMKeyEvent.DOM_VK_DOWN, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_PC_PrintScreen,
+ modifiers: {}, chars:"\uF710", unmodifiedChars:"\uF710"},
+ "F13", "F13", nsIDOMKeyEvent.DOM_VK_PRINTSCREEN, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_PC_Delete,
+ modifiers: {}, chars:"\uF728", unmodifiedChars:"\uF728"},
+ "Delete", "Delete", nsIDOMKeyEvent.DOM_VK_DELETE, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_PC_ScrollLock,
+ modifiers: {}, chars:"\uF711", unmodifiedChars:"\uF711"},
+ "F14", "F14", nsIDOMKeyEvent.DOM_VK_SCROLL_LOCK, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_PC_ContextMenu,
+ modifiers: {}, chars:"\u0010", unmodifiedChars:"\u0010"},
+ "ContextMenu", "ContextMenu", nsIDOMKeyEvent.DOM_VK_CONTEXT_MENU, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_F1,
+ modifiers:{fnKey:1}, chars:"\uF704", unmodifiedChars:"\uF704"},
+ "F1", "F1", nsIDOMKeyEvent.DOM_VK_F1, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_F2,
+ modifiers:{fnKey:1}, chars:"\uF705", unmodifiedChars:"\uF705"},
+ "F2", "F2", nsIDOMKeyEvent.DOM_VK_F2, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_F3,
+ modifiers:{fnKey:1}, chars:"\uF706", unmodifiedChars:"\uF706"},
+ "F3", "F3", nsIDOMKeyEvent.DOM_VK_F3, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_F4,
+ modifiers:{fnKey:1}, chars:"\uF707", unmodifiedChars:"\uF707"},
+ "F4", "F4", nsIDOMKeyEvent.DOM_VK_F4, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_F5,
+ modifiers:{fnKey:1}, chars:"\uF708", unmodifiedChars:"\uF708"},
+ "F5", "F5", nsIDOMKeyEvent.DOM_VK_F5, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_F6,
+ modifiers:{fnKey:1}, chars:"\uF709", unmodifiedChars:"\uF709"},
+ "F6", "F6", nsIDOMKeyEvent.DOM_VK_F6, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_F7,
+ modifiers:{fnKey:1}, chars:"\uF70A", unmodifiedChars:"\uF70A"},
+ "F7", "F7", nsIDOMKeyEvent.DOM_VK_F7, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_F8,
+ modifiers:{fnKey:1}, chars:"\uF70B", unmodifiedChars:"\uF70B"},
+ "F8", "F8", nsIDOMKeyEvent.DOM_VK_F8, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_F9,
+ modifiers:{fnKey:1}, chars:"\uF70C", unmodifiedChars:"\uF70C"},
+ "F9", "F9", nsIDOMKeyEvent.DOM_VK_F9, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_F10,
+ modifiers:{fnKey:1}, chars:"\uF70D", unmodifiedChars:"\uF70D"},
+ "F10", "F10", nsIDOMKeyEvent.DOM_VK_F10, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_F11,
+ modifiers:{fnKey:1}, chars:"\uF70E", unmodifiedChars:"\uF70E"},
+ "F11", "F11", nsIDOMKeyEvent.DOM_VK_F11, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_F12,
+ modifiers:{fnKey:1}, chars:"\uF70F", unmodifiedChars:"\uF70F"},
+ "F12", "F12", nsIDOMKeyEvent.DOM_VK_F12, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_F16,
+ modifiers:{fnKey:1}, chars:"\uF713", unmodifiedChars:"\uF713"},
+ "F16", "F16", nsIDOMKeyEvent.DOM_VK_F16, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_F17,
+ modifiers:{fnKey:1}, chars:"\uF714", unmodifiedChars:"\uF714"},
+ "F17", "F17", nsIDOMKeyEvent.DOM_VK_F17, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_F18,
+ modifiers:{fnKey:1}, chars:"\uF715", unmodifiedChars:"\uF715"},
+ "F18", "F18", nsIDOMKeyEvent.DOM_VK_F18, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_F19,
+ modifiers:{fnKey:1}, chars:"\uF716", unmodifiedChars:"\uF716"},
+ "F19", "F19", nsIDOMKeyEvent.DOM_VK_F19, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // US
+ // Alphabet
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers: {}, chars:"a", unmodifiedChars:"a"},
+ "a", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers:{shiftKey:1}, chars:"A", unmodifiedChars:"A"},
+ "A", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "A", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers:{ctrlKey:1}, chars:"\u0001", unmodifiedChars:"a"},
+ "a", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers:{altKey:1}, chars:"\u00E5", unmodifiedChars:"a"},
+ "\u00E5", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u00E5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers:{metaKey:1}, chars:"a", unmodifiedChars:"a"},
+ "a", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_B,
+ modifiers:{}, chars:"b", unmodifiedChars:"b"},
+ "b", "KeyB", nsIDOMKeyEvent.DOM_VK_B, "b", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_B,
+ modifiers:{shiftKey:1}, chars:"B", unmodifiedChars:"B"},
+ "B", "KeyB", nsIDOMKeyEvent.DOM_VK_B, "B", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_B,
+ modifiers:{ctrlKey:1}, chars:"\u0002", unmodifiedChars:"b"},
+ "b", "KeyB", nsIDOMKeyEvent.DOM_VK_B, "b", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_B,
+ modifiers:{altKey:1}, chars:"\u222B", unmodifiedChars:"b"},
+ "\u222B", "KeyB", nsIDOMKeyEvent.DOM_VK_B, "\u222B", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_B,
+ modifiers:{metaKey:1}, chars:"b", unmodifiedChars:"b"},
+ "b", "KeyB", nsIDOMKeyEvent.DOM_VK_B, "b", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_C,
+ modifiers:{}, chars:"c", unmodifiedChars:"c"},
+ "c", "KeyC", nsIDOMKeyEvent.DOM_VK_C, "c", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_C,
+ modifiers:{shiftKey:1}, chars:"C", unmodifiedChars:"C"},
+ "C", "KeyC", nsIDOMKeyEvent.DOM_VK_C, "C", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_C,
+ modifiers:{ctrlKey:1}, chars:"\u0003", unmodifiedChars:"c"},
+ "c", "KeyC", nsIDOMKeyEvent.DOM_VK_C, "c", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_C,
+ modifiers:{altKey:1}, chars:"\u00E7", unmodifiedChars:"c"},
+ "\u00E7", "KeyC", nsIDOMKeyEvent.DOM_VK_C, "\u00E7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_C,
+ modifiers:{metaKey:1}, chars:"c", unmodifiedChars:"c"},
+ "c", "KeyC", nsIDOMKeyEvent.DOM_VK_C, "c", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_D,
+ modifiers:{}, chars:"d", unmodifiedChars:"d"},
+ "d", "KeyD", nsIDOMKeyEvent.DOM_VK_D, "d", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_D,
+ modifiers:{shiftKey:1}, chars:"D", unmodifiedChars:"D"},
+ "D", "KeyD", nsIDOMKeyEvent.DOM_VK_D, "D", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_D,
+ modifiers:{ctrlKey:1}, chars:"\u0004", unmodifiedChars:"d"},
+ "d", "KeyD", nsIDOMKeyEvent.DOM_VK_D, "d", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_D,
+ modifiers:{altKey:1}, chars:"\u2202", unmodifiedChars:"d"},
+ "\u2202", "KeyD", nsIDOMKeyEvent.DOM_VK_D, "\u2202", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_D,
+ modifiers:{metaKey:1}, chars:"d", unmodifiedChars:"d"},
+ "d", "KeyD", nsIDOMKeyEvent.DOM_VK_D, "d", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_E,
+ modifiers:{}, chars:"e", unmodifiedChars:"e"},
+ "e", "KeyE", nsIDOMKeyEvent.DOM_VK_E, "e", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_E,
+ modifiers:{shiftKey:1}, chars:"E", unmodifiedChars:"E"},
+ "E", "KeyE", nsIDOMKeyEvent.DOM_VK_E, "E", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_E,
+ modifiers:{ctrlKey:1}, chars:"\u0005", unmodifiedChars:"e"},
+ "e", "KeyE", nsIDOMKeyEvent.DOM_VK_E, "e", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_E,
+ modifiers:{altKey:1}, chars:"", unmodifiedChars:"e"},
+ "Dead", "KeyE", nsIDOMKeyEvent.DOM_VK_E, "\u00B4", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD); // dead key
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_E,
+ modifiers:{metaKey:1}, chars:"e", unmodifiedChars:"e"},
+ "e", "KeyE", nsIDOMKeyEvent.DOM_VK_E, "e", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_F,
+ modifiers:{}, chars:"f", unmodifiedChars:"f"},
+ "f", "KeyF", nsIDOMKeyEvent.DOM_VK_F, "f", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_F,
+ modifiers:{shiftKey:1}, chars:"F", unmodifiedChars:"F"},
+ "F", "KeyF", nsIDOMKeyEvent.DOM_VK_F, "F", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_F,
+ modifiers:{ctrlKey:1}, chars:"\u0006", unmodifiedChars:"f"},
+ "f", "KeyF", nsIDOMKeyEvent.DOM_VK_F, "f", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_F,
+ modifiers:{altKey:1}, chars:"\u0192", unmodifiedChars:"f"},
+ "\u0192", "KeyF", nsIDOMKeyEvent.DOM_VK_F, "\u0192", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // XXX This test starts fullscreen mode.
+ // yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_F,
+ // modifiers:{metaKey:1}, chars:"f", unmodifiedChars:"f"},
+ // "f", "KeyF", nsIDOMKeyEvent.DOM_VK_F, "f", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_G,
+ modifiers:{}, chars:"g", unmodifiedChars:"g"},
+ "g", "KeyG", nsIDOMKeyEvent.DOM_VK_G, "g", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_G,
+ modifiers:{shiftKey:1}, chars:"G", unmodifiedChars:"G"},
+ "G", "KeyG", nsIDOMKeyEvent.DOM_VK_G, "G", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_G,
+ modifiers:{ctrlKey:1}, chars:"\u0007", unmodifiedChars:"g"},
+ "g", "KeyG", nsIDOMKeyEvent.DOM_VK_G, "g", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_G,
+ modifiers:{altKey:1}, chars:"\u00A9", unmodifiedChars:"g"},
+ "\u00A9", "KeyG", nsIDOMKeyEvent.DOM_VK_G, "\u00A9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_G,
+ modifiers:{metaKey:1}, chars:"g", unmodifiedChars:"g"},
+ "g", "KeyG", nsIDOMKeyEvent.DOM_VK_G, "g", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_H,
+ modifiers:{}, chars:"h", unmodifiedChars:"h"},
+ "h", "KeyH", nsIDOMKeyEvent.DOM_VK_H, "h", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_H,
+ modifiers:{shiftKey:1}, chars:"H", unmodifiedChars:"H"},
+ "H", "KeyH", nsIDOMKeyEvent.DOM_VK_H, "H", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_H,
+ modifiers:{ctrlKey:1}, chars:"\u0008", unmodifiedChars:"h"},
+ "h", "KeyH", nsIDOMKeyEvent.DOM_VK_H, "h", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_H,
+ modifiers:{altKey:1}, chars:"\u02D9", unmodifiedChars:"h"},
+ "\u02D9", "KeyH", nsIDOMKeyEvent.DOM_VK_H, "\u02D9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_H,
+ modifiers:{metaKey:1}, chars:"h", unmodifiedChars:"h"},
+ "h", "KeyH", nsIDOMKeyEvent.DOM_VK_H, "h", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_I,
+ modifiers:{}, chars:"i", unmodifiedChars:"i"},
+ "i", "KeyI", nsIDOMKeyEvent.DOM_VK_I, "i", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_I,
+ modifiers:{shiftKey:1}, chars:"I", unmodifiedChars:"I"},
+ "I", "KeyI", nsIDOMKeyEvent.DOM_VK_I, "I", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_I,
+ modifiers:{ctrlKey:1}, chars:"\u0009", unmodifiedChars:"i"},
+ "i", "KeyI", nsIDOMKeyEvent.DOM_VK_I, "i", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_I,
+ modifiers:{altKey:1}, chars:"", unmodifiedChars:"i"},
+ "Dead", "KeyI", nsIDOMKeyEvent.DOM_VK_I, "\u02C6", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD); // dead key
+ // XXX This test causes memory leak.
+ // yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_I,
+ // modifiers:{metaKey:1}, chars:"i", unmodifiedChars:"i"},
+ // "i", "KeyI", nsIDOMKeyEvent.DOM_VK_I, "i", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_J,
+ modifiers:{}, chars:"j", unmodifiedChars:"j"},
+ "j", "KeyJ", nsIDOMKeyEvent.DOM_VK_J, "j", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_J,
+ modifiers:{shiftKey:1}, chars:"J", unmodifiedChars:"J"},
+ "J", "KeyJ", nsIDOMKeyEvent.DOM_VK_J, "J", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_J,
+ modifiers:{ctrlKey:1}, chars:"\u000A", unmodifiedChars:"j"},
+ "j", "KeyJ", nsIDOMKeyEvent.DOM_VK_J, "j", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_J,
+ modifiers:{altKey:1}, chars:"\u2206", unmodifiedChars:"j"},
+ "\u2206", "KeyJ", nsIDOMKeyEvent.DOM_VK_J, "\u2206", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_J,
+ modifiers:{metaKey:1}, chars:"j", unmodifiedChars:"j"},
+ "j", "KeyJ", nsIDOMKeyEvent.DOM_VK_J, "j", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_K,
+ modifiers:{}, chars:"k", unmodifiedChars:"k"},
+ "k", "KeyK", nsIDOMKeyEvent.DOM_VK_K, "k", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_K,
+ modifiers:{shiftKey:1}, chars:"K", unmodifiedChars:"K"},
+ "K", "KeyK", nsIDOMKeyEvent.DOM_VK_K, "K", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_K,
+ modifiers:{ctrlKey:1}, chars:"\u000B", unmodifiedChars:"k"},
+ "k", "KeyK", nsIDOMKeyEvent.DOM_VK_K, "k", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_K,
+ modifiers:{altKey:1}, chars:"\u02DA", unmodifiedChars:"k"},
+ "\u02DA", "KeyK", nsIDOMKeyEvent.DOM_VK_K, "\u02DA", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_K,
+ modifiers:{metaKey:1}, chars:"k", unmodifiedChars:"k"},
+ "k", "KeyK", nsIDOMKeyEvent.DOM_VK_K, "k", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_L,
+ modifiers:{}, chars:"l", unmodifiedChars:"l"},
+ "l", "KeyL", nsIDOMKeyEvent.DOM_VK_L, "l", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_L,
+ modifiers:{shiftKey:1}, chars:"L", unmodifiedChars:"L"},
+ "L", "KeyL", nsIDOMKeyEvent.DOM_VK_L, "L", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_L,
+ modifiers:{ctrlKey:1}, chars:"\u000C", unmodifiedChars:"l"},
+ "l", "KeyL", nsIDOMKeyEvent.DOM_VK_L, "l", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_L,
+ modifiers:{altKey:1}, chars:"\u00AC", unmodifiedChars:"l"},
+ "\u00AC", "KeyL", nsIDOMKeyEvent.DOM_VK_L, "\u00AC", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_L,
+ modifiers:{metaKey:1}, chars:"l", unmodifiedChars:"l"},
+ "l", "KeyL", nsIDOMKeyEvent.DOM_VK_L, "l", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_M,
+ modifiers:{}, chars:"m", unmodifiedChars:"m"},
+ "m", "KeyM", nsIDOMKeyEvent.DOM_VK_M, "m", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_M,
+ modifiers:{shiftKey:1}, chars:"M", unmodifiedChars:"M"},
+ "M", "KeyM", nsIDOMKeyEvent.DOM_VK_M, "M", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_M,
+ modifiers:{ctrlKey:1}, chars:"\u000D", unmodifiedChars:"m"},
+ "m", "KeyM", nsIDOMKeyEvent.DOM_VK_M, "m", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_M,
+ modifiers:{altKey:1}, chars:"\u00B5", unmodifiedChars:"m"},
+ "\u00B5", "KeyM", nsIDOMKeyEvent.DOM_VK_M, "\u00B5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_M,
+ modifiers:{metaKey:1}, chars:"m", unmodifiedChars:"m"},
+ "m", "KeyM", nsIDOMKeyEvent.DOM_VK_M, "m", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_N,
+ modifiers:{}, chars:"n", unmodifiedChars:"n"},
+ "n", "KeyN", nsIDOMKeyEvent.DOM_VK_N, "n", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_N,
+ modifiers:{shiftKey:1}, chars:"N", unmodifiedChars:"N"},
+ "N", "KeyN", nsIDOMKeyEvent.DOM_VK_N, "N", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_N,
+ modifiers:{ctrlKey:1}, chars:"\u000E", unmodifiedChars:"n"},
+ "n", "KeyN", nsIDOMKeyEvent.DOM_VK_N, "n", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_N,
+ modifiers:{altKey:1}, chars:"", unmodifiedChars:"n"},
+ "Dead", "KeyN", nsIDOMKeyEvent.DOM_VK_N, "\u02DC", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD); // dead key
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_N,
+ modifiers:{metaKey:1}, chars:"n", unmodifiedChars:"n"},
+ "n", "KeyN", nsIDOMKeyEvent.DOM_VK_N, "n", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_O,
+ modifiers:{}, chars:"o", unmodifiedChars:"o"},
+ "o", "KeyO", nsIDOMKeyEvent.DOM_VK_O, "o", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_O,
+ modifiers:{shiftKey:1}, chars:"O", unmodifiedChars:"O"},
+ "O", "KeyO", nsIDOMKeyEvent.DOM_VK_O, "O", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_O,
+ modifiers:{ctrlKey:1}, chars:"\u000F", unmodifiedChars:"o"},
+ "o", "KeyO", nsIDOMKeyEvent.DOM_VK_O, "o", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_O,
+ modifiers:{altKey:1}, chars:"\u00F8", unmodifiedChars:"o"},
+ "\u00F8", "KeyO", nsIDOMKeyEvent.DOM_VK_O, "\u00F8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_O,
+ modifiers:{metaKey:1}, chars:"o", unmodifiedChars:"o"},
+ "o", "KeyO", nsIDOMKeyEvent.DOM_VK_O, "o", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_P,
+ modifiers:{}, chars:"p", unmodifiedChars:"p"},
+ "p", "KeyP", nsIDOMKeyEvent.DOM_VK_P, "p", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_P,
+ modifiers:{shiftKey:1}, chars:"P", unmodifiedChars:"P"},
+ "P", "KeyP", nsIDOMKeyEvent.DOM_VK_P, "P", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_P,
+ modifiers:{ctrlKey:1}, chars:"\u0010", unmodifiedChars:"p"},
+ "p", "KeyP", nsIDOMKeyEvent.DOM_VK_P, "p", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_P,
+ modifiers:{altKey:1}, chars:"\u03C0", unmodifiedChars:"p"},
+ "\u03C0", "KeyP", nsIDOMKeyEvent.DOM_VK_P, "\u03C0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // XXX This test starts private browsing mode (stopped at the confirmation dialog)
+ // yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_P,
+ // modifiers:{metaKey:1}, chars:"p", unmodifiedChars:"p"},
+ // "p", "KeyP", nsIDOMKeyEvent.DOM_VK_P, "p", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Q,
+ modifiers:{}, chars:"q", unmodifiedChars:"q"},
+ "q", "KeyQ", nsIDOMKeyEvent.DOM_VK_Q, "q", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Q,
+ modifiers:{shiftKey:1}, chars:"Q", unmodifiedChars:"Q"},
+ "Q", "KeyQ", nsIDOMKeyEvent.DOM_VK_Q, "Q", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Q,
+ modifiers:{ctrlKey:1}, chars:"\u0011", unmodifiedChars:"q"},
+ "q", "KeyQ", nsIDOMKeyEvent.DOM_VK_Q, "q", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Q,
+ modifiers:{altKey:1}, chars:"\u0153", unmodifiedChars:"q"},
+ "\u0153", "KeyQ", nsIDOMKeyEvent.DOM_VK_Q, "\u0153", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Q,
+ modifiers:{metaKey:1}, chars:"q", unmodifiedChars:"q"},
+ "q", "KeyQ", nsIDOMKeyEvent.DOM_VK_Q, "q", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_R,
+ modifiers:{}, chars:"r", unmodifiedChars:"r"},
+ "r", "KeyR", nsIDOMKeyEvent.DOM_VK_R, "r", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_R,
+ modifiers:{shiftKey:1}, chars:"R", unmodifiedChars:"R"},
+ "R", "KeyR", nsIDOMKeyEvent.DOM_VK_R, "R", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_R,
+ modifiers:{ctrlKey:1}, chars:"\u0012", unmodifiedChars:"r"},
+ "r", "KeyR", nsIDOMKeyEvent.DOM_VK_R, "r", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_R,
+ modifiers:{altKey:1}, chars:"\u00AE", unmodifiedChars:"r"},
+ "\u00AE", "KeyR", nsIDOMKeyEvent.DOM_VK_R, "\u00AE", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // XXX This test makes some tabs and dialogs.
+ // yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_R,
+ // modifiers:{metaKey:1}, chars:"r", unmodifiedChars:"r"},
+ // "r", "KeyR", nsIDOMKeyEvent.DOM_VK_R, "r", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_S,
+ modifiers:{}, chars:"s", unmodifiedChars:"s"},
+ "s", "KeyS", nsIDOMKeyEvent.DOM_VK_S, "s", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_S,
+ modifiers:{shiftKey:1}, chars:"S", unmodifiedChars:"S"},
+ "S", "KeyS", nsIDOMKeyEvent.DOM_VK_S, "S", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_S,
+ modifiers:{ctrlKey:1}, chars:"\u0013", unmodifiedChars:"s"},
+ "s", "KeyS", nsIDOMKeyEvent.DOM_VK_S, "s", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_S,
+ modifiers:{altKey:1}, chars:"\u00DF", unmodifiedChars:"s"},
+ "\u00DF", "KeyS", nsIDOMKeyEvent.DOM_VK_S, "\u00DF", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_S,
+ modifiers:{metaKey:1}, chars:"s", unmodifiedChars:"s"},
+ "s", "KeyS", nsIDOMKeyEvent.DOM_VK_S, "s", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_T,
+ modifiers:{}, chars:"t", unmodifiedChars:"t"},
+ "t", "KeyT", nsIDOMKeyEvent.DOM_VK_T, "t", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_T,
+ modifiers:{shiftKey:1}, chars:"T", unmodifiedChars:"T"},
+ "T", "KeyT", nsIDOMKeyEvent.DOM_VK_T, "T", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_T,
+ modifiers:{ctrlKey:1}, chars:"\u0014", unmodifiedChars:"t"},
+ "t", "KeyT", nsIDOMKeyEvent.DOM_VK_T, "t", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_T,
+ modifiers:{altKey:1}, chars:"\u2020", unmodifiedChars:"t"},
+ "\u2020", "KeyT", nsIDOMKeyEvent.DOM_VK_T, "\u2020", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_T,
+ modifiers:{metaKey:1}, chars:"t", unmodifiedChars:"t"},
+ "t", "KeyT", nsIDOMKeyEvent.DOM_VK_T, "t", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_U,
+ modifiers:{}, chars:"u", unmodifiedChars:"u"},
+ "u", "KeyU", nsIDOMKeyEvent.DOM_VK_U, "u", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_U,
+ modifiers:{shiftKey:1}, chars:"U", unmodifiedChars:"U"},
+ "U", "KeyU", nsIDOMKeyEvent.DOM_VK_U, "U", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_U,
+ modifiers:{ctrlKey:1}, chars:"\u0015", unmodifiedChars:"u"},
+ "u", "KeyU", nsIDOMKeyEvent.DOM_VK_U, "u", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_U,
+ modifiers:{altKey:1}, chars:"", unmodifiedChars:"u"},
+ "Dead", "KeyU", nsIDOMKeyEvent.DOM_VK_U, "\u00A8", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD); // dead key
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_U,
+ modifiers:{metaKey:1}, chars:"u", unmodifiedChars:"u"},
+ "u", "KeyU", nsIDOMKeyEvent.DOM_VK_U, "u", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_V,
+ modifiers:{}, chars:"v", unmodifiedChars:"v"},
+ "v", "KeyV", nsIDOMKeyEvent.DOM_VK_V, "v", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_V,
+ modifiers:{shiftKey:1}, chars:"V", unmodifiedChars:"V"},
+ "V", "KeyV", nsIDOMKeyEvent.DOM_VK_V, "V", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_V,
+ modifiers:{ctrlKey:1}, chars:"\u0016", unmodifiedChars:"v"},
+ "v", "KeyV", nsIDOMKeyEvent.DOM_VK_V, "v", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_V,
+ modifiers:{altKey:1}, chars:"\u221A", unmodifiedChars:"v"},
+ "\u221A", "KeyV", nsIDOMKeyEvent.DOM_VK_V, "\u221A", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_V,
+ modifiers:{metaKey:1}, chars:"v", unmodifiedChars:"v"},
+ "v", "KeyV", nsIDOMKeyEvent.DOM_VK_V, "v", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_W,
+ modifiers:{}, chars:"w", unmodifiedChars:"w"},
+ "w", "KeyW", nsIDOMKeyEvent.DOM_VK_W, "w", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_W,
+ modifiers:{shiftKey:1}, chars:"W", unmodifiedChars:"W"},
+ "W", "KeyW", nsIDOMKeyEvent.DOM_VK_W, "W", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_W,
+ modifiers:{ctrlKey:1}, chars:"\u0017", unmodifiedChars:"w"},
+ "w", "KeyW", nsIDOMKeyEvent.DOM_VK_W, "w", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_W,
+ modifiers:{altKey:1}, chars:"\u2211", unmodifiedChars:"w"},
+ "\u2211", "KeyW", nsIDOMKeyEvent.DOM_VK_W, "\u2211", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_W,
+ modifiers:{metaKey:1}, chars:"w", unmodifiedChars:"w"},
+ "w", "KeyW", nsIDOMKeyEvent.DOM_VK_W, "w", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_X,
+ modifiers:{}, chars:"x", unmodifiedChars:"x"},
+ "x", "KeyX", nsIDOMKeyEvent.DOM_VK_X, "x", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_X,
+ modifiers:{shiftKey:1}, chars:"X", unmodifiedChars:"X"},
+ "X", "KeyX", nsIDOMKeyEvent.DOM_VK_X, "X", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_X,
+ modifiers:{ctrlKey:1}, chars:"\u0018", unmodifiedChars:"x"},
+ "x", "KeyX", nsIDOMKeyEvent.DOM_VK_X, "x", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_X,
+ modifiers:{altKey:1}, chars:"\u2248", unmodifiedChars:"x"},
+ "\u2248", "KeyX", nsIDOMKeyEvent.DOM_VK_X, "\u2248", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_X,
+ modifiers:{metaKey:1}, chars:"x", unmodifiedChars:"x"},
+ "x", "KeyX", nsIDOMKeyEvent.DOM_VK_X, "x", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Y,
+ modifiers:{}, chars:"y", unmodifiedChars:"y"},
+ "y", "KeyY", nsIDOMKeyEvent.DOM_VK_Y, "y", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Y,
+ modifiers:{shiftKey:1}, chars:"Y", unmodifiedChars:"Y"},
+ "Y", "KeyY", nsIDOMKeyEvent.DOM_VK_Y, "Y", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Y,
+ modifiers:{ctrlKey:1}, chars:"\u0019", unmodifiedChars:"y"},
+ "y", "KeyY", nsIDOMKeyEvent.DOM_VK_Y, "y", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Y,
+ modifiers:{altKey:1}, chars:"\u00A5", unmodifiedChars:"y"},
+ "\u00A5", "KeyY", nsIDOMKeyEvent.DOM_VK_Y, "\u00A5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Y,
+ modifiers:{metaKey:1}, chars:"y", unmodifiedChars:"y"},
+ "y", "KeyY", nsIDOMKeyEvent.DOM_VK_Y, "y", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Z,
+ modifiers:{}, chars:"z", unmodifiedChars:"z"},
+ "z", "KeyZ", nsIDOMKeyEvent.DOM_VK_Z, "z", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Z,
+ modifiers:{shiftKey:1}, chars:"Z", unmodifiedChars:"Z"},
+ "Z", "KeyZ", nsIDOMKeyEvent.DOM_VK_Z, "Z", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Z,
+ modifiers:{ctrlKey:1}, chars:"\u001A", unmodifiedChars:"z"},
+ "z", "KeyZ", nsIDOMKeyEvent.DOM_VK_Z, "z", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Z,
+ modifiers:{altKey:1}, chars:"\u03A9", unmodifiedChars:"z"},
+ "\u03A9", "KeyZ", nsIDOMKeyEvent.DOM_VK_Z, "\u03A9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Z,
+ modifiers:{metaKey:1}, chars:"z", unmodifiedChars:"z"},
+ "z", "KeyZ", nsIDOMKeyEvent.DOM_VK_Z, "z", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // numeric
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_1,
+ modifiers:{}, chars:"1", unmodifiedChars:"1"},
+ "1", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_1,
+ modifiers:{shiftKey:1}, chars:"!", unmodifiedChars:"!"},
+ "!", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "!", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_1,
+ modifiers:{ctrlKey:1}, chars:"1", unmodifiedChars:"1"},
+ "1", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_1,
+ modifiers:{altKey:1}, chars:"\u00A1", unmodifiedChars:"1"},
+ "\u00A1", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "\u00A1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_1,
+ modifiers:{metaKey:1}, chars:"1", unmodifiedChars:"1"},
+ "1", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "1", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_2,
+ modifiers:{}, chars:"2", unmodifiedChars:"2"},
+ "2", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_2,
+ modifiers:{shiftKey:1}, chars:"@", unmodifiedChars:"@"},
+ "@", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "@", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_2,
+ modifiers:{ctrlKey:1}, chars:"2", unmodifiedChars:"2"},
+ "2", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_2,
+ modifiers:{altKey:1}, chars:"\u00A1", unmodifiedChars:"2"},
+ "\u00A1", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "\u00A1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_2,
+ modifiers:{metaKey:1}, chars:"2", unmodifiedChars:"2"},
+ "2", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "2", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_3,
+ modifiers:{}, chars:"3", unmodifiedChars:"3"},
+ "3", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "3", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_3,
+ modifiers:{shiftKey:1}, chars:"#", unmodifiedChars:"#"},
+ "#", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "#", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_3,
+ modifiers:{ctrlKey:1}, chars:"3", unmodifiedChars:"3"},
+ "3", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "3", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_3,
+ modifiers:{altKey:1}, chars:"\u00A3", unmodifiedChars:"3"},
+ "\u00A3", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "\u00A3", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_3,
+ modifiers:{metaKey:1}, chars:"3", unmodifiedChars:"3"},
+ "3", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "3", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_4,
+ modifiers:{}, chars:"4", unmodifiedChars:"4"},
+ "4", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_4,
+ modifiers:{shiftKey:1}, chars:"$", unmodifiedChars:"$"},
+ "$", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "$", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_4,
+ modifiers:{ctrlKey:1}, chars:"4", unmodifiedChars:"4"},
+ "4", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_4,
+ modifiers:{altKey:1}, chars:"\u00A2", unmodifiedChars:"4"},
+ "\u00A2", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "\u00A2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_4,
+ modifiers:{metaKey:1}, chars:"4", unmodifiedChars:"4"},
+ "4", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "4", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_5,
+ modifiers:{}, chars:"5", unmodifiedChars:"5"},
+ "5", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_5,
+ modifiers:{shiftKey:1}, chars:"%", unmodifiedChars:"%"},
+ "%", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "%", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_5,
+ modifiers:{ctrlKey:1}, chars:"5", unmodifiedChars:"5"},
+ "5", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_5,
+ modifiers:{altKey:1}, chars:"\u221E", unmodifiedChars:"5"},
+ "\u221E", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "\u221E", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_5,
+ modifiers:{metaKey:1}, chars:"5", unmodifiedChars:"5"},
+ "5", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "5", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_6,
+ modifiers:{}, chars:"6", unmodifiedChars:"6"},
+ "6", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "6", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_6,
+ modifiers:{shiftKey:1}, chars:"^", unmodifiedChars:"^"},
+ "^", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "^", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_6,
+ modifiers:{ctrlKey:1}, chars:"6", unmodifiedChars:"6"},
+ "6", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "6", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_6,
+ modifiers:{altKey:1}, chars:"\u00A7", unmodifiedChars:"6"},
+ "\u00A7", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "\u00A7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_6,
+ modifiers:{metaKey:1}, chars:"6", unmodifiedChars:"6"},
+ "6", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "6", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_7,
+ modifiers:{}, chars:"7", unmodifiedChars:"7"},
+ "7", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_7,
+ modifiers:{shiftKey:1}, chars:"&", unmodifiedChars:"&"},
+ "&", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "&", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_7,
+ modifiers:{ctrlKey:1}, chars:"7", unmodifiedChars:"7"},
+ "7", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_7,
+ modifiers:{altKey:1}, chars:"\u00B6", unmodifiedChars:"7"},
+ "\u00B6", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "\u00B6", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_7,
+ modifiers:{metaKey:1}, chars:"7", unmodifiedChars:"7"},
+ "7", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "7", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_8,
+ modifiers:{}, chars:"8", unmodifiedChars:"8"},
+ "8", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_8,
+ modifiers:{shiftKey:1}, chars:"*", unmodifiedChars:"*"},
+ "*", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "*", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_8,
+ modifiers:{ctrlKey:1}, chars:"8", unmodifiedChars:"8"},
+ "8", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_8,
+ modifiers:{altKey:1}, chars:"\u2022", unmodifiedChars:"8"},
+ "\u2022", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "\u2022", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_8,
+ modifiers:{metaKey:1}, chars:"8", unmodifiedChars:"8"},
+ "8", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "8", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_9,
+ modifiers:{}, chars:"9", unmodifiedChars:"9"},
+ "9", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_9,
+ modifiers:{shiftKey:1}, chars:"(", unmodifiedChars:"("},
+ "(", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "(", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_9,
+ modifiers:{ctrlKey:1}, chars:"9", unmodifiedChars:"9"},
+ "9", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_9,
+ modifiers:{altKey:1}, chars:"\u00AA", unmodifiedChars:"9"},
+ "\u00AA", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "\u00AA", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_9,
+ modifiers:{metaKey:1}, chars:"9", unmodifiedChars:"9"},
+ "9", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "9", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_0,
+ modifiers:{}, chars:"0", unmodifiedChars:"0"},
+ "0", "Digit0", nsIDOMKeyEvent.DOM_VK_0, "0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_0,
+ modifiers:{shiftKey:1}, chars:")", unmodifiedChars:")"},
+ ")", "Digit0", nsIDOMKeyEvent.DOM_VK_0, ")", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_0,
+ modifiers:{ctrlKey:1}, chars:"0", unmodifiedChars:"0"},
+ "0", "Digit0", nsIDOMKeyEvent.DOM_VK_0, "0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_0,
+ modifiers:{altKey:1}, chars:"\u00BA", unmodifiedChars:"0"},
+ "\u00BA", "Digit0", nsIDOMKeyEvent.DOM_VK_0, "\u00BA", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_0,
+ modifiers:{metaKey:1}, chars:"0", unmodifiedChars:"0"},
+ "0", "Digit0", nsIDOMKeyEvent.DOM_VK_0, "0", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // other chracters
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Grave,
+ modifiers:{}, chars:"`", unmodifiedChars:"`"},
+ "`", "Backquote", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "`", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Grave,
+ modifiers:{shiftKey:1}, chars:"~", unmodifiedChars:"~"},
+ "~", "Backquote", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "~", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Grave,
+ modifiers:{ctrlKey:1}, chars:"`", unmodifiedChars:"`"},
+ "`", "Backquote", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "`", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Grave,
+ modifiers:{altKey:1}, chars:"", unmodifiedChars:"`"},
+ "Dead", "Backquote", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "`", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD); // dead key
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Grave,
+ modifiers:{metaKey:1}, chars:"`", unmodifiedChars:"`"},
+ "`", "Backquote", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "`", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Minus,
+ modifiers:{}, chars:"-", unmodifiedChars:"-"},
+ "-", "Minus", nsIDOMKeyEvent.DOM_VK_HYPHEN_MINUS, "-", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Minus,
+ modifiers:{shiftKey:1}, chars:"_", unmodifiedChars:"_"},
+ "_", "Minus", nsIDOMKeyEvent.DOM_VK_HYPHEN_MINUS, "_", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // TODO:
+ // yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Minus,
+ // modifiers:{ctrlKey:1}, chars:"\u001F", unmodifiedChars:"-"},
+ // "-", "Minus", nsIDOMKeyEvent.DOM_VK_HYPHEN_MINUS, "-", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Minus,
+ modifiers:{altKey:1}, chars:"\u2013", unmodifiedChars:"-"},
+ "\u2013", "Minus", nsIDOMKeyEvent.DOM_VK_HYPHEN_MINUS, "\u2013", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Minus,
+ modifiers:{metaKey:1}, chars:"-", unmodifiedChars:"-"},
+ "-", "Minus", nsIDOMKeyEvent.DOM_VK_HYPHEN_MINUS, "-", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Equal,
+ modifiers:{}, chars:"=", unmodifiedChars:"="},
+ "=", "Equal", nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Equal,
+ modifiers:{shiftKey:1}, chars:"+", unmodifiedChars:"+"},
+ "+", "Equal", nsIDOMKeyEvent.DOM_VK_EQUALS, "+", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Equal,
+ modifiers:{ctrlKey:1}, chars:"=", unmodifiedChars:"="},
+ "=", "Equal", nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Equal,
+ modifiers:{altKey:1}, chars:"\u2260", unmodifiedChars:"="},
+ "\u2260", "Equal", nsIDOMKeyEvent.DOM_VK_EQUALS, "\u2260", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Equal,
+ modifiers:{metaKey:1}, chars:"=", unmodifiedChars:"="},
+ "=", "Equal", nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_LeftBracket,
+ modifiers:{}, chars:"[", unmodifiedChars:"["},
+ "[", "BracketLeft", nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET, "[", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_LeftBracket,
+ modifiers:{shiftKey:1}, chars:"{", unmodifiedChars:"{"},
+ "{", "BracketLeft", nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET, "{", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // TODO:
+ // yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_LeftBracket,
+ // modifiers:{ctrlKey:1}, chars:"\u001B", unmodifiedChars:"["},
+ // "[", "LeftBracket", nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET, "[", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_LeftBracket,
+ modifiers:{altKey:1}, chars:"\u201C", unmodifiedChars:"["},
+ "\u201C", "BracketLeft", nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET, "\u201C", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_LeftBracket,
+ modifiers:{metaKey:1}, chars:"[", unmodifiedChars:"["},
+ "[", "BracketLeft", nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET, "[", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_RightBracket,
+ modifiers:{}, chars:"]", unmodifiedChars:"]"},
+ "]", "BracketRight", nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET, "]", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_RightBracket,
+ modifiers:{shiftKey:1}, chars:"}", unmodifiedChars:"}"},
+ "}", "BracketRight", nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET, "}", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // TODO:
+ // yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_RightBracket,
+ // modifiers:{ctrlKey:1}, chars:"\u001D", unmodifiedChars:"]"},
+ // "]", "BracketRight", nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET, "]", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_RightBracket,
+ modifiers:{altKey:1}, chars:"\u2018", unmodifiedChars:"]"},
+ "\u2018", "BracketRight", nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET, "\u2018", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_RightBracket,
+ modifiers:{metaKey:1}, chars:"]", unmodifiedChars:"]"},
+ "]", "BracketRight", nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET, "]", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Backslash,
+ modifiers:{}, chars:"\\", unmodifiedChars:"\\"},
+ "\\", "Backslash", nsIDOMKeyEvent.DOM_VK_BACK_SLASH, "\\", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Backslash,
+ modifiers:{shiftKey:1}, chars:"|", unmodifiedChars:"|"},
+ "|", "Backslash", nsIDOMKeyEvent.DOM_VK_BACK_SLASH, "|", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // TODO:
+ // yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Backslash,
+ // modifiers:{ctrlKey:1}, chars:"\u001C", unmodifiedChars:"\\"},
+ // "\\", "Backslash", nsIDOMKeyEvent.DOM_VK_BACK_SLASH, "\\", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Backslash,
+ modifiers:{altKey:1}, chars:"\u00AB", unmodifiedChars:"\\"},
+ "\u00AB", "Backslash", nsIDOMKeyEvent.DOM_VK_BACK_SLASH, "\u00AB", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Backslash,
+ modifiers:{metaKey:1}, chars:"\\", unmodifiedChars:"\\"},
+ "\\", "Backslash", nsIDOMKeyEvent.DOM_VK_BACK_SLASH, "\\", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Semicolon,
+ modifiers:{}, chars:";", unmodifiedChars:";"},
+ ";", "Semicolon", nsIDOMKeyEvent.DOM_VK_SEMICOLON, ";", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Semicolon,
+ modifiers:{shiftKey:1}, chars:":", unmodifiedChars:":"},
+ ":", "Semicolon", nsIDOMKeyEvent.DOM_VK_SEMICOLON, ":", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Semicolon,
+ modifiers:{ctrlKey:1}, chars:";", unmodifiedChars:";"},
+ ";", "Semicolon", nsIDOMKeyEvent.DOM_VK_SEMICOLON, ";", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Semicolon,
+ modifiers:{altKey:1}, chars:"\u2026", unmodifiedChars:";"},
+ "\u2026", "Semicolon", nsIDOMKeyEvent.DOM_VK_SEMICOLON, "\u2026", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Semicolon,
+ modifiers:{metaKey:1}, chars:";", unmodifiedChars:";"},
+ ";", "Semicolon", nsIDOMKeyEvent.DOM_VK_SEMICOLON, ";", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Quote,
+ modifiers:{}, chars:"'", unmodifiedChars:"'"},
+ "'", "Quote", nsIDOMKeyEvent.DOM_VK_QUOTE, "'", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Quote,
+ modifiers:{shiftKey:1}, chars:"\"", unmodifiedChars:"\""},
+ "\"", "Quote", nsIDOMKeyEvent.DOM_VK_QUOTE, "\"", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Quote,
+ modifiers:{ctrlKey:1}, chars:"'", unmodifiedChars:"'"},
+ "'", "Quote", nsIDOMKeyEvent.DOM_VK_QUOTE, "'", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Quote,
+ modifiers:{altKey:1}, chars:"\u00E6", unmodifiedChars:"'"},
+ "\u00E6", "Quote", nsIDOMKeyEvent.DOM_VK_QUOTE, "\u00E6", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Quote,
+ modifiers:{metaKey:1}, chars:"'", unmodifiedChars:"'"},
+ "'", "Quote", nsIDOMKeyEvent.DOM_VK_QUOTE, "'", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Comma,
+ modifiers:{}, chars:",", unmodifiedChars:","},
+ ",", "Comma", nsIDOMKeyEvent.DOM_VK_COMMA, ",", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Comma,
+ modifiers:{shiftKey:1}, chars:"<", unmodifiedChars:"<"},
+ "<", "Comma", nsIDOMKeyEvent.DOM_VK_COMMA, "<", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Comma,
+ modifiers:{ctrlKey:1}, chars:",", unmodifiedChars:","},
+ ",", "Comma", nsIDOMKeyEvent.DOM_VK_COMMA, ",", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Comma,
+ modifiers:{altKey:1}, chars:"\u2264", unmodifiedChars:","},
+ "\u2264", "Comma", nsIDOMKeyEvent.DOM_VK_COMMA, "\u2264", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Comma,
+ modifiers:{metaKey:1}, chars:",", unmodifiedChars:","},
+ ",", "Comma", nsIDOMKeyEvent.DOM_VK_COMMA, ",", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Period,
+ modifiers:{}, chars:".", unmodifiedChars:"."},
+ ".", "Period", nsIDOMKeyEvent.DOM_VK_PERIOD, ".", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Period,
+ modifiers:{shiftKey:1}, chars:">", unmodifiedChars:">"},
+ ">", "Period", nsIDOMKeyEvent.DOM_VK_PERIOD, ">", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Period,
+ modifiers:{ctrlKey:1}, chars:".", unmodifiedChars:"."},
+ ".", "Period", nsIDOMKeyEvent.DOM_VK_PERIOD, ".", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Period,
+ modifiers:{altKey:1}, chars:"\u2265", unmodifiedChars:"."},
+ "\u2265", "Period", nsIDOMKeyEvent.DOM_VK_PERIOD, "\u2265", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Period,
+ modifiers:{metaKey:1}, chars:".", unmodifiedChars:"."},
+ ".", "Period", nsIDOMKeyEvent.DOM_VK_PERIOD, ".", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Slash,
+ modifiers:{}, chars:"/", unmodifiedChars:"/"},
+ "/", "Slash", nsIDOMKeyEvent.DOM_VK_SLASH, "/", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Slash,
+ modifiers:{shiftKey:1}, chars:"?", unmodifiedChars:"?"},
+ "?", "Slash", nsIDOMKeyEvent.DOM_VK_SLASH, "?", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Slash,
+ modifiers:{ctrlKey:1}, chars:"/", unmodifiedChars:"/"},
+ "/", "Slash", nsIDOMKeyEvent.DOM_VK_SLASH, "/", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Slash,
+ modifiers:{altKey:1}, chars:"\u00F7", unmodifiedChars:"/"},
+ "\u00F7", "Slash", nsIDOMKeyEvent.DOM_VK_SLASH, "\u00F7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Slash,
+ modifiers:{metaKey:1}, chars:"/", unmodifiedChars:"/"},
+ "/", "Slash", nsIDOMKeyEvent.DOM_VK_SLASH, "/", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // numpad
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad1,
+ modifiers:{numericKeyPadKey:1}, chars:"1", unmodifiedChars:"1"},
+ "1", "Numpad1", nsIDOMKeyEvent.DOM_VK_NUMPAD1, "1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad1,
+ modifiers:{numericKeyPadKey:1, shiftKey:1}, chars:"1", unmodifiedChars:"1"},
+ "1", "Numpad1", nsIDOMKeyEvent.DOM_VK_NUMPAD1, "1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad1,
+ modifiers:{numericKeyPadKey:1, ctrlKey:1}, chars:"1", unmodifiedChars:"1"},
+ "1", "Numpad1", nsIDOMKeyEvent.DOM_VK_NUMPAD1, "1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad1,
+ modifiers:{numericKeyPadKey:1, altKey:1}, chars:"1", unmodifiedChars:"1"},
+ "1", "Numpad1", nsIDOMKeyEvent.DOM_VK_NUMPAD1, "1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad1,
+ modifiers:{numericKeyPadKey:1, metaKey:1}, chars:"1", unmodifiedChars:"1"},
+ "1", "Numpad1", nsIDOMKeyEvent.DOM_VK_NUMPAD1, "1", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad2,
+ modifiers:{numericKeyPadKey:1}, chars:"2", unmodifiedChars:"2"},
+ "2", "Numpad2", nsIDOMKeyEvent.DOM_VK_NUMPAD2, "2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad2,
+ modifiers:{numericKeyPadKey:1, shiftKey:1}, chars:"2", unmodifiedChars:"2"},
+ "2", "Numpad2", nsIDOMKeyEvent.DOM_VK_NUMPAD2, "2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad2,
+ modifiers:{numericKeyPadKey:1, ctrlKey:1}, chars:"2", unmodifiedChars:"2"},
+ "2", "Numpad2", nsIDOMKeyEvent.DOM_VK_NUMPAD2, "2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad2,
+ modifiers:{numericKeyPadKey:1, altKey:1}, chars:"2", unmodifiedChars:"2"},
+ "2", "Numpad2", nsIDOMKeyEvent.DOM_VK_NUMPAD2, "2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad2,
+ modifiers:{numericKeyPadKey:1, metaKey:1}, chars:"2", unmodifiedChars:"2"},
+ "2", "Numpad2", nsIDOMKeyEvent.DOM_VK_NUMPAD2, "2", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad3,
+ modifiers:{numericKeyPadKey:1}, chars:"3", unmodifiedChars:"3"},
+ "3", "Numpad3", nsIDOMKeyEvent.DOM_VK_NUMPAD3, "3", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad3,
+ modifiers:{numericKeyPadKey:1, shiftKey:1}, chars:"3", unmodifiedChars:"3"},
+ "3", "Numpad3", nsIDOMKeyEvent.DOM_VK_NUMPAD3, "3", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad3,
+ modifiers:{numericKeyPadKey:1, ctrlKey:1}, chars:"3", unmodifiedChars:"3"},
+ "3", "Numpad3", nsIDOMKeyEvent.DOM_VK_NUMPAD3, "3", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad3,
+ modifiers:{numericKeyPadKey:1, altKey:1}, chars:"3", unmodifiedChars:"3"},
+ "3", "Numpad3", nsIDOMKeyEvent.DOM_VK_NUMPAD3, "3", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad3,
+ modifiers:{numericKeyPadKey:1, metaKey:1}, chars:"3", unmodifiedChars:"3"},
+ "3", "Numpad3", nsIDOMKeyEvent.DOM_VK_NUMPAD3, "3", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad4,
+ modifiers:{numericKeyPadKey:1}, chars:"4", unmodifiedChars:"4"},
+ "4", "Numpad4", nsIDOMKeyEvent.DOM_VK_NUMPAD4, "4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad4,
+ modifiers:{numericKeyPadKey:1, shiftKey:1}, chars:"4", unmodifiedChars:"4"},
+ "4", "Numpad4", nsIDOMKeyEvent.DOM_VK_NUMPAD4, "4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad4,
+ modifiers:{numericKeyPadKey:1, ctrlKey:1}, chars:"4", unmodifiedChars:"4"},
+ "4", "Numpad4", nsIDOMKeyEvent.DOM_VK_NUMPAD4, "4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad4,
+ modifiers:{numericKeyPadKey:1, altKey:1}, chars:"4", unmodifiedChars:"4"},
+ "4", "Numpad4", nsIDOMKeyEvent.DOM_VK_NUMPAD4, "4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad4,
+ modifiers:{numericKeyPadKey:1, metaKey:1}, chars:"4", unmodifiedChars:"4"},
+ "4", "Numpad4", nsIDOMKeyEvent.DOM_VK_NUMPAD4, "4", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad5,
+ modifiers:{numericKeyPadKey:1}, chars:"5", unmodifiedChars:"5"},
+ "5", "Numpad5", nsIDOMKeyEvent.DOM_VK_NUMPAD5, "5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad5,
+ modifiers:{numericKeyPadKey:1, shiftKey:1}, chars:"5", unmodifiedChars:"5"},
+ "5", "Numpad5", nsIDOMKeyEvent.DOM_VK_NUMPAD5, "5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad5,
+ modifiers:{numericKeyPadKey:1, ctrlKey:1}, chars:"5", unmodifiedChars:"5"},
+ "5", "Numpad5", nsIDOMKeyEvent.DOM_VK_NUMPAD5, "5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad5,
+ modifiers:{numericKeyPadKey:1, altKey:1}, chars:"5", unmodifiedChars:"5"},
+ "5", "Numpad5", nsIDOMKeyEvent.DOM_VK_NUMPAD5, "5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad5,
+ modifiers:{numericKeyPadKey:1, metaKey:1}, chars:"5", unmodifiedChars:"5"},
+ "5", "Numpad5", nsIDOMKeyEvent.DOM_VK_NUMPAD5, "5", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad6,
+ modifiers:{numericKeyPadKey:1}, chars:"6", unmodifiedChars:"6"},
+ "6", "Numpad6", nsIDOMKeyEvent.DOM_VK_NUMPAD6, "6", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad6,
+ modifiers:{numericKeyPadKey:1, shiftKey:1}, chars:"6", unmodifiedChars:"6"},
+ "6", "Numpad6", nsIDOMKeyEvent.DOM_VK_NUMPAD6, "6", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad6,
+ modifiers:{numericKeyPadKey:1, ctrlKey:1}, chars:"6", unmodifiedChars:"6"},
+ "6", "Numpad6", nsIDOMKeyEvent.DOM_VK_NUMPAD6, "6", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad6,
+ modifiers:{numericKeyPadKey:1, altKey:1}, chars:"6", unmodifiedChars:"6"},
+ "6", "Numpad6", nsIDOMKeyEvent.DOM_VK_NUMPAD6, "6", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad6,
+ modifiers:{numericKeyPadKey:1, metaKey:1}, chars:"6", unmodifiedChars:"6"},
+ "6", "Numpad6", nsIDOMKeyEvent.DOM_VK_NUMPAD6, "6", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad7,
+ modifiers:{numericKeyPadKey:1}, chars:"7", unmodifiedChars:"7"},
+ "7", "Numpad7", nsIDOMKeyEvent.DOM_VK_NUMPAD7, "7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad7,
+ modifiers:{numericKeyPadKey:1, shiftKey:1}, chars:"7", unmodifiedChars:"7"},
+ "7", "Numpad7", nsIDOMKeyEvent.DOM_VK_NUMPAD7, "7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad7,
+ modifiers:{numericKeyPadKey:1, ctrlKey:1}, chars:"7", unmodifiedChars:"7"},
+ "7", "Numpad7", nsIDOMKeyEvent.DOM_VK_NUMPAD7, "7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad7,
+ modifiers:{numericKeyPadKey:1, altKey:1}, chars:"7", unmodifiedChars:"7"},
+ "7", "Numpad7", nsIDOMKeyEvent.DOM_VK_NUMPAD7, "7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad7,
+ modifiers:{numericKeyPadKey:1, metaKey:1}, chars:"7", unmodifiedChars:"7"},
+ "7", "Numpad7", nsIDOMKeyEvent.DOM_VK_NUMPAD7, "7", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad8,
+ modifiers:{numericKeyPadKey:1}, chars:"8", unmodifiedChars:"8"},
+ "8", "Numpad8", nsIDOMKeyEvent.DOM_VK_NUMPAD8, "8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad8,
+ modifiers:{numericKeyPadKey:1, shiftKey:1}, chars:"8", unmodifiedChars:"8"},
+ "8", "Numpad8", nsIDOMKeyEvent.DOM_VK_NUMPAD8, "8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad8,
+ modifiers:{numericKeyPadKey:1, ctrlKey:1}, chars:"8", unmodifiedChars:"8"},
+ "8", "Numpad8", nsIDOMKeyEvent.DOM_VK_NUMPAD8, "8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad8,
+ modifiers:{numericKeyPadKey:1, altKey:1}, chars:"8", unmodifiedChars:"8"},
+ "8", "Numpad8", nsIDOMKeyEvent.DOM_VK_NUMPAD8, "8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad8,
+ modifiers:{numericKeyPadKey:1, metaKey:1}, chars:"8", unmodifiedChars:"8"},
+ "8", "Numpad8", nsIDOMKeyEvent.DOM_VK_NUMPAD8, "8", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad9,
+ modifiers:{numericKeyPadKey:1}, chars:"9", unmodifiedChars:"9"},
+ "9", "Numpad9", nsIDOMKeyEvent.DOM_VK_NUMPAD9, "9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad9,
+ modifiers:{numericKeyPadKey:1, shiftKey:1}, chars:"9", unmodifiedChars:"9"},
+ "9", "Numpad9", nsIDOMKeyEvent.DOM_VK_NUMPAD9, "9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad9,
+ modifiers:{numericKeyPadKey:1, ctrlKey:1}, chars:"9", unmodifiedChars:"9"},
+ "9", "Numpad9", nsIDOMKeyEvent.DOM_VK_NUMPAD9, "9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad9,
+ modifiers:{numericKeyPadKey:1, altKey:1}, chars:"9", unmodifiedChars:"9"},
+ "9", "Numpad9", nsIDOMKeyEvent.DOM_VK_NUMPAD9, "9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad9,
+ modifiers:{numericKeyPadKey:1, metaKey:1}, chars:"9", unmodifiedChars:"9"},
+ "9", "Numpad9", nsIDOMKeyEvent.DOM_VK_NUMPAD9, "9", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad0,
+ modifiers:{numericKeyPadKey:1}, chars:"0", unmodifiedChars:"0"},
+ "0", "Numpad0", nsIDOMKeyEvent.DOM_VK_NUMPAD0, "0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad0,
+ modifiers:{numericKeyPadKey:1, shiftKey:1}, chars:"0", unmodifiedChars:"0"},
+ "0", "Numpad0", nsIDOMKeyEvent.DOM_VK_NUMPAD0, "0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad0,
+ modifiers:{numericKeyPadKey:1, ctrlKey:1}, chars:"0", unmodifiedChars:"0"},
+ "0", "Numpad0", nsIDOMKeyEvent.DOM_VK_NUMPAD0, "0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad0,
+ modifiers:{numericKeyPadKey:1, altKey:1}, chars:"0", unmodifiedChars:"0"},
+ "0", "Numpad0", nsIDOMKeyEvent.DOM_VK_NUMPAD0, "0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Keypad0,
+ modifiers:{numericKeyPadKey:1, metaKey:1}, chars:"0", unmodifiedChars:"0"},
+ "0", "Numpad0", nsIDOMKeyEvent.DOM_VK_NUMPAD0, "0", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadEquals,
+ modifiers:{numericKeyPadKey:1}, chars:"=", unmodifiedChars:"="},
+ "=", "NumpadEqual", nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadEquals,
+ modifiers:{numericKeyPadKey:1, shiftKey:1}, chars:"=", unmodifiedChars:"="},
+ "=", "NumpadEqual", nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadEquals,
+ modifiers:{numericKeyPadKey:1, ctrlKey:1}, chars:"=", unmodifiedChars:"="},
+ "=", "NumpadEqual", nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadEquals,
+ modifiers:{numericKeyPadKey:1, altKey:1}, chars:"=", unmodifiedChars:"="},
+ "=", "NumpadEqual", nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadEquals,
+ modifiers:{numericKeyPadKey:1, metaKey:1}, chars:"=", unmodifiedChars:"="},
+ "=", "NumpadEqual", nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadDivide,
+ modifiers:{numericKeyPadKey:1}, chars:"/", unmodifiedChars:"/"},
+ "/", "NumpadDivide", nsIDOMKeyEvent.DOM_VK_DIVIDE, "/", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadDivide,
+ modifiers:{numericKeyPadKey:1, shiftKey:1}, chars:"/", unmodifiedChars:"/"},
+ "/", "NumpadDivide", nsIDOMKeyEvent.DOM_VK_DIVIDE, "/", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadDivide,
+ modifiers:{numericKeyPadKey:1, ctrlKey:1}, chars:"/", unmodifiedChars:"/"},
+ "/", "NumpadDivide", nsIDOMKeyEvent.DOM_VK_DIVIDE, "/", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadDivide,
+ modifiers:{numericKeyPadKey:1, altKey:1}, chars:"/", unmodifiedChars:"/"},
+ "/", "NumpadDivide", nsIDOMKeyEvent.DOM_VK_DIVIDE, "/", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadDivide,
+ modifiers:{numericKeyPadKey:1, metaKey:1}, chars:"/", unmodifiedChars:"/"},
+ "/", "NumpadDivide", nsIDOMKeyEvent.DOM_VK_DIVIDE, "/", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadMultiply,
+ modifiers:{numericKeyPadKey:1}, chars:"*", unmodifiedChars:"*"},
+ "*", "NumpadMultiply", nsIDOMKeyEvent.DOM_VK_MULTIPLY, "*", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadMultiply,
+ modifiers:{numericKeyPadKey:1, shiftKey:1}, chars:"*", unmodifiedChars:"*"},
+ "*", "NumpadMultiply", nsIDOMKeyEvent.DOM_VK_MULTIPLY, "*", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadMultiply,
+ modifiers:{numericKeyPadKey:1, ctrlKey:1}, chars:"*", unmodifiedChars:"*"},
+ "*", "NumpadMultiply", nsIDOMKeyEvent.DOM_VK_MULTIPLY, "*", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadMultiply,
+ modifiers:{numericKeyPadKey:1, altKey:1}, chars:"*", unmodifiedChars:"*"},
+ "*", "NumpadMultiply", nsIDOMKeyEvent.DOM_VK_MULTIPLY, "*", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadMultiply,
+ modifiers:{numericKeyPadKey:1, metaKey:1}, chars:"*", unmodifiedChars:"*"},
+ "*", "NumpadMultiply", nsIDOMKeyEvent.DOM_VK_MULTIPLY, "*", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadMinus,
+ modifiers:{numericKeyPadKey:1}, chars:"-", unmodifiedChars:"-"},
+ "-", "NumpadSubtract", nsIDOMKeyEvent.DOM_VK_SUBTRACT, "-", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadMinus,
+ modifiers:{numericKeyPadKey:1, shiftKey:1}, chars:"-", unmodifiedChars:"-"},
+ "-", "NumpadSubtract", nsIDOMKeyEvent.DOM_VK_SUBTRACT, "-", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadMinus,
+ modifiers:{numericKeyPadKey:1, ctrlKey:1}, chars:"-", unmodifiedChars:"-"},
+ "-", "NumpadSubtract", nsIDOMKeyEvent.DOM_VK_SUBTRACT, "-", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadMinus,
+ modifiers:{numericKeyPadKey:1, altKey:1}, chars:"-", unmodifiedChars:"-"},
+ "-", "NumpadSubtract", nsIDOMKeyEvent.DOM_VK_SUBTRACT, "-", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadMinus,
+ modifiers:{numericKeyPadKey:1, metaKey:1}, chars:"-", unmodifiedChars:"-"},
+ "-", "NumpadSubtract", nsIDOMKeyEvent.DOM_VK_SUBTRACT, "-", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadPlus,
+ modifiers:{numericKeyPadKey:1}, chars:"+", unmodifiedChars:"+"},
+ "+", "NumpadAdd", nsIDOMKeyEvent.DOM_VK_ADD, "+", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadPlus,
+ modifiers:{numericKeyPadKey:1, shiftKey:1}, chars:"+", unmodifiedChars:"+"},
+ "+", "NumpadAdd", nsIDOMKeyEvent.DOM_VK_ADD, "+", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadPlus,
+ modifiers:{numericKeyPadKey:1, ctrlKey:1}, chars:"+", unmodifiedChars:"+"},
+ "+", "NumpadAdd", nsIDOMKeyEvent.DOM_VK_ADD, "+", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadPlus,
+ modifiers:{numericKeyPadKey:1, altKey:1}, chars:"+", unmodifiedChars:"+"},
+ "+", "NumpadAdd", nsIDOMKeyEvent.DOM_VK_ADD, "+", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadPlus,
+ modifiers:{numericKeyPadKey:1, metaKey:1}, chars:"+", unmodifiedChars:"+"},
+ "+", "NumpadAdd", nsIDOMKeyEvent.DOM_VK_ADD, "+", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadEnter,
+ modifiers:{numericKeyPadKey:1}, chars:"\u0003", unmodifiedChars:"\u0003"},
+ "Enter", "NumpadEnter", nsIDOMKeyEvent.DOM_VK_RETURN, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadEnter,
+ modifiers:{numericKeyPadKey:1, shiftKey:1}, chars:"\u0003", unmodifiedChars:"\u0003"},
+ "Enter", "NumpadEnter", nsIDOMKeyEvent.DOM_VK_RETURN, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadEnter,
+ modifiers:{numericKeyPadKey:1, ctrlKey:1}, chars:"\u0003", unmodifiedChars:"\u0003"},
+ "Enter", "NumpadEnter", nsIDOMKeyEvent.DOM_VK_RETURN, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadEnter,
+ modifiers:{numericKeyPadKey:1, altKey:1}, chars:"\u0003", unmodifiedChars:"\u0003"},
+ "Enter", "NumpadEnter", nsIDOMKeyEvent.DOM_VK_RETURN, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_KeypadEnter,
+ modifiers:{numericKeyPadKey:1, metaKey:1}, chars:"\u0003", unmodifiedChars:"\u0003"},
+ "Enter", "NumpadEnter", nsIDOMKeyEvent.DOM_VK_RETURN, "", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_JIS_KeypadComma,
+ modifiers:{numericKeyPadKey:1, shiftKey:1}, chars:",", unmodifiedChars:","},
+ ",", "NumpadComma", nsIDOMKeyEvent.DOM_VK_SEPARATOR, ",", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_JIS_KeypadComma,
+ modifiers:{numericKeyPadKey:1, ctrlKey:1}, chars:",", unmodifiedChars:","},
+ ",", "NumpadComma", nsIDOMKeyEvent.DOM_VK_SEPARATOR, ",", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_JIS_KeypadComma,
+ modifiers:{numericKeyPadKey:1, altKey:1}, chars:",", unmodifiedChars:","},
+ ",", "NumpadComma", nsIDOMKeyEvent.DOM_VK_SEPARATOR, ",", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_JIS_KeypadComma,
+ modifiers:{numericKeyPadKey:1, metaKey:1}, chars:",", unmodifiedChars:","},
+ ",", "NumpadComma", nsIDOMKeyEvent.DOM_VK_SEPARATOR, ",", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+
+ // French, numeric
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_1,
+ modifiers:{}, chars:"&", unmodifiedChars:"&"},
+ "&", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "&", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_1,
+ modifiers:{shiftKey:1}, chars:"1", unmodifiedChars:"1"},
+ "1", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_1,
+ modifiers:{ctrlKey:1}, chars:"1", unmodifiedChars:"&"},
+ "1", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_1,
+ modifiers:{metaKey:1}, chars:"&", unmodifiedChars:"&"},
+ "&", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "&", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_1,
+ modifiers:{metaKey:1, shiftKey:1}, chars:"1", unmodifiedChars:"1"},
+ "1", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "1", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_2,
+ modifiers:{}, chars:"\u00E9", unmodifiedChars:"\u00E9"},
+ "\u00E9", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "\u00E9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_2,
+ modifiers:{shiftKey:1}, chars:"2", unmodifiedChars:"2"},
+ "2", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_2,
+ modifiers:{ctrlKey:1}, chars:"2", unmodifiedChars:"\u00E9"},
+ "2", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_2,
+ modifiers:{metaKey:1}, chars:"\u00E9", unmodifiedChars:"\u00E9"},
+ "\u00E9", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "\u00E9", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_2,
+ modifiers:{metaKey:1, shiftKey:1}, chars:"2", unmodifiedChars:"2"},
+ "2", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "2", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_3,
+ modifiers:{}, chars:"\"", unmodifiedChars:"\""},
+ "\"", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "\"", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_3,
+ modifiers:{shiftKey:1}, chars:"3", unmodifiedChars:"3"},
+ "3", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "3", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_3,
+ modifiers:{ctrlKey:1}, chars:"3", unmodifiedChars:"\""},
+ "3", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "3", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_3,
+ modifiers:{metaKey:1}, chars:"\"", unmodifiedChars:"\""},
+ "\"", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "\"", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // Cmd+Shift+3 is a shortcut key of taking a snapshot
+ // yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_3,
+ // modifiers:{metaKey:1, shiftKey:1}, chars:"\"", unmodifiedChars:"\""},
+ // "3", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "3", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_4,
+ modifiers:{}, chars:"'", unmodifiedChars:"'"},
+ "'", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "'", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_4,
+ modifiers:{shiftKey:1}, chars:"4", unmodifiedChars:"4"},
+ "4", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_4,
+ modifiers:{ctrlKey:1}, chars:"4", unmodifiedChars:"'"},
+ "4", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_4,
+ modifiers:{metaKey:1}, chars:"'", unmodifiedChars:"'"},
+ "'", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "'", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // Cmd+Shift+4 is a shortcut key of taking a snapshot in specific range
+ // yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_4,
+ // modifiers:{metaKey:1, shiftKey:1}, chars:"4", unmodifiedChars:"4"},
+ // "4", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "4", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_5,
+ modifiers:{}, chars:"(", unmodifiedChars:"("},
+ "(", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "(", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_5,
+ modifiers:{shiftKey:1}, chars:"5", unmodifiedChars:"5"},
+ "5", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_5,
+ modifiers:{ctrlKey:1}, chars:"5", unmodifiedChars:"("},
+ "5", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_5,
+ modifiers:{metaKey:1}, chars:"(", unmodifiedChars:"("},
+ "(", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "(", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_5,
+ modifiers:{metaKey:1, shiftKey:1}, chars:"5", unmodifiedChars:"5"},
+ "5", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "5", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_6,
+ modifiers:{}, chars:"\u00A7", unmodifiedChars:"\u00A7"},
+ "\u00A7", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "\u00A7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_6,
+ modifiers:{shiftKey:1}, chars:"6", unmodifiedChars:"6"},
+ "6", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "6", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // TODO:
+ // yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_6,
+ // modifiers:{ctrlKey:1}, chars:"\u001D", unmodifiedChars:"\u00A7"},
+ // "6", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD); // Ctrl+6 sets strange char
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_6,
+ modifiers:{metaKey:1}, chars:"\u00A7", unmodifiedChars:"\u00A7"},
+ "\u00A7", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "\u00A7", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_6,
+ modifiers:{metaKey:1, shiftKey:1}, chars:"6", unmodifiedChars:"6"},
+ "6", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "6", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_7,
+ modifiers:{}, chars:"\u00E8", unmodifiedChars:"\u00E8"},
+ "\u00E8", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "\u00E8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_7,
+ modifiers:{shiftKey:1}, chars:"7", unmodifiedChars:"7"},
+ "7", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_7,
+ modifiers:{ctrlKey:1}, chars:"7", unmodifiedChars:"\u00E8"},
+ "7", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_7,
+ modifiers:{metaKey:1}, chars:"\u00E8", unmodifiedChars:"\u00E8"},
+ "\u00E8", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "\u00E8", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_7,
+ modifiers:{metaKey:1, shiftKey:1}, chars:"7", unmodifiedChars:"7"},
+ "7", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "7", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_8,
+ modifiers:{}, chars:"!", unmodifiedChars:"!"},
+ "!", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "!", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_8,
+ modifiers:{shiftKey:1}, chars:"8", unmodifiedChars:"8"},
+ "8", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_8,
+ modifiers:{ctrlKey:1}, chars:"8", unmodifiedChars:"!"},
+ "8", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_8,
+ modifiers:{metaKey:1}, chars:"!", unmodifiedChars:"!"},
+ "!", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "!", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_8,
+ modifiers:{metaKey:1, shiftKey:1}, chars:"8", unmodifiedChars:"8"},
+ "8", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "8", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_9,
+ modifiers:{}, chars:"\u00E7", unmodifiedChars:"\u00E7"},
+ "\u00E7", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "\u00E7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_9,
+ modifiers:{shiftKey:1}, chars:"9", unmodifiedChars:"9"},
+ "9", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // TODO:
+ // yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_9,
+ // modifiers:{ctrlKey:1}, chars:"\u001C", unmodifiedChars:"\u00E7"},
+ // "9", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD); // Ctrl+9 sets strange char
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_9,
+ modifiers:{metaKey:1}, chars:"\u00E7", unmodifiedChars:"\u00E7"},
+ "\u00E7", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "\u00E7", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_9,
+ modifiers:{metaKey:1, shiftKey:1}, chars:"9", unmodifiedChars:"9"},
+ "9", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "9", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_0,
+ modifiers:{}, chars:"\u00E0", unmodifiedChars:"\u00E0"},
+ "\u00E0", "Digit0", nsIDOMKeyEvent.DOM_VK_0, "\u00E0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_0,
+ modifiers:{shiftKey:1}, chars:"0", unmodifiedChars:"0"},
+ "0", "Digit0", nsIDOMKeyEvent.DOM_VK_0, "0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // XXX No events fired, not sure the reason.
+ // yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_0,
+ // modifiers:{ctrlKey:1}, chars:"", unmodifiedChars:"\u00E0"},
+ // "0", "Digit0", nsIDOMKeyEvent.DOM_VK_0, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_0,
+ modifiers:{metaKey:1}, chars:"\u00E0", unmodifiedChars:"\u00E0"},
+ "\u00E0", "Digit0", nsIDOMKeyEvent.DOM_VK_0, "\u00E0", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:MAC_VK_ANSI_0,
+ modifiers:{metaKey:1, shiftKey:1}, chars:"0", unmodifiedChars:"0"},
+ "0", "Digit0", nsIDOMKeyEvent.DOM_VK_0, "0", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Thai
+ // keycode should be DOM_VK_[A-Z] of the key on the latest ASCII capable keyboard layout is for alphabet
+ yield testKey({layout:KEYBOARD_LAYOUT_THAI, keyCode:MAC_VK_ANSI_A,
+ modifiers:{}, chars:"\u0E1F", unmodifiedChars:"\u0E1F"},
+ "\u0E1F", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u0E1F", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // keycode should be shifted character if unshifted character isn't an ASCII character
+ yield testKey({layout:KEYBOARD_LAYOUT_THAI, keyCode:MAC_VK_ANSI_Quote,
+ modifiers:{}, chars:"\u0E07", unmodifiedChars:"\u0E07"},
+ "\u0E07", "Quote", nsIDOMKeyEvent.DOM_VK_PERIOD, "\u0E07", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // keycode should be zero if the character of the key on the latest ASCII capable keyboard layout isn't for alphabet
+ yield testKey({layout:KEYBOARD_LAYOUT_THAI, keyCode:MAC_VK_ANSI_Period,
+ modifiers:{}, chars:"\u0E43", unmodifiedChars:"\u0E43"},
+ "\u0E43", "Period", 0, "\u0E43", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // keycode should be DOM_VK_[0-9] if the key on the latest ASCII capable keyboard layout is for numeric
+ yield testKey({layout:KEYBOARD_LAYOUT_THAI, keyCode:MAC_VK_ANSI_1,
+ modifiers:{}, chars:"\u0E45", unmodifiedChars:"\u0E45"},
+ "\u0E45", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "\u0E45", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_THAI, keyCode:MAC_VK_ANSI_2,
+ modifiers:{}, chars:"/", unmodifiedChars:"/"},
+ "/", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "/", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_THAI, keyCode:MAC_VK_ANSI_3,
+ modifiers:{}, chars:"_", unmodifiedChars:"_"},
+ "_", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "_", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_THAI, keyCode:MAC_VK_ANSI_4,
+ modifiers:{}, chars:"\u0E20", unmodifiedChars:"\u0E20"},
+ "\u0E20", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "\u0E20", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_THAI, keyCode:MAC_VK_ANSI_5,
+ modifiers:{}, chars:"\u0E16", unmodifiedChars:"\u0E16"},
+ "\u0E16", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "\u0E16", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_THAI, keyCode:MAC_VK_ANSI_6,
+ modifiers:{}, chars:"\u0E38", unmodifiedChars:"\u0E38"},
+ "\u0E38", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "\u0E38", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_THAI, keyCode:MAC_VK_ANSI_7,
+ modifiers:{}, chars:"\u0E36", unmodifiedChars:"\u0E36"},
+ "\u0E36", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "\u0E36", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_THAI, keyCode:MAC_VK_ANSI_8,
+ modifiers:{}, chars:"\u0E04", unmodifiedChars:"\u0E04"},
+ "\u0E04", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "\u0E04", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_THAI, keyCode:MAC_VK_ANSI_9,
+ modifiers:{}, chars:"\u0E15", unmodifiedChars:"\u0E15"},
+ "\u0E15", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "\u0E15", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_THAI, keyCode:MAC_VK_ANSI_0,
+ modifiers:{}, chars:"\u0E08", unmodifiedChars:"\u0E08"},
+ "\u0E08", "Digit0", nsIDOMKeyEvent.DOM_VK_0, "\u0E08", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Dvorak-Qwerty, layout should be changed when Command key is pressed.
+ yield testKey({layout:KEYBOARD_LAYOUT_DVORAK_QWERTY, keyCode:MAC_VK_ANSI_S,
+ modifiers:{}, chars:"o", unmodifiedChars:"o"},
+ "o", "KeyS", nsIDOMKeyEvent.DOM_VK_O, "o", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_DVORAK_QWERTY, keyCode:MAC_VK_ANSI_S,
+ modifiers:{shiftKey:1}, chars:"O", unmodifiedChars:"O"},
+ "O", "KeyS", nsIDOMKeyEvent.DOM_VK_O, "O", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_DVORAK_QWERTY, keyCode:MAC_VK_ANSI_S,
+ modifiers:{ctrlKey:1}, chars:"\u000F", unmodifiedChars:"o"},
+ "o", "KeyS", nsIDOMKeyEvent.DOM_VK_O, "o", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_DVORAK_QWERTY, keyCode:MAC_VK_ANSI_S,
+ modifiers:{altKey:1}, chars:"\u00F8", unmodifiedChars:"o"},
+ "\u00F8", "KeyS", nsIDOMKeyEvent.DOM_VK_O, "\u00F8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_DVORAK_QWERTY, keyCode:MAC_VK_ANSI_S,
+ modifiers:{metaKey:1}, chars:"s", unmodifiedChars:"o"},
+ "s", "KeyS", nsIDOMKeyEvent.DOM_VK_S, "s", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_DVORAK_QWERTY, keyCode:MAC_VK_ANSI_D,
+ modifiers:{}, chars:"e", unmodifiedChars:"e"},
+ "e", "KeyD", nsIDOMKeyEvent.DOM_VK_E, "e", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_DVORAK_QWERTY, keyCode:MAC_VK_ANSI_D,
+ modifiers:{shiftKey:1}, chars:"E", unmodifiedChars:"E"},
+ "E", "KeyD", nsIDOMKeyEvent.DOM_VK_E, "E", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_DVORAK_QWERTY, keyCode:MAC_VK_ANSI_D,
+ modifiers:{ctrlKey:1}, chars:"\u0005", unmodifiedChars:"e"},
+ "e", "KeyD", nsIDOMKeyEvent.DOM_VK_E, "e", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_DVORAK_QWERTY, keyCode:MAC_VK_ANSI_D,
+ modifiers:{altKey:1}, chars:"", unmodifiedChars:"e"},
+ "Dead", "KeyD", nsIDOMKeyEvent.DOM_VK_E, "\u00B4", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD); // dead key
+ yield testKey({layout:KEYBOARD_LAYOUT_DVORAK_QWERTY, keyCode:MAC_VK_ANSI_D,
+ modifiers:{metaKey:1}, chars:"d", unmodifiedChars:"e"},
+ "d", "KeyD", nsIDOMKeyEvent.DOM_VK_D, "d", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_DVORAK_QWERTY, keyCode:MAC_VK_ANSI_I,
+ modifiers:{metaKey:1, altKey:1}, chars:"^", unmodifiedChars:"c"},
+ "^", "KeyI", nsIDOMKeyEvent.DOM_VK_I, "^", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_DVORAK_QWERTY, keyCode:MAC_VK_ANSI_I,
+ modifiers:{metaKey:1, altKey:1, shiftKey:1}, chars:"\u02C6", unmodifiedChars:"C"},
+ "\u02C6", "KeyI", nsIDOMKeyEvent.DOM_VK_I, "\u02C6", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Arabic - PC keyboard layout inputs 2 or more characters with some key.
+ yield testKey({layout:KEYBOARD_LAYOUT_ARABIC_PC, keyCode:MAC_VK_ANSI_G,
+ modifiers:{shiftKey:1}, chars:"\u0644\u0623", unmodifiedChars:"\u0644\u0623"},
+ ["\u0644\u0623", "\u0644", "\u0623", "\u0644\u0623"], "KeyG", nsIDOMKeyEvent.DOM_VK_G, "\u0644\u0623", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_ARABIC_PC, keyCode:MAC_VK_ANSI_T,
+ modifiers:{shiftKey:1}, chars:"\u0644\u0625", unmodifiedChars:"\u0644\u0625"},
+ ["\u0644\u0625", "\u0644", "\u0625", "\u0644\u0625"], "KeyT", nsIDOMKeyEvent.DOM_VK_T, "\u0644\u0625", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_ARABIC_PC, keyCode:MAC_VK_ANSI_B,
+ modifiers:{shiftKey:1}, chars:"\u0644\u0622", unmodifiedChars:"\u0644\u0622"},
+ ["\u0644\u0622", "\u0644", "\u0622", "\u0644\u0622"], "KeyB", nsIDOMKeyEvent.DOM_VK_B, "\u0644\u0622", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_ARABIC_PC, keyCode:MAC_VK_ANSI_B,
+ modifiers:{}, chars:"\u0644\u0627", unmodifiedChars:"\u0644\u0627"},
+ ["\u0644\u0627", "\u0644", "\u0627", "\u0644\u0627"], "KeyB", nsIDOMKeyEvent.DOM_VK_B, "\u0644\u0627", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ cleanup();
+ }
+
+ function testKeysOnWindows()
+ {
+ // On Windows, you can use Spy++ or Winspector (free) to watch window messages.
+ // The keyCode is given by the wParam of the last WM_KEYDOWN message. The
+ // chars string is given by the wParam of the WM_CHAR message. unmodifiedChars
+ // is not needed on Windows.
+
+ // Shift-ctrl-alt generates no WM_CHAR, but we still get a keypress
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_A,
+ modifiers:{altKey:1, ctrlKey:1, shiftKey:1}, chars:""},
+ "A", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "A", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Greek plain text
+ yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:WIN_VK_A,
+ modifiers:{}, chars:"\u03b1"},
+ "\u03b1", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u03b1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:WIN_VK_A,
+ modifiers:{shiftKey:1}, chars:"\u0391"},
+ "\u0391", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u0391", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Greek ctrl keys produce Latin charcodes
+ yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:WIN_VK_A,
+ modifiers:{ctrlKey:1}, chars:"\u0001"},
+ "\u03b1", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:WIN_VK_A,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0001"},
+ "\u0391", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "A", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Caps Lock key event
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_CAPITAL,
+ modifiers:{capsLockKey:1}, chars:""},
+ "CapsLock", "CapsLock", nsIDOMKeyEvent.DOM_VK_CAPS_LOCK, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_CAPITAL,
+ modifiers:{capsLockKey:0}, chars:""},
+ "CapsLock", "CapsLock", nsIDOMKeyEvent.DOM_VK_CAPS_LOCK, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Shift keys
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_LSHIFT,
+ modifiers:{shiftKey:1}, chars:""},
+ "Shift", "ShiftLeft", nsIDOMKeyEvent.DOM_VK_SHIFT, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_LEFT);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_RSHIFT,
+ modifiers:{shiftRightKey:1}, chars:""},
+ "Shift", "ShiftRight", nsIDOMKeyEvent.DOM_VK_SHIFT, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_RIGHT);
+
+ // Ctrl keys
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_LCONTROL,
+ modifiers:{ctrlKey:1}, chars:""},
+ "Control", "ControlLeft", nsIDOMKeyEvent.DOM_VK_CONTROL, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_LEFT);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_RCONTROL,
+ modifiers:{ctrlRightKey:1}, chars:""},
+ "Control", "ControlRight", nsIDOMKeyEvent.DOM_VK_CONTROL, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_RIGHT);
+
+ // Alt keys
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_LMENU,
+ modifiers:{altKey:1}, chars:""},
+ "Alt", "AltLeft", nsIDOMKeyEvent.DOM_VK_ALT, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_LEFT);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_RMENU,
+ modifiers:{altRightKey:1}, chars:""},
+ "Alt", "AltRight", nsIDOMKeyEvent.DOM_VK_ALT, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_RIGHT);
+
+ // Win keys
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_LWIN,
+ modifiers:{}, chars:""},
+ "OS" /* bug 1232918 */, "OSLeft", nsIDOMKeyEvent.DOM_VK_WIN, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_LEFT);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_RWIN,
+ modifiers:{}, chars:""},
+ "OS" /* bug 1232918 */, "OSRight", nsIDOMKeyEvent.DOM_VK_WIN, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_RIGHT);
+
+ // all keys on keyboard (keyCode test)
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_BACK,
+ modifiers:{}, chars:"\u0008"},
+ "Backspace", "Backspace", nsIDOMKeyEvent.DOM_VK_BACK_SPACE, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_TAB,
+ modifiers:{}, chars:"\t"},
+ "Tab", "Tab", nsIDOMKeyEvent.DOM_VK_TAB, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_RETURN,
+ modifiers:{}, chars:"\r"},
+ "Enter", "Enter", nsIDOMKeyEvent.DOM_VK_RETURN, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_PAUSE,
+ modifiers:{}, chars:""},
+ "Pause", "Pause", nsIDOMKeyEvent.DOM_VK_PAUSE, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_KANA,
+ modifiers:{}, chars:""},
+ "Unidentified", "", nsIDOMKeyEvent.DOM_VK_KANA, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_JUNJA,
+ modifiers:{}, chars:""},
+ "JunjaMode", "", nsIDOMKeyEvent.DOM_VK_JUNJA, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_FINAL,
+ modifiers:{}, chars:""},
+ "FinalMode", "", nsIDOMKeyEvent.DOM_VK_FINAL, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_KANJI,
+ modifiers:{}, chars:""},
+ "Unidentified", "", nsIDOMKeyEvent.DOM_VK_KANJI, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_ESCAPE,
+ modifiers:{}, chars:""},
+ "Escape", "Escape", nsIDOMKeyEvent.DOM_VK_ESCAPE, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_CONVERT,
+ modifiers:{}, chars:""},
+ "Convert", "", nsIDOMKeyEvent.DOM_VK_CONVERT, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NONCONVERT,
+ modifiers:{}, chars:""},
+ "NonConvert", "", nsIDOMKeyEvent.DOM_VK_NONCONVERT, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_ACCEPT,
+ modifiers:{}, chars:""},
+ "Accept", "", nsIDOMKeyEvent.DOM_VK_ACCEPT, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_MODECHANGE,
+ modifiers:{}, chars:""},
+ "ModeChange", "", nsIDOMKeyEvent.DOM_VK_MODECHANGE, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_SPACE,
+ modifiers:{}, chars:" "},
+ " ", "Space", nsIDOMKeyEvent.DOM_VK_SPACE, " ", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ // Ctrl+Space causes WM_CHAR with ' '. However, its keypress event's ctrlKey state shouldn't be false.
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_SPACE,
+ modifiers:{ctrlKey:1}, chars:" "},
+ " ", "Space", nsIDOMKeyEvent.DOM_VK_SPACE, " ", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_SELECT,
+ modifiers:{}, chars:""},
+ "Select", "", nsIDOMKeyEvent.DOM_VK_SELECT, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_PRINT,
+ modifiers:{}, chars:""},
+ "Unidentified", "", nsIDOMKeyEvent.DOM_VK_PRINT, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_EXECUTE,
+ modifiers:{}, chars:""},
+ "Execute", "", nsIDOMKeyEvent.DOM_VK_EXECUTE, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_SNAPSHOT,
+ modifiers:{}, chars:""},
+ "PrintScreen", "PrintScreen", nsIDOMKeyEvent.DOM_VK_PRINTSCREEN, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_HELP,
+ modifiers:{}, chars:""},
+ "Help", "", nsIDOMKeyEvent.DOM_VK_HELP, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_SLEEP,
+ modifiers:{}, chars:""},
+ "Standby", "", nsIDOMKeyEvent.DOM_VK_SLEEP, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_PRIOR,
+ modifiers:{}, chars:""},
+ "PageUp", "PageUp", nsIDOMKeyEvent.DOM_VK_PAGE_UP, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NEXT,
+ modifiers:{}, chars:""},
+ "PageDown", "PageDown", nsIDOMKeyEvent.DOM_VK_PAGE_DOWN, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_END,
+ modifiers:{}, chars:""},
+ "End", "End", nsIDOMKeyEvent.DOM_VK_END, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_HOME,
+ modifiers:{}, chars:""},
+ "Home", "Home", nsIDOMKeyEvent.DOM_VK_HOME, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_LEFT,
+ modifiers:{}, chars:""},
+ "ArrowLeft", "ArrowLeft", nsIDOMKeyEvent.DOM_VK_LEFT, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_UP,
+ modifiers:{}, chars:""},
+ "ArrowUp", "ArrowUp", nsIDOMKeyEvent.DOM_VK_UP, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_RIGHT,
+ modifiers:{}, chars:""},
+ "ArrowRight", "ArrowRight", nsIDOMKeyEvent.DOM_VK_RIGHT, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_DOWN,
+ modifiers:{}, chars:""},
+ "ArrowDown", "ArrowDown", nsIDOMKeyEvent.DOM_VK_DOWN, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_INSERT,
+ modifiers:{}, chars:""},
+ "Insert", "Insert", nsIDOMKeyEvent.DOM_VK_INSERT, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_DELETE,
+ modifiers:{}, chars:""},
+ "Delete", "Delete", nsIDOMKeyEvent.DOM_VK_DELETE, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Backspace and Enter are handled with special path in mozilla::widget::NativeKey. So, let's test them with modifiers too.
+ // Note that when both Ctrl and Alt are pressed, they don't cause WM_(SYS)CHAR message.
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_BACK,
+ modifiers:{ctrlKey:1}, chars:"\u007F"},
+ "Backspace", "Backspace", nsIDOMKeyEvent.DOM_VK_BACK_SPACE, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_BACK,
+ modifiers:{altKey:1}, chars:"\u0008"},
+ "Backspace", "Backspace", nsIDOMKeyEvent.DOM_VK_BACK_SPACE, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_BACK,
+ modifiers:{ctrl:1, altKey:1}, chars:""},
+ "Backspace", "Backspace", nsIDOMKeyEvent.DOM_VK_BACK_SPACE, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_RETURN,
+ modifiers:{ctrlKey:1}, chars:"\n"},
+ "Enter", "Enter", nsIDOMKeyEvent.DOM_VK_RETURN, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_RETURN,
+ modifiers:{altKey:1}, chars:"\r"},
+ "Enter", "Enter", nsIDOMKeyEvent.DOM_VK_RETURN, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_RETURN,
+ modifiers:{ctrl:1, altKey:1}, chars:""},
+ "Enter", "Enter", nsIDOMKeyEvent.DOM_VK_RETURN, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Even non-printable key could be mapped as a printable key.
+ // Only "keyup" event cannot know if it *did* cause inputting text.
+ // Therefore, only "keydown" and "keypress" event's key value should be the character inputted by the key.
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_F4,
+ modifiers:{}, chars:"a"},
+ ["a", "a", "F4"], "F4", nsIDOMKeyEvent.DOM_VK_F4, "a", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Even if key message is processed by IME, when the key causes inputting text,
+ // keypress event(s) should be fired.
+ const WIN_VK_PROCESSKEY_WITH_SC_A = WIN_VK_PROCESSKEY | 0x001E0000;
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_PROCESSKEY_WITH_SC_A,
+ modifiers:{}, chars:"a"},
+ ["a", "a", "Unidentified" /* TODO: Process */], "KeyA", 0 /* TODO: 0xE5 */, "a", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_PROCESSKEY_WITH_SC_A,
+ modifiers:{altKey:1}, chars:"a"},
+ ["a", "a", "Unidentified" /* TODO: Process */], "KeyA", 0 /* TODO: 0xE5 */, "a", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // US
+ // Alphabet
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_A,
+ modifiers:{}, chars:"a"},
+ "a", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_A,
+ modifiers:{shiftKey:1}, chars:"A"},
+ "A", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "A", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_A,
+ modifiers:{ctrlKey:1}, chars:"\u0001"},
+ "a", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_A,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0001"},
+ "A", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "A", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_A,
+ modifiers:{altKey:1}, chars:"a"},
+ "a", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_A,
+ modifiers:{altKey:1, shiftKey:1}, chars:"A"},
+ "A", "KeyA", nsIDOMKeyEvent.DOM_VK_A, "A", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_B,
+ modifiers:{}, chars:"b"},
+ "b", "KeyB", nsIDOMKeyEvent.DOM_VK_B, "b", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_B,
+ modifiers:{shiftKey:1}, chars:"B"},
+ "B", "KeyB", nsIDOMKeyEvent.DOM_VK_B, "B", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_B,
+ modifiers:{ctrlKey:1}, chars:"\u0002"},
+ "b", "KeyB", nsIDOMKeyEvent.DOM_VK_B, "b", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_B,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0002"},
+ "B", "KeyB", nsIDOMKeyEvent.DOM_VK_B, "B", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_B,
+ modifiers:{altKey:1}, chars:"b"},
+ "b", "KeyB", nsIDOMKeyEvent.DOM_VK_B, "b", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_B,
+ modifiers:{altKey:1, shiftKey:1}, chars:"B"},
+ "B", "KeyB", nsIDOMKeyEvent.DOM_VK_B, "B", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_C,
+ modifiers:{}, chars:"c"},
+ "c", "KeyC", nsIDOMKeyEvent.DOM_VK_C, "c", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_C,
+ modifiers:{shiftKey:1}, chars:"C"},
+ "C", "KeyC", nsIDOMKeyEvent.DOM_VK_C, "C", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_C,
+ modifiers:{ctrlKey:1}, chars:"\u0003"},
+ "c", "KeyC", nsIDOMKeyEvent.DOM_VK_C, "c", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_C,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0003"},
+ "C", "KeyC", nsIDOMKeyEvent.DOM_VK_C, "C", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_C,
+ modifiers:{altKey:1}, chars:"c"},
+ "c", "KeyC", nsIDOMKeyEvent.DOM_VK_C, "c", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_C,
+ modifiers:{altKey:1, shiftKey:1}, chars:"C"},
+ "C", "KeyC", nsIDOMKeyEvent.DOM_VK_C, "C", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_D,
+ modifiers:{}, chars:"d"},
+ "d", "KeyD", nsIDOMKeyEvent.DOM_VK_D, "d", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_D,
+ modifiers:{shiftKey:1}, chars:"D"},
+ "D", "KeyD", nsIDOMKeyEvent.DOM_VK_D, "D", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_D,
+ modifiers:{ctrlKey:1}, chars:"\u0004"},
+ "d", "KeyD", nsIDOMKeyEvent.DOM_VK_D, "d", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_D,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0004"},
+ "D", "KeyD", nsIDOMKeyEvent.DOM_VK_D, "D", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_D,
+ modifiers:{altKey:1}, chars:"d"},
+ "d", "KeyD", nsIDOMKeyEvent.DOM_VK_D, "d", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_D,
+ modifiers:{altKey:1, shiftKey:1}, chars:"D"},
+ "D", "KeyD", nsIDOMKeyEvent.DOM_VK_D, "D", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_E,
+ modifiers:{}, chars:"e"},
+ "e", "KeyE", nsIDOMKeyEvent.DOM_VK_E, "e", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_E,
+ modifiers:{shiftKey:1}, chars:"E"},
+ "E", "KeyE", nsIDOMKeyEvent.DOM_VK_E, "E", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_E,
+ modifiers:{ctrlKey:1}, chars:"\u0005"},
+ "e", "KeyE", nsIDOMKeyEvent.DOM_VK_E, "e", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_E,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0005"},
+ "E", "KeyE", nsIDOMKeyEvent.DOM_VK_E, "E", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_E,
+ modifiers:{altKey:1}, chars:"e"},
+ "e", "KeyE", nsIDOMKeyEvent.DOM_VK_E, "e", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_E,
+ modifiers:{altKey:1, shiftKey:1}, chars:"E"},
+ "E", "KeyE", nsIDOMKeyEvent.DOM_VK_E, "E", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_F,
+ modifiers:{}, chars:"f"},
+ "f", "KeyF", nsIDOMKeyEvent.DOM_VK_F, "f", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_F,
+ modifiers:{shiftKey:1}, chars:"F"},
+ "F", "KeyF", nsIDOMKeyEvent.DOM_VK_F, "F", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_F,
+ modifiers:{ctrlKey:1}, chars:"\u0006"},
+ "f", "KeyF", nsIDOMKeyEvent.DOM_VK_F, "f", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_F,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0006"},
+ "F", "KeyF", nsIDOMKeyEvent.DOM_VK_F, "F", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_F,
+ modifiers:{altKey:1}, chars:"f"},
+ "f", "KeyF", nsIDOMKeyEvent.DOM_VK_F, "f", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_F,
+ modifiers:{altKey:1, shiftKey:1}, chars:"F"},
+ "F", "KeyF", nsIDOMKeyEvent.DOM_VK_F, "F", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_G,
+ modifiers:{}, chars:"g"},
+ "g", "KeyG", nsIDOMKeyEvent.DOM_VK_G, "g", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_G,
+ modifiers:{shiftKey:1}, chars:"G"},
+ "G", "KeyG", nsIDOMKeyEvent.DOM_VK_G, "G", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_G,
+ modifiers:{ctrlKey:1}, chars:"\u0007"},
+ "g", "KeyG", nsIDOMKeyEvent.DOM_VK_G, "g", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_G,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0007"},
+ "G", "KeyG", nsIDOMKeyEvent.DOM_VK_G, "G", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_G,
+ modifiers:{altKey:1}, chars:"g"},
+ "g", "KeyG", nsIDOMKeyEvent.DOM_VK_G, "g", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_G,
+ modifiers:{altKey:1, shiftKey:1}, chars:"G"},
+ "G", "KeyG", nsIDOMKeyEvent.DOM_VK_G, "G", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_H,
+ modifiers:{}, chars:"h"},
+ "h", "KeyH", nsIDOMKeyEvent.DOM_VK_H, "h", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_H,
+ modifiers:{shiftKey:1}, chars:"H"},
+ "H", "KeyH", nsIDOMKeyEvent.DOM_VK_H, "H", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_H,
+ modifiers:{ctrlKey:1}, chars:"\u0008"},
+ "h", "KeyH", nsIDOMKeyEvent.DOM_VK_H, "h", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_H,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0008"},
+ "H", "KeyH", nsIDOMKeyEvent.DOM_VK_H, "H", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_H,
+ modifiers:{altKey:1}, chars:"h"},
+ "h", "KeyH", nsIDOMKeyEvent.DOM_VK_H, "h", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_H,
+ modifiers:{altKey:1, shiftKey:1}, chars:"H"},
+ "H", "KeyH", nsIDOMKeyEvent.DOM_VK_H, "H", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_I,
+ modifiers:{}, chars:"i"},
+ "i", "KeyI", nsIDOMKeyEvent.DOM_VK_I, "i", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_I,
+ modifiers:{shiftKey:1}, chars:"I"},
+ "I", "KeyI", nsIDOMKeyEvent.DOM_VK_I, "I", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_I,
+ modifiers:{ctrlKey:1}, chars:"\u0009"},
+ "i", "KeyI", nsIDOMKeyEvent.DOM_VK_I, "i", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_I,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0009"},
+ "I", "KeyI", nsIDOMKeyEvent.DOM_VK_I, "I", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_I,
+ modifiers:{altKey:1}, chars:"i"},
+ "i", "KeyI", nsIDOMKeyEvent.DOM_VK_I, "i", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_I,
+ modifiers:{altKey:1, shiftKey:1}, chars:"I"},
+ "I", "KeyI", nsIDOMKeyEvent.DOM_VK_I, "I", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_J,
+ modifiers:{}, chars:"j"},
+ "j", "KeyJ", nsIDOMKeyEvent.DOM_VK_J, "j", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_J,
+ modifiers:{shiftKey:1}, chars:"J"},
+ "J", "KeyJ", nsIDOMKeyEvent.DOM_VK_J, "J", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_J,
+ modifiers:{ctrlKey:1}, chars:"\u000A"},
+ "j", "KeyJ", nsIDOMKeyEvent.DOM_VK_J, "j", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_J,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u000A"},
+ "J", "KeyJ", nsIDOMKeyEvent.DOM_VK_J, "J", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_J,
+ modifiers:{altKey:1}, chars:"j"},
+ "j", "KeyJ", nsIDOMKeyEvent.DOM_VK_J, "j", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_J,
+ modifiers:{altKey:1, shiftKey:1}, chars:"J"},
+ "J", "KeyJ", nsIDOMKeyEvent.DOM_VK_J, "J", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_K,
+ modifiers:{}, chars:"k"},
+ "k", "KeyK", nsIDOMKeyEvent.DOM_VK_K, "k", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_K,
+ modifiers:{shiftKey:1}, chars:"K"},
+ "K", "KeyK", nsIDOMKeyEvent.DOM_VK_K, "K", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_K,
+ modifiers:{ctrlKey:1}, chars:"\u000B"},
+ "k", "KeyK", nsIDOMKeyEvent.DOM_VK_K, "k", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_K,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u000B"},
+ "K", "KeyK", nsIDOMKeyEvent.DOM_VK_K, "K", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_K,
+ modifiers:{altKey:1}, chars:"k"},
+ "k", "KeyK", nsIDOMKeyEvent.DOM_VK_K, "k", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_K,
+ modifiers:{altKey:1, shiftKey:1}, chars:"K"},
+ "K", "KeyK", nsIDOMKeyEvent.DOM_VK_K, "K", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_L,
+ modifiers:{}, chars:"l"},
+ "l", "KeyL", nsIDOMKeyEvent.DOM_VK_L, "l", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_L,
+ modifiers:{shiftKey:1}, chars:"L"},
+ "L", "KeyL", nsIDOMKeyEvent.DOM_VK_L, "L", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_L,
+ modifiers:{ctrlKey:1}, chars:"\u000C"},
+ "l", "KeyL", nsIDOMKeyEvent.DOM_VK_L, "l", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_L,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u000C"},
+ "L", "KeyL", nsIDOMKeyEvent.DOM_VK_L, "L", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_L,
+ modifiers:{altKey:1}, chars:"l"},
+ "l", "KeyL", nsIDOMKeyEvent.DOM_VK_L, "l", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_L,
+ modifiers:{altKey:1, shiftKey:1}, chars:"L"},
+ "L", "KeyL", nsIDOMKeyEvent.DOM_VK_L, "L", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_M,
+ modifiers:{}, chars:"m"},
+ "m", "KeyM", nsIDOMKeyEvent.DOM_VK_M, "m", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_M,
+ modifiers:{shiftKey:1}, chars:"M"},
+ "M", "KeyM", nsIDOMKeyEvent.DOM_VK_M, "M", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_M,
+ modifiers:{ctrlKey:1}, chars:"\u000D"},
+ "m", "KeyM", nsIDOMKeyEvent.DOM_VK_M, "m", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_M,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u000D"},
+ "M", "KeyM", nsIDOMKeyEvent.DOM_VK_M, "M", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_M,
+ modifiers:{altKey:1}, chars:"m"},
+ "m", "KeyM", nsIDOMKeyEvent.DOM_VK_M, "m", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_M,
+ modifiers:{altKey:1, shiftKey:1}, chars:"M"},
+ "M", "KeyM", nsIDOMKeyEvent.DOM_VK_M, "M", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_N,
+ modifiers:{}, chars:"n"},
+ "n", "KeyN", nsIDOMKeyEvent.DOM_VK_N, "n", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_N,
+ modifiers:{shiftKey:1}, chars:"N"},
+ "N", "KeyN", nsIDOMKeyEvent.DOM_VK_N, "N", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_N,
+ modifiers:{ctrlKey:1}, chars:"\u000E"},
+ "n", "KeyN", nsIDOMKeyEvent.DOM_VK_N, "n", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_N,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u000E"},
+ "N", "KeyN", nsIDOMKeyEvent.DOM_VK_N, "N", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_N,
+ modifiers:{altKey:1}, chars:"n"},
+ "n", "KeyN", nsIDOMKeyEvent.DOM_VK_N, "n", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_N,
+ modifiers:{altKey:1, shiftKey:1}, chars:"N"},
+ "N", "KeyN", nsIDOMKeyEvent.DOM_VK_N, "N", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_O,
+ modifiers:{}, chars:"o"},
+ "o", "KeyO", nsIDOMKeyEvent.DOM_VK_O, "o", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_O,
+ modifiers:{shiftKey:1}, chars:"O"},
+ "O", "KeyO", nsIDOMKeyEvent.DOM_VK_O, "O", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_O,
+ modifiers:{ctrlKey:1}, chars:"\u000F"},
+ "o", "KeyO", nsIDOMKeyEvent.DOM_VK_O, "o", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_O,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u000F"},
+ "O", "KeyO", nsIDOMKeyEvent.DOM_VK_O, "O", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_O,
+ modifiers:{altKey:1}, chars:"o"},
+ "o", "KeyO", nsIDOMKeyEvent.DOM_VK_O, "o", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_O,
+ modifiers:{altKey:1, shiftKey:1}, chars:"O"},
+ "O", "KeyO", nsIDOMKeyEvent.DOM_VK_O, "O", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_P,
+ modifiers:{}, chars:"p"},
+ "p", "KeyP", nsIDOMKeyEvent.DOM_VK_P, "p", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_P,
+ modifiers:{shiftKey:1}, chars:"P"},
+ "P", "KeyP", nsIDOMKeyEvent.DOM_VK_P, "P", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_P,
+ modifiers:{ctrlKey:1}, chars:"\u0010"},
+ "p", "KeyP", nsIDOMKeyEvent.DOM_VK_P, "p", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_P,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0010"},
+ "P", "KeyP", nsIDOMKeyEvent.DOM_VK_P, "P", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_P,
+ modifiers:{altKey:1}, chars:"p"},
+ "p", "KeyP", nsIDOMKeyEvent.DOM_VK_P, "p", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_P,
+ modifiers:{altKey:1, shiftKey:1}, chars:"P"},
+ "P", "KeyP", nsIDOMKeyEvent.DOM_VK_P, "P", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_Q,
+ modifiers:{}, chars:"q"},
+ "q", "KeyQ", nsIDOMKeyEvent.DOM_VK_Q, "q", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_Q,
+ modifiers:{shiftKey:1}, chars:"Q"},
+ "Q", "KeyQ", nsIDOMKeyEvent.DOM_VK_Q, "Q", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_Q,
+ modifiers:{ctrlKey:1}, chars:"\u0011"},
+ "q", "KeyQ", nsIDOMKeyEvent.DOM_VK_Q, "q", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_Q,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0011"},
+ "Q", "KeyQ", nsIDOMKeyEvent.DOM_VK_Q, "Q", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_Q,
+ modifiers:{altKey:1}, chars:"q"},
+ "q", "KeyQ", nsIDOMKeyEvent.DOM_VK_Q, "q", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_Q,
+ modifiers:{altKey:1, shiftKey:1}, chars:"Q"},
+ "Q", "KeyQ", nsIDOMKeyEvent.DOM_VK_Q, "Q", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_R,
+ modifiers:{}, chars:"r"},
+ "r", "KeyR", nsIDOMKeyEvent.DOM_VK_R, "r", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_R,
+ modifiers:{shiftKey:1}, chars:"R"},
+ "R", "KeyR", nsIDOMKeyEvent.DOM_VK_R, "R", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_R,
+ modifiers:{ctrlKey:1}, chars:"\u0012"},
+ "r", "KeyR", nsIDOMKeyEvent.DOM_VK_R, "r", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_R,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0012"},
+ "R", "KeyR", nsIDOMKeyEvent.DOM_VK_R, "R", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_R,
+ modifiers:{altKey:1}, chars:"r"},
+ "r", "KeyR", nsIDOMKeyEvent.DOM_VK_R, "r", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_R,
+ modifiers:{altKey:1, shiftKey:1}, chars:"R"},
+ "R", "KeyR", nsIDOMKeyEvent.DOM_VK_R, "R", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_S,
+ modifiers:{}, chars:"s"},
+ "s", "KeyS", nsIDOMKeyEvent.DOM_VK_S, "s", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_S,
+ modifiers:{shiftKey:1}, chars:"S"},
+ "S", "KeyS", nsIDOMKeyEvent.DOM_VK_S, "S", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_S,
+ modifiers:{ctrlKey:1}, chars:"\u0013"},
+ "s", "KeyS", nsIDOMKeyEvent.DOM_VK_S, "s", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_S,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0013"},
+ "S", "KeyS", nsIDOMKeyEvent.DOM_VK_S, "S", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_S,
+ modifiers:{altKey:1}, chars:"s"},
+ "s", "KeyS", nsIDOMKeyEvent.DOM_VK_S, "s", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_S,
+ modifiers:{altKey:1, shiftKey:1}, chars:"S"},
+ "S", "KeyS", nsIDOMKeyEvent.DOM_VK_S, "S", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_T,
+ modifiers:{}, chars:"t"},
+ "t", "KeyT", nsIDOMKeyEvent.DOM_VK_T, "t", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_T,
+ modifiers:{shiftKey:1}, chars:"T"},
+ "T", "KeyT", nsIDOMKeyEvent.DOM_VK_T, "T", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_T,
+ modifiers:{ctrlKey:1}, chars:"\u0014"},
+ "t", "KeyT", nsIDOMKeyEvent.DOM_VK_T, "t", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_T,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0014"},
+ "T", "KeyT", nsIDOMKeyEvent.DOM_VK_T, "T", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_T,
+ modifiers:{altKey:1}, chars:"t"},
+ "t", "KeyT", nsIDOMKeyEvent.DOM_VK_T, "t", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_T,
+ modifiers:{altKey:1, shiftKey:1}, chars:"T"},
+ "T", "KeyT", nsIDOMKeyEvent.DOM_VK_T, "T", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_U,
+ modifiers:{}, chars:"u"},
+ "u", "KeyU", nsIDOMKeyEvent.DOM_VK_U, "u", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_U,
+ modifiers:{shiftKey:1}, chars:"U"},
+ "U", "KeyU", nsIDOMKeyEvent.DOM_VK_U, "U", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_U,
+ modifiers:{ctrlKey:1}, chars:"\u0015"},
+ "u", "KeyU", nsIDOMKeyEvent.DOM_VK_U, "u", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_U,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0015"},
+ "U", "KeyU", nsIDOMKeyEvent.DOM_VK_U, "U", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_U,
+ modifiers:{altKey:1}, chars:"u"},
+ "u", "KeyU", nsIDOMKeyEvent.DOM_VK_U, "u", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_U,
+ modifiers:{altKey:1, shiftKey:1}, chars:"U"},
+ "U", "KeyU", nsIDOMKeyEvent.DOM_VK_U, "U", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_V,
+ modifiers:{}, chars:"v"},
+ "v", "KeyV", nsIDOMKeyEvent.DOM_VK_V, "v", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_V,
+ modifiers:{shiftKey:1}, chars:"V"},
+ "V", "KeyV", nsIDOMKeyEvent.DOM_VK_V, "V", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_V,
+ modifiers:{ctrlKey:1}, chars:"\u0016"},
+ "v", "KeyV", nsIDOMKeyEvent.DOM_VK_V, "v", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_V,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0016"},
+ "V", "KeyV", nsIDOMKeyEvent.DOM_VK_V, "V", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_V,
+ modifiers:{altKey:1}, chars:"v"},
+ "v", "KeyV", nsIDOMKeyEvent.DOM_VK_V, "v", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_V,
+ modifiers:{altKey:1, shiftKey:1}, chars:"V"},
+ "V", "KeyV", nsIDOMKeyEvent.DOM_VK_V, "V", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_W,
+ modifiers:{}, chars:"w"},
+ "w", "KeyW", nsIDOMKeyEvent.DOM_VK_W, "w", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_W,
+ modifiers:{shiftKey:1}, chars:"W"},
+ "W", "KeyW", nsIDOMKeyEvent.DOM_VK_W, "W", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_W,
+ modifiers:{ctrlKey:1}, chars:"\u0017"},
+ "w", "KeyW", nsIDOMKeyEvent.DOM_VK_W, "w", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_W,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0017"},
+ "W", "KeyW", nsIDOMKeyEvent.DOM_VK_W, "W", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_W,
+ modifiers:{altKey:1}, chars:"w"},
+ "w", "KeyW", nsIDOMKeyEvent.DOM_VK_W, "w", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_W,
+ modifiers:{altKey:1, shiftKey:1}, chars:"W"},
+ "W", "KeyW", nsIDOMKeyEvent.DOM_VK_W, "W", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_X,
+ modifiers:{}, chars:"x"},
+ "x", "KeyX", nsIDOMKeyEvent.DOM_VK_X, "x", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_X,
+ modifiers:{shiftKey:1}, chars:"X"},
+ "X", "KeyX", nsIDOMKeyEvent.DOM_VK_X, "X", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_X,
+ modifiers:{ctrlKey:1}, chars:"\u0018"},
+ "x", "KeyX", nsIDOMKeyEvent.DOM_VK_X, "x", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_X,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0018"},
+ "X", "KeyX", nsIDOMKeyEvent.DOM_VK_X, "X", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_X,
+ modifiers:{altKey:1}, chars:"x"},
+ "x", "KeyX", nsIDOMKeyEvent.DOM_VK_X, "x", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_X,
+ modifiers:{altKey:1, shiftKey:1}, chars:"X"},
+ "X", "KeyX", nsIDOMKeyEvent.DOM_VK_X, "X", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_Y,
+ modifiers:{}, chars:"y"},
+ "y", "KeyY", nsIDOMKeyEvent.DOM_VK_Y, "y", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_Y,
+ modifiers:{shiftKey:1}, chars:"Y"},
+ "Y", "KeyY", nsIDOMKeyEvent.DOM_VK_Y, "Y", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_Y,
+ modifiers:{ctrlKey:1}, chars:"\u0019"},
+ "y", "KeyY", nsIDOMKeyEvent.DOM_VK_Y, "y", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_Y,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0019"},
+ "Y", "KeyY", nsIDOMKeyEvent.DOM_VK_Y, "Y", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_Y,
+ modifiers:{altKey:1}, chars:"y"},
+ "y", "KeyY", nsIDOMKeyEvent.DOM_VK_Y, "y", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_Y,
+ modifiers:{altKey:1, shiftKey:1}, chars:"Y"},
+ "Y", "KeyY", nsIDOMKeyEvent.DOM_VK_Y, "Y", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_Z,
+ modifiers:{}, chars:"z"},
+ "z", "KeyZ", nsIDOMKeyEvent.DOM_VK_Z, "z", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_Z,
+ modifiers:{shiftKey:1}, chars:"Z"},
+ "Z", "KeyZ", nsIDOMKeyEvent.DOM_VK_Z, "Z", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_Z,
+ modifiers:{ctrlKey:1}, chars:"\u001A"},
+ "z", "KeyZ", nsIDOMKeyEvent.DOM_VK_Z, "z", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_Z,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u001A"},
+ "Z", "KeyZ", nsIDOMKeyEvent.DOM_VK_Z, "Z", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_Z,
+ modifiers:{altKey:1}, chars:"z"},
+ "z", "KeyZ", nsIDOMKeyEvent.DOM_VK_Z, "z", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_Z,
+ modifiers:{altKey:1, shiftKey:1}, chars:"Z"},
+ "Z", "KeyZ", nsIDOMKeyEvent.DOM_VK_Z, "Z", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Numeric
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_0,
+ modifiers:{}, chars:"0"},
+ "0", "Digit0", nsIDOMKeyEvent.DOM_VK_0, "0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_0,
+ modifiers:{shiftKey:1}, chars:")"},
+ ")", "Digit0", nsIDOMKeyEvent.DOM_VK_0, ")", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_0,
+ modifiers:{ctrlKey:1}, chars:""},
+ "0", "Digit0", nsIDOMKeyEvent.DOM_VK_0, "0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_0,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ ")", "Digit0", nsIDOMKeyEvent.DOM_VK_0, ")", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_0,
+ modifiers:{altKey:1}, chars:"0"},
+ "0", "Digit0", nsIDOMKeyEvent.DOM_VK_0, "0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_0,
+ modifiers:{altKey:1, shiftKey:1}, chars:")"},
+ ")", "Digit0", nsIDOMKeyEvent.DOM_VK_0, ")", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_1,
+ modifiers:{}, chars:"1"},
+ "1", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_1,
+ modifiers:{shiftKey:1}, chars:"!"},
+ "!", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "!", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_1,
+ modifiers:{ctrlKey:1}, chars:""},
+ "1", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_1,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "!", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "!", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_1,
+ modifiers:{altKey:1}, chars:"1"},
+ "1", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_1,
+ modifiers:{altKey:1, shiftKey:1}, chars:"!"},
+ "!", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "!", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_2,
+ modifiers:{}, chars:"2"},
+ "2", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_2,
+ modifiers:{shiftKey:1}, chars:"@"},
+ "@", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "@", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_2,
+ modifiers:{ctrlKey:1}, chars:""},
+ "2", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_2,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "@", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "@", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_2,
+ modifiers:{altKey:1}, chars:"2"},
+ "2", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_2,
+ modifiers:{altKey:1, shiftKey:1}, chars:"@"},
+ "@", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "@", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_3,
+ modifiers:{}, chars:"3"},
+ "3", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "3", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_3,
+ modifiers:{shiftKey:1}, chars:"#"},
+ "#", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "#", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_3,
+ modifiers:{ctrlKey:1}, chars:""},
+ "3", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "3", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_3,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "#", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "#", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_3,
+ modifiers:{altKey:1}, chars:"3"},
+ "3", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "3", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_3,
+ modifiers:{altKey:1, shiftKey:1}, chars:"#"},
+ "#", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "#", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_4,
+ modifiers:{}, chars:"4"},
+ "4", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_4,
+ modifiers:{shiftKey:1}, chars:"$"},
+ "$", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "$", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_4,
+ modifiers:{ctrlKey:1}, chars:""},
+ "4", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_4,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "$", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "$", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_4,
+ modifiers:{altKey:1}, chars:"4"},
+ "4", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_4,
+ modifiers:{altKey:1, shiftKey:1}, chars:"$"},
+ "$", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "$", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_5,
+ modifiers:{}, chars:"5"},
+ "5", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_5,
+ modifiers:{shiftKey:1}, chars:"%"},
+ "%", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "%", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_5,
+ modifiers:{ctrlKey:1}, chars:""},
+ "5", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_5,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "%", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "%", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_5,
+ modifiers:{altKey:1}, chars:"5"},
+ "5", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_5,
+ modifiers:{altKey:1, shiftKey:1}, chars:"%"},
+ "%", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "%", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_6,
+ modifiers:{}, chars:"6"},
+ "6", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "6", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_6,
+ modifiers:{shiftKey:1}, chars:"^"},
+ "^", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "^", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_6,
+ modifiers:{ctrlKey:1}, chars:""},
+ "6", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "6", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_6,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u001E"},
+ "^", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "^", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_6,
+ modifiers:{altKey:1}, chars:"6"},
+ "6", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "6", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_6,
+ modifiers:{altKey:1, shiftKey:1}, chars:"^"},
+ "^", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "^", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_7,
+ modifiers:{}, chars:"7"},
+ "7", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_7,
+ modifiers:{shiftKey:1}, chars:"&"},
+ "&", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "&", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_7,
+ modifiers:{ctrlKey:1}, chars:""},
+ "7", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_7,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "&", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "&", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_7,
+ modifiers:{altKey:1}, chars:"7"},
+ "7", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_7,
+ modifiers:{altKey:1, shiftKey:1}, chars:"&"},
+ "&", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "&", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_8,
+ modifiers:{}, chars:"8"},
+ "8", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_8,
+ modifiers:{shiftKey:1}, chars:"*"},
+ "*", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "*", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_8,
+ modifiers:{ctrlKey:1, }, chars:""},
+ "8", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_8,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "*", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "*", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_8,
+ modifiers:{altKey:1}, chars:"8"},
+ "8", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_8,
+ modifiers:{altKey:1, shiftKey:1}, chars:"*"},
+ "*", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "*", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_9,
+ modifiers:{}, chars:"9"},
+ "9", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_9,
+ modifiers:{shiftKey:1}, chars:"("},
+ "(", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "(", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_9,
+ modifiers:{ctrlKey:1}, chars:""},
+ "9", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_9,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "(", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "(", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_9,
+ modifiers:{altKey:1}, chars:"9"},
+ "9", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_9,
+ modifiers:{altKey:1, shiftKey:1}, chars:"("},
+ "(", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "(", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // OEM keys
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_MINUS,
+ modifiers:{}, chars:"-"},
+ "-", "Minus", nsIDOMKeyEvent.DOM_VK_HYPHEN_MINUS, "-", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_MINUS,
+ modifiers:{shiftKey:1}, chars:"_"},
+ "_", "Minus", nsIDOMKeyEvent.DOM_VK_HYPHEN_MINUS, "_", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_MINUS,
+ modifiers:{ctrlKey:1}, chars:""},
+ "-", "Minus", nsIDOMKeyEvent.DOM_VK_HYPHEN_MINUS, "-", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_MINUS,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u001F"},
+ "_", "Minus", nsIDOMKeyEvent.DOM_VK_HYPHEN_MINUS, "_", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_MINUS,
+ modifiers:{altKey:1}, chars:"-"},
+ "-", "Minus", nsIDOMKeyEvent.DOM_VK_HYPHEN_MINUS, "-", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_MINUS,
+ modifiers:{altKey:1, shiftKey:1}, chars:"_"},
+ "_", "Minus", nsIDOMKeyEvent.DOM_VK_HYPHEN_MINUS, "_", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_PLUS,
+ modifiers:{}, chars:"="},
+ "=", "Equal", nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_PLUS,
+ modifiers:{shiftKey:1}, chars:"+"},
+ "+", "Equal", nsIDOMKeyEvent.DOM_VK_EQUALS, "+", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_PLUS,
+ modifiers:{ctrlKey:1}, chars:""},
+ "=", "Equal", nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_PLUS,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "+", "Equal", nsIDOMKeyEvent.DOM_VK_EQUALS, "+", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_PLUS,
+ modifiers:{altKey:1}, chars:"="},
+ "=", "Equal", nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_PLUS,
+ modifiers:{altKey:1, shiftKey:1}, chars:"+"},
+ "+", "Equal", nsIDOMKeyEvent.DOM_VK_EQUALS, "+", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_4,
+ modifiers:{}, chars:"["},
+ "[", "BracketLeft", nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET, "[", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_4,
+ modifiers:{shiftKey:1}, chars:"{"},
+ "{", "BracketLeft", nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET, "{", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_4,
+ modifiers:{ctrlKey:1}, chars:"\u001B"},
+ "[", "BracketLeft", nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET, "[", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_4,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "{", "BracketLeft", nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET, "{", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_4,
+ modifiers:{altKey:1}, chars:"["},
+ "[", "BracketLeft", nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET, "[", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_4,
+ modifiers:{altKey:1, shiftKey:1}, chars:"{"},
+ "{", "BracketLeft", nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET, "{", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_6,
+ modifiers:{}, chars:"]"},
+ "]", "BracketRight", nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET, "]", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_6,
+ modifiers:{shiftKey:1}, chars:"}"},
+ "}", "BracketRight", nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET, "}", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_6,
+ modifiers:{ctrlKey:1}, chars:"\u001D"},
+ "]", "BracketRight", nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET, "]", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_6,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "}", "BracketRight", nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET, "}", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_6,
+ modifiers:{altKey:1}, chars:"]"},
+ "]", "BracketRight", nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET, "]", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_6,
+ modifiers:{altKey:1, shiftKey:1}, chars:"}"},
+ "}", "BracketRight", nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET, "}", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_1,
+ modifiers:{}, chars:";"},
+ ";", "Semicolon", nsIDOMKeyEvent.DOM_VK_SEMICOLON, ";", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_1,
+ modifiers:{shiftKey:1}, chars:":"},
+ ":", "Semicolon", nsIDOMKeyEvent.DOM_VK_SEMICOLON, ":", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_1,
+ modifiers:{ctrlKey:1}, chars:""},
+ ";", "Semicolon", nsIDOMKeyEvent.DOM_VK_SEMICOLON, ";", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_1,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ ":", "Semicolon", nsIDOMKeyEvent.DOM_VK_SEMICOLON, ":", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_1,
+ modifiers:{altKey:1}, chars:";"},
+ ";", "Semicolon", nsIDOMKeyEvent.DOM_VK_SEMICOLON, ";", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_1,
+ modifiers:{altKey:1, shiftKey:1}, chars:":"},
+ ":", "Semicolon", nsIDOMKeyEvent.DOM_VK_SEMICOLON, ":", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_7,
+ modifiers:{}, chars:"'"},
+ "'", "Quote", nsIDOMKeyEvent.DOM_VK_QUOTE, "'", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_7,
+ modifiers:{shiftKey:1}, chars:"\""},
+ "\"", "Quote", nsIDOMKeyEvent.DOM_VK_QUOTE, "\"", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_7,
+ modifiers:{ctrlKey:1}, chars:""},
+ "'", "Quote", nsIDOMKeyEvent.DOM_VK_QUOTE, "'", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_7,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "\"", "Quote", nsIDOMKeyEvent.DOM_VK_QUOTE, "\"", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_7,
+ modifiers:{altKey:1}, chars:"'"},
+ "'", "Quote", nsIDOMKeyEvent.DOM_VK_QUOTE, "'", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_7,
+ modifiers:{altKey:1, shiftKey:1}, chars:"\""},
+ "\"", "Quote", nsIDOMKeyEvent.DOM_VK_QUOTE, "\"", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_5,
+ modifiers:{}, chars:"\\"},
+ "\\", "Backslash", nsIDOMKeyEvent.DOM_VK_BACK_SLASH, "\\", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_5,
+ modifiers:{shiftKey:1}, chars:"|"},
+ "|", "Backslash", nsIDOMKeyEvent.DOM_VK_BACK_SLASH, "|", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_5,
+ modifiers:{ctrlKey:1}, chars:"\u001C"},
+ "\\", "Backslash", nsIDOMKeyEvent.DOM_VK_BACK_SLASH, "\\", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_5,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "|", "Backslash", nsIDOMKeyEvent.DOM_VK_BACK_SLASH, "|", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_5,
+ modifiers:{altKey:1}, chars:"\\"},
+ "\\", "Backslash", nsIDOMKeyEvent.DOM_VK_BACK_SLASH, "\\", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_5,
+ modifiers:{altKey:1, shiftKey:1}, chars:"|"},
+ "|", "Backslash", nsIDOMKeyEvent.DOM_VK_BACK_SLASH, "|", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_COMMA,
+ modifiers:{}, chars:","},
+ ",", "Comma", nsIDOMKeyEvent.DOM_VK_COMMA, ",", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_COMMA,
+ modifiers:{shiftKey:1}, chars:"<"},
+ "<", "Comma", nsIDOMKeyEvent.DOM_VK_COMMA, "<", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_COMMA,
+ modifiers:{ctrlKey:1}, chars:""},
+ ",", "Comma", nsIDOMKeyEvent.DOM_VK_COMMA, ",", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_COMMA,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "<", "Comma", nsIDOMKeyEvent.DOM_VK_COMMA, "<", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_COMMA,
+ modifiers:{altKey:1}, chars:","},
+ ",", "Comma", nsIDOMKeyEvent.DOM_VK_COMMA, ",", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_COMMA,
+ modifiers:{altKey:1, shiftKey:1}, chars:"<"},
+ "<", "Comma", nsIDOMKeyEvent.DOM_VK_COMMA, "<", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_PERIOD,
+ modifiers:{}, chars:"."},
+ ".", "Period", nsIDOMKeyEvent.DOM_VK_PERIOD, ".", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_PERIOD,
+ modifiers:{shiftKey:1}, chars:">"},
+ ">", "Period", nsIDOMKeyEvent.DOM_VK_PERIOD, ">", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_PERIOD,
+ modifiers:{ctrlKey:1}, chars:""},
+ ".", "Period", nsIDOMKeyEvent.DOM_VK_PERIOD, ".", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_PERIOD,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ ">", "Period", nsIDOMKeyEvent.DOM_VK_PERIOD, ">", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_PERIOD,
+ modifiers:{altKey:1}, chars:"."},
+ ".", "Period", nsIDOMKeyEvent.DOM_VK_PERIOD, ".", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_PERIOD,
+ modifiers:{altKey:1, shiftKey:1}, chars:">"},
+ ">", "Period", nsIDOMKeyEvent.DOM_VK_PERIOD, ">", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_2,
+ modifiers:{}, chars:"/"},
+ "/", "Slash", nsIDOMKeyEvent.DOM_VK_SLASH, "/", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_2,
+ modifiers:{shiftKey:1}, chars:"?"},
+ "?", "Slash", nsIDOMKeyEvent.DOM_VK_SLASH, "?", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_2,
+ modifiers:{ctrlKey:1}, chars:""},
+ "/", "Slash", nsIDOMKeyEvent.DOM_VK_SLASH, "/", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_2,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "?", "Slash", nsIDOMKeyEvent.DOM_VK_SLASH, "?", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_2,
+ modifiers:{altKey:1}, chars:"/"},
+ "/", "Slash", nsIDOMKeyEvent.DOM_VK_SLASH, "/", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_2,
+ modifiers:{altKey:1, shiftKey:1}, chars:"?"},
+ "?", "Slash", nsIDOMKeyEvent.DOM_VK_SLASH, "?", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_3,
+ modifiers:{}, chars:"`"},
+ "`", "Backquote", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "`", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_3,
+ modifiers:{shiftKey:1}, chars:"~"},
+ "~", "Backquote", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "~", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_3,
+ modifiers:{ctrlKey:1}, chars:""},
+ "`", "Backquote", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "`", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_3,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "~", "Backquote", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "~", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_3,
+ modifiers:{altKey:1}, chars:"`"},
+ "`", "Backquote", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "`", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_3,
+ modifiers:{altKey:1, shiftKey:1}, chars:"~"},
+ "~", "Backquote", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "~", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Numpad
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD0,
+ modifiers:{numLockKey:1}, chars:"0"},
+ "0", "Numpad0", nsIDOMKeyEvent.DOM_VK_NUMPAD0, "0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD1,
+ modifiers:{numLockKey:1}, chars:"1"},
+ "1", "Numpad1", nsIDOMKeyEvent.DOM_VK_NUMPAD1, "1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD2,
+ modifiers:{numLockKey:1}, chars:"2"},
+ "2", "Numpad2", nsIDOMKeyEvent.DOM_VK_NUMPAD2, "2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD3,
+ modifiers:{numLockKey:1}, chars:"3"},
+ "3", "Numpad3", nsIDOMKeyEvent.DOM_VK_NUMPAD3, "3", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD4,
+ modifiers:{numLockKey:1}, chars:"4"},
+ "4", "Numpad4", nsIDOMKeyEvent.DOM_VK_NUMPAD4, "4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD5,
+ modifiers:{numLockKey:1}, chars:"5"},
+ "5", "Numpad5", nsIDOMKeyEvent.DOM_VK_NUMPAD5, "5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD6,
+ modifiers:{numLockKey:1}, chars:"6"},
+ "6", "Numpad6", nsIDOMKeyEvent.DOM_VK_NUMPAD6, "6", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD7,
+ modifiers:{numLockKey:1}, chars:"7"},
+ "7", "Numpad7", nsIDOMKeyEvent.DOM_VK_NUMPAD7, "7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD8,
+ modifiers:{numLockKey:1}, chars:"8"},
+ "8", "Numpad8", nsIDOMKeyEvent.DOM_VK_NUMPAD8, "8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD9,
+ modifiers:{numLockKey:1}, chars:"9"},
+ "9", "Numpad9", nsIDOMKeyEvent.DOM_VK_NUMPAD9, "9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_MULTIPLY,
+ modifiers:{numLockKey:1}, chars:"*"},
+ "*", "NumpadMultiply", nsIDOMKeyEvent.DOM_VK_MULTIPLY, "*", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_MULTIPLY,
+ modifiers:{numLockKey:1, shiftKey:1}, chars:"*"},
+ "*", "NumpadMultiply", nsIDOMKeyEvent.DOM_VK_MULTIPLY, "*", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_ADD,
+ modifiers:{numLockKey:1}, chars:"+"},
+ "+", "NumpadAdd", nsIDOMKeyEvent.DOM_VK_ADD, "+", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_ADD,
+ modifiers:{numLockKey:1, shiftKey:1}, chars:"+"},
+ "+", "NumpadAdd", nsIDOMKeyEvent.DOM_VK_ADD, "+", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ // VK_SEPARATOR is keycode for NEC's PC-98 series whose keyboard layout was
+ // different from current PC's keyboard layout and it cannot connect to
+ // current PC. Note that even if we synthesize WM_KEYDOWN with
+ // VK_SEPARATOR, it doesn't work on Win7.
+ //yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_SEPARATOR,
+ // modifiers:{numLockKey:1}, chars:""},
+ // "", "", nsIDOMKeyEvent.DOM_VK_SEPARATOR, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ //yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_SEPARATOR,
+ // modifiers:{numLockKey:1, shiftKey:1}, chars:""},
+ // "", "", nsIDOMKeyEvent.DOM_VK_SEPARATOR, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_SUBTRACT,
+ modifiers:{numLockKey:1}, chars:"-"},
+ "-", "NumpadSubtract", nsIDOMKeyEvent.DOM_VK_SUBTRACT, "-", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_SUBTRACT,
+ modifiers:{numLockKey:1, shiftKey:1}, chars:"-"},
+ "-", "NumpadSubtract", nsIDOMKeyEvent.DOM_VK_SUBTRACT, "-", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_DECIMAL,
+ modifiers:{numLockKey:1}, chars:"."},
+ ".", "NumpadDecimal", nsIDOMKeyEvent.DOM_VK_DECIMAL, ".", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_DECIMAL,
+ modifiers:{numLockKey:1, shiftKey:1}, chars:"."},
+ ".", "NumpadDecimal", nsIDOMKeyEvent.DOM_VK_DECIMAL, ".", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_DIVIDE,
+ modifiers:{numLockKey:1}, chars:"/"},
+ "/", "NumpadDivide", nsIDOMKeyEvent.DOM_VK_DIVIDE, "/", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_DIVIDE,
+ modifiers:{numLockKey:1, shiftKey:1}, chars:"/"},
+ "/", "NumpadDivide", nsIDOMKeyEvent.DOM_VK_DIVIDE, "/", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD_RETURN,
+ modifiers:{numLockKey:1}, chars:"\r"},
+ "Enter", "NumpadEnter", nsIDOMKeyEvent.DOM_VK_RETURN, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD_RETURN,
+ modifiers:{numLockKey:1, shiftKey:1}, chars:"\r"},
+ "Enter", "NumpadEnter", nsIDOMKeyEvent.DOM_VK_RETURN, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+
+ // Numpad without NumLock
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD_PRIOR,
+ modifiers:{}, chars:""},
+ "PageUp", "Numpad9", nsIDOMKeyEvent.DOM_VK_PAGE_UP, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD_NEXT,
+ modifiers:{}, chars:""},
+ "PageDown", "Numpad3", nsIDOMKeyEvent.DOM_VK_PAGE_DOWN, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD_END,
+ modifiers:{}, chars:""},
+ "End", "Numpad1", nsIDOMKeyEvent.DOM_VK_END, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD_HOME,
+ modifiers:{}, chars:""},
+ "Home", "Numpad7", nsIDOMKeyEvent.DOM_VK_HOME, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD_LEFT,
+ modifiers:{}, chars:""},
+ "ArrowLeft", "Numpad4", nsIDOMKeyEvent.DOM_VK_LEFT, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD_UP,
+ modifiers:{}, chars:""},
+ "ArrowUp", "Numpad8", nsIDOMKeyEvent.DOM_VK_UP, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD_RIGHT,
+ modifiers:{}, chars:""},
+ "ArrowRight", "Numpad6", nsIDOMKeyEvent.DOM_VK_RIGHT, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD_DOWN,
+ modifiers:{}, chars:""},
+ "ArrowDown", "Numpad2", nsIDOMKeyEvent.DOM_VK_DOWN, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD_INSERT,
+ modifiers:{}, chars:""},
+ "Insert", "Numpad0", nsIDOMKeyEvent.DOM_VK_INSERT, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_NUMPAD_DELETE,
+ modifiers:{}, chars:""},
+ "Delete", "NumpadDecimal", nsIDOMKeyEvent.DOM_VK_DELETE, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_CLEAR,
+ modifiers:{}, chars:""},
+ "Clear", "Numpad5", nsIDOMKeyEvent.DOM_VK_CLEAR, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+
+ // Even if widget receives unknown keycode, it should dispatch key events
+ // whose keycode is 0 rather than native keycode.
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:0x3A,
+ modifiers:{numLockKey:1}, chars:""},
+ "Unidentified", "", 0, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // French
+ // Numeric
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_0,
+ modifiers:{}, chars:"\u00E0"},
+ "\u00E0", "Digit0", nsIDOMKeyEvent.DOM_VK_0, "\u00E0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_0,
+ modifiers:{shiftKey:1}, chars:"0"},
+ "0", "Digit0", nsIDOMKeyEvent.DOM_VK_0, "0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_1,
+ modifiers:{}, chars:"&"},
+ "&", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "&", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_1,
+ modifiers:{shiftKey:1}, chars:"1"},
+ "1", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_2,
+ modifiers:{}, chars:"\u00E9"},
+ "\u00E9", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "\u00E9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_2,
+ modifiers:{shiftKey:1}, chars:"2"},
+ "2", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_3,
+ modifiers:{}, chars:"\""},
+ "\"", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "\"", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_3,
+ modifiers:{shiftKey:1}, chars:"3"},
+ "3", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "3", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_4,
+ modifiers:{}, chars:"'"},
+ "'", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "'", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_4,
+ modifiers:{shiftKey:1}, chars:"4"},
+ "4", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_5,
+ modifiers:{}, chars:"("},
+ "(", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "(", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_5,
+ modifiers:{shiftKey:1}, chars:"5"},
+ "5", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_6,
+ modifiers:{}, chars:"-"},
+ "-", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "-", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_6,
+ modifiers:{shiftKey:1}, chars:"6"},
+ "6", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "6", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_7,
+ modifiers:{}, chars:"\u00E8"},
+ "\u00E8", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "\u00E8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_7,
+ modifiers:{shiftKey:1}, chars:"7"},
+ "7", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_8,
+ modifiers:{}, chars:"_"},
+ "_", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "_", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_8,
+ modifiers:{shiftKey:1}, chars:"8"},
+ "8", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_9,
+ modifiers:{}, chars:"\u00E7"},
+ "\u00E7", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "\u00E7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_9,
+ modifiers:{shiftKey:1}, chars:"9"},
+ "9", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Numeric with ShiftLock
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_0,
+ modifiers:{capsLockKey:1}, chars:"0"},
+ "0", "Digit0", nsIDOMKeyEvent.DOM_VK_0, "0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_0,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:"\u00E0"},
+ "\u00E0", "Digit0", nsIDOMKeyEvent.DOM_VK_0, "\u00E0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_1,
+ modifiers:{capsLockKey:1}, chars:"1"},
+ "1", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_1,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:"&"},
+ "&", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "&", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_2,
+ modifiers:{capsLockKey:1}, chars:"2"},
+ "2", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_2,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:"\u00E9"},
+ "\u00E9", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "\u00E9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_3,
+ modifiers:{capsLockKey:1}, chars:"3"},
+ "3", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "3", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_3,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:"\""},
+ "\"", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "\"", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_4,
+ modifiers:{capsLockKey:1}, chars:"4"},
+ "4", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_4,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:"'"},
+ "'", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "'", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_5,
+ modifiers:{capsLockKey:1}, chars:"5"},
+ "5", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_5,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:"("},
+ "(", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "(", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_6,
+ modifiers:{capsLockKey:1}, chars:"6"},
+ "6", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "6", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_6,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:"-"},
+ "-", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "-", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_7,
+ modifiers:{capsLockKey:1}, chars:"7"},
+ "7", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_7,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:"\u00E8"},
+ "\u00E8", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "\u00E8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_8,
+ modifiers:{capsLockKey:1}, chars:"8"},
+ "8", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_8,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:"_"},
+ "_", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "_", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_9,
+ modifiers:{capsLockKey:1}, chars:"9"},
+ "9", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_9,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:"\u00E7"},
+ "\u00E7", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "\u00E7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // OEM keys
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_7,
+ modifiers:{}, chars:"\u00B2"},
+ "\u00B2", "Backquote", 0, "\u00B2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_7,
+ modifiers:{shiftKey:1}, chars:""},
+ "", "Backquote", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_4,
+ modifiers:{}, chars:")"},
+ ")", "Minus", nsIDOMKeyEvent.DOM_VK_CLOSE_PAREN, ")", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_4,
+ modifiers:{shiftKey:1}, chars:"\u00B0"},
+ "\u00B0", "Minus", nsIDOMKeyEvent.DOM_VK_CLOSE_PAREN, "\u00B0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_PLUS,
+ modifiers:{}, chars:"="},
+ "=", "Equal", nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_PLUS,
+ modifiers:{shiftKey:1}, chars:"+"},
+ "+", "Equal", nsIDOMKeyEvent.DOM_VK_EQUALS, "+", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ //yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_6,
+ // modifiers:{}, chars:""},
+ // "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD); // Dead-key
+ //yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_6,
+ // modifiers:{shiftKey:1}, chars:""},
+ // ["^^", "^", "^", "^"], "BracketLeft", nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD); // Dead-key
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_1,
+ modifiers:{}, chars:"$"},
+ "$", "BracketRight", nsIDOMKeyEvent.DOM_VK_DOLLAR, "$", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_1,
+ modifiers:{shiftKey:1}, chars:"\u00A3"},
+ "\u00A3", "BracketRight", nsIDOMKeyEvent.DOM_VK_DOLLAR, "\u00A3", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_3,
+ modifiers:{}, chars:"\u00F9"},
+ "\u00F9", "Quote", nsIDOMKeyEvent.DOM_VK_PERCENT, "\u00F9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_3,
+ modifiers:{shiftKey:1}, chars:"%"},
+ "%", "Quote", nsIDOMKeyEvent.DOM_VK_PERCENT, "%", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_5,
+ modifiers:{}, chars:"*"},
+ "*", "Backslash", nsIDOMKeyEvent.DOM_VK_ASTERISK, "*", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_5,
+ modifiers:{shiftKey:1}, chars:"\u00B5"},
+ "\u00B5", "Backslash", nsIDOMKeyEvent.DOM_VK_ASTERISK, "\u00B5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_102,
+ modifiers:{}, chars:"<"},
+ "<", "IntlBackslash", nsIDOMKeyEvent.DOM_VK_LESS_THAN, "<", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_102,
+ modifiers:{shiftKey:1}, chars:">"},
+ ">", "IntlBackslash", nsIDOMKeyEvent.DOM_VK_LESS_THAN, ">", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_COMMA,
+ modifiers:{}, chars:","},
+ ",", "KeyM", nsIDOMKeyEvent.DOM_VK_COMMA, ",", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_COMMA,
+ modifiers:{shiftKey:1}, chars:"?"},
+ "?", "KeyM", nsIDOMKeyEvent.DOM_VK_COMMA, "?", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_PERIOD,
+ modifiers:{}, chars:";"},
+ ";", "Comma", nsIDOMKeyEvent.DOM_VK_SEMICOLON, ";", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_PERIOD,
+ modifiers:{shiftKey:1}, chars:"."},
+ ".", "Comma", nsIDOMKeyEvent.DOM_VK_SEMICOLON, ".", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_2,
+ modifiers:{}, chars:":"},
+ ":", "Period", nsIDOMKeyEvent.DOM_VK_COLON, ":", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_2,
+ modifiers:{shiftKey:1}, chars:"/"},
+ "/", "Period", nsIDOMKeyEvent.DOM_VK_COLON, "/", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_8,
+ modifiers:{}, chars:"!"},
+ "!", "Slash", nsIDOMKeyEvent.DOM_VK_EXCLAMATION, "!", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_8,
+ modifiers:{shiftKey:1}, chars:"\u00A7"},
+ "\u00A7", "Slash", nsIDOMKeyEvent.DOM_VK_EXCLAMATION, "\u00A7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // OEM keys with ShiftLock
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_7,
+ modifiers:{capsLockKey:1}, chars:"\u00B2"},
+ "\u00B2", "Backquote", 0, "\u00B2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_7,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:""},
+ "", "Backquote", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_4,
+ modifiers:{capsLockKey:1}, chars:"\u00B0"},
+ "\u00B0", "Minus", nsIDOMKeyEvent.DOM_VK_CLOSE_PAREN, "\u00B0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_4,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:")"},
+ ")", "Minus", nsIDOMKeyEvent.DOM_VK_CLOSE_PAREN, ")", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_PLUS,
+ modifiers:{capsLockKey:1}, chars:"+"},
+ "+", "Equal", nsIDOMKeyEvent.DOM_VK_EQUALS, "+", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_PLUS,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:"="},
+ "=", "Equal", nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ //yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_6,
+ // modifiers:{capsLockKey:1}, chars:""},
+ // "Dead", "BracketLeft", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD); // Dead-key
+ //yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_6,
+ // modifiers:{capsLockKey:1, shiftKey:1}, chars:""},
+ // ["\u00A8\u00A8", "\u00A8", "\u00A8", "\u00A8"], "BracketLeft", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD); // Dead-key
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_1,
+ modifiers:{capsLockKey:1}, chars:"\u00A3"},
+ "\u00A3", "BracketRight", nsIDOMKeyEvent.DOM_VK_DOLLAR, "\u00A3", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_1,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:"$"},
+ "$", "BracketRight", nsIDOMKeyEvent.DOM_VK_DOLLAR, "$", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_3,
+ modifiers:{capsLockKey:1}, chars:"%"},
+ "%", "Quote", nsIDOMKeyEvent.DOM_VK_PERCENT, "%", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_3,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:"\u00F9"},
+ "\u00F9", "Quote", nsIDOMKeyEvent.DOM_VK_PERCENT, "\u00F9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_5,
+ modifiers:{capsLockKey:1}, chars:"\u00B5"},
+ "\u00B5", "Backslash", nsIDOMKeyEvent.DOM_VK_ASTERISK, "\u00B5", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_5,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:"*"},
+ "*", "Backslash", nsIDOMKeyEvent.DOM_VK_ASTERISK, "*", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_102,
+ modifiers:{capsLockKey:1}, chars:"<"},
+ "<", "IntlBackslash", nsIDOMKeyEvent.DOM_VK_LESS_THAN, "<", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_102,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:">"},
+ ">", "IntlBackslash", nsIDOMKeyEvent.DOM_VK_LESS_THAN, ">", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_COMMA,
+ modifiers:{capsLockKey:1}, chars:"?"},
+ "?", "KeyM", nsIDOMKeyEvent.DOM_VK_COMMA, "?", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_COMMA,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:","},
+ ",", "KeyM", nsIDOMKeyEvent.DOM_VK_COMMA, ",", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_PERIOD,
+ modifiers:{capsLockKey:1}, chars:"."},
+ ".", "Comma", nsIDOMKeyEvent.DOM_VK_SEMICOLON, ".", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_PERIOD,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:";"},
+ ";", "Comma", nsIDOMKeyEvent.DOM_VK_SEMICOLON, ";", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_2,
+ modifiers:{capsLockKey:1}, chars:"/"},
+ "/", "Period", nsIDOMKeyEvent.DOM_VK_COLON, "/", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_2,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:":"},
+ ":", "Period", nsIDOMKeyEvent.DOM_VK_COLON, ":", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_8,
+ modifiers:{capsLockKey:1}, chars:"\u00A7"},
+ "\u00A7", "Slash", nsIDOMKeyEvent.DOM_VK_EXCLAMATION, "\u00A7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_8,
+ modifiers:{capsLockKey:1, shiftKey:1}, chars:"!"},
+ "!", "Slash", nsIDOMKeyEvent.DOM_VK_EXCLAMATION, "!", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // AltGr
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_0,
+ modifiers:{altGrKey:1}, chars:"@"},
+ "@", "Digit0", nsIDOMKeyEvent.DOM_VK_0, "@", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_1,
+ modifiers:{altGrKey:1}, chars:""},
+ "&", "Digit1", nsIDOMKeyEvent.DOM_VK_1, "&", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ //yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_2,
+ // modifiers:{altGrKey:1}, chars:""},
+ // "Dead", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD); // Dead-key
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_3,
+ modifiers:{altGrKey:1}, chars:"#"},
+ "#", "Digit3", nsIDOMKeyEvent.DOM_VK_3, "#", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_4,
+ modifiers:{altGrKey:1}, chars:"{"},
+ "{", "Digit4", nsIDOMKeyEvent.DOM_VK_4, "{", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_5,
+ modifiers:{altGrKey:1}, chars:"["},
+ "[", "Digit5", nsIDOMKeyEvent.DOM_VK_5, "[", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_6,
+ modifiers:{altGrKey:1}, chars:"|"},
+ "|", "Digit6", nsIDOMKeyEvent.DOM_VK_6, "|", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ //yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_7,
+ // modifiers:{altGrKey:1}, chars:""},
+ // "Dead", "Digit7", nsIDOMKeyEvent.DOM_VK_7, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD); // Dead-key
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_8,
+ modifiers:{altGrKey:1}, chars:"\\"},
+ "\\", "Digit8", nsIDOMKeyEvent.DOM_VK_8, "\\", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_9,
+ modifiers:{altGrKey:1}, chars:"^"},
+ "^", "Digit9", nsIDOMKeyEvent.DOM_VK_9, "^", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_4,
+ modifiers:{altGrKey:1}, chars:"]"},
+ "]", "Minus", nsIDOMKeyEvent.DOM_VK_CLOSE_PAREN, "]", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_PLUS,
+ modifiers:{altGrKey:1}, chars:"}"},
+ "}", "Equal", nsIDOMKeyEvent.DOM_VK_EQUALS, "}", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // German
+ yield testKey({layout:KEYBOARD_LAYOUT_GERMAN, keyCode:WIN_VK_OEM_2,
+ modifiers:{}, chars:"#"},
+ "#", "Backslash", nsIDOMKeyEvent.DOM_VK_HASH, "#", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_GERMAN, keyCode:WIN_VK_OEM_2,
+ modifiers:{shiftKey:1}, chars:"'"},
+ "'", "Backslash", nsIDOMKeyEvent.DOM_VK_HASH, "'", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Khmer
+ if (OS_VERSION >= WIN7) {
+ yield testKey({layout:KEYBOARD_LAYOUT_KHMER, keyCode:WIN_VK_2,
+ modifiers:{}, chars:"\u17E2"},
+ "\u17E2", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "\u17E2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_KHMER, keyCode:WIN_VK_2,
+ modifiers:{shiftKey:1}, chars:"\u17D7"},
+ "\u17D7", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "\u17D7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_KHMER, keyCode:WIN_VK_2, // Ctrl+2 should cause inputting Euro sign.
+ modifiers:{ctrlKey:1}, chars:"\u20AC", isInputtingCharacters:true},
+ "\u20AC", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "\u20AC", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_KHMER, keyCode:WIN_VK_2, // Ctrl+Shift+2 shouldn't cause any input.
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "\u17D7", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "\u17D7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_KHMER, keyCode:WIN_VK_2,
+ modifiers:{altKey:1}, chars:"\u17E2"},
+ "\u17E2", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "\u17E2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_KHMER, keyCode:WIN_VK_2,
+ modifiers:{altKey:1, shiftKey:1}, chars:"\u17D7"},
+ "\u17D7", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "\u17D7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_KHMER, keyCode:WIN_VK_2,
+ modifiers:{altGrKey:1}, chars:"2"},
+ "2", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_KHMER, keyCode:WIN_VK_2,
+ modifiers:{altGrKey:1, shiftKey:1}, chars:"\u19E2"},
+ "\u19E2", "Digit2", nsIDOMKeyEvent.DOM_VK_2, "\u19E2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ }
+
+ // Norwegian
+ yield testKey({layout:KEYBOARD_LAYOUT_NORWEGIAN, keyCode:WIN_VK_OEM_5,
+ modifiers:{}, chars:"|"},
+ "|", "Backquote", nsIDOMKeyEvent.DOM_VK_PIPE, "|", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_NORWEGIAN, keyCode:WIN_VK_OEM_5,
+ modifiers:{shiftKey:1}, chars:"\u00A7"},
+ "\u00A7", "Backquote", nsIDOMKeyEvent.DOM_VK_PIPE, "\u00A7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ // Brazilian ABNT
+ yield testKey({layout:KEYBOARD_LAYOUT_BRAZILIAN_ABNT, keyCode:WIN_VK_ABNT_C1,
+ modifiers:{}, chars:"/"},
+ "/", "IntlBackslash", nsIDOMKeyEvent.DOM_VK_SLASH, "/", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_BRAZILIAN_ABNT, keyCode:WIN_VK_ABNT_C1,
+ modifiers:{shiftKey:1}, chars:"?"},
+ "?", "IntlBackslash", nsIDOMKeyEvent.DOM_VK_SLASH, "?", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_BRAZILIAN_ABNT, keyCode:WIN_VK_ABNT_C2,
+ modifiers:{numLockKey:1}, chars:"."},
+ ".", "NumpadComma", nsIDOMKeyEvent.DOM_VK_SEPARATOR, ".", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_BRAZILIAN_ABNT, keyCode:WIN_VK_DECIMAL,
+ modifiers:{numLockKey:1}, chars:","},
+ ",", "NumpadDecimal", nsIDOMKeyEvent.DOM_VK_DECIMAL, ",", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+
+ // Mac JIS keyboard
+ // The separator key on JIS keyboard for Mac doesn't cause any character even with Japanese keyboard layout.
+ yield testKey({layout:KEYBOARD_LAYOUT_JAPANESE, keyCode:WIN_VK_ABNT_C2,
+ modifiers:{numLockKey:1}, chars:""},
+ "", "NumpadComma", nsIDOMKeyEvent.DOM_VK_SEPARATOR, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+ yield testKey({layout:KEYBOARD_LAYOUT_JAPANESE, keyCode:WIN_VK_DECIMAL,
+ modifiers:{numLockKey:1}, chars:"."},
+ ".", "NumpadDecimal", nsIDOMKeyEvent.DOM_VK_DECIMAL, ".", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_NUMPAD);
+
+ // Dead keys on any layouts
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_6,
+ modifiers:{}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_6,
+ modifiers:{}, chars:"^^"},
+ ["^^", "^", "^", "^"], "BracketLeft", nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "^^", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_6,
+ modifiers:{}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_A,
+ modifiers:{}, chars:"\u00E2"},
+ ["\u00E2", "\u00E2", "a"], "KeyQ", nsIDOMKeyEvent.DOM_VK_A, "\u00E2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_6,
+ modifiers:{}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_A,
+ modifiers:{shiftKey:1}, chars:"\u00C2"},
+ ["\u00C2", "\u00C2", "A"], "KeyQ", nsIDOMKeyEvent.DOM_VK_A, "\u00C2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_6,
+ modifiers:{}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_Q,
+ modifiers:{}, chars:"^q"},
+ ["^q", "^", "q", "q"], "KeyA", nsIDOMKeyEvent.DOM_VK_Q, "^q", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_6,
+ modifiers:{shiftKey:1}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_6,
+ modifiers:{shiftKey:1}, chars:"\u00A8\u00A8"},
+ ["\u00A8\u00A8", "\u00A8", "\u00A8", "\u00A8"], "BracketLeft", nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "\u00A8\u00A8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_6,
+ modifiers:{shiftKey:1}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_A,
+ modifiers:{shiftKey:1}, chars:"\u00C4"},
+ ["\u00C4", "\u00C4", "A"], "KeyQ", nsIDOMKeyEvent.DOM_VK_A, "\u00C4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_6,
+ modifiers:{shiftKey:1}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_A,
+ modifiers:{}, chars:"\u00E4"},
+ ["\u00E4", "\u00E4", "a"], "KeyQ", nsIDOMKeyEvent.DOM_VK_A, "\u00E4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_6,
+ modifiers:{shiftKey:1}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_Q,
+ modifiers:{shiftKey:1}, chars:"\u00A8Q"},
+ ["\u00A8Q", "\u00A8", "Q", "Q"], "KeyA", nsIDOMKeyEvent.DOM_VK_Q, "\u00A8Q", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_1,
+ modifiers:{}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_1,
+ modifiers:{}, chars:"``"},
+ ["``", "`", "`", "`"], "BracketLeft", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "``", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_1,
+ modifiers:{}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_A,
+ modifiers:{}, chars:"\u00E0"},
+ ["\u00E0", "\u00E0", "a"], "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u00E0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_1,
+ modifiers:{}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_A,
+ modifiers:{shiftKey:1}, chars:"\u00C0"},
+ ["\u00C0", "\u00C0", "A"], "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u00C0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_1,
+ modifiers:{}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_Q,
+ modifiers:{}, chars:"`q"},
+ ["`q", "`", "q", "q"], "KeyQ", nsIDOMKeyEvent.DOM_VK_Q, "`q", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_1,
+ modifiers:{shiftKey:1}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_1,
+ modifiers:{shiftKey:1}, chars:"^^"},
+ ["^^", "^", "^", "^"], "BracketLeft", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "^^", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_1,
+ modifiers:{shiftKey:1}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_A,
+ modifiers:{shiftKey:1}, chars:"\u00C2"},
+ ["\u00C2", "\u00C2", "A"], "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u00C2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_1,
+ modifiers:{shiftKey:1}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_A,
+ modifiers:{}, chars:"\u00E2"},
+ ["\u00E2", "\u00E2", "a"], "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u00E2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_1,
+ modifiers:{shiftKey:1}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_Q,
+ modifiers:{shiftKey:1}, chars:"^Q"},
+ ["^Q", "^", "Q", "Q"], "KeyQ", nsIDOMKeyEvent.DOM_VK_Q, "^Q", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_7,
+ modifiers:{}, chars:""},
+ "Dead", "Quote", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_7,
+ modifiers:{}, chars:"\u00B4\u00B4"},
+ ["\u00B4\u00B4", "\u00B4", "\u00B4", "\u00B4"], "Quote", 0, "\u00B4\u00B4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_7,
+ modifiers:{}, chars:""},
+ "Dead", "Quote", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_A,
+ modifiers:{}, chars:"\u00E1"},
+ ["\u00E1", "\u00E1", "a"], "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u00E1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_7,
+ modifiers:{}, chars:""},
+ "Dead", "Quote", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_A,
+ modifiers:{shiftKey:1}, chars:"\u00C1"},
+ ["\u00C1", "\u00C1", "A"], "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u00C1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_7,
+ modifiers:{}, chars:""},
+ "Dead", "Quote", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_Q,
+ modifiers:{}, chars:"\u00B4q"},
+ ["\u00B4q", "\u00B4", "q", "q"], "KeyQ", nsIDOMKeyEvent.DOM_VK_Q, "\u00B4q", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_7,
+ modifiers:{shiftKey:1}, chars:""},
+ "Dead", "Quote", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_7,
+ modifiers:{shiftKey:1}, chars:"\u00A8\u00A8"},
+ ["\u00A8\u00A8", "\u00A8", "\u00A8", "\u00A8"], "Quote", 0, "\u00A8\u00A8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_7,
+ modifiers:{shiftKey:1}, chars:""},
+ "Dead", "Quote", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_A,
+ modifiers:{shiftKey:1}, chars:"\u00C4"},
+ ["\u00C4", "\u00C4", "A"], "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u00C4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_7,
+ modifiers:{shiftKey:1}, chars:""},
+ "Dead", "Quote", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_A,
+ modifiers:{}, chars:"\u00E4"},
+ ["\u00E4", "\u00E4", "a"], "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u00E4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_7,
+ modifiers:{shiftKey:1}, chars:""},
+ "Dead", "Quote", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_Q,
+ modifiers:{shiftKey:1}, chars:"\u00A8Q"},
+ ["\u00A8Q", "\u00A8", "Q", "Q"], "KeyQ", nsIDOMKeyEvent.DOM_VK_Q, "\u00A8Q", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ if (OS_VERSION >= WIN8) {
+ // On Russian Mnemonic layout, both 'KeyS' and 'KeyC' are dead key. However, the sequence 'KeyS' -> 'KeyC' causes a composite character.
+ yield testKey({layout:KEYBOARD_LAYOUT_RUSSIAN_MNEMONIC, keyCode:WIN_VK_S,
+ modifiers:{}, chars:""},
+ "Dead", "KeyS", nsIDOMKeyEvent.DOM_VK_S, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_RUSSIAN_MNEMONIC, keyCode:WIN_VK_S,
+ modifiers:{}, chars:"\u0441\u0441"},
+ ["\u0441\u0441", "\u0441", "\u0441", "\u0441"], "KeyS", nsIDOMKeyEvent.DOM_VK_S, "\u0441\u0441", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_RUSSIAN_MNEMONIC, keyCode:WIN_VK_C,
+ modifiers:{}, chars:""},
+ "Dead", "KeyC", nsIDOMKeyEvent.DOM_VK_C, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_RUSSIAN_MNEMONIC, keyCode:WIN_VK_C,
+ modifiers:{}, chars:"\u0446\u0446"},
+ ["\u0446\u0446", "\u0446", "\u0446", "\u0446"], "KeyC", nsIDOMKeyEvent.DOM_VK_C, "\u0446\u0446", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_RUSSIAN_MNEMONIC, keyCode:WIN_VK_S,
+ modifiers:{}, chars:""},
+ "Dead", "KeyS", nsIDOMKeyEvent.DOM_VK_S, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_RUSSIAN_MNEMONIC, keyCode:WIN_VK_C,
+ modifiers:{}, chars:"\u0449"},
+ ["\u0449", "\u0449", "\u0446"], "KeyC", nsIDOMKeyEvent.DOM_VK_C, "\u0449", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ }
+
+ // When Alt key is pressed, dead key sequence is generated with WM_SYSKEYDOWN, WM_SYSDEADCHAR, WM_SYSCHAR and WM_SYSKEYUP.
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_1,
+ modifiers:{altKey:1}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_1,
+ modifiers:{altKey:1}, chars:"``"},
+ ["``", "`", "`", "`"], "BracketLeft", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "``", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_1,
+ modifiers:{altKey:1}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_A,
+ modifiers:{altKey:1}, chars:"\u00E0"},
+ ["\u00E0", "\u00E0", "a"], "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u00E0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_1,
+ modifiers:{altKey:1}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_A,
+ modifiers:{altKey:1, shiftKey:1}, chars:"\u00C0"},
+ ["\u00C0", "\u00C0", "A"], "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u00C0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_1,
+ modifiers:{altKey:1}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_Q,
+ modifiers:{altKey:1}, chars:"`q"},
+ ["`q", "`", "q", "q"], "KeyQ", nsIDOMKeyEvent.DOM_VK_Q, "`q", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_1,
+ modifiers:{altKey:1, shiftKey:1}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_1,
+ modifiers:{altKey:1, shiftKey:1}, chars:"^^"},
+ ["^^", "^", "^", "^"], "BracketLeft", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "^^", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_1,
+ modifiers:{altKey:1, shiftKey:1}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_A,
+ modifiers:{altKey:1, shiftKey:1}, chars:"\u00C2"},
+ ["\u00C2", "\u00C2", "A"], "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u00C2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_1,
+ modifiers:{altKey:1, shiftKey:1}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_A,
+ modifiers:{altKey:1}, chars:"\u00E2"},
+ ["\u00E2", "\u00E2", "a"], "KeyA", nsIDOMKeyEvent.DOM_VK_A, "\u00E2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_1,
+ modifiers:{altKey:1, shiftKey:1}, chars:""},
+ "Dead", "BracketLeft", nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+ yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_Q,
+ modifiers:{altKey:1, shiftKey:1}, chars:"^Q"},
+ ["^Q", "^", "Q", "Q"], "KeyQ", nsIDOMKeyEvent.DOM_VK_Q, "^Q", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+
+ cleanup();
+ }
+
+
+ if (IS_WIN) {
+ yield* testKeysOnWindows();
+ } else if (IS_MAC) {
+ yield* testKeysOnMac();
+ } else {
+ cleanup();
+ }
+}
+
+// Test the activation (or not) of an HTML accesskey
+function* runAccessKeyTests()
+{
+ var button = document.getElementById("button");
+ var activationCount;
+
+ function onClick(e)
+ {
+ ++activationCount;
+ }
+
+ // The first parameter is the complete input event. The second and third parameters are
+ // what to test against.
+ function testKey(aEvent, aAccessKey, aShouldActivate)
+ {
+ activationCount = 0;
+ button.setAttribute("accesskey", aAccessKey);
+
+ return synthesizeKey(aEvent, "button", function() {
+
+ var name = eventToString(aEvent);
+
+ is(activationCount, aShouldActivate ? 1 : 0, name + ", activating '" + aAccessKey + "'");
+
+ continueTest();
+ });
+ }
+
+ button.addEventListener("click", onClick, false);
+
+ // These tests have to be per-plaform.
+ if (IS_MAC) {
+ // Basic sanity checks
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers:{}, chars:"a", unmodifiedChars:"a"},
+ "a", false);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers:{ctrlKey:1}, chars:"\u0001", unmodifiedChars:"a"},
+ "a", false);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers:{ctrlKey:1}, chars:"\u0001", unmodifiedChars:"a"},
+ "A", false);
+
+ // Shift-ctrl does not activate accesskeys
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0001", unmodifiedChars:"A"},
+ "a", false);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0001", unmodifiedChars:"A"},
+ "A", false);
+ // Alt-ctrl activate accesskeys
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers:{ctrlKey:1, altKey:1}, chars:"\u0001", unmodifiedChars:"a"},
+ "a", true);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_A,
+ modifiers:{ctrlKey:1, altKey:1}, chars:"\u0001", unmodifiedChars:"a"},
+ "A", true);
+
+ // Greek layout can activate a Latin accesskey
+ yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:MAC_VK_ANSI_A,
+ modifiers:{ctrlKey:1, altKey:1}, chars:"\u0001", unmodifiedChars:"\u03b1"},
+ "a", true);
+ yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:MAC_VK_ANSI_A,
+ modifiers:{ctrlKey:1, altKey:1}, chars:"\u0001", unmodifiedChars:"\u03b1"},
+ "A", true);
+ // ... and a Greek accesskey!
+ yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:MAC_VK_ANSI_A,
+ modifiers:{ctrlKey:1, altKey:1}, chars:"\u0001", unmodifiedChars:"\u03b1"},
+ "\u03b1", true);
+ yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:MAC_VK_ANSI_A,
+ modifiers:{ctrlKey:1, altKey:1}, chars:"\u0001", unmodifiedChars:"\u03b1"},
+ "\u0391", true);
+
+ // bug 359638
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Period,
+ modifiers:{ctrlKey:1, altKey:1}, chars:".", unmodifiedChars:"."},
+ ".", true);
+
+ // German (KCHR/KeyTranslate case)
+ yield testKey({layout:KEYBOARD_LAYOUT_GERMAN, keyCode:MAC_VK_ANSI_A,
+ modifiers:{ctrlKey:1, altKey:1}, chars:"a", unmodifiedChars:"a"},
+ "a", true);
+ yield testKey({layout:KEYBOARD_LAYOUT_GERMAN, keyCode:MAC_VK_ANSI_A,
+ modifiers:{ctrlKey:1, altKey:1}, chars:"a", unmodifiedChars:"a"},
+ "A", true);
+ yield testKey({layout:KEYBOARD_LAYOUT_GERMAN, keyCode:MAC_VK_ANSI_LeftBracket,
+ modifiers:{ctrlKey:1, altKey:1}, chars:"\u00fc", unmodifiedChars:"\u00fc"},
+ "\u00fc", true);
+ yield testKey({layout:KEYBOARD_LAYOUT_GERMAN, keyCode:MAC_VK_ANSI_LeftBracket,
+ modifiers:{ctrlKey:1, altKey:1}, chars:"\u00fc", unmodifiedChars:"\u00fc"},
+ "\u00dc", true);
+ }
+ else if (IS_WIN) {
+ // Basic sanity checks
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_A,
+ modifiers:{}, chars:"a"},
+ "a", false);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_A,
+ modifiers:{shiftKey:1, altKey:1}, chars:"A"},
+ "a", true);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_A,
+ modifiers:{shiftKey:1, altKey:1}, chars:"A"},
+ "A", true);
+
+ // shift-alt-ctrl does not activate accesskeys
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_A,
+ modifiers:{ctrlKey:1, shiftKey:1, altKey:1}, chars:""},
+ "a", false);
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_A,
+ modifiers:{ctrlKey:1, shiftKey:1, altKey:1}, chars:""},
+ "A", false);
+
+ // Greek layout can activate a Latin accesskey
+ yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:WIN_VK_A,
+ modifiers:{shiftKey:1, altKey:1}, chars:"\u0391"},
+ "a", true);
+ yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:WIN_VK_A,
+ modifiers:{shiftKey:1, altKey:1}, chars:"\u0391"},
+ "A", true);
+ // ... and a Greek accesskey!
+ yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:WIN_VK_A,
+ modifiers:{shiftKey:1, altKey:1}, chars:"\u0391"},
+ "\u03b1", true);
+ yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:WIN_VK_A,
+ modifiers:{shiftKey:1, altKey:1}, chars:"\u0391"},
+ "\u0391", true);
+
+ // bug 359638
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_PERIOD,
+ modifiers:{shiftKey:1, altKey:1}, chars:".", unmodifiedChars:"."},
+ ".", true);
+ }
+
+ button.removeEventListener("click", onClick, false);
+}
+
+function* runXULKeyTests()
+{
+ var commandElements = {
+ expectedCommand: document.getElementById("expectedCommand"),
+ unexpectedCommand: document.getElementById("unexpectedCommand"),
+ expectedReservedCommand: document.getElementById("expectedReservedCommand")
+ };
+ // Enable all command elements.
+ for (var id in commandElements) {
+ commandElements[id].removeAttribute("disabled");
+ }
+
+ var keyEvents = [];
+
+ function onKeyInDefaultEventGroup(aDOMEvent)
+ {
+ if (isModifierKeyEvent(aDOMEvent)) {
+ return;
+ }
+ keyEvents.push({ type: aDOMEvent.type, group: "default", phase: getPhase(aDOMEvent), currentTarget: eventTargetToString(aDOMEvent.currentTarget) });
+ }
+
+ function onKeyInSystemEventGroup(aDOMEvent)
+ {
+ if (isModifierKeyEvent(aDOMEvent)) {
+ return;
+ }
+ keyEvents.push({ type: aDOMEvent.type, group: "system", phase: getPhase(aDOMEvent), currentTarget: eventTargetToString(aDOMEvent.currentTarget) });
+ }
+
+ var buttonParent = document.getElementById("button").parentNode;
+ buttonParent.addEventListener("keydown", onKeyInDefaultEventGroup, true);
+ buttonParent.addEventListener("keypress", onKeyInDefaultEventGroup, true);
+ buttonParent.addEventListener("keydown", onKeyInDefaultEventGroup, false);
+ buttonParent.addEventListener("keypress", onKeyInDefaultEventGroup, false);
+ SpecialPowers.addSystemEventListener(buttonParent, "keydown", onKeyInSystemEventGroup, true);
+ SpecialPowers.addSystemEventListener(buttonParent, "keypress", onKeyInSystemEventGroup, true);
+ SpecialPowers.addSystemEventListener(buttonParent, "keydown", onKeyInSystemEventGroup, false);
+ SpecialPowers.addSystemEventListener(buttonParent, "keypress", onKeyInSystemEventGroup, false);
+
+ function finializeKeyElementTest()
+ {
+ buttonParent.removeEventListener("keydown", onKeyInDefaultEventGroup, true);
+ buttonParent.removeEventListener("keypress", onKeyInDefaultEventGroup, true);
+ buttonParent.removeEventListener("keydown", onKeyInDefaultEventGroup, false);
+ buttonParent.removeEventListener("keypress", onKeyInDefaultEventGroup, false);
+ SpecialPowers.removeSystemEventListener(buttonParent, "keydown", onKeyInSystemEventGroup, true);
+ SpecialPowers.removeSystemEventListener(buttonParent, "keypress", onKeyInSystemEventGroup, true);
+ SpecialPowers.removeSystemEventListener(buttonParent, "keydown", onKeyInSystemEventGroup, false);
+ SpecialPowers.removeSystemEventListener(buttonParent, "keypress", onKeyInSystemEventGroup, false);
+ }
+
+ // If aKeyElement is empty string, this function tests if the event kicks
+ // no key elements.
+ function testKeyElement(aEvent, aKeyElementId)
+ {
+ var testName = "testKeyElement (with non-reserved command element): " + eventToString(aEvent) + " ";
+ var keyElement = aKeyElementId == "" ? null : document.getElementById(aKeyElementId);
+ if (keyElement) {
+ keyElement.setAttribute("command", "expectedCommand");
+ }
+
+ for (var id in commandElements) {
+ commandElements[id].activeCount = 0;
+ }
+
+ keyEvents = [];
+ return synthesizeKey(aEvent, "button", function() {
+ if (keyElement) {
+ is(commandElements["expectedCommand"].activeCount, 1, testName + "the command element (id='expectedCommand') should be preformed");
+ } else {
+ is(commandElements["expectedCommand"].activeCount, 0, testName + "the command element (id='expectedCommand') shouldn't be preformed");
+ }
+ is(commandElements["unexpectedCommand"].activeCount, 0, testName + "the command element (id='unexpectedCommand') shouldn't be preformed");
+ is(commandElements["expectedReservedCommand"].activeCount, 0, testName + "the command element (id='expectedReservedCommand') shouldn't be preformed");
+
+ function checkFiredEvents()
+ {
+ is(keyEvents.length, 8, testName + "wrong number events fired");
+ is(JSON.stringify(keyEvents[0]), JSON.stringify({ type: "keydown", group: "default", phase: "capture", currentTarget: eventTargetToString(buttonParent) }), testName + "keydown event should be fired on chrome in the default event group #0");
+ is(JSON.stringify(keyEvents[1]), JSON.stringify({ type: "keydown", group: "default", phase: "bubble", currentTarget: eventTargetToString(buttonParent) }), testName + "keydown event should be fired on chrome in the default event group #1");
+
+ is(JSON.stringify(keyEvents[2]), JSON.stringify({ type: "keydown", group: "system", phase: "capture", currentTarget: eventTargetToString(buttonParent) }), testName + "keydown event should be fired on chrome in the system event group #2");
+ is(JSON.stringify(keyEvents[3]), JSON.stringify({ type: "keydown", group: "system", phase: "bubble", currentTarget: eventTargetToString(buttonParent) }), testName + "keydown event should be fired on chrome in the system event group #3");
+
+ is(JSON.stringify(keyEvents[4]), JSON.stringify({ type: "keypress", group: "default", phase: "capture", currentTarget: eventTargetToString(buttonParent) }), testName + "keypress event should be fired on chrome in the default event group #4");
+ is(JSON.stringify(keyEvents[5]), JSON.stringify({ type: "keypress", group: "default", phase: "bubble", currentTarget: eventTargetToString(buttonParent) }), testName + "keypress event should be fired on chrome in the default event group #5");
+
+ is(JSON.stringify(keyEvents[6]), JSON.stringify({ type: "keypress", group: "system", phase: "capture", currentTarget: eventTargetToString(buttonParent) }), testName + "keypress event should be fired on chrome in the system event group #6");
+ is(JSON.stringify(keyEvents[7]), JSON.stringify({ type: "keypress", group: "system", phase: "bubble", currentTarget: eventTargetToString(buttonParent) }), testName + "keypress event should be fired on chrome in the system event group #7");
+ }
+
+ checkFiredEvents();
+
+ if (keyElement) {
+ testName = "testKeyElement (with reserved command element): " + eventToString(aEvent) + " ";
+ keyElement.setAttribute("command", "expectedReservedCommand");
+
+ for (id in commandElements) {
+ commandElements[id].activeCount = 0;
+ }
+ keyEvents = [];
+ synthesizeKey(aEvent, "button", function() {
+ is(commandElements["expectedCommand"].activeCount, 0, testName + "the command element (id='expectedCommand') shouldn't be preformed");
+ is(commandElements["unexpectedCommand"].activeCount, 0, testName + "the command element (id='unexpectedCommand') shouldn't be preformed");
+ is(commandElements["expectedReservedCommand"].activeCount, 1, testName + "the command element (id='expectedReservedCommand') should be preformed");
+
+ checkFiredEvents();
+
+ if (keyElement) {
+ keyElement.setAttribute("command", "unexpectedCommand");
+ }
+ continueTest();
+ });
+ } else {
+ if (keyElement) {
+ keyElement.setAttribute("command", "unexpectedCommand");
+ }
+ continueTest();
+ }
+ });
+ }
+
+ if (IS_MAC) {
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Semicolon,
+ modifiers:{metaKey:1}, chars:";", unmodifiedChars:";"},
+ "unshiftedKey");
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Semicolon,
+ modifiers:{metaKey:1, shiftKey:1}, chars:";", unmodifiedChars:":"},
+ "shiftedKey");
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Quote,
+ modifiers:{metaKey:1}, chars:"'", unmodifiedChars:"'"},
+ "reservedUnshiftedKey");
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_Quote,
+ modifiers:{metaKey:1, shiftKey:1}, chars:"\"", unmodifiedChars:"'"},
+ "reservedShiftedKey");
+ }
+ else if (IS_WIN) {
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_1,
+ modifiers:{ctrlKey:1}, chars:""},
+ "unshiftedKey");
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_1,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "shiftedKey");
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_7,
+ modifiers:{ctrlKey:1}, chars:""},
+ "reservedUnshiftedKey");
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_7,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "reservedShiftedKey");
+ }
+
+ // 429160
+ if (IS_MAC) {
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_F,
+ modifiers:{metaKey:1, altKey:1}, chars:"\u0192", unmodifiedChars:"f"},
+ "commandOptionF");
+ }
+ else if (IS_WIN) {
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_F,
+ modifiers:{ctrlKey:1, altKey:1}, chars:"\u0006"},
+ "commandOptionF");
+ }
+
+ // 432112
+ if (IS_MAC) {
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_SWEDISH, keyCode:MAC_VK_ANSI_Minus,
+ modifiers:{metaKey:1, shiftKey:1}, chars:"+", unmodifiedChars:"?"},
+ "question");
+ }
+ else if (IS_WIN) {
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_SWEDISH, keyCode:WIN_VK_OEM_PLUS,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "question");
+ // For testing if Ctrl+? is kicked without Shift state, temporarily disable
+ // Ctrl-+ key element.
+ var unshiftedPlusKeyElement = document.getElementById("unshiftedPlus");
+ unshiftedPlusKeyElement.setAttribute("disabled", "true");
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_SWEDISH, keyCode:WIN_VK_OEM_PLUS,
+ modifiers:{ctrlKey:1}, chars:""},
+ "");
+ unshiftedPlusKeyElement.removeAttribute("disabled");
+ }
+
+ // bug 433192
+ if (IS_WIN) {
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_X,
+ modifiers:{ctrlKey:1}, chars:"\u0018"},
+ "unshiftedX");
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_X,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0018"},
+ "shiftedX");
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_ARABIC, keyCode:WIN_VK_X,
+ modifiers:{ctrlKey:1}, chars:"\u0018"},
+ "unshiftedX");
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_ARABIC, keyCode:WIN_VK_X,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0018"},
+ "shiftedX");
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_HEBREW, keyCode:WIN_VK_X,
+ modifiers:{ctrlKey:1}, chars:"\u0018"},
+ "unshiftedX");
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_HEBREW, keyCode:WIN_VK_X,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:"\u0018"},
+ "shiftedX");
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_JAPANESE, keyCode:WIN_VK_OEM_PLUS,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "unshiftedPlus");
+ }
+
+ // bug 759346
+ if (IS_WIN) {
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_THAI, keyCode:WIN_VK_1,
+ modifiers:{ctrlKey:1}, chars:""},
+ "");
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_THAI, keyCode:WIN_VK_1,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "unshiftedPlus");
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_THAI, keyCode:WIN_VK_OEM_PLUS,
+ modifiers:{ctrlKey:1}, chars:""},
+ "unshiftedPlus");
+ yield testKeyElement({layout:KEYBOARD_LAYOUT_THAI, keyCode:WIN_VK_OEM_PLUS,
+ modifiers:{ctrlKey:1, shiftKey:1}, chars:""},
+ "");
+ }
+
+ for (id in commandElements) {
+ commandElements[id].setAttribute("disabled", "true");
+ }
+ finializeKeyElementTest();
+}
+
+function* runReservedKeyTests()
+{
+ var browser = document.getElementById("browser");
+ var contents = [
+ browser.contentWindow,
+ browser.contentDocument,
+ browser.contentDocument.documentElement,
+ browser.contentDocument.body,
+ browser.contentDocument.getElementById("content_button")
+ ];
+
+ for (var i = 0; i < contents.length; i++) {
+ contents[i].addEventListener("keydown", onKeyInDefaultEventGroup, true);
+ contents[i].addEventListener("keypress", onKeyInDefaultEventGroup, true);
+ contents[i].addEventListener("keydown", onKeyInDefaultEventGroup, false);
+ contents[i].addEventListener("keypress", onKeyInDefaultEventGroup, false);
+ SpecialPowers.addSystemEventListener(contents[i], "keydown", onKeyInSystemEventGroup, true);
+ SpecialPowers.addSystemEventListener(contents[i], "keypress", onKeyInSystemEventGroup, true);
+ SpecialPowers.addSystemEventListener(contents[i], "keydown", onKeyInSystemEventGroup, false);
+ SpecialPowers.addSystemEventListener(contents[i], "keypress", onKeyInSystemEventGroup, false);
+ }
+
+ var keyEvents = [];
+
+ function onKeyInDefaultEventGroup(aDOMEvent)
+ {
+ if (isModifierKeyEvent(aDOMEvent)) {
+ return;
+ }
+ keyEvents.push({ type: aDOMEvent.type, group: "default", phase: getPhase(aDOMEvent), currentTarget: eventTargetToString(aDOMEvent.currentTarget) });
+ }
+
+ function onKeyInSystemEventGroup(aDOMEvent)
+ {
+ if (isModifierKeyEvent(aDOMEvent)) {
+ return;
+ }
+ keyEvents.push({ type: aDOMEvent.type, group: "system", phase: getPhase(aDOMEvent), currentTarget: eventTargetToString(aDOMEvent.currentTarget) });
+ // prevents reserved default action
+ if (aDOMEvent.type == "keypress" &&
+ aDOMEvent.eventPhase == aDOMEvent.BUBBLING_PHASE &&
+ aDOMEvent.currentTarget == browser.contentWindow) {
+ aDOMEvent.preventDefault();
+ }
+ }
+
+ function finializeKeyElementTest()
+ {
+ for (var i = 0; i < contents.length; i++) {
+ contents[i].removeEventListener("keydown", onKeyInDefaultEventGroup, true);
+ contents[i].removeEventListener("keypress", onKeyInDefaultEventGroup, true);
+ contents[i].removeEventListener("keydown", onKeyInDefaultEventGroup, false);
+ contents[i].removeEventListener("keypress", onKeyInDefaultEventGroup, false);
+ SpecialPowers.removeSystemEventListener(contents[i], "keydown", onKeyInSystemEventGroup, true);
+ SpecialPowers.removeSystemEventListener(contents[i], "keypress", onKeyInSystemEventGroup, true);
+ SpecialPowers.removeSystemEventListener(contents[i], "keydown", onKeyInSystemEventGroup, false);
+ SpecialPowers.removeSystemEventListener(contents[i], "keypress", onKeyInSystemEventGroup, false);
+ }
+ }
+
+ function testReservedKey(aEvent)
+ {
+ keyEvents = [];
+ return synthesizeKey(aEvent, "content_button", function() {
+ testName = "testReservedKey: " + eventToString(aEvent) + " ";
+ is(keyEvents.length, 20, testName + "wrong number events fired");
+ is(JSON.stringify(keyEvents[0]), JSON.stringify({ type: "keydown", group: "system", phase: "capture", currentTarget: eventTargetToString(contents[0]) }), testName + "keydown event should be fired on content only in the system event group #0");
+ is(JSON.stringify(keyEvents[1]), JSON.stringify({ type: "keydown", group: "system", phase: "capture", currentTarget: eventTargetToString(contents[1]) }), testName + "keydown event should be fired on content only in the system event group #1");
+ is(JSON.stringify(keyEvents[2]), JSON.stringify({ type: "keydown", group: "system", phase: "capture", currentTarget: eventTargetToString(contents[2]) }), testName + "keydown event should be fired on content only in the system event group #2");
+ is(JSON.stringify(keyEvents[3]), JSON.stringify({ type: "keydown", group: "system", phase: "capture", currentTarget: eventTargetToString(contents[3]) }), testName + "keydown event should be fired on content only in the system event group #3");
+
+ is(JSON.stringify(keyEvents[4]), JSON.stringify({ type: "keydown", group: "system", phase: "target", currentTarget: eventTargetToString(contents[4]) }), testName + "keydown event should be fired on content only in the system event group #4");
+ is(JSON.stringify(keyEvents[5]), JSON.stringify({ type: "keydown", group: "system", phase: "target", currentTarget: eventTargetToString(contents[4]) }), testName + "keydown event should be fired on content only in the system event group #5");
+
+ is(JSON.stringify(keyEvents[6]), JSON.stringify({ type: "keydown", group: "system", phase: "bubble", currentTarget: eventTargetToString(contents[3]) }), testName + "keydown event should be fired on content only in the system event group #6");
+ is(JSON.stringify(keyEvents[7]), JSON.stringify({ type: "keydown", group: "system", phase: "bubble", currentTarget: eventTargetToString(contents[2]) }), testName + "keydown event should be fired on content only in the system event group #7");
+ is(JSON.stringify(keyEvents[8]), JSON.stringify({ type: "keydown", group: "system", phase: "bubble", currentTarget: eventTargetToString(contents[1]) }), testName + "keydown event should be fired on content only in the system event group #8");
+ is(JSON.stringify(keyEvents[9]), JSON.stringify({ type: "keydown", group: "system", phase: "bubble", currentTarget: eventTargetToString(contents[0]) }), testName + "keydown event should be fired on content only in the system event group #9");
+
+ is(JSON.stringify(keyEvents[10]), JSON.stringify({ type: "keypress", group: "system", phase: "capture", currentTarget: eventTargetToString(contents[0]) }), testName + "keypress event should be fired on content only in the system event group #10");
+ is(JSON.stringify(keyEvents[11]), JSON.stringify({ type: "keypress", group: "system", phase: "capture", currentTarget: eventTargetToString(contents[1]) }), testName + "keypress event should be fired on content only in the system event group #11");
+ is(JSON.stringify(keyEvents[12]), JSON.stringify({ type: "keypress", group: "system", phase: "capture", currentTarget: eventTargetToString(contents[2]) }), testName + "keypress event should be fired on content only in the system event group #12");
+ is(JSON.stringify(keyEvents[13]), JSON.stringify({ type: "keypress", group: "system", phase: "capture", currentTarget: eventTargetToString(contents[3]) }), testName + "keypress event should be fired on content only in the system event group #13");
+
+ is(JSON.stringify(keyEvents[14]), JSON.stringify({ type: "keypress", group: "system", phase: "target", currentTarget: eventTargetToString(contents[4]) }), testName + "keypress event should be fired on content only in the system event group #14");
+ is(JSON.stringify(keyEvents[15]), JSON.stringify({ type: "keypress", group: "system", phase: "target", currentTarget: eventTargetToString(contents[4]) }), testName + "keypress event should be fired on content only in the system event group #15");
+
+ is(JSON.stringify(keyEvents[16]), JSON.stringify({ type: "keypress", group: "system", phase: "bubble", currentTarget: eventTargetToString(contents[3]) }), testName + "keypress event should be fired on content only in the system event group #16");
+ is(JSON.stringify(keyEvents[17]), JSON.stringify({ type: "keypress", group: "system", phase: "bubble", currentTarget: eventTargetToString(contents[2]) }), testName + "keypress event should be fired on content only in the system event group #17");
+ is(JSON.stringify(keyEvents[18]), JSON.stringify({ type: "keypress", group: "system", phase: "bubble", currentTarget: eventTargetToString(contents[1]) }), testName + "keypress event should be fired on content only in the system event group #18");
+ is(JSON.stringify(keyEvents[19]), JSON.stringify({ type: "keypress", group: "system", phase: "bubble", currentTarget: eventTargetToString(contents[0]) }), testName + "keypress event should be fired on content only in the system event group #19");
+
+ continueTest();
+ });
+ }
+
+ if (IS_MAC) {
+ // Cmd+T is reserved for opening new tab.
+ yield testReservedKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_T,
+ modifiers:{metaKey:1}, chars:"t", unmodifiedChars:"t"});
+ } else if (IS_WIN) {
+ // Ctrl+T is reserved for opening new tab.
+ yield testReservedKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_T,
+ modifiers:{ctrlKey:1}, chars:"\u0014"});
+ yield testReservedKey({layout:KEYBOARD_LAYOUT_ARABIC, keyCode:WIN_VK_T,
+ modifiers:{ctrlKey:1}, chars:"\u0014"});
+ }
+
+ finializeKeyElementTest();
+}
+
+function* runTextInputTests()
+{
+ var textbox = document.getElementById("textbox");
+
+ function testKey(aEvent, aExpectText) {
+ textbox.value = "";
+ textbox.focus();
+
+ var name = eventToString(aEvent);
+
+ // Check if the text comes with keypress events rather than composition events.
+ var keypress = 0;
+ function onKeypress(aEvent) {
+ keypress++;
+ if (keypress == 1 && aExpectText == "") {
+ if (!aEvent.ctrlKey && !aEvent.altKey) {
+ is(aEvent.charCode, 0, name + ", the charCode value should be 0 when it shouldn't cause inputting text");
+ }
+ return;
+ }
+ if (keypress > aExpectText.length) {
+ ok(false, name + " causes too many keypress events");
+ return;
+ }
+ is(aEvent.key, aExpectText[keypress - 1],
+ name + ", " + keypress + "th keypress event's key value should be '" + aExpectText[keypress - 1] + "'");
+ is(aEvent.charCode, aExpectText.charCodeAt(keypress - 1),
+ name + ", " + keypress + "th keypress event's charCode value should be 0x" + parseInt(aExpectText.charCodeAt(keypress - 1), 16));
+ }
+ textbox.addEventListener("keypress", onKeypress, true);
+
+ return synthesizeKey(aEvent, "textbox", function() {
+ textbox.removeEventListener("keypress", onKeypress, true);
+ if (aExpectText == "") {
+ is(keypress, 1, name + " should cause one keypress event because it doesn't cause inputting text");
+ } else {
+ is(keypress, aExpectText.length, name + " should cause " + aExpectText.length + " keypress events");
+ is(textbox.value, aExpectText, name + " does not input correct text.");
+ }
+
+ continueTest();
+ });
+ }
+
+ if (IS_MAC) {
+ yield testKey({layout:KEYBOARD_LAYOUT_ARABIC_PC, keyCode:MAC_VK_ANSI_G,
+ modifiers:{shiftKey:1}, chars:"\u0644\u0623", unmodifiedChars:"\u0644\u0623"},
+ "\u0644\u0623");
+ yield testKey({layout:KEYBOARD_LAYOUT_ARABIC_PC, keyCode:MAC_VK_ANSI_T,
+ modifiers:{shiftKey:1}, chars:"\u0644\u0625", unmodifiedChars:"\u0644\u0625"},
+ "\u0644\u0625");
+ yield testKey({layout:KEYBOARD_LAYOUT_ARABIC_PC, keyCode:MAC_VK_ANSI_B,
+ modifiers:{shiftKey:1}, chars:"\u0644\u0622", unmodifiedChars:"\u0644\u0622"},
+ "\u0644\u0622");
+ yield testKey({layout:KEYBOARD_LAYOUT_ARABIC_PC, keyCode:MAC_VK_ANSI_B,
+ modifiers:{}, chars:"\u0644\u0627", unmodifiedChars:"\u0644\u0627"},
+ "\u0644\u0627");
+ } else if (IS_WIN) {
+ // Basic sanity checks
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_A,
+ modifiers:{}, chars:"a"},
+ "a");
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_A,
+ modifiers:{shiftKey:1}, chars:"A"},
+ "A");
+ // When Ctrl+Alt are pressed, any text should not be inputted.
+ yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_A,
+ modifiers:{ctrlKey:1, altKey:1}, chars:""},
+ "");
+
+ // Lithuanian AltGr should be consumed at 9/0 keys pressed
+ yield testKey({layout:KEYBOARD_LAYOUT_LITHUANIAN, keyCode:WIN_VK_8,
+ modifiers:{}, chars:"\u016B"},
+ "\u016B");
+ yield testKey({layout:KEYBOARD_LAYOUT_LITHUANIAN, keyCode:WIN_VK_9,
+ modifiers:{}, chars:"9"},
+ "9");
+ yield testKey({layout:KEYBOARD_LAYOUT_LITHUANIAN, keyCode:WIN_VK_0,
+ modifiers:{}, chars:"0"},
+ "0");
+ yield testKey({layout:KEYBOARD_LAYOUT_LITHUANIAN, keyCode:WIN_VK_8,
+ modifiers:{ctrlKey:1, altKey:1}, chars:"8"},
+ "8");
+ yield testKey({layout:KEYBOARD_LAYOUT_LITHUANIAN, keyCode:WIN_VK_9,
+ modifiers:{ctrlKey:1, altKey:1}, chars:"9"},
+ "9");
+ yield testKey({layout:KEYBOARD_LAYOUT_LITHUANIAN, keyCode:WIN_VK_0,
+ modifiers:{ctrlKey:1, altKey:1}, chars:"0"},
+ "0");
+ }
+
+ // XXX We need to move focus for canceling to search the autocomplete
+ // result. If we don't do here, Fx will crash at end of this tests.
+ document.getElementById("button").focus();
+}
+
+function* runAllTests() {
+ yield* runKeyEventTests();
+ yield* runAccessKeyTests();
+ yield* runXULKeyTests();
+ yield* runReservedKeyTests();
+ yield* runTextInputTests();
+}
+
+var gTestContinuation = null;
+
+function continueTest()
+{
+ if (!gTestContinuation) {
+ gTestContinuation = runAllTests();
+ }
+ var ret = gTestContinuation.next();
+ if (ret.done) {
+ SimpleTest.finish();
+ } else {
+ is(ret.value, true, "Key synthesized successfully");
+ }
+}
+
+function runTest()
+{
+ if (!IS_MAC && !IS_WIN) {
+ todo(false, "This test is supported on MacOSX and Windows only. (Bug 431503)");
+ return;
+ }
+
+ if (IS_WIN && OS_VERSION >= WIN8) {
+ // Switching keyboard layout to Russian - Mnemonic causes 2 assertions in KeyboardLayout::LoadLayout().
+ SimpleTest.expectAssertions(2, 2);
+ }
+ SimpleTest.waitForExplicitFinish();
+
+ clearInfobars();
+
+ continueTest();
+}
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/test_mouse_scroll.xul b/widget/tests/test_mouse_scroll.xul
new file mode 100644
index 000000000..570b304ea
--- /dev/null
+++ b/widget/tests/test_mouse_scroll.xul
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Testing composition, text and query content events"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+window.open("window_mouse_scroll_win.html", "_blank",
+ "chrome,width=600,height=600");
+
+]]>
+</script>
+</window>
diff --git a/widget/tests/test_native_key_bindings_mac.html b/widget/tests/test_native_key_bindings_mac.html
new file mode 100644
index 000000000..dc3872f02
--- /dev/null
+++ b/widget/tests/test_native_key_bindings_mac.html
@@ -0,0 +1,343 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset='utf-8'/>
+ <title>Native Key Bindings for Cocoa Test</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ <script type="text/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/NativeKeyCodes.js"></script>
+ </head>
+ <body>
+ <div id="editable" contenteditable>
+ <p>Stretching attack nullam stuck in a tree zzz, suspendisse cras nec
+ suspendisse lick suscipit. Nunc egestas amet litter box, nullam climb the
+ curtains biting I don't like that food tristique biting sleep on your
+ keyboard non. Lay down in your way cras nec tempus chase the red dot cras
+ nec, pharetra pharetra eat the grass leap run orci turpis attack.
+ Consectetur sleep in the sink eat I don't like that food, knock over the
+ lamp catnip in viverra tail flick zzz meow etiam enim. Ac ac hiss shed
+ everywhere kittens rhoncus, attack your ankles zzz iaculis kittens. Nullam
+ pellentesque rip the couch iaculis rhoncus nibh, give me fish orci turpis
+ purr sleep on your face quis nunc bibendum.</p>
+
+ <p>Neque jump on the table bat iaculis, adipiscing sleep on your keyboard
+ jump vel justo shed everywhere suspendisse lick. Zzz enim faucibus
+ hairball faucibus, pharetra sunbathe biting bat leap rip the couch attack.
+ Tortor nibh in viverra quis hairball nam, vulputate adipiscing sleep on
+ your keyboard purr knock over the lamp orci turpis. Vestibulum I don't
+ like that food et chase the red dot, adipiscing neque bibendum rutrum
+ accumsan quis rhoncus claw. Leap accumsan vehicula enim biting sleep on
+ your face, pharetra nam accumsan egestas kittens sunbathe. Pharetra chase
+ the red dot sniff non eat the grass, vulputate fluffy fur aliquam puking
+ judging you.</p>
+
+ <p>Claw purr sollicitudin sollicitudin lay down in your way consectetur,
+ pellentesque vehicula zzz orci turpis consectetur. I don't like that food
+ rhoncus pellentesque sniff attack, rhoncus tortor attack your ankles
+ iaculis scratched hiss vel. Tortor zzz tortor nullam rip the couch rutrum,
+ bat enim ut leap hairball iaculis. Bibendum sunbathe elit suspendisse
+ nibh, puking adipiscing sleep on your face sleep on your face zzz catnip.
+ Judging you rutrum bat sunbathe sleep on your face, jump on the table leap
+ tincidunt a faucibus sleep in the sink. Stuck in a tree tristique zzz hiss
+ in viverra nullam, quis tortor pharetra attack.</p>
+ </div>
+
+ <textarea id="textarea" cols="80">
+ Stretching attack nullam stuck in a tree zzz, suspendisse cras nec
+ suspendisse lick suscipit. Nunc egestas amet litter box, nullam climb the
+ curtains biting I don't like that food tristique biting sleep on your
+ keyboard non. Lay down in your way cras nec tempus chase the red dot cras
+ nec, pharetra pharetra eat the grass leap run orci turpis attack.
+ Consectetur sleep in the sink eat I don't like that food, knock over the
+ lamp catnip in viverra tail flick zzz meow etiam enim. Ac ac hiss shed
+ everywhere kittens rhoncus, attack your ankles zzz iaculis kittens. Nullam
+ pellentesque rip the couch iaculis rhoncus nibh, give me fish orci turpis
+ purr sleep on your face quis nunc bibendum.
+
+ Neque jump on the table bat iaculis, adipiscing sleep on your keyboard
+ jump vel justo shed everywhere suspendisse lick. Zzz enim faucibus
+ hairball faucibus, pharetra sunbathe biting bat leap rip the couch attack.
+ Tortor nibh in viverra quis hairball nam, vulputate adipiscing sleep on
+ your keyboard purr knock over the lamp orci turpis. Vestibulum I don't
+ like that food et chase the red dot, adipiscing neque bibendum rutrum
+ accumsan quis rhoncus claw. Leap accumsan vehicula enim biting sleep on
+ your face, pharetra nam accumsan egestas kittens sunbathe. Pharetra chase
+ the red dot sniff non eat the grass, vulputate fluffy fur aliquam puking
+ judging you.
+
+ Claw purr sollicitudin sollicitudin lay down in your way consectetur,
+ pellentesque vehicula zzz orci turpis consectetur. I don't like that food
+ rhoncus pellentesque sniff attack, rhoncus tortor attack your ankles
+ iaculis scratched hiss vel. Tortor zzz tortor nullam rip the couch rutrum,
+ bat enim ut leap hairball iaculis. Bibendum sunbathe elit suspendisse
+ nibh, puking adipiscing sleep on your face sleep on your face zzz catnip.
+ Judging you rutrum bat sunbathe sleep on your face, jump on the table leap
+ tincidunt a faucibus sleep in the sink. Stuck in a tree tristique zzz hiss
+ in viverra nullam, quis tortor pharetra attack.
+ </textarea>
+
+ <input id="input" type="text"
+ value="Stretching attack nullam stuck in a tree zzz, suspendisse cras nec
+ suspendisse lick suscipit. Nunc egestas amet litter box, nullam climb the
+ curtains biting I don't like that food tristique biting sleep on your
+ keyboard non. Lay down in your way cras nec tempus chase the red dot cras
+ nec, pharetra pharetra eat the grass leap run orci turpis attack.
+ Consectetur sleep in the sink eat I don't like that food, knock over the
+ lamp catnip in viverra tail flick zzz meow etiam enim. Ac ac hiss shed
+ everywhere kittens rhoncus, attack your ankles zzz iaculis kittens.
+ Nullam pellentesque rip the couch iaculis rhoncus nibh, give me fish orci
+ turpis purr sleep on your face quis nunc bibendum.">
+
+ <script type="text/javascript;version=1.8">
+ SimpleTest.waitForExplicitFinish();
+
+ let synthesizedKeys = [];
+ let expectations = [];
+
+ // Move to beginning of line
+ synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_LeftArrow,
+ {ctrlKey: true}, "\uf702", "\uf702"]);
+ expectations.push({
+ editable: [0, 0],
+ textarea: [0, 0],
+ input: [0, 0]
+ });
+
+ // Move to end of line
+ synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_RightArrow,
+ {ctrlKey: true}, "\uf703", "\uf703"]);
+ expectations.push({
+ editable: [73, 73],
+ textarea: [72, 72],
+ input: [732, 732]
+ });
+
+ // Move down
+ synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_N,
+ {ctrlKey: true}, "\u000e", "n"]);
+ expectations.push({
+ editable: [140, 140],
+ textarea: [145, 145],
+ input: [732, 732]
+ });
+
+ // Move to beginning of line
+ synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_LeftArrow,
+ {ctrlKey: true}, "\uf702", "\uf702"]);
+ expectations.push({
+ editable: [73, 73],
+ textarea: [73, 73],
+ input: [0, 0]
+ });
+
+ // Move word right and modify selection
+ synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_RightArrow,
+ {altKey: true, shiftKey: true}, "\uf703", "\uf703"]);
+ expectations.push({
+ editable: [73, 84],
+ textarea: [73, 90],
+ input: [0, 10]
+ });
+
+ // Move word right
+ synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_RightArrow,
+ {altKey: true}, "\uf703", "\uf703"]);
+ expectations.push({
+ editable: [84, 84],
+ textarea: [90, 90],
+ input: [10, 10]
+ });
+
+ // Move word right
+ synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_RightArrow,
+ {altKey: true}, "\uf703", "\uf703"]);
+ expectations.push({
+ editable: [89, 89],
+ textarea: [95, 95],
+ input: [17, 17]
+ });
+
+ // Move down and modify selection
+ synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_DownArrow,
+ {shiftKey: true}, "\uf701", "\uf701"]);
+ expectations.push({
+ editable: [89, 171],
+ textarea: [95, 175],
+ input: [17, 732]
+ });
+
+ // Move backward and modify selection
+ synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_B,
+ {ctrlKey: true, shiftKey: true}, "\u0002", "B"]);
+ expectations.push({
+ editable: [89, 170],
+ textarea: [95, 174],
+ input: [17, 731]
+ });
+
+ // Delete forward
+ synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_D,
+ {ctrlKey: true}, "\u0004", "d"]);
+ expectations.push({
+ editable: [89, 89],
+ textarea: [95, 95],
+ input: [17, 17]
+ });
+
+ // Delete backward
+ synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_H,
+ {ctrlKey: true}, "\u0008", "h"]);
+ expectations.push({
+ editable: [88, 88],
+ textarea: [94, 94],
+ input: [16, 16]
+ });
+
+ // Move backward
+ synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_B,
+ {ctrlKey: true}, "\u0002", "b"]);
+ expectations.push({
+ editable: [87, 87],
+ textarea: [93, 93],
+ input: [15, 15]
+ });
+
+ // Move to beginning of paragraph (line for now)
+ synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_A,
+ {ctrlKey: true}, "\u0001", "a"]);
+ expectations.push({
+ editable: [73, 73],
+ textarea: [73, 73],
+ input: [0, 0]
+ });
+
+ // Move forward
+ synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_F,
+ {ctrlKey: true}, "\u0006", "f"]);
+ expectations.push({
+ editable: [74, 74],
+ textarea: [74, 74],
+ input: [1, 1]
+ });
+
+ // Move word right
+ synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_RightArrow,
+ {altKey: true}, "\uf703", "\uf703"]);
+ expectations.push({
+ editable: [84, 84],
+ textarea: [90, 90],
+ input: [10, 10]
+ });
+
+ // Move word right
+ synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_RightArrow,
+ {altKey: true}, "\uf703", "\uf703"]);
+ expectations.push({
+ editable: [88, 88],
+ textarea: [94, 94],
+ input: [17, 17]
+ });
+
+ // Delete to end of paragraph (line for now)
+ synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_K,
+ {ctrlKey: true}, "\u000b", "k"]);
+ expectations.push({
+ editable: [88, 88],
+ textarea: [94, 94],
+ input: [17, 17]
+ });
+
+ // Move backward and modify selection
+ synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_B,
+ {ctrlKey: true, shiftKey: true}, "\u0002", "B"]);
+ expectations.push({
+ editable: [88, 87],
+ textarea: [93, 94],
+ input: [16, 17]
+ });
+
+ // Move to end of paragraph (line for now)
+ synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_E,
+ {ctrlKey: true}, "\u0005", "e"]);
+ expectations.push({
+ editable: [139, 139],
+ textarea: [94, 94],
+ input: [17, 17]
+ });
+
+ // Move up
+ synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_P,
+ {ctrlKey: true}, "\u0010", "p"]);
+ expectations.push({
+ editable: [73, 73],
+ textarea: [21, 21],
+ input: [0, 0]
+ });
+
+ function checkWindowSelection(aElement, aSelection)
+ {
+ let selection = window.getSelection();
+
+ is(selection.anchorOffset, aSelection[aElement.id][0],
+ aElement.id + ": Incorrect anchor offset");
+ is(selection.focusOffset, aSelection[aElement.id][1],
+ aElement.id + ": Incorrect focus offset");
+ }
+
+ function checkElementSelection(aElement, aSelection)
+ {
+ is(aElement.selectionStart, aSelection[aElement.id][0],
+ aElement.id + ": Incorrect selection start");
+ is(aElement.selectionEnd, aSelection[aElement.id][1],
+ aElement.id + ": Incorrect selection end");
+ }
+
+ function* testRun(aElement, aSelectionCheck, aCallback)
+ {
+ if (document.activeElement) {
+ document.activeElement.blur();
+ }
+
+ aElement.focus();
+
+ for (let i = 0; i < synthesizedKeys.length; i++) {
+ synthesizedKeys[i].push(function() {
+ aSelectionCheck.call(null, aElement, expectations[i]);
+ continueTest();
+ });
+ var synthOk = synthesizeNativeKey.apply(null, synthesizedKeys[i]);
+ synthesizedKeys[i].pop();
+ yield synthOk;
+ }
+ }
+
+ function* doTest() {
+ yield* testRun(document.getElementById("editable"), checkWindowSelection);
+ yield* testRun(document.getElementById("textarea"), checkElementSelection);
+ yield* testRun(document.getElementById("input"), checkElementSelection);
+ }
+
+ let gTestContinuation = null;
+
+ function continueTest()
+ {
+ if (!gTestContinuation) {
+ gTestContinuation = doTest();
+ }
+ var ret = gTestContinuation.next();
+ if (ret.done) {
+ SimpleTest.finish();
+ } else {
+ is(ret.value, true, "Successfully synthesized key");
+ }
+ }
+
+ SimpleTest.waitForFocus(continueTest);
+ </script>
+ </body>
+</html>
diff --git a/widget/tests/test_native_menus.xul b/widget/tests/test_native_menus.xul
new file mode 100644
index 000000000..cf9bcc881
--- /dev/null
+++ b/widget/tests/test_native_menus.xul
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Native menu system tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+window.open("native_menus_window.xul", "NativeMenuWindow",
+ "chrome,width=600,height=600");
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/test_native_mouse_mac.xul b/widget/tests/test_native_mouse_mac.xul
new file mode 100644
index 000000000..5d86864b2
--- /dev/null
+++ b/widget/tests/test_native_mouse_mac.xul
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Native mouse event tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+window.open("native_mouse_mac_window.xul", "NativeMouseWindow",
+ "chrome,width=600,height=600");
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/test_panel_mouse_coords.xul b/widget/tests/test_panel_mouse_coords.xul
new file mode 100644
index 000000000..41ef49044
--- /dev/null
+++ b/widget/tests/test_panel_mouse_coords.xul
@@ -0,0 +1,83 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=835044
+-->
+<window title="Mozilla Bug 835044"
+ onload="startTest()"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+<panel id="thepanel" level="parent"
+ onpopupshown="sendMouseEvent();"
+ onmousemove="checkCoords(event);"
+ width="80" height="80">
+</panel>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=835044"
+ id="anchor"
+ target="_blank">Mozilla Bug 835044</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+SimpleTest.waitForExplicitFinish();
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+let utils = window.QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIDOMWindowUtils);
+let panel = document.getElementById('thepanel');
+let nativeMouseMove;
+let rect;
+
+function startTest() {
+ let widgetToolkit = Cc["@mozilla.org/xre/app-info;1"].
+ getService(Ci.nsIXULRuntime).widgetToolkit;
+
+ if (widgetToolkit == "cocoa") {
+ nativeMouseMove = 5; // NSMouseMoved
+ } else if (widgetToolkit == "windows") {
+ nativeMouseMove = 1; // MOUSEEVENTF_MOVE
+ } else if (/^gtk/.test(widgetToolkit)) {
+ nativeMouseMove = 3; // GDK_MOTION_NOTIFY
+ } else {
+ todo_is("widgetToolkit", widgetToolkit, "Platform not supported");
+ done();
+ }
+
+ // This first event is to ensure that the next event will have different
+ // coordinates to the previous mouse position, and so actually generates
+ // mouse events. The mouse is not moved off the window, as that might
+ // move focus to another application.
+ utils.sendNativeMouseEvent(window.mozInnerScreenX, window.mozInnerScreenY,
+ nativeMouseMove, 0, window.documentElement);
+
+ panel.openPopup(document.getElementById("anchor"), "after_start");
+}
+
+function sendMouseEvent() {
+ rect = panel.getBoundingClientRect();
+ let x = window.mozInnerScreenX + rect.left + 1;
+ let y = window.mozInnerScreenY + rect.top + 2;
+ utils.sendNativeMouseEvent(x, y, nativeMouseMove, 0,
+ window.documentElement);
+}
+
+function checkCoords(event) {
+ is(event.clientX, rect.left + 1, "Motion x coordinate");
+ is(event.clientY, rect.top + 2, "Motion y coordinate");
+ done();
+}
+
+function done() {
+ SimpleTest.finish();
+}
+ ]]>
+ </script>
+</window>
diff --git a/widget/tests/test_picker_no_crash.html b/widget/tests/test_picker_no_crash.html
new file mode 100644
index 000000000..08ba1db12
--- /dev/null
+++ b/widget/tests/test_picker_no_crash.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for crashes when the parent window of a file picker is closed via script</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+var childWindow;
+
+function testStepOne() {
+ childWindow = window.open('window_picker_no_crash_child.html', 'childWindow', 'width=300,height=150');
+ SimpleTest.waitForFocus(testStepTwo, childWindow);
+}
+
+function testStepTwo() {
+ childWindow.document.form1.uploadbox.click();
+ // This should not crash the browser
+ childWindow.close();
+ setTimeout("testStepThree();", 5000);
+}
+
+function testStepThree() {
+ ok(true, "browser didn't crash");
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForFocus(testStepOne);
+</script>
+</body>
+</html>
diff --git a/widget/tests/test_platform_colors.xul b/widget/tests/test_platform_colors.xul
new file mode 100644
index 000000000..b30b9837f
--- /dev/null
+++ b/widget/tests/test_platform_colors.xul
@@ -0,0 +1,107 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Mac platform colors"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=518395">Mozilla Bug 518395</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<box id="colorbox"></box>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+var colors = {
+ "activeborder": ["rgb(0, 0, 0)"],
+ "activecaption": ["rgb(204, 204, 204)"],
+ "appworkspace": ["rgb(255, 255, 255)"],
+ "background": ["rgb(99, 99, 206)"],
+ "buttonface": ["rgb(240, 240, 240)"],
+ "buttonhighlight": ["rgb(255, 255, 255)"],
+ "buttonshadow": ["rgb(220, 220, 220)"],
+ "buttontext": ["rgb(0, 0, 0)"],
+ "captiontext": ["rgb(0, 0, 0)"],
+ "graytext": ["rgb(127, 127, 127)"],
+ "highlight": ["rgb(115, 132, 153)", "rgb(127, 127, 127)", "rgb(56, 117, 215)", "rgb(255, 193, 31)", "rgb(243, 70, 72)", "rgb(255, 138, 34)", "rgb(102, 197, 71)", "rgb(140, 78, 184)"],
+ "highlighttext": ["rgb(255, 255, 255)", "rgb(255, 254, 254)", "rgb(254, 255, 254)"],
+ "inactiveborder": ["rgb(255, 255, 255)"],
+ "inactivecaption": ["rgb(255, 255, 255)"],
+ "inactivecaptiontext": ["rgb(69, 69, 69)"],
+ "infobackground": ["rgb(255, 255, 199)"],
+ "infotext": ["rgb(0, 0, 0)"],
+ "menu": ["rgb(255, 255, 255)", "rgb(254, 255, 254)", "rgb(255, 254, 254)"],
+ "menutext": ["rgb(0, 0, 0)"],
+ "scrollbar": ["rgb(170, 170, 170)"],
+ "threeddarkshadow": ["rgb(220, 220, 220)"],
+ "threedface": ["rgb(240, 240, 240)"],
+ "threedhighlight": ["rgb(255, 255, 255)"],
+ "threedlightshadow": ["rgb(218, 218, 218)"],
+ "threedshadow": ["rgb(224, 224, 224)"],
+ "window": ["rgb(255, 255, 255)"],
+ "windowframe": ["rgb(204, 204, 204)"],
+ "windowtext": ["rgb(0, 0, 0)"],
+ "-moz-activehyperlinktext": ["rgb(238, 0, 0)"],
+ "-moz-buttondefault": ["rgb(220, 220, 220)"],
+ "-moz-buttonhoverface": ["rgb(240, 240, 240)"],
+ "-moz-buttonhovertext": ["rgb(0, 0, 0)"],
+ "-moz-cellhighlight": ["rgb(212, 212, 212)", "rgb(220, 220, 220)"],
+ "-moz-cellhighlighttext": ["rgb(0, 0, 0)"],
+ "-moz-eventreerow": ["rgb(255, 255, 255)"],
+ "-moz-field": ["rgb(255, 255, 255)"],
+ "-moz-fieldtext": ["rgb(0, 0, 0)"],
+ "-moz-dialog": ["rgb(232, 232, 232)"],
+ "-moz-dialogtext": ["rgb(0, 0, 0)"],
+ "-moz-dragtargetzone": ["rgb(199, 208, 218)", "rgb(198, 198, 198)", "rgb(180, 213, 255)", "rgb(250, 236, 115)", "rgb(255, 176, 139)", "rgb(255, 209, 129)", "rgb(194, 249, 144)", "rgb(232, 184, 255)"],
+ "-moz-hyperlinktext": ["rgb(0, 0, 238)"],
+ "-moz-html-cellhighlight": ["rgb(212, 212, 212)"],
+ "-moz-html-cellhighlighttext": ["rgb(0, 0, 0)"],
+ "-moz-mac-buttonactivetext": ["rgb(0, 0, 0)", "rgb(255, 255, 255)"],
+ "-moz-mac-chrome-active": ["rgb(150, 150, 150)", "rgb(167, 167, 167)", "rgb(178, 178, 178)"],
+ "-moz-mac-chrome-inactive": ["rgb(202, 202, 202)", "rgb(216, 216, 216)", "rgb(225, 225, 225)"],
+ "-moz-mac-defaultbuttontext": ["rgb(0, 0, 0)", "rgb(255, 255, 255)"],
+ //"-moz-mac-focusring": ["rgb(83, 144, 210)", "rgb(95, 112, 130)", "rgb(63, 152, 221)", "rgb(108, 126, 141)"],
+ "-moz-mac-menuselect": ["rgb(115, 132, 153)", "rgb(127, 127, 127)", "rgb(56, 117, 215)", "rgb(255, 193, 31)", "rgb(243, 70, 72)", "rgb(255, 138, 34)", "rgb(102, 197, 71)", "rgb(140, 78, 184)"],
+ "-moz-mac-menushadow": ["rgb(163, 163, 163)"],
+ "-moz-mac-menutextdisable": ["rgb(152, 152, 152)"],
+ "-moz-mac-menutextselect": ["rgb(255, 255, 255)"],
+ "-moz-mac-disabledtoolbartext": ["rgb(127, 127, 127)"],
+ "-moz-mac-secondaryhighlight": ["rgb(212, 212, 212)"],
+ "-moz-menuhover": ["rgb(115, 132, 153)", "rgb(127, 127, 127)", "rgb(56, 117, 215)", "rgb(255, 193, 31)", "rgb(243, 70, 72)", "rgb(255, 138, 34)", "rgb(102, 197, 71)", "rgb(140, 78, 184)"],
+ "-moz-menuhovertext": ["rgb(255, 255, 255)", "rgb(255, 254, 254)", "rgb(254, 255, 254)"],
+ "-moz-menubartext": ["rgb(0, 0, 0)"],
+ //"-moz-menubarhovertext": ["rgb(255, 255, 255)"],
+ "-moz-oddtreerow": ["rgb(236, 242, 254)", "rgb(240, 240, 240)", "rgb(243, 245, 250)", "rgb(243, 246, 250)", "rgb(245, 245, 245)"],
+ "-moz-visitedhyperlinktext": ["rgb(85, 26, 139)"],
+ "currentcolor": ["rgb(0, 0, 0)"],
+ //"-moz-win-mediatext": ["rgb(255, 255, 255)"],
+ //"-moz-win-communicationstext": ["rgb(255, 255, 255)"],
+ "-moz-nativehyperlinktext": ["rgb(20, 79, 174)"],
+ "-moz-comboboxtext": ["rgb(0, 0, 0)"],
+ "-moz-combobox": ["rgb(255, 255, 255)"]
+};
+
+var colorbox = document.getElementById('colorbox');
+
+for (var c in colors) {
+ dump("testing color " + c + "\n");
+ colorbox.style.backgroundColor = c;
+ var rgb = document.defaultView.getComputedStyle(colorbox, null).getPropertyValue('background-color');
+ ok(colors[c].indexOf(rgb) != -1 || colors[c].length == 8, 'platform color ' + c + ' is wrong: ' + rgb);
+}
+
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/test_plugin_input_event.html b/widget/tests/test_plugin_input_event.html
new file mode 100644
index 000000000..6ed2a14c4
--- /dev/null
+++ b/widget/tests/test_plugin_input_event.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for plugin input event</title>
+ <script type="text/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/NativeKeyCodes.js"></script>
+ <script type="text/javascript" src="utils.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script type="application/javascript">
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+</script>
+
+<p id="display">
+ <embed id="plugin" type="application/x-test" wmode="opaque">
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var gPlugin = document.getElementById("plugin");
+
+var gUtils = window.
+ QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIDOMWindowUtils);
+
+function* doTest() {
+ gPlugin.focus();
+
+ is(gUtils.IMEStatus, gUtils.IME_STATUS_PLUGIN,
+ "Plugin failed to get focus");
+
+ is(gPlugin.getLastKeyText(), "", "Must be empty before first key test");
+
+ yield synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, WIN_VK_A, {}, "a", "a", continueTest);
+ is(gPlugin.getLastKeyText(), "a", "Invalid character was inputted");
+
+ yield synthesizeNativeKey(KEYBOARD_LAYOUT_GERMAN, WIN_VK_OEM_PLUS, {}, "+", "+", continueTest);
+ is(gPlugin.getLastKeyText(), "+", "Invalid character was inputted");
+
+ yield synthesizeNativeKey(KEYBOARD_LAYOUT_GERMAN, WIN_VK_OEM_PLUS, {altGrKey:1}, "~", "+", continueTest);
+ is(gPlugin.getLastKeyText(), "~", "Invalid character was inputted");
+}
+
+var gTestContinuation = null;
+
+function continueTest() {
+ if (!gTestContinuation) {
+ gTestContinuation = doTest(continueTest);
+ }
+ var ret = gTestContinuation.next();
+ if (ret.done) {
+ SimpleTest.finish();
+ } else {
+ is(ret.value, true, "Key synthesized successfully");
+ }
+}
+
+SimpleTest.waitForFocus(continueTest);
+
+</script>
+</body>
+</html>
diff --git a/widget/tests/test_plugin_scroll_consistency.html b/widget/tests/test_plugin_scroll_consistency.html
new file mode 100644
index 000000000..76d97f4ef
--- /dev/null
+++ b/widget/tests/test_plugin_scroll_consistency.html
@@ -0,0 +1,61 @@
+<html>
+<head>
+ <title>Test for plugin child widgets not being messed up by scrolling</title>
+ <script type="text/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="utils.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body onload="setTimeout(runTests, 0)">
+<script type="application/javascript">
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+</script>
+
+<p id="display">
+ <div style="overflow:hidden; height:100px;" id="scroll">
+ <embed type="application/x-test" wmode="window" width="100" height="800" id="plugin"></object>
+ <div style="height:1000px;"></div>
+ </div>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var plugin = document.getElementById("plugin");
+
+function consistencyCheck(state) {
+ var s = plugin.doInternalConsistencyCheck();
+ ok(s == "", "Consistency check: " + state + ": " + s);
+}
+
+function runTests()
+{
+ consistencyCheck("Initial state");
+
+ var scroll = document.getElementById("scroll");
+ scroll.scrollTop = 10;
+ consistencyCheck("Scrolled down a bit");
+
+ setTimeout(function() {
+ consistencyCheck("Before scrolling back to top");
+ scroll.scrollTop = 0;
+ consistencyCheck("Scrolled to top");
+
+ setTimeout(function() {
+ consistencyCheck("After scrolling to top");
+
+ SimpleTest.finish();
+ }, 0);
+ }, 0);
+}
+
+</script>
+</body>
+
+</html>
diff --git a/widget/tests/test_position_on_resize.xul b/widget/tests/test_position_on_resize.xul
new file mode 100644
index 000000000..e1adfc2b4
--- /dev/null
+++ b/widget/tests/test_position_on_resize.xul
@@ -0,0 +1,94 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window title="Window Position On Resize Test"
+ onload="startTest()"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+<script class="testbody" type="application/javascript"><![CDATA[
+ SimpleTest.waitForExplicitFinish();
+ let win, x, y;
+
+ function startTest() {
+ win = window.openDialog("about:blank",
+ null,
+ "chrome,dialog=no,outerHeight=170,outerWidth=200");
+ waitForSuccess(function() { return win.mozPaintCount },
+ "No paint received", checkInitialSize);
+ }
+
+ function checkInitialSize() {
+ let runtime = Components.classes["@mozilla.org/xre/app-info;1"]
+ .getService(Components.interfaces.nsIXULRuntime);
+ let test = runtime.widgetToolkit == "windows" ? todo_is : is; // bug 602745
+ test(win.outerHeight,170, "initial outerHeight");
+ test(win.outerWidth, 200, "initial outerWidth");
+ x = win.screenX;
+ y = win.screenY;
+ shrink();
+ }
+ function shrink() {
+ win.resizeTo(180, 160);
+ waitForSuccess(function() { return win.outerHeight == 160 },
+ "outerHeight did not change to 160", checkShrink);
+ }
+ function checkShrink() {
+ is(win.outerWidth, 180, "resized outerWidth");
+ is(win.screenY, y, "resized window top should not change");
+ y = win.screenY;
+ restore();
+ }
+ function restore() {
+ win.resizeBy(20, 10);
+ waitForSuccess(function() { return win.outerHeight == 170 },
+ "outerHeight did not change to 170", checkRestore);
+ }
+ function checkRestore() {
+ is(win.outerWidth, 200, "restored outerWidth");
+ is(win.screenX, x, "restored window left should not change");
+ is(win.screenY, y, "restored window top should not change");
+ done();
+ }
+ function done() {
+ win.close();
+ SimpleTest.finish();
+ }
+
+ function waitForSuccess(testForSuccess, failureMsg, nextFunc) {
+ var waitCount = 0;
+
+ function repeatWait() {
+ ++waitCount;
+
+ if (testForSuccess()) {
+ nextFunc();
+ }
+ else if (waitCount > 50) {
+ ok(false, failureMsg);
+ nextFunc();
+ } else {
+ setTimeout(repeatWait, 100);
+ }
+ }
+
+ repeatWait();
+ }
+]]></script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<p id="display">
+</p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+</body>
+
+</window>
diff --git a/widget/tests/test_secure_input.html b/widget/tests/test_secure_input.html
new file mode 100644
index 000000000..2c3ee38db
--- /dev/null
+++ b/widget/tests/test_secure_input.html
@@ -0,0 +1,148 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test for secure input mode</title>
+ <script type="text/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/NativeKeyCodes.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+
+<p>
+ <input id="input_text" type="text"><br>
+ <input id="input_password" type="password"><br>
+ <input id="input_text_readonly" type="text" readonly><br>
+ <input id="input_text_ime_mode_disabled" type="text" style="ime-mode: disabled;"><br>
+ <input id="input_change" type="text"><br>
+ <textarea id="textarea"></textarea><br>
+</p>
+<div id="contenteditable" contenteditable style="min-height: 3em;"></div>
+
+<script class="testbody" type="application/javascript">
+
+ SimpleTest.waitForExplicitFinish();
+
+ function sendAKeyEvent()
+ {
+ synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_A, {}, "a", "a");
+ }
+
+ function isFocused(aElement)
+ {
+ return (SpecialPowers.focusManager.focusedElement == aElement);
+ }
+
+ function runTest()
+ {
+ sendAKeyEvent();
+ ok(true, "Not crashed: input on the document");
+ $("input_text").focus();
+ sendAKeyEvent();
+ ok(true, "Not crashed: input on <input type=\"text\">");
+ $("input_password").focus();
+ sendAKeyEvent();
+ ok(true, "Not crashed: input on <input type=\"password\">");
+ $("input_password").blur();
+ sendAKeyEvent();
+ ok(true, "Not crashed: input on the document after blur() of <input type=\"password\">");
+ $("input_password").focus();
+ $("input_text_readonly").focus();
+ sendAKeyEvent();
+ ok(true, "Not crashed: input on <input type=\"text\" readonly>");
+ $("input_password").focus();
+ $("input_text_ime_mode_disabled").focus();
+ sendAKeyEvent();
+ ok(true, "Not crashed: input on <input type=\"text\" style=\"ime-mode: disabled;\">");
+ $("input_password").focus();
+ $("textarea").focus();
+ sendAKeyEvent();
+ ok(true, "Not crashed: input on <textarea>");
+ $("input_password").focus();
+ $("contenteditable").focus();
+ sendAKeyEvent();
+ ok(true, "Not crashed: input on <div contenteditable>");
+
+ $("input_change").focus();
+ $("input_change").type = "password";
+ sendAKeyEvent();
+ ok(true, "Not crashed: input on <input type=\"password\"> changed from type=\"text\"");
+ $("input_change").type = "text";
+ sendAKeyEvent();
+ ok(true, "Not crashed: input on <input type=\"text\"> changed from type=\"password\"");
+
+ otherWindow =
+ window.open("data:text/html,<input id=\"text\" type\"text\"><input id=\"password\" type\"password\">",
+ "_blank", "chrome,width=100,height=100");
+ ok(otherWindow, "failed to open other window");
+ if (!otherWindow) {
+ SimpleTest.finish();
+ return;
+ }
+
+ $("input_text").focus();
+ otherWindow.focus();
+
+ SimpleTest.waitForFocus(function () {
+ window.focus();
+ sendAKeyEvent();
+ ok(isFocused($("input_text")), "focused element isn't <input type=\"text\">");
+ ok(true, "Not crashed: input on <input type=\"text\"> after the other document has focus");
+
+ $("input_password").focus();
+ otherWindow.focus();
+ window.focus();
+ sendAKeyEvent();
+ ok(isFocused($("input_password")), "focused element isn't <input type=\"password\">");
+ ok(true, "Not crashed: input on <input type=\"password\"> after the other document has focus");
+
+ $("input_text").focus();
+ otherWindow.focus();
+ otherWindow.document.getElementById("text").focus();
+ window.focus();
+ sendAKeyEvent();
+ ok(isFocused($("input_text")), "focused element isn't <input type=\"text\">");
+ ok(true, "Not crashed: input on <input type=\"text\"> after the other document's <input type=\"text\"> has focus");
+
+ $("input_password").focus();
+ otherWindow.focus();
+ otherWindow.document.getElementById("text").focus();
+ window.focus();
+ sendAKeyEvent();
+ ok(isFocused($("input_password")), "focused element isn't <input type=\"password\">");
+ ok(true, "Not crashed: input on <input type=\"password\"> after the other document's <input type=\"text\"> has focus");
+
+ $("input_text").focus();
+ otherWindow.focus();
+ otherWindow.document.getElementById("password").focus();
+ window.focus();
+ sendAKeyEvent();
+ ok(isFocused($("input_text")), "focused element isn't <input type=\"text\">");
+ ok(true, "Not crashed: input on <input type=\"text\"> after the other document's <input type=\"password\"> has focus");
+
+ $("input_password").focus();
+ otherWindow.focus();
+ otherWindow.document.getElementById("password").focus();
+ window.focus();
+ sendAKeyEvent();
+ ok(isFocused($("input_password")), "focused element isn't <input type=\"password\">");
+ ok(true, "Not crashed: input on <input type=\"password\"> after the other document's <input type=\"password\"> has focus");
+
+ SimpleTest.finish();
+
+ }, otherWindow);
+ }
+
+ SimpleTest.waitForFocus(runTest);
+</script>
+</body>
+</html>
diff --git a/widget/tests/test_sizemode_events.xul b/widget/tests/test_sizemode_events.xul
new file mode 100644
index 000000000..e6ab940b2
--- /dev/null
+++ b/widget/tests/test_sizemode_events.xul
@@ -0,0 +1,105 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Test for bug 715867"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+gWindow = null;
+
+gSizeModeDidChange = false;
+gSizeModeDidChangeTo = 0;
+
+function sizemodeChanged(e) {
+ gSizeModeDidChange = true;
+ gSizeModeDidChangeTo = gWindow.windowState;
+}
+
+function expectSizeModeChange(newMode, duringActionCallback) {
+ gSizeModeDidChange = false;
+
+ duringActionCallback();
+
+ if (newMode == 0) {
+ // No change should have taken place, no event should have fired.
+ ok(!gSizeModeDidChange, "No sizemodechange event should have fired.");
+ } else {
+ // Size mode change event was expected to fire.
+ ok(gSizeModeDidChange, "A sizemodechanged event should have fired.");
+ is(gSizeModeDidChangeTo, newMode, "The new sizemode should have the expected value.");
+ }
+}
+
+function startTest() {
+ if (navigator.platform.indexOf("Lin") != -1) {
+ ok(true, "This test is disabled on Linux because it expects window sizemode changes to be synchronous (which is not the case on Linux).");
+ SimpleTest.finish();
+ return;
+ };
+ openWindow();
+}
+
+function openWindow() {
+ gWindow = open('empty_window.xul', '_blank', 'chrome,screenX=50,screenY=50,width=200,height=200,resizable');
+ SimpleTest.waitForFocus(runTest, gWindow);
+}
+
+function runTest() {
+ // Install event handler.
+ gWindow.addEventListener("sizemodechange", sizemodeChanged, false);
+
+ // Run tests.
+ expectSizeModeChange(gWindow.STATE_MINIMIZED, function () {
+ gWindow.minimize();
+ });
+
+ expectSizeModeChange(gWindow.STATE_NORMAL, function () {
+ gWindow.restore();
+ });
+
+ expectSizeModeChange(gWindow.STATE_MAXIMIZED, function () {
+ gWindow.maximize();
+ });
+
+ expectSizeModeChange(gWindow.STATE_NORMAL, function () {
+ gWindow.restore();
+ });
+
+ // Normal window resizing shouldn't fire a sizemodechanged event, bug 715867.
+ expectSizeModeChange(0, function () {
+ gWindow.resizeTo(gWindow.outerWidth + 10, gWindow.outerHeight);
+ });
+
+ expectSizeModeChange(0, function () {
+ gWindow.resizeTo(gWindow.outerWidth, gWindow.outerHeight + 10);
+ });
+
+ gWindow.removeEventListener("sizemodechange", sizemodeChanged, false);
+ gWindow.close();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(startTest);
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/test_standalone_native_menu.xul b/widget/tests/test_standalone_native_menu.xul
new file mode 100644
index 000000000..bac7a352e
--- /dev/null
+++ b/widget/tests/test_standalone_native_menu.xul
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Standalone Native Menu tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+window.open("standalone_native_menu_window.xul", "StandaloneNativeMenuWindow",
+ "chrome,width=600,height=600");
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/test_system_status_bar.xul b/widget/tests/test_system_status_bar.xul
new file mode 100644
index 000000000..784620fc8
--- /dev/null
+++ b/widget/tests/test_system_status_bar.xul
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Testing composition, text and query content events"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<menupopup id="menuContainer">
+ <menu id="menu1" image="data:image/svg+xml,&lt;svg%20xmlns=&quot;http://www.w3.org/2000/svg&quot;%20width=&quot;32&quot;%20height=&quot;32&quot;>&lt;circle%20cx=&quot;16&quot;%20cy=&quot;16&quot;%20r=&quot;16&quot;/>&lt;/svg>">
+ <menupopup>
+ <menuitem label="Item 1 in menu 1"/>
+ <menuitem label="Item 2 in menu 1"/>
+ </menupopup>
+ </menu>
+ <menu id="menu2" image="data:image/svg+xml,&lt;svg%20xmlns=&quot;http://www.w3.org/2000/svg&quot;%20width=&quot;32&quot;%20height=&quot;32&quot;>&lt;path%20d=&quot;M0 16 L 16 0 L 32 16 L 16 32 Z&quot;/>&lt;/svg>">
+ <menupopup>
+ <menuitem label="Item 1 in menu 2"/>
+ <menuitem label="Item 2 in menu 2"/>
+ </menupopup>
+ </menu>
+</menupopup>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+ let Cc = Components.classes;
+ let Ci = Components.interfaces;
+
+ let systemStatusBar = Cc["@mozilla.org/widget/macsystemstatusbar;1"].getService(Ci.nsISystemStatusBar);
+ ok(systemStatusBar, "should have got an nsISystemStatusBar instance");
+
+ let menu1 = document.getElementById("menu1");
+ let menu2 = document.getElementById("menu2");
+
+ // Add and remove the item, just to get basic leak testing coverage.
+ systemStatusBar.addItem(menu1);
+ systemStatusBar.removeItem(menu1);
+
+ // Make sure that calling addItem twice with the same element doesn't leak.
+ systemStatusBar.addItem(menu2);
+ systemStatusBar.addItem(menu2);
+ systemStatusBar.removeItem(menu2);
+
+]]>
+</script>
+</window>
diff --git a/widget/tests/test_taskbar_progress.xul b/widget/tests/test_taskbar_progress.xul
new file mode 100644
index 000000000..e4ff533a0
--- /dev/null
+++ b/widget/tests/test_taskbar_progress.xul
@@ -0,0 +1,126 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Taskbar Previews Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="loaded();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script class="testbody" type="application/javascript">
+ <![CDATA[
+ let Cc = Components.classes;
+ let Ci = Components.interfaces;
+ let Cu = Components.utils;
+ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+ let TP = Ci.nsITaskbarProgress;
+
+ function IsWin7OrHigher() {
+ try {
+ var sysInfo = Cc["@mozilla.org/system-info;1"].
+ getService(Ci.nsIPropertyBag2);
+ var ver = parseFloat(sysInfo.getProperty("version"));
+ if (ver >= 6.1)
+ return true;
+ } catch (ex) { }
+ return false;
+ }
+
+ function winProgress() {
+ let taskbar = Cc["@mozilla.org/windows-taskbar;1"];
+ if (!taskbar) {
+ ok(false, "Taskbar service is always available");
+ return null;
+ }
+ taskbar = taskbar.getService(Ci.nsIWinTaskbar);
+
+ is(taskbar.available, IsWin7OrHigher(), "Expected availability of taskbar");
+ if (!taskbar.available)
+ return null;
+
+ // HACK from mconnor:
+ var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
+ let win = wm.getMostRecentWindow("navigator:browser");
+ let docShell = win.document.docShell;
+
+ let progress = taskbar.getTaskbarProgress(docShell);
+ isnot(progress, null, "Progress is not null");
+
+ try {
+ taskbar.getTaskbarProgress(null);
+ ok(false, "Got progress for null docshell");
+ } catch (e) {
+ ok(true, "Cannot get progress for null docshell");
+ }
+
+ return progress;
+ }
+
+ function macProgress() {
+ let progress = Cc["@mozilla.org/widget/macdocksupport;1"];
+ if (!progress) {
+ ok(false, "Should have gotten Mac progress service.");
+ return null;
+ }
+ return progress.getService(TP);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ function loaded()
+ {
+ let isWin = /Win/.test(navigator.platform);
+ let progress = isWin ? winProgress() : macProgress();
+ if (!TP || !progress) {
+ SimpleTest.finish();
+ return;
+ }
+
+ function shouldThrow(s,c,m) {
+ try {
+ progress.setProgressState(s,c,m);
+ return false;
+ } catch (e) {
+ return true;
+ }
+ }
+
+ function doesntThrow(s,c,m) {
+ return !shouldThrow(s,c,m);
+ }
+
+ ok(doesntThrow(TP.STATE_NO_PROGRESS, 0, 0), "No progress state can be set");
+ ok(doesntThrow(TP.STATE_INDETERMINATE, 0, 0), "Indeterminate state can be set");
+ ok(doesntThrow(TP.STATE_NORMAL, 0, 0), "Normal state can be set");
+ ok(doesntThrow(TP.STATE_ERROR, 0, 0), "Error state can be set");
+ ok(doesntThrow(TP.STATE_PAUSED, 0, 0), "Paused state can be set");
+
+ ok(shouldThrow(TP.STATE_NO_PROGRESS, 1, 1), "Cannot set no progress with values");
+ ok(shouldThrow(TP.STATE_INDETERMINATE, 1, 1), "Cannot set indeterminate with values");
+
+ // Technically passes since unsigned(-1) > 10
+ ok(shouldThrow(TP.STATE_NORMAL, -1, 10), "Cannot set negative progress");
+ todo(shouldThrow(TP.STATE_NORMAL, 1, -1), "Cannot set negative progress");
+ todo(shouldThrow(TP.STATE_NORMAL, -1, -1), "Cannot set negative progress");
+ todo(shouldThrow(TP.STATE_NORMAL, -2, -1), "Cannot set negative progress");
+
+ ok(shouldThrow(TP.STATE_NORMAL, 5, 3), "Cannot set progress greater than max");
+
+ ok(doesntThrow(TP.STATE_NORMAL, 1, 5), "Normal state can be set with values");
+ ok(doesntThrow(TP.STATE_ERROR, 3, 6), "Error state can be set with values");
+ ok(doesntThrow(TP.STATE_PAUSED, 2, 9), "Paused state can be set with values");
+
+ SimpleTest.finish();
+ }
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ </body>
+
+</window>
diff --git a/widget/tests/test_wheeltransaction.xul b/widget/tests/test_wheeltransaction.xul
new file mode 100644
index 000000000..dadd46629
--- /dev/null
+++ b/widget/tests/test_wheeltransaction.xul
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Wheel scroll transaction tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+window.open("window_wheeltransaction.xul", "_blank",
+ "chrome,width=600,height=600");
+
+]]>
+</script>
+</window>
diff --git a/widget/tests/unit/test_macwebapputils.js b/widget/tests/unit/test_macwebapputils.js
new file mode 100644
index 000000000..0701bedf0
--- /dev/null
+++ b/widget/tests/unit/test_macwebapputils.js
@@ -0,0 +1,44 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//Basic tests to verify that MacWebAppUtils works
+
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+var Cu = Components.utils;
+var Cr = Components.results;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function test_find_app()
+{
+ var mwaUtils = Cc["@mozilla.org/widget/mac-web-app-utils;1"].
+ createInstance(Ci.nsIMacWebAppUtils);
+ let sig = "com.apple.TextEdit";
+
+ let path;
+ path = mwaUtils.pathForAppWithIdentifier(sig);
+ do_print("TextEdit path: " + path + "\n");
+ do_check_neq(path, "");
+}
+
+function test_dont_find_fake_app()
+{
+ var mwaUtils = Cc["@mozilla.org/widget/mac-web-app-utils;1"].
+ createInstance(Ci.nsIMacWebAppUtils);
+ let sig = "calliope.penitentiary.dramamine";
+
+ let path;
+ path = mwaUtils.pathForAppWithIdentifier(sig);
+ do_check_eq(path, "");
+}
+
+
+function run_test()
+{
+ test_find_app();
+ test_dont_find_fake_app();
+}
diff --git a/widget/tests/unit/test_taskbar_jumplistitems.js b/widget/tests/unit/test_taskbar_jumplistitems.js
new file mode 100644
index 000000000..d145683eb
--- /dev/null
+++ b/widget/tests/unit/test_taskbar_jumplistitems.js
@@ -0,0 +1,261 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This tests taskbar jump list functionality available on win7 and up.
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+function test_basics()
+{
+ var item = Cc["@mozilla.org/windows-jumplistitem;1"].
+ createInstance(Ci.nsIJumpListItem);
+
+ var sep = Cc["@mozilla.org/windows-jumplistseparator;1"].
+ createInstance(Ci.nsIJumpListSeparator);
+
+ var shortcut = Cc["@mozilla.org/windows-jumplistshortcut;1"].
+ createInstance(Ci.nsIJumpListShortcut);
+
+ var link = Cc["@mozilla.org/windows-jumplistlink;1"].
+ createInstance(Ci.nsIJumpListLink);
+
+ do_check_false(item.equals(sep));
+ do_check_false(item.equals(shortcut));
+ do_check_false(item.equals(link));
+
+ do_check_false(sep.equals(item));
+ do_check_false(sep.equals(shortcut));
+ do_check_false(sep.equals(link));
+
+ do_check_false(shortcut.equals(item));
+ do_check_false(shortcut.equals(sep));
+ do_check_false(shortcut.equals(link));
+
+ do_check_false(link.equals(item));
+ do_check_false(link.equals(sep));
+ do_check_false(link.equals(shortcut));
+
+ do_check_true(item.equals(item));
+ do_check_true(sep.equals(sep));
+ do_check_true(link.equals(link));
+ do_check_true(shortcut.equals(shortcut));
+}
+
+function test_separator()
+{
+ // separators:
+
+ var item = Cc["@mozilla.org/windows-jumplistseparator;1"].
+ createInstance(Ci.nsIJumpListSeparator);
+
+ do_check_true(item.type == Ci.nsIJumpListItem.JUMPLIST_ITEM_SEPARATOR);
+}
+
+function test_hashes()
+{
+ var link = Cc["@mozilla.org/windows-jumplistlink;1"]
+ .createInstance(Ci.nsIJumpListLink);
+ var uri1 = Cc["@mozilla.org/network/simple-uri;1"]
+ .createInstance(Ci.nsIURI);
+ var uri2 = Cc["@mozilla.org/network/simple-uri;1"]
+ .createInstance(Ci.nsIURI);
+
+ uri1.spec = "http://www.123.com/";
+ uri2.spec = "http://www.123.com/";
+
+ link.uri = uri1;
+
+ do_check_true(link.compareHash(uri2))
+ uri2.spec = "http://www.456.com/";
+ do_check_false(link.compareHash(uri2))
+ uri2.spec = "http://www.123.com/";
+ do_check_true(link.compareHash(uri2))
+ uri2.spec = "https://www.123.com/";
+ do_check_false(link.compareHash(uri2))
+ uri2.spec = "http://www.123.com/test/";
+ do_check_false(link.compareHash(uri2))
+ uri1.spec = "http://www.123.com/test/";
+ uri2.spec = "http://www.123.com/test/";
+ do_check_true(link.compareHash(uri2))
+ uri1.spec = "https://www.123.com/test/";
+ uri2.spec = "https://www.123.com/test/";
+ do_check_true(link.compareHash(uri2))
+ uri2.spec = "ftp://www.123.com/test/";
+ do_check_false(link.compareHash(uri2))
+ uri2.spec = "http://123.com/test/";
+ do_check_false(link.compareHash(uri2))
+ uri1.spec = "https://www.123.com/test/";
+ uri2.spec = "https://www.123.com/Test/";
+ do_check_false(link.compareHash(uri2))
+
+ uri1.spec = "http://www.123.com/";
+ do_check_eq(link.uriHash, "QGLmWuwuTozr3tOfXSf5mg==");
+ uri1.spec = "http://www.123.com/test/";
+ do_check_eq(link.uriHash, "AG87Ls+GmaUYSUJFETRr3Q==");
+ uri1.spec = "https://www.123.com/";
+ do_check_eq(link.uriHash, "iSx6UH1a9enVPzUA9JZ42g==");
+
+ var uri3 = Cc["@mozilla.org/network/simple-uri;1"]
+ .createInstance(Ci.nsIURI);
+ link.uri = uri3;
+ do_check_eq(link.uriHash, "hTrpDwNRMkvXPqYV5kh1Fw==");
+}
+
+function test_links()
+{
+ // links:
+ var link1 = Cc["@mozilla.org/windows-jumplistlink;1"]
+ .createInstance(Ci.nsIJumpListLink);
+ var link2 = Cc["@mozilla.org/windows-jumplistlink;1"]
+ .createInstance(Ci.nsIJumpListLink);
+
+ var uri1 = Cc["@mozilla.org/network/simple-uri;1"]
+ .createInstance(Ci.nsIURI);
+ var uri2 = Cc["@mozilla.org/network/simple-uri;1"]
+ .createInstance(Ci.nsIURI);
+
+ uri1.spec = "http://www.test.com/";
+ uri2.spec = "http://www.test.com/";
+
+ link1.uri = uri1;
+ link1.uriTitle = "Test";
+ link2.uri = uri2;
+ link2.uriTitle = "Test";
+
+ do_check_true(link1.equals(link2));
+
+ link2.uriTitle = "Testing";
+
+ do_check_false(link1.equals(link2));
+
+ link2.uriTitle = "Test";
+ uri2.spec = "http://www.testing.com/";
+
+ do_check_false(link1.equals(link2));
+}
+
+function test_shortcuts()
+{
+ // shortcuts:
+ var sc = Cc["@mozilla.org/windows-jumplistshortcut;1"]
+ .createInstance(Ci.nsIJumpListShortcut);
+
+ var handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"]
+ .createInstance(Ci.nsILocalHandlerApp);
+
+ handlerApp.name = "TestApp";
+ handlerApp.detailedDescription = "TestApp detailed description.";
+ handlerApp.appendParameter("-test");
+
+ sc.iconIndex = 1;
+ do_check_eq(sc.iconIndex, 1);
+
+ var faviconPageUri = Cc["@mozilla.org/network/simple-uri;1"]
+ .createInstance(Ci.nsIURI);
+ faviconPageUri.spec = "http://www.123.com/";
+ sc.faviconPageUri = faviconPageUri;
+ do_check_eq(sc.faviconPageUri, faviconPageUri);
+
+ var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
+ getService(Ci.nsIProperties).
+ QueryInterface(Ci.nsIDirectoryService);
+ var notepad = dirSvc.get("WinD", Ci.nsIFile);
+ notepad.append("notepad.exe");
+ if (notepad.exists()) {
+ handlerApp.executable = notepad;
+ sc.app = handlerApp;
+ do_check_eq(sc.app.detailedDescription, "TestApp detailed description.");
+ do_check_eq(sc.app.name, "TestApp");
+ do_check_true(sc.app.parameterExists("-test"));
+ do_check_false(sc.app.parameterExists("-notset"));
+ }
+}
+
+function test_jumplist()
+{
+ // Jump lists can't register links unless the application is the default
+ // protocol handler for the protocol of the link, so we skip off testing
+ // those in these tests. We'll init the jump list for the xpc shell harness,
+ // add a task item, and commit it.
+
+ // not compiled in
+ if (Ci.nsIWinTaskbar == null)
+ return;
+
+ var taskbar = Cc["@mozilla.org/windows-taskbar;1"]
+ .getService(Ci.nsIWinTaskbar);
+
+ var builder = taskbar.createJumpListBuilder();
+
+ do_check_neq(builder, null);
+
+ // Win7 and up only
+ try {
+ var sysInfo = Cc["@mozilla.org/system-info;1"].
+ getService(Ci.nsIPropertyBag2);
+ var ver = parseFloat(sysInfo.getProperty("version"));
+ if (ver < 6.1) {
+ do_check_false(builder.available, false);
+ return;
+ }
+ } catch (ex) { }
+
+ do_check_true(taskbar.available);
+
+ builder.deleteActiveList();
+
+ var items = Cc["@mozilla.org/array;1"]
+ .createInstance(Ci.nsIMutableArray);
+
+ var sc = Cc["@mozilla.org/windows-jumplistshortcut;1"]
+ .createInstance(Ci.nsIJumpListShortcut);
+
+ var handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"]
+ .createInstance(Ci.nsILocalHandlerApp);
+
+ handlerApp.name = "Notepad";
+ handlerApp.detailedDescription = "Testing detailed description.";
+
+ var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
+ getService(Ci.nsIProperties).
+ QueryInterface(Ci.nsIDirectoryService);
+ var notepad = dirSvc.get("WinD", Ci.nsIFile);
+ notepad.append("notepad.exe");
+ if (notepad.exists()) {
+ handlerApp.executable = notepad;
+ sc.app = handlerApp;
+ items.appendElement(sc, false);
+
+ var removed = Cc["@mozilla.org/array;1"]
+ .createInstance(Ci.nsIMutableArray);
+ do_check_true(builder.initListBuild(removed));
+ do_check_true(builder.addListToBuild(builder.JUMPLIST_CATEGORY_TASKS, items));
+ do_check_true(builder.addListToBuild(builder.JUMPLIST_CATEGORY_RECENT));
+ do_check_true(builder.addListToBuild(builder.JUMPLIST_CATEGORY_FREQUENT));
+ do_check_true(builder.commitListBuild());
+
+ builder.deleteActiveList();
+
+ do_check_true(builder.initListBuild(removed));
+ do_check_true(builder.addListToBuild(builder.JUMPLIST_CATEGORY_CUSTOM, items, "Custom List"));
+ do_check_true(builder.commitListBuild());
+
+ builder.deleteActiveList();
+ }
+}
+
+function run_test()
+{
+ if (mozinfo.os != "win") {
+ return;
+ }
+ test_basics();
+ test_separator();
+ test_hashes();
+ test_links();
+ test_shortcuts();
+ test_jumplist();
+}
diff --git a/widget/tests/unit/xpcshell.ini b/widget/tests/unit/xpcshell.ini
new file mode 100644
index 000000000..d0e8f8701
--- /dev/null
+++ b/widget/tests/unit/xpcshell.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+head =
+tail =
+
+[test_taskbar_jumplistitems.js]
+[test_macwebapputils.js]
+skip-if = os != "mac"
diff --git a/widget/tests/utils.js b/widget/tests/utils.js
new file mode 100644
index 000000000..3796c7d2b
--- /dev/null
+++ b/widget/tests/utils.js
@@ -0,0 +1,27 @@
+
+function getTestPlugin(pluginName) {
+ var ph = SpecialPowers.Cc["@mozilla.org/plugin/host;1"]
+ .getService(SpecialPowers.Ci.nsIPluginHost);
+ var tags = ph.getPluginTags();
+ var name = pluginName || "Test Plug-in";
+ for (var tag of tags) {
+ if (tag.name == name) {
+ return tag;
+ }
+ }
+
+ ok(false, "Could not find plugin tag with plugin name '" + name + "'");
+ return null;
+}
+
+// call this to set the test plugin(s) initially expected enabled state.
+// it will automatically be reset to it's previous value after the test
+// ends
+function setTestPluginEnabledState(newEnabledState, pluginName) {
+ var plugin = getTestPlugin(pluginName);
+ var oldEnabledState = plugin.enabledState;
+ plugin.enabledState = newEnabledState;
+ SimpleTest.registerCleanupFunction(function() {
+ getTestPlugin(pluginName).enabledState = oldEnabledState;
+ });
+}
diff --git a/widget/tests/window_bug429954.xul b/widget/tests/window_bug429954.xul
new file mode 100644
index 000000000..6604e91b4
--- /dev/null
+++ b/widget/tests/window_bug429954.xul
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<window title="Mozilla Bug 478536"
+ onload="start();"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml" id="body">
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+function ok(aCondition, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.ok(aCondition, aMessage);
+}
+
+function is(aLeft, aRight, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.is(aLeft, aRight, aMessage);
+}
+
+function isnot(aLeft, aRight, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.isnot(aLeft, aRight, aMessage);
+}
+
+function start() {
+ var oldWidth = window.outerWidth, oldHeight = window.outerHeight;
+ window.maximize();
+ window.restore();
+ is(window.outerWidth, oldWidth, "wrong window width after maximize+restore");
+ is(window.outerHeight, oldHeight, "wrong window height after maximize+restore");
+ window.opener.wrappedJSObject.SimpleTest.finish();
+ window.close();
+}
+
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/window_bug478536.xul b/widget/tests/window_bug478536.xul
new file mode 100644
index 000000000..0a07777b4
--- /dev/null
+++ b/widget/tests/window_bug478536.xul
@@ -0,0 +1,215 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<window title="Mozilla Bug 478536"
+ width="600" height="600"
+ onload="onload();"
+ onunload="onunload();"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/paint_listener.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml" id="body">
+<style type="text/css">
+ #view {
+ overflow: auto;
+ width: 100px;
+ height: 100px;
+ border: 1px solid;
+ margin: 0;
+ }
+</style>
+<pre id="view" onscroll="onScrollView(event);">
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+function ok(aCondition, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.ok(aCondition, aMessage);
+}
+
+function is(aLeft, aRight, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.is(aLeft, aRight, aMessage);
+}
+
+function isnot(aLeft, aRight, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.isnot(aLeft, aRight, aMessage);
+}
+
+var gBody = document.getElementById("body");
+var gView = document.getElementById("view");
+
+/**
+ * Description:
+ *
+ * First, lock the wheel scrolling target to "view" at first step.
+ * Next, scroll back to top most of the "view" at second step.
+ * Finally, scroll back again at third step. This fails to scroll the "view",
+ * then, |onMouseScrollFailed| event should be fired. And at that time, we
+ * can remove the "view". So, in post processing of the event firere, the
+ * "view" should not be referred.
+ *
+ * For suppressing random test failure, all tests will be retried if we handle
+ * unexpected timeout event.
+ */
+
+var gTests = [
+ { scrollToForward: true, shouldScroll: true },
+ { scrollToForward: false, shouldScroll: true },
+ { scrollToForward: false, shouldScroll: false }
+];
+var gCurrentTestIndex = -1;
+var gIgnoreScrollEvent = true;
+
+var gPrefSvc = Components.classes["@mozilla.org/preferences-service;1"].
+ getService(Components.interfaces.nsIPrefBranch);
+const kPrefSmoothScroll = "general.smoothScroll";
+const kPrefNameTimeout = "mousewheel.transaction.timeout";
+const kDefaultTimeout = gPrefSvc.getIntPref(kPrefNameTimeout);
+
+gPrefSvc.setBoolPref(kPrefSmoothScroll, false);
+
+var gTimeout = kDefaultTimeout;
+
+gBody.addEventListener("MozMouseScrollFailed", onMouseScrollFailed, false);
+gBody.addEventListener("MozMouseScrollTransactionTimeout",
+ onTransactionTimeout, false);
+
+function setTimeoutPrefs(aTimeout)
+{
+ gPrefSvc.setIntPref(kPrefNameTimeout, aTimeout);
+ gTimeout = aTimeout;
+}
+
+function resetTimeoutPrefs()
+{
+ if (gTimeout == kDefaultTimeout)
+ return;
+ setTimeoutPrefs(kDefaultTimeout);
+}
+
+function growUpTimeoutPrefs()
+{
+ if (gTimeout != kDefaultTimeout)
+ return;
+ setTimeoutPrefs(5000);
+}
+
+function onload()
+{
+ disableNonTestMouseEvents(true);
+ setTimeout(runNextTest, 0);
+}
+
+function onunload()
+{
+ resetTimeoutPrefs();
+ disableNonTestMouseEvents(false);
+ gPrefSvc.clearUserPref(kPrefSmoothScroll);
+ SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
+ window.opener.wrappedJSObject.SimpleTest.finish();
+}
+
+function finish()
+{
+ window.close();
+}
+
+// testing code
+
+var gTimer;
+function clearTimer()
+{
+ clearTimeout(gTimer);
+ gTimer = 0;
+}
+
+function runNextTest()
+{
+ clearTimer();
+ if (++gCurrentTestIndex >= gTests.length) {
+ ok(true, "didn't crash, succeeded");
+ finish();
+ return;
+ }
+ fireWheelScrollEvent(gTests[gCurrentTestIndex].scrollToForward);
+}
+
+var gRetryCount = 5;
+function retryAllTests()
+{
+ clearTimer();
+ if (--gRetryCount >= 0) {
+ gView.scrollTop = 0;
+ gView.scrollLeft = 0;
+ gCurrentTestIndex = -1;
+ growUpTimeoutPrefs();
+ ok(true, "WARNING: retry current test-list...");
+ gTimer = setTimeout(runNextTest, 0);
+ } else {
+ ok(false, "Failed by unexpected timeout");
+ finish();
+ }
+}
+
+function fireWheelScrollEvent(aForward)
+{
+ gIgnoreScrollEvent = false;
+ var event = { deltaY: aForward ? 4.0 : -4.0,
+ deltaMode: WheelEvent.DOM_DELTA_LINE };
+ sendWheelAndPaint(gView, 5, 5, event, function() {
+ // No callback - we're just forcing the refresh driver to tick.
+ }, window);
+}
+
+function onScrollView(aEvent)
+{
+ if (gIgnoreScrollEvent)
+ return;
+ gIgnoreScrollEvent = true;
+ clearTimer();
+ ok(gTests[gCurrentTestIndex].shouldScroll, "The view is scrolled");
+ gTimer = setTimeout(runNextTest, 0);
+}
+
+function onMouseScrollFailed(aEvent)
+{
+ clearTimer();
+ gIgnoreScrollEvent = true;
+ ok(!gTests[gCurrentTestIndex].shouldScroll, "The view is not scrolled");
+ if (!gTests[gCurrentTestIndex].shouldScroll)
+ gBody.removeChild(gView);
+ runNextTest();
+}
+
+function onTransactionTimeout(aEvent)
+{
+ if (!gTimer)
+ return;
+ gIgnoreScrollEvent = true;
+ retryAllTests();
+}
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/window_bug522217.xul b/widget/tests/window_bug522217.xul
new file mode 100644
index 000000000..8fbb21037
--- /dev/null
+++ b/widget/tests/window_bug522217.xul
@@ -0,0 +1,72 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<window title="Mozilla Bug 522217"
+ onload="start();"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml" id="body">
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+function ok(aCondition, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.ok(aCondition, aMessage);
+}
+
+function is(aLeft, aRight, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.is(aLeft, aRight, aMessage);
+}
+
+function isnot(aLeft, aRight, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.isnot(aLeft, aRight, aMessage);
+}
+
+function executeSoon(aFct)
+{
+ window.opener.wrappedJSObject.SimpleTest.executeSoon(aFct);
+}
+
+function start() {
+ window.onfocus = function () {
+ window.onfocus = null;
+ var oldOuterWidth = window.outerWidth, oldOuterHeight = window.outerHeight;
+ var oldInnerWidth = window.innerWidth, oldInnerHeight = window.innerHeight;
+ document.documentElement.setAttribute("drawintitlebar", "true");
+
+ executeSoon(function() {
+ is(window.outerWidth, oldOuterWidth, "drawintitlebar shouldn't change the window's outerWidth");
+ is(window.outerHeight, oldOuterHeight, "drawintitlebar shouldn't change the window's outerHeight");
+ is(window.innerWidth, oldOuterWidth, "if drawintitlebar is set, innerWidth and outerWidth should be the same");
+ is(window.innerHeight, oldOuterHeight, "if drawintitlebar is set, innerHeight and outerHeight should be the same");
+ window.fullScreen = true;
+ window.fullScreen = false;
+ is(window.outerWidth, oldOuterWidth, "wrong outerWidth after fullscreen mode");
+ is(window.outerHeight, oldOuterHeight, "wrong outerHeight after fullscreen mode");
+ is(window.innerWidth, oldOuterWidth, "wrong innerWidth after fullscreen mode");
+ is(window.innerHeight, oldOuterHeight, "wrong innerHeight after fullscreen mode");
+ document.documentElement.removeAttribute("drawintitlebar");
+
+ executeSoon(function() {
+ is(window.outerWidth, oldOuterWidth, "wrong outerWidth after removing drawintitlebar");
+ is(window.outerHeight, oldOuterHeight, "wrong outerHeight after removing drawintitlebar");
+ is(window.innerWidth, oldInnerWidth, "wrong innerWidth after removing drawintitlebar");
+ is(window.innerHeight, oldInnerHeight, "wrong innerHeight after removing drawintitlebar");
+ window.opener.wrappedJSObject.SimpleTest.finish();
+ window.close();
+ });
+ });
+ }
+}
+
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/window_bug538242.xul b/widget/tests/window_bug538242.xul
new file mode 100644
index 000000000..fb878b138
--- /dev/null
+++ b/widget/tests/window_bug538242.xul
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<window title="Window for Test for Mozilla Bug 538242"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/>
diff --git a/widget/tests/window_bug593307_centerscreen.xul b/widget/tests/window_bug593307_centerscreen.xul
new file mode 100644
index 000000000..24d708760
--- /dev/null
+++ b/widget/tests/window_bug593307_centerscreen.xul
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<window title="Mozilla Bug 593307"
+ width="100" height="100"
+ onload="onload();"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml" id="body">
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+function onload()
+{
+ var SimpleTest = window.opener.SimpleTest;
+ SimpleTest.ok(window.screenX >= 0, "centerscreen window should not start offscreen (X coordinate)");
+ SimpleTest.ok(window.screenY >= 0, "centerscreen window should not start offscreen (Y coordinate)");
+ window.opener.finished();
+}
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/window_bug593307_offscreen.xul b/widget/tests/window_bug593307_offscreen.xul
new file mode 100644
index 000000000..0857c73a6
--- /dev/null
+++ b/widget/tests/window_bug593307_offscreen.xul
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<window title="Mozilla Bug 593307"
+ width="100" height="100"
+ onload="onLoad();"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml" id="body">
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+var centerscreen = null;
+var SimpleTest = window.arguments[0];
+var finish = window.arguments[1];
+
+function onLoad()
+{
+ centerscreen = window.openDialog('window_bug593307_centerscreen.xul','', 'chrome,centerscreen,dependent,dialog=no');
+}
+
+function finished() {
+ centerscreen.close();
+ finish();
+}
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/window_composition_text_querycontent.xul b/widget/tests/window_composition_text_querycontent.xul
new file mode 100644
index 000000000..a5b9e2655
--- /dev/null
+++ b/widget/tests/window_composition_text_querycontent.xul
@@ -0,0 +1,6992 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Testing composition, text and query content events"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onunload="onunload();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
+
+ <panel id="panel" hidden="true"
+ orient="vertical"
+ onpopupshown="onPanelShown(event);"
+ onpopuphidden="onPanelHidden(event);">
+ <vbox id="vbox">
+ <textbox id="textbox" onfocus="onFocusPanelTextbox(event);"
+ multiline="true" cols="20" rows="4" style="font-size: 36px;"/>
+ </vbox>
+ </panel>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<p id="display">
+<div id="div" style="margin: 0; padding: 0; font-size: 36px;">Here is a text frame.</div>
+<textarea style="margin: 0;" id="textarea" cols="20" rows="4"></textarea><br/>
+<iframe id="iframe" width="300" height="150"
+ src="data:text/html,&lt;textarea id='textarea' cols='20' rows='4'&gt;&lt;/textarea&gt;"></iframe><br/>
+<iframe id="iframe2" width="300" height="150"
+ src="data:text/html,&lt;body onload='document.designMode=%22on%22'&gt;body content&lt;/body&gt;"></iframe><br/>
+<iframe id="iframe3" width="300" height="150"
+ src="data:text/html,&lt;body onload='document.designMode=%22on%22'&gt;body content&lt;/body&gt;"></iframe><br/>
+<iframe id="iframe4" width="300" height="150"
+ src="data:text/html,&lt;div contenteditable id='contenteditable'&gt;&lt;/div&gt;"></iframe><br/>
+<input id="input" type="text"/><br/>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+window.opener.wrappedJSObject.SimpleTest.waitForFocus(runTest, window);
+
+function ok(aCondition, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.ok(aCondition, aMessage);
+}
+
+function is(aLeft, aRight, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.is(aLeft, aRight, aMessage);
+}
+
+function isnot(aLeft, aRight, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.isnot(aLeft, aRight, aMessage);
+}
+
+function isSimilarTo(aLeft, aRight, aAllowedDifference, aMessage)
+{
+ if (Math.abs(aLeft - aRight) <= aAllowedDifference) {
+ ok(true, aMessage);
+ } else {
+ ok(false, aMessage + ", got=" + aLeft + ", expected=" + (aRight - aAllowedDifference) + "~" + (aRight + aAllowedDifference));
+ }
+}
+
+function isGreaterThan(aLeft, aRight, aMessage)
+{
+ ok(aLeft > aRight, aMessage + ", got=" + aLeft + ", expected minimum value=" + aRight);
+}
+
+function finish()
+{
+ window.close();
+}
+
+function onunload()
+{
+ window.opener.wrappedJSObject.SimpleTest.finish();
+}
+
+var div = document.getElementById("div");
+var textarea = document.getElementById("textarea");
+var panel = document.getElementById("panel");
+var textbox = document.getElementById("textbox");
+var iframe = document.getElementById("iframe");
+var iframe2 = document.getElementById("iframe2");
+var iframe3 = document.getElementById("iframe3");
+var contenteditable;
+var windowOfContenteditable;
+var input = document.getElementById("input");
+var textareaInFrame;
+
+const nsITextInputProcessorCallback = Components.interfaces.nsITextInputProcessorCallback;
+const nsIDOMNSEditableElement = Components.interfaces.nsIDOMNSEditableElement;
+const nsIEditorIMESupport = Components.interfaces.nsIEditorIMESupport;
+const nsIInterfaceRequestor = Components.interfaces.nsIInterfaceRequestor;
+const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
+const nsIDocShell = Components.interfaces.nsIDocShell;
+
+function hitEventLoop(aFunc, aTimes)
+{
+ if (--aTimes) {
+ setTimeout(hitEventLoop, 0, aFunc, aTimes);
+ } else {
+ setTimeout(aFunc, 20);
+ }
+}
+
+function getEditorIMESupport(aNode)
+{
+ return aNode.QueryInterface(nsIDOMNSEditableElement).
+ editor.
+ QueryInterface(nsIEditorIMESupport);
+}
+
+function getHTMLEditorIMESupport(aWindow)
+{
+ return aWindow.QueryInterface(nsIInterfaceRequestor).
+ getInterface(nsIWebNavigation).
+ QueryInterface(nsIDocShell).
+ editor;
+}
+
+const kIsWin = (navigator.platform.indexOf("Win") == 0);
+const kIsMac = (navigator.platform.indexOf("Mac") == 0);
+
+const kLFLen = kIsWin ? 2 : 1;
+const kLF = kIsWin ? "\r\n" : "\n";
+
+function checkQueryContentResult(aResult, aMessage)
+{
+ ok(aResult, aMessage + ": the result is null");
+ if (!aResult) {
+ return false;
+ }
+ ok(aResult.succeeded, aMessage + ": the query content failed");
+ return aResult.succeeded;
+}
+
+function checkContent(aExpectedText, aMessage, aID)
+{
+ if (!aID) {
+ aID = "";
+ }
+ var textContent = synthesizeQueryTextContent(0, 100);
+ if (!checkQueryContentResult(textContent, aMessage +
+ ": synthesizeQueryTextContent " + aID)) {
+ return false;
+ }
+ is(textContent.text, aExpectedText,
+ aMessage + ": composition string is wrong " + aID);
+ return textContent.text == aExpectedText;
+}
+
+function checkContentRelativeToSelection(aRelativeOffset, aLength, aExpectedOffset, aExpectedText, aMessage, aID)
+{
+ if (!aID) {
+ aID = "";
+ }
+ aMessage += " (aRelativeOffset=" + aRelativeOffset + "): "
+ var textContent = synthesizeQueryTextContent(aRelativeOffset, aLength, true);
+ if (!checkQueryContentResult(textContent, aMessage +
+ "synthesizeQueryTextContent " + aID)) {
+ return false;
+ }
+ is(textContent.offset, aExpectedOffset,
+ aMessage + "offset is wrong " + aID);
+ is(textContent.text, aExpectedText,
+ aMessage + "text is wrong " + aID);
+ return textContent.offset == aExpectedOffset &&
+ textContent.text == aExpectedText;
+}
+
+function checkSelection(aExpectedOffset, aExpectedText, aMessage, aID)
+{
+ if (!aID) {
+ aID = "";
+ }
+ var selectedText = synthesizeQuerySelectedText();
+ if (!checkQueryContentResult(selectedText, aMessage +
+ ": synthesizeQuerySelectedText " + aID)) {
+ return false;
+ }
+ is(selectedText.offset, aExpectedOffset,
+ aMessage + ": selection offset is wrong " + aID);
+ is(selectedText.text, aExpectedText,
+ aMessage + ": selected text is wrong " + aID);
+ return selectedText.offset == aExpectedOffset &&
+ selectedText.text == aExpectedText;
+}
+
+function checkIMESelection(aSelectionType, aExpectedFound, aExpectedOffset, aExpectedText, aMessage, aID)
+{
+ if (!aID) {
+ aID = "";
+ }
+ aMessage += " (" + aSelectionType + ")";
+ var selectionType = 0;
+ switch (aSelectionType) {
+ case "RawClause":
+ selectionType = QUERY_CONTENT_FLAG_SELECTION_IME_RAWINPUT;
+ break;
+ case "SelectedRawClause":
+ selectionType = QUERY_CONTENT_FLAG_SELECTION_IME_SELECTEDRAWTEXT;
+ break;
+ case "ConvertedClause":
+ selectionType = QUERY_CONTENT_FLAG_SELECTION_IME_CONVERTEDTEXT;
+ break;
+ case "SelectedClause":
+ selectionType = QUERY_CONTENT_FLAG_SELECTION_IME_SELECTEDCONVERTEDTEXT;
+ break;
+ default:
+ ok(false, aMessage + ": invalid selection type, " + aSelectionType);
+ }
+ isnot(selectionType, 0, aMessage + ": wrong value");
+ var selectedText = synthesizeQuerySelectedText(selectionType);
+ if (!checkQueryContentResult(selectedText, aMessage +
+ ": synthesizeQuerySelectedText " + aID)) {
+ return false;
+ }
+ is(selectedText.notFound, !aExpectedFound,
+ aMessage + ": selection should " + (aExpectedFound ? "" : "not") + " be found " + aID);
+ if (selectedText.notFound) {
+ return selectedText.notFound == !aExpectedFound;
+ }
+
+ is(selectedText.offset, aExpectedOffset,
+ aMessage + ": selection offset is wrong " + aID);
+ is(selectedText.text, aExpectedText,
+ aMessage + ": selected text is wrong " + aID);
+ return selectedText.offset == aExpectedOffset &&
+ selectedText.text == aExpectedText;
+}
+
+function checkRect(aRect, aExpectedRect, aMessage)
+{
+ is(aRect.left, aExpectedRect.left, aMessage + ": left is wrong");
+ is(aRect.top, aExpectedRect.top, aMessage + " top is wrong");
+ is(aRect.width, aExpectedRect.width, aMessage + ": width is wrong");
+ is(aRect.height, aExpectedRect.height, aMessage + ": height is wrong");
+ return aRect.left == aExpectedRect.left &&
+ aRect.top == aExpectedRect.top &&
+ aRect.width == aExpectedRect.width &&
+ aRect.height == aExpectedRect.height;
+}
+
+function checkRectArray(aQueryTextRectArrayResult, aExpectedTextRectArray, aMessage)
+{
+ for (var i = 1; i < aExpectedTextRectArray.length; ++i) {
+ var rect = { left: {}, top: {}, width: {}, height: {} };
+ try {
+ aQueryTextRectArrayResult.getCharacterRect(i, rect.left, rect.top, rect.width, rect.height);
+ } catch (e) {
+ ok(false, aMessage + ": failed to retrieve " + i + "th rect (" + e + ")");
+ return false;
+ }
+ function toRect(aRect)
+ {
+ return { left: aRect.left.value, top: aRect.top.value, width: aRect.width.value, height: aRect.height.value };
+ }
+ if (!checkRect(toRect(rect), aExpectedTextRectArray[i], aMessage + " " + i + "th rect")) {
+ return false;
+ }
+ }
+ return true;
+}
+
+function checkRectContainsRect(aRect, aContainer, aMessage)
+{
+ var container = { left: Math.ceil(aContainer.left),
+ top: Math.ceil(aContainer.top),
+ width: Math.floor(aContainer.width),
+ height: Math.floor(aContainer.height) };
+
+ var ret = container.left <= aRect.left &&
+ container.top <= aRect.top &&
+ container.left + container.width >= aRect.left + aRect.width &&
+ container.top + container.height >= aRect.top + aRect.height;
+ ret = ret && aMessage;
+ ok(ret, aMessage + " container={ left=" + container.left + ", top=" +
+ container.top + ", width=" + container.width + ", height=" +
+ container.height + " } rect={ left=" + aRect.left + ", top=" + aRect.top +
+ ", width=" + aRect.width + ", height=" + aRect.height + " }");
+ return ret;
+}
+
+function runUndoRedoTest()
+{
+ textarea.value = "";
+ textarea.focus();
+
+ // input raw characters
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306D",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306D\u3053",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 }
+ });
+
+ // convert
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u732B",
+ "clauses":
+ [
+ { "length": 1,
+ "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ // commit
+ synthesizeComposition({ type: "compositioncommitasis" });
+
+ // input raw characters
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u307E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ // cancel the composition
+ synthesizeComposition({ type: "compositioncommit", data: "" });
+
+ // input raw characters
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3080",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3080\u3059",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 }
+ });
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3080\u3059\u3081",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 }
+ });
+
+ // convert
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u5A18",
+ "clauses":
+ [
+ { "length": 1,
+ "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ // commit
+ synthesizeComposition({ type: "compositioncommitasis" });
+
+ synthesizeKey(" ", {});
+ synthesizeKey("m", {});
+ synthesizeKey("e", {});
+ synthesizeKey("a", {});
+ synthesizeKey("n", {});
+ synthesizeKey("t", {});
+ synthesizeKey("VK_BACK_SPACE", {});
+ synthesizeKey("s", {});
+ synthesizeKey(" ", {});
+ synthesizeKey("\"", {});
+ synthesizeKey("c", {});
+ synthesizeKey("a", {});
+ synthesizeKey("t", {});
+ synthesizeKey("-", {});
+ synthesizeKey("g", {});
+ synthesizeKey("i", {});
+ synthesizeKey("r", {});
+ synthesizeKey("l", {});
+ synthesizeKey("\"", {});
+ synthesizeKey(".", {});
+ synthesizeKey(" ", {});
+ synthesizeKey("VK_SHIFT", { type: "keydown" });
+ synthesizeKey("S", { shiftKey: true });
+ synthesizeKey("VK_SHIFT", { type: "keyup" });
+ synthesizeKey("h", {});
+ synthesizeKey("e", {});
+ synthesizeKey(" ", {});
+ synthesizeKey("i", {});
+ synthesizeKey("s", {});
+ synthesizeKey(" ", {});
+ synthesizeKey("a", {});
+ synthesizeKey(" ", {});
+
+ // input raw characters
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3088",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3088\u3046",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 }
+ });
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3088\u3046\u304b",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 }
+ });
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3088\u3046\u304b\u3044",
+ "clauses":
+ [
+ { "length": 4, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 4, "length": 0 }
+ });
+
+ // convert
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u5996\u602a",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 }
+ });
+
+ // commit
+ synthesizeComposition({ type: "compositioncommitasis" });
+
+ synthesizeKey("VK_BACK_SPACE", {});
+ synthesizeKey("VK_BACK_SPACE", {});
+ synthesizeKey("VK_BACK_SPACE", {});
+ synthesizeKey("VK_BACK_SPACE", {});
+ synthesizeKey("VK_BACK_SPACE", {});
+ synthesizeKey("VK_BACK_SPACE", {});
+ synthesizeKey("VK_BACK_SPACE", {});
+ synthesizeKey("VK_BACK_SPACE", {});
+ synthesizeKey("VK_BACK_SPACE", {});
+ synthesizeKey("VK_BACK_SPACE", {});
+ synthesizeKey("VK_BACK_SPACE", {});
+ synthesizeKey("VK_BACK_SPACE", {});
+
+ var i = 0;
+ if (!checkContent("\u732B\u5A18 means \"cat-girl\".",
+ "runUndoRedoTest", "#" + ++i) ||
+ !checkSelection(20, "", "runUndoRedoTest", "#" + i)) {
+ return;
+ }
+
+ synthesizeKey("Z", {accelKey: true});
+
+ if (!checkContent("\u732B\u5A18 means \"cat-girl\". She is a \u5996\u602A",
+ "runUndoRedoTest", "#" + ++i) ||
+ !checkSelection(32, "", "runUndoRedoTest", "#" + i)) {
+ return;
+ }
+
+ synthesizeKey("Z", {accelKey: true});
+
+ if (!checkContent("\u732B\u5A18 means \"cat-girl\". She is a ",
+ "runUndoRedoTest", "#" + ++i) ||
+ !checkSelection(30, "", "runUndoRedoTest", "#" + i)) {
+ return;
+ }
+
+ synthesizeKey("Z", {accelKey: true});
+
+ if (!checkContent("\u732B\u5A18 mean",
+ "runUndoRedoTest", "#" + ++i) ||
+ !checkSelection(7, "", "runUndoRedoTest", "#" + i)) {
+ return;
+ }
+
+ synthesizeKey("Z", {accelKey: true});
+
+ if (!checkContent("\u732B\u5A18 meant",
+ "runUndoRedoTest", "#" + ++i) ||
+ !checkSelection(8, "", "runUndoRedoTest", "#" + i)) {
+ return;
+ }
+
+ synthesizeKey("Z", {accelKey: true});
+
+ if (!checkContent("\u732B\u5A18",
+ "runUndoRedoTest", "#" + ++i) ||
+ !checkSelection(2, "", "runUndoRedoTest", "#" + i)) {
+ return;
+ }
+
+ synthesizeKey("Z", {accelKey: true});
+
+ if (!checkContent("\u732B",
+ "runUndoRedoTest", "#" + ++i) ||
+ !checkSelection(1, "", "runUndoRedoTest", "#" + i)) {
+ return;
+ }
+
+ synthesizeKey("Z", {accelKey: true});
+
+ // XXX this is unexpected behavior, see bug 258291
+ if (!checkContent("\u732B",
+ "runUndoRedoTest", "#" + ++i) ||
+ !checkSelection(1, "", "runUndoRedoTest", "#" + i)) {
+ return;
+ }
+
+ synthesizeKey("Z", {accelKey: true});
+
+ if (!checkContent("",
+ "runUndoRedoTest", "#" + ++i) ||
+ !checkSelection(0, "", "runUndoRedoTest", "#" + i)) {
+ return;
+ }
+
+ synthesizeKey("Z", {accelKey: true});
+
+ if (!checkContent("",
+ "runUndoRedoTest", "#" + ++i) ||
+ !checkSelection(0, "", "runUndoRedoTest", "#" + i)) {
+ return;
+ }
+
+ synthesizeKey("Z", {accelKey: true, shiftKey: true});
+
+ if (!checkContent("\u732B",
+ "runUndoRedoTest", "#" + ++i) ||
+ !checkSelection(1, "", "runUndoRedoTest", "#" + i)) {
+ return;
+ }
+
+ synthesizeKey("Z", {accelKey: true, shiftKey: true});
+
+ // XXX this is unexpected behavior, see bug 258291
+ if (!checkContent("\u732B",
+ "runUndoRedoTest", "#" + ++i) ||
+ !checkSelection(1, "", "runUndoRedoTest", "#" + i)) {
+ return;
+ }
+
+ synthesizeKey("Z", {accelKey: true, shiftKey: true});
+
+ if (!checkContent("\u732B\u5A18",
+ "runUndoRedoTest", "#" + ++i) ||
+ !checkSelection(2, "", "runUndoRedoTest", "#" + i)) {
+ return;
+ }
+
+ synthesizeKey("Z", {accelKey: true, shiftKey: true});
+
+ if (!checkContent("\u732B\u5A18 meant",
+ "runUndoRedoTest", "#" + ++i) ||
+ !checkSelection(8, "", "runUndoRedoTest", "#" + i)) {
+ return;
+ }
+
+ synthesizeKey("Z", {accelKey: true, shiftKey: true});
+
+ if (!checkContent("\u732B\u5A18 mean",
+ "runUndoRedoTest", "#" + ++i) ||
+ !checkSelection(7, "", "runUndoRedoTest", "#" + i)) {
+ return;
+ }
+
+ synthesizeKey("Z", {accelKey: true, shiftKey: true});
+
+ if (!checkContent("\u732B\u5A18 means \"cat-girl\". She is a ",
+ "runUndoRedoTest", "#" + ++i) ||
+ !checkSelection(30, "", "runUndoRedoTest", "#" + i)) {
+ return;
+ }
+
+ synthesizeKey("Z", {accelKey: true, shiftKey: true});
+
+ if (!checkContent("\u732B\u5A18 means \"cat-girl\". She is a \u5996\u602A",
+ "runUndoRedoTest", "#" + ++i) ||
+ !checkSelection(32, "", "runUndoRedoTest", "#" + i)) {
+ return;
+ }
+
+ synthesizeKey("Z", {accelKey: true, shiftKey: true});
+
+ if (!checkContent("\u732B\u5A18 means \"cat-girl\".",
+ "runUndoRedoTest", "#" + ++i) ||
+ !checkSelection(20, "", "runUndoRedoTest", "#" + i)) {
+ return;
+ }
+
+ synthesizeKey("Z", {accelKey: true, shiftKey: true});
+
+ if (!checkContent("\u732B\u5A18 means \"cat-girl\".",
+ "runUndoRedoTest", "#" + ++i) ||
+ !checkSelection(20, "", "runUndoRedoTest", "#" + i)) {
+ return;
+ }
+}
+
+function runCompositionCommitAsIsTest()
+{
+ textarea.focus();
+
+ var result = {};
+ function clearResult()
+ {
+ result = { compositionupdate: false, compositionend: false, text: false, input: false }
+ }
+
+ function handler(aEvent)
+ {
+ result[aEvent.type] = true;
+ }
+
+ textarea.addEventListener("compositionupdate", handler, true);
+ textarea.addEventListener("compositionend", handler, true);
+ textarea.addEventListener("input", handler, true);
+ textarea.addEventListener("text", handler, true);
+
+ // compositioncommitasis with composing string.
+ textarea.value = "";
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3042",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+ is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have composition string #1");
+
+ clearResult();
+ synthesizeComposition({ type: "compositioncommitasis" });
+
+ is(result.compositionupdate, false, "runCompositionCommitAsIsTest: compositionupdate shouldn't be fired after dispatching compositioncommitasis #1");
+ is(result.compositionend, true, "runCompositionCommitAsIsTest: compositionend should be fired after dispatching compositioncommitasis #1");
+ is(result.text, true, "runCompositionCommitAsIsTest: text should be fired after dispatching compositioncommitasis because it's dispatched when there is composing string #1");
+ is(result.input, true, "runCompositionCommitAsIsTest: input should be fired after dispatching compositioncommitasis #1");
+ is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have committed string #1");
+
+ // compositioncommitasis with committed string.
+ textarea.value = "";
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3042",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+ is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have composition string #2");
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3042",
+ "clauses":
+ [
+ { "length": 0, "attr": 0 }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+ is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have committed string #2");
+
+ clearResult();
+ synthesizeComposition({ type: "compositioncommitasis" });
+
+ is(result.compositionupdate, false, "runCompositionCommitAsIsTest: compositionupdate shouldn't be fired after dispatching compositioncommitasis #2");
+ is(result.compositionend, true, "runCompositionCommitAsIsTest: compositionend should be fired after dispatching compositioncommitasis #2");
+ is(result.text, false, "runCompositionCommitAsIsTest: text shouldn't be fired after dispatching compositioncommitasis because it's dispatched when there is already committed string #2");
+ is(result.input, true, "runCompositionCommitAsIsTest: input should be fired after dispatching compositioncommitasis #2");
+ is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have committed string #2");
+
+ // compositioncommitasis with committed string.
+ textarea.value = "";
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3042",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+ is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have composition string #3");
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "",
+ "clauses":
+ [
+ { "length": 0, "attr": 0 }
+ ]
+ },
+ "caret": { "start": 0, "length": 0 }
+ });
+ is(textarea.value, "", "runCompositionCommitAsIsTest: textarea has non-empty composition string #3");
+
+ clearResult();
+ synthesizeComposition({ type: "compositioncommitasis" });
+
+ is(result.compositionupdate, false, "runCompositionCommitAsIsTest: compositionupdate shouldn't be fired after dispatching compositioncommitasis #3");
+ is(result.compositionend, true, "runCompositionCommitAsIsTest: compositionend should be fired after dispatching compositioncommitasis #3");
+ is(result.text, false, "runCompositionCommitAsIsTest: text shouldn't be fired after dispatching compositioncommitasis because it's dispatched when there is empty composition string #3");
+ is(result.input, true, "runCompositionCommitAsIsTest: input should be fired after dispatching compositioncommitasis #3");
+ is(textarea.value, "", "runCompositionCommitAsIsTest: textarea doesn't have committed string #3");
+
+ textarea.removeEventListener("compositionupdate", handler, true);
+ textarea.removeEventListener("compositionend", handler, true);
+ textarea.removeEventListener("input", handler, true);
+ textarea.removeEventListener("text", handler, true);
+}
+
+function runCompositionCommitTest()
+{
+ textarea.focus();
+
+ var result = {};
+ function clearResult()
+ {
+ result = { compositionupdate: false, compositionend: false, text: false, input: false }
+ }
+
+ function handler(aEvent)
+ {
+ result[aEvent.type] = true;
+ }
+
+ textarea.addEventListener("compositionupdate", handler, true);
+ textarea.addEventListener("compositionend", handler, true);
+ textarea.addEventListener("input", handler, true);
+ textarea.addEventListener("text", handler, true);
+
+ // compositioncommit with different composing string.
+ textarea.value = "";
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3042",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+ is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #1");
+
+ clearResult();
+ synthesizeComposition({ type: "compositioncommit", data: "\u3043" });
+
+ is(result.compositionupdate, true, "runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #1");
+ is(result.compositionend, true, "runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #1");
+ is(result.text, true, "runCompositionCommitTest: text should be fired after dispatching compositioncommit because it's dispatched when there is compoing string #1");
+ is(result.input, true, "runCompositionCommitTest: input should be fired after dispatching compositioncommit #1");
+ is(textarea.value, "\u3043", "runCompositionCommitTest: textarea doesn't have committed string #1");
+
+ // compositioncommit with different committed string when there is already committed string
+ textarea.value = "";
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3042",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+ is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #2");
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3042",
+ "clauses":
+ [
+ { "length": 0, "attr": 0 }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+ is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have committed string #2");
+
+ clearResult();
+ synthesizeComposition({ type: "compositioncommit", data: "\u3043" });
+
+ is(result.compositionupdate, true, "runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #2");
+ is(result.compositionend, true, "runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #2");
+ is(result.text, true, "runCompositionCommitTest: text should be fired after dispatching compositioncommit #2");
+ is(result.input, true, "runCompositionCommitTest: input should be fired after dispatching compositioncommit #2");
+ is(textarea.value, "\u3043", "runCompositionCommitTest: textarea doesn't have committed string #2");
+
+ // compositioncommit with empty composition string.
+ textarea.value = "";
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3042",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+ is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #3");
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "",
+ "clauses":
+ [
+ { "length": 0, "attr": 0 }
+ ]
+ },
+ "caret": { "start": 0, "length": 0 }
+ });
+ is(textarea.value, "", "runCompositionCommitTest: textarea has non-empty composition string #3");
+
+ clearResult();
+ synthesizeComposition({ type: "compositioncommit", data: "\u3043" });
+
+ is(result.compositionupdate, true, "runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #3");
+ is(result.compositionend, true, "runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #3");
+ is(result.text, true, "runCompositionCommitTest: text should be fired after dispatching compositioncommit #3");
+ is(result.input, true, "runCompositionCommitTest: input should be fired after dispatching compositioncommit #3");
+ is(textarea.value, "\u3043", "runCompositionCommitTest: textarea doesn't have committed string #3");
+
+ // compositioncommit with non-empty composition string.
+ textarea.value = "";
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3042",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+ is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #4");
+
+ clearResult();
+ synthesizeComposition({ type: "compositioncommit", data: "" });
+
+ is(result.compositionupdate, true, "runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #4");
+ is(result.compositionend, true, "runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #4");
+ is(result.text, true, "runCompositionCommitTest: text should be fired after dispatching compositioncommit #4");
+ is(result.input, true, "runCompositionCommitTest: input should be fired after dispatching compositioncommit #4");
+ is(textarea.value, "", "runCompositionCommitTest: textarea should be empty #4");
+
+ // compositioncommit immediately without compositionstart
+ textarea.value = "";
+
+ clearResult();
+ synthesizeComposition({ type: "compositioncommit", data: "\u3042" });
+
+ is(result.compositionupdate, true, "runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #5");
+ is(result.compositionend, true, "runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #5");
+ is(result.text, true, "runCompositionCommitTest: text should be fired after dispatching compositioncommit #5");
+ is(result.input, true, "runCompositionCommitTest: input should be fired after dispatching compositioncommit #5");
+ is(textarea.value, "\u3042", "runCompositionCommitTest: textarea should be empty #5");
+
+ // compositioncommit with same composition string.
+ textarea.value = "";
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3042",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+ is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #5");
+
+ clearResult();
+ synthesizeComposition({ type: "compositioncommit", data: "\u3042" });
+
+ is(result.compositionupdate, false, "runCompositionCommitTest: compositionupdate shouldn't be fired after dispatching compositioncommit #5");
+ is(result.compositionend, true, "runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #5");
+ is(result.text, true, "runCompositionCommitTest: text should be fired after dispatching compositioncommit because there was composition string #5");
+ is(result.input, true, "runCompositionCommitTest: input should be fired after dispatching compositioncommit #5");
+ is(textarea.value, "\u3042", "runCompositionCommitTest: textarea should have committed string #5");
+
+ // compositioncommit with same composition string when there is committed string
+ textarea.value = "";
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3042",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+ is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #6");
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3042",
+ "clauses":
+ [
+ { "length": 0, "attr": 0 }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+ is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #6");
+
+ clearResult();
+ synthesizeComposition({ type: "compositioncommit", data: "\u3042" });
+
+ is(result.compositionupdate, false, "runCompositionCommitTest: compositionupdate shouldn't be fired after dispatching compositioncommit #6");
+ is(result.compositionend, true, "runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #6");
+ is(result.text, false, "runCompositionCommitTest: text shouldn't be fired after dispatching compositioncommit because there was already committed string #6");
+ is(result.input, true, "runCompositionCommitTest: input should be fired after dispatching compositioncommit #6");
+ is(textarea.value, "\u3042", "runCompositionCommitTest: textarea should have committed string #6");
+
+ textarea.removeEventListener("compositionupdate", handler, true);
+ textarea.removeEventListener("compositionend", handler, true);
+ textarea.removeEventListener("input", handler, true);
+ textarea.removeEventListener("text", handler, true);
+}
+
+function runCompositionTest()
+{
+ textarea.value = "";
+ textarea.focus();
+ var caretRects = [];
+
+ var caretRect = synthesizeQueryCaretRect(0);
+ if (!checkQueryContentResult(caretRect,
+ "runCompositionTest: synthesizeQueryCaretRect #0")) {
+ return false;
+ }
+ caretRects[0] = caretRect;
+
+ // input first character
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ if (!checkContent("\u3089", "runCompositionTest", "#1-1") ||
+ !checkSelection(1, "", "runCompositionTest", "#1-1")) {
+ return;
+ }
+
+ caretRect = synthesizeQueryCaretRect(1);
+ if (!checkQueryContentResult(caretRect,
+ "runCompositionTest: synthesizeQueryCaretRect #1-1")) {
+ return false;
+ }
+ caretRects[1] = caretRect;
+
+ // input second character
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 }
+ });
+
+ if (!checkContent("\u3089\u30FC", "runCompositionTest", "#1-2") ||
+ !checkSelection(2, "", "runCompositionTest", "#1-2")) {
+ return;
+ }
+
+ caretRect = synthesizeQueryCaretRect(2);
+ if (!checkQueryContentResult(caretRect,
+ "runCompositionTest: synthesizeQueryCaretRect #1-2")) {
+ return false;
+ }
+ caretRects[2] = caretRect;
+
+ isnot(caretRects[2].left, caretRects[1].left,
+ "runCompositionTest: caret isn't moved (#1-2)");
+ is(caretRects[2].top, caretRects[1].top,
+ "runCompositionTest: caret is moved to another line (#1-2)");
+ is(caretRects[2].width, caretRects[1].width,
+ "runCompositionTest: caret width is wrong (#1-2)");
+ is(caretRects[2].height, caretRects[1].height,
+ "runCompositionTest: caret width is wrong (#1-2)");
+
+ // input third character
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC\u3081",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 }
+ });
+
+ if (!checkContent("\u3089\u30FC\u3081", "runCompositionTest", "#1-3") ||
+ !checkSelection(3, "", "runCompositionTest", "#1-3")) {
+ return;
+ }
+
+ caretRect = synthesizeQueryCaretRect(3);
+ if (!checkQueryContentResult(caretRect,
+ "runCompositionTest: synthesizeQueryCaretRect #1-3")) {
+ return false;
+ }
+ caretRects[3] = caretRect;
+
+ isnot(caretRects[3].left, caretRects[2].left,
+ "runCompositionTest: caret isn't moved (#1-3)");
+ is(caretRects[3].top, caretRects[2].top,
+ "runCompositionTest: caret is moved to another line (#1-3)");
+ is(caretRects[3].width, caretRects[2].width,
+ "runCompositionTest: caret width is wrong (#1-3)");
+ is(caretRects[3].height, caretRects[2].height,
+ "runCompositionTest: caret height is wrong (#1-3)");
+
+ // moves the caret left
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC\u3081",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 }
+ });
+
+ if (!checkContent("\u3089\u30FC\u3081", "runCompositionTest", "#1-3-1") ||
+ !checkSelection(2, "", "runCompositionTest", "#1-3-1")) {
+ return;
+ }
+
+
+ caretRect = synthesizeQueryCaretRect(2);
+ if (!checkQueryContentResult(caretRect,
+ "runCompositionTest: synthesizeQueryCaretRect #1-3-1")) {
+ return false;
+ }
+
+ is(caretRect.left, caretRects[2].left,
+ "runCompositionTest: caret rects are different (#1-3-1, left)");
+ is(caretRect.top, caretRects[2].top,
+ "runCompositionTest: caret rects are different (#1-3-1, top)");
+ // by bug 335359, the caret width depends on the right side's character.
+ is(caretRect.width, caretRects[2].width + 1,
+ "runCompositionTest: caret rects are different (#1-3-1, width)");
+ is(caretRect.height, caretRects[2].height,
+ "runCompositionTest: caret rects are different (#1-3-1, height)");
+
+ // moves the caret left
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC\u3081",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ if (!checkContent("\u3089\u30FC\u3081", "runCompositionTest", "#1-3-2") ||
+ !checkSelection(1, "", "runCompositionTest", "#1-3-2")) {
+ return;
+ }
+
+
+ caretRect = synthesizeQueryCaretRect(1);
+ if (!checkQueryContentResult(caretRect,
+ "runCompositionTest: synthesizeQueryCaretRect #1-3-2")) {
+ return false;
+ }
+
+ is(caretRect.left, caretRects[1].left,
+ "runCompositionTest: caret rects are different (#1-3-2, left)");
+ is(caretRect.top, caretRects[1].top,
+ "runCompositionTest: caret rects are different (#1-3-2, top)");
+ // by bug 335359, the caret width depends on the right side's character.
+ is(caretRect.width, caretRects[1].width + 1,
+ "runCompositionTest: caret rects are different (#1-3-2, width)");
+ is(caretRect.height, caretRects[1].height,
+ "runCompositionTest: caret rects are different (#1-3-2, height)");
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC\u3081\u3093",
+ "clauses":
+ [
+ { "length": 4, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 4, "length": 0 }
+ });
+
+ if (!checkContent("\u3089\u30FC\u3081\u3093", "runCompositionTest", "#1-4") ||
+ !checkSelection(4, "", "runCompositionTest", "#1-4")) {
+ return;
+ }
+
+
+ // backspace
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC\u3081",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 }
+ });
+
+ if (!checkContent("\u3089\u30FC\u3081", "runCompositionTest", "#1-5") ||
+ !checkSelection(3, "", "runCompositionTest", "#1-5")) {
+ return;
+ }
+
+ // re-input
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC\u3081\u3093",
+ "clauses":
+ [
+ { "length": 4, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 4, "length": 0 }
+ });
+
+ if (!checkContent("\u3089\u30FC\u3081\u3093", "runCompositionTest", "#1-6") ||
+ !checkSelection(4, "", "runCompositionTest", "#1-6")) {
+ return;
+ }
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC\u3081\u3093\u3055",
+ "clauses":
+ [
+ { "length": 5, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 5, "length": 0 }
+ });
+
+ if (!checkContent("\u3089\u30FC\u3081\u3093\u3055", "runCompositionTest", "#1-7") ||
+ !checkSelection(5, "", "runCompositionTest", "#1-7")) {
+ return;
+ }
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC\u3081\u3093\u3055\u3044",
+ "clauses":
+ [
+ { "length": 6, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 6, "length": 0 }
+ });
+
+ if (!checkContent("\u3089\u30FC\u3081\u3093\u3055\u3044", "runCompositionTest", "#1-8") ||
+ !checkSelection(6, "", "runCompositionTest", "#1-8")) {
+ return;
+ }
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC\u3081\u3093\u3055\u3044\u3053",
+ "clauses":
+ [
+ { "length": 7, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 7, "length": 0 }
+ });
+
+ if (!checkContent("\u3089\u30FC\u3081\u3093\u3055\u3044\u3053", "runCompositionTest", "#1-8") ||
+ !checkSelection(7, "", "runCompositionTest", "#1-8")) {
+ return;
+ }
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC\u3081\u3093\u3055\u3044\u3053\u3046",
+ "clauses":
+ [
+ { "length": 8, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 8, "length": 0 }
+ });
+
+ if (!checkContent("\u3089\u30FC\u3081\u3093\u3055\u3044\u3053\u3046",
+ "runCompositionTest", "#1-9") ||
+ !checkSelection(8, "", "runCompositionTest", "#1-9")) {
+ return;
+ }
+
+ // convert
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u30E9\u30FC\u30E1\u30F3\u6700\u9AD8",
+ "clauses":
+ [
+ { "length": 4,
+ "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
+ { "length": 2,
+ "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": 4, "length": 0 }
+ });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700\u9AD8",
+ "runCompositionTest", "#1-10") ||
+ !checkSelection(4, "", "runCompositionTest", "#1-10")) {
+ return;
+ }
+
+ // change the selected clause
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u30E9\u30FC\u30E1\u30F3\u6700\u9AD8",
+ "clauses":
+ [
+ { "length": 4,
+ "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
+ { "length": 2,
+ "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": 6, "length": 0 }
+ });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700\u9AD8",
+ "runCompositionTest", "#1-11") ||
+ !checkSelection(6, "", "runCompositionTest", "#1-11")) {
+ return;
+ }
+
+ // reset clauses
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046",
+ "clauses":
+ [
+ { "length": 5,
+ "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
+ { "length": 3,
+ "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": 5, "length": 0 }
+ });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046",
+ "runCompositionTest", "#1-12") ||
+ !checkSelection(5, "", "runCompositionTest", "#1-12")) {
+ return;
+ }
+
+
+ var textRect1 = synthesizeQueryTextRect(0, 1);
+ var textRect2 = synthesizeQueryTextRect(1, 1);
+ if (!checkQueryContentResult(textRect1,
+ "runCompositionTest: synthesizeQueryTextRect #1-12-1") ||
+ !checkQueryContentResult(textRect2,
+ "runCompositionTest: synthesizeQueryTextRect #1-12-2")) {
+ return false;
+ }
+
+ // commit the composition string
+ synthesizeComposition({ type: "compositioncommitasis" });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046",
+ "runCompositionTest", "#1-13") ||
+ !checkSelection(8, "", "runCompositionTest", "#1-13")) {
+ return;
+ }
+
+ var textRect3 = synthesizeQueryTextRect(0, 1);
+ var textRect4 = synthesizeQueryTextRect(1, 1);
+
+ if (!checkQueryContentResult(textRect3,
+ "runCompositionTest: synthesizeQueryTextRect #1-13-1") ||
+ !checkQueryContentResult(textRect4,
+ "runCompositionTest: synthesizeQueryTextRect #1-13-2")) {
+ return false;
+ }
+
+ checkRect(textRect3, textRect1, "runCompositionTest: textRect #1-13-1");
+ checkRect(textRect4, textRect2, "runCompositionTest: textRect #1-13-2");
+
+ // restart composition and input characters
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3057",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046\u3057",
+ "runCompositionTest", "#2-1") ||
+ !checkSelection(8 + 1, "", "runCompositionTest", "#2-1")) {
+ return;
+ }
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3058",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046\u3058",
+ "runCompositionTest", "#2-2") ||
+ !checkSelection(8 + 1, "", "runCompositionTest", "#2-2")) {
+ return;
+ }
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3058\u3087",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 }
+ });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046\u3058\u3087",
+ "runCompositionTest", "#2-3") ||
+ !checkSelection(8 + 2, "", "runCompositionTest", "#2-3")) {
+ return;
+ }
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3058\u3087\u3046",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 }
+ });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046\u3058\u3087\u3046",
+ "runCompositionTest", "#2-4") ||
+ !checkSelection(8 + 3, "", "runCompositionTest", "#2-4")) {
+ return;
+ }
+
+ // commit the composition string
+ synthesizeComposition({ type: "compositioncommitasis" });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046\u3058\u3087\u3046",
+ "runCompositionTest", "#2-4") ||
+ !checkSelection(8 + 3, "", "runCompositionTest", "#2-4")) {
+ return;
+ }
+
+ // set selection
+ var selectionSetTest = synthesizeSelectionSet(4, 7, false);
+ ok(selectionSetTest, "runCompositionTest: selectionSetTest failed");
+
+ if (!checkSelection(4, "\u3055\u884C\u3053\u3046\u3058\u3087\u3046", "runCompositionTest", "#3-1")) {
+ return;
+ }
+
+ // start composition with selection
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u304A",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u304A",
+ "runCompositionTest", "#3-2") ||
+ !checkSelection(4 + 1, "", "runCompositionTest", "#3-2")) {
+ return;
+ }
+
+ // remove the composition string
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "",
+ "clauses":
+ [
+ { "length": 0, "attr": 0 }
+ ]
+ },
+ "caret": { "start": 0, "length": 0 }
+ });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
+ "runCompositionTest", "#3-3") ||
+ !checkSelection(4, "", "runCompositionTest", "#3-3")) {
+ return;
+ }
+
+ // re-input the composition string
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3046",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3046",
+ "runCompositionTest", "#3-4") ||
+ !checkSelection(4 + 1, "", "runCompositionTest", "#3-4")) {
+ return;
+ }
+
+ // cancel the composition
+ synthesizeComposition({ type: "compositioncommit", data: "" });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
+ "runCompositionTest", "#3-5") ||
+ !checkSelection(4, "", "runCompositionTest", "#3-5")) {
+ return;
+ }
+
+ // bug 271815, some Chinese IMEs for Linux make empty composition string
+ // and compty clause information when it lists up Chinese characters on
+ // its candidate window.
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "",
+ "clauses":
+ [
+ { "length": 0, "attr": 0 }
+ ]
+ },
+ "caret": { "start": 0, "length": 0 }
+ });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
+ "runCompositionTest", "#4-1") ||
+ !checkSelection(4, "", "runCompositionTest", "#4-1")) {
+ return;
+ }
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "",
+ "clauses":
+ [
+ { "length": 0, "attr": 0 }
+ ]
+ },
+ "caret": { "start": 0, "length": 0 }
+ });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
+ "runCompositionTest", "#4-2") ||
+ !checkSelection(4, "", "runCompositionTest", "#4-2")) {
+ return;
+ }
+
+ synthesizeComposition({ type: "compositioncommit", data: "\u6700" });
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700",
+ "runCompositionTest", "#4-3") ||
+ !checkSelection(5, "", "runCompositionTest", "#4-3")) {
+ return;
+ }
+
+ // testing the canceling case
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "",
+ "clauses":
+ [
+ { "length": 0, "attr": 0 }
+ ]
+ },
+ "caret": { "start": 0, "length": 0 }
+ });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700",
+ "runCompositionTest", "#4-5") ||
+ !checkSelection(5, "", "runCompositionTest", "#4-5")) {
+ return;
+ }
+
+ synthesizeComposition({ type: "compositioncommitasis" });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700",
+ "runCompositionTest", "#4-6") ||
+ !checkSelection(5, "", "runCompositionTest", "#4-6")) {
+ return;
+ }
+
+ // testing whether the empty composition string deletes selected string.
+ synthesizeKey("VK_LEFT", { shiftKey: true });
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "",
+ "clauses":
+ [
+ { "length": 0, "attr": 0 }
+ ]
+ },
+ "caret": { "start": 0, "length": 0 }
+ });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
+ "runCompositionTest", "#4-8") ||
+ !checkSelection(4, "", "runCompositionTest", "#4-8")) {
+ return;
+ }
+
+ synthesizeComposition({ type: "compositioncommit", data: "\u9AD8" });
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u9AD8",
+ "runCompositionTest", "#4-9") ||
+ !checkSelection(5, "", "runCompositionTest", "#4-9")) {
+ return;
+ }
+
+ synthesizeKey("VK_BACK_SPACE", {});
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
+ "runCompositionTest", "#4-11") ||
+ !checkSelection(4, "", "runCompositionTest", "#4-11")) {
+ return;
+ }
+
+ // bug 23558, ancient Japanese IMEs on Window may send empty text event
+ // twice at canceling composition.
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u6700",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700",
+ "runCompositionTest", "#5-1") ||
+ !checkSelection(4 + 1, "", "runCompositionTest", "#5-1")) {
+ return;
+ }
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "",
+ "clauses":
+ [
+ { "length": 0, "attr": 0 }
+ ]
+ },
+ "caret": { "start": 0, "length": 0 }
+ });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
+ "runCompositionTest", "#5-2") ||
+ !checkSelection(4, "", "runCompositionTest", "#5-2")) {
+ return;
+ }
+
+ synthesizeComposition({ type: "compositioncommitasis" });
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
+ "runCompositionTest", "#5-3") ||
+ !checkSelection(4, "", "runCompositionTest", "#5-3")) {
+ return;
+ }
+
+ // Undo tests for the testcases for bug 23558 and bug 271815
+ synthesizeKey("Z", { accelKey: true });
+
+ // XXX this is unexpected behavior, see bug 258291
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
+ "runCompositionTest", "#6-1") ||
+ !checkSelection(4, "", "runCompositionTest", "#6-1")) {
+ return;
+ }
+
+ synthesizeKey("Z", { accelKey: true });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u9AD8",
+ "runCompositionTest", "#6-2") ||
+ !checkSelection(5, "", "runCompositionTest", "#6-2")) {
+ return;
+ }
+
+ synthesizeKey("Z", { accelKey: true });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700",
+ "runCompositionTest", "#6-3") ||
+ !checkSelection(4, "\u6700", "runCompositionTest", "#6-3")) {
+ return;
+ }
+
+ synthesizeKey("Z", { accelKey: true });
+
+ // XXX this is unexpected behavior, see bug 258291
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700",
+ "runCompositionTest", "#6-4") ||
+ !checkSelection(5, "", "runCompositionTest", "#6-4")) {
+ return;
+ }
+
+ synthesizeKey("Z", { accelKey: true });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
+ "runCompositionTest", "#6-5") ||
+ !checkSelection(4, "", "runCompositionTest", "#6-5")) {
+ return;
+ }
+}
+
+function runCompositionEventTest()
+{
+ const kDescription = "runCompositionEventTest: ";
+ const kEvents = ["compositionstart", "compositionupdate", "compositionend",
+ "input"];
+
+ input.value = "";
+ input.focus();
+
+ var windowEventCounts = [], windowEventData = [], windowEventLocale = [];
+ var inputEventCounts = [], inputEventData = [], inputEventLocale = [];
+ var preventDefault = false;
+ var stopPropagation = false;
+
+ function initResults()
+ {
+ for (var i = 0; i < kEvents.length; i++) {
+ windowEventCounts[kEvents[i]] = 0;
+ windowEventData[kEvents[i]] = "";
+ windowEventLocale[kEvents[i]] = "";
+ inputEventCounts[kEvents[i]] = 0;
+ inputEventData[kEvents[i]] = "";
+ inputEventLocale[kEvents[i]] = "";
+ }
+ }
+
+ function compositionEventHandlerForWindow(aEvent)
+ {
+ windowEventCounts[aEvent.type]++;
+ windowEventData[aEvent.type] = aEvent.data;
+ windowEventLocale[aEvent.type] = aEvent.locale;
+ if (preventDefault) {
+ aEvent.preventDefault();
+ }
+ if (stopPropagation) {
+ aEvent.stopPropagation();
+ }
+ }
+
+ function formEventHandlerForWindow(aEvent)
+ {
+ ok(aEvent.isTrusted, "input events must be trusted events");
+ windowEventCounts[aEvent.type]++;
+ windowEventData[aEvent.type] = input.value;
+ }
+
+ function compositionEventHandlerForInput(aEvent)
+ {
+ inputEventCounts[aEvent.type]++;
+ inputEventData[aEvent.type] = aEvent.data;
+ inputEventLocale[aEvent.type] = aEvent.locale;
+ if (preventDefault) {
+ aEvent.preventDefault();
+ }
+ if (stopPropagation) {
+ aEvent.stopPropagation();
+ }
+ }
+
+ function formEventHandlerForInput(aEvent)
+ {
+ inputEventCounts[aEvent.type]++;
+ inputEventData[aEvent.type] = input.value;
+ }
+
+ window.addEventListener("compositionstart", compositionEventHandlerForWindow,
+ true, true);
+ window.addEventListener("compositionend", compositionEventHandlerForWindow,
+ true, true);
+ window.addEventListener("compositionupdate", compositionEventHandlerForWindow,
+ true, true);
+ window.addEventListener("input", formEventHandlerForWindow,
+ true, true);
+
+ input.addEventListener("compositionstart", compositionEventHandlerForInput,
+ true, true);
+ input.addEventListener("compositionend", compositionEventHandlerForInput,
+ true, true);
+ input.addEventListener("compositionupdate", compositionEventHandlerForInput,
+ true, true);
+ input.addEventListener("input", formEventHandlerForInput,
+ true, true);
+
+ // test for normal case
+ initResults();
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ is(windowEventCounts["compositionstart"], 1,
+ kDescription + "compositionstart hasn't been handled by window #1");
+ is(windowEventData["compositionstart"], "",
+ kDescription + "data of compositionstart isn't empty (window) #1");
+ is(windowEventLocale["compositionstart"], "",
+ kDescription + "locale of compositionstart isn't empty (window) #1");
+ is(inputEventCounts["compositionstart"], 1,
+ kDescription + "compositionstart hasn't been handled by input #1");
+ is(inputEventData["compositionstart"], "",
+ kDescription + "data of compositionstart isn't empty (input) #1");
+ is(inputEventLocale["compositionstart"], "",
+ kDescription + "locale of compositionstart isn't empty (input) #1");
+
+ is(windowEventCounts["compositionupdate"], 1,
+ kDescription + "compositionupdate hasn't been handled by window #1");
+ is(windowEventData["compositionupdate"], "\u3089",
+ kDescription + "data of compositionupdate doesn't match (window) #1");
+ is(windowEventLocale["compositionupdate"], "",
+ kDescription + "locale of compositionupdate isn't empty (window) #1");
+ is(inputEventCounts["compositionupdate"], 1,
+ kDescription + "compositionupdate hasn't been handled by input #1");
+ is(inputEventData["compositionupdate"], "\u3089",
+ kDescription + "data of compositionupdate doesn't match (input) #1");
+ is(inputEventLocale["compositionupdate"], "",
+ kDescription + "locale of compositionupdate isn't empty (input) #1");
+
+ is(windowEventCounts["compositionend"], 0,
+ kDescription + "compositionend has been handled by window #1");
+ is(inputEventCounts["compositionend"], 0,
+ kDescription + "compositionend has been handled by input #1");
+
+ is(windowEventCounts["input"], 1,
+ kDescription + "input hasn't been handled by window #1");
+ is(windowEventData["input"], "\u3089",
+ kDescription + "value of input element wasn't modified (window) #1");
+ is(inputEventCounts["input"], 1,
+ kDescription + "input hasn't been handled by input #1");
+ is(inputEventData["input"], "\u3089",
+ kDescription + "value of input element wasn't modified (input) #1");
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 }
+ });
+
+ is(windowEventCounts["compositionstart"], 1,
+ kDescription + "compositionstart has been handled more than once by window #2");
+ is(inputEventCounts["compositionstart"], 1,
+ kDescription + "compositionstart has been handled more than once by input #2");
+
+ is(windowEventCounts["compositionupdate"], 2,
+ kDescription + "compositionupdate hasn't been handled by window #2");
+ is(windowEventData["compositionupdate"], "\u3089\u30FC",
+ kDescription + "data of compositionupdate doesn't match (window) #2");
+ is(windowEventLocale["compositionupdate"], "",
+ kDescription + "locale of compositionupdate isn't empty (window) #2");
+ is(inputEventCounts["compositionupdate"], 2,
+ kDescription + "compositionupdate hasn't been handled by input #2");
+ is(inputEventData["compositionupdate"], "\u3089\u30FC",
+ kDescription + "data of compositionupdate doesn't match (input) #2");
+ is(inputEventLocale["compositionupdate"], "",
+ kDescription + "locale of compositionupdate isn't empty (input) #2");
+
+ is(windowEventCounts["compositionend"], 0,
+ kDescription + "compositionend has been handled during composition by window #2");
+ is(inputEventCounts["compositionend"], 0,
+ kDescription + "compositionend has been handled during composition by input #2");
+
+ is(windowEventCounts["input"], 2,
+ kDescription + "input hasn't been handled by window #2");
+ is(windowEventData["input"], "\u3089\u30FC",
+ kDescription + "value of input element wasn't modified (window) #2");
+ is(inputEventCounts["input"], 2,
+ kDescription + "input hasn't been handled by input #2");
+ is(inputEventData["input"], "\u3089\u30FC",
+ kDescription + "value of input element wasn't modified (input) #2");
+
+ // text event shouldn't cause composition update, e.g., at committing.
+ synthesizeComposition({ type: "compositioncommitasis" });
+
+ is(windowEventCounts["compositionstart"], 1,
+ kDescription + "compositionstart has been handled more than once by window #3");
+ is(inputEventCounts["compositionstart"], 1,
+ kDescription + "compositionstart has been handled more than once by input #3");
+
+ is(windowEventCounts["compositionupdate"], 2,
+ kDescription + "compositionupdate has been fired unexpectedly on window #3");
+ is(inputEventCounts["compositionupdate"], 2,
+ kDescription + "compositionupdate has been fired unexpectedly on input #3");
+
+ is(windowEventCounts["compositionend"], 1,
+ kDescription + "compositionend hasn't been handled by window #3");
+ is(windowEventData["compositionend"], "\u3089\u30FC",
+ kDescription + "data of compositionend doesn't match (window) #3");
+ is(windowEventLocale["compositionend"], "",
+ kDescription + "locale of compositionend isn't empty (window) #3");
+ is(inputEventCounts["compositionend"], 1,
+ kDescription + "compositionend hasn't been handled by input #3");
+ is(inputEventData["compositionend"], "\u3089\u30FC",
+ kDescription + "data of compositionend doesn't match (input) #3");
+ is(inputEventLocale["compositionend"], "",
+ kDescription + "locale of compositionend isn't empty (input) #3");
+
+ is(windowEventCounts["input"], 3,
+ kDescription + "input hasn't been handled by window #3");
+ is(windowEventData["input"], "\u3089\u30FC",
+ kDescription + "value of input element wasn't modified (window) #3");
+ is(inputEventCounts["input"], 3,
+ kDescription + "input hasn't been handled by input #3");
+ is(inputEventData["input"], "\u3089\u30FC",
+ kDescription + "value of input element wasn't modified (input) #3");
+
+ // select the second character, then, data of composition start should be
+ // the selected character.
+ initResults();
+ synthesizeKey("VK_LEFT", { shiftKey: true });
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ synthesizeComposition({ type: "compositioncommitasis" });
+
+ is(windowEventCounts["compositionstart"], 1,
+ kDescription + "compositionstart hasn't been handled by window #4");
+ is(windowEventData["compositionstart"], "\u30FC",
+ kDescription + "data of compositionstart is empty (window) #4");
+ is(windowEventLocale["compositionstart"], "",
+ kDescription + "locale of compositionstart isn't empty (window) #4");
+ is(inputEventCounts["compositionstart"], 1,
+ kDescription + "compositionstart hasn't been handled by input #4");
+ is(inputEventData["compositionstart"], "\u30FC",
+ kDescription + "data of compositionstart is empty (input) #4");
+ is(inputEventLocale["compositionstart"], "",
+ kDescription + "locale of compositionstart isn't empty (input) #4");
+
+ is(windowEventCounts["compositionupdate"], 1,
+ kDescription + "compositionupdate hasn't been handled by window #4");
+ is(windowEventData["compositionupdate"], "\u3089",
+ kDescription + "data of compositionupdate doesn't match (window) #4");
+ is(windowEventLocale["compositionupdate"], "",
+ kDescription + "locale of compositionupdate isn't empty (window) #4");
+ is(inputEventCounts["compositionupdate"], 1,
+ kDescription + "compositionupdate hasn't been handled by input #4");
+ is(inputEventData["compositionupdate"], "\u3089",
+ kDescription + "data of compositionupdate doesn't match (input) #4");
+ is(inputEventLocale["compositionupdate"], "",
+ kDescription + "locale of compositionupdate isn't empty (input) #4");
+
+ is(windowEventCounts["compositionend"], 1,
+ kDescription + "compositionend hasn't been handled by window #4");
+ is(windowEventData["compositionend"], "\u3089",
+ kDescription + "data of compositionend doesn't match (window) #4");
+ is(windowEventLocale["compositionend"], "",
+ kDescription + "locale of compositionend isn't empty (window) #4");
+ is(inputEventCounts["compositionend"], 1,
+ kDescription + "compositionend hasn't been handled by input #4");
+ is(inputEventData["compositionend"], "\u3089",
+ kDescription + "data of compositionend doesn't match (input) #4");
+ is(inputEventLocale["compositionend"], "",
+ kDescription + "locale of compositionend isn't empty (input) #4");
+
+ is(windowEventCounts["input"], 2,
+ kDescription + "input hasn't been handled by window #4");
+ is(windowEventData["input"], "\u3089\u3089",
+ kDescription + "value of input element wasn't modified (window) #4");
+ is(inputEventCounts["input"], 2,
+ kDescription + "input hasn't been handled by input #4");
+ is(inputEventData["input"], "\u3089\u3089",
+ kDescription + "value of input element wasn't modified (input) #4");
+
+ // preventDefault() should effect nothing.
+ preventDefault = true;
+
+ initResults();
+ synthesizeKey("A", { accelKey: true }); // Select All
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306D",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ synthesizeComposition({ type: "compositioncommitasis" });
+
+ is(windowEventCounts["compositionstart"], 1,
+ kDescription + "compositionstart hasn't been handled by window #5");
+ is(windowEventData["compositionstart"], "\u3089\u3089",
+ kDescription + "data of compositionstart is empty (window) #5");
+ is(windowEventLocale["compositionstart"], "",
+ kDescription + "locale of compositionstart isn't empty (window) #5");
+ is(inputEventCounts["compositionstart"], 1,
+ kDescription + "compositionstart hasn't been handled by input #5");
+ is(inputEventData["compositionstart"], "\u3089\u3089",
+ kDescription + "data of compositionstart is empty (input) #5");
+ is(inputEventLocale["compositionstart"], "",
+ kDescription + "locale of compositionstart isn't empty (input) #5");
+
+ is(windowEventCounts["compositionupdate"], 1,
+ kDescription + "compositionupdate hasn't been handled by window #5");
+ is(windowEventData["compositionupdate"], "\u306D",
+ kDescription + "data of compositionupdate doesn't match (window) #5");
+ is(windowEventLocale["compositionupdate"], "",
+ kDescription + "locale of compositionupdate isn't empty (window) #5");
+ is(inputEventCounts["compositionupdate"], 1,
+ kDescription + "compositionupdate hasn't been handled by input #5");
+ is(inputEventData["compositionupdate"], "\u306D",
+ kDescription + "data of compositionupdate doesn't match (input) #5");
+ is(inputEventLocale["compositionupdate"], "",
+ kDescription + "locale of compositionupdate isn't empty (input) #5");
+
+ is(windowEventCounts["compositionend"], 1,
+ kDescription + "compositionend hasn't been handled by window #5");
+ is(windowEventData["compositionend"], "\u306D",
+ kDescription + "data of compositionend doesn't match (window) #5");
+ is(windowEventLocale["compositionend"], "",
+ kDescription + "locale of compositionend isn't empty (window) #5");
+ is(inputEventCounts["compositionend"], 1,
+ kDescription + "compositionend hasn't been handled by input #5");
+ is(inputEventData["compositionend"], "\u306D",
+ kDescription + "data of compositionend doesn't match (input) #5");
+ is(inputEventLocale["compositionend"], "",
+ kDescription + "locale of compositionend isn't empty (input) #5");
+
+ is(windowEventCounts["input"], 2,
+ kDescription + "input hasn't been handled by window #5");
+ is(windowEventData["input"], "\u306D",
+ kDescription + "value of input element wasn't modified (window) #5");
+ is(inputEventCounts["input"], 2,
+ kDescription + "input hasn't been handled by input #5");
+ is(inputEventData["input"], "\u306D",
+ kDescription + "value of input element wasn't modified (input) #5");
+
+ prevnetDefault = false;
+
+ // stopPropagation() should effect nothing (except event count)
+ stopPropagation = true;
+
+ initResults();
+ synthesizeKey("A", { accelKey: true }); // Select All
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ synthesizeComposition({ type: "compositioncommitasis" });
+
+ is(windowEventCounts["compositionstart"], 1,
+ kDescription + "compositionstart hasn't been handled by window #6");
+ is(windowEventData["compositionstart"], "\u306D",
+ kDescription + "data of compositionstart is empty #6");
+ is(windowEventLocale["compositionstart"], "",
+ kDescription + "locale of compositionstart isn't empty #6");
+ is(inputEventCounts["compositionstart"], 0,
+ kDescription + "compositionstart has been handled by input #6");
+
+ is(windowEventCounts["compositionupdate"], 1,
+ kDescription + "compositionupdate hasn't been handled by window #6");
+ is(windowEventData["compositionupdate"], "\u306E",
+ kDescription + "data of compositionupdate doesn't match #6");
+ is(windowEventLocale["compositionupdate"], "",
+ kDescription + "locale of compositionupdate isn't empty #6");
+ is(inputEventCounts["compositionupdate"], 0,
+ kDescription + "compositionupdate has been handled by input #6");
+
+ is(windowEventCounts["compositionend"], 1,
+ kDescription + "compositionend hasn't been handled by window #6");
+ is(windowEventData["compositionend"], "\u306E",
+ kDescription + "data of compositionend doesn't match #6");
+ is(windowEventLocale["compositionend"], "",
+ kDescription + "locale of compositionend isn't empty #6");
+ is(inputEventCounts["compositionend"], 0,
+ kDescription + "compositionend has been handled by input #6");
+
+ is(windowEventCounts["input"], 2,
+ kDescription + "input hasn't been handled by window #6");
+ is(windowEventData["input"], "\u306E",
+ kDescription + "value of input element wasn't modified (window) #6");
+ is(inputEventCounts["input"], 2,
+ kDescription + "input hasn't been handled by input #6");
+ is(inputEventData["input"], "\u306E",
+ kDescription + "value of input element wasn't modified (input) #6");
+
+ stopPropagation = false;
+
+ // create event and dispatch it.
+ initResults();
+
+ input.value = "value of input";
+ synthesizeKey("A", { accelKey: true }); // Select All
+
+ var compositionstart = document.createEvent("CompositionEvent");
+ compositionstart.initCompositionEvent("compositionstart",
+ true, true, document.defaultView,
+ "start data", "start locale");
+ is(compositionstart.type, "compositionstart",
+ kDescription + "type doesn't match #7");
+ is(compositionstart.data, "start data",
+ kDescription + "data doesn't match #7");
+ is(compositionstart.locale, "start locale",
+ kDescription + "locale doesn't match #7");
+ is(compositionstart.detail, 0,
+ kDescription + "detail isn't 0 #7");
+
+ input.dispatchEvent(compositionstart);
+
+ is(windowEventCounts["compositionstart"], 1,
+ kDescription + "compositionstart hasn't been handled by window #7");
+ is(windowEventData["compositionstart"], "start data",
+ kDescription + "data of compositionstart was changed (window) #7");
+ is(windowEventLocale["compositionstart"], "start locale",
+ kDescription + "locale of compositionstart was changed (window) #7");
+ is(inputEventCounts["compositionstart"], 1,
+ kDescription + "compositionstart hasn't been handled by input #7");
+ is(inputEventData["compositionstart"], "start data",
+ kDescription + "data of compositionstart was changed (input) #7");
+ is(inputEventLocale["compositionstart"], "start locale",
+ kDescription + "locale of compositionstart was changed (input) #7");
+
+ is(input.value, "value of input",
+ kDescription + "input value was changed #7");
+
+ var compositionupdate1 = document.createEvent("compositionevent");
+ compositionupdate1.initCompositionEvent("compositionupdate",
+ true, false, document.defaultView,
+ "composing string", "composing locale");
+ is(compositionupdate1.type, "compositionupdate",
+ kDescription + "type doesn't match #8");
+ is(compositionupdate1.data, "composing string",
+ kDescription + "data doesn't match #8");
+ is(compositionupdate1.locale, "composing locale",
+ kDescription + "locale doesn't match #8");
+ is(compositionupdate1.detail, 0,
+ kDescription + "detail isn't 0 #8");
+
+ input.dispatchEvent(compositionupdate1);
+
+ is(windowEventCounts["compositionupdate"], 1,
+ kDescription + "compositionupdate hasn't been handled by window #8");
+ is(windowEventData["compositionupdate"], "composing string",
+ kDescription + "data of compositionupdate was changed (window) #8");
+ is(windowEventLocale["compositionupdate"], "composing locale",
+ kDescription + "locale of compositionupdate was changed (window) #8");
+ is(inputEventCounts["compositionupdate"], 1,
+ kDescription + "compositionupdate hasn't been handled by input #8");
+ is(inputEventData["compositionupdate"], "composing string",
+ kDescription + "data of compositionupdate was changed (input) #8");
+ is(inputEventLocale["compositionupdate"], "composing locale",
+ kDescription + "locale of compositionupdate was changed (input) #8");
+
+ is(input.value, "value of input",
+ kDescription + "input value was changed #8");
+
+ var compositionupdate2 = document.createEvent("compositionEvent");
+ compositionupdate2.initCompositionEvent("compositionupdate",
+ true, false, document.defaultView,
+ "commit string", "commit locale");
+ is(compositionupdate2.type, "compositionupdate",
+ kDescription + "type doesn't match #9");
+ is(compositionupdate2.data, "commit string",
+ kDescription + "data doesn't match #9");
+ is(compositionupdate2.locale, "commit locale",
+ kDescription + "locale doesn't match #9");
+ is(compositionupdate2.detail, 0,
+ kDescription + "detail isn't 0 #9");
+
+ input.dispatchEvent(compositionupdate2);
+
+ is(windowEventCounts["compositionupdate"], 2,
+ kDescription + "compositionupdate hasn't been handled by window #9");
+ is(windowEventData["compositionupdate"], "commit string",
+ kDescription + "data of compositionupdate was changed (window) #9");
+ is(windowEventLocale["compositionupdate"], "commit locale",
+ kDescription + "locale of compositionupdate was changed (window) #9");
+ is(inputEventCounts["compositionupdate"], 2,
+ kDescription + "compositionupdate hasn't been handled by input #9");
+ is(inputEventData["compositionupdate"], "commit string",
+ kDescription + "data of compositionupdate was changed (input) #9");
+ is(inputEventLocale["compositionupdate"], "commit locale",
+ kDescription + "locale of compositionupdate was changed (input) #9");
+
+ is(input.value, "value of input",
+ kDescription + "input value was changed #9");
+
+ var compositionend = document.createEvent("Compositionevent");
+ compositionend.initCompositionEvent("compositionend",
+ true, false, document.defaultView,
+ "end data", "end locale");
+ is(compositionend.type, "compositionend",
+ kDescription + "type doesn't match #10");
+ is(compositionend.data, "end data",
+ kDescription + "data doesn't match #10");
+ is(compositionend.locale, "end locale",
+ kDescription + "locale doesn't match #10");
+ is(compositionend.detail, 0,
+ kDescription + "detail isn't 0 #10");
+
+ input.dispatchEvent(compositionend);
+
+ is(windowEventCounts["compositionend"], 1,
+ kDescription + "compositionend hasn't been handled by window #10");
+ is(windowEventData["compositionend"], "end data",
+ kDescription + "data of compositionend was changed (window) #10");
+ is(windowEventLocale["compositionend"], "end locale",
+ kDescription + "locale of compositionend was changed (window) #10");
+ is(inputEventCounts["compositionend"], 1,
+ kDescription + "compositionend hasn't been handled by input #10");
+ is(inputEventData["compositionend"], "end data",
+ kDescription + "data of compositionend was changed (input) #10");
+ is(inputEventLocale["compositionend"], "end locale",
+ kDescription + "locale of compositionend was changed (input) #10");
+
+ is(input.value, "value of input",
+ kDescription + "input value was changed #10");
+
+ window.removeEventListener("compositionstart",
+ compositionEventHandlerForWindow, true);
+ window.removeEventListener("compositionend",
+ compositionEventHandlerForWindow, true);
+ window.removeEventListener("compositionupdate",
+ compositionEventHandlerForWindow, true);
+ window.removeEventListener("input",
+ formEventHandlerForWindow, true);
+
+ input.removeEventListener("compositionstart",
+ compositionEventHandlerForInput, true);
+ input.removeEventListener("compositionend",
+ compositionEventHandlerForInput, true);
+ input.removeEventListener("compositionupdate",
+ compositionEventHandlerForInput, true);
+ input.removeEventListener("input",
+ formEventHandlerForInput, true);
+}
+
+function runQueryTextRectInContentEditableTest()
+{
+ contenteditable.focus();
+
+ contenteditable.innerHTML = "<p>abc</p><p>def</p>";
+ // \n 0 123 4 567
+ // \r\n 01 234 56 789
+
+ var description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
+
+ // "a"
+ var a = synthesizeQueryTextRect(kLFLen, 1);
+ if (!checkQueryContentResult(a, description + "rect for 'a'")) {
+ return;
+ }
+
+ // "b"
+ var b = synthesizeQueryTextRect(kLFLen + 1, 1);
+ if (!checkQueryContentResult(b, description + "rect for 'b'")) {
+ return;
+ }
+
+ is(b.top, a.top, description + "'a' and 'b' should be at same top");
+ isSimilarTo(b.left, a.left + a.width, 2, description + "left of 'b' should be at similar to right of 'a'");
+ is(b.height, a.height, description + "'a' and 'b' should be same height");
+
+ // "c"
+ var c = synthesizeQueryTextRect(kLFLen + 2, 1);
+ if (!checkQueryContentResult(c, description + "rect for 'c'")) {
+ return;
+ }
+
+ is(c.top, b.top, description + "'b' and 'c' should be at same top");
+ isSimilarTo(c.left, b.left + b.width, 2, description + "left of 'c' should be at similar to right of 'b'");
+ is(c.height, b.height, description + "'b' and 'c' should be same height");
+
+ // "abc" as array
+ var abcAsArray = synthesizeQueryTextRectArray(kLFLen, 3);
+ if (!checkQueryContentResult(abcAsArray, description + "rect array for 'abc'") ||
+ !checkRectArray(abcAsArray, [a, b, c], description + "query text rect array result of 'abc' should match with each query text rect result")) {
+ return;
+ }
+
+ // 2nd <p> (can be computed with the rect of 'c')
+ var p2 = synthesizeQueryTextRect(kLFLen + 3, 1);
+ if (!checkQueryContentResult(p2, description + "rect for 2nd <p>")) {
+ return;
+ }
+
+ is(p2.top, c.top, description + "'c' and a line breaker caused by 2nd <p> should be at same top");
+ isSimilarTo(p2.left, c.left + c.width, 2, description + "left of a line breaker caused by 2nd <p> should be at similar to right of 'c'");
+ is(p2.height, c.height, description + "'c' and a line breaker caused by 2nd <p> should be same height");
+
+ // 2nd <p> as array
+ var p2AsArray = synthesizeQueryTextRectArray(kLFLen + 3, 1);
+ if (!checkQueryContentResult(p2AsArray, description + "2nd <p>'s line breaker as array") ||
+ !checkRectArray(p2AsArray, [p2], description + "query text rect array result of 2nd <p> should match with each query text rect result")) {
+ return;
+ }
+
+ if (kLFLen > 1) {
+ // \n of \r\n
+ var p2_2 = synthesizeQueryTextRect(kLFLen + 4, 1);
+ if (!checkQueryContentResult(p2_2, description + "rect for \\n of \\r\\n caused by 2nd <p>")) {
+ return;
+ }
+
+ is(p2_2.top, p2.top, description + "'\\r' and '\\n' should be at same top");
+ is(p2_2.left, p2.left, description + "'\\r' and '\\n' should be at same top");
+ is(p2_2.height, p2.height, description + "'\\r' and '\\n' should be same height");
+ is(p2_2.width, p2.width, description + "'\\r' and '\\n' should be same width");
+
+ // \n of \r\n as array
+ var p2_2AsArray = synthesizeQueryTextRectArray(kLFLen + 4, 1);
+ if (!checkQueryContentResult(p2_2AsArray, description + "rect array for \\n of \\r\\n caused by 2nd <p>") ||
+ !checkRectArray(p2_2AsArray, [p2_2], description + "query text rect array result of \\n of \\r\\n caused by 2nd <p> should match with each query text rect result")) {
+ return;
+ }
+ }
+
+ // "d"
+ var d = synthesizeQueryTextRect(kLFLen * 2 + 3, 1);
+ if (!checkQueryContentResult(d, description + "rect for 'd'")) {
+ return;
+ }
+
+ isGreaterThan(d.top, a.top + a.height, description + "top of 'd' should be greater than bottom of 'a'");
+ is(d.left, a.left, description + "'a' and 'd' should be same at same left");
+ is(d.height, a.height, description + "'a' and 'd' should be same height");
+
+ // "e"
+ var e = synthesizeQueryTextRect(kLFLen * 2 + 4, 1);
+ if (!checkQueryContentResult(e, description + "rect for 'e'")) {
+ return;
+ }
+
+ is(e.top, d.top, description + "'d' and 'd' should be at same top");
+ isSimilarTo(e.left, d.left + d.width, 2, description + "left of 'e' should be at similar to right of 'd'");
+ is(e.height, d.height, description + "'d' and 'e' should be same height");
+
+ // "f"
+ var f = synthesizeQueryTextRect(kLFLen * 2 + 5, 1);
+ if (!checkQueryContentResult(f, description + "rect for 'f'")) {
+ return;
+ }
+
+ is(f.top, e.top, description + "'e' and 'f' should be at same top");
+ isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
+ is(f.height, e.height, description + "'e' and 'f' should be same height");
+
+ // "def" as array
+ var defAsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 3, 3);
+ if (!checkQueryContentResult(defAsArray, description + "rect array for 'def'") ||
+ !checkRectArray(defAsArray, [d, e, f], description + "query text rect array result of 'def' should match with each query text rect result")) {
+ return;
+ }
+
+ // next of "f" (can be computed with rect of 'f')
+ var next_f = synthesizeQueryTextRect(kLFLen * 2 + 6, 1);
+ if (!checkQueryContentResult(next_f, description + "rect for next of 'f'")) {
+ return;
+ }
+
+ is(next_f.top, d.top, 2, description + "'f' and next of 'f' should be at same top");
+ isSimilarTo(next_f.left, f.left + f.width, 2, description + "left of next of 'f' should be at similar to right of 'f'");
+ is(next_f.height, d.height, description + "'f' and next of 'f' should be same height");
+
+ // next of "f" as array
+ var next_fAsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 6, 1);
+ if (!checkQueryContentResult(next_fAsArray, description + "rect array for next of 'f'") ||
+ !checkRectArray(next_fAsArray, [next_f], description + "query text rect array result of next of 'f' should match with each query text rect result")) {
+ return;
+ }
+
+ // too big offset for the editor
+ var tooBigOffset = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
+ if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
+ return;
+ }
+
+ is(tooBigOffset.top, next_f.top, description + "too big offset and next of 'f' should be at same top");
+ is(tooBigOffset.left, next_f.left, description + "too big offset and next of 'f' should be at same left");
+ is(tooBigOffset.height, next_f.height, description + "too big offset and next of 'f' should be same height");
+ is(tooBigOffset.width, next_f.width, description + "too big offset and next of 'f' should be same width");
+
+ // too big offset for the editors as array
+ var tooBigOffsetAsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 7, 1);
+ if (!checkQueryContentResult(tooBigOffsetAsArray, description + "rect array for too big offset") ||
+ !checkRectArray(tooBigOffsetAsArray, [tooBigOffset], description + "query text rect array result with too big offset should match with each query text rect result")) {
+ return;
+ }
+
+ contenteditable.innerHTML = "<p>abc</p><p>def</p><p><br></p>";
+ // \n 0 123 4 567 8 9
+ // \r\n 01 234 56 789 01 23
+
+ description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
+
+ // "f"
+ f = synthesizeQueryTextRect(kLFLen * 2 + 5, 1);
+ if (!checkQueryContentResult(f, description + "rect for 'f'")) {
+ return;
+ }
+
+ is(f.top, e.top, description + "'e' and 'f' should be at same top");
+ is(f.height, e.height, description + "'e' and 'f' should be same height");
+ isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
+
+ // 3rd <p> (can be computed with rect of 'f')
+ var p3 = synthesizeQueryTextRect(kLFLen * 2 + 6, 1);
+ if (!checkQueryContentResult(p3, description + "rect for 3rd <p>")) {
+ return;
+ }
+
+ is(p3.top, f.top, description + "'f' and a line breaker caused by 3rd <p> should be at same top");
+ is(p3.height, f.height, description + "'f' and a line breaker caused by 3rd <p> should be same height");
+ isSimilarTo(p3.left, f.left + f.width, 2, description + "left of a line breaker caused by 3rd <p> should be similar to right of 'f'");
+
+ // 3rd <p> as array
+ var p3AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 6, 1);
+ if (!checkQueryContentResult(p3AsArray, description + "3rd <p>'s line breaker as array") ||
+ !checkRectArray(p3AsArray, [p3], description + "query text rect array result of 3rd <p> should match with each query text rect result")) {
+ return;
+ }
+
+ if (kLFLen > 1) {
+ // \n of \r\n
+ var p3_2 = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
+ if (!checkQueryContentResult(p3_2, description + "rect for \\n of \\r\\n caused by 3rd <p>")) {
+ return;
+ }
+
+ is(p3_2.top, p3.top, description + "'\\r' and '\\n' should be at same top");
+ is(p3_2.left, p3.left, description + "'\\r' and '\\n' should be at same top");
+ is(p3_2.height, p3.height, description + "'\\r' and '\\n' should be same height");
+ is(p3_2.width, p3.width, description + "'\\r' and '\\n' should be same width");
+
+ // \n of \r\n as array
+ var p3_2AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 7, 1);
+ if (!checkQueryContentResult(p3_2AsArray, description + "rect array for \\n of \\r\\n caused by 3rd <p>") ||
+ !checkRectArray(p3_2AsArray, [p3_2], description + "query text rect array result of \\n of \\r\\n caused by 3rd <p> should match with each query text rect result")) {
+ return;
+ }
+ }
+
+ // <br> in 3rd <p>
+ var br = synthesizeQueryTextRect(kLFLen * 3 + 6, 1);
+ if (!checkQueryContentResult(br, description + "rect for <br> in 3rd <p>")) {
+ return;
+ }
+
+ isGreaterThan(br.top, d.top + d.height, description + "a line breaker caused by <br> in 3rd <p> should be greater than bottom of 'd'");
+ isSimilarTo(br.height, d.height, 2, description + "'d' and a line breaker caused by <br> in 3rd <p> should be similar height");
+ is(br.left, d.left, description + "left of a line breaker caused by <br> in 3rd <p> should be same left of 'd'");
+
+ // <br> in 3rd <p> as array
+ var brAsArray = synthesizeQueryTextRectArray(kLFLen * 3 + 6, 1);
+ if (!checkQueryContentResult(brAsArray, description + "<br> in 3rd <p> as array") ||
+ !checkRectArray(brAsArray, [br], description + "query text rect array result of <br> in 3rd <p> should match with each query text rect result")) {
+ return;
+ }
+
+ if (kLFLen > 1) {
+ // \n of \r\n
+ var br_2 = synthesizeQueryTextRect(kLFLen * 3 + 7, 1);
+ if (!checkQueryContentResult(br_2, description + "rect for \\n of \\r\\n caused by <br> in 3rd <p>")) {
+ return;
+ }
+
+ is(br_2.top, br.top, description + "'\\r' and '\\n' should be at same top");
+ is(br_2.left, br.left, description + "'\\r' and '\\n' should be at same top");
+ is(br_2.height, br.height, description + "'\\r' and '\\n' should be same height");
+ is(br_2.width, br.width, description + "'\\r' and '\\n' should be same width");
+
+ // \n of \r\n as array
+ var br_2AsArray = synthesizeQueryTextRectArray(kLFLen * 3 + 7, 1);
+ if (!checkQueryContentResult(br_2AsArray, description + "rect array for \\n of \\r\\n caused by <br> in 3rd <p>") ||
+ !checkRectArray(br_2AsArray, [br_2], description + "query text rect array result of \\n of \\r\\n caused by <br> in 3rd <p> should match with each query text rect result")) {
+ return;
+ }
+ }
+
+ // next of <br> in 3rd <p>
+ var next_br = synthesizeQueryTextRect(kLFLen * 4 + 6, 1);
+ if (!checkQueryContentResult(next_br, description + "rect for next of <br> in 3rd <p>")) {
+ return;
+ }
+
+ is(next_br.top, br.top, description + "next of <br> and <br> should be at same top");
+ is(next_br.left, br.left, description + "next of <br> and <br> should be at same left");
+ is(next_br.height, br.height, description + "next of <br> and <br> should be same height");
+ is(next_br.width, br.width, description + "next of <br> and <br> should be same width");
+
+ // next of <br> in 3rd <p> as array
+ var next_brAsArray = synthesizeQueryTextRectArray(kLFLen * 4 + 6, 1);
+ if (!checkQueryContentResult(next_brAsArray, description + "rect array for next of <br> in 3rd <p>") ||
+ !checkRectArray(next_brAsArray, [next_br], description + "query text rect array result of next of <br> in 3rd <p> should match with each query text rect result")) {
+ return;
+ }
+
+ // too big offset for the editor
+ tooBigOffset = synthesizeQueryTextRect(kLFLen * 4 + 7, 1);
+ if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
+ return;
+ }
+
+ is(tooBigOffset.top, next_br.top, description + "too big offset and next of 3rd <p> should be at same top");
+ is(tooBigOffset.left, next_br.left, description + "too big offset and next of 3rd <p> should be at same left");
+ is(tooBigOffset.height, next_br.height, description + "too big offset and next of 3rd <p> should be same height");
+ is(tooBigOffset.width, next_br.width, description + "too big offset and next of 3rd <p> should be same width");
+
+ // too big offset for the editors as array
+ tooBigOffsetAsArray = synthesizeQueryTextRectArray(kLFLen * 4 + 7, 1);
+ if (!checkQueryContentResult(tooBigOffsetAsArray, description + "rect array for too big offset") ||
+ !checkRectArray(tooBigOffsetAsArray, [tooBigOffset], description + "query text rect array result with too big offset should match with each query text rect result")) {
+ return;
+ }
+
+ contenteditable.innerHTML = "<p>abc</p><p>def</p><p></p>";
+ // \n 0 123 4 567 8
+ // \r\n 01 234 56 789 0
+
+ description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
+
+ // "f"
+ f = synthesizeQueryTextRect(kLFLen * 2 + 5, 1);
+ if (!checkQueryContentResult(f, description + "rect for 'f'")) {
+ return;
+ }
+
+ is(f.top, e.top, description + "'e' and 'f' should be at same top");
+ isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
+ is(f.height, e.height, description + "'e' and 'f' should be same height");
+
+ // 3rd <p> (can be computed with rect of 'f')
+ p3 = synthesizeQueryTextRect(kLFLen * 2 + 6, 1);
+ if (!checkQueryContentResult(p3, description + "rect for 3rd <p>")) {
+ return;
+ }
+
+ is(p3.top, f.top, description + "'f' and a line breaker caused by 3rd <p> should be at same top");
+ is(p3.height, f.height, description + "'f' and a line breaker caused by 3rd <p> should be same height");
+ isSimilarTo(p3.left, f.left + f.width, 2, description + "left of a line breaker caused by 3rd <p> should be similar to right of 'f'");
+
+ // 3rd <p> as array
+ p3AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 6, 1);
+ if (!checkQueryContentResult(p3AsArray, description + "3rd <p>'s line breaker as array") ||
+ !checkRectArray(p3AsArray, [p3], description + "query text rect array result of 3rd <p> should match with each query text rect result")) {
+ return;
+ }
+
+ if (kLFLen > 1) {
+ // \n of \r\n
+ p3_2 = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
+ if (!checkQueryContentResult(p3_2, description + "rect for \\n of \\r\\n caused by 3rd <p>")) {
+ return;
+ }
+
+ is(p3_2.top, p3.top, description + "'\\r' and '\\n' should be at same top");
+ is(p3_2.left, p3.left, description + "'\\r' and '\\n' should be at same top");
+ is(p3_2.height, p3.height, description + "'\\r' and '\\n' should be same height");
+ is(p3_2.width, p3.width, description + "'\\r' and '\\n' should be same width");
+
+ // \n of \r\n as array
+ p3_2AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 7, 1);
+ if (!checkQueryContentResult(p3_2AsArray, description + "rect array for \\n of \\r\\n caused by 3rd <p>") ||
+ !checkRectArray(p3_2AsArray, [p3_2], description + "query text rect array result of \\n of \\r\\n caused by 3rd <p> should match with each query text rect result")) {
+ return;
+ }
+ }
+
+ // next of 3rd <p>
+ var next_p3 = synthesizeQueryTextRect(kLFLen * 3 + 6, 1);
+ if (!checkQueryContentResult(next_p3, description + "rect for next of 3rd <p>")) {
+ return;
+ }
+
+ isGreaterThan(next_p3.top, d.top + d.height, description + "top of next of 3rd <p> should equal to or be bigger than bottom of 'd'");
+ isSimilarTo(next_p3.left, d.left, 2, description + "left of next of 3rd <p> should be at similar to left of 'd'");
+ isSimilarTo(next_p3.height, d.height, 2, description + "next of 3rd <p> and 'd' should be similar height");
+
+ // next of 3rd <p> as array
+ var next_p3AsArray = synthesizeQueryTextRectArray(kLFLen * 3 + 6, 1);
+ if (!checkQueryContentResult(next_p3AsArray, description + "next of 3rd <p> as array") ||
+ !checkRectArray(next_p3AsArray, [next_p3], description + "query text rect array result of next of 3rd <p> should match with each query text rect result")) {
+ return;
+ }
+
+ // too big offset for the editor
+ tooBigOffset = synthesizeQueryTextRect(kLFLen * 3 + 7, 1);
+ if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
+ return;
+ }
+
+ is(tooBigOffset.top, next_p3.top, description + "too big offset and next of 3rd <p> should be at same top");
+ is(tooBigOffset.left, next_p3.left, description + "too big offset and next of 3rd <p> should be at same left");
+ is(tooBigOffset.height, next_p3.height, description + "too big offset and next of 3rd <p> should be same height");
+ is(tooBigOffset.width, next_p3.width, description + "too big offset and next of 3rd <p> should be same width");
+
+ // too big offset for the editors as array
+ tooBigOffsetAsArray = synthesizeQueryTextRectArray(kLFLen * 3 + 7, 1);
+ if (!checkQueryContentResult(tooBigOffsetAsArray, description + "rect array for too big offset") ||
+ !checkRectArray(tooBigOffsetAsArray, [tooBigOffset], description + "query text rect array result with too big offset should match with each query text rect result")) {
+ return;
+ }
+
+ contenteditable.innerHTML = "abc<br>def";
+ // \n 0123 456
+ // \r\n 01234 567
+
+ description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
+
+ // "a"
+ a = synthesizeQueryTextRect(0, 1);
+ if (!checkQueryContentResult(a, description + "rect for 'a'")) {
+ return;
+ }
+
+ // "b"
+ b = synthesizeQueryTextRect(1, 1);
+ if (!checkQueryContentResult(b, description + "rect for 'b'")) {
+ return;
+ }
+
+ is(b.top, a.top, description + "'a' and 'b' should be at same top");
+ isSimilarTo(b.left, a.left + a.width, 2, description + "left of 'b' should be at similar to right of 'a'");
+ is(b.height, a.height, description + "'a' and 'b' should be same height");
+
+ // "c"
+ c = synthesizeQueryTextRect(2, 1);
+ if (!checkQueryContentResult(c, description + "rect for 'c'")) {
+ return;
+ }
+
+ is(c.top, b.top, description + "'b' and 'c' should be at same top");
+ isSimilarTo(c.left, b.left + b.width, 2, description + "left of 'c' should be at similar to right of 'b'");
+ is(c.height, b.height, description + "'b' and 'c' should be same height");
+
+ // "abc" as array
+ abcAsArray = synthesizeQueryTextRectArray(0, 3);
+ if (!checkQueryContentResult(abcAsArray, description + "rect array for 'abc'") ||
+ !checkRectArray(abcAsArray, [a, b, c], description + "query text rect array result of 'abc' should match with each query text rect result")) {
+ return;
+ }
+
+ // <br> (can be computed with the rect of 'c')
+ br = synthesizeQueryTextRect(3, 1);
+ if (!checkQueryContentResult(br, description + "rect for <br>")) {
+ return;
+ }
+
+ is(br.top, c.top, description + "'c' and a line breaker caused by <br> should be at same top");
+ isSimilarTo(br.left, c.left + c.width, 2, description + "left of a line breaker caused by <br> should be at similar to right of 'c'");
+ is(br.height, c.height, description + "'c' and a line breaker caused by <br> should be same height");
+
+ // <br> as array
+ brAsArray = synthesizeQueryTextRectArray(3, 1);
+ if (!checkQueryContentResult(brAsArray, description + "<br>'s line breaker as array") ||
+ !checkRectArray(brAsArray, [br], description + "query text rect array result of <br> should match with each query text rect result")) {
+ return;
+ }
+
+ if (kLFLen > 1) {
+ // \n of \r\n
+ var br_2 = synthesizeQueryTextRect(4, 1);
+ if (!checkQueryContentResult(br_2, description + "rect for \n of \r\n caused by <br>")) {
+ return;
+ }
+
+ is(br_2.top, br.top, description + "'\\r' and '\\n' should be at same top");
+ is(br_2.left, br.left, description + "'\\r' and '\\n' should be at same top");
+ is(br_2.height, br.height, description + "'\\r' and '\\n' should be same height");
+ is(br_2.width, br.width, description + "'\\r' and '\\n' should be same width");
+
+ // \n of \r\n as array
+ var br_2AsArray = synthesizeQueryTextRectArray(4, 1);
+ if (!checkQueryContentResult(br_2AsArray, description + "rect array for \\n of \\r\\n caused by <br>") ||
+ !checkRectArray(br_2AsArray, [br_2], description + "query text rect array result of \\n of \\r\\n caused by <br> should match with each query text rect result")) {
+ return;
+ }
+ }
+
+ // "d"
+ d = synthesizeQueryTextRect(kLFLen + 3, 1);
+ if (!checkQueryContentResult(d, description + "rect for 'd'")) {
+ return;
+ }
+
+ isSimilarTo(d.top, a.top + a.height, 2, description + "top of 'd' should be at similar to bottom of 'a'");
+ is(d.left, a.left, description + "'a' and 'd' should be same at same left");
+ is(d.height, a.height, description + "'a' and 'd' should be same height");
+
+ // "e"
+ e = synthesizeQueryTextRect(kLFLen + 4, 1);
+ if (!checkQueryContentResult(e, description + "rect for 'e'")) {
+ return;
+ }
+
+ is(e.top, d.top, description + "'d' and 'd' should be at same top");
+ isSimilarTo(e.left, d.left + d.width, 2, description + "left of 'e' should be at similar to right of 'd'");
+ is(e.height, d.height, description + "'d' and 'e' should be same height");
+
+ // "f"
+ f = synthesizeQueryTextRect(kLFLen + 5, 1);
+ if (!checkQueryContentResult(f, description + "rect for 'f'")) {
+ return;
+ }
+
+ is(f.top, e.top, description + "'e' and 'f' should be at same top");
+ isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
+ is(f.height, e.height, description + "'e' and 'f' should be same height");
+
+ // "def" as array
+ defAsArray = synthesizeQueryTextRectArray(kLFLen + 3, 3);
+ if (!checkQueryContentResult(defAsArray, description + "rect array for 'def'") ||
+ !checkRectArray(defAsArray, [d, e, f], description + "query text rect array result of 'def' should match with each query text rect result")) {
+ return;
+ }
+
+ // next of "f" (can be computed with rect of 'f')
+ next_f = synthesizeQueryTextRect(kLFLen + 6, 1);
+ if (!checkQueryContentResult(next_f, description + "rect for next of 'f'")) {
+ return;
+ }
+
+ is(next_f.top, d.top, 2, description + "'f' and next of 'f' should be at same top");
+ isSimilarTo(next_f.left, f.left + f.width, 2, description + "left of next of 'f' should be at similar to right of 'f'");
+ is(next_f.height, d.height, description + "'f' and next of 'f' should be same height");
+
+ // next of "f" as array
+ next_fAsArray = synthesizeQueryTextRectArray(kLFLen + 6, 1);
+ if (!checkQueryContentResult(next_fAsArray, description + "rect array for next of 'f'") ||
+ !checkRectArray(next_fAsArray, [next_f], description + "query text rect array result of next of 'f' should match with each query text rect result")) {
+ return;
+ }
+
+ // too big offset for the editor
+ tooBigOffset = synthesizeQueryTextRect(kLFLen + 7, 1);
+ if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
+ return;
+ }
+
+ is(tooBigOffset.top, next_f.top, description + "too big offset and next of 'f' should be at same top");
+ is(tooBigOffset.left, next_f.left, description + "too big offset and next of 'f' should be at same left");
+ is(tooBigOffset.height, next_f.height, description + "too big offset and next of 'f' should be same height");
+ is(tooBigOffset.width, next_f.width, description + "too big offset and next of 'f' should be same width");
+
+ // too big offset for the editors as array
+ tooBigOffsetAsArray = synthesizeQueryTextRectArray(kLFLen + 7, 1);
+ if (!checkQueryContentResult(tooBigOffsetAsArray, description + "rect array for too big offset") ||
+ !checkRectArray(tooBigOffsetAsArray, [tooBigOffset], description + "query text rect array result with too big offset should match with each query text rect result")) {
+ return;
+ }
+
+ // Note that this case does not have an empty line at the end.
+ contenteditable.innerHTML = "abc<br>def<br>";
+ // \n 0123 4567
+ // \r\n 01234 56789
+
+ description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
+
+ // "f"
+ f = synthesizeQueryTextRect(kLFLen + 5, 1);
+ if (!checkQueryContentResult(f, description + "rect for 'f'")) {
+ return;
+ }
+
+ is(f.top, e.top, description + "'e' and 'f' should be at same top");
+ is(f.height, e.height, description + "'e' and 'f' should be same height");
+ isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
+
+ // 2nd <br> (can be computed with rect of 'f')
+ var br2 = synthesizeQueryTextRect(kLFLen + 6, 1);
+ if (!checkQueryContentResult(br2, description + "rect for 2nd <br>")) {
+ return;
+ }
+
+ is(br2.top, f.top, description + "'f' and a line breaker caused by 2nd <br> should be at same top");
+ is(br2.height, f.height, description + "'f' and a line breaker caused by 2nd <br> should be same height");
+ isSimilarTo(br2.left, f.left + f.width, 2, description + "left of a line breaker caused by 2nd <br> should be similar to right of 'f'");
+
+ // 2nd <br> as array
+ var br2AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 6, 1);
+ if (!checkQueryContentResult(br2AsArray, description + "2nd <br>'s line breaker as array") ||
+ !checkRectArray(br2AsArray, [br2], description + "query text rect array result of 2nd <br> should match with each query text rect result")) {
+ return;
+ }
+
+ if (kLFLen > 1) {
+ // \n of \r\n
+ var br2_2 = synthesizeQueryTextRect(kLFLen + 7, 1);
+ if (!checkQueryContentResult(br2_2, description + "rect for \\n of \\r\\n caused by 2nd <br>")) {
+ return;
+ }
+
+ is(br2_2.top, br2.top, description + "'\\r' and '\\n' should be at same top");
+ is(br2_2.left, br2.left, description + "'\\r' and '\\n' should be at same top");
+ is(br2_2.height, br2.height, description + "'\\r' and '\\n' should be same height");
+ is(br2_2.width, br2.width, description + "'\\r' and '\\n' should be same width");
+
+ // \n of \r\n as array
+ var br2_2AsArray = synthesizeQueryTextRectArray(kLFLen + 7, 1);
+ if (!checkQueryContentResult(br2_2AsArray, description + "rect array for \\n of \\r\\n caused by 2nd <br>") ||
+ !checkRectArray(br2_2AsArray, [br2_2], description + "query text rect array result of \\n of \\r\\n caused by 2nd <br> should match with each query text rect result")) {
+ return;
+ }
+ }
+
+ // next of 2nd <br>
+ var next_br2 = synthesizeQueryTextRect(kLFLen * 2 + 6, 1);
+ if (!checkQueryContentResult(next_br2, description + "rect for next of 2nd <br>")) {
+ return;
+ }
+
+ is(next_br2.top, br2.top, description + "2nd <br> and next of 2nd <br> should be at same top");
+ is(next_br2.left, br2.left, description + "2nd <br> and next of 2nd <br> should be at same top");
+ is(next_br2.height, br2.height, description + "2nd <br> and next of 2nd <br> should be same height");
+ is(next_br2.width, br2.width, description + "2nd <br> and next of 2nd <br> should be same width");
+
+ // next of 2nd <br> as array
+ var next_br2AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 6, 1);
+ if (!checkQueryContentResult(next_br2AsArray, description + "rect array for next of 2nd <br>") ||
+ !checkRectArray(next_br2AsArray, [next_br2], description + "query text rect array result of next of 2nd <br> should match with each query text rect result")) {
+ return;
+ }
+
+ // too big offset for the editor
+ tooBigOffset = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
+ if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
+ return;
+ }
+
+ is(tooBigOffset.top, next_br2.top, description + "too big offset and next of 2nd <br> should be at same top");
+ is(tooBigOffset.left, next_br2.left, description + "too big offset and next of 2nd <br> should be at same left");
+ is(tooBigOffset.height, next_br2.height, description + "too big offset and next of 2nd <br> should be same height");
+ is(tooBigOffset.width, next_br2.width, description + "too big offset and next of 2nd <br> should be same width");
+
+ // too big offset for the editors as array
+ tooBigOffsetAsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 7, 1);
+ if (!checkQueryContentResult(tooBigOffsetAsArray, description + "rect array for too big offset") ||
+ !checkRectArray(tooBigOffsetAsArray, [tooBigOffset], description + "query text rect array result with too big offset should match with each query text rect result")) {
+ return;
+ }
+
+ contenteditable.innerHTML = "abc<br>def<br><br>";
+ // \n 0123 4567 8
+ // \r\n 01234 56789 01
+
+ description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
+
+ // "f"
+ f = synthesizeQueryTextRect(kLFLen + 5, 1);
+ if (!checkQueryContentResult(f, description + "rect for 'f'")) {
+ return;
+ }
+
+ is(f.top, e.top, description + "'e' and 'f' should be at same top");
+ isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
+ is(f.height, e.height, description + "'e' and 'f' should be same height");
+
+ // 2nd <br>
+ br2 = synthesizeQueryTextRect(kLFLen + 6, 1);
+ if (!checkQueryContentResult(br2, description + "rect for 2nd <br>")) {
+ return;
+ }
+
+ is(br2.top, f.top, description + "'f' and a line breaker caused by 2nd <br> should be at same top");
+ is(br2.height, f.height, description + "'f' and a line breaker caused by 2nd <br> should be same height");
+ ok(f.left < br2.left, description + "left of a line breaker caused by 2nd <br> should be bigger than left of 'f', f.left=" + f.left + ", br2.left=" + br2.left);
+
+ // 2nd <br> as array
+ br2AsArray = synthesizeQueryTextRectArray(kLFLen + 6, 1);
+ if (!checkQueryContentResult(br2AsArray, description + "2nd <br>'s line breaker as array") ||
+ !checkRectArray(br2AsArray, [br2], description + "query text rect array result of 2nd <br> should match with each query text rect result")) {
+ return;
+ }
+
+ if (kLFLen > 1) {
+ // \n of \r\n
+ br2_2 = synthesizeQueryTextRect(kLFLen + 7, 1);
+ if (!checkQueryContentResult(br2_2, description + "rect for \\n of \\r\\n caused by 2nd <br>")) {
+ return;
+ }
+
+ is(br2_2.top, br2.top, description + "'\\r' and '\\n' should be at same top");
+ is(br2_2.left, br2.left, description + "'\\r' and '\\n' should be at same top");
+ is(br2_2.height, br2.height, description + "'\\r' and '\\n' should be same height");
+ is(br2_2.width, br2.width, description + "'\\r' and '\\n' should be same width");
+
+ // \n of \r\n as array
+ var br2_2AsArray = synthesizeQueryTextRectArray(kLFLen + 7, 1);
+ if (!checkQueryContentResult(br2_2AsArray, description + "rect array for \\n of \\r\\n caused by 2nd <br>") ||
+ !checkRectArray(br2_2AsArray, [br2_2], description + "query text rect array result of \\n of \\r\\n caused by 2nd <br> should match with each query text rect result")) {
+ return;
+ }
+ }
+
+ // 3rd <br>
+ var br3 = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
+ if (!checkQueryContentResult(br3, description + "rect for next of 3rd <br>")) {
+ return;
+ }
+
+ isSimilarTo(br3.top, d.top + d.height, 3, description + "top of next of 3rd <br> should at similar to bottom of 'd'");
+ isSimilarTo(br3.left, d.left, 2, description + "left of next of 3rd <br> should be at similar to left of 'd'");
+ isSimilarTo(br3.height, d.height, 2, description + "next of 3rd <br> and 'd' should be similar height");
+
+ // 3rd <br> as array
+ var br3AsArray = synthesizeQueryTextRectArray(kLFLen + 6, 1);
+ if (!checkQueryContentResult(br3AsArray, description + "3rd <br>'s line breaker as array") ||
+ !checkRectArray(br3AsArray, [br3], description + "query text rect array result of 3rd <br> should match with each query text rect result")) {
+ return;
+ }
+
+ if (kLFLen > 1) {
+ // \n of \r\n
+ var br3_2 = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
+ if (!checkQueryContentResult(br3_2, description + "rect for \\n of \\r\\n caused by 3rd <br>")) {
+ return;
+ }
+
+ is(br3_2.top, br3.top, description + "'\\r' and '\\n' should be at same top");
+ is(br3_2.left, br3.left, description + "'\\r' and '\\n' should be at same left");
+ is(br3_2.height, br3.height, description + "'\\r' and '\\n' should be same height");
+ is(br3_2.width, br3.width, description + "'\\r' and '\\n' should be same width");
+
+ // \n of \r\n as array
+ var br2_2AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 7, 1);
+ if (!checkQueryContentResult(br2_2AsArray, description + "rect array for \\n of \\r\\n caused by 3rd <br>") ||
+ !checkRectArray(br2_2AsArray, [br2_2], description + "query text rect array result of \\n of \\r\\n caused by 3rd <br> should match with each query text rect result")) {
+ return;
+ }
+ }
+
+ // next of 3rd <br>
+ var next_br3 = synthesizeQueryTextRect(kLFLen * 3 + 6, 1);
+ if (!checkQueryContentResult(next_br3, description + "rect for next of 3rd <br>")) {
+ return;
+ }
+
+ is(next_br3.top, br3.top, description + "3rd <br> and next of 3rd <br> should be at same top");
+ is(next_br3.left, br3.left, description + "3rd <br> and next of 3rd <br> should be at same left");
+ is(next_br3.height, br3.height, description + "3rd <br> and next of 3rd <br> should be same height");
+ is(next_br3.width, br3.width, description + "3rd <br> and next of 3rd <br> should be same width");
+
+ // next of 3rd <br> as array
+ var next_br3AsArray = synthesizeQueryTextRectArray(kLFLen * 3 + 6, 1);
+ if (!checkQueryContentResult(next_br3AsArray, description + "rect array for next of 3rd <br>") ||
+ !checkRectArray(next_br3AsArray, [next_br3], description + "query text rect array result of next of 3rd <br> should match with each query text rect result")) {
+ return;
+ }
+
+ // too big offset for the editor
+ tooBigOffset = synthesizeQueryTextRect(kLFLen * 3 + 7, 1);
+ if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
+ return;
+ }
+
+ is(tooBigOffset.top, next_br3.top, description + "too big offset and next of 3rd <br> should be at same top");
+ is(tooBigOffset.left, next_br3.left, description + "too big offset and next of 3rd <br> should be at same left");
+ is(tooBigOffset.height, next_br3.height, description + "too big offset and next of 3rd <br> should be same height");
+ is(tooBigOffset.width, next_br3.width, description + "too big offset and next of 3rd <br> should be same width");
+
+ // too big offset for the editors as array
+ tooBigOffsetAsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 7, 1);
+ if (!checkQueryContentResult(tooBigOffsetAsArray, description + "rect array for too big offset") ||
+ !checkRectArray(tooBigOffsetAsArray, [tooBigOffset], description + "query text rect array result with too big offset should match with each query text rect result")) {
+ return;
+ }
+}
+
+function runCharAtPointTest(aFocusedEditor, aTargetName)
+{
+ aFocusedEditor.value = "This is a test of the\nContent Events";
+ // 012345678901234567890 12345678901234
+ // 0 1 2 3
+
+ aFocusedEditor.focus();
+
+ const kNone = -1;
+ const kTestingOffset = [ 0, 10, 20, 21 + kLFLen, 34 + kLFLen];
+ const kLeftSideOffset = [ kNone, 9, 19, kNone, 33 + kLFLen];
+ const kRightSideOffset = [ 1, 11, kNone, 22 + kLFLen, kNone];
+ const kLeftTentativeCaretOffset = [ 0, 10, 20, 21 + kLFLen, 34 + kLFLen];
+ const kRightTentativeCaretOffset = [ 1, 11, 21, 22 + kLFLen, 35 + kLFLen];
+
+ var editorRect = synthesizeQueryEditorRect();
+ if (!checkQueryContentResult(editorRect,
+ "runCharAtPointTest (" + aTargetName + "): editorRect")) {
+ return;
+ }
+
+ for (var i = 0; i < kTestingOffset.length; i++) {
+ var textRect = synthesizeQueryTextRect(kTestingOffset[i], 1);
+ if (!checkQueryContentResult(textRect,
+ "runCharAtPointTest (" + aTargetName + "): textRect: i=" + i)) {
+ continue;
+ }
+
+ checkRectContainsRect(textRect, editorRect,
+ "runCharAtPointTest (" + aTargetName +
+ "): the text rect isn't in the editor");
+
+ // Test #1, getting same character rect by the point near the top-left.
+ var charAtPt1 = synthesizeCharAtPoint(textRect.left + 1,
+ textRect.top + 1);
+ if (checkQueryContentResult(charAtPt1,
+ "runCharAtPointTest (" + aTargetName + "): charAtPt1: i=" + i)) {
+ ok(!charAtPt1.notFound,
+ "runCharAtPointTest (" + aTargetName + "): charAtPt1 isn't found: i=" + i);
+ if (!charAtPt1.notFound) {
+ is(charAtPt1.offset, kTestingOffset[i],
+ "runCharAtPointTest (" + aTargetName + "): charAtPt1 offset is wrong: i=" + i);
+ checkRect(charAtPt1, textRect, "runCharAtPointTest (" + aTargetName +
+ "): charAtPt1 left is wrong: i=" + i);
+ }
+ ok(!charAtPt1.tentativeCaretOffsetNotFound,
+ "runCharAtPointTest (" + aTargetName + "): tentative caret offset for charAtPt1 isn't found: i=" + i);
+ if (!charAtPt1.tentativeCaretOffsetNotFound) {
+ is(charAtPt1.tentativeCaretOffset, kLeftTentativeCaretOffset[i],
+ "runCharAtPointTest (" + aTargetName + "): tentative caret offset for charAtPt1 is wrong: i=" + i);
+ }
+ }
+
+ // Test #2, getting same character rect by the point near the bottom-right.
+ var charAtPt2 = synthesizeCharAtPoint(textRect.left + textRect.width - 2,
+ textRect.top + textRect.height - 2);
+ if (checkQueryContentResult(charAtPt2,
+ "runCharAtPointTest (" + aTargetName + "): charAtPt2: i=" + i)) {
+ ok(!charAtPt2.notFound,
+ "runCharAtPointTest (" + aTargetName + "): charAtPt2 isn't found: i=" + i);
+ if (!charAtPt2.notFound) {
+ is(charAtPt2.offset, kTestingOffset[i],
+ "runCharAtPointTest (" + aTargetName + "): charAtPt2 offset is wrong: i=" + i);
+ checkRect(charAtPt2, textRect, "runCharAtPointTest (" + aTargetName +
+ "): charAtPt1 left is wrong: i=" + i);
+ }
+ ok(!charAtPt2.tentativeCaretOffsetNotFound,
+ "runCharAtPointTest (" + aTargetName + "): tentative caret offset for charAtPt2 isn't found: i=" + i);
+ if (!charAtPt2.tentativeCaretOffsetNotFound) {
+ is(charAtPt2.tentativeCaretOffset, kRightTentativeCaretOffset[i],
+ "runCharAtPointTest (" + aTargetName + "): tentative caret offset for charAtPt2 is wrong: i=" + i);
+ }
+ }
+
+ // Test #3, getting left character offset.
+ var charAtPt3 = synthesizeCharAtPoint(textRect.left - 2,
+ textRect.top + 1);
+ if (checkQueryContentResult(charAtPt3,
+ "runCharAtPointTest (" + aTargetName + "): charAtPt3: i=" + i)) {
+ is(charAtPt3.notFound, kLeftSideOffset[i] == kNone,
+ kLeftSideOffset[i] == kNone ?
+ "runCharAtPointTest (" + aTargetName + "): charAtPt3 is found: i=" + i :
+ "runCharAtPointTest (" + aTargetName + "): charAtPt3 isn't found: i=" + i);
+ if (!charAtPt3.notFound) {
+ is(charAtPt3.offset, kLeftSideOffset[i],
+ "runCharAtPointTest (" + aTargetName + "): charAtPt3 offset is wrong: i=" + i);
+ }
+ if (kLeftSideOffset[i] == kNone) {
+ // There may be no enough padding-left (depends on platform)
+ todo(false,
+ "runCharAtPointTest (" + aTargetName + "): tentative caret offset for charAtPt3 isn't tested: i=" + i);
+ } else {
+ ok(!charAtPt3.tentativeCaretOffsetNotFound,
+ "runCharAtPointTest (" + aTargetName + "): tentative caret offset for charAtPt3 isn't found: i=" + i);
+ if (!charAtPt3.tentativeCaretOffsetNotFound) {
+ is(charAtPt3.tentativeCaretOffset, kLeftTentativeCaretOffset[i],
+ "runCharAtPointTest (" + aTargetName + "): tentative caret offset for charAtPt3 is wrong: i=" + i);
+ }
+ }
+ }
+
+ // Test #4, getting right character offset.
+ var charAtPt4 = synthesizeCharAtPoint(textRect.left + textRect.width + 1,
+ textRect.top + textRect.height - 2);
+ if (checkQueryContentResult(charAtPt4,
+ "runCharAtPointTest (" + aTargetName + "): charAtPt4: i=" + i)) {
+ is(charAtPt4.notFound, kRightSideOffset[i] == kNone,
+ kRightSideOffset[i] == kNone ?
+ "runCharAtPointTest (" + aTargetName + "): charAtPt4 is found: i=" + i :
+ "runCharAtPointTest (" + aTargetName + "): charAtPt4 isn't found: i=" + i);
+ if (!charAtPt4.notFound) {
+ is(charAtPt4.offset, kRightSideOffset[i],
+ "runCharAtPointTest (" + aTargetName + "): charAtPt4 offset is wrong: i=" + i);
+ }
+ ok(!charAtPt4.tentativeCaretOffsetNotFound,
+ "runCharAtPointTest (" + aTargetName + "): tentative caret offset for charAtPt4 isn't found: i=" + i);
+ if (!charAtPt4.tentativeCaretOffsetNotFound) {
+ is(charAtPt4.tentativeCaretOffset, kRightTentativeCaretOffset[i],
+ "runCharAtPointTest (" + aTargetName + "): tentative caret offset for charAtPt4 is wrong: i=" + i);
+ }
+ }
+ }
+}
+
+function runCharAtPointAtOutsideTest()
+{
+ textarea.focus();
+ textarea.value = "some text";
+ var editorRect = synthesizeQueryEditorRect();
+ if (!checkQueryContentResult(editorRect,
+ "runCharAtPointAtOutsideTest: editorRect")) {
+ return;
+ }
+ // Check on a text node which is at the outside of editor.
+ var charAtPt = synthesizeCharAtPoint(editorRect.left + 20,
+ editorRect.top - 10);
+ if (checkQueryContentResult(charAtPt,
+ "runCharAtPointAtOutsideTest: charAtPt")) {
+ ok(charAtPt.notFound,
+ "runCharAtPointAtOutsideTest: charAtPt is found on outside of editor");
+ ok(charAtPt.tentativeCaretOffsetNotFound,
+ "runCharAtPointAtOutsideTest: tentative caret offset for charAtPt is found on outside of editor");
+ }
+}
+
+function runSetSelectionEventTest()
+{
+ contenteditable.focus();
+
+ var selection = windowOfContenteditable.getSelection();
+
+ // #1
+ contenteditable.innerHTML = "abc<br>def";
+
+ synthesizeSelectionSet(0, 100);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #1 (0, 100), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first text node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #1 (0, 100), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #1 (0, 100), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node of the editor");
+ is(selection.focusOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #1 (0, 100), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of children");
+ checkSelection(0, "abc" + kLF + "def", "runSetSelectionEventTest #1 (0, 100), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(2, 2 + kLFLen);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #1 (2, 2+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first text node");
+ is(selection.anchorOffset, 2,
+ "runSetSelectionEventTest #1 (2, 2+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 2");
+ is(selection.focusNode, contenteditable.lastChild,
+ "runSetSelectionEventTest #1 (2, 2+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node");
+ is(selection.focusOffset, 1,
+ "runSetSelectionEventTest #1 (2, 2+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
+ checkSelection(2, "c" + kLF + "d", "runSetSelectionEventTest #1 (2, 2+kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(1, 2);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #1 (1, 2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first text node");
+ is(selection.anchorOffset, 1,
+ "runSetSelectionEventTest #1 (1, 2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
+ is(selection.focusNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #1 (1, 2), \"" + contenteditable.innerHTML + "\": selection focus node should be the first text node");
+ is(selection.focusOffset, contenteditable.firstChild.wholeText.length,
+ "runSetSelectionEventTest #1 (1, 2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the text node");
+ checkSelection(1, "bc", "runSetSelectionEventTest #1 (1, 2), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(3, kLFLen);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #1 (3, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first text node");
+ is(selection.anchorOffset, contenteditable.firstChild.wholeText.length,
+ "runSetSelectionEventTest #1 (3, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the first text node");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #1 (3, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, 2,
+ "runSetSelectionEventTest #1 (3, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be the index of the last text node");
+ checkSelection(3, kLF, "runSetSelectionEventTest #1 (3, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(6+kLFLen, 0);
+ is(selection.anchorNode, contenteditable.lastChild,
+ "runSetSelectionEventTest #1 (6+kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node");
+ is(selection.anchorOffset, contenteditable.lastChild.wholeText.length,
+ "runSetSelectionEventTest #1 (6+kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the last text node");
+ is(selection.focusNode, contenteditable.lastChild,
+ "runSetSelectionEventTest #1 (6+kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node");
+ is(selection.anchorOffset, contenteditable.lastChild.wholeText.length,
+ "runSetSelectionEventTest #1 (6+kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the last text node");
+ checkSelection(6 + kLFLen, "", "runSetSelectionEventTest #1 (6+kLFLen, 0), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(100, 0);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #1 (100, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node of the editor");
+ is(selection.anchorOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #1 (100, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the count of children");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #1 (100, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node of the editor");
+ is(selection.focusOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #1 (100, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of children");
+ checkSelection(6 + kLFLen, "", "runSetSelectionEventTest #1 (100, 0), \"" + contenteditable.innerHTML + "\"");
+
+ // #2
+ contenteditable.innerHTML = "<p>a<b>b</b>c</p><p>def</p>";
+
+ synthesizeSelectionSet(kLFLen, 4+kLFLen);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #2 (kLFLen, 4+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first <p> node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #2 (kLFLen, 4+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the first <p> node");
+ is(selection.focusNode, contenteditable.lastChild.firstChild,
+ "runSetSelectionEventTest #2 (kLFLen, 4+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node in the second <p> node");
+ is(selection.focusOffset, 1,
+ "runSetSelectionEventTest #2 (kLFLen, 4+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
+ checkSelection(kLFLen, "abc" + kLF + "d", "runSetSelectionEventTest #2 (kLFLen, 4+kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(kLFLen, 2);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #2 (kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first <p> node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #2 (kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the first <p> node");
+ is(selection.focusNode, contenteditable.firstChild.childNodes.item(1).firstChild,
+ "runSetSelectionEventTest #2 (kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node in the <b> node");
+ is(selection.focusOffset, contenteditable.firstChild.childNodes.item(1).firstChild.wholeText.length,
+ "runSetSelectionEventTest #2 (kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the text node in the <b> node");
+ checkSelection(kLFLen, "ab", "runSetSelectionEventTest #2 (kLFLen, 2), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(1+kLFLen, 2);
+ is(selection.anchorNode, contenteditable.firstChild.firstChild,
+ "runSetSelectionEventTest #2 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first text node");
+ is(selection.anchorOffset, 1,
+ "runSetSelectionEventTest #2 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
+ is(selection.focusNode, contenteditable.firstChild.lastChild,
+ "runSetSelectionEventTest #2 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node in the first <p> node");
+ is(selection.focusOffset, contenteditable.firstChild.lastChild.wholeText.length,
+ "runSetSelectionEventTest #2 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the last text node in the first <p> node");
+ checkSelection(1+kLFLen, "bc", "runSetSelectionEventTest #2 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(2+kLFLen, 2+kLFLen);
+ is(selection.anchorNode, contenteditable.firstChild.childNodes.item(1).firstChild,
+ "runSetSelectionEventTest #2 (2+kLFLen, 2+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node in the <b> node");
+ is(selection.anchorOffset, contenteditable.firstChild.childNodes.item(1).firstChild.wholeText.length,
+ "runSetSelectionEventTest #2 (2+kLFLen, 2+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the text node in the <b> node");
+ is(selection.focusNode, contenteditable.lastChild.firstChild,
+ "runSetSelectionEventTest #2 (2+kLFLen, 2+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node in the last <p> node");
+ is(selection.focusOffset, 1,
+ "runSetSelectionEventTest #2 (2+kLFLen, 2+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
+ checkSelection(2+kLFLen, "c" + kLF + "d", "runSetSelectionEventTest #2 (2+kLFLen, 2+kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(3+kLFLen*2, 1);
+ is(selection.anchorNode, contenteditable.lastChild,
+ "runSetSelectionEventTest #2 (3+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection anchor node should be the second <p> node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #2 (3+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the second <p> node");
+ is(selection.focusNode, contenteditable.lastChild.firstChild,
+ "runSetSelectionEventTest #2 (3+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node in the second <p> node");
+ is(selection.focusOffset, 1,
+ "runSetSelectionEventTest #2 (3+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
+ checkSelection(3+kLFLen*2, "d", "runSetSelectionEventTest #2 (3+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(0, 0);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #2 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #2 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #2 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #2 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(0, "", "runSetSelectionEventTest #2 (0, 0), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(0, kLFLen);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #2 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #2 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the first <p> node");
+ is(selection.focusNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #2 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the first <p> node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #2 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(0, kLF, "runSetSelectionEventTest #2 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(2+kLFLen, 1+kLFLen);
+ is(selection.anchorNode, contenteditable.firstChild.childNodes.item(1).firstChild,
+ "runSetSelectionEventTest #2 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node of the <b> node");
+ is(selection.anchorOffset, contenteditable.firstChild.childNodes.item(1).firstChild.wholeText.length,
+ "runSetSelectionEventTest #2 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the last text node of the first <b> node");
+ is(selection.focusNode, contenteditable.lastChild,
+ "runSetSelectionEventTest #2 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the second <p> node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #2 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(2+kLFLen, "c" + kLF, "runSetSelectionEventTest #2 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(3+kLFLen, kLFLen);
+ is(selection.anchorNode, contenteditable.firstChild.lastChild,
+ "runSetSelectionEventTest #2 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node of the first <p> node");
+ is(selection.anchorOffset, contenteditable.firstChild.lastChild.wholeText.length,
+ "runSetSelectionEventTest #2 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the last text node of the first <p> node");
+ is(selection.focusNode, contenteditable.lastChild,
+ "runSetSelectionEventTest #2 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the second <p> node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #2 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(3+kLFLen, kLF, "runSetSelectionEventTest #2 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(3+kLFLen, 1+kLFLen);
+ is(selection.anchorNode, contenteditable.firstChild.lastChild,
+ "runSetSelectionEventTest #2 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node of the first <p> node");
+ is(selection.anchorOffset, contenteditable.firstChild.lastChild.wholeText.length,
+ "runSetSelectionEventTest #2 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the last text node of the first <p> node");
+ is(selection.focusNode, contenteditable.lastChild.firstChild,
+ "runSetSelectionEventTest #2 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node of the second <p> node");
+ is(selection.focusOffset, 1,
+ "runSetSelectionEventTest #2 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
+ checkSelection(3+kLFLen, kLF + "d", "runSetSelectionEventTest #2 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ // #3
+ contenteditable.innerHTML = "<div>abc<p>def</p></div>";
+
+ synthesizeSelectionSet(1+kLFLen, 2);
+ is(selection.anchorNode, contenteditable.firstChild.firstChild,
+ "runSetSelectionEventTest #3 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first text node");
+ is(selection.anchorOffset, 1,
+ "runSetSelectionEventTest #3 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
+ is(selection.focusNode, contenteditable.firstChild.firstChild,
+ "runSetSelectionEventTest #3 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection focus node should be the first text node");
+ is(selection.focusOffset, contenteditable.firstChild.firstChild.wholeText.length,
+ "runSetSelectionEventTest #3 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the first text node");
+ checkSelection(1+kLFLen, "bc", "runSetSelectionEventTest #3 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(1+kLFLen, 3+kLFLen);
+ is(selection.anchorNode, contenteditable.firstChild.firstChild,
+ "runSetSelectionEventTest #3 (1+kLFLen, 3+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first text node");
+ is(selection.anchorOffset, 1,
+ "runSetSelectionEventTest #3 (1+kLFLen, 3+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
+ is(selection.focusNode, contenteditable.firstChild.lastChild.firstChild,
+ "runSetSelectionEventTest #3 (1+kLFLen, 3+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node in the <p> node");
+ is(selection.focusOffset, 1,
+ "runSetSelectionEventTest #3 (1+kLFLen, 3+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
+ checkSelection(1+kLFLen, "bc" + kLF + "d", "runSetSelectionEventTest #3 (1+kLFLen, 3+kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(3+kLFLen, 0);
+ is(selection.anchorNode, contenteditable.firstChild.firstChild,
+ "runSetSelectionEventTest #3 (3+kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first text node");
+ is(selection.anchorOffset, contenteditable.firstChild.firstChild.wholeText.length,
+ "runSetSelectionEventTest #3 (3+kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the first text node");
+ is(selection.focusNode, contenteditable.firstChild.firstChild,
+ "runSetSelectionEventTest #3 (3+kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the first text node");
+ is(selection.focusOffset, contenteditable.firstChild.firstChild.wholeText.length,
+ "runSetSelectionEventTest #3 (3+kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the first text node");
+ checkSelection(3+kLFLen, "", "runSetSelectionEventTest #3 (3+kLFLen, 0), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(0, 6+kLFLen*2);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #3 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #3 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable.firstChild.lastChild.firstChild,
+ "runSetSelectionEventTest #3 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node");
+ is(selection.focusOffset, contenteditable.firstChild.lastChild.firstChild.wholeText.length,
+ "runSetSelectionEventTest #3 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the last text node");
+ checkSelection(0, kLF + "abc" + kLF + "def", "runSetSelectionEventTest #3 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(0, 100);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #3 (0, 100), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #3 (0, 100), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #3 (0, 100), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #3 (0, 100), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
+ checkSelection(0, kLF + "abc" + kLF + "def", "runSetSelectionEventTest #3 (0, 100), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(4+kLFLen*2, 2);
+ is(selection.anchorNode, contenteditable.firstChild.lastChild.firstChild,
+ "runSetSelectionEventTest #3 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node");
+ is(selection.anchorOffset, 1,
+ "runSetSelectionEventTest #3 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
+ is(selection.focusNode, contenteditable.firstChild.lastChild.firstChild,
+ "runSetSelectionEventTest #3 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node");
+ is(selection.focusOffset, contenteditable.firstChild.lastChild.firstChild.wholeText.length,
+ "runSetSelectionEventTest #3 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the last text node");
+ checkSelection(4+kLFLen*2, "ef", "runSetSelectionEventTest #3 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(4+kLFLen*2, 100);
+ is(selection.anchorNode, contenteditable.firstChild.lastChild.firstChild,
+ "runSetSelectionEventTest #3 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node");
+ is(selection.anchorOffset, 1,
+ "runSetSelectionEventTest #3 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #3 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #3 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
+ checkSelection(4+kLFLen*2, "ef", "runSetSelectionEventTest #3 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(6+kLFLen*2, 0);
+ is(selection.anchorNode, contenteditable.firstChild.lastChild.firstChild,
+ "runSetSelectionEventTest #3 (6+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node");
+ is(selection.anchorOffset, contenteditable.firstChild.lastChild.firstChild.wholeText.length,
+ "runSetSelectionEventTest #3 (6+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the last text node");
+ is(selection.focusNode, contenteditable.firstChild.lastChild.firstChild,
+ "runSetSelectionEventTest #3 (6+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node");
+ is(selection.focusOffset, contenteditable.firstChild.lastChild.firstChild.wholeText.length,
+ "runSetSelectionEventTest #3 (6+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the last text node");
+ checkSelection(6+kLFLen*2, "", "runSetSelectionEventTest #3 (6+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(6+kLFLen*2, 1);
+ is(selection.anchorNode, contenteditable.firstChild.lastChild.firstChild,
+ "runSetSelectionEventTest #3 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node");
+ is(selection.anchorOffset, contenteditable.firstChild.lastChild.firstChild.wholeText.length,
+ "runSetSelectionEventTest #3 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the last text node");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #3 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #3 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
+ checkSelection(6+kLFLen*2, "", "runSetSelectionEventTest #3 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(0, kLFLen);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #3 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #3 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the first text node");
+ is(selection.focusNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #3 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the <div> node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #3 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(0, kLF, "runSetSelectionEventTest #3 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(0, 1+kLFLen);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #3 (0, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #3 (0, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the <div> node");
+ is(selection.focusNode, contenteditable.firstChild.firstChild,
+ "runSetSelectionEventTest #3 (0, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the first text node of the <div> node");
+ is(selection.focusOffset, 1,
+ "runSetSelectionEventTest #3 (0, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
+ checkSelection(0, kLF + "a", "runSetSelectionEventTest #3 (0, 1+kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(2+kLFLen, 1+kLFLen);
+ is(selection.anchorNode, contenteditable.firstChild.firstChild,
+ "runSetSelectionEventTest #3 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node of the <div> node");
+ is(selection.anchorOffset, 2,
+ "runSetSelectionEventTest #3 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 2");
+ is(selection.focusNode, contenteditable.firstChild.lastChild,
+ "runSetSelectionEventTest #3 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #3 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(2+kLFLen, "c" + kLF, "runSetSelectionEventTest #3 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(3+kLFLen, kLFLen);
+ is(selection.anchorNode, contenteditable.firstChild.firstChild,
+ "runSetSelectionEventTest #3 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node of the <div> node");
+ is(selection.anchorOffset, contenteditable.firstChild.firstChild.wholeText.length,
+ "runSetSelectionEventTest #3 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the text node of the <div> node");
+ is(selection.focusNode, contenteditable.firstChild.lastChild,
+ "runSetSelectionEventTest #3 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #3 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(3+kLFLen, kLF, "runSetSelectionEventTest #3 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(3+kLFLen, 1+kLFLen);
+ is(selection.anchorNode, contenteditable.firstChild.firstChild,
+ "runSetSelectionEventTest #3 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node of the <div> node");
+ is(selection.anchorOffset, contenteditable.firstChild.firstChild.wholeText.length,
+ "runSetSelectionEventTest #3 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the text node of the <div> node");
+ is(selection.focusNode, contenteditable.firstChild.lastChild.firstChild,
+ "runSetSelectionEventTest #3 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node of the <p> node");
+ is(selection.focusOffset, 1,
+ "runSetSelectionEventTest #3 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
+ checkSelection(3+kLFLen, kLF + "d", "runSetSelectionEventTest #3 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ // #4
+ contenteditable.innerHTML = "<div><p>abc</p>def</div>";
+
+ synthesizeSelectionSet(1+kLFLen*2, 2);
+ is(selection.anchorNode, contenteditable.firstChild.firstChild.firstChild,
+ "runSetSelectionEventTest #4 (1+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node in the <p> node");
+ is(selection.anchorOffset, 1,
+ "runSetSelectionEventTest #4 (1+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
+ is(selection.focusNode, contenteditable.firstChild.firstChild.firstChild,
+ "runSetSelectionEventTest #4 (1+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node in the <p> node");
+ is(selection.focusOffset, contenteditable.firstChild.firstChild.firstChild.wholeText.length,
+ "runSetSelectionEventTest #4 (1+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the text node in the <p> node");
+ checkSelection(1+kLFLen*2, "bc", "runSetSelectionEventTest #4 (1+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(1+kLFLen*2, 3);
+ is(selection.anchorNode, contenteditable.firstChild.firstChild.firstChild,
+ "runSetSelectionEventTest #4 (1+kLFLen*2, 3), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node in the <p> node");
+ is(selection.anchorOffset, 1,
+ "runSetSelectionEventTest #4 (1+kLFLen*2, 3), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
+ is(selection.focusNode, contenteditable.firstChild.lastChild,
+ "runSetSelectionEventTest #4 (1+kLFLen*2, 3), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node");
+ is(selection.focusOffset, 1,
+ "runSetSelectionEventTest #4 (1+kLFLen*2, 3), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
+ checkSelection(1+kLFLen*2, "bcd", "runSetSelectionEventTest #4 (1+kLFLen*2, 3), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(3+kLFLen*2, 0);
+ is(selection.anchorNode, contenteditable.firstChild.firstChild.firstChild,
+ "runSetSelectionEventTest #4 (3+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node in the <p> node");
+ is(selection.anchorOffset, contenteditable.firstChild.firstChild.firstChild.wholeText.length,
+ "runSetSelectionEventTest #4 (3+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the text node in the <p> node");
+ is(selection.focusNode, contenteditable.firstChild.firstChild.firstChild,
+ "runSetSelectionEventTest #4 (3+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node in the <p> node");
+ is(selection.focusOffset, contenteditable.firstChild.firstChild.firstChild.wholeText.length,
+ "runSetSelectionEventTest #4 (3+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the text node in the <p> node");
+ checkSelection(3+kLFLen*2, "", "runSetSelectionEventTest #4 (3+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(0, 6+kLFLen*2);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #4 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #4 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable.firstChild.lastChild,
+ "runSetSelectionEventTest #4 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node");
+ is(selection.focusOffset, contenteditable.firstChild.lastChild.wholeText.length,
+ "runSetSelectionEventTest #4 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the last text node");
+ checkSelection(0, kLF + kLF + "abcdef", "runSetSelectionEventTest #4 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(0, 100);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #4 (0, 100), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #4 (0, 100), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #4 (0, 100), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #4 (0, 100), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
+ checkSelection(0, kLF + kLF + "abcdef", "runSetSelectionEventTest #4 (0, 100), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(4+kLFLen*2, 2);
+ is(selection.anchorNode, contenteditable.firstChild.lastChild,
+ "runSetSelectionEventTest #4 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node");
+ is(selection.anchorOffset, 1,
+ "runSetSelectionEventTest #4 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
+ is(selection.focusNode, contenteditable.firstChild.lastChild,
+ "runSetSelectionEventTest #4 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node");
+ is(selection.focusOffset, contenteditable.firstChild.lastChild.wholeText.length,
+ "runSetSelectionEventTest #4 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the last text node");
+ checkSelection(4+kLFLen*2, "ef", "runSetSelectionEventTest #4 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(4+kLFLen*2, 100);
+ is(selection.anchorNode, contenteditable.firstChild.lastChild,
+ "runSetSelectionEventTest #4 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node");
+ is(selection.anchorOffset, 1,
+ "runSetSelectionEventTest #4 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #4 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #4 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
+ checkSelection(4+kLFLen*2, "ef", "runSetSelectionEventTest #4 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(6+kLFLen*2, 0);
+ is(selection.anchorNode, contenteditable.firstChild.lastChild,
+ "runSetSelectionEventTest #4 (6+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node");
+ is(selection.anchorOffset, contenteditable.firstChild.lastChild.wholeText.length,
+ "runSetSelectionEventTest #4 (6+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the last text node");
+ is(selection.focusNode, contenteditable.firstChild.lastChild,
+ "runSetSelectionEventTest #4 (6+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node");
+ is(selection.focusOffset, contenteditable.firstChild.lastChild.wholeText.length,
+ "runSetSelectionEventTest #4 (6+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the last text node");
+ checkSelection(6+kLFLen*2, "", "runSetSelectionEventTest #4 (6+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(6+kLFLen*2, 1);
+ is(selection.anchorNode, contenteditable.firstChild.lastChild,
+ "runSetSelectionEventTest #4 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node");
+ is(selection.anchorOffset, contenteditable.firstChild.lastChild.wholeText.length,
+ "runSetSelectionEventTest #4 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the last text node");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #4 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #4 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
+ checkSelection(6+kLFLen*2, "", "runSetSelectionEventTest #4 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(0, kLFLen);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #4 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #4 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the <div> node");
+ is(selection.focusNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #4 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the <div> node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #4 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(0, kLF, "runSetSelectionEventTest #4 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(0, kLFLen*2);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #4 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #4 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the <div> node");
+ is(selection.focusNode, contenteditable.firstChild.firstChild,
+ "runSetSelectionEventTest #4 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #4 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(0, kLF + kLF, "runSetSelectionEventTest #4 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(0, 1+kLFLen*2);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #4 (0, 1+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #4 (0, 1+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the <div> node");
+ is(selection.focusNode, contenteditable.firstChild.firstChild.firstChild,
+ "runSetSelectionEventTest #4 (0, 1+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node in the <p> node");
+ is(selection.focusOffset, 1,
+ "runSetSelectionEventTest #4 (0, 1+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
+ checkSelection(0, kLF + kLF + "a", "runSetSelectionEventTest #4 (0, 1+kLFLen*2), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(kLFLen, 0);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #4 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <div> node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #4 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #4 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the <div> node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #4 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(kLFLen, "", "runSetSelectionEventTest #4 (kLFLen, 0), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(kLFLen, kLFLen);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #4 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <div> node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #4 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the <p> node");
+ is(selection.focusNode, contenteditable.firstChild.firstChild,
+ "runSetSelectionEventTest #4 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #4 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(kLFLen, kLF, "runSetSelectionEventTest #4 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(kLFLen, 1+kLFLen);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #4 (kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <div> node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #4 (kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the <p> node");
+ is(selection.focusNode, contenteditable.firstChild.firstChild.firstChild,
+ "runSetSelectionEventTest #4 (kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node in the <p> node");
+ is(selection.focusOffset, 1,
+ "runSetSelectionEventTest #4 (kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
+ checkSelection(kLFLen, kLF +"a", "runSetSelectionEventTest #4 (kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ // #5
+ contenteditable.innerHTML = "<br>";
+
+ synthesizeSelectionSet(0, 0);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #5 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #5 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #5 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #5 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(0, "", "runSetSelectionEventTest #5 (0, 0), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(0, kLFLen);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #5 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #5 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #5 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #5 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
+ checkSelection(0, kLF, "runSetSelectionEventTest #5 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(kLFLen, 0);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #5 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #5 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the count of the root's children");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #5 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #5 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
+ checkSelection(kLFLen, "", "runSetSelectionEventTest #5 (kLFLen, 0), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(kLFLen, 1);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #5 (kLFLen, 1), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #5 (kLFLen, 1), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the count of the root's children");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #5 (kLFLen, 1), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #5 (kLFLen, 1), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
+ checkSelection(kLFLen, "", "runSetSelectionEventTest #5 (kLFLen, 1), \"" + contenteditable.innerHTML + "\"");
+
+ // #6
+ contenteditable.innerHTML = "<p><br></p>";
+
+ synthesizeSelectionSet(kLFLen, kLFLen);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #6 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <p> node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #6 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
+ is(selection.focusNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #6 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
+ is(selection.focusOffset, contenteditable.firstChild.childNodes.length,
+ "runSetSelectionEventTest #6 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the <p>'s children");
+ checkSelection(kLFLen, kLF, "runSetSelectionEventTest #6 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(kLFLen*2, 0);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #6 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <p> node");
+ is(selection.anchorOffset, contenteditable.firstChild.childNodes.length,
+ "runSetSelectionEventTest #6 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the count of the <p>'s children");
+ is(selection.focusNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #6 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
+ is(selection.focusOffset, contenteditable.firstChild.childNodes.length,
+ "runSetSelectionEventTest #6 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the <p>'s children");
+ checkSelection(kLFLen*2, "", "runSetSelectionEventTest #6 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(kLFLen*2, 1);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #6 (kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <p> node");
+ is(selection.anchorOffset, contenteditable.firstChild.childNodes.length,
+ "runSetSelectionEventTest #6 (kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the count of the root's children");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #6 (kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #6 (kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
+ checkSelection(kLFLen*2, "", "runSetSelectionEventTest #6 (kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(0, kLFLen);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #6 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #6 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #6 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #6 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(0, kLF, "runSetSelectionEventTest #6 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(0, kLFLen*2);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #6 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #6 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #6 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
+ is(selection.focusOffset, contenteditable.firstChild.childNodes.length,
+ "runSetSelectionEventTest #6 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the <p>'s children");
+ checkSelection(0, kLF + kLF, "runSetSelectionEventTest #6 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(kLFLen, 0);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #6 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <p> node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #6 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #6 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #6 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(kLFLen, "", "runSetSelectionEventTest #6 (kLFLen, 0), \"" + contenteditable.innerHTML + "\"");
+
+ // #7
+ contenteditable.innerHTML = "<br><br>";
+
+ synthesizeSelectionSet(0, kLFLen);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #7 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #7 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #7 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, 1,
+ "runSetSelectionEventTest #7 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
+ checkSelection(0, kLF, "runSetSelectionEventTest #7 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(0, kLFLen * 2);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #7 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #7 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #7 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #7 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
+ checkSelection(0, kLF + kLF, "runSetSelectionEventTest #7 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(kLFLen, 0);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #7 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 1,
+ "runSetSelectionEventTest #7 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #7 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, 1,
+ "runSetSelectionEventTest #7 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
+ checkSelection(kLFLen, "", "runSetSelectionEventTest #7 (kLFLen, 0), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(kLFLen, kLFLen);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #7 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 1,
+ "runSetSelectionEventTest #7 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #7 (kLFLen, kLFLen) selection focus node should be the root node");
+ is(selection.focusOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #7 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
+ checkSelection(kLFLen, kLF, "runSetSelectionEventTest #7 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(kLFLen * 2, 0);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #7 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #7 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the count of the root's children");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #7 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #7 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
+ checkSelection(kLFLen * 2, "", "runSetSelectionEventTest #7 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\"");
+
+ // #8
+ contenteditable.innerHTML = "<p><br><br></p>";
+
+ synthesizeSelectionSet(kLFLen, kLFLen);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #8 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <p> node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #8 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #8 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
+ is(selection.focusOffset, 1,
+ "runSetSelectionEventTest #8 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
+ checkSelection(kLFLen, kLF, "runSetSelectionEventTest #7 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(kLFLen, kLFLen * 2);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #8 (kLFLen, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <p> node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #8 (kLFLen, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #8 (kLFLen, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
+ is(selection.focusOffset, contenteditable.firstChild.childNodes.length,
+ "runSetSelectionEventTest #8 (kLFLen, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the <p>'s children");
+ checkSelection(kLFLen, kLF + kLF, "runSetSelectionEventTest #8 (kLFLen, kLFLen*2), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(kLFLen*2, 0);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #8 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <p> node");
+ is(selection.anchorOffset, 1,
+ "runSetSelectionEventTest #8 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
+ is(selection.focusNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #8 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
+ is(selection.focusOffset, 1,
+ "runSetSelectionEventTest #8 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
+ checkSelection(kLFLen*2, "", "runSetSelectionEventTest #8 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(kLFLen*2, kLFLen);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #8 (kLFLen*2, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <p> node");
+ is(selection.anchorOffset, 1,
+ "runSetSelectionEventTest #8 (kLFLen*2, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
+ is(selection.focusNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #8 (kLFLen*2, kLFLen) selection focus node should be the <p> node");
+ is(selection.focusOffset, contenteditable.firstChild.childNodes.length,
+ "runSetSelectionEventTest #8 (kLFLen*2, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the <p>'s children");
+ checkSelection(kLFLen*2, kLF, "runSetSelectionEventTest #8 (kLFLen*2, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(kLFLen*3, 0);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #8 (kLFLen*3, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <p> node");
+ is(selection.anchorOffset, contenteditable.firstChild.childNodes.length,
+ "runSetSelectionEventTest #8 (kLFLen*3, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the count of the <p>'s children");
+ is(selection.focusNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #8 (kLFLen*3, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
+ is(selection.focusOffset, contenteditable.firstChild.childNodes.length,
+ "runSetSelectionEventTest #8 (kLFLen*3, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the <p>'s children");
+ checkSelection(kLFLen*3, "", "runSetSelectionEventTest #8 (kLFLen*3, 0), \"" + contenteditable.innerHTML + "\"");
+
+ // #9 (ContentEventHandler cannot distinguish if <p> can have children, so, the result is same as case #5, "<br>")
+ contenteditable.innerHTML = "<p></p>";
+
+ synthesizeSelectionSet(kLFLen, 0);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #9 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 1,
+ "runSetSelectionEventTest #9 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the <p> node + 1");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #9 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
+ is(selection.focusOffset, 1,
+ "runSetSelectionEventTest #9 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the index of the <p> node + 1");
+ checkSelection(kLFLen, "", "runSetSelectionEventTest #9 (kLFLen, 0), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(kLFLen, 1);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #9 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 1,
+ "runSetSelectionEventTest #9 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the <p> node + 1");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #9 (kLFLen, 1), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #9 (kLFLen, 1), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
+ checkSelection(kLFLen, "", "runSetSelectionEventTest #9 (kLFLen, 0), \"" + contenteditable.innerHTML + "\"");
+
+ // #10
+ contenteditable.innerHTML = "";
+
+ synthesizeSelectionSet(0, 0);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #10 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #10 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #10 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #10 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(0, "", "runSetSelectionEventTest #10 (0, 0), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(0, 1);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #10 (0, 1), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #10 (0, 1), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #10 (0, 1), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #10 (0, 1), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(0, "", "runSetSelectionEventTest #10 (0, 1), \"" + contenteditable.innerHTML + "\"");
+
+ // #11
+ contenteditable.innerHTML = "<span></span><i><u></u></i>";
+
+ synthesizeSelectionSet(0, 0);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #11 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #11 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #11 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #11 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(0, "", "runSetSelectionEventTest #11 (0, 0), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(0, 1);
+ is(selection.anchorNode, contenteditable,
+ "runSetSelectionEventTest #11 (0, 1), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #11 (0, 1), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable,
+ "runSetSelectionEventTest #11 (0, 1), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
+ is(selection.focusOffset, contenteditable.childNodes.length,
+ "runSetSelectionEventTest #11 (0, 1), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
+ checkSelection(0, "", "runSetSelectionEventTest #11 (0, 1), \"" + contenteditable.innerHTML + "\"");
+
+ // #12
+ contenteditable.innerHTML = "<span>abc</span><i><u></u></i>";
+
+ synthesizeSelectionSet(0, 0);
+ is(selection.anchorNode, contenteditable.firstChild.firstChild,
+ "runSetSelectionEventTest #12 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #12 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable.firstChild.firstChild,
+ "runSetSelectionEventTest #12 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #12 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(0, "", "runSetSelectionEventTest #12 (0, 0), \"" + contenteditable.innerHTML + "\"");
+
+ // #13
+ contenteditable.innerHTML = "<span></span><i>abc<u></u></i>";
+
+ synthesizeSelectionSet(0, 0);
+ is(selection.anchorNode, contenteditable.childNodes.item(1).firstChild,
+ "runSetSelectionEventTest #13 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #13 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable.childNodes.item(1).firstChild,
+ "runSetSelectionEventTest #13 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #13 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(0, "", "runSetSelectionEventTest #13 (0, 0), \"" + contenteditable.innerHTML + "\"");
+
+ // #14
+ contenteditable.innerHTML = "<span></span><i><u>abc</u></i>";
+
+ synthesizeSelectionSet(0, 0);
+ is(selection.anchorNode, contenteditable.childNodes.item(1).firstChild.firstChild,
+ "runSetSelectionEventTest #14 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #14 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable.childNodes.item(1).firstChild.firstChild,
+ "runSetSelectionEventTest #14 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #14 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(0, "", "runSetSelectionEventTest #14 (0, 0), \"" + contenteditable.innerHTML + "\"");
+
+ // #15
+ contenteditable.innerHTML = "<span></span><i><u></u>abc</i>";
+
+ synthesizeSelectionSet(0, 0);
+ is(selection.anchorNode, contenteditable.childNodes.item(1).lastChild,
+ "runSetSelectionEventTest #15 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #15 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable.childNodes.item(1).lastChild,
+ "runSetSelectionEventTest #15 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #15 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(0, "", "runSetSelectionEventTest #15 (0, 0), \"" + contenteditable.innerHTML + "\"");
+
+ // #16
+ contenteditable.innerHTML = "a<blink>b</blink>c";
+ synthesizeSelectionSet(0, 3);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #16 (0, 3), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first text node");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #16 (0, 3), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable.lastChild,
+ "runSetSelectionEventTest #16 (0, 3), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node");
+ is(selection.focusOffset, contenteditable.lastChild.wholeText.length,
+ "runSetSelectionEventTest #16 (0, 3), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the last text node");
+ checkSelection(0, "abc", "runSetSelectionEventTest #16 (0, 3), \"" + contenteditable.innerHTML + "\"");
+
+ // #17 (bug 1319660 - incorrect adjustment of content iterator last node)
+ contenteditable.innerHTML = "<div>a</div><div><br></div>";
+
+ synthesizeSelectionSet(kLFLen, 1+kLFLen);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #17 (kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first <div> element");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #17 (kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable.lastChild,
+ "runSetSelectionEventTest #17 (kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the second <div> element");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #17 (kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(kLFLen, "a" + kLF, "runSetSelectionEventTest #17 (kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ synthesizeSelectionSet(1+2*kLFLen, 0);
+ is(selection.anchorNode, contenteditable.lastChild,
+ "runSetSelectionEventTest #17 (1+2*kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the second <div> element");
+ is(selection.anchorOffset, 0,
+ "runSetSelectionEventTest #17 (1+2*kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
+ is(selection.focusNode, contenteditable.lastChild,
+ "runSetSelectionEventTest #17 (1+2*kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the second <div> element");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #17 (1+2*kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(1+2*kLFLen, "", "runSetSelectionEventTest #17 (1+2*kLFLen, 0), \"" + contenteditable.innerHTML + "\"");
+
+ // #18 (bug 1319660 - content iterator start node regression)
+ contenteditable.innerHTML = "<div><br></div><div><br></div>";
+
+ synthesizeSelectionSet(2*kLFLen, kLFLen);
+ is(selection.anchorNode, contenteditable.firstChild,
+ "runSetSelectionEventTest #18 (2*kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first <div> element");
+ is(selection.anchorOffset, 1,
+ "runSetSelectionEventTest #18 (2*kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
+ is(selection.focusNode, contenteditable.lastChild,
+ "runSetSelectionEventTest #18 (2*kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the second <div> element");
+ is(selection.focusOffset, 0,
+ "runSetSelectionEventTest #18 (2*kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
+ checkSelection(2*kLFLen, kLF, "runSetSelectionEventTest #18 (2*kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
+}
+
+function runQueryTextContentEventTest()
+{
+ contenteditable.focus();
+
+ var result;
+
+ // #1
+ contenteditable.innerHTML = "abc<br>def";
+
+ result = synthesizeQueryTextContent(0, 6 + kLFLen);
+ is(result.text, "abc" + kLF + "def", "runQueryTextContentEventTest #1 (0, 6+kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(0, 100);
+ is(result.text, "abc" + kLF + "def", "runQueryTextContentEventTest #1 (0, 100), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(2, 2 + kLFLen);
+ is(result.text, "c" + kLF + "d", "runQueryTextContentEventTest #1 (2, 2+kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(1, 2);
+ is(result.text, "bc", "runQueryTextContentEventTest #1 (1, 2), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(3, kLFLen);
+ is(result.text, kLF, "runQueryTextContentEventTest #1 (3, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(6 + kLFLen, 1);
+ is(result.text, "", "runQueryTextContentEventTest #1 (6 + kLFLen, 0), \"" + contenteditable.innerHTML + "\"");
+
+ // #2
+ contenteditable.innerHTML = "<p>a<b>b</b>c</p><p>def</p>";
+
+ result = synthesizeQueryTextContent(kLFLen, 4+kLFLen);
+ is(result.text, "abc" + kLF + "d", "runQueryTextContentEventTest #2 (kLFLen, 4+kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(kLFLen, 2);
+ is(result.text, "ab", "runQueryTextContentEventTest #2 (kLFLen, 2), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(1+kLFLen, 2);
+ is(result.text, "bc", "runQueryTextContentEventTest #2 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(2+kLFLen, 2+kLFLen);
+ is(result.text, "c" + kLF + "d", "runQueryTextContentEventTest #2 (2+kLFLen, 2+kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(3+kLFLen*2, 1);
+ is(result.text, "d", "runQueryTextContentEventTest #2 (3+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(0, kLFLen);
+ is(result.text, kLF, "runQueryTextContentEventTest #2 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(2+kLFLen, 1+kLFLen);
+ is(result.text, "c" + kLF, "runQueryTextContentEventTest #2 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(3+kLFLen, kLFLen);
+ is(result.text, kLF, "runQueryTextContentEventTest #2 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(3+kLFLen, 1+kLFLen);
+ is(result.text, kLF + "d", "runQueryTextContentEventTest #2 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ // #3
+ contenteditable.innerHTML = "<div>abc<p>def</p></div>";
+
+ result = synthesizeQueryTextContent(1+kLFLen, 2);
+ is(result.text, "bc", "runQueryTextContentEventTest #3 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(1+kLFLen, 3+kLFLen);
+ is(result.text, "bc" + kLF + "d", "runQueryTextContentEventTest #3 (1+kLFLen, 3+kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(3+kLFLen*2, 1);
+ is(result.text, "d", "runQueryTextContentEventTest #3 (3+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(0, 6+kLFLen*2);
+ is(result.text, kLF + "abc" + kLF + "def", "runQueryTextContentEventTest #3 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(0, 100);
+ is(result.text, kLF + "abc" + kLF + "def", "runQueryTextContentEventTest #3 (0, 100), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(4+kLFLen*2, 2);
+ is(result.text, "ef", "runQueryTextContentEventTest #3 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(4+kLFLen*2, 100);
+ is(result.text, "ef", "runQueryTextContentEventTest #3 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(6+kLFLen*2, 1);
+ is(result.text, "", "runQueryTextContentEventTest #3 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(0, kLFLen);
+ is(result.text, kLF, "runQueryTextContentEventTest #3 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(0, 1+kLFLen);
+ is(result.text, kLF + "a", "runQueryTextContentEventTest #3 (0, 1+kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(2+kLFLen, 1+kLFLen);
+ is(result.text, "c" + kLF, "runQueryTextContentEventTest #3 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(3+kLFLen, kLFLen);
+ is(result.text, kLF, "runQueryTextContentEventTest #3 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(3+kLFLen, 1+kLFLen);
+ is(result.text, kLF + "d", "runQueryTextContentEventTest #3 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ // #4
+ contenteditable.innerHTML = "<div><p>abc</p>def</div>";
+
+ result = synthesizeQueryTextContent(1+kLFLen*2, 2);
+ is(result.text, "bc", "runQueryTextContentEventTest #4 (1+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(1+kLFLen*2, 3);
+ is(result.text, "bcd", "runQueryTextContentEventTest #4 (1+kLFLen*2, 3), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(3+kLFLen*2, 1);
+ is(result.text, "d", "runQueryTextContentEventTest #4 (3+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(0, 6+kLFLen*2);
+ is(result.text, kLF + kLF + "abcdef", "runQueryTextContentEventTest #4 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(0, 100);
+ is(result.text, kLF + kLF + "abcdef", "runQueryTextContentEventTest #4 (0, 100), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(4+kLFLen*2, 2);
+ is(result.text, "ef", "runQueryTextContentEventTest #4 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(4+kLFLen*2, 100);
+ is(result.text, "ef", "runQueryTextContentEventTest #4 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(6+kLFLen*2, 1);
+ is(result.text, "", "runQueryTextContentEventTest #4 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(0, kLFLen);
+ is(result.text, kLF, "runQueryTextContentEventTest #4 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(0, kLFLen*2);
+ is(result.text, kLF + kLF, "runQueryTextContentEventTest #4 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(0, 1+kLFLen*2);
+ is(result.text, kLF + kLF + "a", "runQueryTextContentEventTest #4 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(kLFLen, kLFLen);
+ is(result.text, kLF, "runQueryTextContentEventTest #4 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(kLFLen, 1+kLFLen);
+ is(result.text, kLF + "a", "runQueryTextContentEventTest #4 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ // #5
+ contenteditable.innerHTML = "<br>";
+
+ result = synthesizeQueryTextContent(0, kLFLen);
+ is(result.text, kLF, "runQueryTextContentEventTest #5 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(kLFLen, 1);
+ is(result.text, "", "runQueryTextContentEventTest #5 (kLFLen, 1), \"" + contenteditable.innerHTML + "\"");
+
+ // #6
+ contenteditable.innerHTML = "<p><br></p>";
+
+ result = synthesizeQueryTextContent(kLFLen, kLFLen);
+ is(result.text, kLF, "runQueryTextContentEventTest #6 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(kLFLen*2, 1);
+ is(result.text, "", "runQueryTextContentEventTest #5 (kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(0, kLFLen);
+ is(result.text, kLF, "runQueryTextContentEventTest #6 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(0, kLFLen*2);
+ is(result.text, kLF + kLF, "runQueryTextContentEventTest #6 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\"");
+
+ // #7
+ contenteditable.innerHTML = "<br><br>";
+
+ result = synthesizeQueryTextContent(0, kLFLen);
+ is(result.text, kLF, "runQueryTextContentEventTest #7 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(0, kLFLen * 2);
+ is(result.text, kLF + kLF, "runQueryTextContentEventTest #7 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(kLFLen, kLFLen);
+ is(result.text, kLF, "runQueryTextContentEventTest #7 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(kLFLen * 2, 1);
+ is(result.text, "", "runQueryTextContentEventTest #7 (kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
+
+ // #8
+ contenteditable.innerHTML = "<p><br><br></p>";
+
+ result = synthesizeQueryTextContent(kLFLen, kLFLen);
+ is(result.text, kLF, "runQueryTextContentEventTest #8 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(kLFLen, kLFLen * 2);
+ is(result.text, kLF + kLF, "runQueryTextContentEventTest #8 (kLFLen, kLFLen*2), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(kLFLen*2, kLFLen);
+ is(result.text, kLF, "runQueryTextContentEventTest #8 (kLFLen*2, kLFLen), \"" + contenteditable.innerHTML + "\"");
+
+ result = synthesizeQueryTextContent(kLFLen*3, 1);
+ is(result.text, "", "runQueryTextContentEventTest #8 (kLFLen*3, 1), \"" + contenteditable.innerHTML + "\"");
+
+ // #16
+ contenteditable.innerHTML = "a<blink>b</blink>c";
+
+ result = synthesizeQueryTextContent(0, 3);
+ is(result.text, "abc", "runQueryTextContentEventTest #16 (0, 3), \"" + contenteditable.innerHTML + "\"");
+}
+
+function runQueryIMESelectionTest()
+{
+ textarea.focus();
+ textarea.value = "before after";
+ var startoffset = textarea.selectionStart = textarea.selectionEnd = "before ".length;
+
+ if (!checkIMESelection("RawClause", false, 0, "", "runQueryIMESelectionTest: before starting composition") ||
+ !checkIMESelection("SelectedRawClause", false, 0, "", "runQueryIMESelectionTest: before starting composition") ||
+ !checkIMESelection("ConvertedClause", false, 0, "", "runQueryIMESelectionTest: before starting composition") ||
+ !checkIMESelection("SelectedClause", false, 0, "", "runQueryIMESelectionTest: before starting composition")) {
+ synthesizeComposition({ type: "compositioncommitasis" });
+ return;
+ }
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "a",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ if (!checkIMESelection("RawClause", true, startoffset, "a", "runQueryIMESelectionTest: inputting raw text") ||
+ !checkIMESelection("SelectedRawClause", false, 0, "", "runQueryIMESelectionTest: inputting raw text") ||
+ !checkIMESelection("ConvertedClause", false, 0, "", "runQueryIMESelectionTest: inputting raw text") ||
+ !checkIMESelection("SelectedClause", false, 0, "", "runQueryIMESelectionTest: inputting raw text")) {
+ synthesizeComposition({ type: "compositioncommitasis" });
+ return;
+ }
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "abcdefgh",
+ "clauses":
+ [
+ { "length": 8, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 8, "length": 0 }
+ });
+
+ if (!checkIMESelection("RawClause", true, startoffset, "abcdefgh", "runQueryIMESelectionTest: updating raw text") ||
+ !checkIMESelection("SelectedRawClause", false, 0, "", "runQueryIMESelectionTest: updating raw text") ||
+ !checkIMESelection("ConvertedClause", false, 0, "", "runQueryIMESelectionTest: updating raw text") ||
+ !checkIMESelection("SelectedClause", false, 0, "", "runQueryIMESelectionTest: updating raw text")) {
+ synthesizeComposition({ type: "compositioncommitasis" });
+ return;
+ }
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "ABCDEFGH",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
+ { "length": 3, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
+ { "length": 3, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
+ ]
+ },
+ "caret": { "start": 2, "length": 0 }
+ });
+
+ if (!checkIMESelection("RawClause", false, 0, "", "runQueryIMESelectionTest: starting to convert") ||
+ !checkIMESelection("SelectedRawClause", false, 0, "", "runQueryIMESelectionTest: starting to convert") ||
+ !checkIMESelection("ConvertedClause", true, startoffset + 2, "CDE", "runQueryIMESelectionTest: starting to convert") ||
+ !checkIMESelection("SelectedClause", true, startoffset, "AB", "runQueryIMESelectionTest: starting to convert")) {
+ synthesizeComposition({ type: "compositioncommitasis" });
+ return;
+ }
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "ABCDEFGH",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
+ { "length": 3, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
+ { "length": 3, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
+ ]
+ },
+ "caret": { "start": 5, "length": 0 }
+ });
+
+ if (!checkIMESelection("RawClause", false, 0, "", "runQueryIMESelectionTest: changing selected clause") ||
+ !checkIMESelection("SelectedRawClause", false, 0, "", "runQueryIMESelectionTest: changing selected clause") ||
+ !checkIMESelection("ConvertedClause", true, startoffset, "AB", "runQueryIMESelectionTest: changing selected clause") ||
+ !checkIMESelection("SelectedClause", true, startoffset + 2, "CDE", "runQueryIMESelectionTest: changing selected clause")) {
+ synthesizeComposition({ type: "compositioncommitasis" });
+ return;
+ }
+
+ synthesizeComposition({ type: "compositioncommitasis" });
+
+ if (!checkIMESelection("RawClause", false, 0, "", "runQueryIMESelectionTest: after committing composition") ||
+ !checkIMESelection("SelectedRawClause", false, 0, "", "runQueryIMESelectionTest: after committing composition") ||
+ !checkIMESelection("ConvertedClause", false, 0, "", "runQueryIMESelectionTest: after committing composition") ||
+ !checkIMESelection("SelectedClause", false, 0, "", "runQueryIMESelectionTest: after committing composition")) {
+ return;
+ }
+
+ startoffset = textarea.selectionStart;
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "abcdefgh",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE },
+ { "length": 1, "attr": COMPOSITION_ATTR_SELECTED_RAW_CLAUSE },
+ { "length": 1, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
+ { "length": 1, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE },
+ { "length": 1, "attr": COMPOSITION_ATTR_SELECTED_RAW_CLAUSE },
+ { "length": 1, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
+ { "length": 1, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
+ ]
+ },
+ "caret": { "start": 8, "length": 0 }
+ });
+
+ if (!checkIMESelection("RawClause", true, startoffset, "a", "runQueryIMESelectionTest: unrealistic testcase") ||
+ !checkIMESelection("SelectedRawClause", true, startoffset + 1, "b", "runQueryIMESelectionTest: unrealistic testcase") ||
+ !checkIMESelection("ConvertedClause", true, startoffset + 2, "c", "runQueryIMESelectionTest: unrealistic testcase") ||
+ !checkIMESelection("SelectedClause", true, startoffset + 3, "d", "runQueryIMESelectionTest: unrealistic testcase")) {
+ synthesizeComposition({ type: "compositioncommitasis" });
+ return;
+ }
+
+ synthesizeComposition({ type: "compositioncommitasis" });
+}
+
+function runQueryContentEventRelativeToInsertionPoint()
+{
+ textarea.focus();
+ textarea.value = "0123456789";
+
+ var startoffset = textarea.selectionStart = textarea.selectionEnd = 0;
+
+ if (!checkContentRelativeToSelection(0, 1, 0, "0", "runQueryContentEventRelativeToInsertionPoint[0-0]", "#1") ||
+ !checkContentRelativeToSelection(-1, 1, 0, "0", "runQueryContentEventRelativeToInsertionPoint[0-0]", "#2") ||
+ !checkContentRelativeToSelection(1, 1, 1, "1", "runQueryContentEventRelativeToInsertionPoint[0-0]", "#3") ||
+ !checkContentRelativeToSelection(5, 10, 5, "56789", "runQueryContentEventRelativeToInsertionPoint[0-0]", "#4") ||
+ !checkContentRelativeToSelection(10, 1, 10, "", "runQueryContentEventRelativeToInsertionPoint[0-0]", "#5")) {
+ return;
+ }
+
+ textarea.selectionEnd = 5;
+
+ if (!checkContentRelativeToSelection(0, 1, 0, "0", "runQueryContentEventRelativeToInsertionPoint[0-5]", "#1") ||
+ !checkContentRelativeToSelection(-1, 1, 0, "0", "runQueryContentEventRelativeToInsertionPoint[0-5]", "#2") ||
+ !checkContentRelativeToSelection(1, 1, 1, "1", "runQueryContentEventRelativeToInsertionPoint[0-5]", "#3") ||
+ !checkContentRelativeToSelection(5, 10, 5, "56789", "runQueryContentEventRelativeToInsertionPoint[0-5]", "#4") ||
+ !checkContentRelativeToSelection(10, 1, 10, "", "runQueryContentEventRelativeToInsertionPoint[0-5]"), "#5") {
+ return;
+ }
+
+ startoffset = textarea.selectionStart = textarea.selectionEnd = 4;
+
+ if (!checkContentRelativeToSelection(0, 1, startOffset + 0, "4", "runQueryContentEventRelativeToInsertionPoint[4-4]", "#1") ||
+ !checkContentRelativeToSelection(-1, 1, startOffset - 1, "3", "runQueryContentEventRelativeToInsertionPoint[4-4]", "#2") ||
+ !checkContentRelativeToSelection(1, 1, startOffset + 1, "5", "runQueryContentEventRelativeToInsertionPoint[4-4]", "#3") ||
+ !checkContentRelativeToSelection(5, 10, startOffset + 5, "9", "runQueryContentEventRelativeToInsertionPoint[4-4]", "#4") ||
+ !checkContentRelativeToSelection(10, 1, 10, "", "runQueryContentEventRelativeToInsertionPoint[4-4]", "#5")) {
+ return;
+ }
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "a",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ if (!checkContentRelativeToSelection(0, 1, startOffset + 0, "a", "runQueryContentEventRelativeToInsertionPoint[composition at 4]", "#1") ||
+ !checkContentRelativeToSelection(-1, 1, startOffset - 1, "3", "runQueryContentEventRelativeToInsertionPoint[composition at 4]", "#2") ||
+ !checkContentRelativeToSelection(1, 1, startOffset + 1, "4", "runQueryContentEventRelativeToInsertionPoint[composition at 4]", "#3") ||
+ !checkContentRelativeToSelection(5, 10, startOffset + 5, "89", "runQueryContentEventRelativeToInsertionPoint[composition at 4]", "#4") ||
+ !checkContentRelativeToSelection(11, 1, 11, "", "runQueryContentEventRelativeToInsertionPoint[composition at 4]")) {
+ synthesizeComposition({ type: "compositioncommitasis" });
+ return;
+ }
+
+ synthesizeComposition({ type: "compositioncommitasis" });
+
+ // Move start of composition at first compositionupdate event.
+ function onCompositionUpdate(aEvent)
+ {
+ startoffset = textarea.selectionStart = textarea.selectionEnd = textarea.selectionStart - 1;
+ textarea.removeEventListener("compositionupdate", onCompositionUpdate);
+ }
+ textarea.addEventListener("compositionupdate", onCompositionUpdate);
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "a",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ if (!checkContentRelativeToSelection(0, 1, startOffset + 0, "b", "runQueryContentEventRelativeToInsertionPoint[composition at 3]", "#1") ||
+ !checkContentRelativeToSelection(-1, 1, startOffset - 1, "3", "runQueryContentEventRelativeToInsertionPoint[composition at 3]", "#2") ||
+ !checkContentRelativeToSelection(1, 1, startOffset + 1, "a", "runQueryContentEventRelativeToInsertionPoint[composition at 3]", "#3") ||
+ !checkContentRelativeToSelection(5, 10, startOffset + 5, "789", "runQueryContentEventRelativeToInsertionPoint[composition at 3]", "#4") ||
+ !checkContentRelativeToSelection(12, 1, 12, "", "runQueryContentEventRelativeToInsertionPoint[composition at 3]", "#5")) {
+ synthesizeComposition({ type: "compositioncommitasis" });
+ return;
+ }
+
+ synthesizeComposition({ type: "compositioncommitasis" });
+}
+
+function runCSSTransformTest()
+{
+ textarea.focus();
+ textarea.value = "some text";
+ textarea.selectionStart = textarea.selectionEnd = textarea.value.length;
+ var editorRect = synthesizeQueryEditorRect();
+ if (!checkQueryContentResult(editorRect,
+ "runCSSTransformTest: editorRect")) {
+ return;
+ }
+ var firstCharRect = synthesizeQueryTextRect(0, 1);
+ if (!checkQueryContentResult(firstCharRect,
+ "runCSSTransformTest: firstCharRect")) {
+ return;
+ }
+ var lastCharRect = synthesizeQueryTextRect(textarea.value.length - 1, textarea.value.length);
+ if (!checkQueryContentResult(lastCharRect,
+ "runCSSTransformTest: lastCharRect")) {
+ return;
+ }
+ var caretRect = synthesizeQueryCaretRect(textarea.selectionStart);
+ if (!checkQueryContentResult(caretRect,
+ "runCSSTransformTest: caretRect")) {
+ return;
+ }
+ var caretRectBeforeFirstChar = synthesizeQueryCaretRect(0);
+ if (!checkQueryContentResult(caretRectBeforeFirstChar,
+ "runCSSTransformTest: caretRectBeforeFirstChar")) {
+ return;
+ }
+
+ try {
+ textarea.style.transform = "translate(10px, 15px)";
+ function movedRect(aRect, aX, aY)
+ {
+ return { left: aRect.left + aX, top: aRect.top + aY, width: aRect.width, height: aRect.height }
+ }
+
+ var editorRectTranslated = synthesizeQueryEditorRect();
+ if (!checkQueryContentResult(editorRectTranslated,
+ "runCSSTransformTest: editorRectTranslated, " + textarea.style.transform) ||
+ !checkRect(editorRectTranslated, movedRect(editorRect, 10, 15),
+ "runCSSTransformTest: editorRectTranslated, " + textarea.style.transform)) {
+ return;
+ }
+ var firstCharRectTranslated = synthesizeQueryTextRect(0, 1);
+ if (!checkQueryContentResult(firstCharRectTranslated,
+ "runCSSTransformTest: firstCharRectTranslated, " + textarea.style.transform) ||
+ !checkRect(firstCharRectTranslated, movedRect(firstCharRect, 10, 15),
+ "runCSSTransformTest: firstCharRectTranslated, " + textarea.style.transform)) {
+ return;
+ }
+ var lastCharRectTranslated = synthesizeQueryTextRect(textarea.value.length - 1, textarea.value.length);
+ if (!checkQueryContentResult(lastCharRectTranslated,
+ "runCSSTransformTest: lastCharRectTranslated, " + textarea.style.transform) ||
+ !checkRect(lastCharRectTranslated, movedRect(lastCharRect, 10, 15),
+ "runCSSTransformTest: lastCharRectTranslated, " + textarea.style.transform)) {
+ return;
+ }
+ var caretRectTranslated = synthesizeQueryCaretRect(textarea.selectionStart);
+ if (!checkQueryContentResult(caretRectTranslated,
+ "runCSSTransformTest: caretRectTranslated, " + textarea.style.transform) ||
+ !checkRect(caretRectTranslated, movedRect(caretRect, 10, 15),
+ "runCSSTransformTest: caretRectTranslated, " + textarea.style.transform)) {
+ return;
+ }
+ var caretRectBeforeFirstCharTranslated = synthesizeQueryCaretRect(0);
+ if (!checkQueryContentResult(caretRectBeforeFirstCharTranslated,
+ "runCSSTransformTest: caretRectBeforeFirstCharTranslated, " + textarea.style.transform) ||
+ !checkRect(caretRectBeforeFirstCharTranslated, movedRect(caretRectBeforeFirstChar, 10, 15),
+ "runCSSTransformTest: caretRectBeforeFirstCharTranslated, " + textarea.style.transform)) {
+ return;
+ }
+ var firstCharRectTranslatedAsArray = synthesizeQueryTextRectArray(0, 1);
+ if (!checkQueryContentResult(firstCharRectTranslatedAsArray, "runCSSTransformTest: firstCharRectTranslatedAsArray, " + textarea.style.transform) ||
+ !checkRectArray(firstCharRectTranslatedAsArray, [firstCharRectTranslated], "runCSSTransformTest: firstCharRectTranslatedAsArray, " + textarea.style.transform)) {
+ return;
+ }
+ var lastCharRectTranslatedAsArray = synthesizeQueryTextRectArray(textarea.value.length - 1, textarea.value.length);
+ if (!checkQueryContentResult(lastCharRectTranslatedAsArray, "runCSSTransformTest: lastCharRectTranslatedAsArray, " + textarea.style.transform) ||
+ !checkRectArray(lastCharRectTranslatedAsArray, [lastCharRectTranslated], "runCSSTransformTest: lastCharRectTranslatedAsArray, " + textarea.style.transform)) {
+ return;
+ }
+
+ // XXX It's too difficult to check the result with scale and rotate...
+ // For now, let's check if query text rect and query text rect array returns same rect.
+ textarea.style.transform = "scale(1.5)";
+ firstCharRectTranslated = synthesizeQueryTextRect(0, 1);
+ if (!checkQueryContentResult(firstCharRectTranslated,
+ "runCSSTransformTest: firstCharRectTranslated, " + textarea.style.transform)) {
+ return;
+ }
+ lastCharRectTranslated = synthesizeQueryTextRect(textarea.value.length - 1, textarea.value.length);
+ if (!checkQueryContentResult(lastCharRectTranslated,
+ "runCSSTransformTest: lastCharRectTranslated, " + textarea.style.transform)) {
+ return;
+ }
+ firstCharRectTranslatedAsArray = synthesizeQueryTextRectArray(0, 1);
+ if (!checkQueryContentResult(firstCharRectTranslatedAsArray, "runCSSTransformTest: firstCharRectTranslatedAsArray, " + textarea.style.transform) ||
+ !checkRectArray(firstCharRectTranslatedAsArray, [firstCharRectTranslated], "runCSSTransformTest: firstCharRectTranslatedAsArray, " + textarea.style.transform)) {
+ return;
+ }
+ lastCharRectTranslatedAsArray = synthesizeQueryTextRectArray(textarea.value.length - 1, textarea.value.length);
+ if (!checkQueryContentResult(lastCharRectTranslatedAsArray, "runCSSTransformTest: lastCharRectTranslatedAsArray, " + textarea.style.transform) ||
+ !checkRectArray(lastCharRectTranslatedAsArray, [lastCharRectTranslated], "runCSSTransformTest: lastCharRectTranslatedAsArray, " + textarea.style.transform)) {
+ return;
+ }
+
+ textarea.style.transform = "rotate(30deg)";
+ firstCharRectTranslated = synthesizeQueryTextRect(0, 1);
+ if (!checkQueryContentResult(firstCharRectTranslated,
+ "runCSSTransformTest: firstCharRectTranslated, " + textarea.style.transform)) {
+ return;
+ }
+ lastCharRectTranslated = synthesizeQueryTextRect(textarea.value.length - 1, textarea.value.length);
+ if (!checkQueryContentResult(lastCharRectTranslated,
+ "runCSSTransformTest: lastCharRectTranslated, " + textarea.style.transform)) {
+ return;
+ }
+ firstCharRectTranslatedAsArray = synthesizeQueryTextRectArray(0, 1);
+ if (!checkQueryContentResult(firstCharRectTranslatedAsArray, "runCSSTransformTest: firstCharRectTranslatedAsArray, " + textarea.style.transform) ||
+ !checkRectArray(firstCharRectTranslatedAsArray, [firstCharRectTranslated], "runCSSTransformTest: firstCharRectTranslatedAsArray, " + textarea.style.transform)) {
+ return;
+ }
+ lastCharRectTranslatedAsArray = synthesizeQueryTextRectArray(textarea.value.length - 1, textarea.value.length);
+ if (!checkQueryContentResult(lastCharRectTranslatedAsArray, "runCSSTransformTest: lastCharRectTranslatedAsArray, " + textarea.style.transform) ||
+ !checkRectArray(lastCharRectTranslatedAsArray, [lastCharRectTranslated], "runCSSTransformTest: lastCharRectTranslatedAsArray, " + textarea.style.transform)) {
+ return;
+ }
+ } finally {
+ textarea.style.transform = "";
+ }
+}
+
+function runBug722639Test()
+{
+ textarea.focus();
+ textarea.value = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
+ textarea.value += textarea.value;
+ textarea.value += textarea.value; // 80 characters
+
+ var firstLine = synthesizeQueryTextRect(0, 1);
+ if (!checkQueryContentResult(firstLine,
+ "runBug722639Test: firstLine")) {
+ return;
+ }
+ ok(true, "runBug722639Test: 1st line, top=" + firstLine.top + ", left=" + firstLine.left);
+ var firstLineAsArray = synthesizeQueryTextRectArray(0, 1);
+ if (!checkQueryContentResult(firstLineAsArray, "runBug722639Test: 1st line as array") ||
+ !checkRectArray(firstLineAsArray, [firstLine], "runBug722639Test: 1st line as array should match with text rect result")) {
+ return;
+ }
+ if (kLFLen > 1) {
+ var firstLineLF = synthesizeQueryTextRect(1, 1);
+ if (!checkQueryContentResult(firstLineLF,
+ "runBug722639Test: firstLineLF")) {
+ return;
+ }
+ is(firstLineLF.top, firstLine.top, "runBug722639Test: 1st line's \\n rect should be same as 1st line's \\r rect");
+ is(firstLineLF.left, firstLine.left, "runBug722639Test: 1st line's \\n rect should be same as 1st line's \\r rect");
+ is(firstLineLF.height, firstLine.height, "runBug722639Test: 1st line's \\n rect should be same as 1st line's \\r rect");
+ is(firstLineLF.width, firstLine.width, "runBug722639Test: 1st line's \\n rect should be same as 1st line's \\r rect");
+ var firstLineLFAsArray = synthesizeQueryTextRectArray(1, 1);
+ if (!checkQueryContentResult(firstLineLFAsArray, "runBug722639Test: 1st line's \\n rect as array") ||
+ !checkRectArray(firstLineLFAsArray, [firstLineLF], "runBug722639Test: 1st line's rect as array should match with text rect result")) {
+ return;
+ }
+ }
+ var secondLine = synthesizeQueryTextRect(kLFLen, 1);
+ if (!checkQueryContentResult(secondLine,
+ "runBug722639Test: secondLine")) {
+ return;
+ }
+ ok(true, "runBug722639Test: 2nd line, top=" + secondLine.top + ", left=" + secondLine.left);
+ var secondLineAsArray = synthesizeQueryTextRectArray(kLFLen, 1);
+ if (!checkQueryContentResult(secondLineAsArray, "runBug722639Test: 2nd line as array") ||
+ !checkRectArray(secondLineAsArray, [secondLine], "runBug722639Test: 2nd line as array should match with text rect result")) {
+ return;
+ }
+ if (kLFLen > 1) {
+ var secondLineLF = synthesizeQueryTextRect(kLFLen + 1, 1);
+ if (!checkQueryContentResult(secondLineLF,
+ "runBug722639Test: secondLineLF")) {
+ return;
+ }
+ is(secondLineLF.top, secondLine.top, "runBug722639Test: 2nd line's \\n rect should be same as 2nd line's \\r rect");
+ is(secondLineLF.left, secondLine.left, "runBug722639Test: 2nd line's \\n rect should be same as 2nd line's \\r rect");
+ is(secondLineLF.height, secondLine.height, "runBug722639Test: 2nd line's \\n rect should be same as 2nd line's \\r rect");
+ is(secondLineLF.width, secondLine.width, "runBug722639Test: 2nd line's \\n rect should be same as 2nd line's \\r rect");
+ var secondLineLFAsArray = synthesizeQueryTextRectArray(kLFLen + 1, 1);
+ if (!checkQueryContentResult(secondLineLFAsArray, "runBug722639Test: 2nd line's \\n rect as array") ||
+ !checkRectArray(secondLineLFAsArray, [secondLineLF], "runBug722639Test: 2nd line's rect as array should match with text rect result")) {
+ return;
+ }
+ }
+ var lineHeight = secondLine.top - firstLine.top;
+ ok(lineHeight > 0,
+ "runBug722639Test: lineHeight must be positive");
+ is(secondLine.left, firstLine.left,
+ "runBug722639Test: the left value must be always same value");
+ is(secondLine.height, firstLine.height,
+ "runBug722639Test: the height must be always same value");
+ var previousTop = secondLine.top;
+ for (var i = 3; i <= textarea.value.length + 1; i++) {
+ var currentLine = synthesizeQueryTextRect(kLFLen * (i - 1), 1);
+ if (!checkQueryContentResult(currentLine,
+ "runBug722639Test: " + i + "th currentLine")) {
+ return;
+ }
+ ok(true, "runBug722639Test: " + i + "th line, top=" + currentLine.top + ", left=" + currentLine.left);
+ var currentLineAsArray = synthesizeQueryTextRectArray(kLFLen * (i - 1), 1);
+ if (!checkQueryContentResult(currentLineAsArray, "runBug722639Test: " + i + "th line as array") ||
+ !checkRectArray(currentLineAsArray, [currentLine], "runBug722639Test: " + i + "th line as array should match with text rect result")) {
+ return;
+ }
+ // NOTE: the top position may be 1px larger or smaller than other lines
+ // due to sub pixel positioning.
+ if (Math.abs(currentLine.top - (previousTop + lineHeight)) <= 1) {
+ ok(true, "runBug722639Test: " + i + "th line's top is expected");
+ } else {
+ is(currentLine.top, previousTop + lineHeight,
+ "runBug722639Test: " + i + "th line's top is unexpected");
+ }
+ is(currentLine.left, firstLine.left,
+ "runBug722639Test: " + i + "th line's left is unexpected");
+ is(currentLine.height, firstLine.height,
+ "runBug722639Test: " + i + "th line's height is unexpected");
+ if (kLFLen > 1) {
+ var currentLineLF = synthesizeQueryTextRect(kLFLen * (i - 1) + 1, 1);
+ if (!checkQueryContentResult(currentLineLF,
+ "runBug722639Test: " + i + "th currentLineLF")) {
+ return;
+ }
+ is(currentLineLF.top, currentLine.top, "runBug722639Test: " + i + "th line's \\n rect should be same as same line's \\r rect");
+ is(currentLineLF.left, currentLine.left, "runBug722639Test: " + i + "th line's \\n rect should be same as same line's \\r rect");
+ is(currentLineLF.height, currentLine.height, "runBug722639Test: " + i + "th line's \\n rect should be same as same line's \\r rect");
+ is(currentLineLF.width, currentLine.width, "runBug722639Test: " + i + "th line's \\n rect should be same as same line's \\r rect");
+ var currentLineLFAsArray = synthesizeQueryTextRectArray(kLFLen * (i - 1) + 1, 1);
+ if (!checkQueryContentResult(currentLineLFAsArray, "runBug722639Test: " + i + "th line's \\n rect as array") ||
+ !checkRectArray(currentLineLFAsArray, [currentLineLF], "runBug722639Test: " + i + "th line's rect as array should match with text rect result")) {
+ return;
+ }
+ }
+ previousTop = currentLine.top;
+ }
+}
+
+function runForceCommitTest()
+{
+ var events;
+ function eventHandler(aEvent)
+ {
+ events.push(aEvent);
+ }
+ window.addEventListener("compositionstart", eventHandler, true);
+ window.addEventListener("compositionupdate", eventHandler, true);
+ window.addEventListener("compositionend", eventHandler, true);
+ window.addEventListener("input", eventHandler, true);
+ window.addEventListener("text", eventHandler, true);
+
+ // Make the composition in textarea commit by click in the textarea
+ textarea.focus();
+ textarea.value = "";
+
+ events = [];
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ is(events.length, 4,
+ "runForceCommitTest: wrong event count #1");
+ is(events[0].type, "compositionstart",
+ "runForceCommitTest: the 1st event must be compositionstart #1");
+ is(events[1].type, "compositionupdate",
+ "runForceCommitTest: the 2nd event must be compositionupdate #1");
+ is(events[2].type, "text",
+ "runForceCommitTest: the 3rd event must be text #1");
+ is(events[3].type, "input",
+ "runForceCommitTest: the 4th event must be input #1");
+
+ events = [];
+ synthesizeMouseAtCenter(textarea, {});
+
+ is(events.length, 3,
+ "runForceCommitTest: wrong event count #2");
+ is(events[0].type, "text",
+ "runForceCommitTest: the 1st event must be text #2");
+ is(events[1].type, "compositionend",
+ "runForceCommitTest: the 2nd event must be compositionend #2");
+ is(events[2].type, "input",
+ "runForceCommitTest: the 3rd event must be input #2");
+ is(events[1].data, "\u306E",
+ "runForceCommitTest: compositionend has wrong data #2");
+ is(events[0].target, textarea,
+ "runForceCommitTest: The 1st event was fired on wrong event target #2");
+ is(events[1].target, textarea,
+ "runForceCommitTest: The 2nd event was fired on wrong event target #2");
+ is(events[2].target, textarea,
+ "runForceCommitTest: The 3rd event was fired on wrong event target #2");
+ ok(!getEditorIMESupport(textarea).isComposing,
+ "runForceCommitTest: the textarea still has composition #2");
+ is(textarea.value, "\u306E",
+ "runForceCommitTest: the textarea doesn't have the committed text #2");
+
+ // Make the composition in textarea commit by click in another editor (input)
+ textarea.focus();
+ textarea.value = "";
+ input.value = "";
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ events = [];
+ synthesizeMouseAtCenter(input, {});
+
+ is(events.length, 3,
+ "runForceCommitTest: wrong event count #3");
+ is(events[0].type, "text",
+ "runForceCommitTest: the 1st event must be text #3");
+ is(events[1].type, "compositionend",
+ "runForceCommitTest: the 2nd event must be compositionend #3");
+ is(events[2].type, "input",
+ "runForceCommitTest: the 3rd event must be input #3");
+ is(events[1].data, "\u306E",
+ "runForceCommitTest: compositionend has wrong data #3");
+ is(events[0].target, textarea,
+ "runForceCommitTest: The 1st event was fired on wrong event target #3");
+ is(events[1].target, textarea,
+ "runForceCommitTest: The 2nd event was fired on wrong event target #3");
+ is(events[2].target, textarea,
+ "runForceCommitTest: The 3rd event was fired on wrong event target #3");
+ ok(!getEditorIMESupport(textarea).isComposing,
+ "runForceCommitTest: the textarea still has composition #3");
+ ok(!getEditorIMESupport(input).isComposing,
+ "runForceCommitTest: the input has composition #3");
+ is(textarea.value, "\u306E",
+ "runForceCommitTest: the textarea doesn't have the committed text #3");
+ is(input.value, "",
+ "runForceCommitTest: the input has the committed text? #3");
+
+ // Make the composition in textarea commit by blur()
+ textarea.focus();
+ textarea.value = "";
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ events = [];
+ textarea.blur();
+
+ is(events.length, 3,
+ "runForceCommitTest: wrong event count #4");
+ is(events[0].type, "text",
+ "runForceCommitTest: the 1st event must be text #4");
+ is(events[1].type, "compositionend",
+ "runForceCommitTest: the 2nd event must be compositionend #4");
+ is(events[2].type, "input",
+ "runForceCommitTest: the 3rd event must be input #4");
+ is(events[1].data, "\u306E",
+ "runForceCommitTest: compositionend has wrong data #4");
+ is(events[0].target, textarea,
+ "runForceCommitTest: The 1st event was fired on wrong event target #4");
+ is(events[1].target, textarea,
+ "runForceCommitTest: The 2nd event was fired on wrong event target #4");
+ is(events[2].target, textarea,
+ "runForceCommitTest: The 3rd event was fired on wrong event target #4");
+ ok(!getEditorIMESupport(textarea).isComposing,
+ "runForceCommitTest: the textarea still has composition #4");
+ is(textarea.value, "\u306E",
+ "runForceCommitTest: the textarea doesn't have the committed text #4");
+
+ // Make the composition in textarea commit by input.focus()
+ textarea.focus();
+ textarea.value = "";
+ input.value = "";
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ events = [];
+ input.focus();
+
+ is(events.length, 3,
+ "runForceCommitTest: wrong event count #5");
+ is(events[0].type, "text",
+ "runForceCommitTest: the 1st event must be text #5");
+ is(events[1].type, "compositionend",
+ "runForceCommitTest: the 2nd event must be compositionend #5");
+ is(events[2].type, "input",
+ "runForceCommitTest: the 3rd event must be input #5");
+ is(events[1].data, "\u306E",
+ "runForceCommitTest: compositionend has wrong data #5");
+ is(events[0].target, textarea,
+ "runForceCommitTest: The 1st event was fired on wrong event target #5");
+ is(events[1].target, textarea,
+ "runForceCommitTest: The 2nd event was fired on wrong event target #5");
+ is(events[2].target, textarea,
+ "runForceCommitTest: The 3rd event was fired on wrong event target #5");
+ ok(!getEditorIMESupport(textarea).isComposing,
+ "runForceCommitTest: the textarea still has composition #5");
+ ok(!getEditorIMESupport(input).isComposing,
+ "runForceCommitTest: the input has composition #5");
+ is(textarea.value, "\u306E",
+ "runForceCommitTest: the textarea doesn't have the committed text #5");
+ is(input.value, "",
+ "runForceCommitTest: the input has the committed text? #5");
+
+ // Make the composition in textarea commit by click in another document's editor
+ textarea.focus();
+ textarea.value = "";
+ textareaInFrame.value = "";
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ events = [];
+ synthesizeMouseAtCenter(textareaInFrame, {}, iframe.contentWindow);
+
+ is(events.length, 3,
+ "runForceCommitTest: wrong event count #6");
+ is(events[0].type, "text",
+ "runForceCommitTest: the 1st event must be text #6");
+ is(events[1].type, "compositionend",
+ "runForceCommitTest: the 2nd event must be compositionend #6");
+ is(events[2].type, "input",
+ "runForceCommitTest: the 3rd event must be input #6");
+ is(events[1].data, "\u306E",
+ "runForceCommitTest: compositionend has wrong data #6");
+ is(events[0].target, textarea,
+ "runForceCommitTest: The 1st event was fired on wrong event target #6");
+ is(events[1].target, textarea,
+ "runForceCommitTest: The 2nd event was fired on wrong event target #6");
+ is(events[2].target, textarea,
+ "runForceCommitTest: The 3rd event was fired on wrong event target #6");
+ ok(!getEditorIMESupport(textarea).isComposing,
+ "runForceCommitTest: the textarea still has composition #6");
+ ok(!getEditorIMESupport(textareaInFrame).isComposing,
+ "runForceCommitTest: the textarea in frame has composition #6");
+ is(textarea.value, "\u306E",
+ "runForceCommitTest: the textarea doesn't have the committed text #6");
+ is(textareaInFrame.value, "",
+ "runForceCommitTest: the textarea in frame has the committed text? #6");
+
+ // Make the composition in textarea commit by another document's editor's focus()
+ textarea.focus();
+ textarea.value = "";
+ textareaInFrame.value = "";
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ events = [];
+ textareaInFrame.focus();
+
+ is(events.length, 3,
+ "runForceCommitTest: wrong event count #7");
+ is(events[0].type, "text",
+ "runForceCommitTest: the 1st event must be text #7");
+ is(events[1].type, "compositionend",
+ "runForceCommitTest: the 2nd event must be compositionend #7");
+ is(events[2].type, "input",
+ "runForceCommitTest: the 3rd event must be input #7");
+ is(events[1].data, "\u306E",
+ "runForceCommitTest: compositionend has wrong data #7");
+ is(events[0].target, textarea,
+ "runForceCommitTest: The 1st event was fired on wrong event target #7");
+ is(events[1].target, textarea,
+ "runForceCommitTest: The 2nd event was fired on wrong event target #7");
+ is(events[2].target, textarea,
+ "runForceCommitTest: The 3rd event was fired on wrong event target #7");
+ ok(!getEditorIMESupport(textarea).isComposing,
+ "runForceCommitTest: the textarea still has composition #7");
+ ok(!getEditorIMESupport(textareaInFrame).isComposing,
+ "runForceCommitTest: the textarea in frame has composition #7");
+ is(textarea.value, "\u306E",
+ "runForceCommitTest: the textarea doesn't have the committed text #7");
+ is(textareaInFrame.value, "",
+ "runForceCommitTest: the textarea in frame has the committed text? #7");
+
+ // Make the composition in a textarea commit by click in another editable document
+ textarea.focus();
+ textarea.value = "";
+ iframe2.contentDocument.body.innerHTML = "Text in the Body";
+ var iframe2BodyInnerHTML = iframe2.contentDocument.body.innerHTML;
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ events = [];
+ synthesizeMouseAtCenter(iframe2.contentDocument.body, {}, iframe2.contentWindow);
+
+ is(events.length, 3,
+ "runForceCommitTest: wrong event count #8");
+ is(events[0].type, "text",
+ "runForceCommitTest: the 1st event must be text #8");
+ is(events[1].type, "compositionend",
+ "runForceCommitTest: the 2nd event must be compositionend #8");
+ is(events[2].type, "input",
+ "runForceCommitTest: the 3rd event must be input #8");
+ is(events[1].data, "\u306E",
+ "runForceCommitTest: compositionend has wrong data #8");
+ is(events[0].target, textarea,
+ "runForceCommitTest: The 1st event was fired on wrong event target #8");
+ is(events[1].target, textarea,
+ "runForceCommitTest: The 2nd event was fired on wrong event target #8");
+ is(events[2].target, textarea,
+ "runForceCommitTest: The 3rd event was fired on wrong event target #8");
+ ok(!getEditorIMESupport(textarea).isComposing,
+ "runForceCommitTest: the textarea still has composition #8");
+ ok(!getHTMLEditorIMESupport(iframe2.contentWindow).isComposing,
+ "runForceCommitTest: the editable document has composition #8");
+ is(textarea.value, "\u306E",
+ "runForceCommitTest: the textarea doesn't have the committed text #8");
+ is(iframe2.contentDocument.body.innerHTML, iframe2BodyInnerHTML,
+ "runForceCommitTest: the editable document has the committed text? #8");
+
+ // Make the composition in an editable document commit by click in it
+ iframe2.contentWindow.focus();
+ iframe2.contentDocument.body.innerHTML = "Text in the Body";
+ iframe2BodyInnerHTML = iframe2.contentDocument.body.innerHTML;
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ }, iframe2.contentWindow);
+
+ events = [];
+ synthesizeMouseAtCenter(iframe2.contentDocument.body, {}, iframe2.contentWindow);
+
+ is(events.length, 3,
+ "runForceCommitTest: wrong event count #9");
+ is(events[0].type, "text",
+ "runForceCommitTest: the 1st event must be text #9");
+ is(events[1].type, "compositionend",
+ "runForceCommitTest: the 2nd event must be compositionend #9");
+ is(events[2].type, "input",
+ "runForceCommitTest: the 3rd event must be input #9");
+ is(events[1].data, "\u306E",
+ "runForceCommitTest: compositionend has wrong data #9");
+ is(events[0].target, iframe2.contentDocument.body,
+ "runForceCommitTest: The 1st event was fired on wrong event target #9");
+ is(events[1].target, iframe2.contentDocument.body,
+ "runForceCommitTest: The 2nd event was fired on wrong event target #9");
+ is(events[2].target, iframe2.contentDocument.body,
+ "runForceCommitTest: The 3rd event was fired on wrong event target #9");
+ ok(!getHTMLEditorIMESupport(iframe2.contentWindow).isComposing,
+ "runForceCommitTest: the editable document still has composition #9");
+ ok(iframe2.contentDocument.body.innerHTML != iframe2BodyInnerHTML &&
+ iframe2.contentDocument.body.innerHTML.indexOf("\u306E") >= 0,
+ "runForceCommitTest: the editable document doesn't have the committed text #9");
+
+ // Make the composition in an editable document commit by click in another document's editor
+ textarea.value = "";
+ iframe2.contentWindow.focus();
+ iframe2.contentDocument.body.innerHTML = "Text in the Body";
+ iframe2BodyInnerHTML = iframe2.contentDocument.body.innerHTML;
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ }, iframe2.contentWindow);
+
+ events = [];
+ synthesizeMouseAtCenter(textarea, {});
+
+ is(events.length, 3,
+ "runForceCommitTest: wrong event count #10");
+ is(events[0].type, "text",
+ "runForceCommitTest: the 1st event must be text #10");
+ is(events[1].type, "compositionend",
+ "runForceCommitTest: the 2nd event must be compositionend #10");
+ is(events[2].type, "input",
+ "runForceCommitTest: the 3rd event must be input #10");
+ is(events[1].data, "\u306E",
+ "runForceCommitTest: compositionend has wrong data #10");
+ is(events[0].target, iframe2.contentDocument.body,
+ "runForceCommitTest: The 1st event was fired on wrong event target #10");
+ is(events[1].target, iframe2.contentDocument.body,
+ "runForceCommitTest: The 2nd event was fired on wrong event target #10");
+ is(events[2].target, iframe2.contentDocument.body,
+ "runForceCommitTest: The 3rd event was fired on wrong event target #10");
+ ok(!getHTMLEditorIMESupport(iframe2.contentWindow).isComposing,
+ "runForceCommitTest: the editable document still has composition #10");
+ ok(!getEditorIMESupport(textarea).isComposing,
+ "runForceCommitTest: the textarea has composition #10");
+ ok(iframe2.contentDocument.body.innerHTML != iframe2BodyInnerHTML &&
+ iframe2.contentDocument.body.innerHTML.indexOf("\u306E") >= 0,
+ "runForceCommitTest: the editable document doesn't have the committed text #10");
+ is(textarea.value, "",
+ "runForceCommitTest: the textarea has the committed text? #10");
+
+ // Make the composition in an editable document commit by click in the another editable document
+ iframe2.contentWindow.focus();
+ iframe2.contentDocument.body.innerHTML = "Text in the Body";
+ iframe2BodyInnerHTML = iframe2.contentDocument.body.innerHTML;
+ iframe3.contentDocument.body.innerHTML = "Text in the Body";
+ iframe3BodyInnerHTML = iframe2.contentDocument.body.innerHTML;
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ }, iframe2.contentWindow);
+
+ events = [];
+ synthesizeMouseAtCenter(iframe3.contentDocument.body, {}, iframe3.contentWindow);
+
+ is(events.length, 3,
+ "runForceCommitTest: wrong event count #11");
+ is(events[0].type, "text",
+ "runForceCommitTest: the 1st event must be text #11");
+ is(events[1].type, "compositionend",
+ "runForceCommitTest: the 2nd event must be compositionend #11");
+ is(events[2].type, "input",
+ "runForceCommitTest: the 3rd event must be input #11");
+ is(events[1].data, "\u306E",
+ "runForceCommitTest: compositionend has wrong data #11");
+ is(events[0].target, iframe2.contentDocument.body,
+ "runForceCommitTest: The 1st event was fired on wrong event target #11");
+ is(events[1].target, iframe2.contentDocument.body,
+ "runForceCommitTest: The 2nd event was fired on wrong event target #11");
+ is(events[2].target, iframe2.contentDocument.body,
+ "runForceCommitTest: The 3rd event was fired on wrong event target #11");
+ ok(!getHTMLEditorIMESupport(iframe2.contentWindow).isComposing,
+ "runForceCommitTest: the editable document still has composition #11");
+ ok(!getHTMLEditorIMESupport(iframe3.contentWindow).isComposing,
+ "runForceCommitTest: the other editable document has composition #11");
+ ok(iframe2.contentDocument.body.innerHTML != iframe2BodyInnerHTML &&
+ iframe2.contentDocument.body.innerHTML.indexOf("\u306E") >= 0,
+ "runForceCommitTest: the editable document doesn't have the committed text #11");
+ is(iframe3.contentDocument.body.innerHTML, iframe3BodyInnerHTML,
+ "runForceCommitTest: the other editable document has the committed text? #11");
+
+ input.focus();
+ input.value = "";
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ events = [];
+ input.value = "set value";
+
+ is(events.length, 3,
+ "runForceCommitTest: wrong event count #12");
+ is(events[0].type, "text",
+ "runForceCommitTest: the 1st event must be text #12");
+ is(events[1].type, "compositionend",
+ "runForceCommitTest: the 2nd event must be compositionend #12");
+ is(events[2].type, "input",
+ "runForceCommitTest: the 3rd event must be input #12");
+ is(events[1].data, "\u306E",
+ "runForceCommitTest: compositionend has wrong data #12");
+ is(events[0].target, input,
+ "runForceCommitTest: The 1st event was fired on wrong event target #12");
+ is(events[1].target, input,
+ "runForceCommitTest: The 2nd event was fired on wrong event target #12");
+ is(events[2].target, input,
+ "runForceCommitTest: The 3rd event was fired on wrong event target #12");
+ ok(!getEditorIMESupport(input).isComposing,
+ "runForceCommitTest: the input still has composition #12");
+ is(input.value, "set value",
+ "runForceCommitTest: the input doesn't have the set text #12");
+
+ textarea.focus();
+ textarea.value = "";
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ events = [];
+ textarea.value = "set value";
+
+ is(events.length, 3,
+ "runForceCommitTest: wrong event count #13");
+ is(events[0].type, "text",
+ "runForceCommitTest: the 1st event must be text #13");
+ is(events[1].type, "compositionend",
+ "runForceCommitTest: the 2nd event must be compositionend #13");
+ is(events[2].type, "input",
+ "runForceCommitTest: the 3rd event must be input #13");
+ is(events[1].data, "\u306E",
+ "runForceCommitTest: compositionend has wrong data #13");
+ is(events[0].target, textarea,
+ "runForceCommitTest: The 1st event was fired on wrong event target #13");
+ is(events[1].target, textarea,
+ "runForceCommitTest: The 2nd event was fired on wrong event target #13");
+ is(events[2].target, textarea,
+ "runForceCommitTest: The 3rd event was fired on wrong event target #13");
+ ok(!getEditorIMESupport(textarea).isComposing,
+ "runForceCommitTest: the textarea still has composition #13");
+ is(textarea.value, "set value",
+ "runForceCommitTest: the textarea doesn't have the set text #13");
+
+ input.focus();
+ input.value = "";
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ events = [];
+ input.value += " appended value";
+
+ is(events.length, 3,
+ "runForceCommitTest: wrong event count #14");
+ is(events[0].type, "text",
+ "runForceCommitTest: the 1st event must be text #14");
+ is(events[1].type, "compositionend",
+ "runForceCommitTest: the 2nd event must be compositionend #14");
+ is(events[2].type, "input",
+ "runForceCommitTest: the 3rd event must be input #14");
+ is(events[1].data, "\u306E",
+ "runForceCommitTest: compositionend has wrong data #14");
+ is(events[0].target, input,
+ "runForceCommitTest: The 1st event was fired on wrong event target #14");
+ is(events[1].target, input,
+ "runForceCommitTest: The 2nd event was fired on wrong event target #14");
+ is(events[2].target, input,
+ "runForceCommitTest: The 3rd event was fired on wrong event target #14");
+ ok(!getEditorIMESupport(input).isComposing,
+ "runForceCommitTest: the input still has composition #14");
+ is(input.value, "\u306E appended value",
+ "runForceCommitTest: the input should have both composed text and appended text #14");
+
+ input.focus();
+ input.value = "abcd";
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ events = [];
+ input.value = "abcd\u306E";
+
+ is(events.length, 0,
+ "runForceCommitTest: setting same value to input with composition shouldn't cause any events #15");
+ is(input.value, "abcd\u306E",
+ "runForceCommitTest: the input has unexpected value #15");
+
+ input.blur(); // commit composition
+
+ textarea.focus();
+ textarea.value = "abcd";
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ events = [];
+ textarea.value = "abcd\u306E";
+
+ is(events.length, 0,
+ "runForceCommitTest: setting same value to textarea with composition shouldn't cause any events #16");
+ is(textarea.value, "abcd\u306E",
+ "runForceCommitTest: the input has unexpected value #16");
+
+ textarea.blur(); // commit composition
+
+ window.removeEventListener("compositionstart", eventHandler, true);
+ window.removeEventListener("compositionupdate", eventHandler, true);
+ window.removeEventListener("compositionend", eventHandler, true);
+ window.removeEventListener("input", eventHandler, true);
+ window.removeEventListener("text", eventHandler, true);
+}
+
+function runNestedSettingValue()
+{
+ var isTesting = false;
+ var events = [];
+ function eventHandler(aEvent)
+ {
+ events.push(aEvent);
+ if (isTesting) {
+ aEvent.target.value += aEvent.type + ", ";
+ }
+ }
+ window.addEventListener("compositionstart", eventHandler, true);
+ window.addEventListener("compositionupdate", eventHandler, true);
+ window.addEventListener("compositionend", eventHandler, true);
+ window.addEventListener("input", eventHandler, true);
+ window.addEventListener("text", eventHandler, true);
+
+ textarea.focus();
+ textarea.value = "";
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ events = [];
+ isTesting = true;
+ textarea.value = "first setting value, ";
+ isTesting = false;
+
+ is(events.length, 3,
+ "runNestedSettingValue: wrong event count #1");
+ is(events[0].type, "text",
+ "runNestedSettingValue: the 1st event must be text #1");
+ is(events[1].type, "compositionend",
+ "runNestedSettingValue: the 2nd event must be compositionend #1");
+ is(events[2].type, "input",
+ "runNestedSettingValue: the 3rd event must be input #1");
+ is(events[1].data, "\u306E",
+ "runNestedSettingValue: compositionend has wrong data #1");
+ is(events[0].target, textarea,
+ "runNestedSettingValue: The 1st event was fired on wrong event target #1");
+ is(events[1].target, textarea,
+ "runNestedSettingValue: The 2nd event was fired on wrong event target #1");
+ is(events[2].target, textarea,
+ "runNestedSettingValue: The 3rd event was fired on wrong event target #1");
+ ok(!getEditorIMESupport(textarea).isComposing,
+ "runNestedSettingValue: the textarea still has composition #1");
+ is(textarea.value, "first setting value, text, compositionend, input, ",
+ "runNestedSettingValue: the textarea should have all string set to value attribute");
+
+ input.focus();
+ input.value = "";
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ events = [];
+ isTesting = true;
+ input.value = "first setting value, ";
+ isTesting = false;
+
+ is(events.length, 3,
+ "runNestedSettingValue: wrong event count #2");
+ is(events[0].type, "text",
+ "runNestedSettingValue: the 1st event must be text #2");
+ is(events[1].type, "compositionend",
+ "runNestedSettingValue: the 2nd event must be compositionend #2");
+ is(events[2].type, "input",
+ "runNestedSettingValue: the 3rd event must be input #2");
+ is(events[1].data, "\u306E",
+ "runNestedSettingValue: compositionend has wrong data #2");
+ is(events[0].target, input,
+ "runNestedSettingValue: The 1st event was fired on wrong event target #2");
+ is(events[1].target, input,
+ "runNestedSettingValue: The 2nd event was fired on wrong event target #2");
+ is(events[2].target, input,
+ "runNestedSettingValue: The 3rd event was fired on wrong event target #2");
+ ok(!getEditorIMESupport(input).isComposing,
+ "runNestedSettingValue: the input still has composition #2");
+ is(textarea.value, "first setting value, text, compositionend, input, ",
+ "runNestedSettingValue: the input should have all string set to value attribute #2");
+
+ textarea.focus();
+ textarea.value = "";
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ events = [];
+ isTesting = true;
+ textarea.setRangeText("first setting value, ");
+ isTesting = false;
+
+ is(events.length, 3,
+ "runNestedSettingValue: wrong event count #3");
+ is(events[0].type, "text",
+ "runNestedSettingValue: the 1st event must be text #3");
+ is(events[1].type, "compositionend",
+ "runNestedSettingValue: the 2nd event must be compositionend #3");
+ is(events[2].type, "input",
+ "runNestedSettingValue: the 3rd event must be input #3");
+ is(events[1].data, "\u306E",
+ "runNestedSettingValue: compositionend has wrong data #3");
+ is(events[0].target, textarea,
+ "runNestedSettingValue: The 1st event was fired on wrong event target #3");
+ is(events[1].target, textarea,
+ "runNestedSettingValue: The 2nd event was fired on wrong event target #3");
+ is(events[2].target, textarea,
+ "runNestedSettingValue: The 3rd event was fired on wrong event target #3");
+ ok(!getEditorIMESupport(textarea).isComposing,
+ "runNestedSettingValue: the textarea still has composition #3");
+ is(textarea.value, "\u306Efirst setting value, text, compositionend, input, ",
+ "runNestedSettingValue: the textarea should have appended by setRangeText() and all string set to value attribute #3");
+
+ input.focus();
+ input.value = "";
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ events = [];
+ isTesting = true;
+ input.setRangeText("first setting value, ");
+ isTesting = false;
+
+ is(events.length, 3,
+ "runNestedSettingValue: wrong event count #4");
+ is(events[0].type, "text",
+ "runNestedSettingValue: the 1st event must be text #4");
+ is(events[1].type, "compositionend",
+ "runNestedSettingValue: the 2nd event must be compositionend #4");
+ is(events[2].type, "input",
+ "runNestedSettingValue: the 3rd event must be input #4");
+ is(events[1].data, "\u306E",
+ "runNestedSettingValue: compositionend has wrong data #4");
+ is(events[0].target, input,
+ "runNestedSettingValue: The 1st event was fired on wrong event target #4");
+ is(events[1].target, input,
+ "runNestedSettingValue: The 2nd event was fired on wrong event target #4");
+ is(events[2].target, input,
+ "runNestedSettingValue: The 3rd event was fired on wrong event target #4");
+ ok(!getEditorIMESupport(input).isComposing,
+ "runNestedSettingValue: the input still has composition #4");
+ is(textarea.value, "\u306Efirst setting value, text, compositionend, input, ",
+ "runNestedSettingValue: the input should have all string appended by setRangeText() and set to value attribute #4");
+
+ window.removeEventListener("compositionstart", eventHandler, true);
+ window.removeEventListener("compositionupdate", eventHandler, true);
+ window.removeEventListener("compositionend", eventHandler, true);
+ window.removeEventListener("input", eventHandler, true);
+ window.removeEventListener("text", eventHandler, true);
+
+}
+
+function runAsyncForceCommitTest(aNextTest)
+{
+ var events;
+ function eventHandler(aEvent)
+ {
+ events.push(aEvent);
+ };
+
+ // If IME commits composition for a request, TextComposition commits
+ // composition automatically because most web apps must expect that active
+ // composition should be committed synchronously. Therefore, in this case,
+ // a click during composition should cause committing composition
+ // synchronously and delayed commit shouldn't cause composition events.
+ var commitRequested = false;
+ function callback(aTIP, aNotification)
+ {
+ ok(true, aNotification.type);
+ if (aNotification.type != "request-to-commit") {
+ return true;
+ }
+ commitRequested = true;
+ setTimeout(function () {
+ events = [];
+ aTIP.commitComposition();
+
+ is(events.length, 0,
+ "runAsyncForceCommitTest: composition events shouldn't been fired by asynchronous call of nsITextInputProcessor.commitComposition()");
+
+ window.removeEventListener("compositionstart", eventHandler, true);
+ window.removeEventListener("compositionupdate", eventHandler, true);
+ window.removeEventListener("compositionend", eventHandler, true);
+ window.removeEventListener("input", eventHandler, true);
+ window.removeEventListener("text", eventHandler, true);
+
+ SimpleTest.executeSoon(aNextTest);
+ }, 1);
+ return true;
+ };
+
+ window.addEventListener("compositionstart", eventHandler, true);
+ window.addEventListener("compositionupdate", eventHandler, true);
+ window.addEventListener("compositionend", eventHandler, true);
+ window.addEventListener("input", eventHandler, true);
+ window.addEventListener("text", eventHandler, true);
+
+ // Make the composition in textarea commit by click in the textarea
+ textarea.focus();
+ textarea.value = "";
+
+ events = [];
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ }, window, callback);
+
+ is(events.length, 4,
+ "runAsyncForceCommitTest: wrong event count #1");
+ is(events[0].type, "compositionstart",
+ "runAsyncForceCommitTest: the 1st event must be compositionstart #1");
+ is(events[1].type, "compositionupdate",
+ "runAsyncForceCommitTest: the 2nd event must be compositionupdate #1");
+ is(events[2].type, "text",
+ "runAsyncForceCommitTest: the 3rd event must be text #1");
+ is(events[3].type, "input",
+ "runAsyncForceCommitTest: the 4th event must be input #1");
+
+ events = [];
+ commitRequested = false;
+ synthesizeMouseAtCenter(textarea, {});
+
+ ok(commitRequested,
+ "runAsyncForceCommitTest: \"request-to-commit\" should've been notified");
+ is(events.length, 3,
+ "runAsyncForceCommitTest: wrong event count #2");
+ is(events[0].type, "text",
+ "runAsyncForceCommitTest: the 1st event must be text #2");
+ is(events[1].type, "compositionend",
+ "runAsyncForceCommitTest: the 2nd event must be compositionend #2");
+ is(events[2].type, "input",
+ "runAsyncForceCommitTest: the 3rd event must be input #2");
+ is(events[1].data, "\u306E",
+ "runAsyncForceCommitTest: compositionend has wrong data #2");
+ is(events[0].target, textarea,
+ "runAsyncForceCommitTest: The 1st event was fired on wrong event target #2");
+ is(events[1].target, textarea,
+ "runAsyncForceCommitTest: The 2nd event was fired on wrong event target #2");
+ is(events[2].target, textarea,
+ "runAsyncForceCommitTest: The 3rd event was fired on wrong event target #2");
+ ok(!getEditorIMESupport(textarea).isComposing,
+ "runAsyncForceCommitTest: the textarea still has composition #2");
+ is(textarea.value, "\u306E",
+ "runAsyncForceCommitTest: the textarea doesn't have the committed text #2");
+}
+
+function runBug811755Test()
+{
+ iframe2.contentDocument.body.innerHTML = "<div>content<br/></div>";
+ iframe2.contentWindow.focus();
+ // Query everything
+ var textContent = synthesizeQueryTextContent(0, 10);
+ if (!checkQueryContentResult(textContent, "runBug811755Test: synthesizeQueryTextContent #1")) {
+ return false;
+ }
+ // Query everything but specify exact end offset, which should be immediately after the <br> node
+ // If PreContentIterator is used, the next node after <br> is the node after </div>.
+ // If ContentIterator is used, the next node is the <div> node itself. In this case, the end
+ // node ends up being before the start node, and an empty string is returned.
+ var queryContent = synthesizeQueryTextContent(0, textContent.text.length);
+ if (!checkQueryContentResult(queryContent, "runBug811755Test: synthesizeQueryTextContent #2")) {
+ return false;
+ }
+ is(queryContent.text, textContent.text, "runBug811755Test: two queried texts don't match");
+ return queryContent.text == textContent.text;
+}
+
+function runIsComposingTest()
+{
+ var expectedIsComposing = false;
+ var descriptionBase = "runIsComposingTest: ";
+ var description = "";
+
+ function eventHandler(aEvent)
+ {
+ if (aEvent.type == "keydown" || aEvent.type == "keyup") {
+ is(aEvent.isComposing, expectedIsComposing,
+ "runIsComposingTest: " + description + " (type=" + aEvent.type + ", key=" + aEvent.key + ")");
+ } else {
+ is(aEvent.isComposing, expectedIsComposing,
+ "runIsComposingTest: " + description + " (type=" + aEvent.type + ")");
+ }
+ }
+
+ function onComposition(aEvent)
+ {
+ if (aEvent.type == "compositionstart") {
+ expectedIsComposing = true;
+ } else if (aEvent.type == "compositionend") {
+ expectedIsComposing = false;
+ }
+ }
+
+ textarea.addEventListener("keydown", eventHandler, true);
+ textarea.addEventListener("keypress", eventHandler, true);
+ textarea.addEventListener("keyup", eventHandler, true);
+ textarea.addEventListener("input", eventHandler, true);
+ textarea.addEventListener("compositionstart", onComposition, true);
+ textarea.addEventListener("compositionend", onComposition, true);
+
+ textarea.focus();
+ textarea.value = "";
+
+ // XXX These cases shouldn't occur in actual native key events because we
+ // don't dispatch key events while composition (bug 354358).
+ SpecialPowers.setBoolPref("dom.keyboardevent.dispatch_during_composition", true);
+ description = "events before dispatching compositionstart";
+ synthesizeKey("VK_LEFT", {});
+
+ description = "events after dispatching compositionchange";
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3042",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 },
+ "key": { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A },
+ });
+
+ // Although, firing keypress event during composition is a bug.
+ synthesizeKey("VK_INSERT", {});
+
+ description = "events for committing composition string";
+
+ synthesizeComposition({ type: "compositioncommitasis",
+ key: { key: "KEY_Enter", code: "Enter", type: "keydown" } });
+
+ // input event will be fired by synthesizing compositionend event.
+ // Then, its isComposing should be false.
+ description = "events after dispatching compositioncommitasis";
+ synthesizeKey("VK_RETURN", { type: "keyup" });
+
+ SpecialPowers.clearUserPref("dom.keyboardevent.dispatch_during_composition");
+
+ textarea.removeEventListener("keydown", eventHandler, true);
+ textarea.removeEventListener("keypress", eventHandler, true);
+ textarea.removeEventListener("keyup", eventHandler, true);
+ textarea.removeEventListener("input", eventHandler, true);
+ textarea.removeEventListener("compositionstart", onComposition, true);
+ textarea.removeEventListener("compositionend", onComposition, true);
+
+ textarea.value = "";
+}
+
+function runRedundantChangeTest()
+{
+ textarea.focus();
+
+ var result = {};
+ function clearResult()
+ {
+ result = { compositionupdate: false, compositionend: false, text: false, input: false, inputaftercompositionend: false };
+ }
+
+ function handler(aEvent)
+ {
+ if (aEvent.type == "input" && result.compositionend) {
+ result.inputaftercompositionend = true;
+ return;
+ }
+ result[aEvent.type] = true;
+ }
+
+ textarea.addEventListener("compositionupdate", handler, true);
+ textarea.addEventListener("compositionend", handler, true);
+ textarea.addEventListener("input", handler, true);
+ textarea.addEventListener("text", handler, true);
+
+ textarea.value = "";
+
+ // synthesize change event
+ clearResult();
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3042",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ is(result.compositionupdate, true, "runRedundantChangeTest: compositionupdate should be fired after synthesizing composition change #1");
+ is(result.compositionend, false, "runRedundantChangeTest: compositionend shouldn't be fired after synthesizing composition change #1");
+ is(result.text, true, "runRedundantChangeTest: text should be fired after synthesizing composition change because it's dispatched when there is composing string #1");
+ is(result.input, true, "runRedundantChangeTest: input should be fired after synthesizing composition change #1");
+ is(textarea.value, "\u3042", "runRedundantChangeTest: textarea has uncommitted string #1");
+
+ // synthesize another change event
+ clearResult();
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3042\u3044",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 }
+ });
+
+ is(result.compositionupdate, true, "runRedundantChangeTest: compositionupdate should be fired after synthesizing composition change #2");
+ is(result.compositionend, false, "runRedundantChangeTest: compositionend shouldn't be fired after synthesizing composition change #2");
+ is(result.text, true, "runRedundantChangeTest: text should be fired after synthesizing composition change because it's dispatched when there is composing string #2");
+ is(result.input, true, "runRedundantChangeTest: input should be fired after synthesizing composition change #2");
+ is(textarea.value, "\u3042\u3044", "runRedundantChangeTest: textarea has uncommitted string #2");
+
+ // synthesize same change event again
+ clearResult();
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3042\u3044",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 }
+ });
+
+ is(result.compositionupdate, false, "runRedundantChangeTest: compositionupdate shouldn't be fired after synthesizing composition change again");
+ is(result.compositionend, false, "runRedundantChangeTest: compositionend shouldn't be fired after synthesizing composition change again");
+ is(result.text, false, "runRedundantChangeTest: text shouldn't be fired after synthesizing composition change again because it's dispatched when there is composing string");
+ is(result.input, false, "runRedundantChangeTest: input shouldn't be fired after synthesizing composition change again");
+ is(textarea.value, "\u3042\u3044", "runRedundantChangeTest: textarea has uncommitted string #3");
+
+ // synthesize commit-as-is
+ clearResult();
+ synthesizeComposition({ type: "compositioncommitasis" });
+ is(result.compositionupdate, false, "runRedundantChangeTest: compositionupdate shouldn't be fired after synthesizing composition commit-as-is");
+ is(result.compositionend, true, "runRedundantChangeTest: compositionend should be fired after synthesizing composition commit-as-is");
+ is(result.text, true, "runRedundantChangeTest: text shouldn't be fired after synthesizing composition commit-as-is for removing the ranges");
+ is(result.input, false, "runRedundantChangeTest: input shouldn't be fired before compositionend at synthesizing commit-as-is");
+ is(result.inputaftercompositionend, true, "runRedundantChangeTest: input should be fired after synthesizing composition commit-as-is after compositionend");
+ is(textarea.value, "\u3042\u3044", "runRedundantChangeTest: textarea has the commit string");
+
+ textarea.removeEventListener("compositionupdate", handler, true);
+ textarea.removeEventListener("compositionend", handler, true);
+ textarea.removeEventListener("input", handler, true);
+ textarea.removeEventListener("text", handler, true);
+}
+
+function runNotRedundantChangeTest()
+{
+ textarea.focus();
+
+ var result = {};
+ function clearResult()
+ {
+ result = { compositionupdate: false, compositionend: false, text: false, input: false, inputaftercompositionend: false };
+ }
+
+ function handler(aEvent)
+ {
+ if (aEvent.type == "input" && result.compositionend) {
+ result.inputaftercompositionend = true;
+ return;
+ }
+ result[aEvent.type] = true;
+ }
+
+ textarea.addEventListener("compositionupdate", handler, true);
+ textarea.addEventListener("compositionend", handler, true);
+ textarea.addEventListener("input", handler, true);
+ textarea.addEventListener("text", handler, true);
+
+ textarea.value = "abcde";
+
+ // synthesize change event with non-null ranges
+ clearResult();
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "ABCDE",
+ "clauses":
+ [
+ { "length": 5, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": 5, "length": 0 }
+ });
+
+ is(result.compositionupdate, true, "runNotRedundantChangeTest: compositionupdate should be fired after synthesizing composition change with non-null ranges");
+ is(result.compositionend, false, "runNotRedundantChangeTest: compositionend shouldn't be fired after synthesizing composition change with non-null ranges");
+ is(result.text, true, "runNotRedundantChangeTest: text should be fired after synthesizing composition change because it's dispatched when there is composing string with non-null ranges");
+ is(result.input, true, "runNotRedundantChangeTest: input should be fired after synthesizing composition change with non-null ranges");
+ is(textarea.value, "abcdeABCDE", "runNotRedundantChangeTest: textarea has uncommitted string #1");
+
+ // synthesize change event with null ranges
+ clearResult();
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "ABCDE",
+ "clauses":
+ [
+ { "length": 0, "attr": 0 }
+ ]
+ },
+ });
+ is(result.compositionupdate, false, "runNotRedundantChangeTest: compositionupdate shouldn't be fired after synthesizing composition change with null ranges after non-null ranges");
+ is(result.compositionend, false, "runNotRedundantChangeTest: compositionend shouldn't be fired after synthesizing composition change with null ranges after non-null ranges");
+ is(result.text, true, "runNotRedundantChangeTest: text should be fired after synthesizing composition change because it's dispatched when there is composing string with null ranges after non-null ranges");
+ is(result.input, true, "runNotRedundantChangeTest: input should be fired after synthesizing composition change with null ranges after non-null ranges");
+ is(textarea.value, "abcdeABCDE", "runNotRedundantChangeTest: textarea has uncommitted string #2");
+
+ // synthesize change event with non-null ranges
+ clearResult();
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "ABCDE",
+ "clauses":
+ [
+ { "length": 5, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": 5, "length": 0 }
+ });
+
+ is(result.compositionupdate, false, "runNotRedundantChangeTest: compositionupdate shouldn't be fired after synthesizing composition change with non-null ranges after null ranges");
+ is(result.compositionend, false, "runNotRedundantChangeTest: compositionend shouldn't be fired after synthesizing composition change with non-null ranges after null ranges");
+ is(result.text, true, "runNotRedundantChangeTest: text should be fired after synthesizing composition change because it's dispatched when there is composing string with non-null ranges after null ranges");
+ is(result.input, true, "runNotRedundantChangeTest: input should be fired after synthesizing composition change with non-null ranges after null ranges");
+ is(textarea.value, "abcdeABCDE", "runNotRedundantChangeTest: textarea has uncommitted string #3");
+
+ // synthesize change event with empty data and null ranges
+ clearResult();
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "",
+ "clauses":
+ [
+ { "length": 0, "attr": 0 }
+ ]
+ },
+ });
+ is(result.compositionupdate, true, "runNotRedundantChangeTest: compositionupdate should be fired after synthesizing composition change with empty data and null ranges after non-null ranges");
+ is(result.compositionend, false, "runNotRedundantChangeTest: compositionend shouldn't be fired after synthesizing composition change with empty data and null ranges after non-null ranges");
+ is(result.text, true, "runNotRedundantChangeTest: text should be fired after synthesizing composition change because it's dispatched when there is composing string with empty data and null ranges after non-null ranges");
+ is(result.input, true, "runNotRedundantChangeTest: input should be fired after synthesizing composition change with empty data and null ranges after non-null ranges");
+ is(textarea.value, "abcde", "runNotRedundantChangeTest: textarea doesn't have uncommitted string #1");
+
+ // synthesize change event with non-null ranges
+ clearResult();
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "ABCDE",
+ "clauses":
+ [
+ { "length": 5, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": 5, "length": 0 }
+ });
+
+ is(result.compositionupdate, true, "runNotRedundantChangeTest: compositionupdate should be fired after synthesizing composition change with non-null ranges after empty data and null ranges");
+ is(result.compositionend, false, "runNotRedundantChangeTest: compositionend shouldn't be fired after synthesizing composition change with non-null ranges after empty data and null ranges");
+ is(result.text, true, "runNotRedundantChangeTest: text should be fired after synthesizing composition change because it's dispatched when there is composing string with non-null ranges after empty data and null ranges");
+ is(result.input, true, "runNotRedundantChangeTest: input should be fired after synthesizing composition change with non-null ranges after empty data and null ranges");
+ is(textarea.value, "abcdeABCDE", "runNotRedundantChangeTest: textarea has uncommitted string #4");
+
+ clearResult();
+ synthesizeComposition({ type: "compositioncommit", data: "" });
+
+ is(result.compositionupdate, true, "runNotRedundantChangeTest: compositionupdate should be fired after synthesizing composition commit with empty data after non-empty data");
+ is(result.compositionend, true, "runNotRedundantChangeTest: compositionend should be fired after synthesizing composition commit with empty data after non-empty data");
+ is(result.text, true, "runNotRedundantChangeTest: text should be fired after synthesizing composition change because it's dispatched when there is composing string with empty data after non-empty data");
+ is(result.input, false, "runNotRedundantChangeTest: input shouldn't be fired before compositionend after synthesizing composition change with empty data after non-empty data");
+ is(result.inputaftercompositionend, true, "runNotRedundantChangeTest: input should be fired after compositionend after synthesizing composition change with empty data after non-empty data");
+ is(textarea.value, "abcde", "runNotRedundantChangeTest: textarea doesn't have uncommitted string #2");
+
+ textarea.removeEventListener("compositionupdate", handler, true);
+ textarea.removeEventListener("compositionend", handler, true);
+ textarea.removeEventListener("input", handler, true);
+ textarea.removeEventListener("text", handler, true);
+}
+
+function runControlCharTest()
+{
+ textarea.focus();
+
+ var result = {};
+ function clearResult()
+ {
+ result = { compositionupdate: null, compositionend: null };
+ }
+
+ function handler(aEvent)
+ {
+ result[aEvent.type] = aEvent.data;
+ }
+
+ textarea.addEventListener("compositionupdate", handler, true);
+ textarea.addEventListener("compositionend", handler, true);
+
+ textarea.value = "";
+
+ var controlChars = String.fromCharCode.apply(null, Object.keys(Array.from({length:0x20}))) + "\x7F";
+ var allowedChars = "\t";
+
+ var data = "AB" + controlChars + "CD" + controlChars + "EF";
+ var removedData = "AB" + allowedChars + "CD" + allowedChars + "EF";
+
+ var DIndex = data.indexOf("D");
+ var removedDIndex = removedData.indexOf("D");
+
+ // input string contains control characters
+ clearResult();
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": data,
+ "clauses":
+ [
+ { "length": DIndex,
+ "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
+ { "length": data.length - DIndex,
+ "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": DIndex, "length": 0 }
+ });
+
+ checkSelection(removedDIndex, "", "runControlCharTest", "#1")
+
+ is(result.compositionupdate, removedData, "runControlCharTest: control characters in event.data should be removed in compositionupdate event #1");
+ is(textarea.value, removedData, "runControlCharTest: control characters should not appear in textarea #1");
+
+ synthesizeComposition({ type: "compositioncommit", data: data });
+
+ is(result.compositionend, removedData, "runControlCharTest: control characters in event.data should be removed in compositionend event #2");
+ is(textarea.value, removedData, "runControlCharTest: control characters should not appear in textarea #2");
+
+ textarea.value = "";
+
+ clearResult();
+
+ SpecialPowers.setBoolPref("dom.compositionevent.allow_control_characters", true);
+
+ // input string contains control characters, allowing control characters
+ clearResult();
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": data,
+ "clauses":
+ [
+ { "length": DIndex,
+ "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
+ { "length": data.length - DIndex,
+ "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": DIndex, "length": 0 }
+ });
+
+ checkSelection(DIndex - 1 + kLFLen, "", "runControlCharTest", "#3")
+
+ is(result.compositionupdate, data, "runControlCharTest: control characters in event.data should not be removed in compositionupdate event #3");
+ is(textarea.value, data.replace(/\r/g, "\n"), "runControlCharTest: control characters should appear in textarea #3");
+
+ synthesizeComposition({ type: "compositioncommit", data: data });
+
+ is(result.compositionend, data, "runControlCharTest: control characters in event.data should not be removed in compositionend event #4");
+ is(textarea.value, data.replace(/\r/g, "\n"), "runControlCharTest: control characters should appear in textarea #4");
+
+ SpecialPowers.clearUserPref("dom.compositionevent.allow_control_characters");
+
+ textarea.removeEventListener("compositionupdate", handler, true);
+ textarea.removeEventListener("compositionend", handler, true);
+}
+
+function runRemoveContentTest(aCallback)
+{
+ var events = [];
+ function eventHandler(aEvent)
+ {
+ events.push(aEvent);
+ }
+ textarea.addEventListener("compositionstart", eventHandler, true);
+ textarea.addEventListener("compositionupdate", eventHandler, true);
+ textarea.addEventListener("compositionend", eventHandler, true);
+ textarea.addEventListener("input", eventHandler, true);
+ textarea.addEventListener("text", eventHandler, true);
+
+ textarea.focus();
+ textarea.value = "";
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u306E",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ var nextSibling = textarea.nextSibling;
+ var parent = textarea.parentElement;
+
+ events = [];
+ parent.removeChild(textarea);
+
+ hitEventLoop(function () {
+ // XXX Currently, "input" event isn't fired on removed content.
+ is(events.length, 3,
+ "runRemoveContentTest: wrong event count #1");
+ is(events[0].type, "compositionupdate",
+ "runRemoveContentTest: the 1st event must be compositionupdate #1");
+ is(events[1].type, "text",
+ "runRemoveContentTest: the 2nd event must be text #1");
+ is(events[2].type, "compositionend",
+ "runRemoveContentTest: the 3rd event must be compositionend #1");
+ is(events[0].data, "",
+ "runRemoveContentTest: compositionupdate has wrong data #1");
+ is(events[2].data, "",
+ "runRemoveContentTest: compositionend has wrong data #1");
+ is(events[0].target, textarea,
+ "runRemoveContentTest: The 1st event was fired on wrong event target #1");
+ is(events[1].target, textarea,
+ "runRemoveContentTest: The 2nd event was fired on wrong event target #1");
+ is(events[2].target, textarea,
+ "runRemoveContentTest: The 3rd event was fired on wrong event target #1");
+ ok(!getEditorIMESupport(textarea).isComposing,
+ "runRemoveContentTest: the textarea still has composition #1");
+ todo_is(textarea.value, "",
+ "runRemoveContentTest: the textarea has the committed text? #1");
+
+ parent.insertBefore(textarea, nextSibling);
+
+ textarea.focus();
+ textarea.value = "";
+
+ synthesizeComposition({ type: "compositionstart" });
+
+ events = [];
+ parent.removeChild(textarea);
+
+ hitEventLoop(function () {
+ // XXX Currently, "input" event isn't fired on removed content.
+ is(events.length, 1,
+ "runRemoveContentTest: wrong event count #2");
+ is(events[0].type, "compositionend",
+ "runRemoveContentTest: the 1st event must be compositionend #2");
+ is(events[0].data, "",
+ "runRemoveContentTest: compositionupdate has wrong data #2");
+ is(events[0].target, textarea,
+ "runRemoveContentTest: The 1st event was fired on wrong event target #2");
+ ok(!getEditorIMESupport(textarea).isComposing,
+ "runRemoveContentTest: the textarea still has composition #2");
+ is(textarea.value, "",
+ "runRemoveContentTest: the textarea has the committed text? #2");
+
+ parent.insertBefore(textarea, nextSibling);
+
+ textarea.removeEventListener("compositionstart", eventHandler, true);
+ textarea.removeEventListener("compositionupdate", eventHandler, true);
+ textarea.removeEventListener("compositionend", eventHandler, true);
+ textarea.removeEventListener("input", eventHandler, true);
+ textarea.removeEventListener("text", eventHandler, true);
+
+ SimpleTest.executeSoon(aCallback);
+ }, 50);
+ }, 50);
+}
+
+function runTestOnAnotherContext(aPanelOrFrame, aFocusedEditor, aTestName)
+{
+ aFocusedEditor.value = "";
+
+ var editorRect = synthesizeQueryEditorRect();
+ if (!checkQueryContentResult(editorRect, aTestName + ": editorRect")) {
+ return;
+ }
+
+ var r = aPanelOrFrame.getBoundingClientRect();
+ var parentRect = { "left": r.left, "top": r.top, "width": r.right - r.left,
+ "height": r.bottom - r.top };
+ checkRectContainsRect(editorRect, parentRect, aTestName +
+ ": the editor rect coordinates are wrong");
+
+ // input characters
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3078\u3093\u3057\u3093",
+ "clauses":
+ [
+ { "length": 4, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 4, "length": 0 }
+ });
+
+ if (!checkContent("\u3078\u3093\u3057\u3093", aTestName, "#1-1") ||
+ !checkSelection(4, "", aTestName, "#1-1")) {
+ return;
+ }
+
+ // convert them #1
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u8FD4\u4FE1",
+ "clauses":
+ [
+ { "length": 2,
+ "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 }
+ });
+
+ if (!checkContent("\u8FD4\u4FE1", aTestName, "#1-2") ||
+ !checkSelection(2, "", aTestName, "#1-2")) {
+ return;
+ }
+
+ // convert them #2
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u5909\u8EAB",
+ "clauses":
+ [
+ { "length": 2,
+ "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 }
+ });
+
+ if (!checkContent("\u5909\u8EAB", aTestName, "#1-3") ||
+ !checkSelection(2, "", aTestName, "#1-3")) {
+ return;
+ }
+
+ // commit them
+ synthesizeComposition({ type: "compositioncommitasis" });
+ if (!checkContent("\u5909\u8EAB", aTestName, "#1-4") ||
+ !checkSelection(2, "", aTestName, "#1-4")) {
+ return;
+ }
+
+ is(aFocusedEditor.value, "\u5909\u8EAB",
+ aTestName + ": composition isn't in the focused editor");
+ if (aFocusedEditor.value != "\u5909\u8EAB") {
+ return;
+ }
+
+ var textRect = synthesizeQueryTextRect(0, 1);
+ var caretRect = synthesizeQueryCaretRect(2);
+ if (!checkQueryContentResult(textRect,
+ aTestName + ": synthesizeQueryTextRect") ||
+ !checkQueryContentResult(caretRect,
+ aTestName + ": synthesizeQueryCaretRect")) {
+ return;
+ }
+ checkRectContainsRect(textRect, editorRect, aTestName + ":testRect");
+ checkRectContainsRect(caretRect, editorRect, aTestName + ":caretRect");
+}
+
+function runFrameTest()
+{
+ textareaInFrame.focus();
+ runTestOnAnotherContext(iframe, textareaInFrame, "runFrameTest");
+ runCharAtPointTest(textareaInFrame, "textarea in the iframe");
+}
+
+var gPanelShown = false;
+var gPanelFocused = false;
+function onPanelShown(aEvent)
+{
+ gPanelShown = true;
+ textbox.focus();
+ setTimeout(doPanelTest, 0);
+}
+
+function onFocusPanelTextbox(aEvent)
+{
+ gPanelFocused = true;
+ setTimeout(doPanelTest, 0);
+}
+
+var gIsPanelHiding = false;
+var gIsRunPanelTestInternal = false;
+function doPanelTest()
+{
+ if (!gPanelFocused || !gPanelShown) {
+ return;
+ }
+ if (gIsRunPanelTestInternal) {
+ return;
+ }
+ gIsRunPanelTestInternal = true;
+ runTestOnAnotherContext(panel, textbox, "runPanelTest");
+ runCharAtPointTest(textbox, "textbox in the panel");
+ gIsPanelHiding = true;
+ panel.hidePopup();
+}
+
+function onPanelHidden(aEvent)
+{
+ panel.hidden = true;
+ ok(gIsPanelHiding, "runPanelTest: the panel is hidden unexpectedly");
+ finish();
+}
+
+function runPanelTest()
+{
+ panel.hidden = false;
+ panel.openPopupAtScreen(window.screenX + window.outerWidth, 0, false);
+}
+
+function runMaxLengthTest()
+{
+ input.maxLength = 1;
+ input.value = "";
+ input.focus();
+
+ var kDesc ="runMaxLengthTest";
+
+ // input first character
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ if (!checkContent("\u3089", kDesc, "#1-1") ||
+ !checkSelection(1, "", kDesc, "#1-1")) {
+ return;
+ }
+
+ // input second character
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 }
+ });
+
+ if (!checkContent("\u3089\u30FC", kDesc, "#1-2") ||
+ !checkSelection(2, "", kDesc, "#1-2")) {
+ return;
+ }
+
+ // input third character
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC\u3081",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 }
+ });
+
+ if (!checkContent("\u3089\u30FC\u3081", kDesc, "#1-3") ||
+ !checkSelection(3, "", kDesc, "#1-3")) {
+ return;
+ }
+
+ // input fourth character
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC\u3081\u3093",
+ "clauses":
+ [
+ { "length": 4, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 4, "length": 0 }
+ });
+
+ if (!checkContent("\u3089\u30FC\u3081\u3093", kDesc, "#1-4") ||
+ !checkSelection(4, "", kDesc, "#1-4")) {
+ return;
+ }
+
+
+ // backspace
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC\u3081",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 }
+ });
+
+ if (!checkContent("\u3089\u30FC\u3081", kDesc, "#1-5") ||
+ !checkSelection(3, "", kDesc, "#1-5")) {
+ return;
+ }
+
+ // re-input
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC\u3081\u3093",
+ "clauses":
+ [
+ { "length": 4, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 4, "length": 0 }
+ });
+
+ if (!checkContent("\u3089\u30FC\u3081\u3093", kDesc, "#1-6") ||
+ !checkSelection(4, "", kDesc, "#1-6")) {
+ return;
+ }
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC\u3081\u3093\u3055",
+ "clauses":
+ [
+ { "length": 5, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 5, "length": 0 }
+ });
+
+ if (!checkContent("\u3089\u30FC\u3081\u3093\u3055", kDesc, "#1-7") ||
+ !checkSelection(5, "", kDesc, "#1-7")) {
+ return;
+ }
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC\u3081\u3093\u3055\u3044",
+ "clauses":
+ [
+ { "length": 6, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 6, "length": 0 }
+ });
+
+ if (!checkContent("\u3089\u30FC\u3081\u3093\u3055\u3044", kDesc, "#1-8") ||
+ !checkSelection(6, "", kDesc, "#1-8")) {
+ return;
+ }
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC\u3081\u3093\u3055\u3044\u3053",
+ "clauses":
+ [
+ { "length": 7, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 7, "length": 0 }
+ });
+
+ if (!checkContent("\u3089\u30FC\u3081\u3093\u3055\u3044\u3053",
+ kDesc, "#1-8") ||
+ !checkSelection(7, "", kDesc, "#1-8")) {
+ return;
+ }
+
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3089\u30FC\u3081\u3093\u3055\u3044\u3053\u3046",
+ "clauses":
+ [
+ { "length": 8, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 8, "length": 0 }
+ });
+
+ if (!checkContent("\u3089\u30FC\u3081\u3093\u3055\u3044\u3053\u3046",
+ kDesc, "#1-9") ||
+ !checkSelection(8, "", kDesc, "#1-9")) {
+ return;
+ }
+
+ // convert
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u30E9\u30FC\u30E1\u30F3\u6700\u9AD8",
+ "clauses":
+ [
+ { "length": 4,
+ "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
+ { "length": 2,
+ "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": 4, "length": 0 }
+ });
+
+ if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700\u9AD8", kDesc, "#1-10") ||
+ !checkSelection(4, "", kDesc, "#1-10")) {
+ return;
+ }
+
+ // commit the composition string
+ synthesizeComposition({ type: "compositioncommitasis" });
+ if (!checkContent("\u30E9", kDesc, "#1-11") ||
+ !checkSelection(1, "", kDesc, "#1-11")) {
+ return;
+ }
+
+ // input characters
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u3057",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ if (!checkContent("\u30E9\u3057", kDesc, "#2-1") ||
+ !checkSelection(1 + 1, "", kDesc, "#2-1")) {
+ return;
+ }
+
+ // commit the composition string
+ synthesizeComposition({ type: "compositioncommit", data: "\u3058" });
+ if (!checkContent("\u30E9", kDesc, "#2-2") ||
+ !checkSelection(1 + 0, "", kDesc, "#2-2")) {
+ return;
+ }
+
+ // Undo
+ synthesizeKey("Z", {accelKey: true});
+
+ // XXX this is unexpected behavior, see bug 258291
+ if (!checkContent("\u30E9", kDesc, "#3-1") ||
+ !checkSelection(1 + 0, "", kDesc, "#3-1")) {
+ return;
+ }
+
+ // Undo
+ synthesizeKey("Z", {accelKey: true});
+ if (!checkContent("", kDesc, "#3-2") ||
+ !checkSelection(0, "", kDesc, "#3-2")) {
+ return;
+ }
+
+ // Redo
+ synthesizeKey("Z", {accelKey: true, shiftKey: true});
+ if (!checkContent("\u30E9", kDesc, "#3-3") ||
+ !checkSelection(1, "", kDesc, "#3-3")) {
+ return;
+ }
+
+ // Redo
+ synthesizeKey("Z", {accelKey: true, shiftKey: true});
+ if (!checkContent("\u30E9", kDesc, "#3-4") ||
+ !checkSelection(1 + 0, "", kDesc, "#3-4")) {
+ return;
+ }
+
+ // The input element whose content length is already maxlength and
+ // the carest is at start of the content.
+ input.value = "X";
+ input.selectionStart = input.selectionEnd = 0;
+
+ // input characters
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u9B54",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+
+ if (!checkContent("\u9B54X", kDesc, "#4-1") ||
+ !checkSelection(1, "", kDesc, "#4-1")) {
+ return;
+ }
+
+ // commit the composition string
+ synthesizeComposition({ type: "compositioncommitasis" });
+
+ // The input text must be discarded. Then, the caret position shouldn't be
+ // updated from its position at compositionstart.
+ if (!checkContent("X", kDesc, "#4-2") ||
+ !checkSelection(0, "", kDesc, "#4-2")) {
+ return;
+ }
+
+ // input characters
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "\u9B54\u6CD5",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 }
+ });
+
+ if (!checkContent("\u9B54\u6CD5X", kDesc, "#5-1") ||
+ !checkSelection(2, "", kDesc, "#5-1")) {
+ return;
+ }
+
+ // commit the composition string
+ synthesizeComposition({ type: "compositioncommitasis" });
+
+ if (!checkContent("X", kDesc, "#5-2") ||
+ !checkSelection(0, "", kDesc, "#5-2")) {
+ return;
+ }
+}
+
+function runEditorReframeTests(aCallback)
+{
+ function runEditorReframeTest(aEditor, aWindow, aEventType, aNextTest)
+ {
+ function getValue()
+ {
+ return aEditor == contenteditable ?
+ aEditor.innerHTML.replace("<br>", "") : aEditor.value;
+ }
+
+ var description = "runEditorReframeTest(" + aEditor.id + ", \"" + aEventType + "\"): ";
+
+ var tests = [
+ { test: function () {
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "a",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+ },
+ check: function () {
+ is(getValue(aEditor), "a", description + "Typing 'a'");
+ },
+ },
+ { test: function () {
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "ab",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 }
+ });
+ },
+ check: function () {
+ is(getValue(aEditor), "ab", description + "Typing 'b' next to 'a'");
+ },
+ },
+ { test: function () {
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "abc",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 }
+ });
+ },
+ check: function () {
+ is(getValue(aEditor), "abc", description + "Typing 'c' next to 'ab'");
+ },
+ },
+ { test: function () {
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "abc",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
+ { "length": 1, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 }
+ });
+ },
+ check: function () {
+ is(getValue(aEditor), "abc", description + "Starting to convert 'ab][c'");
+ },
+ },
+ { test: function () {
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "ABc",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
+ { "length": 1, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 }
+ });
+ },
+ check: function () {
+ is(getValue(aEditor), "ABc", description + "Starting to convert 'AB][c'");
+ },
+ },
+ { test: function () {
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "ABC",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
+ { "length": 1, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 }
+ });
+ },
+ check: function () {
+ is(getValue(aEditor), "ABC", description + "Starting to convert 'AB][C'");
+ },
+ },
+ { test: function () {
+ // Commit composition
+ synthesizeComposition({ type: "compositioncommitasis" });
+ },
+ check: function () {
+ is(getValue(aEditor), "ABC", description + "Committed as 'ABC'");
+ },
+ },
+ { test: function () {
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "d",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+ },
+ check: function () {
+ is(getValue(aEditor), "ABCd", description + "Typing 'd' next to ABC");
+ },
+ },
+ { test: function () {
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "de",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 }
+ });
+ },
+ check: function () {
+ is(getValue(aEditor), "ABCde", description + "Typing 'e' next to ABCd");
+ },
+ },
+ { test: function () {
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "def",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 }
+ });
+ },
+ check: function () {
+ is(getValue(aEditor), "ABCdef", description + "Typing 'f' next to ABCde");
+ },
+ },
+ { test: function () {
+ // Commit composition
+ synthesizeComposition({ type: "compositioncommitasis" });
+ },
+ check: function () {
+ is(getValue(aEditor), "ABCdef", description + "Commit 'def' without convert");
+ },
+ },
+ { test: function () {
+ // Select "Cd"
+ synthesizeKey("KEY_ArrowLeft", { code: "ArrowLeft" });
+ synthesizeKey("KEY_ArrowLeft", { code: "ArrowLeft" });
+ synthesizeKey("KEY_Shift", { type: "keydown", code: "ShiftLeft", shiftKey: true });
+ synthesizeKey("KEY_ArrowLeft", { code: "ArrowLeft", shiftKey: true });
+ synthesizeKey("KEY_ArrowLeft", { code: "ArrowLeft", shiftKey: true });
+ synthesizeKey("KEY_Shift", { type: "keyup", code: "ShiftLeft" });
+ },
+ check: function () {
+ },
+ },
+ { test: function () {
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "g",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ });
+ },
+ check: function () {
+ is(getValue(aEditor), "ABgef", description + "Typing 'g' next to AB");
+ },
+ },
+ { test: function () {
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "gh",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 }
+ });
+ },
+ check: function () {
+ is(getValue(aEditor), "ABghef", description + "Typing 'h' next to ABg");
+ },
+ },
+ { test: function () {
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "ghi",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 }
+ });
+ },
+ check: function () {
+ is(getValue(aEditor), "ABghief", description + "Typing 'i' next to ABgh");
+ },
+ },
+ { test: function () {
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "GHI",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 }
+ });
+ },
+ check: function () {
+ is(getValue(aEditor), "ABGHIef", description + "Convert 'ghi' to 'GHI'");
+ },
+ },
+ { test: function () {
+ // Commit composition
+ synthesizeComposition({ type: "compositioncommitasis" });
+ },
+ check: function () {
+ is(getValue(aEditor), "ABGHIef", description + "Commit 'GHI'");
+ },
+ },
+ ];
+
+ var index = 0;
+ function doReframe(aEvent)
+ {
+ aEvent.target.style.overflow =
+ aEvent.target.style.overflow != "hidden" ? "hidden" : "auto";
+ }
+ aEditor.focus();
+ aEditor.addEventListener(aEventType, doReframe);
+
+ function doNext()
+ {
+ if (tests.length <= index) {
+ aEditor.style.overflow = "auto";
+ aEditor.removeEventListener(aEventType, doReframe);
+ requestAnimationFrame(function() { setTimeout(aNextTest); });
+ return;
+ }
+ tests[index].test();
+ hitEventLoop(function () {
+ tests[index].check();
+ index++;
+ setTimeout(doNext, 0);
+ }, 20);
+ }
+ doNext();
+ }
+
+ input.value = "";
+ runEditorReframeTest(input, window, "input", function () {
+ input.value = "";
+ runEditorReframeTest(input, window, "compositionupdate", function () {
+ textarea.value = "";
+ runEditorReframeTest(textarea, window, "input", function () {
+ textarea.value = "";
+ runEditorReframeTest(textarea, window, "compositionupdate", function () {
+ contenteditable.innerHTML = "";
+ runEditorReframeTest(contenteditable, windowOfContenteditable, "input", function () {
+ contenteditable.innerHTML = "";
+ runEditorReframeTest(contenteditable, windowOfContenteditable, "compositionupdate", function () {
+ aCallback();
+ });
+ });
+ });
+ });
+ });
+ });
+}
+
+function runTest()
+{
+ contenteditable = document.getElementById("iframe4").contentDocument.getElementById("contenteditable");
+ windowOfContenteditable = document.getElementById("iframe4").contentWindow;
+ textareaInFrame = iframe.contentDocument.getElementById("textarea");
+
+ runUndoRedoTest();
+ runCompositionCommitAsIsTest();
+ runCompositionCommitTest();
+ runCompositionTest();
+ runCompositionEventTest();
+ runQueryTextRectInContentEditableTest();
+ runCharAtPointTest(textarea, "textarea in the document");
+ runCharAtPointAtOutsideTest();
+ runSetSelectionEventTest();
+ runQueryTextContentEventTest();
+ runQueryIMESelectionTest();
+ runQueryContentEventRelativeToInsertionPoint();
+ runCSSTransformTest();
+ runBug722639Test();
+ runForceCommitTest();
+ runNestedSettingValue();
+ runBug811755Test();
+ runIsComposingTest();
+ runRedundantChangeTest();
+ runNotRedundantChangeTest();
+ runControlCharTest();
+ runEditorReframeTests(function () {
+ runAsyncForceCommitTest(function () {
+ runRemoveContentTest(function () {
+ runFrameTest();
+ runPanelTest();
+ runMaxLengthTest();
+ });
+ });
+ });
+}
+
+]]>
+</script>
+
+</window>
diff --git a/widget/tests/window_imestate_iframes.html b/widget/tests/window_imestate_iframes.html
new file mode 100644
index 000000000..064cf19a5
--- /dev/null
+++ b/widget/tests/window_imestate_iframes.html
@@ -0,0 +1,380 @@
+<html>
+<head>
+ <title>Test for IME state controling and focus moving for iframes</title>
+ <script type="text/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+ <style type="text/css">
+ iframe {
+ border: none;
+ height: 100px;
+ }
+ </style>
+</head>
+<body onunload="onUnload();">
+<p id="display">
+ <!-- Use input[readonly] because it isn't affected by the partial focus
+ movement on Mac -->
+ <input id="prev" readonly><br>
+ <iframe id="iframe_not_editable"
+ src="data:text/html,&lt;html&gt;&lt;body&gt;&lt;input id='editor'&gt;&lt;/body&gt;&lt;/html&gt;"></iframe><br>
+
+ <!-- Testing IME state and focus movement, the anchor elements cannot get focus -->
+ <iframe id="iframe_html"
+ src="data:text/html,&lt;html id='editor' contenteditable='true'&gt;&lt;body&gt;&lt;a href='about:blank'&gt;about:blank;&lt;/a&gt;&lt;/body&gt;&lt;/html&gt;"></iframe><br>
+ <iframe id="iframe_designMode"
+ src="data:text/html,&lt;body id='editor' onload='document.designMode=&quot;on&quot;;'&gt;&lt;a href='about:blank'&gt;about:blank;&lt;/a&gt;&lt;/body&gt;"></iframe><br>
+ <iframe id="iframe_body"
+ src="data:text/html,&lt;body id='editor' contenteditable='true'&gt;&lt;a href='about:blank'&gt;about:blank;&lt;/a&gt;&lt;/body&gt;"></iframe><br>
+ <iframe id="iframe_p"
+ src="data:text/html,&lt;body&gt;&lt;p id='editor' contenteditable='true'&gt;&lt;a href='about:blank'&gt;about:blank;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;"></iframe><br>
+
+ <input id="next" readonly><br>
+</p>
+<script class="testbody" type="application/javascript">
+
+window.opener.wrappedJSObject.SimpleTest.waitForFocus(runTests, window);
+
+function ok(aCondition, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.ok(aCondition, aMessage);
+}
+
+function is(aLeft, aRight, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.is(aLeft, aRight, aMessage);
+}
+
+function onUnload()
+{
+ window.opener.wrappedJSObject.onFinish();
+}
+
+var gFocusObservingElement = null;
+var gBlurObservingElement = null;
+
+function onFocus(aEvent)
+{
+ if (aEvent.target != gFocusObservingElement) {
+ return;
+ }
+ ok(gFocusObservingElement.willFocus,
+ "focus event is fired on unexpected element");
+ gFocusObservingElement.willFocus = false;
+}
+
+function onBlur(aEvent)
+{
+ if (aEvent.target != gBlurObservingElement) {
+ return;
+ }
+ ok(gBlurObservingElement.willBlur,
+ "blur event is fired on unexpected element");
+ gBlurObservingElement.willBlur = false;
+}
+
+function observeFocusBlur(aNextFocusedNode, aWillFireFocusEvent,
+ aNextBlurredNode, aWillFireBlurEvent)
+{
+ if (gFocusObservingElement) {
+ if (gFocusObservingElement.willFocus) {
+ ok(false, "focus event was never fired on " + gFocusObservingElement);
+ }
+ gFocusObservingElement.removeEventListener("focus", onFocus, true);
+ gFocusObservingElement.willFocus = NaN;
+ gFocusObservingElement = null;
+ }
+ if (gBlurObservingElement) {
+ if (gBlurObservingElement.willBlur) {
+ ok(false, "blur event was never fired on " + gBlurObservingElement);
+ }
+ gBlurObservingElement.removeEventListener("blur", onBlur, true);
+ gBlurObservingElement.willBlur = NaN;
+ gBlurObservingElement = null;
+ }
+ if (aNextFocusedNode) {
+ gFocusObservingElement = aNextFocusedNode;
+ gFocusObservingElement.willFocus = aWillFireFocusEvent;
+ gFocusObservingElement.addEventListener("focus", onFocus, true);
+ }
+ if (aNextBlurredNode) {
+ gBlurObservingElement = aNextBlurredNode;
+ gBlurObservingElement.willBlur = aWillFireBlurEvent;
+ gBlurObservingElement.addEventListener("blur", onBlur, true);
+ }
+}
+
+function runTests()
+{
+ var utils =
+ window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIDOMWindowUtils);
+ var fm =
+ Components.classes["@mozilla.org/focus-manager;1"]
+ .getService(Components.interfaces.nsIFocusManager);
+
+ var iframe, editor, root, input;
+ var prev = document.getElementById("prev");
+ var next = document.getElementById("next");
+ var html = document.documentElement;
+
+ function resetFocusToInput(aDescription)
+ {
+ observeFocusBlur(null, false, null, false);
+ prev.focus();
+ is(fm.focusedElement, prev,
+ "input#prev[readonly] element didn't get focus: " + aDescription);
+ is(utils.IMEStatus, utils.IME_STATUS_DISABLED,
+ "IME enabled on input#prev[readonly]: " + aDescription);
+ }
+
+ function resetFocusToParentHTML(aDescription)
+ {
+ observeFocusBlur(null, false, null, false);
+ html.focus();
+ is(fm.focusedElement, html,
+ "Parent html element didn't get focus: " + aDescription);
+ is(utils.IMEStatus, utils.IME_STATUS_DISABLED,
+ "IME enabled on parent html element: " + aDescription);
+ }
+
+ function testTabKey(aForward,
+ aNextFocusedNode, aWillFireFocusEvent,
+ aNextBlurredNode, aWillFireBlurEvent,
+ aIMEShouldBeEnabled, aTestingCaseDescription)
+ {
+ observeFocusBlur(aNextFocusedNode, aWillFireFocusEvent,
+ aNextBlurredNode, aWillFireBlurEvent);
+ synthesizeKey("VK_TAB", { shiftKey: !aForward });
+ var description = "Tab key test: ";
+ if (!aForward) {
+ description = "Shift-" + description;
+ }
+ description += aTestingCaseDescription + ": ";
+ is(fm.focusedElement, aNextFocusedNode,
+ description + "didn't move focus as expected");
+ is(utils.IMEStatus,
+ aIMEShouldBeEnabled ?
+ utils.IME_STATUS_ENABLED : utils.IME_STATUS_DISABLED,
+ description + "didn't set IME state as expected");
+ }
+
+ function testMouseClick(aNextFocusedNode, aWillFireFocusEvent,
+ aWillAllNodeLostFocus,
+ aNextBlurredNode, aWillFireBlurEvent,
+ aIMEShouldBeEnabled, aTestingCaseDescription)
+ {
+ observeFocusBlur(aNextFocusedNode, aWillFireFocusEvent,
+ aNextBlurredNode, aWillFireBlurEvent);
+ // We're relying on layout inside the iframe being up to date, so make it so
+ iframe.contentDocument.documentElement.getBoundingClientRect();
+ synthesizeMouse(iframe, 10, 80, { });
+ var description = "Click test: " + aTestingCaseDescription + ": ";
+ is(fm.focusedElement, !aWillAllNodeLostFocus ? aNextFocusedNode : null,
+ description + "didn't move focus as expected");
+ is(utils.IMEStatus,
+ aIMEShouldBeEnabled ?
+ utils.IME_STATUS_ENABLED : utils.IME_STATUS_DISABLED,
+ description + "didn't set IME state as expected");
+ }
+
+ function testOnEditorFlagChange(aDescription, aIsInDesignMode)
+ {
+ const kReadonly =
+ Components.interfaces.nsIPlaintextEditor.eEditorReadonlyMask;
+ var description = "testOnEditorFlagChange: " + aDescription;
+ resetFocusToParentHTML(description);
+ var htmlEditor =
+ iframe.contentWindow.
+ QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIWebNavigation).
+ QueryInterface(Components.interfaces.nsIDocShell).editor;
+ var e = aIsInDesignMode ? root : editor;
+ e.focus();
+ is(fm.focusedElement, e,
+ description + ": focus() of editor didn't move focus as expected");
+ is(utils.IMEStatus, utils.IME_STATUS_ENABLED,
+ description + ": IME isn't enabled when the editor gets focus");
+ var flags = htmlEditor.flags;
+ htmlEditor.flags |= kReadonly;
+ is(fm.focusedElement, e,
+ description + ": when editor becomes readonly, focus moved unexpectedly");
+ is(utils.IMEStatus, utils.IME_STATUS_DISABLED,
+ description + ": when editor becomes readonly, IME is still enabled");
+ htmlEditor.flags = flags;
+ is(fm.focusedElement, e,
+ description + ": when editor becomes read-write, focus moved unexpectedly");
+ is(utils.IMEStatus, utils.IME_STATUS_ENABLED,
+ description + ": when editor becomes read-write, IME is still disabled");
+ }
+
+ // hide all iframes
+ document.getElementById("iframe_not_editable").style.display = "none";
+ document.getElementById("iframe_html").style.display = "none";
+ document.getElementById("iframe_designMode").style.display = "none";
+ document.getElementById("iframe_body").style.display = "none";
+ document.getElementById("iframe_p").style.display = "none";
+
+ // non editable HTML element and input element can get focus.
+ iframe = document.getElementById("iframe_not_editable");
+ iframe.style.display = "inline";
+ editor = iframe.contentDocument.getElementById("editor");
+ root = iframe.contentDocument.documentElement;
+ resetFocusToInput("initializing for iframe_not_editable");
+
+ testTabKey(true, root, false, prev, true,
+ false, "input#prev[readonly] -> html");
+ testTabKey(true, editor, true, root, false,
+ true, "html -> input in the subdoc");
+ testTabKey(true, next, true, editor, true,
+ false, "input in the subdoc -> input#next[readonly]");
+ testTabKey(false, editor, true, next, true,
+ true, "input#next[readonly] -> input in the subdoc");
+ testTabKey(false, root, false, editor, true,
+ false, "input in the subdoc -> html");
+ testTabKey(false, prev, true, root, false,
+ false, "html -> input#next[readonly]");
+
+ iframe.style.display = "none";
+
+ // HTML element (of course, it's root) must enables IME.
+ iframe = document.getElementById("iframe_html");
+ iframe.style.display = "inline";
+ editor = iframe.contentDocument.getElementById("editor");
+ root = iframe.contentDocument.documentElement;
+ resetFocusToInput("initializing for iframe_html");
+
+ testTabKey(true, editor, true, prev, true,
+ true, "input#prev[readonly] -> html[contentediable=true]");
+ testTabKey(true, next, true, editor, true,
+ false, "html[contentediable=true] -> input#next[readonly]");
+ testTabKey(false, editor, true, next, true,
+ true, "input#next[readonly] -> html[contentediable=true]");
+ testTabKey(false, prev, true, editor, true,
+ false, "html[contenteditable=true] -> input[readonly]");
+
+ prev.style.display = "none";
+ resetFocusToParentHTML("testing iframe_html");
+ testTabKey(true, editor, true, html, false,
+ true, "html of parent -> html[contentediable=true]");
+ testTabKey(false, html, false, editor, true,
+ false, "html[contenteditable=true] -> html of parent");
+ prev.style.display = "inline";
+ resetFocusToInput("after parent html <-> html[contenteditable=true]");
+
+ testMouseClick(editor, true, false, prev, true, true, "iframe_html");
+
+ testOnEditorFlagChange("html[contentediable=true]", false);
+
+ iframe.style.display = "none";
+
+ // designMode should behave like <html contenteditable="true"></html>
+ // but focus/blur events shouldn't be fired on its root element because
+ // any elements shouldn't be focused state in designMode.
+ iframe = document.getElementById("iframe_designMode");
+ iframe.style.display = "inline";
+ iframe.contentDocument.designMode = "on";
+ editor = iframe.contentDocument.getElementById("editor");
+ root = iframe.contentDocument.documentElement;
+ resetFocusToInput("initializing for iframe_designMode");
+
+ testTabKey(true, root, false, prev, true,
+ true, "input#prev[readonly] -> html in designMode");
+ testTabKey(true, next, true, root, false,
+ false, "html in designMode -> input#next[readonly]");
+ testTabKey(false, root, false, next, true,
+ true, "input#next[readonly] -> html in designMode");
+ testTabKey(false, prev, true, root, false,
+ false, "html in designMode -> input#prev[readonly]");
+
+ prev.style.display = "none";
+ resetFocusToParentHTML("testing iframe_designMode");
+ testTabKey(true, root, false, html, false,
+ true, "html of parent -> html in designMode");
+ testTabKey(false, html, false, root, false,
+ false, "html in designMode -> html of parent");
+ prev.style.display = "inline";
+ resetFocusToInput("after parent html <-> html in designMode");
+
+ testMouseClick(editor, false, true, prev, true, true, "iframe_designMode");
+
+ testOnEditorFlagChange("html in designMode", true);
+
+ iframe.style.display = "none";
+
+ // When there is no HTML element but the BODY element is editable,
+ // the body element should get focus and enables IME.
+ iframe = document.getElementById("iframe_body");
+ iframe.style.display = "inline";
+ editor = iframe.contentDocument.getElementById("editor");
+ root = iframe.contentDocument.documentElement;
+ resetFocusToInput("initializing for iframe_body");
+
+ testTabKey(true, editor, true, prev, true,
+ true, "input#prev[readonly] -> body[contentediable=true]");
+ testTabKey(true, next, true, editor, true,
+ false, "body[contentediable=true] -> input#next[readonly]");
+ testTabKey(false, editor, true, next, true,
+ true, "input#next[readonly] -> body[contentediable=true]");
+ testTabKey(false, prev, true, editor, true,
+ false, "body[contenteditable=true] -> input#prev[readonly]");
+
+ prev.style.display = "none";
+ resetFocusToParentHTML("testing iframe_body");
+ testTabKey(true, editor, true, html, false,
+ true, "html of parent -> body[contentediable=true]");
+ testTabKey(false, html, false, editor, true,
+ false, "body[contenteditable=true] -> html of parent");
+ prev.style.display = "inline";
+ resetFocusToInput("after parent html <-> body[contenteditable=true]");
+
+ testMouseClick(editor, true, false, prev, true, true, "iframe_body");
+
+ testOnEditorFlagChange("body[contentediable=true]", false);
+
+ iframe.style.display = "none";
+
+ // When HTML/BODY elements are not editable, focus shouldn't be moved to
+ // the editable content directly.
+ iframe = document.getElementById("iframe_p");
+ iframe.style.display = "inline";
+ editor = iframe.contentDocument.getElementById("editor");
+ root = iframe.contentDocument.documentElement;
+ resetFocusToInput("initializing for iframe_p");
+
+ testTabKey(true, root, false, prev, true,
+ false, "input#prev[readonly] -> html (has p[contenteditable=true])");
+ testTabKey(true, editor, true, root, false,
+ true, "html (has p[contenteditable=true]) -> p[contentediable=true]");
+ testTabKey(true, next, true, editor, true,
+ false, "p[contentediable=true] -> input#next[readonly]");
+ testTabKey(false, editor, true, next, true,
+ true, "input#next[readonly] -> p[contentediable=true]");
+ testTabKey(false, root, false, editor, true,
+ false, "p[contenteditable=true] -> html (has p[contenteditable=true])");
+ testTabKey(false, prev, true, root, false,
+ false, "html (has p[contenteditable=true]) -> input#prev[readonly]");
+ prev.style.display = "none";
+
+ resetFocusToParentHTML("testing iframe_p");
+ testTabKey(true, root, false, html, false,
+ false, "html of parent -> html (has p[contentediable=true])");
+ testTabKey(false, html, false, root, false,
+ false, "html (has p[contentediable=true]) -> html of parent");
+ prev.style.display = "inline";
+ resetFocusToInput("after parent html <-> html (has p[contentediable=true])");
+
+ testMouseClick(root, false, true, prev, true, false, "iframe_p");
+
+ testOnEditorFlagChange("p[contenteditable=true]", false);
+
+ iframe.style.display = "none";
+
+ window.close();
+}
+
+</script>
+</body>
+
+</html>
diff --git a/widget/tests/window_mouse_scroll_win.html b/widget/tests/window_mouse_scroll_win.html
new file mode 100644
index 000000000..4a83e23ef
--- /dev/null
+++ b/widget/tests/window_mouse_scroll_win.html
@@ -0,0 +1,1531 @@
+<html lang="en-US"
+ style="font-family: Arial; font-size: 10px; line-height: 16px;">
+<head>
+ <title>Test for mouse scroll handling on Windows</title>
+ <script type="text/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body onunload="onUnload();">
+<div id="display" style="width: 5000px; height: 5000px;">
+<p id="p1" style="font-size: 16px; width: 100px; height: 100px;">1st &lt;p&gt;.</p>
+<p id="p2" style="font-size: 32px; width: 100px; height: 100px;">2nd &lt;p&gt;.</p>
+</div>
+<script class="testbody" type="application/javascript">
+
+window.opener.wrappedJSObject.SimpleTest.waitForFocus(prepareTests, window);
+
+const nsIDOMWindowUtils = Components.interfaces.nsIDOMWindowUtils;
+
+const WHEEL_PAGESCROLL = 4294967295;
+
+const WM_VSCROLL = 0x0115;
+const WM_HSCROLL = 0x0114;
+const WM_MOUSEWHEEL = 0x020A;
+const WM_MOUSEHWHEEL = 0x020E;
+
+const SB_LINEUP = 0;
+const SB_LINELEFT = 0;
+const SB_LINEDOWN = 1;
+const SB_LINERIGHT = 1;
+const SB_PAGEUP = 2;
+const SB_PAGELEFT = 2;
+const SB_PAGEDOWN = 3;
+const SB_PAGERIGHT = 3;
+
+const SHIFT_L = 0x0100;
+const SHIFT_R = 0x0200;
+const CTRL_L = 0x0400;
+const CTRL_R = 0x0800;
+const ALT_L = 0x1000;
+const ALT_R = 0x2000;
+
+const DOM_PAGE_SCROLL_DELTA = 32768;
+
+const kSystemScrollSpeedOverridePref = "mousewheel.system_scroll_override_on_root_content.enabled";
+
+const kAltKeyActionPref = "mousewheel.with_alt.action";
+const kCtrlKeyActionPref = "mousewheel.with_control.action";
+const kShiftKeyActionPref = "mousewheel.with_shift.action";
+const kWinKeyActionPref = "mousewheel.with_win.action";
+
+const kAltKeyDeltaMultiplierXPref = "mousewheel.with_alt.delta_multiplier_x";
+const kAltKeyDeltaMultiplierYPref = "mousewheel.with_alt.delta_multiplier_y";
+const kCtrlKeyDeltaMultiplierXPref = "mousewheel.with_control.delta_multiplier_x";
+const kCtrlKeyDeltaMultiplierYPref = "mousewheel.with_control.delta_multiplier_y";
+const kShiftKeyDeltaMultiplierXPref = "mousewheel.with_shift.delta_multiplier_x";
+const kShiftKeyDeltaMultiplierYPref = "mousewheel.with_shift.delta_multiplier_y";
+const kWinKeyDeltaMultiplierXPref = "mousewheel.with_win.delta_multiplier_x";
+const kWinKeyDeltaMultiplierYPref = "mousewheel.with_win.delta_multiplier_y";
+
+const kEmulateWheelByWMSCROLLPref = "mousewheel.emulate_at_wm_scroll";
+const kVAmountPref = "mousewheel.windows.vertical_amount_override";
+const kHAmountPref = "mousewheel.windows.horizontal_amount_override";
+const kTimeoutPref = "mousewheel.windows.transaction.timeout";
+
+const kMouseLineScrollEvent = "DOMMouseScroll";
+const kMousePixelScrollEvent = "MozMousePixelScroll";
+
+const kVAxis = Components.interfaces.nsIDOMMouseScrollEvent.VERTICAL_AXIS;
+const kHAxis = Components.interfaces.nsIDOMMouseScrollEvent.HORIZONTAL_AXIS;
+
+var gLineHeight = 0;
+var gCharWidth = 0;
+var gPageHeight = 0;
+var gPageWidth = 0;
+
+var gP1 = document.getElementById("p1");
+var gP2 = document.getElementById("p2");
+
+var gOtherWindow;
+
+function ok(aCondition, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.ok(aCondition, aMessage);
+}
+
+function is(aLeft, aRight, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.is(aLeft, aRight, aMessage);
+}
+
+function isnot(aLeft, aRight, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.isnot(aLeft, aRight, aMessage);
+}
+
+function todo_is(aLeft, aRight, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.todo_is(aLeft, aRight, aMessage);
+}
+
+function onUnload()
+{
+ SpecialPowers.clearUserPref(kAltKeyActionPref);
+ SpecialPowers.clearUserPref(kCtrlKeyActionPref);
+ SpecialPowers.clearUserPref(kShiftKeyActionPref);
+ SpecialPowers.clearUserPref(kWinKeyActionPref);
+
+ SpecialPowers.clearUserPref(kAltKeyDeltaMultiplierXPref);
+ SpecialPowers.clearUserPref(kAltKeyDeltaMultiplierYPref);
+ SpecialPowers.clearUserPref(kCtrlKeyDeltaMultiplierXPref);
+ SpecialPowers.clearUserPref(kCtrlKeyDeltaMultiplierYPref);
+ SpecialPowers.clearUserPref(kShiftKeyDeltaMultiplierXPref);
+ SpecialPowers.clearUserPref(kShiftKeyDeltaMultiplierYPref);
+ SpecialPowers.clearUserPref(kWinKeyDeltaMultiplierXPref);
+ SpecialPowers.clearUserPref(kWinKeyDeltaMultiplierYPref);
+
+ SpecialPowers.clearUserPref(kSystemScrollSpeedOverridePref);
+ SpecialPowers.clearUserPref(kEmulateWheelByWMSCROLLPref);
+ SpecialPowers.clearUserPref(kVAmountPref);
+ SpecialPowers.clearUserPref(kHAmountPref);
+ SpecialPowers.clearUserPref(kTimeoutPref);
+ window.opener.wrappedJSObject.SimpleTest.finish();
+}
+
+function getWindowUtils(aWindow)
+{
+ if (!aWindow) {
+ aWindow = window;
+ }
+ return aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(nsIDOMWindowUtils);
+}
+
+function getPointInScreen(aElement, aWindow)
+{
+ if (!aWindow) {
+ aWindow = window;
+ }
+ var bounds = aElement.getBoundingClientRect();
+ return { x: bounds.left + aWindow.mozInnerScreenX,
+ y: bounds.top + aWindow.mozInnerScreenY };
+}
+
+function cut(aNum)
+{
+ return (aNum >= 0) ? Math.floor(aNum) : Math.ceil(aNum);
+}
+
+/**
+ * Make each steps for the tests in following arrays in global scope. Each item
+ * of the arrays will be executed after previous test is finished.
+ *
+ * description:
+ * Set the description of the test. This will be used for the message of is()
+ * or the others.
+ *
+ * message:
+ * aNativeMessage of nsIDOMWindowUtils.sendNativeMouseScrollEvent().
+ * Must be WM_MOUSEWHEEL, WM_MOUSEHWHEEL, WM_VSCROLL or WM_HSCROLL.
+ *
+ * delta:
+ * The native delta value for WM_MOUSEWHEEL or WM_MOUSEHWHEEL.
+ * Or one of the SB_* const value for WM_VSCROLL or WM_HSCROLL.
+ *
+ * target:
+ * The target element, under the mouse cursor.
+ *
+ * window:
+ * The window which is used for getting nsIDOMWindowUtils.
+ *
+ * modifiers:
+ * Pressed modifier keys, 0 means no modifier key is pressed.
+ * Otherwise, one or more values of SHIFT_L, SHIFT_R, CTRL_L, CTRL_R,
+ * ALT_L or ALT_R.
+ *
+ * additionalFlags:
+ * aAdditionalFlags of nsIDOMWindowUtils.sendNativeMouseScrollEvent().
+ * See the document of nsIDOMWindowUtils for the detail of the values.
+ *
+ * onLineScrollEvent:
+ * Must be a function or null.
+ * If the value is a function, it will be called when DOMMouseScroll event
+ * is received by the synthesized event.
+ * If return true, the common checks are canceled.
+ *
+ * onPixelScrollEvent:
+ * Must be a function or null.
+ * If the value is a function, it will be called when MozMousePixelScroll
+ * event is received by the synthesized event.
+ * If return true, the common checks are canceled.
+ *
+ * expected:
+ * Must not be null and this must have:
+ * axis:
+ * kVAxis if the synthesized event causes vertical scroll. Otherwise,
+ * it causes horizontal scroll, kHAxis.
+ * lines:
+ * Integer value which is expected detail attribute value of
+ * DOMMouseScroll. If the event shouldn't be fired, must be 0.
+ * pixels:
+ * Integer value or a function which returns double value. The value is
+ * expected detail attribute value of MozMousePixelScroll.
+ * If the event shouldn't be fired, must be 0.
+ *
+ * Note that if both lines and pixels are 0, the test framework waits
+ * a few seconds. After that, go to next test.
+ *
+ * init:
+ * Must be a function or null. If this value is a function, it's called
+ * before synthesizing the native event.
+ *
+ * finish:
+ * Must be a function or null. If this value is a function, it's called
+ * after received all expected events or timeout if no events are expected.
+ */
+
+// First, get the computed line height, char width, page height and page width.
+var gPreparingSteps = [
+ { description: "Preparing gLineHeight",
+ message: WM_MOUSEWHEEL, delta: -120,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ onLineScrollEvent: function (aEvent) {
+ return true;
+ },
+ onPixelScrollEvent: function (aEvent) {
+ gLineHeight = aEvent.detail;
+ return true;
+ },
+ expected: {
+ axis: kVAxis, lines: 1, pixels: 1,
+ },
+ init: function () {
+ SpecialPowers.setIntPref(kVAmountPref, 1);
+ SpecialPowers.setIntPref(kHAmountPref, 1);
+ },
+ },
+ { description: "Preparing gCharWidth",
+ message: WM_MOUSEHWHEEL, delta: 120,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ onLineScrollEvent: function (aEvent) {
+ return true;
+ },
+ onPixelScrollEvent: function (aEvent) {
+ gCharWidth = aEvent.detail;
+ return true;
+ },
+ expected: {
+ axis: kVAxis, lines: 1, pixels: 1,
+ },
+ },
+ { description: "Preparing gPageHeight",
+ message: WM_MOUSEWHEEL, delta: -120,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ onLineScrollEvent: function (aEvent) {
+ return true;
+ },
+ onPixelScrollEvent: function (aEvent) {
+ gPageHeight = aEvent.detail;
+ return true;
+ },
+ expected: {
+ axis: kHAxis, lines: 1, pixels: 1,
+ },
+ init: function () {
+ SpecialPowers.setIntPref(kVAmountPref, 0xFFFF);
+ SpecialPowers.setIntPref(kHAmountPref, 0xFFFF);
+ },
+ },
+ { description: "Preparing gPageWidth",
+ message: WM_MOUSEHWHEEL, delta: 120,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ onLineScrollEvent: function (aEvent) {
+ return true;
+ },
+ onPixelScrollEvent: function (aEvent) {
+ gPageWidth = aEvent.detail;
+ return true;
+ },
+ expected: {
+ axis: kHAxis, lines: 1, pixels: 1,
+ },
+ finish: function () {
+ ok(gLineHeight > 0, "gLineHeight isn't positive got " + gLineHeight);
+ ok(gCharWidth > 0, "gCharWidth isn't positive got " + gCharWidth);
+ ok(gPageHeight > 0, "gPageHeight isn't positive got " + gPageHeight);
+ ok(gPageWidth > 0, "gPageWidth isn't positive got " + gPageWidth);
+
+ ok(gPageHeight > gLineHeight,
+ "gPageHeight must be larger than gLineHeight");
+ ok(gPageWidth > gCharWidth,
+ "gPageWidth must be larger than gCharWidth");
+ runNextTest(gBasicTests, 0)
+ }
+ },
+];
+
+var gBasicTests = [
+ // Widget shouldn't dispatch a pixel event if the delta can be devided by
+ // lines to be scrolled. However, pixel events should be fired by ESM.
+ { description: "WM_MOUSEWHEEL, -120, 3 lines",
+ message: WM_MOUSEWHEEL, delta: -120,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 3, pixels: function () { return gLineHeight * 3; },
+ },
+ init: function () {
+ SpecialPowers.setIntPref(kVAmountPref, 3);
+ SpecialPowers.setIntPref(kHAmountPref, 3);
+ },
+ },
+
+ { description: "WM_MOUSEWHEEL, 120, -3 lines",
+ message: WM_MOUSEWHEEL, delta: 120,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: -3, pixels: function () { return gLineHeight * -3; },
+ },
+ },
+
+ { description: "WM_MOUSEHWHEEL, 120, 3 chars",
+ message: WM_MOUSEHWHEEL, delta: 120,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 3, pixels: function () { return gCharWidth * 3; },
+ },
+ },
+
+ { description: "WM_MOUSEHWHEEL, -120, -3 chars",
+ message: WM_MOUSEHWHEEL, delta: -120,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: -3, pixels: function () { return gCharWidth * -3; },
+ },
+ },
+
+ // Pixel scroll event should be fired always but line scroll event should be
+ // fired only when accumulated delta value is over a line.
+ { description: "WM_MOUSEWHEEL, -20, 0.5 lines",
+ message: WM_MOUSEWHEEL, delta: -20,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 0, pixels: function () { return gLineHeight / 2; },
+ },
+ },
+ { description: "WM_MOUSEWHEEL, -20, 0.5 lines (pending: 0.5 lines)",
+ message: WM_MOUSEWHEEL, delta: -20,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 1, pixels: function () { return gLineHeight / 2; },
+ },
+ },
+ { description: "WM_MOUSEWHEEL, -20, 0.5 lines",
+ message: WM_MOUSEWHEEL, delta: -20,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 0, pixels: function () { return gLineHeight / 2; },
+ },
+ },
+
+ { description: "WM_MOUSEWHEEL, 20, -0.5 lines (pending: 0.5 lines)",
+ message: WM_MOUSEWHEEL, delta: 20,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 0, pixels: function () { return gLineHeight / -2; },
+ },
+ },
+ { description: "WM_MOUSEWHEEL, 20, -0.5 lines (pending: -0.5 lines)",
+ message: WM_MOUSEWHEEL, delta: 20,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: -1, pixels: function () { return -gLineHeight / 2; },
+ },
+ },
+ { description: "WM_MOUSEWHEEL, 20, -0.5 lines",
+ message: WM_MOUSEWHEEL, delta: 20,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 0, pixels: function () { return gLineHeight / -2; },
+ },
+ },
+
+ { description: "WM_MOUSEHWHEEL, 20, 0.5 chars",
+ message: WM_MOUSEHWHEEL, delta: 20,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 0, pixels: function () { return gCharWidth / 2; },
+ },
+ },
+ { description: "WM_MOUSEHWHEEL, 20, 0.5 chars (pending: 0.5 chars)",
+ message: WM_MOUSEHWHEEL, delta: 20,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 1, pixels: function () { return gCharWidth / 2; },
+ },
+ },
+ { description: "WM_MOUSEHWHEEL, 20, 0.5 chars",
+ message: WM_MOUSEHWHEEL, delta: 20,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 0, pixels: function () { return gCharWidth / 2; },
+ },
+ },
+
+ { description: "WM_MOUSEHWHEEL, -20, -0.5 chars (pending: 0.5 chars)",
+ message: WM_MOUSEHWHEEL, delta: -20,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 0, pixels: function () { return gCharWidth / -2; },
+ },
+ },
+ { description: "WM_MOUSEHWHEEL, -20, -0.5 chars (pending: -0.5 chars)",
+ message: WM_MOUSEHWHEEL, delta: -20,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: -1, pixels: function () { return -gCharWidth / 2; },
+ },
+ },
+ { description: "WM_MOUSEHWHEEL, -20, -0.5 chars",
+ message: WM_MOUSEHWHEEL, delta: -20,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 0, pixels: function () { return gCharWidth / -2; },
+ },
+ },
+
+ // Even if the mouse cursor is an element whose font-size is different than
+ // the scrollable element, the pixel scroll amount shouldn't be changed.
+ // Widget shouldn't dispatch a pixel event if the delta can be devided by
+ // lines to be scrolled. However, pixel events should be fired by ESM.
+ { description: "WM_MOUSEWHEEL, -120, 3 lines, on the other div whose font-size is larger",
+ message: WM_MOUSEWHEEL, delta: -120,
+ target: gP2, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 3, pixels: function () { return gLineHeight * 3; },
+ },
+ },
+
+ { description: "WM_MOUSEWHEEL, 120, -3 lines, on the other div whose font-size is larger",
+ message: WM_MOUSEWHEEL, delta: 120,
+ target: gP2, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: -3, pixels: function () { return gLineHeight * -3; },
+ },
+ },
+
+ { description: "WM_MOUSEHWHEEL, 120, 3 chars, on the other div whose font-size is larger",
+ message: WM_MOUSEHWHEEL, delta: 120,
+ target: gP2, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 3, pixels: function () { return gCharWidth * 3; },
+ },
+ },
+
+ { description: "WM_MOUSEHWHEEL, -120, -3 chars, on the other div whose font-size is larger",
+ message: WM_MOUSEHWHEEL, delta: -120,
+ target: gP2, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: -3, pixels: function () { return gCharWidth * -3; },
+ },
+ },
+
+ // Modifier key tests
+ { description: "WM_MOUSEWHEEL, -40, 1 line with left Shift",
+ message: WM_MOUSEWHEEL, delta: -40,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: SHIFT_L,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 1, pixels: function () { return gLineHeight; },
+ },
+ },
+ { description: "WM_MOUSEWHEEL, -40, 1 line with right Shift",
+ message: WM_MOUSEWHEEL, delta: -40,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: SHIFT_R,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 1, pixels: function () { return gLineHeight; },
+ },
+ },
+ { description: "WM_MOUSEWHEEL, -40, 1 line with left Ctrl",
+ message: WM_MOUSEWHEEL, delta: -40,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: CTRL_L,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 1, pixels: function () { return gLineHeight; },
+ },
+ },
+ { description: "WM_MOUSEWHEEL, -40, 1 line with right Ctrl",
+ message: WM_MOUSEWHEEL, delta: -40,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: CTRL_R,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 1, pixels: function () { return gLineHeight; },
+ },
+ },
+ { description: "WM_MOUSEWHEEL, -40, 1 line with left Alt",
+ message: WM_MOUSEWHEEL, delta: -40,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: ALT_L,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 1, pixels: function () { return gLineHeight; },
+ },
+ },
+ { description: "WM_MOUSEWHEEL, -40, 1 line with right Alt",
+ message: WM_MOUSEWHEEL, delta: -40,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: ALT_R,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 1, pixels: function () { return gLineHeight; },
+ },
+ },
+
+ { description: "WM_MOUSEHWHEEL, 40, 1 character with left Shift",
+ message: WM_MOUSEHWHEEL, delta: 40,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: SHIFT_L,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 1, pixels: function () { return gCharWidth; },
+ },
+ },
+ { description: "WM_MOUSEHWHEEL, 40, 1 character with right Shift",
+ message: WM_MOUSEHWHEEL, delta: 40,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: SHIFT_R,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 1, pixels: function () { return gCharWidth; },
+ },
+ },
+ { description: "WM_MOUSEHWHEEL, 40, 1 character with left Ctrl",
+ message: WM_MOUSEHWHEEL, delta: 40,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: CTRL_L,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 1, pixels: function () { return gCharWidth; },
+ },
+ },
+ { description: "WM_MOUSEHWHEEL, 40, 1 character with right Ctrl",
+ message: WM_MOUSEHWHEEL, delta: 40,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: CTRL_R,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 1, pixels: function () { return gCharWidth; },
+ },
+ },
+ { description: "WM_MOUSEHWHEEL, 40, 1 character with left Alt",
+ message: WM_MOUSEHWHEEL, delta: 40,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: ALT_L,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 1, pixels: function () { return gCharWidth; },
+ },
+ },
+ { description: "WM_MOUSEHWHEEL, 40, 1 character with right Alt",
+ message: WM_MOUSEHWHEEL, delta: 40,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: ALT_R,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 1, pixels: function () { return gCharWidth; },
+ },
+
+ finish: function () {
+ runNextTest(gScrollMessageTests, 0);
+ }
+ },
+];
+
+var gPageScrllTests = [
+ // Pixel scroll event should be fired always but line scroll event should be
+ // fired only when accumulated delta value is over a line.
+ { description: "WM_MOUSEWHEEL, -60, 0.5 pages",
+ message: WM_MOUSEWHEEL, delta: -60,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 0, pixels: function () { return gPageHeight / 2; },
+ },
+ },
+ { description: "WM_MOUSEWHEEL, -60, 0.5 pages (pending: 0.5 pages)",
+ message: WM_MOUSEWHEEL, delta: -60,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: DOM_PAGE_SCROLL_DELTA,
+ pixels: function () { return ((gPageHeight / 2) + (gPageHeight % 2)); },
+ },
+ },
+ { description: "WM_MOUSEWHEEL, -60, 0.5 pages",
+ message: WM_MOUSEWHEEL, delta: -60,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 0, pixels: function () { return gPageHeight / 2; },
+ },
+ },
+
+ { description: "WM_MOUSEWHEEL, 60, -0.5 pages (pending: 0.5 pages)",
+ message: WM_MOUSEWHEEL, delta: 60,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 0, pixels: function () { return gPageHeight / -2; },
+ },
+ },
+ { description: "WM_MOUSEWHEEL, 60, -0.5 pages (pending: -0.5 pages)",
+ message: WM_MOUSEWHEEL, delta: 60,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: -DOM_PAGE_SCROLL_DELTA,
+ pixels: function () { return -((gPageHeight / 2) + (gPageHeight % 2)); },
+ },
+ },
+ { description: "WM_MOUSEWHEEL, 60, -0.5 pages",
+ message: WM_MOUSEWHEEL, delta: 60,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 0, pixels: function () { return gPageHeight / -2; },
+ },
+ },
+
+ { description: "WM_MOUSEHWHEEL, 60, 0.5 pages",
+ message: WM_MOUSEHWHEEL, delta: 60,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 0, pixels: function () { return gPageWidth / 2; },
+ },
+ },
+ { description: "WM_MOUSEHWHEEL, 60, 0.5 pages (pending: 0.5 pages)",
+ message: WM_MOUSEHWHEEL, delta: 60,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: DOM_PAGE_SCROLL_DELTA,
+ pixels: function () { return ((gPageWidth / 2) + (gPageWidth % 2)); },
+ },
+ },
+ { description: "WM_MOUSEHWHEEL, 60, 0.5 pages",
+ message: WM_MOUSEHWHEEL, delta: 60,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 0, pixels: function () { return gPageWidth / 2; },
+ },
+ },
+
+ { description: "WM_MOUSEHWHEEL, -60, -0.5 pages (pending: 0.5 pages)",
+ message: WM_MOUSEHWHEEL, delta: -60,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 0, pixels: function () { return gCharWidth / -2; },
+ },
+ },
+ { description: "WM_MOUSEHWHEEL, -60, -0.5 pages (pending: -0.5 pages)",
+ message: WM_MOUSEHWHEEL, delta: -60,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: -DOM_PAGE_SCROLL_DELTA,
+ pixels: function () { return -((gCharWidth / 2) + (gCharWidth % 2)); },
+ },
+ },
+ { description: "WM_MOUSEHWHEEL, -60, -0.5 pages",
+ message: WM_MOUSEHWHEEL, delta: -60,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 0, pixels: function () { return gCharWidth / -2; },
+ },
+ },
+];
+
+var gScrollMessageTests = [
+ // Widget should dispatch neither line scroll event nor pixel scroll event if
+ // the WM_*SCROLL's lParam is NULL and mouse wheel emulation is disabled.
+ { description: "WM_VSCROLL, SB_LINEDOWN, lParam is NULL, emulation disabled",
+ message: WM_VSCROLL, delta: SB_LINEDOWN,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 0, pixels: 0,
+ },
+ init: function () {
+ SpecialPowers.setIntPref(kVAmountPref, 3);
+ SpecialPowers.setIntPref(kHAmountPref, 3);
+ SpecialPowers.setBoolPref(kEmulateWheelByWMSCROLLPref, false);
+ },
+ },
+
+ { description: "WM_VSCROLL, SB_LINEUP, lParam is NULL, emulation disabled",
+ message: WM_VSCROLL, delta: SB_LINEUP,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 0, pixels: 0,
+ },
+ },
+
+ { description: "WM_HSCROLL, SB_LINERIGHT, lParam is NULL, emulation disabled",
+ message: WM_HSCROLL, delta: SB_LINERIGHT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 0, pixels: 0,
+ },
+ },
+
+ { description: "WM_HSCROLL, SB_LINELEFT, lParam is NULL, emulation disabled",
+ message: WM_HSCROLL, delta: SB_LINELEFT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 0, pixels: 0,
+ },
+ },
+
+ // Widget should emulate mouse wheel behavior for WM_*SCROLL even if the
+ // kEmulateWheelByWMSCROLLPref is disabled but the message's lParam is not
+ // NULL. Then, widget doesn't dispatch a pixel event for WM_*SCROLL messages,
+ // but ESM dispatches it instead.
+ { description: "WM_VSCROLL, SB_LINEUP, lParam is not NULL, emulation disabled",
+ message: WM_VSCROLL, delta: SB_LINEUP,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kVAxis, lines: -1, pixels: function () { return -gLineHeight; },
+ },
+ init: function () {
+ SpecialPowers.setBoolPref(kEmulateWheelByWMSCROLLPref, false);
+ },
+ },
+
+ { description: "WM_VSCROLL, SB_LINEDOWN, lParam is not NULL, emulation disabled",
+ message: WM_VSCROLL, delta: SB_LINEDOWN,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kVAxis, lines: 1, pixels: function () { return gLineHeight; },
+ },
+ },
+
+ { description: "WM_HSCROLL, SB_LINELEFT, lParam is not NULL, emulation disabled",
+ message: WM_HSCROLL, delta: SB_LINELEFT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kHAxis, lines: -1, pixels: function () { return -gCharWidth; },
+ },
+ },
+
+ { description: "WM_HSCROLL, SB_LINERIGHT, lParam is not NULL, emulation disabled",
+ message: WM_HSCROLL, delta: SB_LINERIGHT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kHAxis, lines: 1, pixels: function () { return gCharWidth; },
+ },
+ },
+
+ { description: "WM_VSCROLL, SB_PAGEUP, lParam is not NULL, emulation disabled",
+ message: WM_VSCROLL, delta: SB_PAGEUP,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kVAxis, lines: -DOM_PAGE_SCROLL_DELTA,
+ pixels: function () { return -gPageHeight; },
+ },
+ },
+
+ { description: "WM_VSCROLL, SB_PAGEDOWN, lParam is not NULL, emulation disabled",
+ message: WM_VSCROLL, delta: SB_PAGEDOWN,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kVAxis, lines: DOM_PAGE_SCROLL_DELTA,
+ pixels: function () { return gPageHeight; },
+ },
+ },
+
+ { description: "WM_HSCROLL, SB_PAGELEFT, lParam is not NULL, emulation disabled",
+ message: WM_HSCROLL, delta: SB_PAGELEFT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kHAxis, lines: -DOM_PAGE_SCROLL_DELTA,
+ pixels: function () { return -gPageWidth; },
+ },
+ },
+
+ { description: "WM_HSCROLL, SB_PAGERIGHT, lParam is not NULL, emulation disabled",
+ message: WM_HSCROLL, delta: SB_PAGERIGHT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kHAxis, lines: DOM_PAGE_SCROLL_DELTA,
+ pixels: function () { return gPageWidth; },
+ },
+ },
+
+ // Widget should emulate mouse wheel behavior for WM_*SCROLL when the
+ // kEmulateWheelByWMSCROLLPref is enabled even if the message's lParam is
+ // NULL. Then, widget doesn't dispatch a pixel event for WM_*SCROLL messages,
+ // but ESM dispatches it instead.
+ { description: "WM_VSCROLL, SB_LINEUP, lParam is NULL, emulation enabled",
+ message: WM_VSCROLL, delta: SB_LINEUP,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: -1, pixels: function () { return -gLineHeight; },
+ },
+ init: function () {
+ SpecialPowers.setBoolPref(kEmulateWheelByWMSCROLLPref, true);
+ },
+ },
+
+ { description: "WM_VSCROLL, SB_LINEDOWN, lParam is NULL, emulation enabled",
+ message: WM_VSCROLL, delta: SB_LINEDOWN,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 1, pixels: function () { return gLineHeight; },
+ },
+ },
+
+ { description: "WM_HSCROLL, SB_LINELEFT, lParam is NULL, emulation enabled",
+ message: WM_HSCROLL, delta: SB_LINELEFT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: -1, pixels: function () { return -gCharWidth; },
+ },
+ },
+
+ { description: "WM_HSCROLL, SB_LINERIGHT, lParam is NULL, emulation enabled",
+ message: WM_HSCROLL, delta: SB_LINERIGHT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 1, pixels: function () { return gCharWidth; },
+ },
+ },
+
+ { description: "WM_VSCROLL, SB_PAGEUP, lParam is NULL, emulation enabled",
+ message: WM_VSCROLL, delta: SB_PAGEUP,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: -DOM_PAGE_SCROLL_DELTA,
+ pixels: function () { return -gPageHeight; },
+ },
+ },
+
+ { description: "WM_VSCROLL, SB_PAGEDOWN, lParam is NULL, emulation enabled",
+ message: WM_VSCROLL, delta: SB_PAGEDOWN,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: DOM_PAGE_SCROLL_DELTA,
+ pixels: function () { return gPageHeight; },
+ },
+ },
+
+ { description: "WM_HSCROLL, SB_PAGELEFT, lParam is NULL, emulation enabled",
+ message: WM_HSCROLL, delta: SB_PAGELEFT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: -DOM_PAGE_SCROLL_DELTA,
+ pixels: function () { return -gPageWidth; },
+ },
+ },
+
+ { description: "WM_HSCROLL, SB_PAGERIGHT, lParam is NULL, emulation enabled",
+ message: WM_HSCROLL, delta: SB_PAGERIGHT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: DOM_PAGE_SCROLL_DELTA,
+ pixels: function () { return gPageWidth; },
+ },
+ },
+
+ // Modifier key tests for WM_*SCROLL
+ { description: "WM_VSCROLL, SB_LINEDOWN, lParam is not NULL, emulation disabled, with left Shift",
+ message: WM_VSCROLL, delta: SB_LINEDOWN,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: SHIFT_L,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kVAxis, lines: 1, pixels: function () { return gLineHeight; },
+ },
+ init: function () {
+ SpecialPowers.setBoolPref(kEmulateWheelByWMSCROLLPref, false);
+ },
+ },
+ { description: "WM_VSCROLL, SB_LINEDOWN, lParam is not NULL, emulation disabled, with right Shift",
+ message: WM_VSCROLL, delta: SB_LINEDOWN,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: SHIFT_R,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kVAxis, lines: 1, pixels: function () { return gLineHeight; },
+ },
+ },
+ { description: "WM_VSCROLL, SB_LINEDOWN, lParam is not NULL, emulation disabled, with left Ctrl",
+ message: WM_VSCROLL, delta: SB_LINEDOWN,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: CTRL_L,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kVAxis, lines: 1, pixels: function () { return gLineHeight; },
+ },
+ },
+ { description: "WM_VSCROLL, SB_LINEDOWN, lParam is not NULL, emulation disabled, with right Ctrl",
+ message: WM_VSCROLL, delta: SB_LINEDOWN,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: CTRL_L,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kVAxis, lines: 1, pixels: function () { return gLineHeight; },
+ },
+ },
+ { description: "WM_VSCROLL, SB_LINEDOWN, lParam is not NULL, emulation disabled, with left Alt",
+ message: WM_VSCROLL, delta: SB_LINEDOWN,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: ALT_L,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kVAxis, lines: 1, pixels: function () { return gLineHeight; },
+ },
+ },
+ { description: "WM_VSCROLL, SB_LINEDOWN, lParam is not NULL, emulation disabled, with right Alt",
+ message: WM_VSCROLL, delta: SB_LINEDOWN,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: ALT_R,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kVAxis, lines: 1, pixels: function () { return gLineHeight; },
+ },
+ },
+
+ { description: "WM_HSCROLL, SB_LINERIGHT, lParam is not NULL, emulation disabled, with left Shift",
+ message: WM_HSCROLL, delta: SB_LINERIGHT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: SHIFT_L,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kHAxis, lines: 1, pixels: function () { return gCharWidth; },
+ },
+ },
+ { description: "WM_HSCROLL, SB_LINERIGHT, lParam is not NULL, emulation disabled, with right Shift",
+ message: WM_HSCROLL, delta: SB_LINERIGHT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: SHIFT_R,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kHAxis, lines: 1, pixels: function () { return gCharWidth; },
+ },
+ },
+ { description: "WM_HSCROLL, SB_LINERIGHT, lParam is not NULL, emulation disabled, with left Ctrl",
+ message: WM_HSCROLL, delta: SB_LINERIGHT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: CTRL_L,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kHAxis, lines: 1, pixels: function () { return gCharWidth; },
+ },
+ },
+ { description: "WM_HSCROLL, SB_LINERIGHT, lParam is not NULL, emulation disabled, with right Ctrl",
+ message: WM_HSCROLL, delta: SB_LINERIGHT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: CTRL_L,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kHAxis, lines: 1, pixels: function () { return gCharWidth; },
+ },
+ },
+ { description: "WM_HSCROLL, SB_LINERIGHT, lParam is not NULL, emulation disabled, with left Alt",
+ message: WM_HSCROLL, delta: SB_LINERIGHT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: ALT_L,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kHAxis, lines: 1, pixels: function () { return gCharWidth; },
+ },
+ },
+ { description: "WM_HSCROLL, SB_LINERIGHT, lParam is not NULL, emulation disabled, with right Alt",
+ message: WM_HSCROLL, delta: SB_LINERIGHT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: ALT_R,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kHAxis, lines: 1, pixels: function () { return gCharWidth; },
+ },
+
+ finish: function () {
+ runDeactiveWindowTests();
+ }
+ },
+];
+
+var gDeactiveWindowTests = [
+ // Typically, mouse drivers send wheel messages to focused window.
+ // However, we prefer to scroll a scrollable element under the mouse cursor.
+ { description: "WM_MOUSEWHEEL, -120, 3 lines, window is deactive",
+ message: WM_MOUSEWHEEL, delta: -120,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 3, pixels: function () { return gLineHeight * 3; },
+ },
+ init: function () {
+ SpecialPowers.setIntPref(kVAmountPref, 3);
+ SpecialPowers.setIntPref(kHAmountPref, 3);
+ },
+ onLineScrollEvent: function (aEvent) {
+ var fm = Components.classes["@mozilla.org/focus-manager;1"].
+ getService(Components.interfaces.nsIFocusManager);
+ is(fm.activeWindow, gOtherWindow, "The other window isn't activated");
+ },
+ },
+
+ { description: "WM_MOUSEWHEEL, 120, -3 lines, window is deactive",
+ message: WM_MOUSEWHEEL, delta: 120,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: -3, pixels: function () { return gLineHeight * -3; },
+ },
+ },
+
+ { description: "WM_MOUSEHWHEEL, 120, 3 chars, window is deactive",
+ message: WM_MOUSEHWHEEL, delta: 120,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 3, pixels: function () { return gCharWidth * 3; },
+ },
+ },
+
+ { description: "WM_MOUSEHWHEEL, -120, -3 chars, window is deactive",
+ message: WM_MOUSEHWHEEL, delta: -120,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: -3, pixels: function () { return gCharWidth * -3; },
+ },
+ },
+
+ // Of course, even if some drivers prefer the cursor position, we don't need
+ // to change anything.
+ { description: "WM_MOUSEWHEEL, -120, 3 lines, window is deactive (receive the message directly)",
+ message: WM_MOUSEWHEEL, delta: -120,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
+ expected: {
+ axis: kVAxis, lines: 3, pixels: function () { return gLineHeight * 3; },
+ },
+ },
+
+ { description: "WM_MOUSEWHEEL, 120, -3 lines, window is deactive (receive the message directly)",
+ message: WM_MOUSEWHEEL, delta: 120,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
+ expected: {
+ axis: kVAxis, lines: -3, pixels: function () { return gLineHeight * -3; },
+ },
+ },
+
+ { description: "WM_MOUSEHWHEEL, 120, 3 chars, window is deactive (receive the message directly)",
+ message: WM_MOUSEHWHEEL, delta: 120,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
+ expected: {
+ axis: kHAxis, lines: 3, pixels: function () { return gCharWidth * 3; },
+ },
+ },
+
+ { description: "WM_MOUSEHWHEEL, -120, -3 chars, window is deactive (receive the message directly)",
+ message: WM_MOUSEHWHEEL, delta: -120,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
+ expected: {
+ axis: kHAxis, lines: -3, pixels: function () { return gCharWidth * -3; },
+ },
+ },
+
+ // Same for WM_*SCROLL if lParam is not NULL
+ { description: "WM_VSCROLL, SB_LINEUP, lParam is not NULL, emulation disabled, window is deactive",
+ message: WM_VSCROLL, delta: SB_LINEUP,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kVAxis, lines: -1, pixels: function () { return -gLineHeight; },
+ },
+ init: function () {
+ SpecialPowers.setBoolPref(kEmulateWheelByWMSCROLLPref, false);
+ },
+ },
+
+ { description: "WM_VSCROLL, SB_LINEDOWN, lParam is not NULL, emulation disabled, window is deactive",
+ message: WM_VSCROLL, delta: SB_LINEDOWN,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kVAxis, lines: 1, pixels: function () { return gLineHeight; },
+ },
+ },
+
+ { description: "WM_HSCROLL, SB_LINELEFT, lParam is not NULL, emulation disabled, window is deactive",
+ message: WM_HSCROLL, delta: SB_LINELEFT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kHAxis, lines: -1, pixels: function () { return -gCharWidth; },
+ },
+ },
+
+ { description: "WM_HSCROLL, SB_LINERIGHT, lParam is not NULL, emulation disabled, window is deactive",
+ message: WM_HSCROLL, delta: SB_LINERIGHT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
+ expected: {
+ axis: kHAxis, lines: 1, pixels: function () { return gCharWidth; },
+ },
+ },
+
+ // Same for WM_*SCROLL if lParam is NULL but emulation is enabled
+ { description: "WM_VSCROLL, SB_LINEUP, lParam is NULL, emulation enabled, window is deactive",
+ message: WM_VSCROLL, delta: SB_LINEUP,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: -1, pixels: function () { return -gLineHeight; },
+ },
+ init: function () {
+ SpecialPowers.setBoolPref(kEmulateWheelByWMSCROLLPref, true);
+ },
+ },
+
+ { description: "WM_VSCROLL, SB_LINEDOWN, lParam is NULL, emulation enabled, window is deactive",
+ message: WM_VSCROLL, delta: SB_LINEDOWN,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kVAxis, lines: 1, pixels: function () { return gLineHeight; },
+ },
+ },
+
+ { description: "WM_HSCROLL, SB_LINELEFT, lParam is NULL, emulation enabled, window is deactive",
+ message: WM_HSCROLL, delta: SB_LINELEFT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: -1, pixels: function () { return -gCharWidth; },
+ },
+ },
+
+ { description: "WM_HSCROLL, SB_LINERIGHT, lParam is NULL, emulation enabled, window is deactive",
+ message: WM_HSCROLL, delta: SB_LINERIGHT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: 0,
+ expected: {
+ axis: kHAxis, lines: 1, pixels: function () { return gCharWidth; },
+ },
+ },
+
+ // Same for WM_*SCROLL if lParam is not NULL and message sent to the deactive window directly
+ { description: "WM_VSCROLL, SB_LINEUP, lParam is not NULL, emulation disabled, window is deactive (receive the message directly)",
+ message: WM_VSCROLL, delta: SB_LINEUP,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL |
+ nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
+ expected: {
+ axis: kVAxis, lines: -1, pixels: function () { return -gLineHeight; },
+ },
+ init: function () {
+ SpecialPowers.setBoolPref(kEmulateWheelByWMSCROLLPref, false);
+ },
+ },
+
+ { description: "WM_VSCROLL, SB_LINEDOWN, lParam is not NULL, emulation disabled, window is deactive (receive the message directly)",
+ message: WM_VSCROLL, delta: SB_LINEDOWN,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL |
+ nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
+ expected: {
+ axis: kVAxis, lines: 1, pixels: function () { return gLineHeight; },
+ },
+ },
+
+ { description: "WM_HSCROLL, SB_LINELEFT, lParam is not NULL, emulation disabled, window is deactive (receive the message directly)",
+ message: WM_HSCROLL, delta: SB_LINELEFT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL |
+ nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
+ expected: {
+ axis: kHAxis, lines: -1, pixels: function () { return -gCharWidth; },
+ },
+ },
+
+ { description: "WM_HSCROLL, SB_LINERIGHT, lParam is not NULL, emulation disabled, window is deactive (receive the message directly)",
+ message: WM_HSCROLL, delta: SB_LINERIGHT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL |
+ nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
+ expected: {
+ axis: kHAxis, lines: 1, pixels: function () { return gCharWidth; },
+ },
+ },
+
+ // Same for WM_*SCROLL if lParam is NULL but emulation is enabled, and message sent to the deactive window directly
+ { description: "WM_VSCROLL, SB_LINEUP, lParam is NULL, emulation enabled, window is deactive (receive the message directly)",
+ message: WM_VSCROLL, delta: SB_LINEUP,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
+ expected: {
+ axis: kVAxis, lines: -1, pixels: function () { return -gLineHeight; },
+ },
+ init: function () {
+ SpecialPowers.setBoolPref(kEmulateWheelByWMSCROLLPref, true);
+ },
+ },
+
+ { description: "WM_VSCROLL, SB_LINEDOWN, lParam is NULL, emulation enabled, window is deactive (receive the message directly)",
+ message: WM_VSCROLL, delta: SB_LINEDOWN,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
+ expected: {
+ axis: kVAxis, lines: 1, pixels: function () { return gLineHeight; },
+ },
+ },
+
+ { description: "WM_HSCROLL, SB_LINELEFT, lParam is NULL, emulation enabled, window is deactive (receive the message directly)",
+ message: WM_HSCROLL, delta: SB_LINELEFT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
+ expected: {
+ axis: kHAxis, lines: -1, pixels: function () { return -gCharWidth; },
+ },
+ },
+
+ { description: "WM_HSCROLL, SB_LINERIGHT, lParam is NULL, emulation enabled, window is deactive (receive the message directly)",
+ message: WM_HSCROLL, delta: SB_LINERIGHT,
+ target: gP1, x: 10, y: 10, window: window,
+ modifiers: 0,
+ additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
+ expected: {
+ axis: kHAxis, lines: 1, pixels: function () { return gCharWidth; },
+ },
+
+ finish: function () {
+ gOtherWindow.close();
+ gOtherWindow = null;
+ window.close();
+ }
+ },
+];
+
+function runDeactiveWindowTests()
+{
+ gOtherWindow = window.open("data:text/html,", "_blank",
+ "chrome,width=100,height=100,top=700,left=700");
+
+ window.opener.wrappedJSObject.SimpleTest.waitForFocus(function () {
+ runNextTest(gDeactiveWindowTests, 0);
+ }, gOtherWindow);
+}
+
+function runNextTest(aTests, aIndex)
+{
+ if (aIndex > 0 && aTests[aIndex - 1] && aTests[aIndex - 1].finish) {
+ aTests[aIndex - 1].finish();
+ }
+
+ if (aTests.length == aIndex) {
+ return;
+ }
+
+ var test = aTests[aIndex++];
+ if (test.init) {
+ test.init();
+ }
+ test.handled = { lines: false, pixels: false };
+
+ switch (test.message) {
+ case WM_MOUSEWHEEL:
+ case WM_MOUSEHWHEEL:
+ case WM_VSCROLL:
+ case WM_HSCROLL:
+ var expectedLines = test.expected.lines;
+ var expectedPixels =
+ cut((typeof test.expected.pixels == "function") ?
+ test.expected.pixels() : test.expected.pixels);
+ var handler = function (aEvent) {
+ var doCommonTests = true;
+
+ if (!aEvent) {
+ ok(!test.handled.lines,
+ test.description + ", line scroll event has been handled");
+ ok(!test.handled.pixels,
+ test.description + ", pixel scroll event has been handled");
+ doCommonTests = false;
+ } else if (aEvent.type == kMouseLineScrollEvent) {
+ ok(!test.handled.lines,
+ test.description + ":(" + aEvent.type + "), same event has already been handled");
+ test.handled.lines = true;
+ isnot(expectedLines, 0,
+ test.description + ":(" + aEvent.type + "), event shouldn't be fired");
+ if (test.onLineScrollEvent && test.onLineScrollEvent(aEvent)) {
+ doCommonTests = false;
+ }
+ } else if (aEvent.type == kMousePixelScrollEvent) {
+ ok(!test.handled.pixels,
+ test.description + ":(" + aEvent.type + "), same event has already been handled");
+ test.handled.pixels = true;
+ isnot(expectedPixels, 0,
+ test.description + ":(" + aEvent.type + "), event shouldn't be fired");
+ if (test.onPixelScrollEvent && test.onPixelScrollEvent(aEvent)) {
+ doCommonTests = false;
+ }
+ }
+
+ if (doCommonTests) {
+ var expectedDelta =
+ (aEvent.type == kMouseLineScrollEvent) ?
+ expectedLines : expectedPixels;
+ is(aEvent.target.id, test.target.id,
+ test.description + ":(" + aEvent.type + "), ID mismatch");
+ is(aEvent.axis, test.expected.axis,
+ test.description + ":(" + aEvent.type + "), axis mismatch");
+ ok(aEvent.detail != 0,
+ test.description + ":(" + aEvent.type + "), delta must not be 0");
+ is(aEvent.detail, expectedDelta,
+ test.description + ":(" + aEvent.type + "), delta mismatch");
+ is(aEvent.shiftKey, (test.modifiers & (SHIFT_L | SHIFT_R)) != 0,
+ test.description + ":(" + aEvent.type + "), shiftKey mismatch");
+ is(aEvent.ctrlKey, (test.modifiers & (CTRL_L | CTRL_R)) != 0,
+ test.description + ":(" + aEvent.type + "), ctrlKey mismatch");
+ is(aEvent.altKey, (test.modifiers & (ALT_L | ALT_R)) != 0,
+ test.description + ":(" + aEvent.type + "), altKey mismatch");
+ }
+
+ if (!aEvent || (test.handled.lines || expectedLines == 0) &&
+ (test.handled.pixels || expectedPixels == 0)) {
+ // Don't scroll actually.
+ if (aEvent) {
+ aEvent.preventDefault();
+ }
+ test.target.removeEventListener(kMouseLineScrollEvent, handler, true);
+ test.target.removeEventListener(kMousePixelScrollEvent, handler, true);
+ setTimeout(runNextTest, 0, aTests, aIndex);
+ }
+ };
+
+ test.target.addEventListener(kMouseLineScrollEvent, handler, true);
+ test.target.addEventListener(kMousePixelScrollEvent, handler, true);
+
+ if (expectedLines == 0 && expectedPixels == 0) {
+ // The timeout might not be enough if system is slow by other process,
+ // so, the test might be passed unexpectedly. However, it must be able
+ // to be detected by random orange.
+ setTimeout(handler, 500);
+ }
+
+ var utils = getWindowUtils(test.window);
+ var ptInScreen = getPointInScreen(test.target, test.window);
+ var isVertical =
+ ((test.message == WM_MOUSEWHEEL) || (test.message == WM_VSCROLL));
+ var deltaX = !isVertical ? test.delta : 0;
+ var deltaY = isVertical ? test.delta : 0;
+ utils.sendNativeMouseScrollEvent(ptInScreen.x + test.x,
+ ptInScreen.y + test.y,
+ test.message, deltaX, deltaY, 0,
+ test.modifiers,
+ test.additionalFlags,
+ test.target);
+ break;
+ default:
+ ok(false, test.description + ": invalid message");
+ // Let's timeout.
+ }
+}
+
+function prepareTests()
+{
+ // Disable special action with modifier key
+ SpecialPowers.setIntPref(kAltKeyActionPref, 1);
+ SpecialPowers.setIntPref(kCtrlKeyActionPref, 1);
+ SpecialPowers.setIntPref(kShiftKeyActionPref, 1);
+ SpecialPowers.setIntPref(kWinKeyActionPref, 1);
+
+ SpecialPowers.setIntPref(kAltKeyDeltaMultiplierXPref, 100);
+ SpecialPowers.setIntPref(kAltKeyDeltaMultiplierYPref, 100);
+ SpecialPowers.setIntPref(kCtrlKeyDeltaMultiplierXPref, 100);
+ SpecialPowers.setIntPref(kCtrlKeyDeltaMultiplierYPref, 100);
+ SpecialPowers.setIntPref(kShiftKeyDeltaMultiplierXPref, 100);
+ SpecialPowers.setIntPref(kShiftKeyDeltaMultiplierYPref, 100);
+ SpecialPowers.setIntPref(kWinKeyDeltaMultiplierXPref, 100);
+ SpecialPowers.setIntPref(kWinKeyDeltaMultiplierYPref, 100);
+
+ SpecialPowers.setBoolPref(kSystemScrollSpeedOverridePref, false);
+ SpecialPowers.setIntPref(kTimeoutPref, -1);
+
+ runNextTest(gPreparingSteps, 0);
+}
+
+</script>
+</body>
+
+</html>
diff --git a/widget/tests/window_picker_no_crash_child.html b/widget/tests/window_picker_no_crash_child.html
new file mode 100644
index 000000000..51bf1b1e6
--- /dev/null
+++ b/widget/tests/window_picker_no_crash_child.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+ <title>Picker window</title>
+</head>
+<body>
+<form name="form1">
+<input type="file" name="uploadbox">
+</form>
+</body>
+</html>
diff --git a/widget/tests/window_state_windows.xul b/widget/tests/window_state_windows.xul
new file mode 100644
index 000000000..9643e1dad
--- /dev/null
+++ b/widget/tests/window_state_windows.xul
@@ -0,0 +1,87 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window id="NativeWindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="300"
+ height="300"
+ onload="onLoad();"
+ title="Window State Tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript">
+ <![CDATA[
+
+ let Cc = Components.classes;
+ let Ci = Components.interfaces;
+ let Cu = Components.utils;
+ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+ SimpleTest.waitForExplicitFinish();
+
+ function onLoad() {
+ var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
+ var win = wm.getMostRecentWindow("navigator:browser");
+
+ /*
+ switch(win.windowState) {
+ case win.STATE_FULLSCREEN:
+ dump("STATE_FULLSCREEN \n");
+ break;
+ case win.STATE_MAXIMIZED:
+ dump("STATE_MAXIMIZED \n");
+ break;
+ case win.STATE_MINIMIZED:
+ dump("STATE_MINIMIZED \n");
+ break;
+ case win.STATE_NORMAL:
+ dump("STATE_NORMAL \n");
+ break;
+ }
+ */
+
+ // Make sure size mode changes are reflected in the widget.
+ win.restore();
+ ok(win.windowState == win.STATE_NORMAL, "window state is restored.");
+ win.minimize();
+ ok(win.windowState == win.STATE_MINIMIZED, "window state is minimized.");
+
+ // Windows resizes children to 0x0. Code in nsWindow filters these changes out. Without
+ // this all sorts of screwy things can happen in child widgets.
+ ok(document.height > 0, "document height should not be zero for a minimized window!");
+ ok(document.width > 0, "document width should not be zero for a minimized window!");
+
+ // Make sure size mode changes are reflected in the widget.
+ win.restore();
+ ok(win.windowState == win.STATE_NORMAL, "window state is restored.");
+ win.maximize();
+ ok(win.windowState == win.STATE_MAXIMIZED, "window state is maximized.");
+ win.restore();
+ ok(win.windowState == win.STATE_NORMAL, "window state is restored.");
+
+ /*
+ dump(win.screenX + "\n");
+ win.minimize();
+ dump(win.screenX + "\n");
+ win.restore();
+ dump(win.screenX + "\n");
+ */
+
+ SimpleTest.finish();
+ }
+
+ ]]>
+ </script>
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/widget/tests/window_wheeltransaction.xul b/widget/tests/window_wheeltransaction.xul
new file mode 100644
index 000000000..8573eb3a4
--- /dev/null
+++ b/widget/tests/window_wheeltransaction.xul
@@ -0,0 +1,1560 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<window title="Wheel scroll tests"
+ width="600" height="600"
+ onload="onload();"
+ onunload="onunload();"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/paint_listener.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<style type="text/css">
+ #rootview {
+ overflow: auto;
+ width: 400px;
+ height: 400px;
+ border: 1px solid;
+ }
+ #container {
+ overflow: auto;
+ width: 600px;
+ height: 600px;
+ }
+ #rootview pre {
+ margin: 20px 0 20px 20px;
+ padding: 0;
+ overflow: auto;
+ display: block;
+ width: 100px;
+ height: 100.5px;
+ font-size: 16px;
+ }
+</style>
+<div id="rootview" onscroll="onScrollView(event);">
+ <div id="container">
+ <pre id="subview1" onscroll="onScrollView(event);">
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+ </pre>
+ <pre id="subview2" onscroll="onScrollView(event);">
+Text.
+Text.
+Text.
+Text.
+Text.
+Text.
+Text.
+Text.
+Text.
+Text.
+ </pre>
+ <pre id="subview3" onscroll="onScrollView(event);">
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+ </pre>
+ </div>
+</div>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+function ok(aCondition, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.ok(aCondition, aMessage);
+}
+
+function is(aLeft, aRight, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.is(aLeft, aRight, aMessage);
+}
+
+function isnot(aLeft, aRight, aMessage)
+{
+ window.opener.wrappedJSObject.SimpleTest.isnot(aLeft, aRight, aMessage);
+}
+
+var gCurrentTestListStatus = { nextListIndex: 0 };
+var gCurrentTest;
+
+const kListenEvent_None = 0;
+const kListenEvent_OnScroll = 1;
+const kListenEvent_OnScrollFailed = 2;
+const kListenEvent_OnTransactionTimeout = 4;
+const kListenEvent_All = kListenEvent_OnScroll |
+ kListenEvent_OnScrollFailed |
+ kListenEvent_OnTransactionTimeout;
+var gLitesnEvents = kListenEvent_None;
+
+/**
+ * At unexpected transaction timeout, we need to stop *all* timers. But it is
+ * difficult and it can be create more complex testing code. So, we should use
+ * only one timer at one time. For that, we must store the timer id to this
+ * variable. And the functions which may be called via a timer must clear the
+ * current timer by |_clearTimer| function.
+ */
+var gTimer;
+
+var gPrefSvc = Components.classes["@mozilla.org/preferences-service;1"].
+ getService(Components.interfaces.nsIPrefBranch);
+const kPrefSmoothScroll = "general.smoothScroll";
+const kPrefNameTimeout = "mousewheel.transaction.timeout";
+const kPrefNameIgnoreMoveDelay = "mousewheel.transaction.ignoremovedelay";
+const kPrefTestEventsAsyncEnabled = "test.events.async.enabled";
+
+const kDefaultTimeout = gPrefSvc.getIntPref(kPrefNameTimeout);
+const kDefaultIgnoreMoveDelay = gPrefSvc.getIntPref(kPrefNameIgnoreMoveDelay);
+
+gPrefSvc.setBoolPref(kPrefSmoothScroll, false);
+gPrefSvc.setBoolPref(kPrefTestEventsAsyncEnabled, true);
+
+var gTimeout, gIgnoreMoveDelay;
+var gEnoughForTimeout, gEnoughForIgnoreMoveDelay;
+
+function setTimeoutPrefs(aTimeout, aIgnoreMoveDelay)
+{
+ gPrefSvc.setIntPref(kPrefNameTimeout, aTimeout);
+ gPrefSvc.setIntPref(kPrefNameIgnoreMoveDelay, aIgnoreMoveDelay);
+ gTimeout = aTimeout;
+ gIgnoreMoveDelay = aIgnoreMoveDelay;
+ gEnoughForTimeout = gTimeout * 2;
+ gEnoughForIgnoreMoveDelay = gIgnoreMoveDelay * 1.2;
+}
+
+function resetTimeoutPrefs()
+{
+ if (gTimeout == kDefaultTimeout)
+ return;
+ setTimeoutPrefs(kDefaultTimeout, kDefaultIgnoreMoveDelay);
+ initTestList();
+}
+
+function growUpTimeoutPrefs()
+{
+ if (gTimeout != kDefaultTimeout)
+ return;
+ setTimeoutPrefs(5000, 1000);
+ initTestList();
+}
+
+// setting enough time for testing.
+gPrefSvc.setIntPref(kPrefNameTimeout, gTimeout);
+gPrefSvc.setIntPref(kPrefNameIgnoreMoveDelay, gIgnoreMoveDelay);
+
+var gRootView = document.getElementById("rootview");
+var gSubView1 = document.getElementById("subview1");
+var gSubView2 = document.getElementById("subview2");
+var gSubView3 = document.getElementById("subview3");
+
+gRootView.addEventListener("MozMouseScrollFailed", onMouseScrollFailed, false);
+gRootView.addEventListener("MozMouseScrollTransactionTimeout",
+ onTransactionTimeout, false);
+
+function finish()
+{
+ window.close();
+}
+
+function onload()
+{
+ runNextTestList();
+}
+
+function onunload()
+{
+ resetTimeoutPrefs();
+ gPrefSvc.clearUserPref(kPrefSmoothScroll);
+ gPrefSvc.clearUserPref(kPrefTestEventsAsyncEnabled);
+ disableNonTestMouseEvents(false);
+ SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
+ window.opener.wrappedJSObject.SimpleTest.finish();
+}
+
+const kSubView1Offset = { x: 20, y: 20 };
+const kSubView2Offset = { x: 20, y: 20 + 100 + 20 };
+const kSubView3Offset = { x: 20, y: 20 + (100 + 20) * 2 };
+
+function _getSubViewTestPtForV(aPt)
+{
+ return { x: aPt.x + 10, y: aPt.y + 10 };
+}
+
+const kPtInRootViewForV = { x: kSubView1Offset.x + 10,
+ y: kSubView1Offset.y - 10 };
+const kPtInSubView1ForV = _getSubViewTestPtForV(kSubView1Offset);
+const kPtInSubView2ForV = _getSubViewTestPtForV(kSubView2Offset);
+const kPtInSubView3ForV = _getSubViewTestPtForV(kSubView3Offset);
+
+function _convertTestPtForH(aPt)
+{
+ return { x: aPt.y, y: aPt.x };
+}
+
+const kPtInRootViewForH = _convertTestPtForH(kPtInRootViewForV);
+const kPtInSubView1ForH = _convertTestPtForH(kPtInSubView1ForV);
+const kPtInSubView2ForH = _convertTestPtForH(kPtInSubView2ForV);
+const kPtInSubView3ForH = _convertTestPtForH(kPtInSubView3ForV);
+
+/**
+ * Define the tests here:
+ * Scrolls are processed async always. Therefore, we need to call all tests
+ * by timer. gTestLists is array of testing lists. In other words, an item
+ * of gTestList is a group of one or more testing. Each items has following
+ * properties:
+ *
+ * - retryWhenTransactionTimeout
+ * The testing of wheel transaction might be fialed randomly by
+ * timeout. Then, automatically the failed test list will be retested
+ * automatically only this number of times.
+ *
+ * - steps
+ * This property is array of testing. Each steps must have following
+ * properties at least.
+ *
+ * - func
+ * This property means function which will be called via
+ * |setTimeout|. The function cannot have params. If you need
+ * some additional parameters, you can specify some original
+ * properties for the test function. If you do so, you should
+ * document it in the testing function.
+ * - delay
+ * This property means delay time until the function to be called.
+ * I.e., the value used for the second param of |setTimeout|.
+ *
+ * And also you need one more property when you call a testing function.
+ *
+ * - description
+ * This property is description of the test. This is used for
+ * logging.
+ *
+ * At testing, you can access to current step via |gCurrentTest|.
+ */
+
+var gTestLists;
+function initTestList()
+{
+ gTestLists = [
+ /**************************************************************************
+ * Continuous scrolling test for |gRootView|
+ * |gRootView| has both scrollbars and it has three children which are
+ * |gSubView1|, |gSubView2| and |gSubView3|. They have scrollbars. If
+ * the current transaction targets |gRootView|, other children should not
+ * be scrolled even if the wheel events are fired on them.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical case
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Vertical wheel events should scroll |gRootView| even if the position
+ // of wheel events in a child view which has scrollbar.
+ { func: testContinuousScroll, delay: 0, offset: kPtInRootViewForV,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ description: "Continuous scrolling test for root view (vertical/forward)" },
+ { func: testContinuousScroll, delay: 0, offset: kPtInRootViewForV,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ description: "Continuous scrolling test for root view (vertical/backward)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal case
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Horizontal wheel events should scroll |gRootView| even if the
+ // position of wheel events in a child view which has scrollbar.
+ { func: testContinuousScroll, delay: 0, offset: kPtInRootViewForH,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ description: "Continuous scrolling test for root view (horizontal/forward)" },
+ { func: testContinuousScroll, delay: 0, offset: kPtInRootViewForH,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ description: "Continuous scrolling test for root view (horizontal/backward)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Continuous scrolling test for |gSubView1|
+ * |gSubView1| has both scrollbars.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical case
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Vertical wheel events should scroll |gSubView1|.
+ { func: testContinuousScroll, delay: 0, offset: kPtInSubView1ForV,
+ isForward: true, isVertical: true, expectedView: gSubView1,
+ description: "Continuous scrolling test for sub view 1 (vertical/forward)" },
+ { func: testContinuousScroll, delay: 0, offset: kPtInSubView1ForV,
+ isForward: false, isVertical: true, expectedView: gSubView1,
+ description: "Continuous scrolling test for sub view 1 (vertical/backward)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal case
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Horitontal wheel events should scroll |gSubView1|.
+ { func: testContinuousScroll, delay: 0, offset: kPtInSubView1ForH,
+ isForward: true, isVertical: false, expectedView: gSubView1,
+ description: "Continuous scrolling test for sub view 1 (horizontal/forward)" },
+ { func: testContinuousScroll, delay: 0, offset: kPtInSubView1ForH,
+ isForward: false, isVertical: false, expectedView: gSubView1,
+ description: "Continuous scrolling test for sub view 1 (horizontal/backward)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Continuous scrolling test for |gSubView2|
+ * |gSubView2| has only vertical scrollbar.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical case
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Vertical wheel events should scroll |gSubView2|.
+ { func: testContinuousScroll, delay: 0, offset: kPtInSubView2ForV,
+ isForward: true, isVertical: true, expectedView: gSubView2,
+ description: "Continuous scrolling test for sub view 2 (vertical/forward)" },
+ { func: testContinuousScroll, delay: 0, offset: kPtInSubView2ForV,
+ isForward: false, isVertical: true, expectedView: gSubView2,
+ description: "Continuous scrolling test for sub view 2 (vertical/backward)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal case
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Horizontal wheel events should scroll its nearest scrollable ancestor
+ // view, i.e., it is |gRootView|.
+ { func: testContinuousScroll, delay: 0, offset: kPtInSubView2ForH,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ description: "Continuous scrolling test for sub view 2 (horizontal/forward)" },
+ { func: testContinuousScroll, delay: 0, offset: kPtInSubView2ForH,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ description: "Continuous scrolling test for sub view 2 (horizontal/backward)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Continuous scrolling test for |gSubView3|
+ * |gSubView3| has only horizontal scrollbar.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical case
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Vertical wheel events should scroll its nearest scrollable ancestor
+ // view, i.e., it is |gRootView|.
+ { func: testContinuousScroll, delay: 0, offset: kPtInSubView3ForV,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ description: "Continuous scrolling test for sub view 3 (vertical/forward)" },
+ { func: testContinuousScroll, delay: 0, offset: kPtInSubView3ForV,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ description: "Continuous scrolling test for sub view 3 (vertical/backward)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal case
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Horitontal wheel events should scroll |gSubView3|.
+ { func: testContinuousScroll, delay: 0, offset: kPtInSubView3ForH,
+ isForward: true, isVertical: false, expectedView: gSubView3,
+ description: "Continuous scrolling test for sub view 3 (horizontal/forward)" },
+ { func: testContinuousScroll, delay: 0, offset: kPtInSubView3ForH,
+ isForward: false, isVertical: false, expectedView: gSubView3,
+ description: "Continuous scrolling test for sub view 3 (horizontal/backward)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Don't reset transaction by a different direction wheel event
+ * Even if a wheel event doesn't same direction as last wheel event, the
+ * current transaction should not be reset.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical -> Horizontal
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gRootView| by a vertical wheel
+ // event.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForV,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ description: "Don't reset transaction by a different direction wheel event (1-1)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForV,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ description: "Don't reset transaction by a different direction wheel event (1-2)" },
+ // Send a horizontal wheel event over |gSubView1| but |gRootView| should
+ // be scrolled.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Don't reset transaction by a different direction wheel event (1-3)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal -> Vertical
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gRootView| by a horizontal wheel
+ // event.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForH,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ description: "Don't reset transaction by a different direction wheel event (2-1)" },
+ // Scroll back to left-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForH,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ description: "Don't reset transaction by a different direction wheel event (2-2)" },
+ // Send a vertical wheel event over |gSubView1| but |gRootView| should
+ // be scrolled.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForH,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Don't reset transaction by a different direction wheel event (2-3)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Don't reset transaction even if a wheel event cannot scroll
+ * Even if a wheel event cannot scroll to specified direction in the
+ * current target view, the transaction should not be reset. E.g., there
+ * are some devices which can scroll obliquely. If so, probably, users
+ * cannot input only intended direction.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // A view only has vertical scrollbar case.
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gSubView2|.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView2ForV,
+ isForward: true, isVertical: true, expectedView: gSubView2,
+ description: "Don't reset transaction even if a wheel event cannot scroll (1-1)" },
+ // |gSubView2| doesn't have horizontal scrollbar but should not scroll
+ // any views.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView2ForV,
+ isForward: true, isVertical: false, expectedView: null,
+ description: "Don't reset transaction even if a wheel event cannot scroll (1-2)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // A view only has horizontal scrollbar case.
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gSubView3|.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView3ForV,
+ isForward: true, isVertical: false, expectedView: gSubView3,
+ description: "Don't reset transaction even if a wheel event cannot scroll (2-1)" },
+ // |gSubView3| doesn't have vertical scrollbar but should not scroll any
+ // views.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView3ForV,
+ isForward: true, isVertical: true, expectedView: null,
+ description: "Don't reset transaction even if a wheel event cannot scroll (2-2)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Reset transaction by mouse down/mouse up events
+ * Mouse down and mouse up events should cause resetting the current
+ * transaction.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical case
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gRootView|.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForV,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ description: "Reset transaction by mouse down/mouse up events (v-1)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForV,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ description: "Reset transaction by mouse down/mouse up events (v-2)" },
+ // Send mouse button events which should reset the current transaction.
+ // So, the next wheel event should scroll |gSubView1|.
+ { func: sendMouseButtonEvents, delay: 0,
+ description: "sendMouseButtonEvents" },
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
+ isForward: true, isVertical: true, expectedView: gSubView1,
+ description: "Reset transaction by mouse down/mouse up events (v-3)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal case
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gRootView|.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForH,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ description: "Reset transaction by mouse down/mouse up events (h-1)" },
+ // Scroll back to left-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForH,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ description: "Reset transaction by mouse down/mouse up events (h-2)" },
+ // Send mouse button events which should reset the current transaction.
+ // So, the next wheel event should scroll |gSubView1|.
+ { func: sendMouseButtonEvents, delay: 0,
+ description: "sendMouseButtonEvents" },
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForH,
+ isForward: true, isVertical: false, expectedView: gSubView1,
+ description: "Reset transaction by mouse down/mouse up events (h-3)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Reset transaction by a key event
+ * A key event should cause resetting the current transaction.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical case
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gRootView|.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForV,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ description: "Reset transaction by a key event (v-1)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForV,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ description: "Reset transaction by a key event (v-2)" },
+ // Send a key event which should reset the current transaction. So, the
+ // next wheel event should scroll |gSubView1|.
+ { func: sendKeyEvents, delay: 0, key: "a",
+ description: "sendKeyEvents" },
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
+ isForward: true, isVertical: true, expectedView: gSubView1,
+ description: "Reset transaction by a key event (v-3)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal case
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gRootView|.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForH,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ description: "Reset transaction by a key event (h-1)" },
+ // Scroll back to left-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForH,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ description: "Reset transaction by a key event (h-2)" },
+ // Send a key event which should reset the current transaction. So, the
+ // next wheel event should scroll |gSubView1|.
+ { func: sendKeyEvents, delay: 0, key: "a",
+ description: "sendKeyEvents" },
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForH,
+ isForward: true, isVertical: false, expectedView: gSubView1,
+ description: "Reset transaction by a key event (h-3)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Reset transaction by a mouse move event
+ * A mouse move event can cause reseting the current transaction even if
+ * mouse cursor is inside the target view of current transaction. Only
+ * when a wheel event is fired after |gIgnoreMoveDelay| milliseconds since
+ * the first mouse move event from last wheel event, the transaction
+ * should be reset.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical case
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gRootView|.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForV,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ description: "Reset transaction by a mouse move event (v-1)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForV,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ description: "Reset transaction by a mouse move event (v-2)" },
+ // Send a mouse move event immediately after last wheel event, then,
+ // current transaction should be kept.
+ { func: sendMouseMoveEvent, delay: 0, offset: kPtInSubView1ForV,
+ description: "sendMouseMoveEvent" },
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (v-3)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (v-4)" },
+ // Send a mouse move event after |gIgnoreMoveDelay| milliseconds since
+ // last wheel event, then, current transaction should be kept.
+ { func: sendMouseMoveEvent, delay: gEnoughForIgnoreMoveDelay,
+ offset: kPtInSubView1ForV,
+ description: "sendMouseMoveEvent" },
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (v-5)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (v-6)" },
+ // Send a wheel event after |gIgnoreMoveDelay| milliseconds since last
+ // mouse move event but it is fired immediately after the last wheel
+ // event, then, current transaction should be kept.
+ { func: sendMouseMoveEvent, delay: 0, offset: kPtInSubView1ForV,
+ description: "sendMouseMoveEvent" },
+ { func: testOneTimeScroll, delay: gEnoughForIgnoreMoveDelay,
+ offset: kPtInSubView1ForV,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (v-7)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (v-8)" },
+ // Send a wheel event after |gIgnoreMoveDelay| milliseconds have passed
+ // since last mouse move event which is fired after |gIgnoreMoveDelay|
+ // milliseconds since last wheel event, then, current transaction should
+ // be reset.
+ { func: sendMouseMoveEvent, delay: gEnoughForIgnoreMoveDelay,
+ offset: kPtInSubView1ForV,
+ description: "sendMouseMoveEvent" },
+ { func: testOneTimeScroll, delay: gEnoughForIgnoreMoveDelay,
+ offset: kPtInSubView1ForV,
+ isForward: true, isVertical: true, expectedView: gSubView1,
+ canFailRandomly: { possibleView: gRootView },
+ description: "Reset transaction by a mouse move event (v-9)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal case
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gRootView|.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForH,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (h-1)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForH,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (h-2)" },
+ // Send a mouse move event immediately after last wheel event, then,
+ // current transaction should be kept.
+ { func: sendMouseMoveEvent, delay: 0, offset: kPtInSubView1ForH,
+ description: "sendMouseMoveEvent" },
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForH,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (h-3)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForH,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (h-4)" },
+ // Send a mouse move event after |gIgnoreMoveDelay| milliseconds since
+ // last wheel event, then, current transaction should be kept.
+ { func: sendMouseMoveEvent, delay: gEnoughForIgnoreMoveDelay,
+ offset: kPtInSubView1ForH,
+ description: "sendMouseMoveEvent" },
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForH,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (h-5)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForH,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (h-6)" },
+ // Send a wheel event after |gIgnoreMoveDelay| milliseconds since last
+ // mouse move event but it is fired immediately after the last wheel
+ // event, then, current transaction should be kept.
+ { func: sendMouseMoveEvent, delay: 0, offset: kPtInSubView1ForH,
+ description: "sendMouseMoveEvent" },
+ { func: testOneTimeScroll, delay: gEnoughForIgnoreMoveDelay,
+ offset: kPtInSubView1ForH,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (h-7)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForH,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (h-8)" },
+ // Send a wheel event after |gIgnoreMoveDelay| milliseconds have passed
+ // since last mouse move event which is fired after |gIgnoreMoveDelay|
+ // milliseconds since last wheel event, then, current transaction should
+ // be reset.
+ { func: sendMouseMoveEvent, delay: gEnoughForIgnoreMoveDelay,
+ offset: kPtInSubView1ForH,
+ description: "sendMouseMoveEvent" },
+ { func: testOneTimeScroll, delay: gEnoughForIgnoreMoveDelay,
+ offset: kPtInSubView1ForH,
+ isForward: true, isVertical: false, expectedView: gSubView1,
+ canFailRandomly: { possibleView: gRootView },
+ description: "Reset transaction by a mouse move event (h-9)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Reset transaction by a mouse move event on outside of view
+ * When mouse cursor is moved to outside of the current target view, the
+ * transaction should be reset immediately.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical case
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gSubView1|.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
+ isForward: true, isVertical: true, expectedView: gSubView1,
+ description: "Reset transaction by a mouse move event on outside of view (v-1)" },
+ // Send mouse move event over |gRootView|.
+ { func: sendMouseMoveEvent, delay: 0, offset: kPtInRootViewForV,
+ description: "sendMouseMoveEvent" },
+ // Send Wheel event over |gRootView| which should be scrolled.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForV,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ description: "Reset transaction by a mouse move event on outside of view (v-2)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal case
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gSubView1|.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForH,
+ isForward: true, isVertical: true, expectedView: gSubView1,
+ description: "Reset transaction by a mouse move event on outside of view (h-1)" },
+ // Send mouse move event over |gRootView|.
+ { func: sendMouseMoveEvent, delay: 0, offset: kPtInRootViewForH,
+ description: "sendMouseMoveEvent" },
+ // Send Wheel event over |gRootView| which should be scrolled.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForH,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ description: "Reset transaction by a mouse move event on outside of view (h-2)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Timeout test
+ * A view should not be scrolled during another to be transaction for
+ * another view scrolling. However, a wheel event which is sent after
+ * timeout, a view which is under the mouse cursor should be scrolled.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical case
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // First, create a transaction which should target the |gRootView|.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForV,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ description: "Timeout test (v-1)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForV,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ description: "Timeout test (v-2)" },
+ // A wheel event over |gSubView1| should not scroll it during current
+ // transaction.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Timeout test (v-3)" },
+ // Scroll back to top-most again.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Timeout test (v-4)" },
+ // A wheel event over |gSubView1| after timeout should scroll
+ // |gSubView1|.
+ { func: testOneTimeScroll, delay: gEnoughForTimeout,
+ offset: kPtInSubView1ForV,
+ isForward: true, isVertical: true, expectedView: gSubView1,
+ isTimeoutTesting: true,
+ description: "Timeout test (v-5)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal case
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // First, create a transaction which should target the |gRootView|.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForH,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ description: "Timeout test (h-1)" },
+ // Scroll back to left-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForH,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ description: "Timeout test (h-2)" },
+ // A wheel event over |gSubView1| should not scroll it during current
+ // transaction.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForH,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Timeout test (h-3)" },
+ // Scroll back to left-most again.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForH,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Timeout test (h-4)" },
+ // A wheel event over |gSubView1| after timeout should scroll
+ // |gSubView1|.
+ { func: testOneTimeScroll, delay: gEnoughForTimeout,
+ offset: kPtInSubView1ForH,
+ isForward: true, isVertical: false, expectedView: gSubView1,
+ isTimeoutTesting: true,
+ description: "Timeout test (h-5)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Timeout test even with many wheel events
+ * This tests whether timeout is occurred event if wheel events are sent.
+ * The transaction should not be updated by non-scrollable wheel events.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical case
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Scroll |gSubView1| to bottom-most.
+ { func: testContinuousScroll, delay: 0, offset: kPtInSubView1ForV,
+ isForward: true, isVertical: true, expectedView: gSubView1,
+ description: "Timeout test even with many wheel events (v-1)" },
+ // Don't scroll any views before timeout.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
+ isForward: true, isVertical: true, expectedView: null,
+ canFailRandomly: { possibleView: gRootView },
+ description: "Timeout test even with many wheel events (v-2)" },
+ // Recreate a transaction which is scrolling |gRootView| after time out.
+ { func: testRestartScroll, delay: 0, offset: kPtInSubView1ForV,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ description: "Timeout test even with many wheel events (v-3)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal case
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Scroll |gSubView1| to right-most.
+ { func: testContinuousScroll, delay: 0, offset: kPtInSubView1ForH,
+ isForward: true, isVertical: false, expectedView: gSubView1,
+ description: "Timeout test even with many wheel events (h-1)" },
+ // Don't scroll any views before timeout.
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForH,
+ isForward: true, isVertical: false, expectedView: null,
+ canFailRandomly: { possibleView: gRootView },
+ description: "Timeout test even with many wheel events (h-2)" },
+ // Recreate a transaction which is scrolling |gRootView| after time out.
+ { func: testRestartScroll, delay: 0, offset: kPtInSubView1ForH,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ description: "Timeout test even with many wheel events (h-3)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Very large scrolling wheel event
+ * If the delta value is larger than the scrolling page size, it should be
+ * scrolled only one page instead of the delta value.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
+ isForward: true, isVertical: true, expectedView: gSubView1,
+ delta: 5000,
+ description: "Very large delta scrolling (v-1)" },
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
+ isForward: true, isVertical: true, expectedView: gSubView1,
+ delta: 5000,
+ description: "Very large delta scrolling (v-2)" },
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
+ isForward: true, isVertical: false, expectedView: gSubView1,
+ delta: 5000,
+ description: "Very large delta scrolling (h-1)" },
+ { func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
+ isForward: true, isVertical: false, expectedView: gSubView1,
+ delta: 5000,
+ description: "Very large delta scrolling (h-2)" }
+ ]
+ }
+ ];
+}
+
+/******************************************************************************
+ * Actions for preparing tests
+ ******************************************************************************/
+
+function initElements()
+{
+ _clearTimer();
+
+ function resetScrollPosition(aElement)
+ {
+ aElement.scrollTop = 0;
+ aElement.scrollLeft = 0;
+ }
+
+ function initInRootView(aElement, aPt)
+ {
+ aElement.offset =
+ gCurrentTest.forVertical ? aPt : { x: aPt.y, y: aPt.x };
+ }
+
+ const kDisplay = gCurrentTest.forVertical ? "block" : "inline-block";
+ gSubView1.style.display = kDisplay;
+ gSubView2.style.display = kDisplay;
+ gSubView3.style.display = kDisplay;
+
+ resetScrollPosition(gRootView);
+ resetScrollPosition(gSubView1);
+ resetScrollPosition(gSubView2);
+ resetScrollPosition(gSubView3);
+ _getDOMWindowUtils(window).advanceTimeAndRefresh(0);
+
+ runNextTestStep();
+}
+
+function clearWheelTransaction()
+{
+ _clearTimer();
+ _clearTransaction();
+ runNextTestStep();
+}
+
+function sendKeyEvents()
+{
+ _clearTimer();
+ synthesizeKey(gCurrentTest.key, {}, window);
+ runNextTestStep();
+}
+
+function sendMouseButtonEvents()
+{
+ _clearTimer();
+ synthesizeMouse(gRootView, -1, -1, { type:"mousedown" }, window);
+ synthesizeMouse(gRootView, -1, -1, { type:"mouseup" }, window);
+ runNextTestStep();
+}
+
+function sendMouseMoveEvent()
+{
+ _clearTimer();
+ _fireMouseMoveEvent(gCurrentTest.offset);
+ runNextTestStep();
+}
+
+/******************************************************************************
+ * Utilities for testing functions
+ ******************************************************************************/
+
+function _clearTransaction()
+{
+ synthesizeMouse(gRootView, -1, -1, { type:"mousedown" }, window);
+ synthesizeMouse(gRootView, -1, -1, { type:"mouseup" }, window);
+}
+
+function _saveScrollPositions()
+{
+ function save(aElement)
+ {
+ aElement.prevTop = aElement.scrollTop;
+ aElement.prevLeft = aElement.scrollLeft;
+ }
+ save(gRootView);
+ save(gSubView1);
+ save(gSubView2);
+ save(gSubView3);
+}
+
+function _fireMouseMoveEvent(aOffset)
+{
+ synthesizeMouse(gRootView, aOffset.x, aOffset.y, { type:"mousemove" }, window);
+}
+
+function _fireWheelScrollEvent(aOffset, aIsVertical, aForward, aDelta)
+{
+ var event = { deltaMode: WheelEvent.DOM_DELTA_LINE };
+ if (aIsVertical) {
+ event.deltaY = aForward ? aDelta : -aDelta;
+ } else {
+ event.deltaX = aForward ? aDelta : -aDelta;
+ }
+ sendWheelAndPaint(gRootView, aOffset.x, aOffset.y, event, null, window);
+}
+
+function _canScroll(aElement, aIsVertical, aForward)
+{
+ if (aIsVertical) {
+ if (!aForward)
+ return aElement.scrollTop > 0;
+ return aElement.scrollHeight > aElement.scrollTop + aElement.clientHeight;
+ }
+ if (!aForward)
+ return aElement.scrollLeft > 0;
+ return aElement.scrollWidth > aElement.scrollLeft + aElement.clientWidth;
+}
+
+const kNotScrolled = 0;
+const kScrolledToTop = 1;
+const kScrolledToBottom = 2;
+const kScrolledToLeft = 4;
+const kScrolledToRight = 8;
+
+const kScrolledVertical = kScrolledToTop | kScrolledToBottom;
+const kScrolledHorizontal = kScrolledToLeft | kScrolledToRight;
+
+function _getScrolledState(aElement)
+{
+ var ret = kNotScrolled;
+ if (aElement.scrollTop != aElement.prevTop) {
+ ret |= aElement.scrollTop < aElement.prevTop ? kScrolledToTop :
+ kScrolledToBottom;
+ }
+ if (aElement.scrollLeft != aElement.prevLeft) {
+ ret |= aElement.scrollLeft < aElement.prevLeft ? kScrolledToLeft :
+ kScrolledToRight;
+ }
+ return ret;
+}
+
+function _getExpectedScrolledState()
+{
+ return gCurrentTest.isVertical ?
+ gCurrentTest.isForward ? kScrolledToBottom : kScrolledToTop :
+ gCurrentTest.isForward ? kScrolledToRight : kScrolledToLeft;
+}
+
+function _getScrolledStateText(aScrolledState)
+{
+ if (aScrolledState == kNotScrolled)
+ return "Not scrolled";
+
+ var s = "scrolled to ";
+ if (aScrolledState & kScrolledVertical) {
+ s += aScrolledState & kScrolledToTop ? "backward" : "forward";
+ s += " (vertical)"
+ if (aScrolledState & kScrolledHorizontal)
+ s += " and to ";
+ }
+ if (aScrolledState & kScrolledHorizontal) {
+ s += aScrolledState & kScrolledToLeft ? "backward" : "forward";
+ s += " (horizontal)"
+ }
+ return s;
+}
+
+function _getCurrentTestList()
+{
+ return gTestLists[gCurrentTestListStatus.nextListIndex - 1];
+}
+
+function _clearTimer()
+{
+ clearTimeout(gTimer);
+ gTimer = 0;
+}
+
+/******************************************************************************
+ * Testing functions
+ ******************************************************************************/
+
+/**
+ * Note that testing functions must set following variables:
+ *
+ * gCurrentTest.repeatTest: See comment in |continueTest|.
+ * gCurrentTest.autoRepeatDelay: See comment in |continueTest|.
+ * gListenScrollEvent: When this is not true, the event handlers ignores the
+ * events.
+ */
+
+function testContinuousScroll()
+{
+ /**
+ * Testing continuous scrolling. This function synthesizes a wheel event. If
+ * the test was success, this function will be recalled automatically.
+ * And when a generating wheel event cannot scroll the expected view, this
+ * function fires the wheel event only one time.
+ *
+ * @param gCurrentTest.offset
+ * The cursor position of firing wheel event. The values are offset
+ * from |gRootView|.
+ * @param gCurrentTest.isVertical
+ * Whether the wheel event is for virtical scrolling or horizontal.
+ * @param gCurrentTest.isForward
+ * Whether the wheel event is to forward or to backward.
+ * @param gCurrentTest.expectedView
+ * The expected view which will be scrolled by wheel event. This
+ * value must not be null.
+ */
+
+ _clearTimer();
+ _saveScrollPositions();
+ if (!gCurrentTest.expectedView) {
+ runNextTestStep();
+ return;
+ }
+
+ gLitesnEvents = kListenEvent_All;
+ gCurrentTest.repeatTest = true;
+ gCurrentTest.autoRepeatDelay = 0;
+
+ if (!_canScroll(gCurrentTest.expectedView,
+ gCurrentTest.isVertical, gCurrentTest.isForward)) {
+ gCurrentTest.expectedView = null;
+ }
+ var delta = gCurrentTest.delta ? gCurrentTest.delta : 4;
+ _fireWheelScrollEvent(gCurrentTest.offset,
+ gCurrentTest.isVertical, gCurrentTest.isForward, delta);
+}
+
+function testOneTimeScroll()
+{
+ /**
+ * Testing one wheel event. |runNextTestStep| will be called immediately
+ * after this function by |onScrollView| or |onTimeout|.
+ *
+ * @param gCurrentTest.offset
+ * The cursor position of firing wheel event. The values are offset
+ * from |gRootView|.
+ * @param gCurrentTest.isVertical
+ * Whether the wheel event is for virtical scrolling or horizontal.
+ * @param gCurrentTest.isForward
+ * Whether the wheel event is to forward or to backward.
+ * @param gCurrentTest.expectedView
+ * The expected view which will be scrolled by wheel event. This
+ * value can be null. It means any views should not be scrolled.
+ */
+
+ _clearTimer();
+ _saveScrollPositions();
+
+ gLitesnEvents = kListenEvent_All;
+ gCurrentTest.repeatTest = false;
+ gCurrentTest.autoRepeatDelay = 0;
+
+ var delta = gCurrentTest.delta ? gCurrentTest.delta : 4;
+ _fireWheelScrollEvent(gCurrentTest.offset,
+ gCurrentTest.isVertical, gCurrentTest.isForward, delta);
+}
+
+function testRestartScroll()
+{
+ /**
+ * Testing restart to scroll in expected view after timeout from the current
+ * transaction. This function recall this itself until to success this test
+ * or timeout from this test.
+ *
+ * @param gCurrentTest.offset
+ * The cursor position of firing wheel event. The values are offset
+ * from |gRootView|.
+ * @param gCurrentTest.isVertical
+ * Whether the wheel event is for virtical scrolling or horizontal.
+ * @param gCurrentTest.isForward
+ * Whether the wheel event is to forward or to backward.
+ * @param gCurrentTest.expectedView
+ * The expected view which will be scrolled by wheel event. This
+ * value must not be null.
+ */
+
+ _clearTimer();
+ _saveScrollPositions();
+
+ if (!gCurrentTest.wasTransactionTimeout) {
+ gCurrentTest.repeatTest = true;
+ gCurrentTest.autoRepeatDelay = gTimeout / 3;
+ gLitesnEvents = kListenEvent_All;
+ gCurrentTest.isTimeoutTesting = true;
+ if (gCurrentTest.expectedView) {
+ gCurrentTest.expectedViewAfterTimeout = gCurrentTest.expectedView;
+ gCurrentTest.expectedView = null;
+ }
+ } else {
+ gCurrentTest.repeatTest = false;
+ gCurrentTest.autoRepeatDelay = 0;
+ gLitesnEvents = kListenEvent_All;
+ gCurrentTest.isTimeoutTesting = false;
+ gCurrentTest.expectedView = gCurrentTest.expectedViewAfterTimeout;
+ }
+
+ var delta = gCurrentTest.delta ? gCurrentTest.delta : 4;
+ _fireWheelScrollEvent(gCurrentTest.offset,
+ gCurrentTest.isVertical, gCurrentTest.isForward, delta);
+}
+
+/******************************************************************************
+ * Event handlers
+ ******************************************************************************/
+
+function onScrollView(aEvent)
+{
+ /**
+ * Scroll event handler of |gRootView|, |gSubView1|, |gSubView2| and
+ * |gSubView3|. If testing is failed, this function cancels all left tests.
+ * For checking the event is expected, the event firer must call
+ * |_saveScrollPositions|.
+ *
+ * @param gCurrentTest.expectedView
+ * The expected view which should be scrolled by the wheel event.
+ * This value can be null. It means any views should not be
+ * scrolled.
+ * @param gCurrentTest.isVertical
+ * The expected view should be scrolled vertical or horizontal.
+ * @param gCurrentTest.isForward
+ * The expected view should be scrolled to forward or backward.
+ * @param gCurrentTest.canFailRandomly
+ * If this is not undefined, this test can fail by unexpected view
+ * scrolling which is caused by unexpected timeout. If this is
+ * defined, |gCurrentTest.possibleView| must be set. If the view is
+ * same as the event target, the failure can be random. At this
+ * time, we should retry the current test list.
+ */
+
+ if (!(gLitesnEvents & kListenEvent_OnScroll))
+ return;
+
+ // Now testing a timeout, but a view is scrolled before timeout.
+ if (gCurrentTest.isTimeoutTesting && !gCurrentTest.wasTransactionTimeout) {
+ is(aEvent.target.id, "",
+ "The view scrolled before timeout (the expected view after timeout is " +
+ gCurrentTest.expectedView ? gCurrentTest.expectedView.id : "null" +
+ "): " + gCurrentTest.description);
+ runNextTestList();
+ return;
+ }
+
+ // Check whether the scrolled event should be fired or not.
+ if (!gCurrentTest.expectedView) {
+ is(aEvent.target.id, "",
+ "no views should be scrolled (" +
+ _getScrolledStateText(_getScrolledState(aEvent.target)) + "): " +
+ gCurrentTest.description);
+ runNextTestList();
+ return;
+ }
+
+ // Check whether the scrolled view is expected or not.
+ if (aEvent.target != gCurrentTest.expectedView) {
+ // If current test can fail randomly and the possible view is same as the
+ // event target, this failure may be caused by unexpected timeout.
+ // At this time, we should retry the current tests with slower settings.
+ if (gCurrentTest.canFailRandomly &&
+ gCurrentTest.canFailRandomly.possibleView == aEvent.target &&
+ gCurrentTestListStatus.retryWhenTransactionTimeout > 0) {
+ gCurrentTestListStatus.retryWhenTransactionTimeout--;
+ retryCurrentTestList();
+ return;
+ }
+ is(aEvent.target.id, gCurrentTest.expectedView.id,
+ "wrong view was scrolled: " + gCurrentTest.description);
+ runNextTestList();
+ return;
+ }
+
+ // Check whether the scrolling direction is expected or not.
+ var expectedState = _getExpectedScrolledState();
+ var currentState = _getScrolledState(aEvent.target);
+ if (expectedState != currentState) {
+ is(_getScrolledStateText(currentState),
+ _getScrolledStateText(expectedState),
+ "scrolled to wrong direction: " + gCurrentTest.description);
+ runNextTestList();
+ return;
+ }
+
+ ok(true, "passed: " + gCurrentTest.description);
+ continueTest();
+}
+
+function onMouseScrollFailed()
+{
+ /**
+ * Scroll failed event handler. If testing is failed, this function cancels
+ * all remains of current test-list, and go to next test-list.
+ *
+ * NOTE: This event is fired immediately after |_fireWheelScrollEvent|.
+ *
+ * @param gCurrentTest.expectedView
+ * The expected view which should be scrolled by the wheel event.
+ * This value can be null. It means any views should not be
+ * scrolled. When this is not null, this event means the test may
+ * be failed.
+ */
+
+ if (!(gLitesnEvents & kListenEvent_OnScrollFailed))
+ return;
+
+ ok(!gCurrentTest.expectedView,
+ "failed to scroll on current target: " + gCurrentTest.description);
+ if (gCurrentTest.expectedView) {
+ runNextTestList();
+ return;
+ }
+
+ continueTest();
+}
+
+function onTransactionTimeout()
+{
+ /**
+ * Scroll transaction timeout event handler. If the timeout is unexpected,
+ * i.e., |gCurrentTest.isTimeoutTesting| is not true, this function retry
+ * the current test-list. However, if the current test-list failed by timeout
+ * |gCurrentTestListStatus.retryWhenTransactionTimeout| times already, marking
+ * to failed the current test-list, and go to next test-list.
+ *
+ * @param gCurrentTest.expectedView
+ * The expected view which should be scrolled by the wheel event.
+ * This value can be null. It means any views should not be
+ * scrolled. When this is not null, this event means the testing may
+ * be failed.
+ * @param gCurrentTest.isTimeoutTesting
+ * If this value is true, the current testing have waited this
+ * event. Otherwise, the testing may be failed.
+ * @param gCurrentTestListStatus.retryWhenTransactionTimeout
+ * If |gCurrentTest.isTimeoutTesting| is not true but this event is
+ * fired, the failure may be randomly. Then, this event handler
+ * retry to test the current test-list until this cound will be zero.
+ */
+
+ if (!gCurrentTest.isTimeoutTesting &&
+ gCurrentTestListStatus.retryWhenTransactionTimeout > 0) {
+ gCurrentTestListStatus.retryWhenTransactionTimeout--;
+ // retry current test list
+ retryCurrentTestList();
+ return;
+ }
+
+ gCurrentTest.wasTransactionTimeout = true;
+
+ if (!(gLitesnEvents & kListenEvent_OnTransactionTimeout))
+ return;
+
+ ok(gCurrentTest.isTimeoutTesting,
+ "transaction timeout: " + gCurrentTest.description);
+ if (!gCurrentTest.isTimeoutTesting) {
+ runNextTestList();
+ return;
+ }
+
+ continueTest();
+}
+
+/******************************************************************************
+ * Main function for this tests
+ ******************************************************************************/
+
+function runNextTestStep()
+{
+ // When this is first time or the current test list is finised, load next
+ // test-list.
+ _clearTimer();
+ if (!gCurrentTest)
+ runNextTestList();
+ else
+ runTestStepAt(gCurrentTestListStatus.nextStepIndex);
+}
+
+function runNextTestList()
+{
+ _clearTimer();
+
+ gLitesnEvents = kListenEvent_None;
+ _clearTransaction();
+ resetTimeoutPrefs();
+ if (gCurrentTestListStatus.nextListIndex >= gTestLists.length) {
+ finish();
+ return;
+ }
+
+ gCurrentTestListStatus.nextListIndex++;
+ gCurrentTestListStatus.retryWhenTransactionTimeout =
+ _getCurrentTestList().retryWhenTransactionTimeout;
+ runTestStepAt(0);
+}
+
+function runTestStepAt(aStepIndex)
+{
+ _clearTimer();
+
+ disableNonTestMouseEvents(true);
+
+ // load a step of testing.
+ gCurrentTestListStatus.nextStepIndex = aStepIndex;
+ gCurrentTest =
+ _getCurrentTestList().steps[gCurrentTestListStatus.nextStepIndex++];
+ if (gCurrentTest) {
+ gCurrentTest.wasTransactionTimeout = false;
+ gTimer = setTimeout(gCurrentTest.func, gCurrentTest.delay);
+ } else {
+ // If current test-list doesn't have more testing, go to next test-list
+ // after cleaning up the current transaction.
+ _clearTransaction();
+ runNextTestList();
+ }
+}
+
+function retryCurrentTestList()
+{
+ _clearTimer();
+
+ gLitesnEvents = kListenEvent_None;
+ _clearTransaction();
+ ok(true, "WARNING: retry current test-list...");
+ growUpTimeoutPrefs(); // retry the test with longer timeout settings.
+ runTestStepAt(0);
+}
+
+function continueTest()
+{
+ /**
+ * This function is called from an event handler when a test succeeded.
+ *
+ * @param gCurrentTest.repeatTest
+ * When this is true, onScrollView calls |gCurrentTest.func|. So,
+ * same test can repeat. Otherwise, this calls |runNextTestStep|.
+ * @param gCurrentTest.autoRepeatDelay
+ * The delay value in milliseconds, this is used to call
+ * |gCurrentTest.func| via |setTimeout|.
+ */
+
+ _clearTimer();
+ gLitesnEvents = kListenEvent_OnTransactionTimeout;
+
+ // We should call each functions via setTimeout. Because sometimes this test
+ // is broken by stack overflow.
+ if (gCurrentTest.repeatTest) {
+ gTimer = setTimeout(gCurrentTest.func, gCurrentTest.autoRepeatDelay);
+ } else {
+ gTimer = setTimeout(runNextTestStep, 0);
+ }
+}
+
+]]>
+</script>
+
+</window>