Bug 691583 - Dispatch showfullscreenwarning chrome event when restricted key pressed in DOM full-screen mode. r=smaug

This commit is contained in:
Chris Pearce 2011-11-01 18:11:40 +13:00
parent ec9520006e
commit 861af8c44e
5 changed files with 339 additions and 168 deletions

View File

@ -21,147 +21,10 @@ Test that restricted key pressed drop documents out of DOM full-screen mode.
/** Test for Bug 545812 **/
// List of key codes, and whether they're restricted in full-screen mode.
// List of key codes which should exit full-screen mode.
var keyList = [
// Allowed: DOM_VK_CANCEL to DOM_VK_CAPS_LOCK, inclusive
{ code: "VK_CANCEL", allowed: true},
{ code: "VK_HELP", allowed: true},
{ code: "VK_BACK_SPACE", allowed: true},
{ code: "VK_TAB", allowed: true},
{ code: "VK_CLEAR", allowed: true},
{ code: "VK_RETURN", allowed: true},
{ code: "VK_ENTER", allowed: true},
{ code: "VK_SHIFT", allowed: true},
{ code: "VK_CONTROL", allowed: true},
{ code: "VK_ALT", allowed: true},
{ code: "VK_PAUSE", allowed: true},
{ code: "VK_CAPS_LOCK", allowed: true},
{ code: "VK_KANA", allowed: false},
{ code: "VK_HANGUL", allowed: false},
{ code: "VK_JUNJA", allowed: false},
{ code: "VK_FINAL", allowed: false},
{ code: "VK_HANJA", allowed: false},
{ code: "VK_KANJI", allowed: false},
{ code: "VK_ESCAPE", allowed: false, suppressed: true},
{ code: "VK_CONVERT", allowed: false},
{ code: "VK_NONCONVERT", allowed: false},
{ code: "VK_ACCEPT", allowed: false},
{ code: "VK_MODECHANGE", allowed: false},
// Allowed: DOM_VK_SPACE to DOM_VK_DELETE, inclusive
{ code: "VK_SPACE", allowed: true},
{ code: "VK_PAGE_UP", allowed: true},
{ code: "VK_PAGE_DOWN", allowed: true},
{ code: "VK_END", allowed: true},
{ code: "VK_HOME", allowed: true},
{ code: "VK_LEFT", allowed: true},
{ code: "VK_UP", allowed: true},
{ code: "VK_RIGHT", allowed: true},
{ code: "VK_DOWN", allowed: true},
{ code: "VK_SELECT", allowed: true},
{ code: "VK_PRINT", allowed: true},
{ code: "VK_EXECUTE", allowed: true},
{ code: "VK_PRINTSCREEN", allowed: true},
{ code: "VK_INSERT", allowed: true},
{ code: "VK_DELETE", allowed: true},
{ code: "VK_0", allowed: false},
{ code: "VK_1", allowed: false},
{ code: "VK_2", allowed: false},
{ code: "VK_3", allowed: false},
{ code: "VK_4", allowed: false},
{ code: "VK_5", allowed: false},
{ code: "VK_6", allowed: false},
{ code: "VK_7", allowed: false},
{ code: "VK_8", allowed: false},
{ code: "VK_9", allowed: false},
// Allowed: DOM_VK_SPACE to DOM_VK_DELETE, inclusive
{ code: "VK_SEMICOLON", allowed: true},
{ code: "VK_EQUALS", allowed: true},
{ code: "VK_A", allowed: false},
{ code: "VK_B", allowed: false},
{ code: "VK_C", allowed: false},
{ code: "VK_D", allowed: false},
{ code: "VK_E", allowed: false},
{ code: "VK_F", allowed: false},
{ code: "VK_G", allowed: false},
{ code: "VK_H", allowed: false},
{ code: "VK_I", allowed: false},
{ code: "VK_J", allowed: false},
{ code: "VK_K", allowed: false},
{ code: "VK_L", allowed: false},
{ code: "VK_M", allowed: false},
{ code: "VK_N", allowed: false},
{ code: "VK_O", allowed: false},
{ code: "VK_P", allowed: false},
{ code: "VK_Q", allowed: false},
{ code: "VK_R", allowed: false},
{ code: "VK_S", allowed: false},
{ code: "VK_T", allowed: false},
{ code: "VK_U", allowed: false},
{ code: "VK_V", allowed: false},
{ code: "VK_W", allowed: false},
{ code: "VK_X", allowed: false},
{ code: "VK_Y", allowed: false},
{ code: "VK_Z", allowed: false},
{ code: "VK_CONTEXT_MENU", allowed: false},
{ code: "VK_SLEEP", allowed: false},
{ code: "VK_NUMPAD0", allowed: false},
{ code: "VK_NUMPAD1", allowed: false},
{ code: "VK_NUMPAD2", allowed: false},
{ code: "VK_NUMPAD3", allowed: false},
{ code: "VK_NUMPAD4", allowed: false},
{ code: "VK_NUMPAD5", allowed: false},
{ code: "VK_NUMPAD6", allowed: false},
{ code: "VK_NUMPAD7", allowed: false},
{ code: "VK_NUMPAD8", allowed: false},
{ code: "VK_NUMPAD9", allowed: false},
// Allowed: DOM_VK_MULTIPLY to DOM_VK_META, inclusive
{ code: "VK_MULTIPLY", allowed: true},
{ code: "VK_ADD", allowed: true},
{ code: "VK_SEPARATOR", allowed: true},
{ code: "VK_SUBTRACT", allowed: true},
{ code: "VK_DECIMAL", allowed: true},
{ code: "VK_DIVIDE", allowed: true},
{ code: "VK_F1", allowed: true},
{ code: "VK_F2", allowed: true},
{ code: "VK_F3", allowed: true},
{ code: "VK_F4", allowed: true},
{ code: "VK_F5", allowed: true},
{ code: "VK_F6", allowed: true},
{ code: "VK_F7", allowed: true},
{ code: "VK_F8", allowed: true},
{ code: "VK_F9", allowed: true},
{ code: "VK_F10", allowed: true},
{ code: "VK_F11", allowed: true},
{ code: "VK_F12", allowed: true},
{ code: "VK_F13", allowed: true},
{ code: "VK_F14", allowed: true},
{ code: "VK_F15", allowed: true},
{ code: "VK_F16", allowed: true},
{ code: "VK_F17", allowed: true},
{ code: "VK_F18", allowed: true},
{ code: "VK_F19", allowed: true},
{ code: "VK_F20", allowed: true},
{ code: "VK_F21", allowed: true},
{ code: "VK_F22", allowed: true},
{ code: "VK_F23", allowed: true},
{ code: "VK_F24", allowed: true},
{ code: "VK_NUM_LOCK", allowed: true},
{ code: "VK_SCROLL_LOCK", allowed: true},
{ code: "VK_COMMA", allowed: true},
{ code: "VK_PERIOD", allowed: true},
{ code: "VK_SLASH", allowed: true},
{ code: "VK_BACK_QUOTE", allowed: true},
{ code: "VK_OPEN_BRACKET", allowed: true},
{ code: "VK_BACK_SLASH", allowed: true},
{ code: "VK_CLOSE_BRACKET", allowed: true},
{ code: "VK_QUOTE", allowed: true},
{ code: "VK_META", allowed: true},
{ code: "VK_ESCAPE", suppressed: true},
{ code: "VK_F11", suppressed: false},
];
function ok(condition, msg) {
@ -175,21 +38,16 @@ function is(a, b, msg) {
var gKeyTestIndex = 0;
var gKeyName;
var gKeyCode;
var gKeyAllowed;
var gKeySuppressed;
var gKeyReceived = false;
function keyHandler(event) {
event.preventDefault();
gKeyReceived = true;
}
function checkKeyEffect() {
is(gKeySuppressed, !gKeyReceived, "Should not receive suppressed key events");
is(document.mozFullScreen, gKeyAllowed,
(gKeyAllowed ? ("Should remain in full-screen mode for allowed key press " + gKeyName)
: ("Should drop out of full-screen mode for restricted key press " + gKeyName)));
is(gKeySuppressed, !gKeyReceived, "Should not receive key events for " + gKeyName);
is(document.mozFullScreen, false, "Should exit full-screen mode for " + gKeyName + " press");
if (gKeyTestIndex < keyList.length) {
setTimeout(testNextKey, 0);
} else {
@ -206,7 +64,7 @@ function testTrustedKeyEvents() {
}
function testScriptInitiatedKeyEvents() {
// Script initiated untrusted key events should not be blocked.
// Script initiated untrusted key events should not cause full-screen exit.
document.body.focus();
gKeyReceived = false;
var evt = document.createEvent("KeyEvents");
@ -240,9 +98,7 @@ function testNextKey() {
gKeyName = keyList[gKeyTestIndex].code;
gKeyCode = KeyEvent["DOM_" + gKeyName];
gKeyAllowed = keyList[gKeyTestIndex].allowed;
gKeySuppressed = (keyList[gKeyTestIndex].suppressed != undefined) ?
keyList[gKeyTestIndex].suppressed : false;
gKeySuppressed = keyList[gKeyTestIndex].suppressed;
gKeyTestIndex++;
testScriptInitiatedKeyEvents();

View File

@ -45,6 +45,8 @@ include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_TEST_FILES = \
test_dom_fullscreen_warning.xul \
dom_fullscreen_warning.xul \
test_fullscreen.xul \
fullscreen.xul \
test_fullscreen_preventdefault.xul \

View File

@ -0,0 +1,267 @@
<?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"?>
<!--
Test that "MozShowFullScreenWarning" is dispatched to chrome on restricted keypress.
-->
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="start();">
<script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript"><![CDATA[
// List of key codes, and whether they should cause a warning in full-screen mode.
var keyList = [
// Allowed: DOM_VK_CANCEL to DOM_VK_CAPS_LOCK, inclusive
{ code: "VK_CANCEL", warn: false},
{ code: "VK_HELP", warn: false},
{ code: "VK_BACK_SPACE", warn: false},
{ code: "VK_TAB", warn: false},
{ code: "VK_CLEAR", warn: false},
{ code: "VK_RETURN", warn: false},
{ code: "VK_ENTER", warn: false},
{ code: "VK_SHIFT", warn: false},
{ code: "VK_CONTROL", warn: false},
{ code: "VK_ALT", warn: false},
{ code: "VK_PAUSE", warn: false},
{ code: "VK_CAPS_LOCK", warn: false},
{ code: "VK_KANA", warn: true},
{ code: "VK_HANGUL", warn: true},
{ code: "VK_JUNJA", warn: true},
{ code: "VK_FINAL", warn: true},
{ code: "VK_HANJA", warn: true},
{ code: "VK_KANJI", warn: true},
{ code: "VK_ESCAPE", warn: false, exit: true},
{ code: "VK_CONVERT", warn: true},
{ code: "VK_NONCONVERT", warn: true},
{ code: "VK_ACCEPT", warn: true},
{ code: "VK_MODECHANGE", warn: true},
// Allowed: DOM_VK_SPACE to DOM_VK_DELETE, inclusive
{ code: "VK_SPACE", warn: false},
{ code: "VK_PAGE_UP", warn: false},
{ code: "VK_PAGE_DOWN", warn: false},
{ code: "VK_END", warn: false},
{ code: "VK_HOME", warn: false},
{ code: "VK_LEFT", warn: false},
{ code: "VK_UP", warn: false},
{ code: "VK_RIGHT", warn: false},
{ code: "VK_DOWN", warn: false},
{ code: "VK_SELECT", warn: false},
{ code: "VK_PRINT", warn: false},
{ code: "VK_EXECUTE", warn: false},
{ code: "VK_PRINTSCREEN", warn: false},
{ code: "VK_INSERT", warn: false},
{ code: "VK_DELETE", warn: false},
{ code: "VK_0", warn: true},
{ code: "VK_1", warn: true},
{ code: "VK_2", warn: true},
{ code: "VK_3", warn: true},
{ code: "VK_4", warn: true},
{ code: "VK_5", warn: true},
{ code: "VK_6", warn: true},
{ code: "VK_7", warn: true},
{ code: "VK_8", warn: true},
{ code: "VK_9", warn: true},
// Allowed: DOM_VK_SPACE to DOM_VK_DELETE, inclusive
{ code: "VK_SEMICOLON", warn: false},
{ code: "VK_EQUALS", warn: false},
{ code: "VK_A", warn: true},
{ code: "VK_B", warn: true},
{ code: "VK_C", warn: true},
{ code: "VK_D", warn: true},
{ code: "VK_E", warn: true},
{ code: "VK_F", warn: true},
{ code: "VK_G", warn: true},
{ code: "VK_H", warn: true},
{ code: "VK_I", warn: true},
{ code: "VK_J", warn: true},
{ code: "VK_K", warn: true},
{ code: "VK_L", warn: true},
{ code: "VK_M", warn: true},
{ code: "VK_N", warn: true},
{ code: "VK_O", warn: true},
{ code: "VK_P", warn: true},
{ code: "VK_Q", warn: true},
{ code: "VK_R", warn: true},
{ code: "VK_S", warn: true},
{ code: "VK_T", warn: true},
{ code: "VK_U", warn: true},
{ code: "VK_V", warn: true},
{ code: "VK_W", warn: true},
{ code: "VK_X", warn: true},
{ code: "VK_Y", warn: true},
{ code: "VK_Z", warn: true},
{ code: "VK_CONTEXT_MENU", warn: true},
{ code: "VK_SLEEP", warn: true},
{ code: "VK_NUMPAD0", warn: true},
{ code: "VK_NUMPAD1", warn: true},
{ code: "VK_NUMPAD2", warn: true},
{ code: "VK_NUMPAD3", warn: true},
{ code: "VK_NUMPAD4", warn: true},
{ code: "VK_NUMPAD5", warn: true},
{ code: "VK_NUMPAD6", warn: true},
{ code: "VK_NUMPAD7", warn: true},
{ code: "VK_NUMPAD8", warn: true},
{ code: "VK_NUMPAD9", warn: true},
// Allowed: DOM_VK_MULTIPLY to DOM_VK_META, inclusive
{ code: "VK_MULTIPLY", warn: false},
{ code: "VK_ADD", warn: false},
{ code: "VK_SEPARATOR", warn: false},
{ code: "VK_SUBTRACT", warn: false},
{ code: "VK_DECIMAL", warn: false},
{ code: "VK_DIVIDE", warn: false},
{ code: "VK_F1", warn: false},
{ code: "VK_F2", warn: false},
{ code: "VK_F3", warn: false},
{ code: "VK_F4", warn: false},
{ code: "VK_F5", warn: false},
{ code: "VK_F6", warn: false},
{ code: "VK_F7", warn: false},
{ code: "VK_F8", warn: false},
{ code: "VK_F9", warn: false},
{ code: "VK_F10", warn: false},
{ code: "VK_F11", warn: false}, // F11 exit full-screen handler is in browser.js, so won't cause exit here.
{ code: "VK_F12", warn: false},
{ code: "VK_F13", warn: false},
{ code: "VK_F14", warn: false},
{ code: "VK_F15", warn: false},
{ code: "VK_F16", warn: false},
{ code: "VK_F17", warn: false},
{ code: "VK_F18", warn: false},
{ code: "VK_F19", warn: false},
{ code: "VK_F20", warn: false},
{ code: "VK_F21", warn: false},
{ code: "VK_F22", warn: false},
{ code: "VK_F23", warn: false},
{ code: "VK_F24", warn: false},
{ code: "VK_NUM_LOCK", warn: false},
{ code: "VK_SCROLL_LOCK", warn: false},
{ code: "VK_COMMA", warn: false},
{ code: "VK_PERIOD", warn: false},
{ code: "VK_SLASH", warn: false},
{ code: "VK_BACK_QUOTE", warn: false},
{ code: "VK_OPEN_BRACKET", warn: false},
{ code: "VK_BACK_SLASH", warn: false},
{ code: "VK_CLOSE_BRACKET", warn: false},
{ code: "VK_QUOTE", warn: false},
{ code: "VK_META", warn: false},
];
function ok(condition, msg) {
window.opener.wrappedJSObject.ok(condition, msg);
}
function is(a, b, msg) {
window.opener.wrappedJSObject.is(a, b, msg);
}
var gKeyTestIndex = 0;
var gKeyName;
var gKeyCode;
var gWarningEventReceived = false;
var gExpectWarning;
var gExpectExit;
var gBrowser;
function checkKeyEffect() {
is(gBrowser.contentDocument.mozFullScreen, !gExpectExit,
(gExpectExit ? ("Should exit full-screen for " + gKeyName + " press ")
: ("Should remain in full-screen for " + gKeyName + " press")));
is(gWarningEventReceived, gExpectWarning, "Should " + (gExpectWarning ? "" : "not ") +
"receive MozShowFullScreenWarning for " + gKeyName + " press");
if (gKeyTestIndex < keyList.length) {
setTimeout(startNextTest, 0);
} else {
gBrowser.contentDocument.mozCancelFullScreen();
window.opener.wrappedJSObject.done();
}
}
function testTrustedKeyEvents() {
gBrowser.contentWindow.focus();
gWarningEventReceived = false;
synthesizeKey(gKeyName, {});
setTimeout(checkKeyEffect, 0);
}
function testScriptInitiatedKeyEvents() {
// Script initiated untrusted key events should not cause the warning to show.
gBrowser.contentWindow.focus();
gWarningEventReceived = false;
var evt = gBrowser.contentDocument.createEvent("KeyEvents");
evt.initKeyEvent("keydown", true, true, window,
false, false, false, false,
gKeyCode, 0);
gBrowser.contentDocument.body.dispatchEvent(evt);
evt = gBrowser.contentDocument.createEvent("KeyEvents");
evt.initKeyEvent("keypress", true, true, window,
false, false, false, false,
gKeyCode, 0);
gBrowser.contentDocument.body.dispatchEvent(evt);
evt = gBrowser.contentDocument.createEvent("KeyEvents");
evt.initKeyEvent("keyup", true, true, window,
false, false, false, false,
gKeyCode, 0);
gBrowser.contentDocument.body.dispatchEvent(evt);
setTimeout(checkScriptInitiatedEvents, 0);
}
function checkScriptInitiatedEvents() {
is(gWarningEventReceived, false, "Should not receive MozShowFullScreenWarning on synthesized key events.");
ok(gBrowser.contentDocument.mozFullScreen,
"Should remain in full-screen mode for script initiated key events for " + gKeyName);
testTrustedKeyEvents();
}
function testNextKey() {
ok(gBrowser.contentDocument.mozFullScreen, "Must be in full-screen mode");
gKeyName = keyList[gKeyTestIndex].code;
gKeyCode = KeyEvent["DOM_" + gKeyName];
gExpectWarning = keyList[gKeyTestIndex].warn;
gExpectExit = (keyList[gKeyTestIndex].exit != undefined) ?
keyList[gKeyTestIndex].exit : false;
gKeyTestIndex++;
testScriptInitiatedKeyEvents();
}
function startNextTest() {
if (!gBrowser.contentDocument.mozFullScreen) {
gBrowser.contentDocument.body.mozRequestFullScreen();
}
// mozRequestFullScreen is async...
setTimeout(testNextKey, 0);
}
function keyHandler(event) {
event.preventDefault();
}
window.addEventListener("MozShowFullScreenWarning", function(){ gWarningEventReceived = true; }, true);
window.addEventListener("keydown", keyHandler, true);
window.addEventListener("keyup", keyHandler, true);
window.addEventListener("keypress", keyHandler, true);
function start() {
gBrowser = document.getElementById("browser");
gBrowser.contentDocument.body.mozRequestFullScreen();
setTimeout(startNextTest, 0);
}
]]>
</script>
<browser type="content" id="browser" width="400" height="400"/>
</window>

View File

@ -0,0 +1,37 @@
<?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"?>
<!--
Test that "MozShowFullScreenWarning" is dispatched to chrome on restricted keypress.
-->
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" width="400" height="400">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script>
SimpleTest.waitForExplicitFinish();
// Ensure the full-screen api is enabled, and will be disabled on test exit.
var gPrevEnabled = SpecialPowers.getBoolPref("full-screen-api.enabled");
SpecialPowers.setBoolPref("full-screen-api.enabled", true);
var gPrevTrusted = SpecialPowers.getBoolPref("full-screen-api.allow-trusted-requests-only");
SpecialPowers.setBoolPref("full-screen-api.allow-trusted-requests-only", false);
newwindow = window.open("dom_fullscreen_warning.xul", "_blank","chrome,resizable=yes,width=400,height=400");
function done()
{
newwindow.close();
SpecialPowers.setBoolPref("full-screen-api.enabled", gPrevEnabled);
SpecialPowers.setBoolPref("full-screen-api.allow-trusted-requests-only", gPrevTrusted);
SimpleTest.finish();
}
</script>
<body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/>
</window>

View File

@ -202,6 +202,7 @@
#include "mozilla/Telemetry.h"
#include "Layers.h"
#include "nsPLDOMEvent.h"
#ifdef NS_FUNCTION_TIMER
#define NS_TIME_FUNCTION_DECLARE_DOCURL \
@ -6345,25 +6346,33 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsIView *aView,
case NS_KEY_PRESS:
case NS_KEY_DOWN:
case NS_KEY_UP: {
if (IsFullScreenAndRestrictedKeyEvent(mCurrentEventContent, aEvent) &&
aEvent->message == NS_KEY_UP) {
// We're in DOM full-screen mode, and a key with a restricted key
// code has been pressed. Exit full-screen mode.
NS_DispatchToCurrentThread(
NS_NewRunnableMethod(mCurrentEventContent->OwnerDoc(),
&nsIDocument::CancelFullScreen));
}
nsIDocument *doc = mCurrentEventContent ?
mCurrentEventContent->OwnerDoc() : nsnull;
if (doc &&
doc->IsFullScreenDoc() &&
static_cast<const nsKeyEvent*>(aEvent)->keyCode == NS_VK_ESCAPE) {
// Prevent default action on ESC key press when exiting
// DOM full-screen mode. This prevents the browser ESC key
// handler from stopping all loads in the document, which
// would cause <video> loads to stop.
aEvent->flags |= (NS_EVENT_FLAG_NO_DEFAULT |
NS_EVENT_FLAG_ONLY_CHROME_DISPATCH);
if (doc &&
doc->IsFullScreenDoc() &&
static_cast<const nsKeyEvent*>(aEvent)->keyCode == NS_VK_ESCAPE) {
// Prevent default action on ESC key press when exiting
// DOM full-screen mode. This prevents the browser ESC key
// handler from stopping all loads in the document, which
// would cause <video> loads to stop.
aEvent->flags |= (NS_EVENT_FLAG_NO_DEFAULT |
NS_EVENT_FLAG_ONLY_CHROME_DISPATCH);
if (aEvent->message == NS_KEY_UP) {
// ESC key released while in DOM full-screen mode.
// Exit full-screen mode.
NS_DispatchToCurrentThread(
NS_NewRunnableMethod(mCurrentEventContent->OwnerDoc(),
&nsIDocument::CancelFullScreen));
}
} else if (IsFullScreenAndRestrictedKeyEvent(mCurrentEventContent, aEvent)) {
// Restricted key press while in DOM full-screen mode. Dispatch
// an event to chrome so it knows to show a warning message
// informing the user how to exit full-screen.
nsRefPtr<nsPLDOMEvent> e =
new nsPLDOMEvent(doc, NS_LITERAL_STRING("MozShowFullScreenWarning"),
true, true);
e->PostDOMEvent();
}
// Else not full-screen mode or key code is unrestricted, fall
// through to normal handling.