зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1560032 - part 2: Make cut/copy in password field available r=m_kato,smaug
First, we need to make `nsCopySupport::FireClipboardEvent()` keep handling `eCopy` and `eCut` event even in password field, only if `TextEditor` allows them. Then, we need to make `nsPlainTextSerializer::AppendText()` not expose masked password for making users safer. Although `TextEditor` does not allow `eCopy` nor `eCut` when selection is not in unmasked range. Fortunately, retrieving masked and unmasked password from `nsTextFragment` has already been implemented in `ContentEventHandler.cpp`. This patch moves it into `EditorUtils` and makes `ContentEventHandler.cpp` and `nsPlaintextSerializer` share it. Differential Revision: https://phabricator.services.mozilla.com/D39000 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
ffbb14909f
Коммит
fafe168f04
|
@ -53,12 +53,14 @@
|
|||
#endif
|
||||
|
||||
#include "mozilla/ContentEvents.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/dom/Selection.h"
|
||||
#include "mozilla/TextEditor.h"
|
||||
#include "mozilla/IntegerRange.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/HTMLInputElement.h"
|
||||
#include "mozilla/dom/Selection.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -878,11 +880,17 @@ bool nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
|
|||
sourceContent = targetElement->FindFirstNonChromeOnlyAccessContent();
|
||||
}
|
||||
|
||||
// check if we are looking at a password input
|
||||
nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(sourceContent);
|
||||
if (formControl) {
|
||||
if (formControl->ControlType() == NS_FORM_INPUT_PASSWORD) {
|
||||
return false;
|
||||
// If it's <input type="password"> and there is no unmasked range or
|
||||
// there is unmasked range but it's collapsed or it'll be masked
|
||||
// automatically, the selected password shouldn't be copied into the
|
||||
// clipboard.
|
||||
if (HTMLInputElement* inputElement =
|
||||
HTMLInputElement::FromNodeOrNull(sourceContent)) {
|
||||
if (TextEditor* textEditor = inputElement->GetTextEditor()) {
|
||||
if (textEditor->IsPasswordEditor() &&
|
||||
!textEditor->IsCopyToClipboardAllowed()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,10 @@
|
|||
#include "nsReadableUtils.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "nsCRT.h"
|
||||
#include "mozilla/EditorUtils.h"
|
||||
#include "mozilla/dom/CharacterData.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/Text.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/BinarySearch.h"
|
||||
#include "nsComputedDOMStyle.h"
|
||||
|
@ -315,6 +318,11 @@ nsPlainTextSerializer::AppendText(nsIContent* aText, int32_t aStartOffset,
|
|||
CopyASCIItoUTF16(Substring(data + aStartOffset, data + endoffset), textstr);
|
||||
}
|
||||
|
||||
// Mask the text if the text node is in a password field.
|
||||
if (content->HasFlag(NS_MAYBE_MASKED)) {
|
||||
EditorUtils::MaskString(textstr, content->AsText(), 0, aStartOffset);
|
||||
}
|
||||
|
||||
mOutputString = &aStr;
|
||||
|
||||
// We have to split the string across newlines
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
#include "ContentEventHandler.h"
|
||||
|
||||
#include "mozilla/ContentIterator.h"
|
||||
#include "mozilla/EditorUtils.h"
|
||||
#include "mozilla/IMEStateManager.h"
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/RangeUtils.h"
|
||||
#include "mozilla/TextComposition.h"
|
||||
#include "mozilla/TextEditor.h"
|
||||
#include "mozilla/TextEvents.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/HTMLUnknownElement.h"
|
||||
|
@ -506,71 +506,11 @@ static void ConvertToNativeNewlines(nsString& aString) {
|
|||
#endif
|
||||
}
|
||||
|
||||
// Helper method for `AppendString()` and `AppendSubString()`. This should
|
||||
// be called only when `aText` is in a password field. This method masks
|
||||
// A part of or all of `aText` (`aStartOffsetInText` and later) should've
|
||||
// been copied (apppended) to `aString`. `aStartOffsetInString` is where
|
||||
// the password was appended into `aString`.
|
||||
static void MaskString(nsString& aString, Text* aText,
|
||||
uint32_t aStartOffsetInString,
|
||||
uint32_t aStartOffsetInText) {
|
||||
MOZ_ASSERT(aText->HasFlag(NS_MAYBE_MASKED));
|
||||
MOZ_ASSERT(aStartOffsetInString == 0 || aStartOffsetInText == 0);
|
||||
|
||||
uint32_t unmaskStart = UINT32_MAX, unmaskLength = 0;
|
||||
TextEditor* textEditor =
|
||||
nsContentUtils::GetTextEditorFromAnonymousNodeWithoutCreation(aText);
|
||||
if (textEditor && textEditor->UnmaskedLength() > 0) {
|
||||
unmaskStart = textEditor->UnmaskedStart();
|
||||
unmaskLength = textEditor->UnmaskedLength();
|
||||
// If text is copied from after unmasked range, we can treat this case
|
||||
// as mask all.
|
||||
if (aStartOffsetInText >= unmaskStart + unmaskLength) {
|
||||
unmaskLength = 0;
|
||||
unmaskStart = UINT32_MAX;
|
||||
} else {
|
||||
// If text is copied from middle of unmasked range, reduce the length
|
||||
// and adjust start offset.
|
||||
if (aStartOffsetInText > unmaskStart) {
|
||||
unmaskLength = unmaskStart + unmaskLength - aStartOffsetInText;
|
||||
unmaskStart = 0;
|
||||
}
|
||||
// If text is copied from before start of unmasked range, just adjust
|
||||
// the start offset.
|
||||
else {
|
||||
unmaskStart -= aStartOffsetInText;
|
||||
}
|
||||
// Make the range is in the string.
|
||||
unmaskStart += aStartOffsetInString;
|
||||
}
|
||||
}
|
||||
|
||||
const char16_t kPasswordMask = TextEditor::PasswordMask();
|
||||
for (uint32_t i = aStartOffsetInString; i < aString.Length(); ++i) {
|
||||
bool isSurrogatePair = NS_IS_HIGH_SURROGATE(aString.CharAt(i)) &&
|
||||
i < aString.Length() - 1 &&
|
||||
NS_IS_LOW_SURROGATE(aString.CharAt(i + 1));
|
||||
if (i < unmaskStart || i >= unmaskStart + unmaskLength) {
|
||||
if (isSurrogatePair) {
|
||||
aString.SetCharAt(kPasswordMask, i);
|
||||
aString.SetCharAt(kPasswordMask, i + 1);
|
||||
} else {
|
||||
aString.SetCharAt(kPasswordMask, i);
|
||||
}
|
||||
}
|
||||
|
||||
// Skip the following low surrogate.
|
||||
if (isSurrogatePair) {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void AppendString(nsString& aString, Text* aText) {
|
||||
uint32_t oldXPLength = aString.Length();
|
||||
aText->TextFragment().AppendTo(aString);
|
||||
if (aText->HasFlag(NS_MAYBE_MASKED)) {
|
||||
MaskString(aString, aText, oldXPLength, 0);
|
||||
EditorUtils::MaskString(aString, aText, oldXPLength, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -580,7 +520,7 @@ static void AppendSubString(nsString& aString, Text* aText, uint32_t aXPOffset,
|
|||
aText->TextFragment().AppendTo(aString, static_cast<int32_t>(aXPOffset),
|
||||
static_cast<int32_t>(aXPLength));
|
||||
if (aText->HasFlag(NS_MAYBE_MASKED)) {
|
||||
MaskString(aString, aText, oldXPLength, aXPOffset);
|
||||
EditorUtils::MaskString(aString, aText, oldXPLength, aXPOffset);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1133,7 +1133,7 @@ EditorBase::CanCut(bool* aCanCut) {
|
|||
if (NS_WARN_IF(!aCanCut)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
*aCanCut = AsTextEditor()->CanCut();
|
||||
*aCanCut = AsTextEditor()->IsCutCommandEnabled();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1145,7 +1145,7 @@ EditorBase::CanCopy(bool* aCanCopy) {
|
|||
if (NS_WARN_IF(!aCanCopy)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
*aCanCopy = AsTextEditor()->CanCopy();
|
||||
*aCanCopy = AsTextEditor()->IsCopyCommandEnabled();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -327,7 +327,8 @@ bool CutCommand::IsCommandEnabled(Command aCommand,
|
|||
if (!aTextEditor) {
|
||||
return false;
|
||||
}
|
||||
return aTextEditor->IsSelectionEditable() && aTextEditor->CanCut();
|
||||
return aTextEditor->IsSelectionEditable() &&
|
||||
aTextEditor->IsCutCommandEnabled();
|
||||
}
|
||||
|
||||
nsresult CutCommand::DoCommand(Command aCommand, TextEditor& aTextEditor,
|
||||
|
@ -391,7 +392,7 @@ bool CopyCommand::IsCommandEnabled(Command aCommand,
|
|||
if (!aTextEditor) {
|
||||
return false;
|
||||
}
|
||||
return aTextEditor->CanCopy();
|
||||
return aTextEditor->IsCopyCommandEnabled();
|
||||
}
|
||||
|
||||
nsresult CopyCommand::DoCommand(Command aCommand, TextEditor& aTextEditor,
|
||||
|
|
|
@ -8,7 +8,10 @@
|
|||
#include "mozilla/ContentIterator.h"
|
||||
#include "mozilla/EditorDOMPoint.h"
|
||||
#include "mozilla/OwningNonNull.h"
|
||||
#include "mozilla/TextEditor.h"
|
||||
#include "mozilla/dom/Selection.h"
|
||||
#include "mozilla/dom/Text.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsError.h"
|
||||
#include "nsIContent.h"
|
||||
|
@ -111,4 +114,60 @@ bool EditorUtils::IsDescendantOf(const nsINode& aNode, const nsINode& aParent,
|
|||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
void EditorUtils::MaskString(nsString& aString, Text* aText,
|
||||
uint32_t aStartOffsetInString,
|
||||
uint32_t aStartOffsetInText) {
|
||||
MOZ_ASSERT(aText->HasFlag(NS_MAYBE_MASKED));
|
||||
MOZ_ASSERT(aStartOffsetInString == 0 || aStartOffsetInText == 0);
|
||||
|
||||
uint32_t unmaskStart = UINT32_MAX, unmaskLength = 0;
|
||||
TextEditor* textEditor =
|
||||
nsContentUtils::GetTextEditorFromAnonymousNodeWithoutCreation(aText);
|
||||
if (textEditor && textEditor->UnmaskedLength() > 0) {
|
||||
unmaskStart = textEditor->UnmaskedStart();
|
||||
unmaskLength = textEditor->UnmaskedLength();
|
||||
// If text is copied from after unmasked range, we can treat this case
|
||||
// as mask all.
|
||||
if (aStartOffsetInText >= unmaskStart + unmaskLength) {
|
||||
unmaskLength = 0;
|
||||
unmaskStart = UINT32_MAX;
|
||||
} else {
|
||||
// If text is copied from middle of unmasked range, reduce the length
|
||||
// and adjust start offset.
|
||||
if (aStartOffsetInText > unmaskStart) {
|
||||
unmaskLength = unmaskStart + unmaskLength - aStartOffsetInText;
|
||||
unmaskStart = 0;
|
||||
}
|
||||
// If text is copied from before start of unmasked range, just adjust
|
||||
// the start offset.
|
||||
else {
|
||||
unmaskStart -= aStartOffsetInText;
|
||||
}
|
||||
// Make the range is in the string.
|
||||
unmaskStart += aStartOffsetInString;
|
||||
}
|
||||
}
|
||||
|
||||
const char16_t kPasswordMask = TextEditor::PasswordMask();
|
||||
for (uint32_t i = aStartOffsetInString; i < aString.Length(); ++i) {
|
||||
bool isSurrogatePair = NS_IS_HIGH_SURROGATE(aString.CharAt(i)) &&
|
||||
i < aString.Length() - 1 &&
|
||||
NS_IS_LOW_SURROGATE(aString.CharAt(i + 1));
|
||||
if (i < unmaskStart || i >= unmaskStart + unmaskLength) {
|
||||
if (isSurrogatePair) {
|
||||
aString.SetCharAt(kPasswordMask, i);
|
||||
aString.SetCharAt(kPasswordMask, i + 1);
|
||||
} else {
|
||||
aString.SetCharAt(kPasswordMask, i);
|
||||
}
|
||||
}
|
||||
|
||||
// Skip the following low surrogate.
|
||||
if (isSurrogatePair) {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -497,6 +497,17 @@ class EditorUtils final {
|
|||
EditorRawDOMPoint* aOutPoint = nullptr);
|
||||
static bool IsDescendantOf(const nsINode& aNode, const nsINode& aParent,
|
||||
EditorDOMPoint* aOutPoint);
|
||||
|
||||
/**
|
||||
* Helper method for `AppendString()` and `AppendSubString()`. This should
|
||||
* be called only when `aText` is in a password field. This method masks
|
||||
* A part of or all of `aText` (`aStartOffsetInText` and later) should've
|
||||
* been copied (apppended) to `aString`. `aStartOffsetInString` is where
|
||||
* the password was appended into `aString`.
|
||||
*/
|
||||
static void MaskString(nsString& aString, dom::Text* aText,
|
||||
uint32_t aStartOffsetInString,
|
||||
uint32_t aStartOffsetInText);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -1732,7 +1732,7 @@ nsresult TextEditor::RedoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
bool TextEditor::CanCutOrCopy() const {
|
||||
bool TextEditor::IsCopyToClipboardAllowedInternal() const {
|
||||
MOZ_ASSERT(IsEditActionDataAvailable());
|
||||
if (SelectionRefPtr()->IsCollapsed()) {
|
||||
return false;
|
||||
|
@ -1804,7 +1804,7 @@ nsresult TextEditor::CutAsAction(nsIPrincipal* aPrincipal) {
|
|||
actionTaken ? NS_OK : NS_ERROR_EDITOR_ACTION_CANCELED);
|
||||
}
|
||||
|
||||
bool TextEditor::CanCut() const {
|
||||
bool TextEditor::IsCutCommandEnabled() const {
|
||||
AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
|
||||
if (NS_WARN_IF(!editActionData.CanHandle())) {
|
||||
return false;
|
||||
|
@ -1818,7 +1818,7 @@ bool TextEditor::CanCut() const {
|
|||
return true;
|
||||
}
|
||||
|
||||
return IsModifiable() && CanCutOrCopy();
|
||||
return IsModifiable() && IsCopyToClipboardAllowedInternal();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -1835,7 +1835,7 @@ TextEditor::Copy() {
|
|||
actionTaken ? NS_OK : NS_ERROR_EDITOR_ACTION_CANCELED);
|
||||
}
|
||||
|
||||
bool TextEditor::CanCopy() const {
|
||||
bool TextEditor::IsCopyCommandEnabled() const {
|
||||
AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
|
||||
if (NS_WARN_IF(!editActionData.CanHandle())) {
|
||||
return false;
|
||||
|
@ -1849,7 +1849,7 @@ bool TextEditor::CanCopy() const {
|
|||
return true;
|
||||
}
|
||||
|
||||
return CanCutOrCopy();
|
||||
return IsCopyToClipboardAllowedInternal();
|
||||
}
|
||||
|
||||
bool TextEditor::CanDeleteSelection() const {
|
||||
|
|
|
@ -95,24 +95,35 @@ class TextEditor : public EditorBase,
|
|||
MOZ_CAN_RUN_SCRIPT nsresult CutAsAction(nsIPrincipal* aPrincipal = nullptr);
|
||||
|
||||
/**
|
||||
* CanCut() always returns true if we're in non-chrome HTML/XHTML document.
|
||||
* Otherwise, returns true when:
|
||||
* - `Selection` is not collapsed and we're not a password editor.
|
||||
* - `Selection` is not collapsed and we're a password editor but selection
|
||||
* range in unmasked range.
|
||||
* IsCutCommandEnabled() returns whether cut command can be enabled or
|
||||
* disabled. This always returns true if we're in non-chrome HTML/XHTML
|
||||
* document. Otherwise, same as the result of `IsCopyToClipboardAllowed()`.
|
||||
*/
|
||||
bool CanCut() const;
|
||||
bool IsCutCommandEnabled() const;
|
||||
|
||||
NS_IMETHOD Copy() override;
|
||||
|
||||
/**
|
||||
* CanCopy() always returns true if we're in non-chrome HTML/XHTML document.
|
||||
* Otherwise, returns true when:
|
||||
* IsCopyCommandEnabled() returns copy command can be enabled or disabled.
|
||||
* This always returns true if we're in non-chrome HTML/XHTML document.
|
||||
* Otherwise, same as the result of `IsCopyToClipboardAllowed()`.
|
||||
*/
|
||||
bool IsCopyCommandEnabled() const;
|
||||
|
||||
/**
|
||||
* IsCopyToClipboardAllowed() returns true if the selected content can
|
||||
* be copied into the clipboard. This returns true when:
|
||||
* - `Selection` is not collapsed and we're not a password editor.
|
||||
* - `Selection` is not collapsed and we're a password editor but selection
|
||||
* range in unmasked range.
|
||||
* range is in unmasked range.
|
||||
*/
|
||||
bool CanCopy() const;
|
||||
bool IsCopyToClipboardAllowed() const {
|
||||
AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
|
||||
if (NS_WARN_IF(!editActionData.CanHandle())) {
|
||||
return false;
|
||||
}
|
||||
return IsCopyToClipboardAllowedInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* CanDeleteSelection() returns true if `Selection` is not collapsed and
|
||||
|
@ -714,10 +725,9 @@ class TextEditor : public EditorBase,
|
|||
nsAString& aResult);
|
||||
|
||||
/**
|
||||
* CanCutOrCopy() returns true if "cut" or "copy" command is available
|
||||
* right now.
|
||||
* See comment of IsCopyToClipboardAllowed() for the detail.
|
||||
*/
|
||||
bool CanCutOrCopy() const;
|
||||
bool IsCopyToClipboardAllowedInternal() const;
|
||||
|
||||
bool FireClipboardEvent(EventMessage aEventMessage, int32_t aSelectionType,
|
||||
bool* aActionTaken = nullptr);
|
||||
|
|
|
@ -271,6 +271,8 @@ skip-if = os != 'mac' # bug 574005
|
|||
[test_composition_event_created_in_chrome.html]
|
||||
[test_contenteditable_focus.html]
|
||||
[test_cut_copy_delete_command_enabled.html]
|
||||
[test_cut_copy_password.html]
|
||||
tags = clipboard
|
||||
[test_documentCharacterSet.html]
|
||||
[test_dom_input_event_on_htmleditor.html]
|
||||
[test_dom_input_event_on_texteditor.html]
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for cut/copy in password field</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<input type="password">
|
||||
<script>
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.waitForFocus(async () => {
|
||||
let input = document.getElementsByTagName("input")[0];
|
||||
let editor = SpecialPowers.wrap(input).editor;
|
||||
const kMask = editor.passwordMask;
|
||||
function copyToClipboard(aExpectedValue) {
|
||||
return new Promise(async resolve => {
|
||||
try {
|
||||
await SimpleTest.promiseClipboardChange(
|
||||
aExpectedValue, () => { SpecialPowers.doCommand(window, "cmd_copy"); },
|
||||
undefined, undefined, aExpectedValue === null);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
function cutToClipboard(aExpectedValue) {
|
||||
return new Promise(async resolve => {
|
||||
try {
|
||||
await SimpleTest.promiseClipboardChange(
|
||||
aExpectedValue, () => { SpecialPowers.doCommand(window, "cmd_cut"); },
|
||||
undefined, undefined, aExpectedValue === null);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
input.value = "abcdef";
|
||||
input.focus();
|
||||
|
||||
input.setSelectionRange(0, 6);
|
||||
ok(true, "Trying to copy masked password...");
|
||||
await copyToClipboard(null);
|
||||
isnot(SpecialPowers.getClipboardData("text/unicode"), "abcdef",
|
||||
"Copying masked password shouldn't copy raw value into the clipboard");
|
||||
isnot(SpecialPowers.getClipboardData("text/unicode"), `${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
||||
"Copying masked password shouldn't copy masked value into the clipboard");
|
||||
ok(true, "Trying to cut masked password...");
|
||||
await cutToClipboard(null);
|
||||
isnot(SpecialPowers.getClipboardData("text/unicode"), "abcdef",
|
||||
"Cutting masked password shouldn't copy raw value into the clipboard");
|
||||
isnot(SpecialPowers.getClipboardData("text/unicode"), `${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
||||
"Cutting masked password shouldn't copy masked value into the clipboard");
|
||||
is(input.value, "abcdef",
|
||||
"Cutting masked password shouldn't modify the value");
|
||||
|
||||
editor.unmask(2, 4);
|
||||
input.setSelectionRange(0, 6);
|
||||
ok(true, "Trying to copy partially masked password...");
|
||||
await copyToClipboard(null);
|
||||
isnot(SpecialPowers.getClipboardData("text/unicode"), "abcdef",
|
||||
"Copying partially masked password shouldn't copy raw value into the clipboard");
|
||||
isnot(SpecialPowers.getClipboardData("text/unicode"), `${kMask}${kMask}cd${kMask}${kMask}`,
|
||||
"Copying partially masked password shouldn't copy partially masked value into the clipboard");
|
||||
isnot(SpecialPowers.getClipboardData("text/unicode"), `${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
||||
"Copying partially masked password shouldn't copy masked value into the clipboard");
|
||||
ok(true, "Trying to cut partially masked password...");
|
||||
await cutToClipboard(null);
|
||||
isnot(SpecialPowers.getClipboardData("text/unicode"), "abcdef",
|
||||
"Cutting partially masked password shouldn't copy raw value into the clipboard");
|
||||
isnot(SpecialPowers.getClipboardData("text/unicode"), `${kMask}${kMask}cd${kMask}${kMask}`,
|
||||
"Cutting partially masked password shouldn't copy partially masked value into the clipboard");
|
||||
isnot(SpecialPowers.getClipboardData("text/unicode"), `${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
||||
"Cutting partially masked password shouldn't copy masked value into the clipboard");
|
||||
is(input.value, "abcdef",
|
||||
"Cutting partially masked password shouldn't modify the value");
|
||||
|
||||
input.setSelectionRange(2, 4);
|
||||
ok(true, "Trying to copy unmasked password...");
|
||||
await copyToClipboard("cd");
|
||||
is(input.value, "abcdef",
|
||||
"Copying unmasked password shouldn't modify the value");
|
||||
|
||||
input.value = "012345";
|
||||
editor.unmask(2, 4);
|
||||
input.setSelectionRange(2, 4);
|
||||
ok(true, "Trying to cut unmasked password...");
|
||||
await cutToClipboard("23");
|
||||
is(input.value, "0145",
|
||||
"Cutting unmasked password should modify the value");
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче