Source code
Revision control
Copy as Markdown
Other Tools
// This test is called from both test_clipboard_editor.html and test_clipboard_noeditor.html
// This is to test that the code works both in the presence of a contentEditable node, and in the absense of one
var WATCH_TIMEOUT = 300;
// Some global variables to make the debug messages easier to track down
var gTestN0 = 0,
gTestN1 = 0,
gTestN2 = 0;
function testLoc() {
return " " + gTestN0 + " - " + gTestN1 + " - " + gTestN2;
}
// Listen for cut & copy events
var gCopyCount = 0,
gCutCount = 0;
document.addEventListener("copy", function () {
gCopyCount++;
});
document.addEventListener("cut", function () {
gCutCount++;
});
// Helper methods
function selectNode(aSelector, aCb) {
var dn = document.querySelector(aSelector);
var range = document.createRange();
range.selectNodeContents(dn);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
if (aCb) {
aCb();
}
}
function selectInputNode(aSelector, aCb) {
var dn = document.querySelector(aSelector);
synthesizeMouse(dn, 10, 10, {});
SimpleTest.executeSoon(function () {
synthesizeKey("A", { accelKey: true });
// Clear the user activation state which is set from synthesized mouse and
// key event.
SpecialPowers.wrap(document).clearUserGestureActivation();
SimpleTest.executeSoon(aCb);
});
}
// Callback functions for attaching to the button
function execCommand(aCommand, aShouldSucceed, aAsync = false) {
var cb = function (e) {
e.preventDefault();
document.removeEventListener("keydown", cb);
if (aAsync) {
setTimeout(() => {
is(
aShouldSucceed,
document.execCommand(aCommand),
"Keydown caused " + aCommand + " invocation" + testLoc()
);
}, 0);
} else {
is(
aShouldSucceed,
document.execCommand(aCommand),
"Keydown caused " + aCommand + " invocation" + testLoc()
);
}
};
return cb;
}
// The basic test set. Tries to cut/copy everything
function cutCopyAll(
aDoCut,
aDoCopy,
aDone,
aNegate,
aClipOverride,
aJustClipboardNegate
) {
var execCommandAlwaysSucceed = !!(aClipOverride || aJustClipboardNegate);
function waitForClipboard(aCond, aSetup, aNext, aNegateOne) {
if (aClipOverride) {
aCond = aClipOverride;
aNegateOne = false;
}
if (aNegate || aNegateOne || aJustClipboardNegate) {
SimpleTest.waitForClipboard(
null,
aSetup,
aNext,
aNext,
"text/plain",
WATCH_TIMEOUT,
true
);
} else {
SimpleTest.waitForClipboard(aCond, aSetup, aNext, aNext);
}
}
function validateCutCopy(aExpectedCut, aExpectedCopy) {
if (aNegate) {
aExpectedCut = aExpectedCopy = 0;
} // When we are negating - we always expect callbacks not to be run
is(
aExpectedCut,
gCutCount,
(aExpectedCut > 0
? "Expect cut callback to run"
: "Expect cut callback not to run") + testLoc()
);
is(
aExpectedCopy,
gCopyCount,
(aExpectedCopy > 0
? "Expect copy callback to run"
: "Expect copy callback not to run") + testLoc()
);
gCutCount = gCopyCount = 0;
}
function step(n) {
function nextStep() {
step(n + 1);
}
// Reset the user activation state before running next test.
SpecialPowers.wrap(document).clearUserGestureActivation();
document.querySelector("span").textContent = "span text";
document.querySelector("input[type=text]").value = "text text";
document.querySelector("input[type=password]").value = "password text";
document.querySelector("textarea").value = "textarea text";
var contentEditableNode = document.querySelector(
"div[contentEditable=true]"
);
if (contentEditableNode) {
contentEditableNode.textContent = "contenteditable text";
}
gTestN2 = n;
switch (n) {
case 0:
// copy on readonly selection
selectNode("span");
waitForClipboard(
"span text",
function () {
aDoCopy(true);
},
nextStep
);
return;
case 1:
validateCutCopy(0, 1);
// cut on readonly selection
selectNode("span");
waitForClipboard(
"span text",
function () {
aDoCut(execCommandAlwaysSucceed);
},
nextStep,
true
);
return;
case 2:
validateCutCopy(1, 0);
// copy on textbox selection
selectInputNode("input[type=text]", nextStep);
return;
case 3:
waitForClipboard(
"text text",
function () {
selectInputNode("input[type=text]", function () {
aDoCopy(true);
});
},
nextStep
);
return;
case 4:
validateCutCopy(0, 1);
// cut on textbox selection
selectInputNode("input[type=text]", nextStep);
return;
case 5:
waitForClipboard(
"text text",
function () {
aDoCut(true);
},
nextStep
);
return;
case 6:
validateCutCopy(1, 0);
// copy on password selection
selectInputNode("input[type=password]", nextStep);
return;
case 7:
waitForClipboard(
null,
function () {
aDoCopy(execCommandAlwaysSucceed);
},
nextStep,
true
);
return;
case 8:
validateCutCopy(0, 1);
// cut on password selection
selectInputNode("input[type=password]", nextStep);
return;
case 9:
waitForClipboard(
null,
function () {
aDoCut(execCommandAlwaysSucceed);
},
nextStep,
true
);
return;
case 10:
validateCutCopy(1, 0);
// copy on textarea selection
selectInputNode("textarea", nextStep);
return;
case 11:
waitForClipboard(
"textarea text",
function () {
aDoCopy(true);
},
nextStep
);
return;
case 12:
validateCutCopy(0, 1);
// cut on password selection
selectInputNode("textarea", nextStep);
return;
case 13:
waitForClipboard(
"textarea text",
function () {
aDoCut(true);
},
nextStep
);
return;
case 14:
validateCutCopy(1, 0);
// copy on no selection
document.querySelector("textarea").blur();
waitForClipboard(
null,
function () {
aDoCopy(true);
},
nextStep,
true
);
return;
case 15:
validateCutCopy(0, 1);
// cut on no selection
waitForClipboard(
null,
function () {
aDoCut(execCommandAlwaysSucceed);
},
nextStep,
true
);
return;
case 16:
validateCutCopy(1, 0);
if (!document.querySelector("div[contentEditable=true]")) {
// We're done! (no contentEditable node!)
step(-1);
return;
}
break;
case 17:
// copy on contenteditable selection
waitForClipboard(
"contenteditable text",
function () {
selectNode("div[contentEditable=true]", function () {
aDoCopy(true);
});
},
nextStep
);
return;
case 18:
validateCutCopy(0, 1);
break;
case 19:
// cut on contenteditable selection
waitForClipboard(
"contenteditable text",
function () {
selectNode("div[contentEditable=true]", function () {
aDoCut(true);
});
},
nextStep
);
return;
case 20:
validateCutCopy(1, 0);
break;
default:
aDone();
return;
}
SimpleTest.executeSoon(function () {
step(n + 1);
});
}
step(0);
}
function allMechanisms(aCb, aClipOverride, aNegateAll) {
function testStep(n) {
gTestN1 = n;
switch (n) {
case 0:
// Keyboard issued
cutCopyAll(
function docut() {
synthesizeKey("x", { accelKey: true });
},
function docopy() {
synthesizeKey("c", { accelKey: true });
},
function done() {
testStep(n + 1);
},
false,
aClipOverride,
aNegateAll
);
return;
case 1:
// Button issued
cutCopyAll(
function docut(aSucc) {
document.addEventListener("keydown", execCommand("cut", aSucc));
sendString("Q");
},
function docopy(aSucc) {
document.addEventListener("keydown", execCommand("copy", aSucc));
sendString("Q");
},
function done() {
testStep(n + 1);
},
false,
aClipOverride,
aNegateAll
);
return;
case 2:
// Not triggered by user gesture
cutCopyAll(
function doCut() {
is(
false,
document.execCommand("cut"),
"Can't directly execCommand not in a user callback"
);
},
function doCopy() {
is(
false,
document.execCommand("copy"),
"Can't directly execCommand not in a user callback"
);
},
function done() {
testStep(n + 1);
},
true,
aClipOverride,
aNegateAll
);
return;
case 3:
// Button issued async
cutCopyAll(
function docut(aSucc) {
document.addEventListener(
"keydown",
execCommand("cut", aSucc, true)
);
sendString("Q");
},
function docopy(aSucc) {
document.addEventListener(
"keydown",
execCommand("copy", aSucc, true)
);
sendString("Q");
},
function done() {
testStep(n + 1);
},
false,
aClipOverride,
aNegateAll
);
return;
default:
aCb();
}
}
testStep(0);
}
// Run the tests
SimpleTest.waitForExplicitFinish();
SimpleTest.requestLongerTimeout(5); // On the emulator - this times out occasionally
SimpleTest.waitForFocus(function () {
function justCancel(aEvent) {
aEvent.preventDefault();
}
function override(aEvent) {
aEvent.clipboardData.setData("text/plain", "overridden");
aEvent.preventDefault();
}
allMechanisms(function () {
gTestN0 = 1;
document.addEventListener("cut", override);
document.addEventListener("copy", override);
allMechanisms(function () {
gTestN0 = 2;
document.removeEventListener("cut", override);
document.removeEventListener("copy", override);
document.addEventListener("cut", justCancel);
document.addEventListener("copy", justCancel);
allMechanisms(
function () {
SimpleTest.finish();
},
null,
true
);
}, "overridden");
});
});