document.execCommand('paste') not working correctly on web extension background page
Categories
(WebExtensions :: General, defect, P3)
Tracking
(firefox72 affected, firefox73 affected, firefox74 affected)
People
(Reporter: toasted.nutbread, Assigned: robwu)
References
Details
document.execCommand('paste')
does not seem to be working correctly on web extension background pages despite clipboardRead
permission being granted. Depending on the HTML, the command will sometimes work and sometimes not work. Invalid HTML seems to allow it to work.
Using this bg.js
script for testing:
function checkClipboard() {
const target = document.querySelector('#target');
target.innerText = '';
target.focus();
document.execCommand('paste');
const content = target.innerText;
target.innerText = '<empty>';
console.log('content', content);
}
setInterval(checkClipboard, 1000);
The following background HTML page does not work (logs empty string):
<html>
<head>
<script src="bg.js"></script>
</head>
<body>
<div id="target" contenteditable="true"></div>
</body>
</html>
Whereas the this HTML page does work (logs clipboard contents):
<html>
<head>
<script src="bg.js"></script>
</head>
<body>
<div id="target" contenteditable="true" />
</body>
</html>
Note the incorrect ending tag on the <div>
.
Example repository demonstrating the bug can be found at:
https://github.com/toasted-nutbread/firefox-clipboard-paste-bug
For more context, there is at least one extension that relies on this buggy behavior:
https://github.com/kmltml/clipboard-inserter/blob/924a8f1350f8ba7d8d2adb69612d6ac0e201e97c/bg/index.html#L7
The advantage of this method over navigator.clipboard.readText
is that it works on Chrome the same way. Chrome doesn't allow using navigator.clipboard.readText
on unfocused pages:
https://stackoverflow.com/questions/56306153/domexception-on-calling-navigator-clipboard-readtext
Updated•5 years ago
|
Comment 2•5 years ago
|
||
The priority flag is not set for this bug.
:jimm, could you have a look please?
For more information, please visit auto_nag documentation.
Comment 3•5 years ago
|
||
Hello,
I have managed to reproduce the issue using the extensions provided via the repository in Comment 0 on the latest Nightly (74.0a1/20200203085242), Beta (73.0b11/20200128001646) and Release (72.0.2/20200117190643) under Windows 10 Pro 64-bit and macOS High Sierra 10.15.
Using the extension and background page from the ext1
folder, no content from the clipboard will be displayed in the console when inspecting the extension.
However, using the extension and background page from the ext2
folder, clipboard content will indeed be displayed in the extension console, as mentioned in Comment 0.
Updated•5 years ago
|
Comment 4•5 years ago
|
||
Would this be related to the copy issue you were looking at? Should this use the new api?
Assignee | ||
Comment 5•5 years ago
|
||
This is different from the other issue I looked at (bug 1611799).
The consistent reproduction steps helps with debugging.
With GDB, I found a difference between runs of ext1
and ext2
, at:
#0 mozilla::EditorBase::IsSelectionEditable
at editor/libeditor/EditorBase.cpp:616
#1 mozilla::PasteCommand::IsCommandEnabled
at editor/libeditor/EditorCommands.cpp:461
#2 mozilla::dom::Document::ExecCommand
at dom/base/Document.cpp:4788
ext1
(bad): focusNode
and anchorNode
is the <body>
element.
ext2
(good): focusNode
and anchorNode
is the <div>
element.
Now back to the reproduction:
<body>
<div contenteditable />
</body>
Contrary to appearances, <div />
is NOT a self-closing tag. The slash is ignored, and what ends up happening is that the text node with the line break becomes part of the <div>
element.
When properly closed, the body has whitespace after the <div>
.
Apparently, when whitespace is present after the content-editable <div>
, the <div>
does not become the sole anchor/focus node, and the parent <body>
element is included. This only happens in the background page (hidden browser window), NOT in a browser tab.
A work-around to the problem is to ensure that there is no whitespace after the content-editable node (provided that there are no other bugs...).
Assignee | ||
Comment 6•5 years ago
|
||
str |
Minimal reproduction steps:
- Create the following extension:
manifest.json from https://github.com/toasted-nutbread/firefox-clipboard-paste-bug/blob/65f29fdbf99bb1d77850d42cd961a5442766b5c9/ext1/manifest.json
bg.html
<script src="bg.js"></script>
<div contenteditable></div>
(with line break and content after div = bug, without anything after </div> = no bug)
// bg.js
window.onload = function() {
document.querySelector('#target').focus();
console.log(window.getSelection().focusNode); // Expected: <div>. Actual: <body>
};
- Load the extension at
about:debugging
. - Click on Inspect at the extension to view the console.
Expected:
<div contenteditable>
Actual:
<body>
When bg.html
is edited, to remove all content (including line break) after </div>
(or simply remove </div>
and everything that follows), the expected result happens.
Assignee | ||
Comment 7•5 years ago
|
||
The selection is initialized with the node set to the last container element of the document (**). In ext1
's case, that is the <body>
.
In ext2
's case, that is the <div>
element.
The .focus()
method does not appear to change the selection when a content-editable element is focused in the background page, which prevents paste from working.
Focusing works for <input>
and <textarea>
(in bug 1272869) when WebExtensions were moved to a separate process, though we did not investigate why it started to work.
The next step in the investigation is to figure out why .focus()
does not work on a content-editable element.
(**) Initialization of selection is as follows:
#0 mozilla::EditorBase::CollapseSelectionToEnd
at editor/libeditor/EditorBase.cpp:1099
#1 mozilla::TextEditor::InitEditorContentAndSelection
at editor/libeditor/TextEditSubActionHandler.cpp:62
#2 mozilla::HTMLEditor::InitEditorContentAndSelection
at editor/libeditor/HTMLEditSubActionHandler.cpp:227
#3 mozilla::HTMLEditor::Init
at editor/libeditor/HTMLEditor.cpp:267
#4 nsEditingSession::SetupEditorOnWindow
at editor/composer/nsEditingSession.cpp:416
#5 nsEditingSession::MakeWindowEditable
at editor/composer/nsEditingSession.cpp:162
#6 mozilla::dom::Document::EditingStateChanged
at dom/base/Document.cpp:5469
Assignee | ||
Comment 8•5 years ago
|
||
To debug nsFocusManager, I ran with: MOZ_LOG=Focus:5
Bad (background page):
[Child 1809667: Main Thread]: D/Focus <<SetFocus begin>>
[Child 1809667: Main Thread]: D/Focus Shift Focus: div
[Child 1809667: Main Thread]: D/Focus Flags: 400010 Current Window: (nil) New Window: 0x7f80732e63e0 Current Element: (nil)
[Child 1809667: Main Thread]: D/Focus In Active Window: 0 In Focused Window: 0 SendFocus: 0
[Child 1809667: Main Thread]: D/Focus <<SetFocus end>>
Good (same page, but in a tab):
[Child 1809667: Main Thread]: D/Focus <<SetFocus begin>>
[Child 1809667: Main Thread]: D/Focus Shift Focus: div
[Child 1809667: Main Thread]: D/Focus Flags: 400010 Current Window: 0x7f80732e6060 New Window: 0x7f80732e6060 Current Element: (nil)
[Child 1809667: Main Thread]: D/Focus In Active Window: 1 In Focused Window: 1 SendFocus: 1
[Child 1809667: Main Thread]: D/Focus <<Blur begin>>
[Child 1809667: Main Thread]: D/Focus Element (none) has been blurred
[Child 1809667: Main Thread]: D/Focus Update Caret: 0 1
[Child 1809667: Main Thread]: D/Focus <<Focus begin>>
[Child 1809667: Main Thread]: D/Focus Element div has been focused
[Child 1809667: Main Thread]: D/Focus from html
[Child 1809667: Main Thread]: D/Focus [Newdoc: 0 FocusChanged: 1 Raised: 0 Flags: 400010]
[Child 1809667: Main Thread]: D/Focus Update Caret: 1 0
[Child 1809667: Main Thread]: D/Focus <<SetFocus end>>
The Update Caret
message in the good case is missing from the bad case. This part triggers the update of the selection.
nsFocusManager::MoveCaretToFocus
is called by nsFocusManager::UpdateCaret
, and usually called by nsFocusManager::focus
, but skipped when the window is not marked as active.
To update the selection, a MoveCaretToFocus
call needs to be added around this place: https://searchfox.org/mozilla-central/rev/5a10be606f2d76ef22f1f44565749490de991d35/dom/base/nsFocusManager.cpp#1471-1483
This is not enough: When an element to focus has no child nodes, the caret is placed before it. ... but in case of a contentEditable element, placing the caret before it means that the focus moves to the <body>
element.
Updated•5 years ago
|
Updated•4 years ago
|
Updated•3 years ago
|
Updated•2 years ago
|
Description
•