зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1533989 - Make InputEvent.data and InputEvent.dataTransfer not expose clipboard data if user disables clipboard events r=smaug
If user disables clipboard events, it means that they don't want to expose clipboard data to web apps even if web apps cannot handle "paste" operation. Therefore, they must not want to leak clipboard data with `InputEvent.data` and `InputEvent.dataTransfer`. This patch makes `InputEvent::GetData()` and `InputEvent::GetDataTransfer()` returns empty string or new `DataTransfer` object which has only empty string if: - They are called by content JS. - The event is a trusted event. - `inputType` value is `insertFromPaste` or `insertFromPasteAsQuotation`. The reason why we don't return null for both is, Input Events spec declares `data` or `dataTransfer` shouldn't be null in the `inputType` values. And the reason why we don't return empty `DataTransfer` is, web apps may expect at least one data is stored in non-null `dataTransfer` value. Differential Revision: https://phabricator.services.mozilla.com/D25350 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
2dc9aa5a90
Коммит
e28c807e5a
|
@ -718,8 +718,7 @@ bool nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
|
|||
// next, fire the cut, copy or paste event
|
||||
bool doDefault = true;
|
||||
RefPtr<DataTransfer> clipboardData;
|
||||
if (chromeShell ||
|
||||
Preferences::GetBool("dom.event.clipboardevents.enabled", true)) {
|
||||
if (chromeShell || StaticPrefs::dom_event_clipboardevents_enabled()) {
|
||||
clipboardData =
|
||||
new DataTransfer(doc->GetScopeObject(), aEventMessage,
|
||||
originalEventMessage == ePaste, aClipboardType);
|
||||
|
|
|
@ -27,16 +27,40 @@ InputEvent::InputEvent(EventTarget* aOwner, nsPresContext* aPresContext,
|
|||
}
|
||||
}
|
||||
|
||||
void InputEvent::GetData(nsAString& aData) {
|
||||
void InputEvent::GetData(nsAString& aData, CallerType aCallerType) {
|
||||
InternalEditorInputEvent* editorInputEvent = mEvent->AsEditorInputEvent();
|
||||
MOZ_ASSERT(editorInputEvent);
|
||||
// If clipboard event is disabled, user may not want to leak clipboard
|
||||
// information via DOM events. If so, we should return empty string instead.
|
||||
if (mEvent->IsTrusted() && aCallerType != CallerType::System &&
|
||||
!StaticPrefs::dom_event_clipboardevents_enabled() &&
|
||||
ExposesClipboardDataOrDataTransfer(editorInputEvent->mInputType)) {
|
||||
aData = editorInputEvent->mData.IsVoid() ? VoidString() : EmptyString();
|
||||
return;
|
||||
}
|
||||
aData = editorInputEvent->mData;
|
||||
}
|
||||
|
||||
DataTransfer* InputEvent::GetDataTransfer() {
|
||||
already_AddRefed<DataTransfer> InputEvent::GetDataTransfer(
|
||||
CallerType aCallerType) {
|
||||
InternalEditorInputEvent* editorInputEvent = mEvent->AsEditorInputEvent();
|
||||
MOZ_ASSERT(editorInputEvent);
|
||||
return editorInputEvent->mDataTransfer;
|
||||
// If clipboard event is disabled, user may not want to leak clipboard
|
||||
// information via DOM events. If so, we should return DataTransfer which
|
||||
// has empty string instead. The reason why we make it have empty string is,
|
||||
// web apps may not expect that InputEvent.dataTransfer returns empty and
|
||||
// non-null DataTransfer instance.
|
||||
if (mEvent->IsTrusted() && aCallerType != CallerType::System &&
|
||||
!StaticPrefs::dom_event_clipboardevents_enabled() &&
|
||||
ExposesClipboardDataOrDataTransfer(editorInputEvent->mInputType)) {
|
||||
if (!editorInputEvent->mDataTransfer) {
|
||||
return nullptr;
|
||||
}
|
||||
return do_AddRef(
|
||||
new DataTransfer(editorInputEvent->mDataTransfer->GetParentObject(),
|
||||
editorInputEvent->mMessage, EmptyString()));
|
||||
}
|
||||
return do_AddRef(editorInputEvent->mDataTransfer);
|
||||
}
|
||||
|
||||
void InputEvent::GetInputType(nsAString& aInputType) {
|
||||
|
|
|
@ -34,8 +34,9 @@ class InputEvent : public UIEvent {
|
|||
}
|
||||
|
||||
void GetInputType(nsAString& aInputType);
|
||||
void GetData(nsAString& aData);
|
||||
DataTransfer* GetDataTransfer();
|
||||
void GetData(nsAString& aData, CallerType aCallerType = CallerType::System);
|
||||
already_AddRefed<DataTransfer> GetDataTransfer(
|
||||
CallerType aCallerType = CallerType::System);
|
||||
bool IsComposing();
|
||||
|
||||
protected:
|
||||
|
|
|
@ -602,9 +602,11 @@ add_task(async function test_eventspref_disabled() {
|
|||
});
|
||||
|
||||
var event_fired = false;
|
||||
var input_data = undefined;
|
||||
contentInput.oncut = function() { event_fired = true; };
|
||||
contentInput.oncopy = function() { event_fired = true; };
|
||||
contentInput.onpaste = function() { event_fired = true; };
|
||||
contentInput.oninput = function(event) { input_data = event.data; };
|
||||
try {
|
||||
selectContentInput();
|
||||
contentInput.setSelectionRange(1, 4);
|
||||
|
@ -613,24 +615,30 @@ add_task(async function test_eventspref_disabled() {
|
|||
synthesizeKey("x", {accelKey: 1});
|
||||
}, "cut changed clipboard when preference is disabled");
|
||||
is(contentInput.value, "IT TEXT", "cut changed text when preference is disabled");
|
||||
ok(!event_fired, "cut event did not fire when preference is disabled")
|
||||
ok(!event_fired, "cut event did not fire when preference is disabled");
|
||||
is(input_data, null, "cut should cause input event whose data value is null");
|
||||
|
||||
event_fired = false;
|
||||
input_data = undefined;
|
||||
contentInput.setSelectionRange(3, 6);
|
||||
await putOnClipboard("TEX", () => {
|
||||
synthesizeKey("c", {accelKey: 1});
|
||||
}, "copy changed clipboard when preference is disabled");
|
||||
ok(!event_fired, "copy event did not fire when preference is disabled")
|
||||
is(input_data, undefined, "copy shouldn't cause input event");
|
||||
|
||||
event_fired = false;
|
||||
contentInput.setSelectionRange(0, 2);
|
||||
synthesizeKey("v", {accelKey: 1});
|
||||
is(contentInput.value, "TEX TEXT", "paste changed text when preference is disabled");
|
||||
ok(!event_fired, "paste event did not fire when preference is disabled")
|
||||
ok(!event_fired, "paste event did not fire when preference is disabled");
|
||||
is(input_data, "",
|
||||
"paste should cause input event but whose data value should be empty string if clipboard event is disabled");
|
||||
} finally {
|
||||
contentInput.oncut = null;
|
||||
contentInput.oncopy = null;
|
||||
contentInput.onpaste = null;
|
||||
contentInput.oninput = null;
|
||||
}
|
||||
|
||||
await new Promise(resolve => {
|
||||
|
|
|
@ -12,7 +12,7 @@ interface InputEvent : UIEvent
|
|||
[Pref="dom.inputevent.inputtype.enabled"]
|
||||
readonly attribute DOMString inputType;
|
||||
|
||||
[Pref="dom.inputevent.data.enabled"]
|
||||
[NeedsCallerType, Pref="dom.inputevent.data.enabled"]
|
||||
readonly attribute DOMString? data;
|
||||
};
|
||||
|
||||
|
@ -32,7 +32,7 @@ dictionary InputEventInit : UIEventInit
|
|||
// https://rawgit.com/w3c/input-events/v1/index.html#interface-InputEvent
|
||||
partial interface InputEvent
|
||||
{
|
||||
[Pref="dom.inputevent.datatransfer.enabled"]
|
||||
[NeedsCallerType, Pref="dom.inputevent.datatransfer.enabled"]
|
||||
readonly attribute DataTransfer? dataTransfer;
|
||||
};
|
||||
|
||||
|
|
|
@ -305,6 +305,24 @@ async function doContenteditableTests(aEditableDiv) {
|
|||
'No "input" event should be fired if "auxclick" event is canceled (contenteditable)');
|
||||
aEditableDiv.innerHTML = "";
|
||||
|
||||
// If clipboard event is disabled, InputEvent.dataTransfer should have only empty string.
|
||||
await SpecialPowers.pushPrefEnv({"set": [["dom.event.clipboardevents.enabled", false]]});
|
||||
await copyPlaintext("abc");
|
||||
aEditableDiv.focus();
|
||||
pasteEventCount = 0;
|
||||
inputEvents = [];
|
||||
synthesizeMouseAtCenter(aEditableDiv, {button: 1});
|
||||
is(aEditableDiv.innerHTML, "abc",
|
||||
"Even if clipboard event is disabled, paste should be done");
|
||||
is(pasteEventCount, 0,
|
||||
"If clipboard event is disabled, 'paste' event shouldn't be fired once");
|
||||
is(inputEvents.length, 1,
|
||||
'One "input" event should be fired even if clipboard event is disabled (contenteditable)');
|
||||
checkInputEvent(inputEvents[0], "insertFromPaste", null, [{type: "text/plain", data: ""}],
|
||||
"when clipboard event is disabled (contenteditable)");
|
||||
await SpecialPowers.pushPrefEnv({"set": [["dom.event.clipboardevents.enabled", true]]});
|
||||
aEditableDiv.innerHTML = "";
|
||||
|
||||
aEditableDiv.removeEventListener("paste", pasteEventLogger);
|
||||
|
||||
// Oddly, copyHTMLContent fails randomly only on Linux. Let's skip this.
|
||||
|
@ -418,7 +436,8 @@ async function doAfterRemoveOfClickedElementTest(aEditableDiv) {
|
|||
|
||||
async function doTests() {
|
||||
await SpecialPowers.pushPrefEnv({"set": [["middlemouse.paste", true],
|
||||
["middlemouse.contentLoadURL", false]]});
|
||||
["middlemouse.contentLoadURL", false],
|
||||
["dom.event.clipboardevents.enabled", true]]});
|
||||
let container = document.getElementById("container");
|
||||
container.innerHTML = "<textarea id=\"editor\"></textarea>";
|
||||
await doTextareaTests(document.getElementById("editor"));
|
||||
|
|
|
@ -209,6 +209,15 @@ VARCACHE_PREF(
|
|||
)
|
||||
#undef PREF_VALUE
|
||||
|
||||
// If this is true, it's allowed to fire "cut", "copy" and "paste" events.
|
||||
// Additionally, "input" events may expose clipboard content when inputType
|
||||
// is "insertFromPaste" or something.
|
||||
VARCACHE_PREF(
|
||||
"dom.event.clipboardevents.enabled",
|
||||
dom_event_clipboardevents_enabled,
|
||||
bool, true
|
||||
)
|
||||
|
||||
// If this is true, "keypress" event's keyCode value and charCode value always
|
||||
// become same if the event is not created/initialized by JS.
|
||||
VARCACHE_PREF(
|
||||
|
|
|
@ -1441,7 +1441,6 @@ pref("privacy.resistFingerprinting.reduceTimerPrecision.microseconds", 1000);
|
|||
pref("privacy.resistFingerprinting.reduceTimerPrecision.jitter", true);
|
||||
|
||||
pref("dom.event.contextmenu.enabled", true);
|
||||
pref("dom.event.clipboardevents.enabled", true);
|
||||
pref("dom.event.coalesce_mouse_move", true);
|
||||
|
||||
pref("javascript.enabled", true);
|
||||
|
|
|
@ -130,6 +130,16 @@ enum class EditorInputType : EditorInputTypeType {
|
|||
|
||||
#undef NS_DEFINE_INPUTTYPE
|
||||
|
||||
inline bool ExposesClipboardDataOrDataTransfer(EditorInputType aInputType) {
|
||||
switch (aInputType) {
|
||||
case EditorInputType::eInsertFromPaste:
|
||||
case EditorInputType::eInsertFromPasteAsQuotation:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IsDataAvailableOnTextEditor() returns true if aInputType on TextEditor
|
||||
* should have non-null InputEvent.data value.
|
||||
|
|
Загрузка…
Ссылка в новой задаче