зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1490884 - Make nsCopySupport set event target of clipboard events to conform to Clipboard API and events spec r=smaug
Clipboard API and events spec, event target of clipboard events should always be an element node which contains the selection start and if there is no Selection ranges, should use <body> or <frameset> of the document: https://www.w3.org/TR/clipboard-apis/#fire-a-clipboard-event This patch does not include the test for the latter because I have no idea how to avoid adjusting selection adjustments immediately before pasting in editor's middle click event handler or enable copy or paste commands without selection ranges. Differential Revision: https://phabricator.services.mozilla.com/D5743 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
d9b9a8b93b
Коммит
f4441de2f2
|
@ -684,6 +684,22 @@ IsSelectionInsideRuby(Selection* aSelection)
|
|||
return true;
|
||||
}
|
||||
|
||||
static Element*
|
||||
GetElementOrNearestFlattenedTreeParentElement(nsINode* aNode)
|
||||
{
|
||||
if (!aNode->IsContent()) {
|
||||
return nullptr;
|
||||
}
|
||||
for (nsIContent* content = aNode->AsContent();
|
||||
content;
|
||||
content = content->GetFlattenedTreeParent()) {
|
||||
if (content->IsElement()) {
|
||||
return content->AsElement();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
|
||||
int32_t aClipboardType,
|
||||
|
@ -716,29 +732,32 @@ nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
|
|||
if (!piWindow)
|
||||
return false;
|
||||
|
||||
// if a selection was not supplied, try to find it
|
||||
nsCOMPtr<nsIContent> content;
|
||||
// Event target of clipboard events should be an element node which
|
||||
// contains selection start container.
|
||||
RefPtr<Element> targetElement;
|
||||
|
||||
// If a selection was not supplied, try to find it.
|
||||
RefPtr<Selection> sel = aSelection;
|
||||
if (!sel) {
|
||||
content = GetSelectionForCopy(doc, getter_AddRefs(sel));
|
||||
GetSelectionForCopy(doc, getter_AddRefs(sel));
|
||||
}
|
||||
|
||||
// retrieve the event target node from the start of the selection
|
||||
// Retrieve the event target node from the start of the selection.
|
||||
if (sel) {
|
||||
RefPtr<nsRange> range = sel->GetRangeAt(0);
|
||||
nsRange* range = sel->GetRangeAt(0);
|
||||
if (range) {
|
||||
nsINode* startContainer = range->GetStartContainer();
|
||||
if (startContainer) {
|
||||
content = do_QueryInterface(startContainer);
|
||||
}
|
||||
targetElement =
|
||||
GetElementOrNearestFlattenedTreeParentElement(
|
||||
range->GetStartContainer());
|
||||
}
|
||||
}
|
||||
|
||||
// if no content node was set, just get the root
|
||||
if (!content) {
|
||||
content = doc->GetRootElement();
|
||||
if (!content)
|
||||
// If there is no selection ranges, use the <body> or <frameset> element.
|
||||
if (!targetElement) {
|
||||
targetElement = doc->GetBody();
|
||||
if (!targetElement) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// It seems to be unsafe to fire an event handler during reflow (bug 393696)
|
||||
|
@ -762,7 +781,7 @@ nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
|
|||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
InternalClipboardEvent evt(true, originalEventMessage);
|
||||
evt.mClipboardData = clipboardData;
|
||||
EventDispatcher::Dispatch(content, presShell->GetPresContext(), &evt,
|
||||
EventDispatcher::Dispatch(targetElement, presShell->GetPresContext(), &evt,
|
||||
nullptr, &status);
|
||||
// If the event was cancelled, don't do the clipboard operation
|
||||
doDefault = (status != nsEventStatus_eConsumeNoDefault);
|
||||
|
@ -807,13 +826,13 @@ nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
|
|||
uint32_t count = 0;
|
||||
if (doDefault) {
|
||||
// find the focused node
|
||||
nsCOMPtr<nsIContent> srcNode = content;
|
||||
if (content->IsInNativeAnonymousSubtree()) {
|
||||
srcNode = content->FindFirstNonChromeOnlyAccessContent();
|
||||
nsIContent* sourceContent = targetElement.get();
|
||||
if (targetElement->IsInNativeAnonymousSubtree()) {
|
||||
sourceContent = targetElement->FindFirstNonChromeOnlyAccessContent();
|
||||
}
|
||||
|
||||
// check if we are looking at a password input
|
||||
nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(srcNode);
|
||||
nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(sourceContent);
|
||||
if (formControl) {
|
||||
if (formControl->ControlType() == NS_FORM_INPUT_PASSWORD) {
|
||||
return false;
|
||||
|
@ -822,7 +841,7 @@ nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
|
|||
|
||||
// when cutting non-editable content, do nothing
|
||||
// XXX this is probably the wrong editable flag to check
|
||||
if (originalEventMessage != eCut || content->IsEditable()) {
|
||||
if (originalEventMessage != eCut || targetElement->IsEditable()) {
|
||||
// get the data from the selection if any
|
||||
if (sel->IsCollapsed()) {
|
||||
if (aActionTaken) {
|
||||
|
|
|
@ -18,10 +18,13 @@
|
|||
oncopy="compareSynthetic(event, 'copy')"
|
||||
onpaste="compareSynthetic(event, 'paste')">Spot</div>
|
||||
|
||||
<div id="contenteditableContainer"></div>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
var content = document.getElementById("content");
|
||||
var contentInput = document.getElementById("content-input");
|
||||
var contenteditableContainer = document.getElementById("contenteditableContainer");
|
||||
var clipboardInitialValue = "empty";
|
||||
|
||||
var cachedCutData, cachedCopyData, cachedPasteData;
|
||||
|
@ -768,6 +771,44 @@ add_task(async function test_image_dataTransfer() {
|
|||
}
|
||||
});
|
||||
|
||||
add_task(async function test_event_target() {
|
||||
await reset();
|
||||
|
||||
let copyTarget = null;
|
||||
document.addEventListener("copy", (event) => { copyTarget = event.target; }, {once: true});
|
||||
|
||||
if (document.activeElement) {
|
||||
document.activeElement.blur();
|
||||
}
|
||||
|
||||
let selection = document.getSelection();
|
||||
selection.setBaseAndExtent(content.firstChild, "CONTENT ".length,
|
||||
content.firstChild, "CONTENT TEXT".length);
|
||||
|
||||
await putOnClipboard("TEXT", () => {
|
||||
synthesizeKey("c", {accelKey: 1});
|
||||
}, "copy text from non-editable element");
|
||||
|
||||
is(copyTarget.getAttribute("id"), "content", "Copy event's target should be always an element");
|
||||
|
||||
// Create a contenteditable element to check complicated event target.
|
||||
contenteditableContainer.innerHTML = '<div contenteditable><p id="p1">foo</p><p id="p2">bar</p></div>';
|
||||
contenteditableContainer.firstChild.focus();
|
||||
|
||||
let p1 = document.getElementById("p1");
|
||||
let p2 = document.getElementById("p2");
|
||||
selection.setBaseAndExtent(p1.firstChild, 1, p2.firstChild, 1);
|
||||
|
||||
let pasteTarget = null;
|
||||
document.addEventListener("paste", (event) => { pasteTarget = event.target; }, {once: true});
|
||||
|
||||
synthesizeKey("v", {accelKey: 1});
|
||||
is(pasteTarget.getAttribute("id"), "p1",
|
||||
"'paste' event's target should be always an element which includes start container of the first Selection range");
|
||||
|
||||
contenteditableContainer.innerHTML = "";
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
|
Загрузка…
Ссылка в новой задаче