cool#8465 clipboard: improve handling of plain text copy, complex case
In case the selection is complex (not simple), we used to just request
HTML, and then the browser converted that to plain text, which has the
downsides already mentioned in commit
7f9de46688a64b42ba8f65cceb9fe2c6ddab89ef (cool#8465 clipboard: improve
handling of plain text copy, simple case, 2024-03-08).
Steps to support this:
1) Clipboard.js now asks for the text/html,text/plain;charset=utf-8 MIME
types.
2) wsd: ClientRequestDispatcher::handleClipboardRequest() now maps this
to DocumentBroker::CLIP_REQUEST_GET_HTML_PLAIN_ONLY
3) ClientSession::handleClipboardRequest() maps this to the HTML+plain
text MIME type list.
4) kit: ChildSession::getClipboard() is now improved to take a list of
MIME types, not just 1 or everything.
5) kit: ChildSession::getClipboard() now emits JSON in case not all, but
multiple MIME types are requested.
6) wsd: ClientSession::postProcessCopyPayload() now knows how to
postprocess clipboardcontent messages, which may or may not be JSON
(it's JSON if more formats are requested explicitly, leaving the 1
format or all format cases unchanged)
7) Control.DownloadProgress.js now handles the case when we get JSON and
sets the core-provided plain text next to the HTML.
Leave the handling of non-JSON case in, because this means we can
copy from an old COOL server to a new one.
Note that this approach has the benefit that once the clipboard marker
is inserted, the length of the text/html format would change, which
means we can't parse the clipboard data till the marker is removed.
Emitting JSON for html+text means adding the marker keeps the ability to
parse the HTML and the plain text part of the clipboard in JS.
Signed-off-by: Miklos Vajna <vmiklos@collabora.com>
Change-Id: I67a1f669e8a638d34cc25a2f288a7b30884b9892
2024-03-19 06:12:16 -05:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
|
|
|
/*
|
|
|
|
* Copyright the Collabora Online contributors.
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: MPL-2.0
|
|
|
|
*
|
|
|
|
* 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/.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Test various copy/paste pieces ...
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
2024-03-20 07:42:03 -05:00
|
|
|
#include <HttpRequest.hpp>
|
cool#8465 clipboard: improve handling of plain text copy, complex case
In case the selection is complex (not simple), we used to just request
HTML, and then the browser converted that to plain text, which has the
downsides already mentioned in commit
7f9de46688a64b42ba8f65cceb9fe2c6ddab89ef (cool#8465 clipboard: improve
handling of plain text copy, simple case, 2024-03-08).
Steps to support this:
1) Clipboard.js now asks for the text/html,text/plain;charset=utf-8 MIME
types.
2) wsd: ClientRequestDispatcher::handleClipboardRequest() now maps this
to DocumentBroker::CLIP_REQUEST_GET_HTML_PLAIN_ONLY
3) ClientSession::handleClipboardRequest() maps this to the HTML+plain
text MIME type list.
4) kit: ChildSession::getClipboard() is now improved to take a list of
MIME types, not just 1 or everything.
5) kit: ChildSession::getClipboard() now emits JSON in case not all, but
multiple MIME types are requested.
6) wsd: ClientSession::postProcessCopyPayload() now knows how to
postprocess clipboardcontent messages, which may or may not be JSON
(it's JSON if more formats are requested explicitly, leaving the 1
format or all format cases unchanged)
7) Control.DownloadProgress.js now handles the case when we get JSON and
sets the core-provided plain text next to the HTML.
Leave the handling of non-JSON case in, because this means we can
copy from an old COOL server to a new one.
Note that this approach has the benefit that once the clipboard marker
is inserted, the length of the text/html format would change, which
means we can't parse the clipboard data till the marker is removed.
Emitting JSON for html+text means adding the marker keeps the ability to
parse the HTML and the plain text part of the clipboard in JS.
Signed-off-by: Miklos Vajna <vmiklos@collabora.com>
Change-Id: I67a1f669e8a638d34cc25a2f288a7b30884b9892
2024-03-19 06:12:16 -05:00
|
|
|
#include <Unit.hpp>
|
2024-03-20 07:42:03 -05:00
|
|
|
#include <WebSocketSession.hpp>
|
cool#8465 clipboard: improve handling of plain text copy, complex case
In case the selection is complex (not simple), we used to just request
HTML, and then the browser converted that to plain text, which has the
downsides already mentioned in commit
7f9de46688a64b42ba8f65cceb9fe2c6ddab89ef (cool#8465 clipboard: improve
handling of plain text copy, simple case, 2024-03-08).
Steps to support this:
1) Clipboard.js now asks for the text/html,text/plain;charset=utf-8 MIME
types.
2) wsd: ClientRequestDispatcher::handleClipboardRequest() now maps this
to DocumentBroker::CLIP_REQUEST_GET_HTML_PLAIN_ONLY
3) ClientSession::handleClipboardRequest() maps this to the HTML+plain
text MIME type list.
4) kit: ChildSession::getClipboard() is now improved to take a list of
MIME types, not just 1 or everything.
5) kit: ChildSession::getClipboard() now emits JSON in case not all, but
multiple MIME types are requested.
6) wsd: ClientSession::postProcessCopyPayload() now knows how to
postprocess clipboardcontent messages, which may or may not be JSON
(it's JSON if more formats are requested explicitly, leaving the 1
format or all format cases unchanged)
7) Control.DownloadProgress.js now handles the case when we get JSON and
sets the core-provided plain text next to the HTML.
Leave the handling of non-JSON case in, because this means we can
copy from an old COOL server to a new one.
Note that this approach has the benefit that once the clipboard marker
is inserted, the length of the text/html format would change, which
means we can't parse the clipboard data till the marker is removed.
Emitting JSON for html+text means adding the marker keeps the ability to
parse the HTML and the plain text part of the clipboard in JS.
Signed-off-by: Miklos Vajna <vmiklos@collabora.com>
Change-Id: I67a1f669e8a638d34cc25a2f288a7b30884b9892
2024-03-19 06:12:16 -05:00
|
|
|
#include <common/JsonUtil.hpp>
|
|
|
|
#include <helpers.hpp>
|
|
|
|
#include <lokassert.hpp>
|
2024-03-20 07:42:03 -05:00
|
|
|
#include <wsd/COOLWSD.hpp>
|
|
|
|
#include <wsd/ClientSession.hpp>
|
cool#8465 clipboard: improve handling of plain text copy, complex case
In case the selection is complex (not simple), we used to just request
HTML, and then the browser converted that to plain text, which has the
downsides already mentioned in commit
7f9de46688a64b42ba8f65cceb9fe2c6ddab89ef (cool#8465 clipboard: improve
handling of plain text copy, simple case, 2024-03-08).
Steps to support this:
1) Clipboard.js now asks for the text/html,text/plain;charset=utf-8 MIME
types.
2) wsd: ClientRequestDispatcher::handleClipboardRequest() now maps this
to DocumentBroker::CLIP_REQUEST_GET_HTML_PLAIN_ONLY
3) ClientSession::handleClipboardRequest() maps this to the HTML+plain
text MIME type list.
4) kit: ChildSession::getClipboard() is now improved to take a list of
MIME types, not just 1 or everything.
5) kit: ChildSession::getClipboard() now emits JSON in case not all, but
multiple MIME types are requested.
6) wsd: ClientSession::postProcessCopyPayload() now knows how to
postprocess clipboardcontent messages, which may or may not be JSON
(it's JSON if more formats are requested explicitly, leaving the 1
format or all format cases unchanged)
7) Control.DownloadProgress.js now handles the case when we get JSON and
sets the core-provided plain text next to the HTML.
Leave the handling of non-JSON case in, because this means we can
copy from an old COOL server to a new one.
Note that this approach has the benefit that once the clipboard marker
is inserted, the length of the text/html format would change, which
means we can't parse the clipboard data till the marker is removed.
Emitting JSON for html+text means adding the marker keeps the ability to
parse the HTML and the plain text part of the clipboard in JS.
Signed-off-by: Miklos Vajna <vmiklos@collabora.com>
Change-Id: I67a1f669e8a638d34cc25a2f288a7b30884b9892
2024-03-19 06:12:16 -05:00
|
|
|
|
|
|
|
using namespace Poco::Net;
|
|
|
|
|
|
|
|
std::shared_ptr<ClientSession> getChildSession(size_t session)
|
|
|
|
{
|
|
|
|
std::shared_ptr<DocumentBroker> broker;
|
|
|
|
std::shared_ptr<ClientSession> clientSession;
|
|
|
|
|
|
|
|
std::vector<std::shared_ptr<DocumentBroker>> brokers = COOLWSD::getBrokersTestOnly();
|
|
|
|
assert(brokers.size() > 0);
|
|
|
|
broker = brokers[0];
|
|
|
|
auto sessions = broker->getSessionsTestOnlyUnsafe();
|
|
|
|
assert(sessions.size() > 0 && session < sessions.size());
|
|
|
|
return sessions[session];
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getSessionClipboardURI(size_t session)
|
|
|
|
{
|
|
|
|
std::shared_ptr<ClientSession> clientSession = getChildSession(session);
|
|
|
|
|
|
|
|
std::string tag = clientSession->getClipboardURI(false); // nominally thread unsafe
|
|
|
|
return tag;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Inside the WSD process
|
|
|
|
class UnitCopyPasteWriter : public UnitWSD
|
|
|
|
{
|
|
|
|
STATE_ENUM(Phase, RunTest, WaitDocClose, PostCloseTest, Done) _phase;
|
|
|
|
|
|
|
|
public:
|
|
|
|
UnitCopyPasteWriter()
|
|
|
|
: UnitWSD("UnitCopyPasteWriter")
|
|
|
|
, _phase(Phase::RunTest)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void onDocBrokerDestroy(const std::string& /*docKey*/) override
|
|
|
|
{
|
|
|
|
LOK_ASSERT_STATE(_phase, Phase::WaitDocClose);
|
|
|
|
TRANSITION_STATE(_phase, Phase::PostCloseTest);
|
|
|
|
}
|
|
|
|
|
|
|
|
void runTest()
|
|
|
|
{
|
|
|
|
// Given a Writer document with bullets:
|
|
|
|
std::string documentPath, documentURL;
|
|
|
|
helpers::getDocumentPathAndURL("bullets.odt", documentPath, documentURL, testname);
|
|
|
|
std::shared_ptr<http::WebSocketSession> socket = helpers::loadDocAndGetSession(
|
|
|
|
socketPoll(), Poco::URI(helpers::getTestServerURI()), documentURL, testname);
|
|
|
|
helpers::sendTextFrame(socket, "uno .uno:SelectAll", testname);
|
|
|
|
|
|
|
|
// When copying the cnotent of the document:
|
|
|
|
helpers::sendAndDrain(socket, testname, "uno .uno:Copy", "statechanged:");
|
|
|
|
|
|
|
|
// Then make sure asking for multiple, specific formats results in a JSON answer, it's what
|
|
|
|
// JS expects:
|
2024-04-02 09:06:45 -05:00
|
|
|
{
|
|
|
|
std::string clipURI = getSessionClipboardURI(0);
|
|
|
|
clipURI += "&MimeType=text/html,text/plain;charset=utf-8";
|
|
|
|
std::shared_ptr<http::Session> httpSession = http::Session::create(clipURI);
|
|
|
|
std::shared_ptr<const http::Response> httpResponse =
|
|
|
|
httpSession->syncRequest(http::Request(Poco::URI(clipURI).getPathAndQuery()));
|
|
|
|
LOK_ASSERT_EQUAL(http::StatusCode::OK, httpResponse->statusLine().statusCode());
|
|
|
|
std::string body = httpResponse->getBody();
|
|
|
|
Poco::JSON::Object::Ptr object;
|
|
|
|
// This failed, we didn't return JSON.
|
|
|
|
LOK_ASSERT(JsonUtil::parseJSON(body, object));
|
|
|
|
LOK_ASSERT(object->has("text/html"));
|
|
|
|
std::string expectedPlainText(" • first\n • second\n • third");
|
|
|
|
std::string actualPlainText = object->get("text/plain;charset=utf-8").toString();
|
|
|
|
LOK_ASSERT_EQUAL(actualPlainText, expectedPlainText);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now also test HTML only:
|
|
|
|
{
|
|
|
|
std::string clipURI = getSessionClipboardURI(0);
|
|
|
|
clipURI += "&MimeType=text/html";
|
|
|
|
std::shared_ptr<http::Session> httpSession = http::Session::create(clipURI);
|
|
|
|
std::shared_ptr<const http::Response> httpResponse =
|
|
|
|
httpSession->syncRequest(http::Request(Poco::URI(clipURI).getPathAndQuery()));
|
|
|
|
LOK_ASSERT_EQUAL(http::StatusCode::OK, httpResponse->statusLine().statusCode());
|
|
|
|
std::string body = httpResponse->getBody();
|
|
|
|
Poco::JSON::Object::Ptr object;
|
|
|
|
LOK_ASSERT(JsonUtil::parseJSON(body, object));
|
|
|
|
LOK_ASSERT(object->has("text/html"));
|
|
|
|
}
|
|
|
|
|
cool#8465 clipboard: improve handling of plain text copy, complex case
In case the selection is complex (not simple), we used to just request
HTML, and then the browser converted that to plain text, which has the
downsides already mentioned in commit
7f9de46688a64b42ba8f65cceb9fe2c6ddab89ef (cool#8465 clipboard: improve
handling of plain text copy, simple case, 2024-03-08).
Steps to support this:
1) Clipboard.js now asks for the text/html,text/plain;charset=utf-8 MIME
types.
2) wsd: ClientRequestDispatcher::handleClipboardRequest() now maps this
to DocumentBroker::CLIP_REQUEST_GET_HTML_PLAIN_ONLY
3) ClientSession::handleClipboardRequest() maps this to the HTML+plain
text MIME type list.
4) kit: ChildSession::getClipboard() is now improved to take a list of
MIME types, not just 1 or everything.
5) kit: ChildSession::getClipboard() now emits JSON in case not all, but
multiple MIME types are requested.
6) wsd: ClientSession::postProcessCopyPayload() now knows how to
postprocess clipboardcontent messages, which may or may not be JSON
(it's JSON if more formats are requested explicitly, leaving the 1
format or all format cases unchanged)
7) Control.DownloadProgress.js now handles the case when we get JSON and
sets the core-provided plain text next to the HTML.
Leave the handling of non-JSON case in, because this means we can
copy from an old COOL server to a new one.
Note that this approach has the benefit that once the clipboard marker
is inserted, the length of the text/html format would change, which
means we can't parse the clipboard data till the marker is removed.
Emitting JSON for html+text means adding the marker keeps the ability to
parse the HTML and the plain text part of the clipboard in JS.
Signed-off-by: Miklos Vajna <vmiklos@collabora.com>
Change-Id: I67a1f669e8a638d34cc25a2f288a7b30884b9892
2024-03-19 06:12:16 -05:00
|
|
|
TRANSITION_STATE(_phase, Phase::WaitDocClose);
|
|
|
|
socket->asyncShutdown();
|
|
|
|
LOK_ASSERT(socket->waitForDisconnection(std::chrono::seconds(5)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void invokeWSDTest() override
|
|
|
|
{
|
|
|
|
switch (_phase)
|
|
|
|
{
|
|
|
|
case Phase::RunTest:
|
|
|
|
{
|
|
|
|
runTest();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Phase::WaitDocClose:
|
|
|
|
break;
|
|
|
|
case Phase::PostCloseTest:
|
|
|
|
TRANSITION_STATE(_phase, Phase::Done);
|
|
|
|
passTest();
|
|
|
|
break;
|
|
|
|
case Phase::Done:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
UnitBase* unit_create_wsd(void) { return new UnitCopyPasteWriter(); }
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|