Bug 1833284 - Do not wrap text conversion of event descriptions. r=mkmelin

Differential Revision: https://phabricator.services.mozilla.com/D178571

--HG--
extra : amend_source : 7d4291888e540c9f03380fb8f4bf316f8a948809
This commit is contained in:
Sean Burke 2023-05-24 20:37:35 +10:00
Родитель 6c196999d2
Коммит c4c8de57ad
4 изменённых файлов: 162 добавлений и 6 удалений

Просмотреть файл

@ -1560,8 +1560,9 @@ function saveDialog(item) {
// The editor gives us output wrapped in a body tag. We don't really want
// that, so strip it. (Yes, it's a regex with HTML, but a _very_ specific
// one.)
item.descriptionHTML = editorOutput.replace(/^<body>(.+)<\/body>$/, "$1");
// one.) We use the `s` flag to match across newlines in case there's a
// <pre/> tag, in which case <br/> will not be inserted.
item.descriptionHTML = editorOutput.replace(/^<body>(.+)<\/body>$/s, "$1");
}
// Event Status

Просмотреть файл

@ -421,13 +421,13 @@ calItemBase.prototype = {
set descriptionHTML(html) {
if (html) {
// We need to output a plaintext version of the description, even if we're
// using the ALTREP parameter.
// using the ALTREP parameter. We use the "preformatted" option in case
// the HTML contains a <pre/> tag with newlines.
let mode =
Ci.nsIDocumentEncoder.OutputDropInvisibleBreak |
Ci.nsIDocumentEncoder.OutputWrap |
Ci.nsIDocumentEncoder.OutputLFLineBreak |
Ci.nsIDocumentEncoder.OutputBodyOnly;
let text = gParserUtils.convertToPlainText(html, mode, 80);
Ci.nsIDocumentEncoder.OutputPreformatted;
let text = gParserUtils.convertToPlainText(html, mode, 0);
this.setProperty("DESCRIPTION", text);

Просмотреть файл

@ -21,6 +21,7 @@ support-files = data/**
[browser_attendeesDialogRemove.js]
[browser_attendeesDialogUpdate.js]
[browser_eventDialog.js]
[browser_eventDialogDescriptionEditor.js]
[browser_eventDialogEditButton.js]
[browser_eventDialogModificationPrompt.js]
[browser_utf8.js]

Просмотреть файл

@ -0,0 +1,154 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const { CalEvent } = ChromeUtils.import("resource:///modules/CalEvent.jsm");
const { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
add_setup(async function() {
await CalendarTestUtils.setCalendarView(window, "day");
CalendarTestUtils.goToDate(window, 2023, 2, 18);
});
add_task(async function testPastePreformattedWithLinebreak() {
const calendar = CalendarTestUtils.createCalendar();
// Create an event which currently has no description.
const event = await calendar.addItem(
new CalEvent(CalendarTestUtils.dedent`
BEGIN:VEVENT
SUMMARY:An event
DTSTART:20230218T100000Z
DTEND:20230218T110000Z
END:VEVENT
`)
);
// Remember event details so we can refetch it after editing.
const eventId = event.id;
const eventModified = event.lastModifiedTime;
// Sanity check.
Assert.equal(event.descriptionHTML, null, "event should not have an HTML description");
Assert.equal(event.descriptionText, null, "event should not have a text description");
// Open our event for editing.
const { dialogWindow: eventWindow, iframeDocument } = await CalendarTestUtils.dayView.editEventAt(
window,
1
);
const editor = iframeDocument.getElementById("item-description");
editor.focus();
const expectedHTML =
"<pre><code>This event is one which includes\nan explicit linebreak inside a pre tag.</code></pre>";
// Create a paste which includes HTML data, which the editor will recognize as
// HTML and paste with formatting by default.
const stringData = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
stringData.data = expectedHTML;
const transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
transferable.init(null);
transferable.addDataFlavor("text/html");
transferable.setTransferData("text/html", stringData);
Services.clipboard.setData(transferable, null, Ci.nsIClipboard.kGlobalClipboard);
// Paste.
EventUtils.synthesizeKey("v", { accelKey: true }, eventWindow);
await CalendarTestUtils.items.saveAndCloseItemDialog(eventWindow);
await TestUtils.waitForCondition(async () => {
const item = await calendar.getItem(eventId);
return item.lastModifiedTime != eventModified;
});
const editedEvent = await calendar.getItem(eventId);
// Verify that the description has been set appropriately. There should be no
// change to the HTML, which is preformatted, and the text description should
// include a linebreak in the same place as the HTML.
Assert.equal(editedEvent.descriptionHTML, expectedHTML, "HTML description should match input");
Assert.equal(
editedEvent.descriptionText,
"This event is one which includes\nan explicit linebreak inside a pre tag.",
"text description should include linebreak"
);
CalendarTestUtils.removeCalendar(calendar);
});
add_task(async function testTypeLongTextWithLinebreaks() {
const calendar = CalendarTestUtils.createCalendar();
// Create an event which currently has no description.
const event = await calendar.addItem(
new CalEvent(CalendarTestUtils.dedent`
BEGIN:VEVENT
SUMMARY:An event
DTSTART:20230218T100000Z
DTEND:20230218T110000Z
END:VEVENT
`)
);
// Remember event details so we can refetch it after editing.
const eventId = event.id;
const eventModified = event.lastModifiedTime;
// Sanity check.
Assert.equal(event.descriptionHTML, null, "event should not have an HTML description");
Assert.equal(event.descriptionText, null, "event should not have a text description");
// Open our event for editing.
const {
dialogWindow: eventWindow,
iframeDocument,
iframeWindow,
} = await CalendarTestUtils.dayView.editEventAt(window, 1);
const editor = iframeDocument.getElementById("item-description");
editor.focus();
// Insert text with several long lines and explicit linebreaks.
const firstLine =
"This event is pretty much just plain text, albeit it has some pretty long lines so that we can ensure that we don't accidentally wrap it during conversion.";
EventUtils.sendString(firstLine, iframeWindow);
EventUtils.sendKey("RETURN", iframeWindow);
const secondLine = "This line follows immediately after a linebreak.";
EventUtils.sendString(secondLine, iframeWindow);
EventUtils.sendKey("RETURN", iframeWindow);
EventUtils.sendKey("RETURN", iframeWindow);
const thirdLine =
"And one after a couple more linebreaks, for good measure. It might as well be a fairly long string as well, just so we're certain.";
EventUtils.sendString(thirdLine, iframeWindow);
await CalendarTestUtils.items.saveAndCloseItemDialog(eventWindow);
await TestUtils.waitForCondition(async () => {
const item = await calendar.getItem(eventId);
return item.lastModifiedTime != eventModified;
});
const editedEvent = await calendar.getItem(eventId);
// Verify that the description has been set appropriately. The HTML should
// match the input and use <br> as a linebreak, while the text should not be
// wrapped and should use \n as a linebreak.
Assert.equal(
editedEvent.descriptionHTML,
`${firstLine}<br>${secondLine}<br><br>${thirdLine}`,
"HTML description should match input with <br> for linebreaks"
);
Assert.equal(
editedEvent.descriptionText,
`${firstLine}\n${secondLine}\n\n${thirdLine}`,
"text description should match input with linebreaks"
);
CalendarTestUtils.removeCalendar(calendar);
});