put media files under link update control
A media object whose content lives at an external URL, rather than inside the document, is registered with the link manager like a linked graphic. So it then shares the usual link update mechanism. Media copied into the document is extracted to a temp file and is not a link, so that's left alone. Change-Id: I59dadaf05567ee9d9c0492b13cbc7174aca953da Signed-off-by: Caolán McNamara <caolan.mcnamara@collabora.com> Reviewed-on: https://gerrit.collaboraoffice.com/c/core/+/6980 Tested-by: Jenkins CPCI <releng@collaboraoffice.com> Reviewed-by: Miklos Vajna <vmiklos@collabora.com> (cherry picked from commit 1ea4fc54036587c7eb4d4cea74e776de9e6baaf3) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208322 Tested-by: Jenkins Tested-by: Xisco Fauli <xiscofauli@libreoffice.org> Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
committed by
Xisco Fauli
parent
696ec888bc
commit
b45d468a3d
@@ -27,10 +27,12 @@
|
||||
namespace sdr::contact { class ViewContactOfSdrMediaObj; }
|
||||
namespace com::sun::star::graphic { class XGraphic; }
|
||||
|
||||
class SdrMediaLink;
|
||||
|
||||
class SVXCORE_DLLPUBLIC SdrMediaObj final : public SdrRectObj
|
||||
{
|
||||
friend class sdr::contact::ViewContactOfSdrMediaObj;
|
||||
friend class SdrMediaLink;
|
||||
|
||||
private:
|
||||
// protected destructor - due to final, make private
|
||||
@@ -76,6 +78,17 @@ public:
|
||||
private:
|
||||
SAL_DLLPRIVATE void mediaPropertiesChanged( const ::avmedia::MediaItem& rNewState );
|
||||
SAL_DLLPRIVATE virtual std::unique_ptr<sdr::contact::ViewContact> CreateObjectSpecificViewContact() override;
|
||||
SAL_DLLPRIVATE virtual void handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage) override;
|
||||
|
||||
// Register an external media reference as a document link, so it takes
|
||||
// part in the normal link update permission just like a linked graphic.
|
||||
// Media stored inside the document is not a link and is left alone.
|
||||
SAL_DLLPRIVATE void ImpRegisterLink();
|
||||
SAL_DLLPRIVATE void ImpDeregisterLink();
|
||||
|
||||
// Fetch a snapshot frame from the media URL. The caller decides
|
||||
// whether a fetch is allowed.
|
||||
SAL_DLLPRIVATE void grabSnapshot(const OUString& rRealURL) const;
|
||||
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> m_xImpl;
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
|
||||
xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"
|
||||
xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"
|
||||
office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.presentation">
|
||||
<office:body>
|
||||
<office:presentation>
|
||||
<draw:page draw:name="p1">
|
||||
<draw:frame svg:width="4cm" svg:height="3cm" svg:x="1cm" svg:y="1cm">
|
||||
<draw:plugin xlink:href="file:///does-not-exist/external.mp4" xlink:type="simple"
|
||||
xlink:show="embed" xlink:actuate="onLoad"
|
||||
draw:mime-type="application/vnd.sun.star.media"/>
|
||||
</draw:frame>
|
||||
</draw:page>
|
||||
</office:presentation>
|
||||
</office:body>
|
||||
</office:document>
|
||||
@@ -7,6 +7,8 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include <config_features.h>
|
||||
|
||||
#include <test/unoapi_test.hxx>
|
||||
|
||||
#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
|
||||
@@ -23,6 +25,7 @@
|
||||
#include <svx/sdr/contact/viewobjectcontact.hxx>
|
||||
#include <svx/svdmodel.hxx>
|
||||
#include <svx/svdoashp.hxx>
|
||||
#include <svx/svdomedia.hxx>
|
||||
#include <svx/svdpage.hxx>
|
||||
#include <svx/unopage.hxx>
|
||||
#include <vcl/virdev.hxx>
|
||||
@@ -210,6 +213,37 @@ CPPUNIT_TEST_FIXTURE(SdrTest, testSlideBackground)
|
||||
assertXPath(pDocument, "//bitmap", 1);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(SdrTest, testMediaLinkNotFetchedWhenUpdatesDisallowed)
|
||||
{
|
||||
// A media object referenced by an external xlink:href, with no copy of
|
||||
// the media stored inside the document.
|
||||
loadFromFile(u"media-link.fodp");
|
||||
uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
|
||||
uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
|
||||
uno::UNO_QUERY);
|
||||
auto* pSvxDrawPage = dynamic_cast<SvxDrawPage*>(xDrawPage.get());
|
||||
CPPUNIT_ASSERT(pSvxDrawPage);
|
||||
auto* pMedia = dynamic_cast<SdrMediaObj*>(pSvxDrawPage->GetSdrPage()->GetObj(0));
|
||||
CPPUNIT_ASSERT(pMedia);
|
||||
|
||||
// With link updates disallowed, as when loading or converting a document
|
||||
// without a user present to approve them, asking for the placeholder image
|
||||
// must not reach out to the external URL. Without the fix the snapshot is
|
||||
// generated (an empty placeholder bitmap even where no media backend is built,
|
||||
// and the referenced content where one is), so the returned graphic is set.
|
||||
SfxObjectShell* pShell = SfxObjectShell::GetShellFromComponent(mxComponent);
|
||||
CPPUNIT_ASSERT(pShell);
|
||||
pShell->getEmbeddedObjectContainer().setUserAllowsLinkUpdate(false);
|
||||
CPPUNIT_ASSERT(!pMedia->getSnapshot().is());
|
||||
|
||||
#if HAVE_FEATURE_AVMEDIA
|
||||
// Once updates are allowed the same request produces a graphic, so it was
|
||||
// the permission alone that blocked the fetch above.
|
||||
pShell->getEmbeddedObjectContainer().setUserAllowsLinkUpdate(true);
|
||||
CPPUNIT_ASSERT(pMedia->getSnapshot().is());
|
||||
#endif
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(SdrTest, test3DRotatedText)
|
||||
{
|
||||
// The document contains a shape with text "Vertical" and a 3D scene camera rotation of 90 deg.
|
||||
|
||||
+140
-14
@@ -22,6 +22,7 @@
|
||||
#include <svx/svdomedia.hxx>
|
||||
|
||||
#include <com/sun/star/text/GraphicCrop.hpp>
|
||||
#include <com/sun/star/uno/Any.hxx>
|
||||
|
||||
#include <rtl/ustring.hxx>
|
||||
#include <sal/log.hxx>
|
||||
@@ -38,10 +39,29 @@
|
||||
#include <sdr/contact/viewcontactofsdrmediaobj.hxx>
|
||||
#include <avmedia/mediawindow.hxx>
|
||||
#include <comphelper/diagnose_ex.hxx>
|
||||
#include <comphelper/embeddedobjectcontainer.hxx>
|
||||
#include <sfx2/lnkbase.hxx>
|
||||
#include <sfx2/linkmgr.hxx>
|
||||
#include <sfx2/objsh.hxx>
|
||||
|
||||
using namespace ::com::sun::star;
|
||||
|
||||
|
||||
// A media object that points at an external URL is a document link. Registering
|
||||
// it with the link manager lets it share the normal link update permission: the
|
||||
// snapshot is only fetched from the URL once the user has allowed link updates.
|
||||
class SdrMediaLink final : public sfx2::SvBaseLink
|
||||
{
|
||||
SdrMediaObj& mrMediaObj;
|
||||
|
||||
public:
|
||||
explicit SdrMediaLink(SdrMediaObj& rObj);
|
||||
|
||||
virtual void Closed() override;
|
||||
virtual ::sfx2::SvBaseLink::UpdateResult DataChanged(
|
||||
const OUString& rMimeType, const css::uno::Any& rValue) override;
|
||||
};
|
||||
|
||||
struct SdrMediaObj::Impl
|
||||
{
|
||||
::avmedia::MediaItem m_MediaProperties;
|
||||
@@ -54,8 +74,41 @@ struct SdrMediaObj::Impl
|
||||
#endif
|
||||
uno::Reference< graphic::XGraphic > m_xCachedSnapshot;
|
||||
OUString m_LastFailedPkgURL;
|
||||
// owned by the link manager while registered, null otherwise
|
||||
SdrMediaLink* m_pMediaLink = nullptr;
|
||||
};
|
||||
|
||||
SdrMediaLink::SdrMediaLink(SdrMediaObj& rObj)
|
||||
: ::sfx2::SvBaseLink(::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SIMPLE_FILE)
|
||||
, mrMediaObj(rObj)
|
||||
{
|
||||
SetSynchron(false);
|
||||
}
|
||||
|
||||
::sfx2::SvBaseLink::UpdateResult SdrMediaLink::DataChanged(
|
||||
const OUString&, const css::uno::Any&)
|
||||
{
|
||||
// Reached only after a link update was allowed, so drop the placeholder
|
||||
// and fetch a fresh snapshot now, like a linked graphic refetches.
|
||||
mrMediaObj.m_xImpl->m_xCachedSnapshot.clear();
|
||||
#if HAVE_FEATURE_AVMEDIA
|
||||
mrMediaObj.m_xImpl->m_xPlayerListener.clear();
|
||||
OUString aRealURL = mrMediaObj.m_xImpl->m_MediaProperties.getTempURL();
|
||||
if (aRealURL.isEmpty())
|
||||
aRealURL = mrMediaObj.m_xImpl->m_MediaProperties.getURL();
|
||||
if (!aRealURL.isEmpty())
|
||||
mrMediaObj.grabSnapshot(aRealURL);
|
||||
#endif
|
||||
mrMediaObj.ActionChanged();
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
void SdrMediaLink::Closed()
|
||||
{
|
||||
mrMediaObj.m_xImpl->m_pMediaLink = nullptr;
|
||||
SvBaseLink::Closed();
|
||||
}
|
||||
|
||||
SdrMediaObj::SdrMediaObj(SdrModel& rSdrModel)
|
||||
: SdrRectObj(rSdrModel)
|
||||
,m_xImpl( new Impl )
|
||||
@@ -91,6 +144,7 @@ SdrMediaObj::SdrMediaObj(
|
||||
|
||||
SdrMediaObj::~SdrMediaObj()
|
||||
{
|
||||
ImpDeregisterLink();
|
||||
}
|
||||
|
||||
bool SdrMediaObj::HasTextEdit() const
|
||||
@@ -182,26 +236,47 @@ uno::Reference< graphic::XGraphic > const & SdrMediaObj::getSnapshot() const
|
||||
|
||||
OUString aRealURL = m_xImpl->m_MediaProperties.getTempURL();
|
||||
if( aRealURL.isEmpty() )
|
||||
{
|
||||
// No extracted copy inside the document, so the media is reached
|
||||
// through its URL. Treat it like a linked graphic and only fetch
|
||||
// once the user has allowed link updates, so opening or converting
|
||||
// a document never silently fetches the referenced content. A
|
||||
// model with no document shell has no user to ask, so it does not
|
||||
// fetch either.
|
||||
sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager());
|
||||
SfxObjectShell* pShell = pLinkManager ? pLinkManager->GetPersist() : nullptr;
|
||||
if (!pShell || !pShell->getEmbeddedObjectContainer().getUserAllowsLinkUpdate())
|
||||
return m_xImpl->m_xCachedSnapshot;
|
||||
aRealURL = m_xImpl->m_MediaProperties.getURL();
|
||||
OUString sReferer = m_xImpl->m_MediaProperties.getReferer();
|
||||
OUString sMimeType = m_xImpl->m_MediaProperties.getMimeType();
|
||||
uno::Reference<graphic::XGraphic> xCachedSnapshot = m_xImpl->m_xCachedSnapshot;
|
||||
|
||||
m_xImpl->m_xPlayerListener.set(new avmedia::PlayerListener(
|
||||
[this, xCachedSnapshot, aRealURL, sReferer, sMimeType](const css::uno::Reference<css::media::XPlayer>& rPlayer){
|
||||
SolarMutexGuard g;
|
||||
uno::Reference<graphic::XGraphic> xGraphic
|
||||
= m_xImpl->m_MediaProperties.getGraphic().GetXGraphic();
|
||||
m_xImpl->m_xCachedSnapshot = avmedia::MediaWindow::grabFrame(rPlayer, xGraphic);
|
||||
ActionChanged();
|
||||
}));
|
||||
|
||||
avmedia::MediaWindow::grabFrame(aRealURL, sReferer, sMimeType, m_xImpl->m_xPlayerListener);
|
||||
}
|
||||
grabSnapshot(aRealURL);
|
||||
}
|
||||
#endif
|
||||
return m_xImpl->m_xCachedSnapshot;
|
||||
}
|
||||
|
||||
void SdrMediaObj::grabSnapshot(const OUString& rRealURL) const
|
||||
{
|
||||
#if HAVE_FEATURE_AVMEDIA
|
||||
OUString sReferer = m_xImpl->m_MediaProperties.getReferer();
|
||||
OUString sMimeType = m_xImpl->m_MediaProperties.getMimeType();
|
||||
uno::Reference<graphic::XGraphic> xCachedSnapshot = m_xImpl->m_xCachedSnapshot;
|
||||
|
||||
m_xImpl->m_xPlayerListener.set(new avmedia::PlayerListener(
|
||||
[this, xCachedSnapshot, rRealURL, sReferer, sMimeType](const css::uno::Reference<css::media::XPlayer>& rPlayer){
|
||||
SolarMutexGuard g;
|
||||
uno::Reference<graphic::XGraphic> xGraphic
|
||||
= m_xImpl->m_MediaProperties.getGraphic().GetXGraphic();
|
||||
m_xImpl->m_xCachedSnapshot = avmedia::MediaWindow::grabFrame(rPlayer, xGraphic);
|
||||
ActionChanged();
|
||||
}));
|
||||
|
||||
avmedia::MediaWindow::grabFrame(rRealURL, sReferer, sMimeType, m_xImpl->m_xPlayerListener);
|
||||
#else
|
||||
(void)rRealURL;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SdrMediaObj::AdjustToMaxRect( const tools::Rectangle& rMaxRect, bool bShrinkOnly /* = false */ )
|
||||
{
|
||||
Size aSize( Application::GetDefaultDevice()->PixelToLogic(
|
||||
@@ -400,6 +475,8 @@ void SdrMediaObj::mediaPropertiesChanged( const ::avmedia::MediaItem& rNewProper
|
||||
{
|
||||
m_xImpl->m_xCachedSnapshot.clear();
|
||||
m_xImpl->m_xPlayerListener.clear();
|
||||
// the URL is changing, so any link registered for the old one is stale
|
||||
ImpDeregisterLink();
|
||||
m_xImpl->m_MediaProperties.setFallbackURL( rNewProperties.getFallbackURL() );
|
||||
OUString const& url(rNewProperties.getURL());
|
||||
if (url.startsWithIgnoreAsciiCase("vnd.sun.star.Package:"))
|
||||
@@ -441,6 +518,7 @@ void SdrMediaObj::mediaPropertiesChanged( const ::avmedia::MediaItem& rNewProper
|
||||
m_xImpl->m_pTempFile.reset();
|
||||
m_xImpl->m_MediaProperties.setURL(url, u""_ustr, rNewProperties.getReferer());
|
||||
}
|
||||
ImpRegisterLink();
|
||||
bBroadcastChanged = true;
|
||||
}
|
||||
|
||||
@@ -466,4 +544,52 @@ void SdrMediaObj::mediaPropertiesChanged( const ::avmedia::MediaItem& rNewProper
|
||||
}
|
||||
}
|
||||
|
||||
void SdrMediaObj::ImpRegisterLink()
|
||||
{
|
||||
sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager());
|
||||
if (!pLinkManager || m_xImpl->m_pMediaLink)
|
||||
return;
|
||||
|
||||
#if HAVE_FEATURE_AVMEDIA
|
||||
// Media copied into the document is extracted to a temporary file and is
|
||||
// not a link. Only an external reference gets registered.
|
||||
if (m_xImpl->m_pTempFile)
|
||||
return;
|
||||
#endif
|
||||
const OUString& rURL = m_xImpl->m_MediaProperties.getURL();
|
||||
if (rURL.isEmpty() || rURL.startsWithIgnoreAsciiCase("vnd.sun.star.Package:"))
|
||||
return;
|
||||
|
||||
// Registration happens even in a build without media playback, so the
|
||||
// external reference stays visible to the user as a document link.
|
||||
m_xImpl->m_pMediaLink = new SdrMediaLink(*this);
|
||||
pLinkManager->InsertFileLink(*m_xImpl->m_pMediaLink,
|
||||
sfx2::SvBaseLinkObjectType::ClientFile, rURL);
|
||||
}
|
||||
|
||||
void SdrMediaObj::ImpDeregisterLink()
|
||||
{
|
||||
sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager());
|
||||
if (pLinkManager && m_xImpl->m_pMediaLink)
|
||||
{
|
||||
// Remove implicitly deletes the link object
|
||||
pLinkManager->Remove(m_xImpl->m_pMediaLink);
|
||||
m_xImpl->m_pMediaLink = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SdrMediaObj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
|
||||
{
|
||||
const bool bRemove(pNewPage == nullptr && pOldPage != nullptr);
|
||||
const bool bInsert(pNewPage != nullptr && pOldPage == nullptr);
|
||||
|
||||
if (bRemove)
|
||||
ImpDeregisterLink();
|
||||
|
||||
SdrRectObj::handlePageChange(pOldPage, pNewPage);
|
||||
|
||||
if (bInsert)
|
||||
ImpRegisterLink();
|
||||
}
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
||||
|
||||
Reference in New Issue
Block a user