diff options
Diffstat (limited to 'embeddedobj/source/msole')
24 files changed, 8390 insertions, 0 deletions
diff --git a/embeddedobj/source/msole/advisesink.cxx b/embeddedobj/source/msole/advisesink.cxx new file mode 100644 index 0000000000..5601c8d9eb --- /dev/null +++ b/embeddedobj/source/msole/advisesink.cxx @@ -0,0 +1,124 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <osl/diagnose.h> +#include "advisesink.hxx" +#include "olecomponent.hxx" + +#include <rtl/ref.hxx> + +OleWrapperAdviseSink::OleWrapperAdviseSink( OleComponent* pOleComp ) +: m_nRefCount( 0 ) +, m_pOleComp( pOleComp ) +{ + OSL_ENSURE( m_pOleComp, "No ole component is provided!" ); +} + +OleWrapperAdviseSink::~OleWrapperAdviseSink() +{ +} + +STDMETHODIMP OleWrapperAdviseSink::QueryInterface( REFIID riid , void** ppv ) +{ + *ppv=nullptr; + + if ( riid == IID_IUnknown ) + *ppv = static_cast<IUnknown*>(this); + + if ( riid == IID_IAdviseSink ) + *ppv = static_cast<IAdviseSink*>(this); + + if ( *ppv != nullptr ) + { + static_cast<IUnknown*>(*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) OleWrapperAdviseSink::AddRef() +{ + return osl_atomic_increment( &m_nRefCount); +} + +STDMETHODIMP_(ULONG) OleWrapperAdviseSink::Release() +{ + ULONG nReturn = --m_nRefCount; + if ( m_nRefCount == 0 ) + delete this; + + return nReturn; +} + +void OleWrapperAdviseSink::disconnectOleComponent() +{ + // must not be called from the descructor of OleComponent!!! + osl::MutexGuard aGuard( m_aMutex ); + m_pOleComp = nullptr; +} + +STDMETHODIMP_(void) OleWrapperAdviseSink::OnDataChange(FORMATETC *, STGMEDIUM *) +{ + // Unused for now ( no registration for IDataObject events ) +} + +STDMETHODIMP_(void) OleWrapperAdviseSink::OnViewChange(DWORD dwAspect, LONG) +{ + ::rtl::Reference< OleComponent > xLockComponent; + + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pOleComp ) + xLockComponent = m_pOleComp; + } + + if ( xLockComponent.is() ) + xLockComponent->OnViewChange_Impl( dwAspect ); +} + +STDMETHODIMP_(void) OleWrapperAdviseSink::OnRename(IMoniker *) +{ + // handled by default inprocess handler +} + +STDMETHODIMP_(void) OleWrapperAdviseSink::OnSave() +{ + // TODO: ??? + // The object already knows about document saving, since it controls it as ClientSide + // other interested listeners must be registered for the object +} + +STDMETHODIMP_(void) OleWrapperAdviseSink::OnClose() +{ + ::rtl::Reference< OleComponent > xLockComponent; + + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pOleComp ) + xLockComponent = m_pOleComp; + } + + if ( xLockComponent.is() ) + xLockComponent->OnClose_Impl(); + + // TODO: sometimes it can be necessary to simulate OnShowWindow( False ) here +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/advisesink.hxx b/embeddedobj/source/msole/advisesink.hxx new file mode 100644 index 0000000000..cd1bc38928 --- /dev/null +++ b/embeddedobj/source/msole/advisesink.hxx @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <osl/interlck.h> +#include <osl/mutex.hxx> +#include "platform.h" + +class OleComponent; +class OleWrapperAdviseSink : public IAdviseSink +{ +protected: + osl::Mutex m_aMutex; + oslInterlockedCount m_nRefCount; + OleComponent* m_pOleComp; + +public: + OleWrapperAdviseSink(OleComponent* pOleComp); + OleWrapperAdviseSink(); + virtual ~OleWrapperAdviseSink(); + + void disconnectOleComponent(); + STDMETHODIMP QueryInterface(REFIID, void**) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + STDMETHODIMP_(void) OnDataChange(FORMATETC*, STGMEDIUM*) override; + STDMETHODIMP_(void) OnViewChange(DWORD, LONG) override; + STDMETHODIMP_(void) OnRename(IMoniker*) override; + STDMETHODIMP_(void) OnSave() override; + STDMETHODIMP_(void) OnClose() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/closepreventer.cxx b/embeddedobj/source/msole/closepreventer.cxx new file mode 100644 index 0000000000..4669758982 --- /dev/null +++ b/embeddedobj/source/msole/closepreventer.cxx @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <closepreventer.hxx> + +#include <com/sun/star/util/CloseVetoException.hpp> +#include <osl/diagnose.h> + +void SAL_CALL OClosePreventer::queryClosing(const css::lang::EventObject&, sal_Bool) +{ + throw css::util::CloseVetoException(); +} + +void SAL_CALL OClosePreventer::notifyClosing(const css::lang::EventObject&) +{ + // just a disaster + OSL_FAIL("The object can not be prevented from closing!"); +} + +void SAL_CALL OClosePreventer::disposing(const css::lang::EventObject&) +{ + // just a disaster + OSL_FAIL("The object can not be prevented from closing!"); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/emboleobj.component b/embeddedobj/source/msole/emboleobj.component new file mode 100644 index 0000000000..ac09d3a6d3 --- /dev/null +++ b/embeddedobj/source/msole/emboleobj.component @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.embed.OLEEmbeddedObjectFactory" + constructor="embeddedobj_OleEmbeddedObjectFactory_get_implementation" + single-instance="true"> + <service name="com.sun.star.comp.embed.OLEEmbeddedObjectFactory"/> + <service name="com.sun.star.embed.OLEEmbeddedObjectFactory"/> + </implementation> +</component> diff --git a/embeddedobj/source/msole/emboleobj.windows.component b/embeddedobj/source/msole/emboleobj.windows.component new file mode 100644 index 0000000000..89b644fc79 --- /dev/null +++ b/embeddedobj/source/msole/emboleobj.windows.component @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.embed.MSOLEObjectSystemCreator" + constructor="embeddedobj_MSOLEDialogObjectCreator_get_implementation" + single-instance="true"> + <service name="com.sun.star.comp.embed.MSOLEObjectSystemCreator"/> + <service name="com.sun.star.embed.MSOLEObjectSystemCreator"/> + </implementation> + <implementation name="com.sun.star.comp.embed.OLEEmbeddedObjectFactory" + constructor="embeddedobj_OleEmbeddedObjectFactory_get_implementation" + single-instance="true"> + <service name="com.sun.star.comp.embed.OLEEmbeddedObjectFactory"/> + <service name="com.sun.star.embed.OLEEmbeddedObjectFactory"/> + </implementation> +</component> diff --git a/embeddedobj/source/msole/graphconvert.cxx b/embeddedobj/source/msole/graphconvert.cxx new file mode 100644 index 0000000000..f853f03dc1 --- /dev/null +++ b/embeddedobj/source/msole/graphconvert.cxx @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <unotools/streamwrap.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/seqstream.hxx> +#include <tools/stream.hxx> +#include <vcl/graphicfilter.hxx> + +#include "graphconvert.hxx" +#include "mtnotification.hxx" +#include <oleembobj.hxx> + + +using namespace ::com::sun::star; + + +bool ConvertBufferToFormat( void* pBuf, + sal_uInt32 nBufSize, + const OUString& aMimeType, + uno::Any& aResult ) +{ + // produces sequence with data in requested format and returns it in aResult + if ( pBuf ) + { + // First, in case the buffer is already in the requested format, then avoid a conversion. + SvMemoryStream aMemoryStream(pBuf, nBufSize, StreamMode::READ); + GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter(); + sal_uInt16 nRetFormat = 0; + if (rFilter.CanImportGraphic(u"", aMemoryStream, GRFILTER_FORMAT_DONTKNOW, &nRetFormat) == ERRCODE_NONE && + rFilter.GetImportFormatMediaType(nRetFormat) == aMimeType) + { + aResult <<= uno::Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemoryStream.GetData() ), aMemoryStream.TellEnd() ); + return true; + } + + uno::Sequence < sal_Int8 > aData( static_cast<sal_Int8*>(pBuf), nBufSize ); + uno::Reference < io::XInputStream > xIn = new comphelper::SequenceInputStream( aData ); + try + { + uno::Reference < graphic::XGraphicProvider > xGraphicProvider( graphic::GraphicProvider::create(comphelper::getProcessComponentContext())); + uno::Sequence< beans::PropertyValue > aMediaProperties{ comphelper::makePropertyValue( + "InputStream", xIn) }; + uno::Reference< graphic::XGraphic > xGraphic( xGraphicProvider->queryGraphic( aMediaProperties ) ); + if( xGraphic.is() ) + { + SvMemoryStream aNewStream( 65535, 65535 ); + uno::Reference < io::XStream > xOut = new utl::OStreamWrapper( aNewStream ); + uno::Sequence< beans::PropertyValue > aOutMediaProperties{ + comphelper::makePropertyValue("OutputStream", xOut), + comphelper::makePropertyValue("MimeType", aMimeType) + }; + + xGraphicProvider->storeGraphic( xGraphic, aOutMediaProperties ); + aResult <<= uno::Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aNewStream.GetData() ), aNewStream.TellEnd() ); + return true; + } + } + catch (const uno::Exception&) + {} + } + + return false; +} + + +// MainThreadNotificationRequest + +MainThreadNotificationRequest::MainThreadNotificationRequest( const ::rtl::Reference< OleEmbeddedObject >& xObj, sal_uInt16 nNotificationType, sal_uInt32 nAspect ) +: m_pObject( xObj.get() ) +, m_xObject( static_cast< embed::XEmbeddedObject* >( xObj.get() ) ) +, m_nNotificationType( nNotificationType ) +, m_nAspect( nAspect ) +{} + +void SAL_CALL MainThreadNotificationRequest::notify (const uno::Any& ) +{ + if ( m_pObject ) + { + try + { + uno::Reference< uno::XInterface > xLock = m_xObject.get(); + if ( xLock.is() ) + { + // this is the main thread, the solar mutex must be locked + if ( m_nNotificationType == OLECOMP_ONCLOSE ) + m_pObject->OnClosed_Impl(); + else if ( m_nAspect == embed::Aspects::MSOLE_CONTENT ) + m_pObject->OnViewChanged_Impl(); + else if ( m_nAspect == embed::Aspects::MSOLE_ICON ) + OleEmbeddedObject::OnIconChanged_Impl(); + } + } + catch( const uno::Exception& ) + { + // ignore all the errors + } + } +} + +MainThreadNotificationRequest::~MainThreadNotificationRequest() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/graphconvert.hxx b/embeddedobj/source/msole/graphconvert.hxx new file mode 100644 index 0000000000..cc5066de58 --- /dev/null +++ b/embeddedobj/source/msole/graphconvert.hxx @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <rtl/ustring.hxx> +#include <sal/types.h> + +namespace com::sun::star::uno { class Any; } + +bool ConvertBufferToFormat( + void * pBuf, sal_uInt32 nBufSize, OUString const & aFormatShortName, + css::uno::Any & aResult); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/mtnotification.hxx b/embeddedobj/source/msole/mtnotification.hxx new file mode 100644 index 0000000000..09888d04a5 --- /dev/null +++ b/embeddedobj/source/msole/mtnotification.hxx @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/awt/XCallback.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/weakref.hxx> + +#include <rtl/ref.hxx> + +class OleEmbeddedObject; + +#define OLECOMP_ONVIEWCHANGE 1 +#define OLECOMP_ONCLOSE 2 + +class MainThreadNotificationRequest : public cppu::WeakImplHelper< css::awt::XCallback > +{ + OleEmbeddedObject* m_pObject; + css::uno::WeakReference< css::embed::XEmbeddedObject > m_xObject; + + sal_uInt16 m_nNotificationType; + sal_uInt32 m_nAspect; + +public: + virtual void SAL_CALL notify (const css::uno::Any& rUserData) override; + MainThreadNotificationRequest( const ::rtl::Reference< OleEmbeddedObject >& xObj, sal_uInt16 nNotificationType, sal_uInt32 nAspect = 0 ); + ~MainThreadNotificationRequest() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olecomponent.cxx b/embeddedobj/source/msole/olecomponent.cxx new file mode 100644 index 0000000000..f981a43046 --- /dev/null +++ b/embeddedobj/source/msole/olecomponent.cxx @@ -0,0 +1,1692 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/embed/UnreachableStateException.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/ucb/XSimpleFileAccess.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/io/XTruncate.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/awt/XRequestCallback.hpp> + +#include "platform.h" +#include <comphelper/multicontainer2.hxx> +#include <comphelper/mimeconfighelper.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/windowserrorstring.hxx> +#include <osl/file.hxx> +#include <rtl/ref.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include <o3tl/unit_conversion.hxx> +#include <systools/win32/comtools.hxx> +#include <vcl/threadex.hxx> + +#include "graphconvert.hxx" +#include "olecomponent.hxx" +#include "olepersist.hxx" +#include "olewrapclient.hxx" +#include "advisesink.hxx" +#include <oleembobj.hxx> +#include "mtnotification.hxx" +#include <memory> +#include <string> + +using namespace ::com::sun::star; +using namespace ::comphelper; +#define MAX_ENUM_ELE 20 +#define FORMATS_NUM 3 + +typedef std::vector< FORMATETC* > FormatEtcList; + +FORMATETC const pFormatTemplates[FORMATS_NUM] = { + { CF_ENHMETAFILE, nullptr, 0, -1, TYMED_ENHMF }, + { CF_METAFILEPICT, nullptr, 0, -1, TYMED_MFPICT }, + { CF_BITMAP, nullptr, 0, -1, TYMED_GDI } }; + + +struct OleComponentNative_Impl { + sal::systools::COMReference< IUnknown > m_pObj; + sal::systools::COMReference< IOleObject > m_pOleObject; + sal::systools::COMReference< IViewObject2 > m_pViewObject2; + sal::systools::COMReference< IStorage > m_pIStorage; + FormatEtcList m_aFormatsList; + uno::Sequence< datatransfer::DataFlavor > m_aSupportedGraphFormats; + + OleComponentNative_Impl() + { + // TODO: Extend format list + m_aSupportedGraphFormats = { + + datatransfer::DataFlavor( + "application/x-openoffice-emf;windows_formatname=\"Image EMF\"", + "Windows Enhanced Metafile", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ), + + datatransfer::DataFlavor( + "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"", + "Windows Metafile", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ), + + datatransfer::DataFlavor( + "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"", + "Bitmap", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ), + + datatransfer::DataFlavor( + "image/png", + "PNG", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ), + + datatransfer::DataFlavor( + "application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"", + "GDIMetafile", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ) + }; + } + + bool ConvertDataForFlavor( const STGMEDIUM& aMedium, + const datatransfer::DataFlavor& aFlavor, + uno::Any& aResult ); + + bool GraphicalFlavor( const datatransfer::DataFlavor& aFlavor ); + + uno::Sequence< datatransfer::DataFlavor > GetFlavorsForAspects( sal_uInt32 nSupportedAspects ); +}; + + +static DWORD GetAspectFromFlavor( const datatransfer::DataFlavor& aFlavor ) +{ + if ( aFlavor.MimeType.indexOf( ";Aspect=THUMBNAIL" ) != -1 ) + return DVASPECT_THUMBNAIL; + else if ( aFlavor.MimeType.indexOf( ";Aspect=ICON" ) != -1 ) + return DVASPECT_ICON; + else if ( aFlavor.MimeType.indexOf( ";Aspect=DOCPRINT" ) != -1 ) + return DVASPECT_DOCPRINT; + else + return DVASPECT_CONTENT; +} + + +static OUString GetFlavorSuffixFromAspect( DWORD nAsp ) +{ + OUString aResult; + + if ( nAsp == DVASPECT_THUMBNAIL ) + aResult = ";Aspect=THUMBNAIL"; + else if ( nAsp == DVASPECT_ICON ) + aResult = ";Aspect=ICON"; + else if ( nAsp == DVASPECT_DOCPRINT ) + aResult = ";Aspect=DOCPRINT"; + + // no suffix for DVASPECT_CONTENT + + return aResult; +} + + +static HRESULT OpenIStorageFromURL_Impl( const OUString& aURL, IStorage** ppIStorage ) +{ + OSL_ENSURE( ppIStorage, "The pointer must not be empty!" ); + + OUString aFilePath; + if ( !ppIStorage || ::osl::FileBase::getSystemPathFromFileURL( aURL, aFilePath ) != ::osl::FileBase::E_None ) + throw uno::RuntimeException(); // TODO: something dangerous happened + + return StgOpenStorage( o3tl::toW(aFilePath.getStr()), + nullptr, + STGM_READWRITE | STGM_TRANSACTED, // | STGM_DELETEONRELEASE, + nullptr, + 0, + ppIStorage ); +} + + +bool OleComponentNative_Impl::ConvertDataForFlavor( const STGMEDIUM& aMedium, + const datatransfer::DataFlavor& aFlavor, + uno::Any& aResult ) +{ + bool bAnyIsReady = false; + + // try to convert data from Medium format to specified Flavor format + if ( aFlavor.DataType == cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ) + { + // first the GDI-metafile must be generated + + std::unique_ptr<sal_Int8[]> pBuf; + sal_uInt32 nBufSize = 0; + OUString aFormat; + + if ( aMedium.tymed == TYMED_MFPICT ) // Win Metafile + { + aFormat = "image/x-wmf"; + METAFILEPICT* pMF = static_cast<METAFILEPICT*>(GlobalLock( aMedium.hMetaFilePict )); + if ( pMF ) + { + nBufSize = GetMetaFileBitsEx( pMF->hMF, 0, nullptr ) + 22; + pBuf.reset(new sal_Int8[nBufSize]); + + + // TODO/LATER: the unit size must be calculated correctly + *reinterpret_cast<long*>( pBuf.get() ) = 0x9ac6cdd7L; + *reinterpret_cast<short*>( pBuf.get()+6 ) = SHORT(0); + *reinterpret_cast<short*>( pBuf.get()+8 ) = SHORT(0); + *reinterpret_cast<short*>( pBuf.get()+10 ) = static_cast<SHORT>(pMF->xExt); + *reinterpret_cast<short*>( pBuf.get()+12 ) = static_cast<SHORT>(pMF->yExt); + *reinterpret_cast<short*>( pBuf.get()+14 ) = USHORT(2540); + + + if ( nBufSize && nBufSize == GetMetaFileBitsEx( pMF->hMF, nBufSize - 22, pBuf.get() + 22 ) ) + { + if ( aFlavor.MimeType.matchAsciiL( "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"", 57 ) ) + { + aResult <<= uno::Sequence< sal_Int8 >( pBuf.get(), nBufSize ); + bAnyIsReady = true; + } + } + + GlobalUnlock( aMedium.hMetaFilePict ); + } + } + else if ( aMedium.tymed == TYMED_ENHMF ) // Enh Metafile + { + aFormat = "image/x-emf"; + nBufSize = GetEnhMetaFileBits( aMedium.hEnhMetaFile, 0, nullptr ); + pBuf.reset(new sal_Int8[nBufSize]); + if ( nBufSize && nBufSize == GetEnhMetaFileBits( aMedium.hEnhMetaFile, nBufSize, reinterpret_cast<LPBYTE>(pBuf.get()) ) ) + { + if ( aFlavor.MimeType.matchAsciiL( "application/x-openoffice-emf;windows_formatname=\"Image EMF\"", 57 ) ) + { + aResult <<= uno::Sequence< sal_Int8 >( pBuf.get(), nBufSize ); + bAnyIsReady = true; + } + } + } + else if ( aMedium.tymed == TYMED_GDI ) // Bitmap + { + aFormat = "image/x-MS-bmp"; + + // Find out size of buffer: deprecated GetBitmapBits does not have a mode to return + // required buffer size + BITMAP aBmp; + GetObjectW(aMedium.hBitmap, sizeof(aBmp), &aBmp); + nBufSize = aBmp.bmWidthBytes * aBmp.bmHeight; + + pBuf.reset(new sal_Int8[nBufSize]); + if ( nBufSize && nBufSize == sal::static_int_cast< ULONG >( GetBitmapBits( aMedium.hBitmap, nBufSize, pBuf.get() ) ) ) + { + if ( aFlavor.MimeType.matchAsciiL( "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"", 54 ) ) + { + aResult <<= uno::Sequence< sal_Int8 >( pBuf.get(), nBufSize ); + bAnyIsReady = true; + } + } + } + + if ( pBuf && !bAnyIsReady ) + { + for ( auto const & supportedFormat : std::as_const(m_aSupportedGraphFormats) ) + if ( aFlavor.MimeType.match( supportedFormat.MimeType ) + && aFlavor.DataType == supportedFormat.DataType + && aFlavor.DataType == cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ) + { + bAnyIsReady = ConvertBufferToFormat( pBuf.get(), nBufSize, aFormat, aResult ); + break; + } + } + } + + return bAnyIsReady; +} + + +bool OleComponentNative_Impl::GraphicalFlavor( const datatransfer::DataFlavor& aFlavor ) +{ + // Actually all the required graphical formats must be supported + for ( auto const & supportedFormat : std::as_const(m_aSupportedGraphFormats) ) + if ( aFlavor.MimeType.match( supportedFormat.MimeType ) + && aFlavor.DataType == supportedFormat.DataType ) + return true; + + return false; +} + + +static bool GetClassIDFromSequence_Impl( uno::Sequence< sal_Int8 > const & aSeq, CLSID& aResult ) +{ + if ( aSeq.getLength() == 16 ) + { + aResult.Data1 = ( ( ( ( ( static_cast<sal_uInt8>(aSeq[0]) << 8 ) + static_cast<sal_uInt8>(aSeq[1]) ) << 8 ) + static_cast<sal_uInt8>(aSeq[2]) ) << 8 ) + static_cast<sal_uInt8>(aSeq[3]); + aResult.Data2 = ( static_cast<sal_uInt8>(aSeq[4]) << 8 ) + static_cast<sal_uInt8>(aSeq[5]); + aResult.Data3 = ( static_cast<sal_uInt8>(aSeq[6]) << 8 ) + static_cast<sal_uInt8>(aSeq[7]); + for( int nInd = 0; nInd < 8; nInd++ ) + aResult.Data4[nInd] = static_cast<sal_uInt8>(aSeq[nInd+8]); + + return true; + } + + return false; +} + + +static OUString WinAccToVcl_Impl( const sal_Unicode* pStr ) +{ + OUString aResult; + + if( pStr ) + { + while ( *pStr ) + { + if ( *pStr == '&' ) + { + aResult += "~"; + while( *( ++pStr ) == '&' ); + } + else + { + aResult += OUStringChar( *pStr ); + pStr++; + } + } + } + + return aResult; +} + + +OleComponent::OleComponent( const uno::Reference< uno::XComponentContext >& xContext, OleEmbeddedObject* pUnoOleObject ) +: m_pInterfaceContainer( nullptr ) +, m_bDisposed( false ) +, m_bModified( false ) +, m_pNativeImpl( new OleComponentNative_Impl() ) +, m_pUnoOleObject( pUnoOleObject ) +, m_pOleWrapClientSite( nullptr ) +, m_pImplAdviseSink( nullptr ) +, m_nOLEMiscFlags( 0 ) +, m_nAdvConn( 0 ) +, m_xContext( xContext ) +, m_bOleInitialized( false ) +, m_bWorkaroundActive( false ) +{ + OSL_ENSURE( m_pUnoOleObject, "No owner object is provided!" ); + + HRESULT hr = OleInitialize( nullptr ); + if ( hr == S_OK || hr == S_FALSE ) + m_bOleInitialized = true; + else + { + SAL_WARN("embeddedobj.ole", "OleComponent ctor: OleInitialize() failed with 0x" + << OUString::number(static_cast<sal_uInt32>(hr), 16) << ": " + << WindowsErrorStringFromHRESULT(hr)); + } + + m_pOleWrapClientSite = new OleWrapperClientSite( this ); + m_pOleWrapClientSite->AddRef(); + + m_pImplAdviseSink = new OleWrapperAdviseSink( this ); + m_pImplAdviseSink->AddRef(); + +} + + +OleComponent::~OleComponent() +{ + OSL_ENSURE( !m_pOleWrapClientSite && !m_pImplAdviseSink && !m_pInterfaceContainer && !m_bOleInitialized, + "The object was not closed successfully! DISASTER is possible!" ); + + if ( m_pOleWrapClientSite || m_pImplAdviseSink || m_pInterfaceContainer || m_bOleInitialized ) + { + osl_atomic_increment(&m_refCount); + try { + Dispose(); + } catch( const uno::Exception& ) {} + } + + for (auto const& format : m_pNativeImpl->m_aFormatsList) + { + delete format; + } + m_pNativeImpl->m_aFormatsList.clear(); + + delete m_pNativeImpl; +} + +void OleComponent::Dispose() +{ + if ( m_bDisposed ) + return; + + // Call CloseObject() without m_aMutex locked, since it will call + // IOleObject::Close(), which can call event listeners, which can run on a + // different thread. + CloseObject(); + + osl::MutexGuard aGuard(m_aMutex); + if ( m_pOleWrapClientSite ) + { + m_pOleWrapClientSite->disconnectOleComponent(); + m_pOleWrapClientSite->Release(); + m_pOleWrapClientSite = nullptr; + } + + if ( m_pImplAdviseSink ) + { + m_pImplAdviseSink->disconnectOleComponent(); + m_pImplAdviseSink->Release(); + m_pImplAdviseSink = nullptr; + } + + if ( m_pInterfaceContainer ) + { + lang::EventObject aEvent( static_cast< ::cppu::OWeakObject* >( this ) ); + m_pInterfaceContainer->disposeAndClear( aEvent ); + + delete m_pInterfaceContainer; + m_pInterfaceContainer = nullptr; + } + + if ( m_bOleInitialized ) + { + // since the disposing can happen not only from main thread but also from a clipboard + // the deinitialization might lead to a disaster, SO7 does not deinitialize OLE at all + // so currently the same approach is selected as workaround + // OleUninitialize(); + m_bOleInitialized = false; + } + + m_bDisposed = true; +} + + +void OleComponent::disconnectEmbeddedObject() +{ + // must not be called from destructor of UNO OLE object!!! + osl::MutexGuard aGuard( m_aMutex ); + m_pUnoOleObject = nullptr; +} + + +void OleComponent::CreateNewIStorage_Impl() +{ + // TODO: in future a global memory could be used instead of file. + + // write the stream to the temporary file + OUString aTempURL; + + OSL_ENSURE( m_pUnoOleObject, "Unexpected object absence!" ); + if ( m_pUnoOleObject ) + aTempURL = m_pUnoOleObject->CreateTempURLEmpty_Impl(); + else + aTempURL = GetNewTempFileURL_Impl( m_xContext ); + + if ( !aTempURL.getLength() ) + throw uno::RuntimeException(); // TODO + + // open an IStorage based on the temporary file + OUString aTempFilePath; + if ( ::osl::FileBase::getSystemPathFromFileURL( aTempURL, aTempFilePath ) != ::osl::FileBase::E_None ) + throw uno::RuntimeException(); // TODO: something dangerous happened + + HRESULT hr = StgCreateDocfile( o3tl::toW(aTempFilePath.getStr()), STGM_CREATE | STGM_READWRITE | STGM_TRANSACTED | STGM_DELETEONRELEASE, 0, &m_pNativeImpl->m_pIStorage ); + if ( FAILED( hr ) || !m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO: transport error code? +} + + +uno::Sequence< datatransfer::DataFlavor > OleComponentNative_Impl::GetFlavorsForAspects( sal_uInt32 nSupportedAspects ) +{ + uno::Sequence< datatransfer::DataFlavor > aResult; + for ( sal_uInt32 nAsp = 1; nAsp <= 8; nAsp *= 2 ) + if ( ( nSupportedAspects & nAsp ) == nAsp ) + { + OUString aAspectSuffix = GetFlavorSuffixFromAspect( nAsp ); + + sal_Int32 nLength = aResult.getLength(); + aResult.realloc( nLength + m_aSupportedGraphFormats.getLength() ); + auto pResult = aResult.getArray(); + + for ( sal_Int32 nInd = 0; nInd < m_aSupportedGraphFormats.getLength(); nInd++ ) + { + pResult[nLength + nInd].MimeType = m_aSupportedGraphFormats[nInd].MimeType + aAspectSuffix; + pResult[nLength + nInd].HumanPresentableName = m_aSupportedGraphFormats[nInd].HumanPresentableName; + pResult[nLength + nInd].DataType = m_aSupportedGraphFormats[nInd].DataType; + } + } + + return aResult; +} + + +void OleComponent::RetrieveObjectDataFlavors_Impl() +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + if ( !m_aDataFlavors.getLength() ) + { + sal::systools::COMReference< IDataObject > pDataObject(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( pDataObject ) + { + sal::systools::COMReference< IEnumFORMATETC > pFormatEnum; + HRESULT hr = pDataObject->EnumFormatEtc( DATADIR_GET, &pFormatEnum ); + if ( SUCCEEDED( hr ) && pFormatEnum ) + { + FORMATETC pElem[ MAX_ENUM_ELE ]; + ULONG nNum = 0; + + // if it is possible to retrieve at least one supported graphical format for an aspect + // this format can be converted to other supported formats + sal_uInt32 nSupportedAspects = 0; + do + { + HRESULT hr2 = pFormatEnum->Next( MAX_ENUM_ELE, pElem, &nNum ); + if( hr2 == S_OK || hr2 == S_FALSE ) + { + for( sal_uInt32 nInd = 0; nInd < FORMATS_NUM; nInd++ ) + { + if ( pElem[nInd].cfFormat == pFormatTemplates[nInd].cfFormat + && pElem[nInd].tymed == pFormatTemplates[nInd].tymed ) + nSupportedAspects |= pElem[nInd].dwAspect; + } + } + else + break; + } + while( nNum == MAX_ENUM_ELE ); + + m_aDataFlavors = m_pNativeImpl->GetFlavorsForAspects( nSupportedAspects ); + } + } + + if ( !m_aDataFlavors.getLength() ) + { + // TODO: + // for any reason the object could not provide this information + // try to get access to the cached representation + } + } +} + + +bool OleComponent::InitializeObject_Impl() +// There will be no static objects! +{ + if ( !m_pNativeImpl->m_pObj ) + return false; + + // the linked object will be detected here + OSL_ENSURE( m_pUnoOleObject, "Unexpected object absence!" ); + if ( m_pUnoOleObject ) + m_pUnoOleObject->SetObjectIsLink_Impl( m_pNativeImpl->m_pObj.QueryInterface<IOleLink>(sal::systools::COM_QUERY).is() ); + + if ( !m_pNativeImpl->m_pViewObject2.set(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY) ) + return false; + + // remove all the caches + if ( sal::systools::COMReference< IOleCache > pIOleCache{ m_pNativeImpl->m_pObj, sal::systools::COM_QUERY } ) + { + IEnumSTATDATA* pEnumSD = nullptr; + HRESULT hr2 = pIOleCache->EnumCache( &pEnumSD ); + + if ( SUCCEEDED( hr2 ) && pEnumSD ) + { + pEnumSD->Reset(); + STATDATA aSD; + DWORD nNum; + while( SUCCEEDED( pEnumSD->Next( 1, &aSD, &nNum ) ) && nNum == 1 ) + hr2 = pIOleCache->Uncache( aSD.dwConnection ); + } + + // No IDataObject implementation, caching must be used instead + DWORD nConn; + FORMATETC aFormat = { 0, nullptr, DVASPECT_CONTENT, -1, TYMED_MFPICT }; + hr2 = pIOleCache->Cache( &aFormat, ADVFCACHE_ONSAVE, &nConn ); + } + + if ( !m_pNativeImpl->m_pOleObject.set(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY) ) + return false; // Static objects are not supported, they should be inserted as graphics + + m_pNativeImpl->m_pOleObject->GetMiscStatus( DVASPECT_CONTENT, reinterpret_cast<DWORD*>(&m_nOLEMiscFlags) ); + // TODO: use other misc flags also + // the object should have drawable aspect even in case it supports only iconic representation + // if ( m_nOLEMiscFlags & OLEMISC_ONLYICONIC ) + + m_pNativeImpl->m_pOleObject->SetClientSite( m_pOleWrapClientSite ); + + // the only need in this registration is workaround for close notification + m_pNativeImpl->m_pOleObject->Advise( m_pImplAdviseSink, reinterpret_cast<DWORD*>(&m_nAdvConn) ); + m_pNativeImpl->m_pViewObject2->SetAdvise( DVASPECT_CONTENT, 0, m_pImplAdviseSink ); + + OleSetContainedObject( m_pNativeImpl->m_pOleObject, TRUE ); + + return true; +} + +namespace +{ + HRESULT OleLoadSeh(LPSTORAGE pIStorage, LPVOID* ppObj) + { + HRESULT hr = E_FAIL; + // tdf#119039: there is a nasty bug in OleLoad, that may call an unpaired + // IUnknown::Release on pIStorage on STG_E_FILENOTFOUND: see + // https://developercommunity.visualstudio.com/t/10144795 + // Workaround it here to avoid crash in smart COM pointer destructor that + // would try to release already released object. Since we don't know if + // the bug appears each time STG_E_FILENOTFOUND is returned, this might + // potentially leak the storage object. + if (pIStorage) + pIStorage->AddRef(); + + __try { + hr = OleLoad(pIStorage, IID_IUnknown, nullptr, ppObj); + } __except( EXCEPTION_EXECUTE_HANDLER ) { + hr = E_FAIL; + } + if (pIStorage && hr != STG_E_FILENOTFOUND) + pIStorage->Release(); + + return hr; + } +} + +void OleComponent::LoadEmbeddedObject( const OUString& aTempURL ) +{ + if ( !aTempURL.getLength() ) + throw lang::IllegalArgumentException(); // TODO + + if ( m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO the object is already initialized or wrong initialization is done + + // open an IStorage based on the temporary file + HRESULT hr = OpenIStorageFromURL_Impl( aTempURL, &m_pNativeImpl->m_pIStorage ); + + if ( FAILED( hr ) || !m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO: transport error code? + + hr = OleLoadSeh(m_pNativeImpl->m_pIStorage, reinterpret_cast<void**>(&m_pNativeImpl->m_pObj)); + if ( FAILED( hr ) || !m_pNativeImpl->m_pObj ) + { + throw uno::RuntimeException(); + } + + if ( !InitializeObject_Impl() ) + throw uno::RuntimeException(); // TODO +} + + +void OleComponent::CreateObjectFromClipboard() +{ + if ( m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO:the object is already initialized + + CreateNewIStorage_Impl(); + if ( !m_pNativeImpl->m_pIStorage ) + throw uno::RuntimeException(); // TODO + + IDataObject * pDO = nullptr; + HRESULT hr = OleGetClipboard( &pDO ); + if( SUCCEEDED( hr ) && pDO ) + { + hr = OleQueryCreateFromData( pDO ); + if( S_OK == GetScode( hr ) ) + { + hr = OleCreateFromData( pDO, + IID_IUnknown, + OLERENDER_DRAW, // OLERENDER_FORMAT + nullptr, // &aFormat, + nullptr, + m_pNativeImpl->m_pIStorage, + reinterpret_cast<void**>(&m_pNativeImpl->m_pObj) ); + } + else + { + // Static objects are not supported + pDO->Release(); + } + } + + if ( FAILED( hr ) || !m_pNativeImpl->m_pObj ) + throw uno::RuntimeException(); + + if ( !InitializeObject_Impl() ) + throw uno::RuntimeException(); // TODO +} + + +void OleComponent::CreateNewEmbeddedObject( const uno::Sequence< sal_Int8 >& aSeqCLSID ) +{ + CLSID aClsID; + + if ( !GetClassIDFromSequence_Impl( aSeqCLSID, aClsID ) ) + throw lang::IllegalArgumentException(); // TODO + + if ( m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO:the object is already initialized + + CreateNewIStorage_Impl(); + if ( !m_pNativeImpl->m_pIStorage ) + throw uno::RuntimeException(); // TODO + + // FORMATETC aFormat = { CF_METAFILEPICT, NULL, nAspect, -1, TYMED_MFPICT }; // for OLE..._DRAW should be NULL + + HRESULT hr = OleCreate( aClsID, + IID_IUnknown, + OLERENDER_DRAW, // OLERENDER_FORMAT + nullptr, // &aFormat, + nullptr, + m_pNativeImpl->m_pIStorage, + reinterpret_cast<void**>(&m_pNativeImpl->m_pObj) ); + + if ( FAILED( hr ) || !m_pNativeImpl->m_pObj ) + throw uno::RuntimeException(); // TODO + + if ( !InitializeObject_Impl() ) + throw uno::RuntimeException(); // TODO + + // TODO: getExtent??? +} + + +void OleComponent::CreateObjectFromData( const uno::Reference< datatransfer::XTransferable >& ) +// Static objects are not supported, they should be inserted as graphics +{ + // TODO: May be this call is useless since there are no static objects + // and nonstatic objects will be created based on OLEstorage ( stream ). + // ??? + + // OleQueryCreateFromData... +} + + +void OleComponent::CreateObjectFromFile( const OUString& aFileURL ) +{ + if ( m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO:the object is already initialized + + CreateNewIStorage_Impl(); + if ( !m_pNativeImpl->m_pIStorage ) + throw uno::RuntimeException(); // TODO: + + OUString aFilePath; + if ( ::osl::FileBase::getSystemPathFromFileURL( aFileURL, aFilePath ) != ::osl::FileBase::E_None ) + throw uno::RuntimeException(); // TODO: something dangerous happened + + HRESULT hr = OleCreateFromFile( CLSID_NULL, + o3tl::toW(aFilePath.getStr()), + IID_IUnknown, + OLERENDER_DRAW, // OLERENDER_FORMAT + nullptr, + nullptr, + m_pNativeImpl->m_pIStorage, + reinterpret_cast<void**>(&m_pNativeImpl->m_pObj) ); + + if ( FAILED( hr ) || !m_pNativeImpl->m_pObj ) + throw uno::RuntimeException(); // TODO + + if ( !InitializeObject_Impl() ) + throw uno::RuntimeException(); // TODO +} + + +void OleComponent::CreateLinkFromFile( const OUString& aFileURL ) +{ + if ( m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO:the object is already initialized + + CreateNewIStorage_Impl(); + if ( !m_pNativeImpl->m_pIStorage ) + throw uno::RuntimeException(); // TODO: + + OUString aFilePath; + if ( ::osl::FileBase::getSystemPathFromFileURL( aFileURL, aFilePath ) != ::osl::FileBase::E_None ) + throw uno::RuntimeException(); // TODO: something dangerous happened + + HRESULT hr = OleCreateLinkToFile( o3tl::toW(aFilePath.getStr()), + IID_IUnknown, + OLERENDER_DRAW, // OLERENDER_FORMAT + nullptr, + nullptr, + m_pNativeImpl->m_pIStorage, + reinterpret_cast<void**>(&m_pNativeImpl->m_pObj) ); + + if ( FAILED( hr ) || !m_pNativeImpl->m_pObj ) + throw uno::RuntimeException(); // TODO + + if ( !InitializeObject_Impl() ) + throw uno::RuntimeException(); // TODO +} + + +void OleComponent::InitEmbeddedCopyOfLink( rtl::Reference<OleComponent> const & pOleLinkComponent ) +{ + if ( !pOleLinkComponent || !pOleLinkComponent->m_pNativeImpl->m_pObj ) + throw lang::IllegalArgumentException(); // TODO + + if ( m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO:the object is already initialized + + sal::systools::COMReference< IDataObject > pDataObject(pOleLinkComponent->m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( pDataObject && SUCCEEDED( OleQueryCreateFromData( pDataObject ) ) ) + { + // the object must be already disconnected from the temporary URL + CreateNewIStorage_Impl(); + if ( !m_pNativeImpl->m_pIStorage ) + throw uno::RuntimeException(); // TODO: + + OleCreateFromData( pDataObject, + IID_IUnknown, + OLERENDER_DRAW, + nullptr, + nullptr, + m_pNativeImpl->m_pIStorage, + reinterpret_cast<void**>(&m_pNativeImpl->m_pObj) ); + } + + if ( !m_pNativeImpl->m_pObj ) + { + sal::systools::COMReference< IOleLink > pOleLink(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( !pOleLink ) + throw io::IOException(); // TODO: the object doesn't support IOleLink + + sal::systools::COMReference< IMoniker > pMoniker; + HRESULT hr = pOleLink->GetSourceMoniker( &pMoniker ); + if ( FAILED( hr ) || !pMoniker ) + throw io::IOException(); // TODO: can not retrieve moniker + + // In case of file moniker life is easy : ) + DWORD aMonType = 0; + hr = pMoniker->IsSystemMoniker( &aMonType ); + if ( SUCCEEDED( hr ) && aMonType == MKSYS_FILEMONIKER ) + { + sal::systools::COMReference< IMalloc > pMalloc; + hr = CoGetMalloc( 1, &pMalloc ); // if fails there will be a memory leak + OSL_ENSURE(SUCCEEDED(hr) && pMalloc, "CoGetMalloc() failed!"); + + LPOLESTR pOleStr = nullptr; + hr = pOleLink->GetSourceDisplayName( &pOleStr ); + if ( SUCCEEDED( hr ) && pOleStr ) + { + std::wstring aFilePath( pOleStr ); + if ( pMalloc ) + pMalloc->Free( pOleStr ); + + hr = OleCreateFromFile( CLSID_NULL, + aFilePath.c_str(), + IID_IUnknown, + OLERENDER_DRAW, // OLERENDER_FORMAT + nullptr, + nullptr, + m_pNativeImpl->m_pIStorage, + reinterpret_cast<void**>(&m_pNativeImpl->m_pObj) ); + } + } + + // in case of other moniker types the only way is to get storage + if ( !m_pNativeImpl->m_pObj ) + { + sal::systools::COMReference< IBindCtx > pBindCtx; + hr = CreateBindCtx( 0, &pBindCtx ); + if ( SUCCEEDED( hr ) && pBindCtx ) + { + sal::systools::COMReference< IStorage > pObjectStorage; + hr = pMoniker->BindToStorage( pBindCtx, nullptr, IID_IStorage, reinterpret_cast<void**>(&pObjectStorage) ); + if ( SUCCEEDED( hr ) && pObjectStorage ) + { + hr = pObjectStorage->CopyTo( 0, nullptr, nullptr, m_pNativeImpl->m_pIStorage ); + if ( SUCCEEDED( hr ) ) + hr = OleLoadSeh(m_pNativeImpl->m_pIStorage, reinterpret_cast<void**>(&m_pNativeImpl->m_pObj)); + } + } + } + } + + // If object could not be created the only way is to use graphical representation + if ( !m_pNativeImpl->m_pObj ) + throw uno::RuntimeException(); // TODO + + if ( !InitializeObject_Impl() ) + throw uno::RuntimeException(); // TODO +} + + +void OleComponent::RunObject() +{ + OSL_ENSURE( m_pNativeImpl->m_pOleObject, "The pointer can not be set to NULL here!" ); + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + if ( !OleIsRunning( m_pNativeImpl->m_pOleObject ) ) + { + HRESULT hr = OleRun( m_pNativeImpl->m_pObj ); + + if ( FAILED( hr ) ) + { + OUString error = WindowsErrorStringFromHRESULT(hr); + if ( hr == REGDB_E_CLASSNOTREG ) + { + if (auto pOleObj + = m_pNativeImpl->m_pObj.QueryInterface<IOleObject>(sal::systools::COM_QUERY)) + { + LPOLESTR lpUserType = nullptr; + if (SUCCEEDED(pOleObj->GetUserType(USERCLASSTYPE_FULL, &lpUserType))) + { + error += OUString::Concat("\n") + o3tl::toU(lpUserType); + sal::systools::COMReference<IMalloc> pMalloc; + hr = CoGetMalloc(1, &pMalloc); // if fails there will be a memory leak + SAL_WARN_IF(FAILED(hr) || !pMalloc, "embeddedobj.ole", "CoGetMalloc() failed"); + if (pMalloc) + pMalloc->Free(lpUserType); + } + } + throw embed::UnreachableStateException( + error, getXWeak(), -1, + css::embed::EmbedStates::RUNNING); // the object server is not installed + } + else + throw io::IOException(error, getXWeak()); + } + } +} + + +awt::Size OleComponent::CalculateWithFactor( const awt::Size& aSize, + const awt::Size& aMultiplier, + const awt::Size& aDivisor ) +{ + awt::Size aResult; + + sal_Int64 nWidth = static_cast<sal_Int64>(aSize.Width) * static_cast<sal_Int64>(aMultiplier.Width) / static_cast<sal_Int64>(aDivisor.Width); + sal_Int64 nHeight = static_cast<sal_Int64>(aSize.Height) * static_cast<sal_Int64>(aMultiplier.Height) / static_cast<sal_Int64>(aDivisor.Height); + OSL_ENSURE( nWidth < SAL_MAX_INT32 && nWidth > SAL_MIN_INT32 + && nHeight < SAL_MAX_INT32 && nHeight > SAL_MIN_INT32, + "Unacceptable result size!" ); + + aResult.Width = static_cast<sal_Int32>(nWidth); + aResult.Height = static_cast<sal_Int32>(nHeight); + + return aResult; +} + + +void OleComponent::CloseObject() +{ + if ( m_pNativeImpl->m_pOleObject && OleIsRunning( m_pNativeImpl->m_pOleObject ) ) + m_pNativeImpl->m_pOleObject->Close( OLECLOSE_NOSAVE ); // must be saved before +} + + +uno::Sequence< embed::VerbDescriptor > OleComponent::GetVerbList() +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + if( !m_aVerbList.getLength() ) + { + sal::systools::COMReference< IEnumOLEVERB > pEnum; + if( SUCCEEDED( m_pNativeImpl->m_pOleObject->EnumVerbs( &pEnum ) ) ) + { + OLEVERB szEle[ MAX_ENUM_ELE ]; + ULONG nNum = 0; + sal_Int32 nSeqSize = 0; + + do + { + HRESULT hr = pEnum->Next( MAX_ENUM_ELE, szEle, &nNum ); + if( hr == S_OK || hr == S_FALSE ) + { + m_aVerbList.realloc( nSeqSize += nNum ); + auto pVerbList = m_aVerbList.getArray(); + for( sal_uInt32 nInd = 0; nInd < nNum; nInd++ ) + { + pVerbList[nSeqSize-nNum+nInd].VerbID = szEle[ nInd ].lVerb; + pVerbList[nSeqSize-nNum+nInd].VerbName = WinAccToVcl_Impl( o3tl::toU(szEle[ nInd ].lpszVerbName) ); + pVerbList[nSeqSize-nNum+nInd].VerbFlags = szEle[ nInd ].fuFlags; + pVerbList[nSeqSize-nNum+nInd].VerbAttributes = szEle[ nInd ].grfAttribs; + } + } + else + break; + } + while( nNum == MAX_ENUM_ELE ); + } + } + + return m_aVerbList; +} + + +void OleComponent::ExecuteVerb( sal_Int32 nVerbID ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO + + HRESULT hr = OleRun( m_pNativeImpl->m_pOleObject ); + if ( FAILED( hr ) ) + throw io::IOException(); // TODO: a specific exception that transport error code can be thrown here + + // TODO: probably extents should be set here and stored in aRect + // TODO: probably the parent window also should be set + hr = m_pNativeImpl->m_pOleObject->DoVerb( nVerbID, nullptr, m_pOleWrapClientSite, 0, nullptr, nullptr ); + + if ( FAILED( hr ) ) + throw io::IOException(); // TODO +} + + +void OleComponent::SetHostName( const OUString& aEmbDocName ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + m_pNativeImpl->m_pOleObject->SetHostNames( L"app name", o3tl::toW( aEmbDocName.getStr() ) ); +} + + +void OleComponent::SetExtent( const awt::Size& aVisAreaSize, sal_Int64 nAspect ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + DWORD nMSAspect = static_cast<DWORD>(nAspect); // first 32 bits are for MS aspects + + SIZEL aSize = { aVisAreaSize.Width, aVisAreaSize.Height }; + HRESULT hr = m_pNativeImpl->m_pOleObject->SetExtent( nMSAspect, &aSize ); + + if ( FAILED( hr ) ) + { + // TODO/LATER: is it correct? In future user code probably should be ready for the exception. + // if the object is running but not activated, RPC_E_SERVER_DIED error code is returned by OLE package + // in this case just do nothing + // Also Visio returns E_FAIL on resize if it is in running state + // if ( hr != RPC_E_SERVER_DIED ) + throw io::IOException(); // TODO + } +} + + +awt::Size OleComponent::GetExtent( sal_Int64 nAspect ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + DWORD nMSAspect = static_cast<DWORD>(nAspect); // first 32 bits are for MS aspects + awt::Size aSize; + bool bGotSize = false; + + if ( nMSAspect == DVASPECT_CONTENT ) + { + // Try to get the size from the replacement image first + sal::systools::COMReference< IDataObject > pDataObject(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( pDataObject ) + { + STGMEDIUM aMedium; + FORMATETC aFormat = pFormatTemplates[1]; // use windows metafile format + aFormat.dwAspect = nMSAspect; + + HRESULT hr = pDataObject->GetData( &aFormat, &aMedium ); + + if (hr == RPC_E_WRONG_THREAD) + { + // Assume that the OLE object was loaded on the main thread. + vcl::solarthread::syncExecute([this, &hr, &pDataObject, &aFormat, &aMedium]() { + // Make sure that the current state is embed::EmbedStates::RUNNING. + RunObject(); + // Now try again on the correct thread. + hr = pDataObject->GetData(&aFormat, &aMedium); + }); + } + + if ( SUCCEEDED( hr ) && aMedium.tymed == TYMED_MFPICT ) // Win Metafile + { + METAFILEPICT* pMF = static_cast<METAFILEPICT*>(GlobalLock( aMedium.hMetaFilePict )); + if ( pMF ) + { + // the object uses 0.01 mm as unit, so the metafile size should be converted to object unit + o3tl::Length eFrom = o3tl::Length::mm100; + switch( pMF->mm ) + { + case MM_HIENGLISH: + eFrom = o3tl::Length::in1000; + break; + + case MM_LOENGLISH: + eFrom = o3tl::Length::in100; + break; + + case MM_LOMETRIC: + eFrom = o3tl::Length::mm10; + break; + + case MM_TWIPS: + eFrom = o3tl::Length::twip; + break; + + case MM_ISOTROPIC: + case MM_ANISOTROPIC: + case MM_HIMETRIC: + // do nothing + break; + } + + sal_Int64 nX = o3tl::convert(abs( pMF->xExt ), eFrom, o3tl::Length::mm100); + sal_Int64 nY = o3tl::convert(abs( pMF->yExt ), eFrom, o3tl::Length::mm100); + if ( nX < SAL_MAX_INT32 && nY < SAL_MAX_INT32 ) + { + aSize.Width = static_cast<sal_Int32>(nX); + aSize.Height = static_cast<sal_Int32>(nY); + bGotSize = true; + } + else + OSL_FAIL( "Unexpected size is provided!" ); + } + } + else if (!SUCCEEDED(hr)) + { + SAL_WARN("embeddedobj.ole", " OleComponent::GetExtent: GetData() failed"); + } + // i113605, to release storage medium + if ( SUCCEEDED( hr ) ) + ::ReleaseStgMedium(&aMedium); + } + } + + if ( !bGotSize ) + throw lang::IllegalArgumentException(); + + return aSize; +} + + +awt::Size OleComponent::GetCachedExtent( sal_Int64 nAspect ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + DWORD nMSAspect = static_cast<DWORD>(nAspect); // first 32 bits are for MS aspects + SIZEL aSize; + + HRESULT hr = m_pNativeImpl->m_pViewObject2->GetExtent( nMSAspect, -1, nullptr, &aSize ); + + if ( FAILED( hr ) ) + { + // TODO/LATER: is it correct? + // if there is no appropriate cache for the aspect, OLE_E_BLANK error code is returned + // if ( hr == OLE_E_BLANK ) + // throw lang::IllegalArgumentException(); + //else + // throw io::IOException(); // TODO + + SAL_WARN("embeddedobj.ole", " OleComponent::GetCachedExtent: GetExtent() failed"); + throw lang::IllegalArgumentException(); + } + + return awt::Size( aSize.cx, aSize.cy ); +} + + +awt::Size OleComponent::GetRecommendedExtent( sal_Int64 nAspect ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + DWORD nMSAspect = static_cast<DWORD>(nAspect); // first 32 bits are for MS aspects + SIZEL aSize; + HRESULT hr = m_pNativeImpl->m_pOleObject->GetExtent( nMSAspect, &aSize ); + if ( FAILED( hr ) ) + { + SAL_WARN("embeddedobj.ole", " OleComponent::GetRecommendedExtent: GetExtent() failed"); + throw lang::IllegalArgumentException(); + } + + return awt::Size( aSize.cx, aSize.cy ); +} + + +sal_Int64 OleComponent::GetMiscStatus( sal_Int64 nAspect ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + DWORD nResult; + m_pNativeImpl->m_pOleObject->GetMiscStatus( static_cast<DWORD>(nAspect), &nResult ); + return static_cast<sal_Int64>(nResult); // first 32 bits are for MS flags +} + + +uno::Sequence< sal_Int8 > OleComponent::GetCLSID() +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + GUID aCLSID; + HRESULT hr = m_pNativeImpl->m_pOleObject->GetUserClassID( &aCLSID ); + if ( FAILED( hr ) ) + throw io::IOException(); // TODO: + + return MimeConfigurationHelper::GetSequenceClassID( aCLSID.Data1, aCLSID.Data2, aCLSID.Data3, + aCLSID.Data4[0], aCLSID.Data4[1], + aCLSID.Data4[2], aCLSID.Data4[3], + aCLSID.Data4[4], aCLSID.Data4[5], + aCLSID.Data4[6], aCLSID.Data4[7] ); +} + + +bool OleComponent::IsDirty() +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + if ( IsWorkaroundActive() ) + return true; + + sal::systools::COMReference< IPersistStorage > pPersistStorage(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( !pPersistStorage ) + throw io::IOException(); // TODO + + HRESULT hr = pPersistStorage->IsDirty(); + return ( hr != S_FALSE ); +} + + +void OleComponent::StoreOwnTmpIfNecessary() +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + sal::systools::COMReference< IPersistStorage > pPersistStorage(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( !pPersistStorage ) + throw io::IOException(); // TODO + + if ( m_bWorkaroundActive || pPersistStorage->IsDirty() != S_FALSE ) + { + HRESULT hr = OleSave( pPersistStorage, m_pNativeImpl->m_pIStorage, TRUE ); + if ( FAILED( hr ) ) + { + // Till now was required only for AcrobatReader7.0.8 + GUID aCLSID; + hr = m_pNativeImpl->m_pOleObject->GetUserClassID( &aCLSID ); + if ( FAILED( hr ) ) + { + SAL_WARN("embeddedobj.ole", "OleComponent::StoreOwnTmpIfNecessary: GetUserClassID() failed"); + throw io::IOException(); // TODO + } + + hr = WriteClassStg( m_pNativeImpl->m_pIStorage, aCLSID ); + if ( FAILED( hr ) ) + throw io::IOException(); // TODO + + // the result of the following call is not checked because some objects, for example AcrobatReader7.0.8 + // return error even in case the saving was done correctly + hr = pPersistStorage->Save( m_pNativeImpl->m_pIStorage, TRUE ); + + // another workaround for AcrobatReader7.0.8 object, this object might think that it is not changed + // when it has been created from file, although it must be saved + m_bWorkaroundActive = true; + } + + hr = m_pNativeImpl->m_pIStorage->Commit( STGC_DEFAULT ); + if ( FAILED( hr ) ) + throw io::IOException(); // TODO + + hr = pPersistStorage->SaveCompleted( nullptr ); + if ( FAILED( hr ) && hr != E_UNEXPECTED ) + throw io::IOException(); // TODO + + } +} + + +bool OleComponent::SaveObject_Impl() +{ + bool bResult = false; + OleEmbeddedObject* pLockObject = nullptr; + + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pUnoOleObject ) + { + pLockObject = m_pUnoOleObject; + pLockObject->acquire(); + } + } + + if ( pLockObject ) + { + bResult = pLockObject->SaveObject_Impl(); + pLockObject->release(); + } + + return bResult; +} + + +bool OleComponent::OnShowWindow_Impl( bool bShow ) +{ + bool bResult = false; + OleEmbeddedObject* pLockObject = nullptr; + + { + osl::MutexGuard aGuard( m_aMutex ); + + if ( m_pUnoOleObject ) + { + pLockObject = m_pUnoOleObject; + pLockObject->acquire(); + } + } + + if ( pLockObject ) + { + bResult = pLockObject->OnShowWindow_Impl( bShow ); + pLockObject->release(); + } + + return bResult; +} + + +void OleComponent::OnViewChange_Impl( sal_uInt32 dwAspect ) +{ + // TODO: check if it is enough or may be saving notifications are required for Visio2000 + ::rtl::Reference< OleEmbeddedObject > xLockObject; + + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pUnoOleObject ) + xLockObject = m_pUnoOleObject; + } + + if ( xLockObject.is() ) + { + uno::Reference < awt::XRequestCallback > xRequestCallback( + m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.awt.AsyncCallback", m_xContext), + uno::UNO_QUERY ); + xRequestCallback->addCallback( new MainThreadNotificationRequest( xLockObject, OLECOMP_ONVIEWCHANGE, dwAspect ), uno::Any() ); + } +} + + +void OleComponent::OnClose_Impl() +{ + ::rtl::Reference< OleEmbeddedObject > xLockObject; + + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pUnoOleObject ) + xLockObject = m_pUnoOleObject; + } + + if ( xLockObject.is() ) + { + uno::Reference < awt::XRequestCallback > xRequestCallback( + m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.awt.AsyncCallback", m_xContext), + uno::UNO_QUERY ); + xRequestCallback->addCallback( new MainThreadNotificationRequest( xLockObject, OLECOMP_ONCLOSE ), uno::Any() ); + } +} + +// XCloseable + +void SAL_CALL OleComponent::close( sal_Bool bDeliverOwnership ) +{ + uno::Reference< uno::XInterface > xSelfHold; + { + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw lang::DisposedException(); // TODO + + xSelfHold.set(static_cast<::cppu::OWeakObject*>(this)); + lang::EventObject aSource(static_cast<::cppu::OWeakObject*>(this)); + + if (m_pInterfaceContainer) + { + comphelper::OInterfaceContainerHelper2* pContainer + = m_pInterfaceContainer->getContainer(cppu::UnoType<util::XCloseListener>::get()); + if (pContainer != nullptr) + { + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + static_cast<util::XCloseListener*>(pIterator.next()) + ->queryClosing(aSource, bDeliverOwnership); + } + catch (const uno::RuntimeException&) + { + pIterator.remove(); + } + } + } + + pContainer + = m_pInterfaceContainer->getContainer(cppu::UnoType<util::XCloseListener>::get()); + if (pContainer != nullptr) + { + comphelper::OInterfaceIteratorHelper2 pCloseIterator(*pContainer); + while (pCloseIterator.hasMoreElements()) + { + try + { + static_cast<util::XCloseListener*>(pCloseIterator.next()) + ->notifyClosing(aSource); + } + catch (const uno::RuntimeException&) + { + pCloseIterator.remove(); + } + } + } + } + } + + Dispose(); +} + + +void SAL_CALL OleComponent::addCloseListener( const uno::Reference< util::XCloseListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer = new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex ); + + m_pInterfaceContainer->addInterface( cppu::UnoType<util::XCloseListener>::get(), xListener ); +} + + +void SAL_CALL OleComponent::removeCloseListener( const uno::Reference< util::XCloseListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<util::XCloseListener>::get(), + xListener ); +} + +// XTransferable + +uno::Any SAL_CALL OleComponent::getTransferData( const datatransfer::DataFlavor& aFlavor ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + uno::Any aResult; + bool bSupportedFlavor = false; + + if ( m_pNativeImpl->GraphicalFlavor( aFlavor ) ) + { + DWORD nRequestedAspect = GetAspectFromFlavor( aFlavor ); + // if own icon is set and icon aspect is requested the own icon can be returned directly + + sal::systools::COMReference< IDataObject > pDataObject(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( !pDataObject ) + throw io::IOException(); // TODO: transport error code + + // The following optimization does not make much sense currently just because + // only one aspect is supported, and only three formats for the aspect are supported + // and moreover it is not guaranteed that the once returned format will be supported further + // example - i52106 + // TODO/LATER: bring the optimization back when other aspects are supported + + // FORMATETC* pFormatEtc = m_pNativeImpl->GetSupportedFormatForAspect( nRequestedAspect ); + // if ( pFormatEtc ) + // { + // STGMEDIUM aMedium; + // hr = pDataObject->GetData( pFormatEtc, &aMedium ); + // if ( SUCCEEDED( hr ) ) + // bSupportedFlavor = m_pNativeImpl->ConvertDataForFlavor( aMedium, aFlavor, aResult ); + // } + // else + { + // the supported format of the application is still not found, find one + for ( sal_Int32 nInd = 0; nInd < FORMATS_NUM; nInd++ ) + { + STGMEDIUM aMedium; + FORMATETC aFormat = pFormatTemplates[nInd]; + aFormat.dwAspect = nRequestedAspect; + + HRESULT hr = pDataObject->GetData( &aFormat, &aMedium ); + if ( SUCCEEDED( hr ) ) + { + bSupportedFlavor = m_pNativeImpl->ConvertDataForFlavor( aMedium, aFlavor, aResult ); + ::ReleaseStgMedium(&aMedium); // i113605, to release storage medium + if ( bSupportedFlavor ) + { + // TODO/LATER: bring the optimization back when other aspects are supported + // m_pNativeImpl->AddSupportedFormat( aFormat ); + break; + } + } + } + } + + // If the replacement could not be retrieved, the cached representation should be used + // currently it is not necessary to retrieve it here, so it is implemented in the object itself + } + // TODO: Investigate if there is already the format name + // and whether this format is really required + else if ( aFlavor.DataType == cppu::UnoType<io::XInputStream>::get() + && aFlavor.MimeType == "application/x-openoffice-contentstream" ) + { + // allow to retrieve stream-representation of the object persistence + bSupportedFlavor = true; + uno::Reference < io::XStream > xTempFileStream( + io::TempFile::create(m_xContext), + uno::UNO_QUERY_THROW ); + + uno::Reference< io::XOutputStream > xTempOutStream = xTempFileStream->getOutputStream(); + uno::Reference< io::XInputStream > xTempInStream = xTempFileStream->getInputStream(); + if ( !(xTempOutStream.is() && xTempInStream.is()) ) + throw io::IOException(); // TODO: + + OSL_ENSURE( m_pUnoOleObject, "Unexpected object absence!" ); + if ( !m_pUnoOleObject ) + throw uno::RuntimeException(); + + m_pUnoOleObject->StoreObjectToStream( xTempOutStream ); + + xTempOutStream->closeOutput(); + xTempOutStream.clear(); + + aResult <<= xTempInStream; + } + + if ( !bSupportedFlavor ) + throw datatransfer::UnsupportedFlavorException(); + + return aResult; +} + + +uno::Sequence< datatransfer::DataFlavor > SAL_CALL OleComponent::getTransferDataFlavors() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + RetrieveObjectDataFlavors_Impl(); + + return m_aDataFlavors; +} + + +sal_Bool SAL_CALL OleComponent::isDataFlavorSupported( const datatransfer::DataFlavor& aFlavor ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + if ( !m_aDataFlavors.getLength() ) + { + RetrieveObjectDataFlavors_Impl(); + } + + for ( auto const & supportedFormat : std::as_const(m_aDataFlavors) ) + if ( supportedFormat.MimeType.equals( aFlavor.MimeType ) && supportedFormat.DataType == aFlavor.DataType ) + return true; + + return false; +} + +void SAL_CALL OleComponent::dispose() +{ + try + { + close( true ); + } + catch ( const uno::Exception& ) + { + } +} + +void SAL_CALL OleComponent::addEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer = new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex ); + + m_pInterfaceContainer->addInterface( cppu::UnoType<lang::XEventListener>::get(), xListener ); +} + + +void SAL_CALL OleComponent::removeEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<lang::XEventListener>::get(), + xListener ); +} + +sal_Int64 SAL_CALL OleComponent::getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) +{ + try + { + uno::Sequence < sal_Int8 > aCLSID = GetCLSID(); + if ( MimeConfigurationHelper::ClassIDsEqual( aIdentifier, aCLSID ) ) + return comphelper::getSomething_cast(static_cast<IUnknown*>(m_pNativeImpl->m_pObj)); + + // compatibility hack for old versions: CLSID was used in wrong order (SvGlobalName order) + sal_Int32 nLength = aIdentifier.getLength(); + if ( nLength == 16 ) + { + for ( sal_Int32 n=8; n<16; n++ ) + if ( aIdentifier[n] != aCLSID[n] ) + return 0; + if ( aIdentifier[7] == aCLSID[6] && + aIdentifier[6] == aCLSID[7] && + aIdentifier[5] == aCLSID[4] && + aIdentifier[4] == aCLSID[5] && + aIdentifier[3] == aCLSID[0] && + aIdentifier[2] == aCLSID[1] && + aIdentifier[1] == aCLSID[2] && + aIdentifier[0] == aCLSID[3] ) + return reinterpret_cast<sal_Int64>(static_cast<IUnknown*>(m_pNativeImpl->m_pObj)); + } + } + catch ( const uno::Exception& ) + { + } + + return 0; +} + +sal_Bool SAL_CALL OleComponent::isModified() +{ + return m_bModified; +} + +void SAL_CALL OleComponent::setModified( sal_Bool bModified ) +{ + m_bModified = bModified; + + if ( bModified && m_pInterfaceContainer ) + { + comphelper::OInterfaceContainerHelper2* pContainer = + m_pInterfaceContainer->getContainer( cppu::UnoType<util::XModifyListener>::get()); + if ( pContainer != nullptr ) + { + comphelper::OInterfaceIteratorHelper2 pIterator( *pContainer ); + while ( pIterator.hasMoreElements() ) + { + try + { + lang::EventObject aEvent( static_cast<util::XModifiable*>(this) ); + static_cast<util::XModifyListener*>(pIterator.next())->modified( aEvent ); + } + catch( const uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + } +} + +void SAL_CALL OleComponent::addModifyListener( const css::uno::Reference < css::util::XModifyListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer = new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex ); + + m_pInterfaceContainer->addInterface( cppu::UnoType<util::XModifyListener>::get(), xListener ); +} + +void SAL_CALL OleComponent::removeModifyListener( const css::uno::Reference < css::util::XModifyListener >& xListener) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<util::XModifyListener>::get(), + xListener ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olecomponent.hxx b/embeddedobj/source/msole/olecomponent.hxx new file mode 100644 index 0000000000..5a96b64fcb --- /dev/null +++ b/embeddedobj/source/msole/olecomponent.hxx @@ -0,0 +1,161 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/embed/VerbDescriptor.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/util/XModifiable.hpp> +#include <com/sun/star/util/XModifyListener.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <rtl/ref.hxx> + +namespace comphelper { + class OMultiTypeInterfaceContainerHelper2; +} + +class OleWrapperClientSite; +class OleWrapperAdviseSink; +class OleEmbeddedObject; +struct OleComponentNative_Impl; + +class OleComponent : public ::cppu::WeakImplHelper< css::util::XCloseable, css::lang::XComponent, + css::lang::XUnoTunnel, css::util::XModifiable, + css::datatransfer::XTransferable > +{ + ::osl::Mutex m_aMutex; + comphelper::OMultiTypeInterfaceContainerHelper2* m_pInterfaceContainer; + + bool m_bDisposed; + bool m_bModified; + OleComponentNative_Impl* m_pNativeImpl; + + OleEmbeddedObject* m_pUnoOleObject; + OleWrapperClientSite* m_pOleWrapClientSite; + OleWrapperAdviseSink* m_pImplAdviseSink; + + sal_Int32 m_nOLEMiscFlags; + sal_Int32 m_nAdvConn; + + css::uno::Sequence< css::embed::VerbDescriptor > m_aVerbList; + css::uno::Sequence< css::datatransfer::DataFlavor > m_aDataFlavors; + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + bool m_bOleInitialized; + + // specifies whether the workaround for some rare embedded objects is activated ( f.e. AcrobatReader 7.0.8 object ) + // such objects report the dirty state wrongly sometimes and do not allow to store them any time + bool m_bWorkaroundActive; + + bool InitializeObject_Impl(); + + void CreateNewIStorage_Impl(); + void RetrieveObjectDataFlavors_Impl(); + void Dispose(); + + +public: + OleComponent( const css::uno::Reference< css::uno::XComponentContext >& xContext, + OleEmbeddedObject* pOleObj ); + + virtual ~OleComponent() override; + + OleComponent* createEmbeddedCopyOfLink(); + + void disconnectEmbeddedObject(); + + static css::awt::Size CalculateWithFactor( const css::awt::Size& aSize, + const css::awt::Size& aMultiplier, + const css::awt::Size& aDivisor ); + + css::awt::Size CalculateTheRealSize( const css::awt::Size& aContSize, bool bUpdate ); + + // ==== Initialization ================================================== + void LoadEmbeddedObject( const OUString& aTempURL ); + void CreateObjectFromClipboard(); + void CreateNewEmbeddedObject( const css::uno::Sequence< sal_Int8 >& aSeqCLSID ); + static void CreateObjectFromData( + const css::uno::Reference< css::datatransfer::XTransferable >& xTransfer ); + void CreateObjectFromFile( const OUString& aFileName ); + void CreateLinkFromFile( const OUString& aFileName ); + void InitEmbeddedCopyOfLink( rtl::Reference<OleComponent> const & pOleLinkComponent ); + + + void RunObject(); // switch OLE object to running state + void CloseObject(); // switch OLE object to loaded state + + css::uno::Sequence< css::embed::VerbDescriptor > GetVerbList(); + + void ExecuteVerb( sal_Int32 nVerbID ); + void SetHostName( const OUString& aEmbDocName ); + void SetExtent( const css::awt::Size& aVisAreaSize, sal_Int64 nAspect ); + + css::awt::Size GetExtent( sal_Int64 nAspect ); + css::awt::Size GetCachedExtent( sal_Int64 nAspect ); + css::awt::Size GetRecommendedExtent( sal_Int64 nAspect ); + + sal_Int64 GetMiscStatus( sal_Int64 nAspect ); + + css::uno::Sequence< sal_Int8 > GetCLSID(); + + bool IsWorkaroundActive() const { return m_bWorkaroundActive; } + bool IsDirty(); + + void StoreOwnTmpIfNecessary(); + + bool SaveObject_Impl(); + bool OnShowWindow_Impl( bool bShow ); + void OnViewChange_Impl( sal_uInt32 dwAspect ); + void OnClose_Impl(); + + // XCloseable + virtual void SAL_CALL close( sal_Bool DeliverOwnership ) override; + virtual void SAL_CALL addCloseListener( const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + virtual void SAL_CALL removeCloseListener( const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + + // XTransferable + virtual css::uno::Any SAL_CALL getTransferData( const css::datatransfer::DataFlavor& aFlavor ) override; + virtual css::uno::Sequence< css::datatransfer::DataFlavor > SAL_CALL getTransferDataFlavors( ) override; + virtual sal_Bool SAL_CALL isDataFlavorSupported( const css::datatransfer::DataFlavor& aFlavor ) override; + + // XComponent + virtual void SAL_CALL dispose() override; + virtual void SAL_CALL addEventListener(const css::uno::Reference < css::lang::XEventListener >& aListener) override; + virtual void SAL_CALL removeEventListener(const css::uno::Reference < css::lang::XEventListener >& aListener) override; + + // XUnoTunnel + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override; + + // XModifiable + virtual sal_Bool SAL_CALL isModified() override; + virtual void SAL_CALL setModified( sal_Bool bModified ) override; + virtual void SAL_CALL addModifyListener( const css::uno::Reference < css::util::XModifyListener >& xListener ) override; + virtual void SAL_CALL removeModifyListener( const css::uno::Reference < css::util::XModifyListener >& xListener) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/oleembed.cxx b/embeddedobj/source/msole/oleembed.cxx new file mode 100644 index 0000000000..2bc6cdb606 --- /dev/null +++ b/embeddedobj/source/msole/oleembed.cxx @@ -0,0 +1,1179 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <string_view> + +#include <oleembobj.hxx> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/EmbedVerbs.hpp> +#include <com/sun/star/embed/UnreachableStateException.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/EmbedUpdateModes.hpp> +#include <com/sun/star/embed/NeedsRunningStateException.hpp> +#include <com/sun/star/embed/StateChangeInProgressException.hpp> +#include <com/sun/star/embed/EmbedMisc.hpp> +#include <com/sun/star/embed/XEmbedObjectCreator.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XLoadable.hpp> +#include <com/sun/star/document/XStorageBasedDocument.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/system/SystemShellExecute.hpp> +#include <com/sun/star/system/SystemShellExecuteFlags.hpp> + +#include <cppuhelper/exc_hlp.hxx> +#include <comphelper/multicontainer2.hxx> +#include <comphelper/mimeconfighelper.hxx> +#include <comphelper/propertyvalue.hxx> +#include <sal/log.hxx> +#include <comphelper/diagnose_ex.hxx> + + +#include <targetstatecontrol.hxx> + +#include "ownview.hxx" + +#if defined(_WIN32) +#include "olecomponent.hxx" +#endif + +using namespace ::com::sun::star; + +#ifdef _WIN32 + +void OleEmbeddedObject::SwitchComponentToRunningState_Impl() +{ + if ( !m_pOleComponent ) + { + throw embed::UnreachableStateException(); + } + try + { + m_pOleComponent->RunObject(); + } + catch( const embed::UnreachableStateException& ) + { + GetRidOfComponent(); + throw; + } + catch( const embed::WrongStateException& ) + { + GetRidOfComponent(); + throw; + } +} + + +uno::Sequence< sal_Int32 > OleEmbeddedObject::GetReachableStatesList_Impl( + const uno::Sequence< embed::VerbDescriptor >& aVerbList ) +{ + uno::Sequence< sal_Int32 > aStates { embed::EmbedStates::LOADED, embed::EmbedStates::RUNNING }; + for ( embed::VerbDescriptor const & vd : aVerbList ) + if ( vd.VerbID == embed::EmbedVerbs::MS_OLEVERB_OPEN ) + { + aStates.realloc(3); + aStates.getArray()[2] = embed::EmbedStates::ACTIVE; + break; + } + + return aStates; +} + + +uno::Sequence< sal_Int32 > OleEmbeddedObject::GetIntermediateVerbsSequence_Impl( sal_Int32 nNewState ) +{ + SAL_WARN_IF( m_nObjectState == embed::EmbedStates::LOADED, "embeddedobj.ole", "Loaded object is switched to running state without verbs using!" ); + + // actually there will be only one verb + if ( m_nObjectState == embed::EmbedStates::RUNNING && nNewState == embed::EmbedStates::ACTIVE ) + { + return { embed::EmbedVerbs::MS_OLEVERB_OPEN }; + } + + return uno::Sequence< sal_Int32 >(); +} +#endif + +void OleEmbeddedObject::MoveListeners() +{ + if ( !m_pInterfaceContainer ) + return; + + // move state change listeners + { + comphelper::OInterfaceContainerHelper2* pStateChangeContainer = + m_pInterfaceContainer->getContainer( cppu::UnoType<embed::XStateChangeListener>::get()); + if ( pStateChangeContainer != nullptr ) + { + if ( m_xWrappedObject.is() ) + { + comphelper::OInterfaceIteratorHelper2 pIterator( *pStateChangeContainer ); + while ( pIterator.hasMoreElements() ) + { + try + { + m_xWrappedObject->addStateChangeListener( static_cast<embed::XStateChangeListener*>(pIterator.next()) ); + } + catch( const uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + } + } + + // move event listeners + { + comphelper::OInterfaceContainerHelper2* pEventContainer = + m_pInterfaceContainer->getContainer( cppu::UnoType<document::XEventListener>::get()); + if ( pEventContainer != nullptr ) + { + if ( m_xWrappedObject.is() ) + { + comphelper::OInterfaceIteratorHelper2 pIterator( *pEventContainer ); + while ( pIterator.hasMoreElements() ) + { + try + { + m_xWrappedObject->addEventListener( static_cast<document::XEventListener*>(pIterator.next()) ); + } + catch( const uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + } + } + + // move close listeners + { + comphelper::OInterfaceContainerHelper2* pCloseContainer = + m_pInterfaceContainer->getContainer( cppu::UnoType<util::XCloseListener>::get()); + if ( pCloseContainer != nullptr ) + { + if ( m_xWrappedObject.is() ) + { + comphelper::OInterfaceIteratorHelper2 pIterator( *pCloseContainer ); + while ( pIterator.hasMoreElements() ) + { + try + { + m_xWrappedObject->addCloseListener( static_cast<util::XCloseListener*>(pIterator.next()) ); + } + catch( const uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + } + } + + m_pInterfaceContainer.reset(); +} + + +uno::Reference< embed::XStorage > OleEmbeddedObject::CreateTemporarySubstorage( OUString& o_aStorageName ) +{ + uno::Reference< embed::XStorage > xResult; + + for ( sal_Int32 nInd = 0; nInd < 32000 && !xResult.is(); nInd++ ) + { + OUString aName = OUString::number( nInd ) + "TMPSTOR" + m_aEntryName; + if ( !m_xParentStorage->hasByName( aName ) ) + { + xResult = m_xParentStorage->openStorageElement( aName, embed::ElementModes::READWRITE ); + o_aStorageName = aName; + } + } + + if ( !xResult.is() ) + { + o_aStorageName.clear(); + throw uno::RuntimeException("Failed to create temporary storage for OLE embed object"); + } + + return xResult; +} + + +OUString OleEmbeddedObject::MoveToTemporarySubstream() +{ + OUString aResult; + for ( sal_Int32 nInd = 0; nInd < 32000 && aResult.isEmpty(); nInd++ ) + { + OUString aName = OUString::number( nInd ) + "TMPSTREAM" + m_aEntryName; + if ( !m_xParentStorage->hasByName( aName ) ) + { + m_xParentStorage->renameElement( m_aEntryName, aName ); + aResult = aName; + } + } + + if ( aResult.isEmpty() ) + throw uno::RuntimeException("Failed to rename temporary storage for OLE embed object"); + + return aResult; +} + + +bool OleEmbeddedObject::TryToConvertToOOo( const uno::Reference< io::XStream >& xStream ) +{ + bool bResult = false; + + OUString aStorageName; + OUString aTmpStreamName; + sal_Int32 nStep = 0; + + if ( m_pOleComponent || m_bReadOnly ) + return false; + + try + { + changeState( embed::EmbedStates::LOADED ); + + // the stream must be seekable + uno::Reference< io::XSeekable > xSeekable( xStream, uno::UNO_QUERY_THROW ); + xSeekable->seek( 0 ); + m_aFilterName = OwnView_Impl::GetFilterNameFromExtentionAndInStream( m_xContext, std::u16string_view(), xStream->getInputStream() ); + + if ( !m_aFilterName.isEmpty() + && ( m_aFilterName == "Calc MS Excel 2007 XML" || m_aFilterName == "Impress MS PowerPoint 2007 XML" || m_aFilterName == "MS Word 2007 XML" + || m_aFilterName == "MS Excel 97 Vorlage/Template" || m_aFilterName == "MS Word 97 Vorlage" ) ) + { + uno::Reference< container::XNameAccess > xFilterFactory( + m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.FilterFactory", m_xContext), + uno::UNO_QUERY_THROW ); + + OUString aDocServiceName; + uno::Any aFilterAnyData = xFilterFactory->getByName( m_aFilterName ); + uno::Sequence< beans::PropertyValue > aFilterData; + if ( aFilterAnyData >>= aFilterData ) + { + for ( beans::PropertyValue const & prop : std::as_const(aFilterData) ) + if ( prop.Name == "DocumentService" ) + prop.Value >>= aDocServiceName; + } + + if ( !aDocServiceName.isEmpty() ) + { + // create the model + uno::Sequence< uno::Any > aArguments{ uno::Any( + beans::NamedValue( "EmbeddedObject", uno::Any( true ))) }; + + uno::Reference< util::XCloseable > xDocument( m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( aDocServiceName, aArguments, m_xContext ), uno::UNO_QUERY_THROW ); + uno::Reference< frame::XLoadable > xLoadable( xDocument, uno::UNO_QUERY_THROW ); + uno::Reference< document::XStorageBasedDocument > xStorDoc( xDocument, uno::UNO_QUERY_THROW ); + + // let the model behave as embedded one + uno::Reference< frame::XModel > xModel( xDocument, uno::UNO_QUERY_THROW ); + uno::Sequence< beans::PropertyValue > aSeq{ comphelper::makePropertyValue( + "SetEmbedded", true) }; + xModel->attachResource( OUString(), aSeq ); + + // load the model from the stream + uno::Sequence< beans::PropertyValue > aArgs{ + comphelper::makePropertyValue("HierarchicalDocumentName", m_aEntryName), + comphelper::makePropertyValue("ReadOnly", true), + comphelper::makePropertyValue("FilterName", m_aFilterName), + comphelper::makePropertyValue("URL", OUString( "private:stream" )), + comphelper::makePropertyValue("InputStream", xStream->getInputStream()) + }; + + xSeekable->seek( 0 ); + xLoadable->load( aArgs ); + + // the model is successfully loaded, create a new storage and store the model to the storage + uno::Reference< embed::XStorage > xTmpStorage = CreateTemporarySubstorage( aStorageName ); + xStorDoc->storeToStorage( xTmpStorage, uno::Sequence< beans::PropertyValue >() ); + xDocument->close( true ); + uno::Reference< beans::XPropertySet > xStorProps( xTmpStorage, uno::UNO_QUERY_THROW ); + OUString aMediaType; + xStorProps->getPropertyValue("MediaType") >>= aMediaType; + xTmpStorage->dispose(); + + // look for the related embedded object factory + ::comphelper::MimeConfigurationHelper aConfigHelper( m_xContext ); + OUString aEmbedFactory; + if ( !aMediaType.isEmpty() ) + aEmbedFactory = aConfigHelper.GetFactoryNameByMediaType( aMediaType ); + + if ( aEmbedFactory.isEmpty() ) + throw uno::RuntimeException("Failed to get OLE embedded object factory"); + + uno::Reference< uno::XInterface > xFact = m_xContext->getServiceManager()->createInstanceWithContext( aEmbedFactory, m_xContext ); + + uno::Reference< embed::XEmbedObjectCreator > xEmbCreator( xFact, uno::UNO_QUERY_THROW ); + + // now the object should be adjusted to become the wrapper + nStep = 1; + uno::Reference< lang::XComponent > xComp( m_xObjectStream, uno::UNO_QUERY_THROW ); + xComp->dispose(); + m_xObjectStream.clear(); + m_nObjectState = -1; + + nStep = 2; + aTmpStreamName = MoveToTemporarySubstream(); + + nStep = 3; + m_xParentStorage->renameElement( aStorageName, m_aEntryName ); + + nStep = 4; + m_xWrappedObject.set( xEmbCreator->createInstanceInitFromEntry( m_xParentStorage, m_aEntryName, uno::Sequence< beans::PropertyValue >(), uno::Sequence< beans::PropertyValue >() ), uno::UNO_QUERY_THROW ); + + // remember parent document name to show in the title bar + m_xWrappedObject->setContainerName( m_aContainerName ); + + bResult = true; // the change is no more revertable + try + { + m_xParentStorage->removeElement( aTmpStreamName ); + } + catch( const uno::Exception& ) + { + // the success of the removing is not so important + } + } + } + } + catch( const uno::Exception& ) + { + // repair the object if necessary + switch( nStep ) + { + case 4: + case 3: + if ( !aTmpStreamName.isEmpty() && aTmpStreamName != m_aEntryName ) + try + { + if ( m_xParentStorage->hasByName( m_aEntryName ) ) + m_xParentStorage->removeElement( m_aEntryName ); + m_xParentStorage->renameElement( aTmpStreamName, m_aEntryName ); + } + catch ( const uno::Exception& ex ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + try { + close( true ); + } catch( const uno::Exception& ) {} + + m_xParentStorage->dispose(); // ??? the storage has information loss, it should be closed without committing! + throw css::lang::WrappedTargetRuntimeException( ex.Message, + nullptr, anyEx ); // the repairing is not possible + } + [[fallthrough]]; + case 2: + try + { + m_xObjectStream = m_xParentStorage->openStreamElement( m_aEntryName, m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE ); + m_nObjectState = embed::EmbedStates::LOADED; + } + catch( const uno::Exception& ex ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + try { + close( true ); + } catch( const uno::Exception& ) {} + + throw css::lang::WrappedTargetRuntimeException( ex.Message, + nullptr, anyEx ); // the repairing is not possible + } + [[fallthrough]]; + + case 1: + case 0: + if ( !aStorageName.isEmpty() ) + try { + m_xParentStorage->removeElement( aStorageName ); + } catch( const uno::Exception& ) { SAL_WARN( "embeddedobj.ole", "Can not remove temporary storage!" ); } + break; + } + } + + if ( bResult ) + { + // the conversion was done successfully, now the additional initializations should happen + + MoveListeners(); + m_xWrappedObject->setClientSite( m_xClientSite ); + if ( m_xParent.is() ) + { + uno::Reference< container::XChild > xChild( m_xWrappedObject, uno::UNO_QUERY ); + if ( xChild.is() ) + xChild->setParent( m_xParent ); + } + + } + + return bResult; +} + + +void SAL_CALL OleEmbeddedObject::changeState( sal_Int32 nNewState ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->changeState( nNewState ); + return; + } + // end wrapping related part ==================== + + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // in case the object is already in requested state + if ( m_nObjectState == nNewState ) + return; + +#ifdef _WIN32 + if ( m_pOleComponent ) + { + if ( m_nTargetState != -1 ) + { + // means that the object is currently trying to reach the target state + throw embed::StateChangeInProgressException( OUString(), + uno::Reference< uno::XInterface >(), + m_nTargetState ); + } + + TargetStateControl_Impl aControl( m_nTargetState, nNewState ); + + // TODO: additional verbs can be a problem, since nobody knows how the object + // will behave after activation + + sal_Int32 nOldState = m_nObjectState; + aGuard.clear(); + StateChangeNotification_Impl( true, nOldState, nNewState ); + aGuard.reset(); + + try + { + if ( nNewState == embed::EmbedStates::LOADED ) + { + // This means just closing of the current object + // If component can not be closed the object stays in loaded state + // and it holds reference to "incomplete" component + // If the object is switched to running state later + // the component will become "complete" + + // the loaded state must be set before, because of notifications! + m_nObjectState = nNewState; + + { + VerbExecutionControllerGuard aVerbGuard( m_aVerbExecutionController ); + m_pOleComponent->CloseObject(); + } + + aGuard.clear(); + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + aGuard.reset(); + } + else if ( nNewState == embed::EmbedStates::RUNNING || nNewState == embed::EmbedStates::ACTIVE ) + { + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + // if the target object is in loaded state and a different state is specified + // as a new one the object first must be switched to running state. + + // the component can exist already in nonrunning state + // it can be created during loading to detect type of object + CreateOleComponentAndLoad_Impl( m_pOleComponent ); + + SwitchComponentToRunningState_Impl(); + m_nObjectState = embed::EmbedStates::RUNNING; + aGuard.clear(); + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + aGuard.reset(); + + if ( m_pOleComponent && m_bHasSizeToSet ) + { + aGuard.clear(); + try { + m_pOleComponent->SetExtent( m_aSizeToSet, m_nAspectToSet ); + m_bHasSizeToSet = false; + } + catch( const uno::Exception& ) {} + aGuard.reset(); + } + + if ( m_nObjectState == nNewState ) + return; + } + + // so now the object is either switched from Active to Running state or viceversa + // the notification about object state change will be done asynchronously + if ( m_nObjectState == embed::EmbedStates::RUNNING && nNewState == embed::EmbedStates::ACTIVE ) + { + // execute OPEN verb, if object does not reach active state it is an object's problem + aGuard.clear(); + m_pOleComponent->ExecuteVerb( embed::EmbedVerbs::MS_OLEVERB_OPEN ); + aGuard.reset(); + + // some objects do not allow to set the size even in running state + if ( m_pOleComponent && m_bHasSizeToSet ) + { + aGuard.clear(); + try { + m_pOleComponent->SetExtent( m_aSizeToSet, m_nAspectToSet ); + m_bHasSizeToSet = false; + } + catch( uno::Exception& ) {} + aGuard.reset(); + } + + m_nObjectState = nNewState; + } + else if ( m_nObjectState == embed::EmbedStates::ACTIVE && nNewState == embed::EmbedStates::RUNNING ) + { + aGuard.clear(); + m_pOleComponent->CloseObject(); + m_pOleComponent->RunObject(); // Should not fail, the object already was active + aGuard.reset(); + m_nObjectState = nNewState; + } + else + { + throw embed::UnreachableStateException(); + } + } + else + throw embed::UnreachableStateException(); + } + catch( uno::Exception& ) + { + aGuard.clear(); + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + throw; + } + } + else +#endif + { + throw embed::UnreachableStateException(); + } +} + + +uno::Sequence< sal_Int32 > SAL_CALL OleEmbeddedObject::getReachableStates() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getReachableStates(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + +#ifdef _WIN32 + if ( m_pOleComponent ) + { + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + // the list of supported verbs can be retrieved only when object is in running state + throw embed::NeedsRunningStateException(); // TODO: + } + + // the list of states can only be guessed based on standard verbs, + // since there is no way to detect what additional verbs do + return GetReachableStatesList_Impl( m_pOleComponent->GetVerbList() ); + } + else +#endif + { + return uno::Sequence< sal_Int32 >(); + } +} + + +sal_Int32 SAL_CALL OleEmbeddedObject::getCurrentState() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getCurrentState(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // TODO: Shouldn't we ask object? ( I guess no ) + return m_nObjectState; +} + +namespace +{ + bool lcl_CopyStream(const uno::Reference<io::XInputStream>& xIn, const uno::Reference<io::XOutputStream>& xOut, sal_Int32 nMaxCopy = SAL_MAX_INT32) + { + if (nMaxCopy <= 0) + return false; + + const sal_Int32 nChunkSize = 4096; + uno::Sequence< sal_Int8 > aData(nChunkSize); + sal_Int32 nTotalRead = 0; + sal_Int32 nRead; + do + { + if (nTotalRead + aData.getLength() > nMaxCopy) + { + aData.realloc(nMaxCopy - nTotalRead); + } + nRead = xIn->readBytes(aData, aData.getLength()); + nTotalRead += nRead; + xOut->writeBytes(aData); + } while (nRead == nChunkSize && nTotalRead <= nMaxCopy); + return nTotalRead != 0; + } + + uno::Reference < io::XStream > lcl_GetExtractedStream( OUString& rUrl, + const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Reference< css::io::XStream >& xObjectStream ) + { + uno::Reference <io::XTempFile> xNativeTempFile( + io::TempFile::create(xContext), + uno::UNO_SET_THROW); + uno::Reference < io::XStream > xStream(xNativeTempFile); + + uno::Sequence< uno::Any > aArgs{ uno::Any(xObjectStream), + uno::Any(true) }; // do not create copy + uno::Reference< container::XNameContainer > xNameContainer( + xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.embed.OLESimpleStorage", + aArgs, xContext ), uno::UNO_QUERY_THROW ); + + //various stream names that can contain the real document contents for + //this object in a straightforward direct way + static const std::u16string_view aStreamNames[] = + { + u"CONTENTS", + u"Package", + u"EmbeddedOdf", + u"WordDocument", + u"Workbook", + u"PowerPoint Document" + }; + + bool bCopied = false; + for (size_t i = 0; i < std::size(aStreamNames) && !bCopied; ++i) + { + uno::Reference<io::XStream> xEmbeddedFile; + try + { + xNameContainer->getByName(OUString(aStreamNames[i])) >>= xEmbeddedFile; + } + catch (const container::NoSuchElementException&) + { + // ignore + } + bCopied = xEmbeddedFile.is() && lcl_CopyStream(xEmbeddedFile->getInputStream(), xStream->getOutputStream()); + } + + if (!bCopied) + { + uno::Reference< io::XStream > xOle10Native; + try + { + xNameContainer->getByName("\1Ole10Native") >>= xOle10Native; + } + catch (container::NoSuchElementException const&) + { + // ignore + } + if (xOle10Native.is()) + { + const uno::Reference<io::XInputStream> xIn = xOle10Native->getInputStream(); + xIn->skipBytes(4); //size of the entire stream minus 4 bytes + xIn->skipBytes(2); //word that represent the directory type + uno::Sequence< sal_Int8 > aData(1); + sal_Int32 nRead; + do + { + nRead = xIn->readBytes(aData, 1); + } while (nRead == 1 && aData[0] != 0); // file name plus extension of the attachment null terminated + do + { + nRead = xIn->readBytes(aData, 1); + } while (nRead == 1 && aData[0] != 0); // Fully Qualified File name with extension + xIn->skipBytes(1); //single byte + xIn->skipBytes(1); //single byte + xIn->skipBytes(2); //Word that represent the directory type + xIn->skipBytes(4); //len of string + do + { + nRead = xIn->readBytes(aData, 1); + } while (nRead == 1 && aData[0] != 0); // Actual string representing the file path + uno::Sequence< sal_Int8 > aLenData(4); + xIn->readBytes(aLenData, 4); //len of attachment + sal_uInt32 nLen = static_cast<sal_uInt32>( + (aLenData[0] & 0xFF) | + ((aLenData[1] & 0xFF) << 8) | + ((aLenData[2] & 0xFF) << 16) | + ((aLenData[3] & 0xFF) << 24)); + + bCopied = lcl_CopyStream(xIn, xStream->getOutputStream(), nLen); + } + } + + uno::Reference< io::XSeekable > xSeekableStor(xObjectStream, uno::UNO_QUERY); + if (xSeekableStor.is()) + xSeekableStor->seek(0); + + if (!bCopied) + bCopied = lcl_CopyStream(xObjectStream->getInputStream(), xStream->getOutputStream()); + + if (bCopied) + { + xNativeTempFile->setRemoveFile(false); + rUrl = xNativeTempFile->getUri(); + + xNativeTempFile.clear(); + + uno::Reference < ucb::XSimpleFileAccess3 > xSimpleFileAccess( + ucb::SimpleFileAccess::create( xContext ) ); + + xSimpleFileAccess->setReadOnly(rUrl, true); + } + else + { + xNativeTempFile->setRemoveFile(true); + } + + return xStream; + } + + //Dump the objects content to a tempfile, just the "CONTENTS" stream if + //there is one for non-compound documents, otherwise the whole content. + //On success a file is returned which must be removed by the caller + OUString lcl_ExtractObject(const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Reference< css::io::XStream >& xObjectStream) + { + OUString sUrl; + + // the solution is only active for Unix systems +#ifndef _WIN32 + lcl_GetExtractedStream(sUrl, xContext, xObjectStream); +#else + (void) xContext; + (void) xObjectStream; +#endif + return sUrl; + } + + uno::Reference < io::XStream > lcl_ExtractObjectStream( const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Reference< css::io::XStream >& xObjectStream ) + { + OUString sUrl; + return lcl_GetExtractedStream( sUrl, xContext, xObjectStream ); + } +} + + +void SAL_CALL OleEmbeddedObject::doVerb( sal_Int32 nVerbID ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->doVerb(embed::EmbedVerbs::MS_OLEVERB_OPEN); // open content in the window not in-place + return; + } + // end wrapping related part ==================== + + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + +#ifdef _WIN32 + if ( m_pOleComponent ) + { + sal_Int32 nOldState = m_nObjectState; + + // TODO/LATER detect target state here and do a notification + // StateChangeNotification_Impl( sal_True, nOldState, nNewState ); + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + // if the target object is in loaded state + // it must be switched to running state to execute verb + aGuard.clear(); + changeState( embed::EmbedStates::RUNNING ); + aGuard.reset(); + } + + try { + if ( !m_pOleComponent ) + throw uno::RuntimeException("Null reference to OLE component"); + + // ==== the STAMPIT related solution ============================= + m_aVerbExecutionController.StartControlExecution(); + + + m_pOleComponent->ExecuteVerb( nVerbID ); + m_pOleComponent->SetHostName( m_aContainerName ); + + // ==== the STAMPIT related solution ============================= + bool bModifiedOnExecution = m_aVerbExecutionController.EndControlExecution_WasModified(); + + // this workaround is implemented for STAMPIT object + // if object was modified during verb execution it is saved here + if ( bModifiedOnExecution && m_pOleComponent->IsDirty() ) + SaveObject_Impl(); + + } + catch( uno::Exception& ) + { + // ==== the STAMPIT related solution ============================= + m_aVerbExecutionController.EndControlExecution_WasModified(); + + + aGuard.clear(); + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + throw; + } + + } + else +#endif + { + if ( nVerbID != -9 ) + { + + throw embed::UnreachableStateException(); + } + + // the workaround verb to show the object in case no server is available + + // if it is possible, the object will be converted to OOo format + if ( !m_bTriedConversion ) + { + m_bTriedConversion = true; + if ( TryToConvertToOOo( m_xObjectStream ) ) + { + changeState( embed::EmbedStates::ACTIVE ); + return; + } + } + + if ( !m_xOwnView.is() && m_xObjectStream.is() && m_aFilterName != "Text" ) + { + try { + uno::Reference< io::XSeekable > xSeekable( m_xObjectStream, uno::UNO_QUERY ); + if ( xSeekable.is() ) + xSeekable->seek( 0 ); + + m_xOwnView = new OwnView_Impl( m_xContext, m_xObjectStream->getInputStream() ); + } + catch( uno::RuntimeException& ) + { + throw; + } + catch (uno::Exception const&) + { + TOOLS_WARN_EXCEPTION("embeddedobj.ole", "OleEmbeddedObject::doVerb: -9 fallback path:"); + } + } + + // it may be the OLE Storage, try to extract stream + if ( !m_xOwnView.is() && m_xObjectStream.is() && m_aFilterName == "Text" ) + { + uno::Reference< io::XStream > xStream = lcl_ExtractObjectStream( m_xContext, m_xObjectStream ); + + if ( TryToConvertToOOo( xStream ) ) + { + changeState( embed::EmbedStates::ACTIVE ); + return; + } + } + + if (!m_xOwnView.is() || !m_xOwnView->Open()) + { + //Make a RO copy and see if the OS can find something to at + //least display the content for us + if (m_aTempDumpURL.isEmpty()) + m_aTempDumpURL = lcl_ExtractObject(m_xContext, m_xObjectStream); + + if (m_aTempDumpURL.isEmpty()) + throw embed::UnreachableStateException(); + + uno::Reference< css::system::XSystemShellExecute > xSystemShellExecute( + css::system::SystemShellExecute::create( m_xContext ) ); + xSystemShellExecute->execute(m_aTempDumpURL, OUString(), css::system::SystemShellExecuteFlags::URIS_ONLY); + + } + + } +} + + +uno::Sequence< embed::VerbDescriptor > SAL_CALL OleEmbeddedObject::getSupportedVerbs() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getSupportedVerbs(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); +#ifdef _WIN32 + if ( m_pOleComponent ) + { + // registry could be used in this case + // if ( m_nObjectState == embed::EmbedStates::LOADED ) + // { + // // the list of supported verbs can be retrieved only when object is in running state + // throw embed::NeedsRunningStateException(); // TODO: + // } + + return m_pOleComponent->GetVerbList(); + } + else +#endif + { + // tdf#140079 Claim support for the OleEmbeddedObject::doVerb -9 fallback. + // So in SfxViewFrame::GetState_Impl in case SID_OBJECT hasVerbs is not + // empty, so that the doVerb attempt with -9 fallback is attempted + uno::Sequence<embed::VerbDescriptor> aRet(1); + aRet.getArray()[0].VerbID = -9; + return aRet; + } +} + + +void SAL_CALL OleEmbeddedObject::setClientSite( + const uno::Reference< embed::XEmbeddedClient >& xClient ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setClientSite( xClient ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_xClientSite != xClient) + { + if ( m_nObjectState != embed::EmbedStates::LOADED && m_nObjectState != embed::EmbedStates::RUNNING ) + throw embed::WrongStateException( + "The client site can not be set currently!", + static_cast< ::cppu::OWeakObject* >(this) ); + + m_xClientSite = xClient; + } +} + + +uno::Reference< embed::XEmbeddedClient > SAL_CALL OleEmbeddedObject::getClientSite() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getClientSite(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_xClientSite; +} + + +void SAL_CALL OleEmbeddedObject::update() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->update(); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_nUpdateMode == embed::EmbedUpdateModes::EXPLICIT_UPDATE ) + { + // TODO: update view representation + } + else + { + // the object must be up to date + SAL_WARN_IF( m_nUpdateMode != embed::EmbedUpdateModes::ALWAYS_UPDATE, "embeddedobj.ole", "Unknown update mode!" ); + } +} + + +void SAL_CALL OleEmbeddedObject::setUpdateMode( sal_Int32 nMode ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setUpdateMode( nMode ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + OSL_ENSURE( nMode == embed::EmbedUpdateModes::ALWAYS_UPDATE + || nMode == embed::EmbedUpdateModes::EXPLICIT_UPDATE, + "Unknown update mode!" ); + m_nUpdateMode = nMode; +} + + +sal_Int64 SAL_CALL OleEmbeddedObject::getStatus( sal_Int64 + nAspect +) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getStatus( nAspect ); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object must be in running state!", + static_cast< ::cppu::OWeakObject* >(this) ); + + sal_Int64 nResult = 0; + +#ifdef _WIN32 + if ( m_bGotStatus && m_nStatusAspect == nAspect ) + nResult = m_nStatus; + else if ( m_pOleComponent ) + { + + m_nStatus = m_pOleComponent->GetMiscStatus( nAspect ); + m_nStatusAspect = nAspect; + m_bGotStatus = true; + nResult = m_nStatus; + } +#endif + + // this implementation needs size to be provided after object loading/creating to work in optimal way + return ( nResult | embed::EmbedMisc::EMBED_NEEDSSIZEONLOAD ); +} + + +void SAL_CALL OleEmbeddedObject::setContainerName( const OUString& sName ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setContainerName( sName ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + m_aContainerName = sName; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olemisc.cxx b/embeddedobj/source/msole/olemisc.cxx new file mode 100644 index 0000000000..4f50aedeba --- /dev/null +++ b/embeddedobj/source/msole/olemisc.cxx @@ -0,0 +1,710 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cassert> + +#include <com/sun/star/embed/EmbedUpdateModes.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/DisposedException.hpp> + +#include <comphelper/multicontainer2.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <osl/diagnose.h> + +#include <oleembobj.hxx> +#include <utility> +#include "olepersist.hxx" + +#include "ownview.hxx" + +#include "olecomponent.hxx" + +using namespace ::com::sun::star; + + +OleEmbeddedObject::OleEmbeddedObject( uno::Reference< uno::XComponentContext > xContext, + const uno::Sequence< sal_Int8 >& aClassID, + OUString aClassName ) +: m_bReadOnly( false ) +, m_bDisposed( false ) +, m_nObjectState( -1 ) +, m_nTargetState( -1 ) +, m_nUpdateMode ( embed::EmbedUpdateModes::ALWAYS_UPDATE ) +, m_xContext(std::move( xContext )) +, m_aClassID( aClassID ) +, m_aClassName(std::move( aClassName )) +, m_bWaitSaveCompleted( false ) +, m_bNewVisReplInStream( true ) +, m_bStoreLoaded( false ) +, m_bVisReplInitialized( false ) +, m_bVisReplInStream( false ) +, m_bStoreVisRepl( false ) +, m_bIsLink( false ) +, m_bHasCachedSize( false ) +, m_nCachedAspect( 0 ) +, m_bHasSizeToSet( false ) +, m_nAspectToSet( 0 ) +, m_bGotStatus( false ) +, m_nStatus( 0 ) +, m_nStatusAspect( 0 ) +, m_bFromClipboard( false ) +, m_bTriedConversion( false ) +{ +} + + +// In case of loading from persistent entry the classID of the object +// will be retrieved from the entry, during construction it is unknown +OleEmbeddedObject::OleEmbeddedObject( uno::Reference< uno::XComponentContext > xContext, bool bLink ) +: m_bReadOnly( false ) +, m_bDisposed( false ) +, m_nObjectState( -1 ) +, m_nTargetState( -1 ) +, m_nUpdateMode( embed::EmbedUpdateModes::ALWAYS_UPDATE ) +, m_xContext(std::move( xContext )) +, m_bWaitSaveCompleted( false ) +, m_bNewVisReplInStream( true ) +, m_bStoreLoaded( false ) +, m_bVisReplInitialized( false ) +, m_bVisReplInStream( false ) +, m_bStoreVisRepl( false ) +, m_bIsLink( bLink ) +, m_bHasCachedSize( false ) +, m_nCachedAspect( 0 ) +, m_bHasSizeToSet( false ) +, m_nAspectToSet( 0 ) +, m_bGotStatus( false ) +, m_nStatus( 0 ) +, m_nStatusAspect( 0 ) +, m_bFromClipboard( false ) +, m_bTriedConversion( false ) +{ +} +#ifdef _WIN32 + +// this constructor let object be initialized from clipboard +OleEmbeddedObject::OleEmbeddedObject( const uno::Reference< uno::XComponentContext >& xContext ) +: m_bReadOnly( false ) +, m_bDisposed( false ) +, m_nObjectState( -1 ) +, m_nTargetState( -1 ) +, m_nUpdateMode( embed::EmbedUpdateModes::ALWAYS_UPDATE ) +, m_xContext( xContext ) +, m_bWaitSaveCompleted( false ) +, m_bNewVisReplInStream( true ) +, m_bStoreLoaded( false ) +, m_bVisReplInitialized( false ) +, m_bVisReplInStream( false ) +, m_bStoreVisRepl( false ) +, m_bIsLink( false ) +, m_bHasCachedSize( false ) +, m_nCachedAspect( 0 ) +, m_bHasSizeToSet( false ) +, m_nAspectToSet( 0 ) +, m_bGotStatus( false ) +, m_nStatus( 0 ) +, m_nStatusAspect( 0 ) +, m_bFromClipboard( true ) +, m_bTriedConversion( false ) +{ +} +#endif + +OleEmbeddedObject::~OleEmbeddedObject() +{ + OSL_ENSURE( !m_pInterfaceContainer && !m_pOleComponent && !m_xObjectStream.is(), + "The object is not closed! DISASTER is possible!" ); + + if ( m_pOleComponent || m_pInterfaceContainer || m_xObjectStream.is() ) + { + // the component must be cleaned during closing + osl_atomic_increment(&m_refCount); // to avoid crash + try { + Dispose(); + } catch( const uno::Exception& ) {} + } + + if ( !m_aTempURL.isEmpty() ) + KillFile_Impl( m_aTempURL, m_xContext ); + + if ( !m_aTempDumpURL.isEmpty() ) + KillFile_Impl( m_aTempDumpURL, m_xContext ); +} + + +void OleEmbeddedObject::MakeEventListenerNotification_Impl( const OUString& aEventName ) +{ + if ( !m_pInterfaceContainer ) + return; + + comphelper::OInterfaceContainerHelper2* pContainer = + m_pInterfaceContainer->getContainer( + cppu::UnoType<document::XEventListener>::get()); + if ( pContainer == nullptr ) + return; + + document::EventObject aEvent( static_cast< ::cppu::OWeakObject* >( this ), aEventName ); + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + static_cast<document::XEventListener*>(pIterator.next())->notifyEvent( aEvent ); + } + catch( const uno::RuntimeException& ) + { + } + } +} +#ifdef _WIN32 + +void OleEmbeddedObject::StateChangeNotification_Impl( bool bBeforeChange, sal_Int32 nOldState, sal_Int32 nNewState ) +{ + if ( m_pInterfaceContainer ) + { + comphelper::OInterfaceContainerHelper2* pContainer = m_pInterfaceContainer->getContainer( + cppu::UnoType<embed::XStateChangeListener>::get()); + if ( pContainer != nullptr ) + { + lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >( this ) ); + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + + while (pIterator.hasMoreElements()) + { + if ( bBeforeChange ) + { + try + { + static_cast<embed::XStateChangeListener*>(pIterator.next())->changingState( aSource, nOldState, nNewState ); + } + catch( const uno::Exception& ) + { + // even if the listener complains ignore it for now + } + } + else + { + try + { + static_cast<embed::XStateChangeListener*>(pIterator.next())->stateChanged( aSource, nOldState, nNewState ); + } + catch( const uno::Exception& ) + { + // if anything happened it is problem of listener, ignore it + } + } + } + } + } +} +#endif + +void OleEmbeddedObject::GetRidOfComponent() +{ +#ifdef _WIN32 + if ( m_pOleComponent ) + { + if ( m_nObjectState != -1 && m_nObjectState != embed::EmbedStates::LOADED ) + SaveObject_Impl(); + + m_pOleComponent->removeCloseListener( m_xClosePreventer ); + try + { + m_pOleComponent->close( false ); + } + catch( const uno::Exception& ) + { + // TODO: there should be a special listener to wait for component closing + // and to notify object, may be object itself can be such a listener + m_pOleComponent->addCloseListener( m_xClosePreventer ); + throw; + } + + m_pOleComponent->disconnectEmbeddedObject(); + m_pOleComponent.clear(); + } +#endif +} + + +void OleEmbeddedObject::Dispose() +{ + if ( m_pInterfaceContainer ) + { + lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >( this ) ); + m_pInterfaceContainer->disposeAndClear( aSource ); + m_pInterfaceContainer.reset(); + } + + if ( m_xOwnView.is() ) + { + m_xOwnView->Close(); + m_xOwnView.clear(); + } + + if ( m_pOleComponent ) + try { + GetRidOfComponent(); + } catch( const uno::Exception& ) + { + m_bDisposed = true; + throw; // TODO: there should be a special listener that will close object when + // component is finally closed + } + + if ( m_xObjectStream.is() ) + { + uno::Reference< lang::XComponent > xComp( m_xObjectStream, uno::UNO_QUERY ); + OSL_ENSURE( xComp.is(), "Storage stream doesn't support XComponent!" ); + + if ( xComp.is() ) + { + try { + xComp->dispose(); + } catch( const uno::Exception& ) {} + } + m_xObjectStream.clear(); + } + + m_xParentStorage.clear(); + m_xClientSite.clear(); + m_xClosePreventer.clear(); + m_xNewCachedVisRepl.clear(); + m_xNewParentStorage.clear(); + m_xNewObjectStream.clear(); + m_xCachedVisualRepresentation.clear(); + m_xWrappedObject.clear(); + m_xParent.clear(); + m_pOleComponent.clear(); + + m_bDisposed = true; +} + + +uno::Sequence< sal_Int8 > SAL_CALL OleEmbeddedObject::getClassID() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getClassID(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + return m_aClassID; +} + + +OUString SAL_CALL OleEmbeddedObject::getClassName() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getClassName(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + return m_aClassName; +} + + +void SAL_CALL OleEmbeddedObject::setClassInfo( + const uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setClassInfo( aClassID, aClassName ); + return; + } + // end wrapping related part ==================== + + // the object class info can not be changed explicitly + throw lang::NoSupportException(); //TODO: +} + + +uno::Reference< util::XCloseable > SAL_CALL OleEmbeddedObject::getComponent() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getComponent(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) // || m_nObjectState == embed::EmbedStates::LOADED ) + { + // the object is still not running + throw uno::RuntimeException( "The object is not loaded!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + +#if defined(_WIN32) + if (m_pOleComponent.is()) + { + return uno::Reference< util::XCloseable >( m_pOleComponent ); + } +#endif + + assert(!m_pOleComponent.is()); + // TODO/LATER: Is it correct??? + return uno::Reference< util::XCloseable >(); + // throw uno::RuntimeException(); // TODO +} + + +void SAL_CALL OleEmbeddedObject::addStateChangeListener( const uno::Reference< embed::XStateChangeListener >& xListener ) +{ + // begin wrapping related part ==================== + if ( m_xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + m_xWrappedObject->addStateChangeListener( xListener ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer.reset(new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex )); + + m_pInterfaceContainer->addInterface( cppu::UnoType<embed::XStateChangeListener>::get(), + xListener ); +} + + +void SAL_CALL OleEmbeddedObject::removeStateChangeListener( + const uno::Reference< embed::XStateChangeListener >& xListener ) +{ + // begin wrapping related part ==================== + if ( m_xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + m_xWrappedObject->removeStateChangeListener( xListener ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<embed::XStateChangeListener>::get(), + xListener ); +} + + +void SAL_CALL OleEmbeddedObject::close( sal_Bool bDeliverOwnership ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->close( bDeliverOwnership ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + uno::Reference< uno::XInterface > xSelfHold( static_cast< ::cppu::OWeakObject* >( this ) ); + lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >( this ) ); + + if ( m_pInterfaceContainer ) + { + comphelper::OInterfaceContainerHelper2* pContainer = + m_pInterfaceContainer->getContainer( cppu::UnoType<util::XCloseListener>::get()); + if ( pContainer != nullptr ) + { + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + static_cast<util::XCloseListener*>(pIterator.next())->queryClosing( aSource, bDeliverOwnership ); + } + catch( const uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + + pContainer = m_pInterfaceContainer->getContainer( + cppu::UnoType<util::XCloseListener>::get()); + if ( pContainer != nullptr ) + { + comphelper::OInterfaceIteratorHelper2 pCloseIterator(*pContainer); + while (pCloseIterator.hasMoreElements()) + { + try + { + static_cast<util::XCloseListener*>(pCloseIterator.next())->notifyClosing( aSource ); + } + catch( const uno::RuntimeException& ) + { + pCloseIterator.remove(); + } + } + } + } + + Dispose(); +} + + +void SAL_CALL OleEmbeddedObject::addCloseListener( const uno::Reference< util::XCloseListener >& xListener ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->addCloseListener( xListener ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer.reset(new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex )); + + m_pInterfaceContainer->addInterface( cppu::UnoType<util::XCloseListener>::get(), xListener ); +} + + +void SAL_CALL OleEmbeddedObject::removeCloseListener( const uno::Reference< util::XCloseListener >& xListener ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->removeCloseListener( xListener ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<util::XCloseListener>::get(), + xListener ); +} + + +void SAL_CALL OleEmbeddedObject::addEventListener( const uno::Reference< document::XEventListener >& xListener ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->addEventListener( xListener ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer.reset(new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex )); + + m_pInterfaceContainer->addInterface( cppu::UnoType<document::XEventListener>::get(), xListener ); +} + + +void SAL_CALL OleEmbeddedObject::removeEventListener( + const uno::Reference< document::XEventListener >& xListener ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->removeEventListener( xListener ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<document::XEventListener>::get(), + xListener ); +} + +// XInplaceObject ( wrapper related implementation ) + +void SAL_CALL OleEmbeddedObject::setObjectRectangles( const awt::Rectangle& aPosRect, + const awt::Rectangle& aClipRect ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XInplaceObject > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setObjectRectangles( aPosRect, aClipRect ); + return; + } + // end wrapping related part ==================== + + throw embed::WrongStateException(); +} + + +void SAL_CALL OleEmbeddedObject::enableModeless( sal_Bool bEnable ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XInplaceObject > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->enableModeless( bEnable ); + return; + } + // end wrapping related part ==================== + + throw embed::WrongStateException(); +} + + +void SAL_CALL OleEmbeddedObject::translateAccelerators( + const uno::Sequence< awt::KeyEvent >& aKeys ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XInplaceObject > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->translateAccelerators( aKeys ); + return; + } + // end wrapping related part ==================== + +} + +// XChild + +css::uno::Reference< css::uno::XInterface > SAL_CALL OleEmbeddedObject::getParent() +{ + // begin wrapping related part ==================== + uno::Reference< container::XChild > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getParent(); + } + // end wrapping related part ==================== + + return m_xParent; +} + + +void SAL_CALL OleEmbeddedObject::setParent( const css::uno::Reference< css::uno::XInterface >& xParent ) +{ + // begin wrapping related part ==================== + uno::Reference< container::XChild > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setParent( xParent ); + return; + } + // end wrapping related part ==================== + + m_xParent = xParent; +} + +void OleEmbeddedObject::setStream(const css::uno::Reference<css::io::XStream>& xStream) +{ + m_xObjectStream = xStream; +} + +css::uno::Reference<css::io::XStream> OleEmbeddedObject::getStream() +{ + return m_xObjectStream; +} + +void OleEmbeddedObject::initialize(const uno::Sequence<uno::Any>& rArguments) +{ + if (!rArguments.hasElements()) + return; + + comphelper::SequenceAsHashMap aValues(rArguments[0]); + auto it = aValues.find("StreamReadOnly"); + if (it != aValues.end()) + it->second >>= m_bStreamReadOnly; +} + +OUString SAL_CALL OleEmbeddedObject::getImplementationName() +{ + return "com.sun.star.comp.embed.OleEmbeddedObject"; +} + +sal_Bool SAL_CALL OleEmbeddedObject::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence<OUString> SAL_CALL OleEmbeddedObject::getSupportedServiceNames() +{ + return { "com.sun.star.comp.embed.OleEmbeddedObject" }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olepersist.cxx b/embeddedobj/source/msole/olepersist.cxx new file mode 100644 index 0000000000..86403f41bb --- /dev/null +++ b/embeddedobj/source/msole/olepersist.cxx @@ -0,0 +1,2019 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <oleembobj.hxx> +#include "olepersist.hxx" +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/EmbedVerbs.hpp> +#include <com/sun/star/embed/EntryInitModes.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/EmbedUpdateModes.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/XOptimizedStorage.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/io/XTruncate.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/packages/WrongPasswordException.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/io/IOException.hpp> + +#include <comphelper/storagehelper.hxx> +#include <comphelper/mimeconfighelper.hxx> +#include <comphelper/classids.hxx> +#include <osl/diagnose.h> +#include <osl/thread.hxx> +#include <rtl/ref.hxx> +#include <sal/log.hxx> + +#include <closepreventer.hxx> + +#if defined(_WIN32) +#include "olecomponent.hxx" +#endif + +using namespace ::com::sun::star; +using namespace ::comphelper; + + +bool KillFile_Impl( const OUString& aURL, const uno::Reference< uno::XComponentContext >& xContext ) +{ + if ( !xContext.is() ) + return false; + + bool bRet = false; + + try + { + uno::Reference < ucb::XSimpleFileAccess3 > xAccess( + ucb::SimpleFileAccess::create( xContext ) ); + + xAccess->kill( aURL ); + bRet = true; + } + catch( const uno::Exception& ) + { + } + + return bRet; +} + + +OUString GetNewTempFileURL_Impl( const uno::Reference< uno::XComponentContext >& xContext ) +{ + SAL_WARN_IF( !xContext.is(), "embeddedobj.ole", "No factory is provided!" ); + + OUString aResult; + + uno::Reference < io::XTempFile > xTempFile( + io::TempFile::create(xContext), + uno::UNO_SET_THROW ); + + try { + xTempFile->setRemoveFile( false ); + aResult = xTempFile->getUri(); + } + catch ( const uno::Exception& ) + { + } + + if ( aResult.isEmpty() ) + throw uno::RuntimeException("Cannot create tempfile."); + + return aResult; +} + + +OUString GetNewFilledTempFile_Impl( const uno::Reference< io::XInputStream >& xInStream, + const uno::Reference< uno::XComponentContext >& xContext ) +{ + OSL_ENSURE( xInStream.is() && xContext.is(), "Wrong parameters are provided!" ); + + OUString aResult = GetNewTempFileURL_Impl( xContext ); + + if ( !aResult.isEmpty() ) + { + try { + uno::Reference < ucb::XSimpleFileAccess3 > xTempAccess( + ucb::SimpleFileAccess::create( xContext ) ); + + uno::Reference< io::XOutputStream > xTempOutStream = xTempAccess->openFileWrite( aResult ); + if ( !xTempOutStream.is() ) + throw io::IOException(); // TODO: + // copy stream contents to the file + ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xTempOutStream ); + xTempOutStream->closeOutput(); + xTempOutStream.clear(); + } + catch( const packages::WrongPasswordException& ) + { + KillFile_Impl( aResult, xContext ); + throw io::IOException(); //TODO: + } + catch( const io::IOException& ) + { + KillFile_Impl( aResult, xContext ); + throw; + } + catch( const uno::RuntimeException& ) + { + KillFile_Impl( aResult, xContext ); + throw; + } + catch( const uno::Exception& ) + { + KillFile_Impl( aResult, xContext ); + aResult.clear(); + } + } + + return aResult; +} +#ifdef _WIN32 +/// @throws io::IOException +/// @throws uno::RuntimeException +static OUString GetNewFilledTempFile_Impl( const uno::Reference< embed::XOptimizedStorage >& xParentStorage, const OUString& aEntryName, const uno::Reference< uno::XComponentContext >& xContext ) +{ + OUString aResult; + + try + { + uno::Reference < io::XTempFile > xTempFile( + io::TempFile::create(xContext), + uno::UNO_SET_THROW ); + + xParentStorage->copyStreamElementData( aEntryName, xTempFile ); + + xTempFile->setRemoveFile( false ); + aResult = xTempFile->getUri(); + } + catch( const uno::RuntimeException& ) + { + throw; + } + catch( const uno::Exception& ) + { + } + + if ( aResult.isEmpty() ) + throw io::IOException(); + + return aResult; +} + + +static void SetStreamMediaType_Impl( const uno::Reference< io::XStream >& xStream, const OUString& aMediaType ) +{ + uno::Reference< beans::XPropertySet > xPropSet( xStream, uno::UNO_QUERY_THROW ); + xPropSet->setPropertyValue("MediaType", uno::Any( aMediaType ) ); +} +#endif + +static void LetCommonStoragePassBeUsed_Impl( const uno::Reference< io::XStream >& xStream ) +{ + uno::Reference< beans::XPropertySet > xPropSet( xStream, uno::UNO_QUERY_THROW ); + xPropSet->setPropertyValue("UseCommonStoragePasswordEncryption", + uno::Any( true ) ); +} +#ifdef _WIN32 + +void VerbExecutionController::StartControlExecution() +{ + osl::MutexGuard aGuard( m_aVerbExecutionMutex ); + + // the class is used to detect STAMPIT object, that can never be active + if ( !m_bVerbExecutionInProgress && !m_bWasEverActive ) + { + m_bVerbExecutionInProgress = true; + m_nVerbExecutionThreadIdentifier = osl::Thread::getCurrentIdentifier(); + m_bChangedOnVerbExecution = false; + } +} + + +bool VerbExecutionController::EndControlExecution_WasModified() +{ + osl::MutexGuard aGuard( m_aVerbExecutionMutex ); + + bool bResult = false; + if ( m_bVerbExecutionInProgress && m_nVerbExecutionThreadIdentifier == osl::Thread::getCurrentIdentifier() ) + { + bResult = m_bChangedOnVerbExecution; + m_bVerbExecutionInProgress = false; + } + + return bResult; +} + + +void VerbExecutionController::ModificationNotificationIsDone() +{ + osl::MutexGuard aGuard( m_aVerbExecutionMutex ); + + if ( m_bVerbExecutionInProgress && osl::Thread::getCurrentIdentifier() == m_nVerbExecutionThreadIdentifier ) + m_bChangedOnVerbExecution = true; +} +#endif + +void VerbExecutionController::LockNotification() +{ + osl::MutexGuard aGuard( m_aVerbExecutionMutex ); + if ( m_nNotificationLock < SAL_MAX_INT32 ) + m_nNotificationLock++; +} + + +void VerbExecutionController::UnlockNotification() +{ + osl::MutexGuard aGuard( m_aVerbExecutionMutex ); + if ( m_nNotificationLock > 0 ) + m_nNotificationLock--; +} + + +uno::Reference< io::XStream > OleEmbeddedObject::GetNewFilledTempStream_Impl( const uno::Reference< io::XInputStream >& xInStream ) +{ + SAL_WARN_IF( !xInStream.is(), "embeddedobj.ole", "Wrong parameter is provided!" ); + + uno::Reference < io::XStream > xTempFile( + io::TempFile::create(m_xContext), + uno::UNO_QUERY_THROW ); + + uno::Reference< io::XOutputStream > xTempOutStream = xTempFile->getOutputStream(); + if ( !xTempOutStream.is() ) + throw io::IOException(); // TODO: + ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xTempOutStream ); + xTempOutStream->flush(); + return xTempFile; +} + + +uno::Reference< io::XStream > OleEmbeddedObject::TryToGetAcceptableFormat_Impl( const uno::Reference< io::XStream >& xStream ) +{ + // TODO/LATER: Actually this should be done by a centralized component ( may be a graphical filter ) + if ( !m_xContext.is() ) + throw uno::RuntimeException(); + + uno::Reference< io::XInputStream > xInStream = xStream->getInputStream(); + if ( !xInStream.is() ) + throw uno::RuntimeException(); + + uno::Reference< io::XSeekable > xSeek( xStream, uno::UNO_QUERY_THROW ); + xSeek->seek( 0 ); + + uno::Sequence< sal_Int8 > aData( 8 ); + sal_Int32 nRead = xInStream->readBytes( aData, 8 ); + xSeek->seek( 0 ); + + if ( ( nRead >= 2 && aData[0] == 'B' && aData[1] == 'M' ) + || ( nRead >= 4 && aData[0] == 1 && aData[1] == 0 && aData[2] == 9 && aData[3] == 0 ) ) + { + // it should be a bitmap or a Metafile + return xStream; + } + + + sal_uInt32 nHeaderOffset = 0; + if ( ( nRead >= 8 && aData[0] == -1 && aData[1] == -1 && aData[2] == -1 && aData[3] == -1 ) + && ( aData[4] == 2 || aData[4] == 3 || aData[4] == 14 ) && aData[5] == 0 && aData[6] == 0 && aData[7] == 0 ) + { + nHeaderOffset = 40; + xSeek->seek( 8 ); + + // TargetDevice might be used in future, currently the cache has specified NULL + uno::Sequence< sal_Int8 > aHeadData( 4 ); + nRead = xInStream->readBytes( aHeadData, 4 ); + sal_uInt32 nLen = 0; + if ( nRead == 4 && aHeadData.getLength() == 4 ) + nLen = ( ( ( static_cast<sal_uInt32>(aHeadData[3]) * 0x100 + static_cast<sal_uInt32>(aHeadData[2]) ) * 0x100 ) + static_cast<sal_uInt32>(aHeadData[1]) ) * 0x100 + static_cast<sal_uInt32>(aHeadData[0]); + if ( nLen > 4 ) + { + xInStream->skipBytes( nLen - 4 ); + nHeaderOffset += nLen - 4; + } + + } + else if ( nRead > 4 ) + { + // check whether the first bytes represent the size + sal_uInt32 nSize = 0; + for ( sal_Int32 nInd = 3; nInd >= 0; nInd-- ) + nSize = ( nSize << 8 ) + static_cast<sal_uInt8>(aData[nInd]); + + if ( nSize == xSeek->getLength() - 4 ) + nHeaderOffset = 4; + } + + if ( nHeaderOffset ) + { + // this is either a bitmap or a metafile clipboard format, retrieve the pure stream + uno::Reference < io::XStream > xResult( + io::TempFile::create(m_xContext), + uno::UNO_QUERY_THROW ); + uno::Reference < io::XSeekable > xResultSeek( xResult, uno::UNO_QUERY_THROW ); + uno::Reference < io::XOutputStream > xResultOut = xResult->getOutputStream(); + uno::Reference < io::XInputStream > xResultIn = xResult->getInputStream(); + if ( !xResultOut.is() || !xResultIn.is() ) + throw uno::RuntimeException(); + + xSeek->seek( nHeaderOffset ); // header size for these formats + ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xResultOut ); + xResultOut->closeOutput(); + xResultSeek->seek( 0 ); + xSeek->seek( 0 ); + + return xResult; + } + + return uno::Reference< io::XStream >(); +} + + +void OleEmbeddedObject::InsertVisualCache_Impl( const uno::Reference< io::XStream >& xTargetStream, + const uno::Reference< io::XStream >& xCachedVisualRepresentation ) +{ + OSL_ENSURE( xTargetStream.is() && xCachedVisualRepresentation.is(), "Invalid arguments!" ); + + if ( !xTargetStream.is() || !xCachedVisualRepresentation.is() ) + throw uno::RuntimeException(); + + uno::Sequence< uno::Any > aArgs{ uno::Any(xTargetStream), + uno::Any(true) }; // do not create copy + + uno::Reference< container::XNameContainer > xNameContainer( + m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.embed.OLESimpleStorage", + aArgs, m_xContext ), + uno::UNO_QUERY_THROW ); + + uno::Reference< io::XSeekable > xCachedSeek( xCachedVisualRepresentation, uno::UNO_QUERY_THROW ); + xCachedSeek->seek( 0 ); + + uno::Reference < io::XStream > xTempFile( + io::TempFile::create(m_xContext), + uno::UNO_QUERY_THROW ); + + uno::Reference< io::XSeekable > xTempSeek( xTempFile, uno::UNO_QUERY_THROW ); + uno::Reference< io::XOutputStream > xTempOutStream = xTempFile->getOutputStream(); + if ( !xTempOutStream.is() ) + throw io::IOException(); // TODO: + + // the OlePres stream must have additional header + // TODO/LATER: might need to be extended in future (actually makes sense only for SO7 format) + uno::Reference< io::XInputStream > xInCacheStream = xCachedVisualRepresentation->getInputStream(); + if ( !xInCacheStream.is() ) + throw uno::RuntimeException(); + + // write 0xFFFFFFFF at the beginning + uno::Sequence< sal_Int8 > aData( 4 ); + auto pData = aData.getArray(); + * reinterpret_cast<sal_uInt32*>(pData) = 0xFFFFFFFF; + + xTempOutStream->writeBytes( aData ); + + // write clipboard format + uno::Sequence< sal_Int8 > aSigData( 2 ); + xInCacheStream->readBytes( aSigData, 2 ); + if ( aSigData.getLength() < 2 ) + throw io::IOException(); + + if ( aSigData[0] == 'B' && aSigData[1] == 'M' ) + { + // it's a bitmap + pData[0] = 0x02; pData[1] = 0; pData[2] = 0; pData[3] = 0; + } + else + { + // treat it as a metafile + pData[0] = 0x03; pData[1] = 0; pData[2] = 0; pData[3] = 0; + } + xTempOutStream->writeBytes( aData ); + + // write job related information + pData[0] = 0x04; pData[1] = 0; pData[2] = 0; pData[3] = 0; + xTempOutStream->writeBytes( aData ); + + // write aspect + pData[0] = 0x01; pData[1] = 0; pData[2] = 0; pData[3] = 0; + xTempOutStream->writeBytes( aData ); + + // write l-index + * reinterpret_cast<sal_uInt32*>(pData) = 0xFFFFFFFF; + xTempOutStream->writeBytes( aData ); + + // write adv. flags + pData[0] = 0x02; pData[1] = 0; pData[2] = 0; pData[3] = 0; + xTempOutStream->writeBytes( aData ); + + // write compression + * reinterpret_cast<sal_uInt32*>(pData) = 0x0; + xTempOutStream->writeBytes( aData ); + + // get the size + awt::Size aSize = getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ); + sal_Int32 nIndex = 0; + + // write width + for ( nIndex = 0; nIndex < 4; nIndex++ ) + { + pData[nIndex] = static_cast<sal_Int8>( aSize.Width % 0x100 ); + aSize.Width /= 0x100; + } + xTempOutStream->writeBytes( aData ); + + // write height + for ( nIndex = 0; nIndex < 4; nIndex++ ) + { + pData[nIndex] = static_cast<sal_Int8>( aSize.Height % 0x100 ); + aSize.Height /= 0x100; + } + xTempOutStream->writeBytes( aData ); + + // write garbage, it will be overwritten by the size + xTempOutStream->writeBytes( aData ); + + // write first bytes that was used to detect the type + xTempOutStream->writeBytes( aSigData ); + + // write the rest of the stream + ::comphelper::OStorageHelper::CopyInputToOutput( xInCacheStream, xTempOutStream ); + + // write the size of the stream + sal_Int64 nLength = xTempSeek->getLength() - 40; + if ( nLength < 0 || nLength >= 0xFFFFFFFF ) + { + SAL_WARN( "embeddedobj.ole", "Length is not acceptable!" ); + return; + } + for ( sal_Int32 nInd = 0; nInd < 4; nInd++ ) + { + pData[nInd] = static_cast<sal_Int8>( static_cast<sal_uInt64>(nLength) % 0x100 ); + nLength /= 0x100; + } + xTempSeek->seek( 36 ); + xTempOutStream->writeBytes( aData ); + + xTempOutStream->flush(); + + xTempSeek->seek( 0 ); + if ( xCachedSeek.is() ) + xCachedSeek->seek( 0 ); + + // insert the result file as replacement image + OUString aCacheName = "\002OlePres000"; + if ( xNameContainer->hasByName( aCacheName ) ) + xNameContainer->replaceByName( aCacheName, uno::Any( xTempFile ) ); + else + xNameContainer->insertByName( aCacheName, uno::Any( xTempFile ) ); + + uno::Reference< embed::XTransactedObject > xTransacted( xNameContainer, uno::UNO_QUERY_THROW ); + xTransacted->commit(); +} + + +void OleEmbeddedObject::RemoveVisualCache_Impl( const uno::Reference< io::XStream >& xTargetStream ) +{ + OSL_ENSURE( xTargetStream.is(), "Invalid argument!" ); + if ( !xTargetStream.is() ) + throw uno::RuntimeException(); + + uno::Sequence< uno::Any > aArgs{ uno::Any(xTargetStream), + uno::Any(true) }; // do not create copy + uno::Reference< container::XNameContainer > xNameContainer( + m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.embed.OLESimpleStorage", + aArgs, m_xContext ), + uno::UNO_QUERY_THROW ); + + for ( sal_uInt8 nInd = 0; nInd < 10; nInd++ ) + { + OUString aStreamName = "\002OlePres00" + OUString::number( nInd ); + if ( xNameContainer->hasByName( aStreamName ) ) + xNameContainer->removeByName( aStreamName ); + } + + uno::Reference< embed::XTransactedObject > xTransacted( xNameContainer, uno::UNO_QUERY_THROW ); + xTransacted->commit(); +} + + +void OleEmbeddedObject::SetVisReplInStream( bool bExists ) +{ + m_bVisReplInitialized = true; + m_bVisReplInStream = bExists; +} + + +bool OleEmbeddedObject::HasVisReplInStream() +{ + if ( !m_bVisReplInitialized ) + { + if ( m_xCachedVisualRepresentation.is() ) + SetVisReplInStream( true ); + else + { + SAL_INFO( "embeddedobj.ole", "embeddedobj (mv76033) OleEmbeddedObject::HasVisualReplInStream, analyzing" ); + + uno::Reference< io::XInputStream > xStream; + + OSL_ENSURE( !m_pOleComponent || !m_aTempURL.isEmpty(), "The temporary file must exist if there is a component!" ); + if ( !m_aTempURL.isEmpty() ) + { + try + { + // open temporary file for reading + uno::Reference < ucb::XSimpleFileAccess3 > xTempAccess( + ucb::SimpleFileAccess::create( m_xContext ) ); + + xStream = xTempAccess->openFileRead( m_aTempURL ); + } + catch( const uno::Exception& ) + {} + } + + if ( !xStream.is() ) + xStream = m_xObjectStream->getInputStream(); + + if ( xStream.is() ) + { + bool bExists = false; + + uno::Sequence< uno::Any > aArgs{ uno::Any(xStream), + uno::Any(true) }; // do not create copy + uno::Reference< container::XNameContainer > xNameContainer( + m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.embed.OLESimpleStorage", + aArgs, m_xContext ), + uno::UNO_QUERY ); + + if ( xNameContainer.is() ) + { + for ( sal_uInt8 nInd = 0; nInd < 10 && !bExists; nInd++ ) + { + OUString aStreamName = "\002OlePres00" + OUString::number( nInd ); + try + { + bExists = xNameContainer->hasByName( aStreamName ); + } + catch( const uno::Exception& ) + {} + } + } + + SetVisReplInStream( bExists ); + } + } + } + + return m_bVisReplInStream; +} + + +uno::Reference< io::XStream > OleEmbeddedObject::TryToRetrieveCachedVisualRepresentation_Impl( + const uno::Reference< io::XStream >& xStream, + bool bAllowToRepair50 ) + noexcept +{ + uno::Reference< io::XStream > xResult; + + if ( xStream.is() ) + { + SAL_INFO( "embeddedobj.ole", "embeddedobj (mv76033) OleEmbeddedObject::TryToRetrieveCachedVisualRepresentation, retrieving" ); + + uno::Reference< container::XNameContainer > xNameContainer; + uno::Sequence< uno::Any > aArgs{ uno::Any(xStream), + uno::Any(true) }; // do not create copy + try + { + xNameContainer.set( + m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.embed.OLESimpleStorage", + aArgs, m_xContext ), + uno::UNO_QUERY ); + } + catch( const uno::Exception& ) + {} + + if ( xNameContainer.is() ) + { + for ( sal_uInt8 nInd = 0; nInd < 10; nInd++ ) + { + OUString aStreamName = "\002OlePres00" + OUString::number( nInd ); + uno::Reference< io::XStream > xCachedCopyStream; + try + { + if ( ( xNameContainer->getByName( aStreamName ) >>= xCachedCopyStream ) && xCachedCopyStream.is() ) + { + xResult = TryToGetAcceptableFormat_Impl( xCachedCopyStream ); + if ( xResult.is() ) + break; + } + } + catch( const uno::Exception& ) + {} + + if ( nInd == 0 ) + { + // to be compatible with the old versions Ole10Native is checked after OlePress000 + aStreamName = "\001Ole10Native"; + try + { + if ( ( xNameContainer->getByName( aStreamName ) >>= xCachedCopyStream ) && xCachedCopyStream.is() ) + { + xResult = TryToGetAcceptableFormat_Impl( xCachedCopyStream ); + if ( xResult.is() ) + break; + } + } + catch( const uno::Exception& ) + {} + } + } + + try + { + if ( bAllowToRepair50 && !xResult.is() ) + { + OUString aOrigContName( "Ole-Object" ); + if ( xNameContainer->hasByName( aOrigContName ) ) + { + uno::Reference< embed::XClassifiedObject > xClassified( xNameContainer, uno::UNO_QUERY_THROW ); + if ( MimeConfigurationHelper::ClassIDsEqual( xClassified->getClassID(), MimeConfigurationHelper::GetSequenceClassID( SO3_OUT_CLASSID ) ) ) + { + // this is an OLE object wrongly stored in 5.0 format + // this object must be repaired since SO7 has done it + + uno::Reference< io::XOutputStream > xOutputStream = xStream->getOutputStream(); + uno::Reference< io::XTruncate > xTruncate( xOutputStream, uno::UNO_QUERY_THROW ); + + uno::Reference< io::XInputStream > xOrigInputStream; + if ( ( xNameContainer->getByName( aOrigContName ) >>= xOrigInputStream ) + && xOrigInputStream.is() ) + { + // the provided input stream must be based on temporary medium and must be independent + // from the stream the storage is based on + uno::Reference< io::XSeekable > xOrigSeekable( xOrigInputStream, uno::UNO_QUERY ); + if ( xOrigSeekable.is() ) + xOrigSeekable->seek( 0 ); + + uno::Reference< lang::XComponent > xNameContDisp( xNameContainer, uno::UNO_QUERY_THROW ); + xNameContDisp->dispose(); // free the original stream + + xTruncate->truncate(); + ::comphelper::OStorageHelper::CopyInputToOutput( xOrigInputStream, xOutputStream ); + xOutputStream->flush(); + + if ( xStream == m_xObjectStream ) + { + if ( !m_aTempURL.isEmpty() ) + { + // this is the own stream, so the temporary URL must be cleaned if it exists + KillFile_Impl( m_aTempURL, m_xContext ); + m_aTempURL.clear(); + } + +#ifdef _WIN32 + // retry to create the component after recovering + GetRidOfComponent(); + + try + { + CreateOleComponentAndLoad_Impl(); + m_aClassID = m_pOleComponent->GetCLSID(); // was not set during construction + } + catch( const uno::Exception& ) + { + GetRidOfComponent(); + } +#endif + } + + xResult = TryToRetrieveCachedVisualRepresentation_Impl( xStream ); + } + } + } + } + } + catch( const uno::Exception& ) + {} + } + } + + return xResult; +} + + +void OleEmbeddedObject::SwitchOwnPersistence( const uno::Reference< embed::XStorage >& xNewParentStorage, + const uno::Reference< io::XStream >& xNewObjectStream, + const OUString& aNewName ) +{ + if ( xNewParentStorage == m_xParentStorage && aNewName == m_aEntryName ) + { + SAL_WARN_IF( xNewObjectStream != m_xObjectStream, "embeddedobj.ole", "The streams must be the same!" ); + return; + } + + uno::Reference<io::XSeekable> xNewSeekable(xNewObjectStream, uno::UNO_QUERY); + if (xNewSeekable.is() && xNewSeekable->getLength() == 0) + { + uno::Reference<io::XSeekable> xOldSeekable(m_xObjectStream, uno::UNO_QUERY); + if (xOldSeekable.is() && xOldSeekable->getLength() > 0) + { + SAL_WARN("embeddedobj.ole", "OleEmbeddedObject::SwitchOwnPersistence(stream version): " + "empty new stream, reusing old one"); + uno::Reference<io::XInputStream> xInput = m_xObjectStream->getInputStream(); + uno::Reference<io::XOutputStream> xOutput = xNewObjectStream->getOutputStream(); + xOldSeekable->seek(0); + comphelper::OStorageHelper::CopyInputToOutput(xInput, xOutput); + xNewSeekable->seek(0); + } + } + + try { + uno::Reference< lang::XComponent > xComponent( m_xObjectStream, uno::UNO_QUERY ); + OSL_ENSURE( !m_xObjectStream.is() || xComponent.is(), "Wrong stream implementation!" ); + if ( xComponent.is() ) + xComponent->dispose(); + } + catch ( const uno::Exception& ) + { + } + + m_xObjectStream = xNewObjectStream; + m_xParentStorage = xNewParentStorage; + m_aEntryName = aNewName; +} + + +void OleEmbeddedObject::SwitchOwnPersistence( const uno::Reference< embed::XStorage >& xNewParentStorage, + const OUString& aNewName ) +{ + if ( xNewParentStorage == m_xParentStorage && aNewName == m_aEntryName ) + return; + + sal_Int32 nStreamMode = m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE; + + uno::Reference< io::XStream > xNewOwnStream = xNewParentStorage->openStreamElement( aNewName, nStreamMode ); + + uno::Reference<io::XSeekable> xNewSeekable (xNewOwnStream, uno::UNO_QUERY); + if (xNewSeekable.is() && xNewSeekable->getLength() == 0) + { + uno::Reference<io::XSeekable> xOldSeekable(m_xObjectStream, uno::UNO_QUERY); + if (xOldSeekable.is() && xOldSeekable->getLength() > 0) + { + SAL_WARN("embeddedobj.ole", "OleEmbeddedObject::SwitchOwnPersistence: empty new stream, reusing old one"); + uno::Reference<io::XInputStream> xInput = m_xObjectStream->getInputStream(); + uno::Reference<io::XOutputStream> xOutput = xNewOwnStream->getOutputStream(); + comphelper::OStorageHelper::CopyInputToOutput(xInput, xOutput); + xNewSeekable->seek(0); + } + } + + SAL_WARN_IF( !xNewOwnStream.is(), "embeddedobj.ole", "The method can not return empty reference!" ); + + SwitchOwnPersistence( xNewParentStorage, xNewOwnStream, aNewName ); +} + +#ifdef _WIN32 + +bool OleEmbeddedObject::SaveObject_Impl() +{ + bool bResult = false; + + if ( m_xClientSite.is() ) + { + try + { + m_xClientSite->saveObject(); + bResult = true; + } + catch( const uno::Exception& ) + { + } + } + + return bResult; +} + + +bool OleEmbeddedObject::OnShowWindow_Impl( bool bShow ) +{ + osl::ClearableMutexGuard aGuard(m_aMutex); + + bool bResult = false; + + SAL_WARN_IF( m_nObjectState == -1, "embeddedobj.ole", "The object has no persistence!" ); + SAL_WARN_IF( m_nObjectState == embed::EmbedStates::LOADED, "embeddedobj.ole", "The object get OnShowWindow in loaded state!" ); + if ( m_nObjectState == -1 || m_nObjectState == embed::EmbedStates::LOADED ) + return false; + + // the object is either activated or deactivated + sal_Int32 nOldState = m_nObjectState; + if ( bShow && m_nObjectState == embed::EmbedStates::RUNNING ) + { + m_nObjectState = embed::EmbedStates::ACTIVE; + m_aVerbExecutionController.ObjectIsActive(); + + aGuard.clear(); + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + } + else if ( !bShow && m_nObjectState == embed::EmbedStates::ACTIVE ) + { + m_nObjectState = embed::EmbedStates::RUNNING; + aGuard.clear(); + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + } + + if ( m_xClientSite.is() ) + { + try + { + m_xClientSite->visibilityChanged( bShow ); + bResult = true; + } + catch( const uno::Exception& ) + { + } + } + + return bResult; +} + + +void OleEmbeddedObject::OnIconChanged_Impl() +{ + // TODO/LATER: currently this notification seems to be impossible + // MakeEventListenerNotification_Impl( OUString( "OnIconChanged" ) ); +} + + +void OleEmbeddedObject::OnViewChanged_Impl() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); + + // For performance reasons the notification currently is ignored, STAMPIT object is the exception, + // it can never be active and never call SaveObject, so it is the only way to detect that it is changed + + // ==== the STAMPIT related solution ============================= + // the following variable is used to detect whether the object was modified during verb execution + m_aVerbExecutionController.ModificationNotificationIsDone(); + + // The following things are controlled by VerbExecutionController: + // - if the verb execution is in progress and the view is changed the object will be stored + // after the execution, so there is no need to send the notification. + // - the STAMPIT object can never be active. + if (m_aVerbExecutionController.CanDoNotification() && + m_pOleComponent && m_nUpdateMode == embed::EmbedUpdateModes::ALWAYS_UPDATE && + (MimeConfigurationHelper::ClassIDsEqual(m_aClassID, MimeConfigurationHelper::GetSequenceClassID(0x852ee1c9, 0x9058, 0x44ba, 0x8c, 0x6c, 0x0c, 0x5f, 0xc6, 0x6b, 0xdb, 0x8d)) || + MimeConfigurationHelper::ClassIDsEqual(m_aClassID, MimeConfigurationHelper::GetSequenceClassID(0xcf1b4491, 0xbea3, 0x4c9f, 0xa7, 0x0f, 0x22, 0x1b, 0x1e, 0xca, 0xef, 0x3e))) + ) + { + // The view is changed while the object is in running state, save the new object + m_xCachedVisualRepresentation.clear(); + SaveObject_Impl(); + MakeEventListenerNotification_Impl( "OnVisAreaChanged" ); + } + +} + + +void OleEmbeddedObject::OnClosed_Impl() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); + + if ( m_nObjectState != embed::EmbedStates::LOADED ) + { + sal_Int32 nOldState = m_nObjectState; + m_nObjectState = embed::EmbedStates::LOADED; + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + } +} + + +OUString OleEmbeddedObject::CreateTempURLEmpty_Impl() +{ + SAL_WARN_IF( !m_aTempURL.isEmpty(), "embeddedobj.ole", "The object has already the temporary file!" ); + m_aTempURL = GetNewTempFileURL_Impl( m_xContext ); + + return m_aTempURL; +} + + +OUString OleEmbeddedObject::GetTempURL_Impl() +{ + if ( m_aTempURL.isEmpty() ) + { + SAL_INFO( "embeddedobj.ole", "embeddedobj (mv76033) OleEmbeddedObject::GetTempURL_Impl, tempfile creation" ); + + // if there is no temporary file, it will be created from the own entry + uno::Reference< embed::XOptimizedStorage > xOptParStorage( m_xParentStorage, uno::UNO_QUERY ); + if ( xOptParStorage.is() ) + { + m_aTempURL = GetNewFilledTempFile_Impl( xOptParStorage, m_aEntryName, m_xContext ); + } + else if ( m_xObjectStream.is() ) + { + // load object from the stream + uno::Reference< io::XInputStream > xInStream = m_xObjectStream->getInputStream(); + if ( !xInStream.is() ) + throw io::IOException(); // TODO: access denied + + m_aTempURL = GetNewFilledTempFile_Impl( xInStream, m_xContext ); + } + } + + return m_aTempURL; +} + + +void OleEmbeddedObject::CreateOleComponent_Impl( + rtl::Reference<OleComponent> const & pOleComponent ) +{ + if ( !m_pOleComponent ) + { + m_pOleComponent = pOleComponent ? pOleComponent : new OleComponent( m_xContext, this ); + + if ( !m_xClosePreventer.is() ) + m_xClosePreventer = new OClosePreventer; + + m_pOleComponent->addCloseListener( m_xClosePreventer ); + } +} + + +void OleEmbeddedObject::CreateOleComponentAndLoad_Impl( + rtl::Reference<OleComponent> const & pOleComponent ) +{ + if ( !m_pOleComponent ) + { + if ( !m_xObjectStream.is() ) + throw uno::RuntimeException(); + + CreateOleComponent_Impl( pOleComponent ); + + // after the loading the object can appear as a link + // will be detected later by olecomponent + + GetTempURL_Impl(); + if ( m_aTempURL.isEmpty() ) + throw uno::RuntimeException(); // TODO + + m_pOleComponent->LoadEmbeddedObject( m_aTempURL ); + } +} + + +void OleEmbeddedObject::CreateOleComponentFromClipboard_Impl( OleComponent* pOleComponent ) +{ + if ( !m_pOleComponent ) + { + if ( !m_xObjectStream.is() ) + throw uno::RuntimeException(); + + CreateOleComponent_Impl( pOleComponent ); + + // after the loading the object can appear as a link + // will be detected later by olecomponent + m_pOleComponent->CreateObjectFromClipboard(); + } +} + + +uno::Reference< io::XOutputStream > OleEmbeddedObject::GetStreamForSaving() +{ + if ( !m_xObjectStream.is() ) + throw uno::RuntimeException(); //TODO: + + uno::Reference< io::XOutputStream > xOutStream = m_xObjectStream->getOutputStream(); + if ( !xOutStream.is() ) + throw io::IOException(); //TODO: access denied + + uno::Reference< io::XTruncate > xTruncate( xOutStream, uno::UNO_QUERY_THROW ); + xTruncate->truncate(); + + return xOutStream; +} + + +void OleEmbeddedObject::StoreObjectToStream( uno::Reference< io::XOutputStream > const & xOutStream ) +{ + // this method should be used only on windows + if ( m_pOleComponent ) + m_pOleComponent->StoreOwnTmpIfNecessary(); + + // now all the changes should be in temporary location + if ( m_aTempURL.isEmpty() ) + throw uno::RuntimeException(); + + // open temporary file for reading + uno::Reference < ucb::XSimpleFileAccess3 > xTempAccess( + ucb::SimpleFileAccess::create( m_xContext ) ); + + uno::Reference< io::XInputStream > xTempInStream = xTempAccess->openFileRead( m_aTempURL ); + SAL_WARN_IF( !xTempInStream.is(), "embeddedobj.ole", "The object's temporary file can not be reopened for reading!" ); + + // TODO: use bStoreVisReplace + + if ( !xTempInStream.is() ) + { + throw io::IOException(); // TODO: + } + + // write all the contents to XOutStream + uno::Reference< io::XTruncate > xTrunc( xOutStream, uno::UNO_QUERY_THROW ); + xTrunc->truncate(); + + ::comphelper::OStorageHelper::CopyInputToOutput( xTempInStream, xOutStream ); + + // TODO: should the view replacement be in the stream ??? + // probably it must be specified on storing +} +#endif + +void OleEmbeddedObject::StoreToLocation_Impl( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& lObjArgs, + bool bSaveAs ) +{ + // TODO: use lObjArgs + // TODO: exchange StoreVisualReplacement by SO file format version? + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "Can't store object without persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + OSL_ENSURE( m_xParentStorage.is() && m_xObjectStream.is(), "The object has no valid persistence!" ); + + bool bVisReplIsStored = false; + + bool bTryOptimization = false; + bool bStoreVis = m_bStoreVisRepl; + uno::Reference< io::XStream > xCachedVisualRepresentation; + for ( beans::PropertyValue const & prop : lObjArgs ) + { + if ( prop.Name == "StoreVisualReplacement" ) + prop.Value >>= bStoreVis; + else if ( prop.Name == "VisualReplacement" ) + prop.Value >>= xCachedVisualRepresentation; + else if ( prop.Name == "CanTryOptimization" ) + prop.Value >>= bTryOptimization; + } + + // ignore visual representation provided from outside if it should not be stored + if ( !bStoreVis ) + xCachedVisualRepresentation.clear(); + + if ( bStoreVis && !HasVisReplInStream() && !xCachedVisualRepresentation.is() ) + throw io::IOException(); // TODO: there is no cached visual representation and nothing is provided from outside + + // if the representation is provided from outside it should be copied to a local stream + bool bNeedLocalCache = xCachedVisualRepresentation.is(); + + uno::Reference< io::XStream > xTargetStream; + + bool bStoreLoaded = false; + if ( m_nObjectState == embed::EmbedStates::LOADED +#ifdef _WIN32 + // if the object was NOT modified after storing it can be just copied + // as if it was in loaded state + || ( m_pOleComponent && !m_pOleComponent->IsDirty() ) +#endif + ) + { + bool bOptimizedCopyingDone = false; + + if ( bTryOptimization && bStoreVis == HasVisReplInStream() ) + { + try + { + uno::Reference< embed::XOptimizedStorage > xSourceOptStor( m_xParentStorage, uno::UNO_QUERY_THROW ); + uno::Reference< embed::XOptimizedStorage > xTargetOptStor( xStorage, uno::UNO_QUERY_THROW ); + xSourceOptStor->copyElementDirectlyTo( m_aEntryName, xTargetOptStor, sEntName ); + bOptimizedCopyingDone = true; + } + catch( const uno::Exception& ) + { + } + } + + if ( !bOptimizedCopyingDone ) + { + // if optimized copying fails a normal one should be tried + m_xParentStorage->copyElementTo( m_aEntryName, xStorage, sEntName ); + } + + // the locally retrieved representation is always preferable + // since the object is in loaded state the representation is unchanged + if ( m_xCachedVisualRepresentation.is() ) + { + xCachedVisualRepresentation = m_xCachedVisualRepresentation; + bNeedLocalCache = false; + } + + bVisReplIsStored = HasVisReplInStream(); + bStoreLoaded = true; + } +#ifdef _WIN32 + else if ( m_pOleComponent ) + { + xTargetStream = + xStorage->openStreamElement( sEntName, embed::ElementModes::READWRITE ); + if ( !xTargetStream.is() ) + throw io::IOException(); //TODO: access denied + + SetStreamMediaType_Impl( xTargetStream, "application/vnd.sun.star.oleobject" ); + uno::Reference< io::XOutputStream > xOutStream = xTargetStream->getOutputStream(); + if ( !xOutStream.is() ) + throw io::IOException(); //TODO: access denied + + StoreObjectToStream( xOutStream ); + bVisReplIsStored = true; + + if ( bSaveAs ) + { + // no need to do it on StoreTo since in this case the replacement is in the stream + // and there is no need to cache it even if it is thrown away because the object + // is not changed by StoreTo action + + uno::Reference< io::XStream > xTmpCVRepresentation = + TryToRetrieveCachedVisualRepresentation_Impl( xTargetStream ); + + // the locally retrieved representation is always preferable + if ( xTmpCVRepresentation.is() ) + { + xCachedVisualRepresentation = xTmpCVRepresentation; + bNeedLocalCache = false; + } + } + } +#endif + else if (true) // loplugin:flatten + { + throw io::IOException(); // TODO + } + + if ( !xTargetStream.is() ) + { + xTargetStream = + xStorage->openStreamElement( sEntName, embed::ElementModes::READWRITE ); + if ( !xTargetStream.is() ) + throw io::IOException(); //TODO: access denied + } + + LetCommonStoragePassBeUsed_Impl( xTargetStream ); + + if ( bStoreVis != bVisReplIsStored ) + { + if ( bStoreVis ) + { + if ( !xCachedVisualRepresentation.is() ) + xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( xTargetStream ); + + SAL_WARN_IF( !xCachedVisualRepresentation.is(), "embeddedobj.ole", "No representation is available!" ); + + // the following copying will be done in case it is SaveAs anyway + // if it is not SaveAs the seekable access is not required currently + // TODO/LATER: may be required in future + if ( bSaveAs ) + { + uno::Reference< io::XSeekable > xCachedSeek( xCachedVisualRepresentation, uno::UNO_QUERY ); + if ( !xCachedSeek.is() ) + { + xCachedVisualRepresentation + = GetNewFilledTempStream_Impl( xCachedVisualRepresentation->getInputStream() ); + bNeedLocalCache = false; + } + } + + InsertVisualCache_Impl( xTargetStream, xCachedVisualRepresentation ); + } + else + { + // the removed representation could be cached by this method + if ( !xCachedVisualRepresentation.is() ) + xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( xTargetStream ); + + if (!m_bStreamReadOnly) + RemoveVisualCache_Impl(xTargetStream); + } + } + + if ( bSaveAs ) + { + m_bWaitSaveCompleted = true; + m_xNewObjectStream = xTargetStream; + m_xNewParentStorage = xStorage; + m_aNewEntryName = sEntName; + m_bNewVisReplInStream = bStoreVis; + m_bStoreLoaded = bStoreLoaded; + + if ( xCachedVisualRepresentation.is() ) + { + if ( bNeedLocalCache ) + m_xNewCachedVisRepl = GetNewFilledTempStream_Impl( xCachedVisualRepresentation->getInputStream() ); + else + m_xNewCachedVisRepl = xCachedVisualRepresentation; + } + + // TODO: register listeners for storages above, in case they are disposed + // an exception will be thrown on saveCompleted( true ) + } + else + { + uno::Reference< lang::XComponent > xComp( xTargetStream, uno::UNO_QUERY ); + if ( xComp.is() ) + { + try { + xComp->dispose(); + } catch( const uno::Exception& ) + { + } + } + } +} + + +void SAL_CALL OleEmbeddedObject::setPersistentEntry( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + sal_Int32 nEntryConnectionMode, + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setPersistentEntry( xStorage, sEntName, nEntryConnectionMode, lArguments, lObjArgs ); + return; + } + // end wrapping related part ==================== + + // TODO: use lObjArgs + + // the type of the object must be already set + // a kind of typedetection should be done in the factory; + // the only exception is object initialized from a stream, + // the class ID will be detected from the stream + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + // May be LOADED should be forbidden here ??? + if ( ( m_nObjectState != -1 || nEntryConnectionMode == embed::EntryInitModes::NO_INIT ) + && ( m_nObjectState == -1 || nEntryConnectionMode != embed::EntryInitModes::NO_INIT ) ) + { + // if the object is not loaded + // it can not get persistent representation without initialization + + // if the object is loaded + // it can switch persistent representation only without initialization + + throw embed::WrongStateException( + "Can't change persistent representation of activated object!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + { + if ( nEntryConnectionMode != embed::EntryInitModes::NO_INIT ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + saveCompleted( m_xParentStorage != xStorage || m_aEntryName != sEntName ); + } + + uno::Reference< container::XNameAccess > xNameAccess( xStorage, uno::UNO_QUERY_THROW ); + + // detect entry existence + bool bElExists = xNameAccess->hasByName( sEntName ); + + m_bReadOnly = false; + for ( beans::PropertyValue const & prop : lArguments ) + if ( prop.Name == "ReadOnly" ) + prop.Value >>= m_bReadOnly; + +#ifdef _WIN32 + sal_Int32 nStorageMode = m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE; +#endif + + SwitchOwnPersistence( xStorage, sEntName ); + + for ( beans::PropertyValue const & prop : lObjArgs ) + if ( prop.Name == "StoreVisualReplacement" ) + prop.Value >>= m_bStoreVisRepl; + +#ifdef _WIN32 + if ( nEntryConnectionMode == embed::EntryInitModes::DEFAULT_INIT ) + { + if ( m_bFromClipboard ) + { + // the object should be initialized from clipboard + // impossibility to initialize the object means error here + CreateOleComponentFromClipboard_Impl(); + m_aClassID = m_pOleComponent->GetCLSID(); // was not set during construction + m_pOleComponent->RunObject(); + m_nObjectState = embed::EmbedStates::RUNNING; + } + else if ( bElExists ) + { + // load object from the stream + // after the loading the object can appear as a link + // will be detected by olecomponent + try + { + CreateOleComponentAndLoad_Impl(); + m_aClassID = m_pOleComponent->GetCLSID(); // was not set during construction + } + catch( const uno::Exception& ) + { + // TODO/LATER: detect classID of the object if possible + // means that the object inprocess server could not be successfully instantiated + GetRidOfComponent(); + } + + m_nObjectState = embed::EmbedStates::LOADED; + } + else + { + // create a new object + CreateOleComponent_Impl(); + m_pOleComponent->CreateNewEmbeddedObject( m_aClassID ); + m_pOleComponent->RunObject(); + m_nObjectState = embed::EmbedStates::RUNNING; + } + } + else + { + if ( ( nStorageMode & embed::ElementModes::READWRITE ) != embed::ElementModes::READWRITE ) + throw io::IOException(); + + if ( nEntryConnectionMode == embed::EntryInitModes::NO_INIT ) + { + // the document just already changed its stream to store to; + // the links to OLE documents switch their persistence in the same way + // as normal embedded objects + } + else if ( nEntryConnectionMode == embed::EntryInitModes::TRUNCATE_INIT ) + { + // create a new object, that will be stored in specified stream + CreateOleComponent_Impl(); + + m_pOleComponent->CreateNewEmbeddedObject( m_aClassID ); + m_pOleComponent->RunObject(); + m_nObjectState = embed::EmbedStates::RUNNING; + } + else if ( nEntryConnectionMode == embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT ) + { + // use URL ( may be content or stream later ) from MediaDescriptor to initialize object + OUString aURL; + for ( beans::PropertyValue const & prop : lArguments ) + if ( prop.Name == "URL" ) + prop.Value >>= aURL; + + if ( aURL.isEmpty() ) + throw lang::IllegalArgumentException( + "Empty URL is provided in the media descriptor!", + static_cast< ::cppu::OWeakObject* >(this), + 4 ); + + CreateOleComponent_Impl(); + + // TODO: the m_bIsLink value must be set already + if ( !m_bIsLink ) + m_pOleComponent->CreateObjectFromFile( aURL ); + else + m_pOleComponent->CreateLinkFromFile( aURL ); + + m_pOleComponent->RunObject(); + m_aClassID = m_pOleComponent->GetCLSID(); // was not set during construction + + m_nObjectState = embed::EmbedStates::RUNNING; + } + //else if ( nEntryConnectionMode == embed::EntryInitModes::TRANSFERABLE_INIT ) + //{ + //TODO: + //} + else + throw lang::IllegalArgumentException( "Wrong connection mode is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 3 ); + } +#else + // On Unix the OLE object can not do anything except storing itself somewhere + if ( nEntryConnectionMode == embed::EntryInitModes::DEFAULT_INIT && bElExists ) + { + // TODO/LATER: detect classID of the object + // can be a real problem for the links + + m_nObjectState = embed::EmbedStates::LOADED; + } + else if ( nEntryConnectionMode == embed::EntryInitModes::NO_INIT ) + { + // do nothing, the object has already switched it's persistence + } + else + throw lang::IllegalArgumentException( "Wrong connection mode is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 3 ); + +#endif +} + + +void SAL_CALL OleEmbeddedObject::storeToEntry( const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->storeToEntry( xStorage, sEntName, lArguments, lObjArgs ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + VerbExecutionControllerGuard aVerbGuard( m_aVerbExecutionController ); + + StoreToLocation_Impl( xStorage, sEntName, lObjArgs, false ); + + // TODO: should the listener notification be done? +} + + +void SAL_CALL OleEmbeddedObject::storeAsEntry( const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->storeAsEntry( xStorage, sEntName, lArguments, lObjArgs ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + VerbExecutionControllerGuard aVerbGuard( m_aVerbExecutionController ); + + StoreToLocation_Impl( xStorage, sEntName, lObjArgs, true ); + + // TODO: should the listener notification be done here or in saveCompleted? +} + + +void SAL_CALL OleEmbeddedObject::saveCompleted( sal_Bool bUseNew ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->saveCompleted( bUseNew ); + return; + } + // end wrapping related part ==================== + + osl::ClearableMutexGuard aGuard(m_aMutex); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "Can't store object without persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + // it is allowed to call saveCompleted( false ) for nonstored objects + if ( !m_bWaitSaveCompleted && !bUseNew ) + return; + + SAL_WARN_IF( !m_bWaitSaveCompleted, "embeddedobj.ole", "Unexpected saveCompleted() call!" ); + if ( !m_bWaitSaveCompleted ) + throw io::IOException(); // TODO: illegal call + + OSL_ENSURE( m_xNewObjectStream.is() && m_xNewParentStorage.is() , "Internal object information is broken!" ); + if ( !m_xNewObjectStream.is() || !m_xNewParentStorage.is() ) + throw uno::RuntimeException(); // TODO: broken internal information + + if ( bUseNew ) + { + SwitchOwnPersistence( m_xNewParentStorage, m_xNewObjectStream, m_aNewEntryName ); + m_bStoreVisRepl = m_bNewVisReplInStream; + SetVisReplInStream( m_bNewVisReplInStream ); + m_xCachedVisualRepresentation = m_xNewCachedVisRepl; + } + else + { + // close remembered stream + try { + uno::Reference< lang::XComponent > xComponent( m_xNewObjectStream, uno::UNO_QUERY ); + SAL_WARN_IF( !xComponent.is(), "embeddedobj.ole", "Wrong storage implementation!" ); + if ( xComponent.is() ) + xComponent->dispose(); + } + catch ( const uno::Exception& ) + { + } + } + + bool bStoreLoaded = m_bStoreLoaded; + + m_xNewObjectStream.clear(); + m_xNewParentStorage.clear(); + m_aNewEntryName.clear(); + m_bWaitSaveCompleted = false; + m_bNewVisReplInStream = false; + m_xNewCachedVisRepl.clear(); + m_bStoreLoaded = false; + + if ( bUseNew && m_pOleComponent && m_nUpdateMode == embed::EmbedUpdateModes::ALWAYS_UPDATE && !bStoreLoaded + && m_nObjectState != embed::EmbedStates::LOADED ) + { + // the object replacement image should be updated, so the cached size as well + m_bHasCachedSize = false; + try + { + // the call will cache the size in case of success + // probably it might need to be done earlier, while the object is in active state + getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ); + } + catch( const uno::Exception& ) + {} + } + + aGuard.clear(); + if ( bUseNew ) + { + MakeEventListenerNotification_Impl( "OnSaveAsDone"); + + // the object can be changed only on windows + // the notification should be done only if the object is not in loaded state + if ( m_pOleComponent && m_nUpdateMode == embed::EmbedUpdateModes::ALWAYS_UPDATE && !bStoreLoaded ) + { + MakeEventListenerNotification_Impl( "OnVisAreaChanged"); + } + } +} + + +sal_Bool SAL_CALL OleEmbeddedObject::hasEntry() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->hasEntry(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_xObjectStream.is() ) + return true; + + return false; +} + + +OUString SAL_CALL OleEmbeddedObject::getEntryName() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getEntryName(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "The object persistence is not initialized!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_aEntryName; +} + + +void SAL_CALL OleEmbeddedObject::storeOwn() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->storeOwn(); + return; + } + // end wrapping related part ==================== + + // during switching from Activated to Running and from Running to Loaded states the object will + // ask container to store the object, the container has to make decision + // to do so or not + + osl::ClearableMutexGuard aGuard(m_aMutex); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + VerbExecutionControllerGuard aVerbGuard( m_aVerbExecutionController ); + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "Can't store object without persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_bReadOnly ) + throw io::IOException(); // TODO: access denied + + LetCommonStoragePassBeUsed_Impl( m_xObjectStream ); + + bool bStoreLoaded = true; + +#ifdef _WIN32 + if ( m_nObjectState != embed::EmbedStates::LOADED && m_pOleComponent && m_pOleComponent->IsDirty() ) + { + bStoreLoaded = false; + + OSL_ENSURE( m_xParentStorage.is() && m_xObjectStream.is(), "The object has no valid persistence!" ); + + if ( !m_xObjectStream.is() ) + throw io::IOException(); //TODO: access denied + + SetStreamMediaType_Impl( m_xObjectStream, "application/vnd.sun.star.oleobject" ); + uno::Reference< io::XOutputStream > xOutStream = m_xObjectStream->getOutputStream(); + if ( !xOutStream.is() ) + throw io::IOException(); //TODO: access denied + + // TODO: does this work for links too? + StoreObjectToStream( GetStreamForSaving() ); + + // the replacement is changed probably, and it must be in the object stream + if ( !m_pOleComponent->IsWorkaroundActive() ) + m_xCachedVisualRepresentation.clear(); + SetVisReplInStream( true ); + } +#endif + + if ( m_bStoreVisRepl != HasVisReplInStream() ) + { + if ( m_bStoreVisRepl ) + { + // the m_xCachedVisualRepresentation must be set or it should be already stored + if ( m_xCachedVisualRepresentation.is() ) + InsertVisualCache_Impl( m_xObjectStream, m_xCachedVisualRepresentation ); + else + { + m_xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( m_xObjectStream ); + SAL_WARN_IF( !m_xCachedVisualRepresentation.is(), "embeddedobj.ole", "No representation is available!" ); + } + } + else + { + if ( !m_xCachedVisualRepresentation.is() ) + m_xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( m_xObjectStream ); + RemoveVisualCache_Impl( m_xObjectStream ); + } + + SetVisReplInStream( m_bStoreVisRepl ); + } + + if ( m_pOleComponent && m_nUpdateMode == embed::EmbedUpdateModes::ALWAYS_UPDATE && !bStoreLoaded ) + { + // the object replacement image should be updated, so the cached size as well + m_bHasCachedSize = false; + try + { + // the call will cache the size in case of success + // probably it might need to be done earlier, while the object is in active state + getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ); + } + catch( const uno::Exception& ) + {} + } + + aGuard.clear(); + + MakeEventListenerNotification_Impl( "OnSaveDone"); + + // the object can be changed only on Windows + // the notification should be done only if the object is not in loaded state + if ( m_pOleComponent && m_nUpdateMode == embed::EmbedUpdateModes::ALWAYS_UPDATE && !bStoreLoaded ) + MakeEventListenerNotification_Impl( "OnVisAreaChanged"); +} + + +sal_Bool SAL_CALL OleEmbeddedObject::isReadonly() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->isReadonly(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "The object persistence is not initialized!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_bReadOnly; +} + + +void SAL_CALL OleEmbeddedObject::reload( + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->reload( lArguments, lObjArgs ); + return; + } + // end wrapping related part ==================== + + // TODO: use lObjArgs + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "The object persistence is not initialized!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // TODO: + // throw away current document + // load new document from current storage + // use meaningful part of lArguments +} + + +void SAL_CALL OleEmbeddedObject::breakLink( const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XLinkageSupport > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->breakLink( xStorage, sEntName ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + // TODO: The object must be at least in Running state; + if ( !m_bIsLink || m_nObjectState == -1 || !m_pOleComponent ) + { + // it must be a linked initialized object + throw embed::WrongStateException( + "The object is not a valid linked object!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bReadOnly ) + throw io::IOException(); // TODO: Access denied + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + +#ifdef _WIN32 + // TODO: create an object based on the link + + // disconnect the old temporary URL + OUString aOldTempURL = m_aTempURL; + m_aTempURL.clear(); + + rtl::Reference<OleComponent> pNewOleComponent = new OleComponent(m_xContext, this); + try { + pNewOleComponent->InitEmbeddedCopyOfLink(m_pOleComponent); + } + catch (const uno::Exception&) + { + if (!m_aTempURL.isEmpty()) + KillFile_Impl(m_aTempURL, m_xContext); + m_aTempURL = aOldTempURL; + throw; + } + + try { + GetRidOfComponent(); + } + catch (const uno::Exception&) + { + if (!m_aTempURL.isEmpty()) + KillFile_Impl(m_aTempURL, m_xContext); + m_aTempURL = aOldTempURL; + throw; + } + + KillFile_Impl(aOldTempURL, m_xContext); + + CreateOleComponent_Impl(pNewOleComponent); + + if (m_xParentStorage != xStorage || !m_aEntryName.equals(sEntName)) + SwitchOwnPersistence(xStorage, sEntName); + + if (m_nObjectState != embed::EmbedStates::LOADED) + { + // TODO: should we activate the new object if the link was activated? + + const sal_Int32 nTargetState = m_nObjectState; + m_nObjectState = embed::EmbedStates::LOADED; + + if (nTargetState == embed::EmbedStates::RUNNING) + m_pOleComponent->RunObject(); // the object already was in running state, the server must be installed + else // nTargetState == embed::EmbedStates::ACTIVE + { + m_pOleComponent->RunObject(); // the object already was in running state, the server must be installed + m_pOleComponent->ExecuteVerb(embed::EmbedVerbs::MS_OLEVERB_OPEN); + } + + m_nObjectState = nTargetState; + } + + m_bIsLink = false; + m_aLinkURL.clear(); +#else // ! _WIN32 + throw io::IOException(); //TODO: +#endif // _WIN32 +} + + +sal_Bool SAL_CALL OleEmbeddedObject::isLink() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XLinkageSupport > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->isLink(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + return m_bIsLink; +} + + +OUString SAL_CALL OleEmbeddedObject::getLinkURL() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XLinkageSupport > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getLinkURL(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( !m_bIsLink ) + throw embed::WrongStateException( + "The object is not a link object!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // TODO: probably the link URL can be retrieved from OLE + + return m_aLinkURL; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olepersist.hxx b/embeddedobj/source/msole/olepersist.hxx new file mode 100644 index 0000000000..9a3e76c684 --- /dev/null +++ b/embeddedobj/source/msole/olepersist.hxx @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <com/sun/star/uno/Reference.hxx> +#include <rtl/ustring.hxx> + +namespace com::sun::star { + namespace io { class XInputStream; } + namespace uno { class XComponentContext; } +} + +OUString GetNewTempFileURL_Impl( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + +/// @throws css::io::IOException +/// @throws css::uno::RuntimeException +OUString GetNewFilledTempFile_Impl( + css::uno::Reference<css::io::XInputStream > const & xInStream, + css::uno::Reference<css::uno::XComponentContext> const & xContext); + +bool KillFile_Impl( const OUString& aURL, const css::uno::Reference< css::uno::XComponentContext >& xContext ); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olevisual.cxx b/embeddedobj/source/msole/olevisual.cxx new file mode 100644 index 0000000000..7d8de650db --- /dev/null +++ b/embeddedobj/source/msole/olevisual.cxx @@ -0,0 +1,432 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/EmbedMapUnits.hpp> +#include <com/sun/star/embed/EmbedMisc.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/embed/NoVisualAreaSizeException.hpp> + +#include <oleembobj.hxx> +#if defined (_WIN32) +#include <comphelper/mimeconfighelper.hxx> +#endif +#include <comphelper/seqstream.hxx> +#include <filter/msfilter/classids.hxx> +#include <sal/log.hxx> + +#if defined(_WIN32) +#include "olecomponent.hxx" +#include <comphelper/diagnose_ex.hxx> +#endif + +using namespace ::com::sun::star; +using namespace ::comphelper; + +embed::VisualRepresentation OleEmbeddedObject::GetVisualRepresentationInNativeFormat_Impl( + const uno::Reference< io::XStream >& xCachedVisRepr ) +{ + embed::VisualRepresentation aVisualRepr; + + // TODO: detect the format in the future for now use workaround + uno::Reference< io::XInputStream > xInStream = xCachedVisRepr->getInputStream(); + if ( !xInStream.is() ) + throw uno::RuntimeException(); + uno::Reference< io::XSeekable > xSeekable( xCachedVisRepr, uno::UNO_QUERY_THROW ); + + uno::Sequence< sal_Int8 > aSeq( 2 ); + xInStream->readBytes( aSeq, 2 ); + xSeekable->seek( 0 ); + if ( aSeq.getLength() == 2 && aSeq[0] == 'B' && aSeq[1] == 'M' ) + { + // it's a bitmap + aVisualRepr.Flavor = datatransfer::DataFlavor( + "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"", + "Bitmap", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ); + } + else + { + // it's a metafile + aVisualRepr.Flavor = datatransfer::DataFlavor( + "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"", + "Windows Metafile", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ); + } + + sal_Int32 nStreamLength = static_cast<sal_Int32>(xSeekable->getLength()); + uno::Sequence< sal_Int8 > aRepresent( nStreamLength ); + xInStream->readBytes( aRepresent, nStreamLength ); + aVisualRepr.Data <<= aRepresent; + + return aVisualRepr; +} + +void SAL_CALL OleEmbeddedObject::setVisualAreaSize( sal_Int64 nAspect, const awt::Size& aSize ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setVisualAreaSize( nAspect, aSize ); + return; + } + // end wrapping related part ==================== + + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.ole", "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object is not loaded!", + static_cast< ::cppu::OWeakObject* >(this) ); + +#ifdef _WIN32 + // RECOMPOSE_ON_RESIZE misc flag means that the object has to be switched to running state on resize. + // SetExtent() is called only for objects that require it, + // it should not be called for MSWord documents to workaround problem i49369 + // If cached size is not set, that means that this is the size initialization, so there is no need to set the real size + bool bAllowToSetExtent = + ( ( getStatus( nAspect ) & embed::EmbedMisc::MS_EMBED_RECOMPOSEONRESIZE ) + && !MimeConfigurationHelper::ClassIDsEqual(m_aClassID, MimeConfigurationHelper::GetSequenceClassID(MSO_WW8_CLASSID)) + && m_bHasCachedSize ); + + if ( m_nObjectState == embed::EmbedStates::LOADED && bAllowToSetExtent ) + { + aGuard.clear(); + try { + changeState( embed::EmbedStates::RUNNING ); + } + catch( const uno::Exception& ) + { + SAL_WARN( "embeddedobj.ole", "The object should not be resized without activation!" ); + } + aGuard.reset(); + } + + if ( m_pOleComponent && m_nObjectState != embed::EmbedStates::LOADED && bAllowToSetExtent ) + { + awt::Size aSizeToSet = aSize; + aGuard.clear(); + try { + m_pOleComponent->SetExtent( aSizeToSet, nAspect ); // will throw an exception in case of failure + m_bHasSizeToSet = false; + } + catch( const uno::Exception& ) + { + // some objects do not allow to set the size even in running state + m_bHasSizeToSet = true; + m_aSizeToSet = aSizeToSet; + m_nAspectToSet = nAspect; + } + aGuard.reset(); + } +#endif + + // cache the values + m_bHasCachedSize = true; + m_aCachedSize = aSize; + m_nCachedAspect = nAspect; +} + +awt::Size SAL_CALL OleEmbeddedObject::getVisualAreaSize( sal_Int64 nAspect ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getVisualAreaSize( nAspect ); + } + // end wrapping related part ==================== + + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.ole", "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object is not loaded!", + static_cast< ::cppu::OWeakObject* >(this) ); + + awt::Size aResult; + +#ifdef _WIN32 + // TODO/LATER: Support different aspects + if ( m_pOleComponent && !m_bHasSizeToSet && nAspect == embed::Aspects::MSOLE_CONTENT ) + { + try + { + // the cached size updated every time the object is stored + if ( m_bHasCachedSize ) + { + aResult = m_aCachedSize; + } + else + { + // there is no internal cache + awt::Size aSize; + aGuard.clear(); + + bool bBackToLoaded = false; + + bool bSuccess = false; + if ( getCurrentState() == embed::EmbedStates::LOADED ) + { + SAL_WARN( "embeddedobj.ole", "Loaded object has no cached size!" ); + + // try to switch the object to RUNNING state and request the value again + try { + changeState( embed::EmbedStates::RUNNING ); + // the links should be switched back to loaded state to avoid too + // many open MathType instances + bBackToLoaded = true; + } + catch( const uno::Exception& ) + { + throw embed::NoVisualAreaSizeException( + "No size available!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + } + + try + { + // first try to get size using replacement image + aSize = m_pOleComponent->GetExtent( nAspect ); // will throw an exception in case of failure + bSuccess = true; + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("embeddedobj.ole", "OleEmbeddedObject::getVisualAreaSize: GetExtent() failed:"); + } + + if (bBackToLoaded) + { + try + { + changeState(embed::EmbedStates::LOADED); + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("embeddedobj.ole", "ignoring "); + } + } + + if ( !bSuccess ) + { + try + { + // second try the cached replacement image + aSize = m_pOleComponent->GetCachedExtent( nAspect ); // will throw an exception in case of failure + bSuccess = true; + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("embeddedobj.ole", "OleEmbeddedObject::getVisualAreaSize: GetCachedExtent() failed:"); + } + } + + if ( !bSuccess ) + { + try + { + // third try the size reported by the object + aSize = m_pOleComponent->GetRecommendedExtent( nAspect ); // will throw an exception in case of failure + bSuccess = true; + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("embeddedobj.ole", "OleEmbeddedObject::getVisualAreaSize: GetRecommendedExtent() failed:"); + } + } + + if ( !bSuccess ) + throw embed::NoVisualAreaSizeException( + "No size available!", + static_cast< ::cppu::OWeakObject* >(this) ); + + aGuard.reset(); + + m_aCachedSize = aSize; + m_nCachedAspect = nAspect; + m_bHasCachedSize = true; + + aResult = m_aCachedSize; + } + } + catch ( const embed::NoVisualAreaSizeException& ) + { + throw; + } + catch ( const uno::Exception& ) + { + throw embed::NoVisualAreaSizeException( + "No size available!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + } + else +#endif + { + // return cached value + if ( !m_bHasCachedSize ) + { + throw embed::NoVisualAreaSizeException( + "No size available!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + SAL_WARN_IF( nAspect != m_nCachedAspect, "embeddedobj.ole", "Unexpected aspect is requested!" ); + aResult = m_aCachedSize; + } + + return aResult; +} + +embed::VisualRepresentation SAL_CALL OleEmbeddedObject::getPreferredVisualRepresentation( sal_Int64 nAspect ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getPreferredVisualRepresentation( nAspect ); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.ole", "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // TODO: if the object has cached representation then it should be returned + // TODO: if the object has no cached representation and is in loaded state it should switch itself to the running state + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object is not loaded!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // TODO: in case of different aspects they must be applied to the mediatype and XTransferable must be used + // the cache is used only as a fallback if object is not in loaded state + if ( !m_xCachedVisualRepresentation.is() && ( !m_bVisReplInitialized || m_bVisReplInStream ) + && m_nObjectState == embed::EmbedStates::LOADED ) + { + m_xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( m_xObjectStream, true ); + SetVisReplInStream( m_xCachedVisualRepresentation.is() ); + } + +#ifdef _WIN32 + if ( !m_xCachedVisualRepresentation.is() && m_pOleComponent ) + { + try + { + if ( m_nObjectState == embed::EmbedStates::LOADED ) + changeState( embed::EmbedStates::RUNNING ); + + datatransfer::DataFlavor aDataFlavor( + "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"", + "Windows Metafile", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ); + + embed::VisualRepresentation aVisualRepr; + aVisualRepr.Data = m_pOleComponent->getTransferData( aDataFlavor ); + aVisualRepr.Flavor = aDataFlavor; + + uno::Sequence< sal_Int8 > aVisReplSeq; + aVisualRepr.Data >>= aVisReplSeq; + if ( aVisReplSeq.getLength() ) + { + m_xCachedVisualRepresentation = GetNewFilledTempStream_Impl( + uno::Reference< io::XInputStream >( + new ::comphelper::SequenceInputStream(aVisReplSeq))); + } + + return aVisualRepr; + } + catch( const uno::Exception& ) + {} + } +#endif + + // the cache is used only as a fallback if object is not in loaded state + if ( !m_xCachedVisualRepresentation.is() && ( !m_bVisReplInitialized || m_bVisReplInStream ) ) + { + m_xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( m_xObjectStream ); + SetVisReplInStream( m_xCachedVisualRepresentation.is() ); + } + + if ( !m_xCachedVisualRepresentation.is() ) + { + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + return GetVisualRepresentationInNativeFormat_Impl( m_xCachedVisualRepresentation ); +} + +sal_Int32 SAL_CALL OleEmbeddedObject::getMapUnit( sal_Int64 nAspect ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getMapUnit( nAspect ); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.ole", "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object is not loaded!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return embed::EmbedMapUnits::ONE_100TH_MM; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olewrapclient.cxx b/embeddedobj/source/msole/olewrapclient.cxx new file mode 100644 index 0000000000..15d5f16632 --- /dev/null +++ b/embeddedobj/source/msole/olewrapclient.cxx @@ -0,0 +1,148 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <osl/diagnose.h> + +#include "olewrapclient.hxx" +#include "olecomponent.hxx" + +// TODO: May be a mutex must be introduced + +OleWrapperClientSite::OleWrapperClientSite( OleComponent* pOleComp ) +: m_nRefCount( 0 ) +, m_pOleComp( pOleComp ) +{ + OSL_ENSURE( m_pOleComp, "No ole component is provided!" ); +} + +OleWrapperClientSite::~OleWrapperClientSite() +{ +} + +STDMETHODIMP OleWrapperClientSite::QueryInterface( REFIID riid , void** ppv ) +{ + *ppv=nullptr; + + if ( riid == IID_IUnknown ) + *ppv = static_cast<IUnknown*>(this); + + if ( riid == IID_IOleClientSite ) + *ppv = static_cast<IOleClientSite*>(this); + + if ( *ppv != nullptr ) + { + static_cast<IUnknown*>(*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) OleWrapperClientSite::AddRef() +{ + return osl_atomic_increment( &m_nRefCount); +} + +STDMETHODIMP_(ULONG) OleWrapperClientSite::Release() +{ + ULONG nReturn = --m_nRefCount; + if ( m_nRefCount == 0 ) + delete this; + + return nReturn; +} + +void OleWrapperClientSite::disconnectOleComponent() +{ + // must not be called from the descructor of OleComponent!!! + osl::MutexGuard aGuard( m_aMutex ); + m_pOleComp = nullptr; +} + +STDMETHODIMP OleWrapperClientSite::SaveObject() +{ + OleComponent* pLockComponent = nullptr; + HRESULT hResult = E_FAIL; + + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pOleComp ) + { + pLockComponent = m_pOleComp; + pLockComponent->acquire(); + } + } + + if ( pLockComponent ) + { + if ( pLockComponent->SaveObject_Impl() ) + hResult = S_OK; + + pLockComponent->release(); + } + + return hResult; +} + +STDMETHODIMP OleWrapperClientSite::GetMoniker( DWORD, DWORD, IMoniker **ppmk ) +{ + *ppmk = nullptr; + return E_NOTIMPL; +} + +STDMETHODIMP OleWrapperClientSite::GetContainer( IOleContainer** ppContainer ) +{ + *ppContainer = nullptr; + return E_NOTIMPL; +} + +STDMETHODIMP OleWrapperClientSite::ShowObject() +{ + return S_OK; +} + +STDMETHODIMP OleWrapperClientSite::OnShowWindow( BOOL bShow ) +{ + OleComponent* pLockComponent = nullptr; + + // TODO/LATER: redirect the notification to the main thread so that SolarMutex can be locked + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pOleComp ) + { + pLockComponent = m_pOleComp; + pLockComponent->acquire(); + } + } + + if ( pLockComponent ) + { + pLockComponent->OnShowWindow_Impl( bShow ); // the result is not interesting + pLockComponent->release(); + } + + return S_OK; +} + +STDMETHODIMP OleWrapperClientSite::RequestNewObjectLayout() +{ + return E_NOTIMPL; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olewrapclient.hxx b/embeddedobj/source/msole/olewrapclient.hxx new file mode 100644 index 0000000000..99f4e16ea5 --- /dev/null +++ b/embeddedobj/source/msole/olewrapclient.hxx @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <osl/interlck.h> +#include <osl/mutex.hxx> +#include "platform.h" + +class OleComponent; +class OleWrapperClientSite : public IOleClientSite +{ + osl::Mutex m_aMutex; + oslInterlockedCount m_nRefCount; + OleComponent* m_pOleComp; + +public: + explicit OleWrapperClientSite(OleComponent* pOleComp); + virtual ~OleWrapperClientSite(); + + void disconnectOleComponent(); + + STDMETHODIMP QueryInterface(REFIID, void**) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + STDMETHODIMP SaveObject() override; + STDMETHODIMP GetMoniker(DWORD, DWORD, IMoniker**) override; + STDMETHODIMP GetContainer(IOleContainer**) override; + STDMETHODIMP ShowObject() override; + STDMETHODIMP OnShowWindow(BOOL) override; + STDMETHODIMP RequestNewObjectLayout() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/ownview.cxx b/embeddedobj/source/msole/ownview.cxx new file mode 100644 index 0000000000..5576f8633c --- /dev/null +++ b/embeddedobj/source/msole/ownview.cxx @@ -0,0 +1,609 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/embed/XClassifiedObject.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/util/XCloseable.hpp> + +#include <com/sun/star/document/XEventBroadcaster.hpp> +#include <com/sun/star/document/XEventListener.hpp> +#include <com/sun/star/document/XTypeDetection.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <cppuhelper/implbase.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/mimeconfighelper.hxx> +#include <comphelper/diagnose_ex.hxx> + +#include "olepersist.hxx" +#include "ownview.hxx" + + +using namespace ::com::sun::star; +using namespace ::comphelper; + +namespace { + +class DummyHandler_Impl : public ::cppu::WeakImplHelper< task::XInteractionHandler > +{ +public: + DummyHandler_Impl() {} + + virtual void SAL_CALL handle( const uno::Reference< task::XInteractionRequest >& xRequest ) override; +}; + +} + +void SAL_CALL DummyHandler_Impl::handle( const uno::Reference< task::XInteractionRequest >& ) +{ +} + + +// Object viewer + + +OwnView_Impl::OwnView_Impl( const uno::Reference< uno::XComponentContext >& xContext, + const uno::Reference< io::XInputStream >& xInputStream ) +: m_xContext( xContext ) +, m_bBusy( false ) +, m_bUseNative( false ) +{ + if ( !xContext.is() || !xInputStream.is() ) + throw uno::RuntimeException(); + + m_aTempFileURL = GetNewFilledTempFile_Impl( xInputStream, m_xContext ); +} + + +OwnView_Impl::~OwnView_Impl() +{ + try { + KillFile_Impl( m_aTempFileURL, m_xContext ); + } catch( uno::Exception& ) {} + + try { + if ( !m_aNativeTempURL.isEmpty() ) + KillFile_Impl( m_aNativeTempURL, m_xContext ); + } catch( uno::Exception& ) {} +} + + +bool OwnView_Impl::CreateModelFromURL( const OUString& aFileURL ) +{ + bool bResult = false; + + if ( !aFileURL.isEmpty() ) + { + try { + uno::Reference < frame::XDesktop2 > xDocumentLoader = frame::Desktop::create(m_xContext); + + uno::Sequence< beans::PropertyValue > aArgs( m_aFilterName.isEmpty() ? 4 : 5 ); + auto pArgs = aArgs.getArray(); + + pArgs[0].Name = "URL"; + pArgs[0].Value <<= aFileURL; + + pArgs[1].Name = "ReadOnly"; + pArgs[1].Value <<= true; + + pArgs[2].Name = "InteractionHandler"; + pArgs[2].Value <<= uno::Reference< task::XInteractionHandler >( new DummyHandler_Impl() ); + + pArgs[3].Name = "DontEdit"; + pArgs[3].Value <<= true; + + if ( !m_aFilterName.isEmpty() ) + { + pArgs[4].Name = "FilterName"; + pArgs[4].Value <<= m_aFilterName; + } + + uno::Reference< frame::XModel > xModel( xDocumentLoader->loadComponentFromURL( + aFileURL, + "_blank", + 0, + aArgs ), + uno::UNO_QUERY ); + + if ( xModel.is() ) + { + uno::Reference< document::XEventBroadcaster > xBroadCaster( xModel, uno::UNO_QUERY ); + if ( xBroadCaster.is() ) + xBroadCaster->addEventListener( uno::Reference< document::XEventListener >(this) ); + + uno::Reference< util::XCloseable > xCloseable( xModel, uno::UNO_QUERY ); + if ( xCloseable.is() ) + { + xCloseable->addCloseListener( uno::Reference< util::XCloseListener >(this) ); + + ::osl::MutexGuard aGuard( m_aMutex ); + m_xModel = xModel; + bResult = true; + } + } + } + catch (uno::Exception const&) + { + TOOLS_WARN_EXCEPTION("embeddedobj.ole", "OwnView_Impl::CreateModelFromURL:"); + } + } + + return bResult; +} + + +bool OwnView_Impl::CreateModel( bool bUseNative ) +{ + bool bResult = false; + + try { + bResult = CreateModelFromURL( bUseNative ? m_aNativeTempURL : m_aTempFileURL ); + } + catch( uno::Exception& ) + { + } + + return bResult; +} + + +OUString OwnView_Impl::GetFilterNameFromExtentionAndInStream( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + std::u16string_view aNameWithExtention, + const uno::Reference< io::XInputStream >& xInputStream ) +{ + if ( !xInputStream.is() ) + throw uno::RuntimeException(); + + uno::Reference< document::XTypeDetection > xTypeDetection( + xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", xContext), + uno::UNO_QUERY_THROW ); + + OUString aTypeName; + + if ( !aNameWithExtention.empty() ) + { + OUString aURLToAnalyze = OUString::Concat("file:///") + aNameWithExtention; + aTypeName = xTypeDetection->queryTypeByURL( aURLToAnalyze ); + } + + uno::Sequence< beans::PropertyValue > aArgs( aTypeName.isEmpty() ? 2 : 3 ); + auto pArgs = aArgs.getArray(); + pArgs[0].Name = "URL"; + pArgs[0].Value <<= OUString( "private:stream" ); + pArgs[1].Name = "InputStream"; + pArgs[1].Value <<= xInputStream; + if ( !aTypeName.isEmpty() ) + { + pArgs[2].Name = "TypeName"; + pArgs[2].Value <<= aTypeName; + } + + aTypeName = xTypeDetection->queryTypeByDescriptor( aArgs, true ); + + OUString aFilterName; + for ( beans::PropertyValue const & prop : std::as_const(aArgs) ) + if ( prop.Name == "FilterName" ) + prop.Value >>= aFilterName; + + if ( aFilterName.isEmpty() && !aTypeName.isEmpty() ) + { + // get the default filter name for the type + uno::Reference< container::XNameAccess > xNameAccess( xTypeDetection, uno::UNO_QUERY_THROW ); + uno::Sequence< beans::PropertyValue > aTypes; + + if ( xNameAccess.is() && ( xNameAccess->getByName( aTypeName ) >>= aTypes ) ) + { + for ( beans::PropertyValue const & prop : std::as_const(aTypes) ) + { + if ( prop.Name == "PreferredFilter" && ( prop.Value >>= aFilterName ) ) + { + prop.Value >>= aFilterName; + break; + } + } + } + } + + return aFilterName; +} + + +bool OwnView_Impl::ReadContentsAndGenerateTempFile( const uno::Reference< io::XInputStream >& xInStream, + bool bParseHeader ) +{ + uno::Reference< io::XSeekable > xSeekable( xInStream, uno::UNO_QUERY_THROW ); + xSeekable->seek( 0 ); + + // create m_aNativeTempURL + OUString aNativeTempURL; + uno::Reference < io::XTempFile > xNativeTempFile( + io::TempFile::create(m_xContext), + uno::UNO_SET_THROW ); + uno::Reference < io::XOutputStream > xNativeOutTemp = xNativeTempFile->getOutputStream(); + uno::Reference < io::XInputStream > xNativeInTemp = xNativeTempFile->getInputStream(); + if ( !xNativeOutTemp.is() || !xNativeInTemp.is() ) + throw uno::RuntimeException(); + + try { + xNativeTempFile->setRemoveFile( false ); + aNativeTempURL = xNativeTempFile->getUri(); + } + catch ( uno::Exception& ) + { + } + + bool bFailed = false; + OUString aFileSuffix; + + if ( bParseHeader ) + { + uno::Sequence< sal_Int8 > aReadSeq( 4 ); + // read the complete size of the Object Package + if ( xInStream->readBytes( aReadSeq, 4 ) != 4 ) + return false; + // read the first header ( have no idea what does this header mean ) + if ( xInStream->readBytes( aReadSeq, 2 ) != 2 || aReadSeq[0] != 2 || aReadSeq[1] != 0 ) + return false; + + // read file name + // only extension is interesting so only subset of symbols is accepted + do + { + if ( xInStream->readBytes( aReadSeq, 1 ) != 1 ) + return false; + + if ( + (aReadSeq[0] >= '0' && aReadSeq[0] <= '9') || + (aReadSeq[0] >= 'a' && aReadSeq[0] <= 'z') || + (aReadSeq[0] >= 'A' && aReadSeq[0] <= 'Z') || + aReadSeq[0] == '.' + ) + { + aFileSuffix += OUStringChar( sal_Unicode(aReadSeq[0]) ); + } + + } while( aReadSeq[0] ); + + // skip url + do + { + if ( xInStream->readBytes( aReadSeq, 1 ) != 1 ) + return false; + } while( aReadSeq[0] ); + + // check the next header + if ( xInStream->readBytes( aReadSeq, 4 ) != 4 + || aReadSeq[0] || aReadSeq[1] || aReadSeq[2] != 3 || aReadSeq[3] ) + return false; + + // get the size of the next entry + if ( xInStream->readBytes( aReadSeq, 4 ) != 4 ) + return false; + + sal_uInt32 nUrlSize = static_cast<sal_uInt8>(aReadSeq[0]) + + static_cast<sal_uInt8>(aReadSeq[1]) * 0x100 + + static_cast<sal_uInt8>(aReadSeq[2]) * 0x10000 + + static_cast<sal_uInt8>(aReadSeq[3]) * 0x1000000; + sal_Int64 nTargetPos = xSeekable->getPosition() + nUrlSize; + + xSeekable->seek( nTargetPos ); + + // get the size of stored data + if ( xInStream->readBytes( aReadSeq, 4 ) != 4 ) + return false; + + sal_uInt32 nDataSize = static_cast<sal_uInt8>(aReadSeq[0]) + + static_cast<sal_uInt8>(aReadSeq[1]) * 0x100 + + static_cast<sal_uInt8>(aReadSeq[2]) * 0x10000 + + static_cast<sal_uInt8>(aReadSeq[3]) * 0x1000000; + + aReadSeq.realloc( 32000 ); + sal_uInt32 nRead = 0; + while ( nRead < nDataSize ) + { + sal_uInt32 nToRead = std::min<sal_uInt32>( nDataSize - nRead, 32000 ); + sal_uInt32 nLocalRead = xInStream->readBytes( aReadSeq, nToRead ); + + + if ( !nLocalRead ) + { + bFailed = true; + break; + } + else if ( nLocalRead == 32000 ) + xNativeOutTemp->writeBytes( aReadSeq ); + else + { + uno::Sequence< sal_Int8 > aToWrite( aReadSeq ); + aToWrite.realloc( nLocalRead ); + xNativeOutTemp->writeBytes( aToWrite ); + } + + nRead += nLocalRead; + } + } + else + { + uno::Sequence< sal_Int8 > aData( 8 ); + if ( xInStream->readBytes( aData, 8 ) == 8 + && aData[0] == -1 && aData[1] == -1 && aData[2] == -1 && aData[3] == -1 + && ( aData[4] == 2 || aData[4] == 3 ) && aData[5] == 0 && aData[6] == 0 && aData[7] == 0 ) + { + // the header has to be removed + xSeekable->seek( 40 ); + } + else + { + // the usual Ole10Native format + xSeekable->seek( 4 ); + } + + ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xNativeOutTemp ); + } + + xNativeOutTemp->closeOutput(); + + // The temporary native file is created, now the filter must be detected + if ( !bFailed ) + { + m_aFilterName = GetFilterNameFromExtentionAndInStream( m_xContext, aFileSuffix, xNativeInTemp ); + m_aNativeTempURL = aNativeTempURL; + } + + return !bFailed; +} + + +void OwnView_Impl::CreateNative() +{ + if ( !m_aNativeTempURL.isEmpty() ) + return; + + try + { + uno::Reference < ucb::XSimpleFileAccess3 > xAccess( + ucb::SimpleFileAccess::create( m_xContext ) ); + + uno::Reference< io::XInputStream > xInStream = xAccess->openFileRead( m_aTempFileURL ); + if ( !xInStream.is() ) + throw uno::RuntimeException(); + + uno::Sequence< uno::Any > aArgs{ uno::Any(xInStream) }; + uno::Reference< container::XNameAccess > xNameAccess( + m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.embed.OLESimpleStorage", + aArgs, m_xContext ), + uno::UNO_QUERY_THROW ); + + static constexpr OUString aSubStreamName(u"\1Ole10Native"_ustr); + uno::Reference< embed::XClassifiedObject > xStor( xNameAccess, uno::UNO_QUERY_THROW ); + uno::Sequence< sal_Int8 > aStorClassID = xStor->getClassID(); + + if ( xNameAccess->hasByName( aSubStreamName ) ) + { + sal_uInt8 const aClassID[] = + { 0x00, 0x03, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 }; + // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence + uno::Sequence< sal_Int8 > aPackageClassID( reinterpret_cast<sal_Int8 const *>(aClassID), 16 ); + + uno::Reference< io::XStream > xSubStream; + xNameAccess->getByName( aSubStreamName ) >>= xSubStream; + if ( xSubStream.is() ) + { + bool bOk = false; + + if ( MimeConfigurationHelper::ClassIDsEqual( aPackageClassID, aStorClassID ) ) + { + // the storage represents Object Package + + bOk = ReadContentsAndGenerateTempFile( xSubStream->getInputStream(), true ); + + if ( !bOk && !m_aNativeTempURL.isEmpty() ) + { + KillFile_Impl( m_aNativeTempURL, m_xContext ); + m_aNativeTempURL.clear(); + } + } + + if ( !bOk ) + { + bOk = ReadContentsAndGenerateTempFile( xSubStream->getInputStream(), false ); + + if ( !bOk && !m_aNativeTempURL.isEmpty() ) + { + KillFile_Impl( m_aNativeTempURL, m_xContext ); + m_aNativeTempURL.clear(); + } + } + } + } + else + { + // TODO/LATER: No native stream, needs a new solution + } + } + catch( uno::Exception& ) + {} +} + + +bool OwnView_Impl::Open() +{ + bool bResult = false; + + uno::Reference< frame::XModel > xExistingModel; + + { + ::osl::MutexGuard aGuard( m_aMutex ); + xExistingModel = m_xModel; + if ( m_bBusy ) + return false; + + m_bBusy = true; + } + + if ( xExistingModel.is() ) + { + try { + uno::Reference< frame::XController > xController = xExistingModel->getCurrentController(); + if ( xController.is() ) + { + uno::Reference< frame::XFrame > xFrame = xController->getFrame(); + if ( xFrame.is() ) + { + xFrame->activate(); + uno::Reference<awt::XTopWindow> xTopWindow( xFrame->getContainerWindow(), uno::UNO_QUERY ); + if(xTopWindow.is()) + xTopWindow->toFront(); + + bResult = true; + } + } + } + catch( uno::Exception& ) + { + } + } + else + { + bResult = CreateModel( m_bUseNative ); + + if ( !bResult && !m_bUseNative ) + { + // the original storage can not be recognized + if ( m_aNativeTempURL.isEmpty() ) + { + // create a temporary file for the native representation if there is no + CreateNative(); + } + + if ( !m_aNativeTempURL.isEmpty() ) + { + bResult = CreateModel( true ); + if ( bResult ) + m_bUseNative = true; + } + } + } + + m_bBusy = false; + + return bResult; +} + + +void OwnView_Impl::Close() +{ + uno::Reference< frame::XModel > xModel; + + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !m_xModel.is() ) + return; + xModel = m_xModel; + m_xModel.clear(); + + if ( m_bBusy ) + return; + + m_bBusy = true; + } + + try { + uno::Reference< document::XEventBroadcaster > xBroadCaster( xModel, uno::UNO_QUERY ); + if ( xBroadCaster.is() ) + xBroadCaster->removeEventListener( uno::Reference< document::XEventListener >( this ) ); + + uno::Reference< util::XCloseable > xCloseable( xModel, uno::UNO_QUERY ); + if ( xCloseable.is() ) + { + xCloseable->removeCloseListener( uno::Reference< util::XCloseListener >( this ) ); + xCloseable->close( true ); + } + } + catch( uno::Exception& ) + {} + + m_bBusy = false; +} + + +void SAL_CALL OwnView_Impl::notifyEvent( const document::EventObject& aEvent ) +{ + + uno::Reference< frame::XModel > xModel; + + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( aEvent.Source == m_xModel && aEvent.EventName == "OnSaveAsDone" ) + { + // SaveAs operation took place, so just forget the model and deregister listeners + xModel = m_xModel; + m_xModel.clear(); + } + } + + if ( !xModel.is() ) + return; + + try { + uno::Reference< document::XEventBroadcaster > xBroadCaster( xModel, uno::UNO_QUERY ); + if ( xBroadCaster.is() ) + xBroadCaster->removeEventListener( uno::Reference< document::XEventListener >( this ) ); + + uno::Reference< util::XCloseable > xCloseable( xModel, uno::UNO_QUERY ); + if ( xCloseable.is() ) + xCloseable->removeCloseListener( uno::Reference< util::XCloseListener >( this ) ); + } + catch( uno::Exception& ) + {} +} + + +void SAL_CALL OwnView_Impl::queryClosing( const lang::EventObject&, sal_Bool ) +{ +} + + +void SAL_CALL OwnView_Impl::notifyClosing( const lang::EventObject& Source ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( Source.Source == m_xModel ) + m_xModel.clear(); +} + + +void SAL_CALL OwnView_Impl::disposing( const lang::EventObject& Source ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( Source.Source == m_xModel ) + m_xModel.clear(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/ownview.hxx b/embeddedobj/source/msole/ownview.hxx new file mode 100644 index 0000000000..d939aff860 --- /dev/null +++ b/embeddedobj/source/msole/ownview.hxx @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <string_view> + +#include <com/sun/star/util/XCloseListener.hpp> +#include <com/sun/star/document/XEventListener.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/implbase.hxx> + +#include <osl/mutex.hxx> + +class OwnView_Impl : public ::cppu::WeakImplHelper < css::util::XCloseListener, + css::document::XEventListener > +{ + ::osl::Mutex m_aMutex; + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::frame::XModel > m_xModel; + + OUString m_aTempFileURL; + OUString m_aNativeTempURL; + + OUString m_aFilterName; + + bool m_bBusy; + + bool m_bUseNative; + +private: + bool CreateModelFromURL( const OUString& aFileURL ); + + bool CreateModel( bool bUseNative ); + + bool ReadContentsAndGenerateTempFile( const css::uno::Reference< css::io::XInputStream >& xStream, bool bParseHeader ); + + void CreateNative(); + +public: + static OUString GetFilterNameFromExtentionAndInStream( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + std::u16string_view aNameWithExtention, + const css::uno::Reference< css::io::XInputStream >& xInputStream ); + + OwnView_Impl( const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Reference< css::io::XInputStream >& xStream ); + virtual ~OwnView_Impl() override; + + bool Open(); + + void Close(); + + virtual void SAL_CALL notifyEvent( const css::document::EventObject& Event ) override; + + virtual void SAL_CALL queryClosing( const css::lang::EventObject& Source, sal_Bool GetsOwnership ) override; + virtual void SAL_CALL notifyClosing( const css::lang::EventObject& Source ) override; + + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/platform.h b/embeddedobj/source/msole/platform.h new file mode 100644 index 0000000000..68127b6622 --- /dev/null +++ b/embeddedobj/source/msole/platform.h @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#define STRICT +#define _ATL_APARTMENT_THREADED + +#if !defined WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <ole2.h> + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/xdialogcreator.cxx b/embeddedobj/source/msole/xdialogcreator.cxx new file mode 100644 index 0000000000..7e4e991562 --- /dev/null +++ b/embeddedobj/source/msole/xdialogcreator.cxx @@ -0,0 +1,356 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <string_view> + +#include <com/sun/star/embed/EmbeddedObjectCreator.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/EntryInitModes.hpp> +#include <com/sun/star/embed/OLEEmbeddedObjectFactory.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/datatransfer/DataFlavor.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/ucb/CommandAbortedException.hpp> +#include <com/sun/star/task/XStatusIndicatorFactory.hpp> + +#include <osl/thread.h> +#include <osl/file.hxx> +#include <osl/module.hxx> +#include <comphelper/classids.hxx> + +#include "platform.h" +#include <comphelper/mimeconfighelper.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <comphelper/sequenceashashmap.hxx> + +#include "xdialogcreator.hxx" +#include <oleembobj.hxx> + + +#ifdef _WIN32 + +#include <oledlg.h> +#include <vcl/winscheduler.hxx> + +namespace { + +class InitializedOleGuard +{ +public: + InitializedOleGuard() + { + if ( !SUCCEEDED( OleInitialize( nullptr ) ) ) + throw css::uno::RuntimeException(); + } + + ~InitializedOleGuard() + { + OleUninitialize(); + } +}; + +} + +extern "C" { +typedef UINT STDAPICALLTYPE OleUIInsertObjectA_Type(LPOLEUIINSERTOBJECTA); +} + +#endif + + +using namespace ::com::sun::star; +using namespace ::comphelper; + +static uno::Sequence< sal_Int8 > GetRelatedInternalID_Impl( const uno::Sequence< sal_Int8 >& aClassID ) +{ + // Writer + if ( MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SW_OLE_EMBED_CLASSID_60 ) ) + || MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SW_OLE_EMBED_CLASSID_8 ) ) ) + return MimeConfigurationHelper::GetSequenceClassID( SO3_SW_CLASSID_60 ); + + // Calc + if ( MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SC_OLE_EMBED_CLASSID_60 ) ) + || MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SC_OLE_EMBED_CLASSID_8 ) ) ) + return MimeConfigurationHelper::GetSequenceClassID( SO3_SC_CLASSID_60 ); + + // Impress + if ( MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SIMPRESS_OLE_EMBED_CLASSID_60 ) ) + || MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SIMPRESS_OLE_EMBED_CLASSID_8 ) ) ) + return MimeConfigurationHelper::GetSequenceClassID( SO3_SIMPRESS_CLASSID_60 ); + + // Draw + if ( MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SDRAW_OLE_EMBED_CLASSID_60 ) ) + || MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SDRAW_OLE_EMBED_CLASSID_8 ) ) ) + return MimeConfigurationHelper::GetSequenceClassID( SO3_SDRAW_CLASSID_60 ); + + // Chart + if ( MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SCH_OLE_EMBED_CLASSID_60 ) ) + || MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SCH_OLE_EMBED_CLASSID_8 ) ) ) + return MimeConfigurationHelper::GetSequenceClassID( SO3_SCH_CLASSID_60 ); + + // Math + if ( MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SM_OLE_EMBED_CLASSID_60 ) ) + || MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SM_OLE_EMBED_CLASSID_8 ) ) ) + return MimeConfigurationHelper::GetSequenceClassID( SO3_SM_CLASSID_60 ); + + return aClassID; +} + + +embed::InsertedObjectInfo SAL_CALL MSOLEDialogObjectCreator::createInstanceByDialog( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& aInObjArgs ) +{ + embed::InsertedObjectInfo aObjectInfo; + uno::Sequence< beans::PropertyValue > aObjArgs( aInObjArgs ); + +#ifdef _WIN32 + + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( !sEntName.getLength() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + InitializedOleGuard aGuard; + + OLEUIINSERTOBJECT io = {}; + char szFile[MAX_PATH]; + UINT uTemp; + + io.cbStruct = sizeof(io); + io.hWndOwner = GetActiveWindow(); + + szFile[0] = 0; + io.lpszFile = szFile; + io.cchFile = MAX_PATH; + + io.dwFlags = IOF_SELECTCREATENEW | IOF_DISABLELINK; + + + ::osl::Module aOleDlgLib; + if( !aOleDlgLib.load( "oledlg" )) + throw uno::RuntimeException(); + + OleUIInsertObjectA_Type * pInsertFct = reinterpret_cast<OleUIInsertObjectA_Type *>( + aOleDlgLib.getSymbol( "OleUIInsertObjectA" )); + if( !pInsertFct ) + throw uno::RuntimeException(); + + // Disable any event loop shortcuts by enabling a real timer. + // This way the native windows dialog won't block our own processing. + WinScheduler::SetForceRealTimer(); + + uTemp=pInsertFct(&io); + + if ( OLEUI_OK != uTemp ) + throw ucb::CommandAbortedException(); + + if (io.dwFlags & IOF_SELECTCREATENEW) + { + uno::Reference< embed::XEmbeddedObjectCreator > xEmbCreator = embed::EmbeddedObjectCreator::create( m_xContext ); + + uno::Sequence< sal_Int8 > aClassID = MimeConfigurationHelper::GetSequenceClassID( io.clsid.Data1, + io.clsid.Data2, + io.clsid.Data3, + io.clsid.Data4[0], + io.clsid.Data4[1], + io.clsid.Data4[2], + io.clsid.Data4[3], + io.clsid.Data4[4], + io.clsid.Data4[5], + io.clsid.Data4[6], + io.clsid.Data4[7] ); + + aClassID = GetRelatedInternalID_Impl( aClassID ); + + //TODO: retrieve ClassName + aObjectInfo.Object.set( xEmbCreator->createInstanceInitNew( aClassID, OUString(), xStorage, sEntName, aObjArgs ), + uno::UNO_QUERY ); + } + else + { + OUString aFileName + = OStringToOUString( std::string_view( szFile ), osl_getThreadTextEncoding() ); + OUString aFileURL; + if ( osl::FileBase::getFileURLFromSystemPath( aFileName, aFileURL ) != osl::FileBase::E_None ) + throw uno::RuntimeException(); + + uno::Sequence< beans::PropertyValue > aMediaDescr{ comphelper::makePropertyValue("URL", + aFileURL) }; + + // TODO: use config helper for type detection + uno::Reference< embed::XEmbeddedObjectCreator > xEmbCreator; + ::comphelper::MimeConfigurationHelper aHelper( m_xContext ); + + if ( aHelper.AddFilterNameCheckOwnFile( aMediaDescr ) ) + xEmbCreator = embed::EmbeddedObjectCreator::create( m_xContext ); + else + xEmbCreator = embed::OLEEmbeddedObjectFactory::create( m_xContext ); + + if ( !xEmbCreator.is() ) + throw uno::RuntimeException(); + + uno::Reference<task::XStatusIndicator> xProgress; + OUString aProgressText; + comphelper::SequenceAsHashMap aMap(aInObjArgs); + auto it = aMap.find("StatusIndicator"); + if (it != aMap.end()) + { + it->second >>= xProgress; + } + it = aMap.find("StatusIndicatorText"); + if (it != aMap.end()) + { + it->second >>= aProgressText; + } + if (xProgress.is()) + { + xProgress->start(aProgressText, 100); + } + + aObjectInfo.Object.set( xEmbCreator->createInstanceInitFromMediaDescriptor( xStorage, sEntName, aMediaDescr, aObjArgs ), + uno::UNO_QUERY ); + + if (xProgress.is()) + { + xProgress->end(); + } + } + + if ( ( io.dwFlags & IOF_CHECKDISPLAYASICON) && io.hMetaPict != nullptr ) + { + METAFILEPICT* pMF = static_cast<METAFILEPICT*>(GlobalLock( io.hMetaPict )); + if ( pMF ) + { + sal_uInt32 nBufSize = GetMetaFileBitsEx( pMF->hMF, 0, nullptr ); + uno::Sequence< sal_Int8 > aMetafile( nBufSize + 22 ); + sal_Int8* pBuf = aMetafile.getArray(); + *reinterpret_cast<long*>( pBuf ) = 0x9ac6cdd7L; + *reinterpret_cast<short*>( pBuf+6 ) = SHORT(0); + *reinterpret_cast<short*>( pBuf+8 ) = SHORT(0); + *reinterpret_cast<short*>( pBuf+10 ) = static_cast<SHORT>(pMF->xExt); + *reinterpret_cast<short*>( pBuf+12 ) = static_cast<SHORT>(pMF->yExt); + *reinterpret_cast<short*>( pBuf+14 ) = USHORT(2540); + + if ( nBufSize && nBufSize == GetMetaFileBitsEx( pMF->hMF, nBufSize, pBuf+22 ) ) + { + datatransfer::DataFlavor aFlavor( + "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"", + "Image WMF", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ); + + aObjectInfo.Options = { { "Icon", css::uno::Any(aMetafile) }, + { "IconFormat", css::uno::Any(aFlavor) } }; + } + + GlobalUnlock( io.hMetaPict ); + } + } + + OSL_ENSURE( aObjectInfo.Object.is(), "No object was created!" ); + if ( !aObjectInfo.Object.is() ) + throw uno::RuntimeException(); + + return aObjectInfo; +#else + throw lang::NoSupportException(); // TODO: +#endif +} + + +embed::InsertedObjectInfo SAL_CALL MSOLEDialogObjectCreator::createInstanceInitFromClipboard( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntryName, + const uno::Sequence< beans::PropertyValue >& aObjectArgs ) +{ + embed::InsertedObjectInfo aObjectInfo; + +#ifdef _WIN32 + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( !sEntryName.getLength() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + uno::Reference< embed::XEmbeddedObject > xResult( + new OleEmbeddedObject( m_xContext ) ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, + sEntryName, + embed::EntryInitModes::DEFAULT_INIT, + uno::Sequence< beans::PropertyValue >(), + aObjectArgs ); + + aObjectInfo.Object = xResult; + + // TODO/LATER: in case of iconify object the icon should be stored in aObjectInfo + + OSL_ENSURE( aObjectInfo.Object.is(), "No object was created!" ); + if ( !aObjectInfo.Object.is() ) + throw uno::RuntimeException(); + + return aObjectInfo; +#else + throw lang::NoSupportException(); // TODO: +#endif +} + + +OUString SAL_CALL MSOLEDialogObjectCreator::getImplementationName() +{ + return "com.sun.star.comp.embed.MSOLEObjectSystemCreator"; +} + + +sal_Bool SAL_CALL MSOLEDialogObjectCreator::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + + +uno::Sequence< OUString > SAL_CALL MSOLEDialogObjectCreator::getSupportedServiceNames() +{ + return { "com.sun.star.embed.MSOLEObjectSystemCreator", + "com.sun.star.comp.embed.MSOLEObjectSystemCreator" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +embeddedobj_MSOLEDialogObjectCreator_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new MSOLEDialogObjectCreator(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/xdialogcreator.hxx b/embeddedobj/source/msole/xdialogcreator.hxx new file mode 100644 index 0000000000..a7c28df802 --- /dev/null +++ b/embeddedobj/source/msole/xdialogcreator.hxx @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/embed/XInsertObjectDialog.hpp> +#include <com/sun/star/embed/XEmbedObjectClipboardCreator.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <cppuhelper/implbase.hxx> +#include <osl/diagnose.h> + + +class MSOLEDialogObjectCreator : public ::cppu::WeakImplHelper< + css::embed::XInsertObjectDialog, + css::embed::XEmbedObjectClipboardCreator, + css::lang::XServiceInfo > +{ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + +public: + explicit MSOLEDialogObjectCreator( + const css::uno::Reference< css::uno::XComponentContext >& xContext ) + : m_xContext( xContext ) + { + OSL_ENSURE( xContext.is(), "No service manager is provided!" ); + } + + // XInsertObjectDialog + virtual css::embed::InsertedObjectInfo SAL_CALL createInstanceByDialog( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XEmbedObjectClipboardCreator + virtual css::embed::InsertedObjectInfo SAL_CALL createInstanceInitFromClipboard( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntryName, const css::uno::Sequence< css::beans::PropertyValue >& aObjectArgs ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/xolefactory.cxx b/embeddedobj/source/msole/xolefactory.cxx new file mode 100644 index 0000000000..735e1438c5 --- /dev/null +++ b/embeddedobj/source/msole/xolefactory.cxx @@ -0,0 +1,264 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/embed/EntryInitModes.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> + +#include "xolefactory.hxx" +#include <oleembobj.hxx> + +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> + +#include <officecfg/Office/Common.hxx> + +using namespace ::com::sun::star; + +// TODO: do not create OLE objects that represent OOo documents + + +uno::Reference< uno::XInterface > SAL_CALL OleEmbeddedObjectFactory::createInstanceInitFromEntry( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& aMedDescr, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + if ( officecfg::Office::Common::Security::Scripting::DisableActiveContent::get() ) + throw lang::NoSupportException("Active OLE content is disabled!"); + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + uno::Reference< container::XNameAccess > xNameAccess( xStorage, uno::UNO_QUERY_THROW ); + + // detect entry existence + if ( !xNameAccess->hasByName( sEntName ) ) + throw container::NoSuchElementException(); + + if ( !xStorage->isStreamElement( sEntName ) ) + { + // if it is not an OLE object throw an exception + throw io::IOException(); // TODO: + } + + uno::Reference< uno::XInterface > xResult( + static_cast< ::cppu::OWeakObject* > ( new OleEmbeddedObject( m_xContext, false ) ), + uno::UNO_QUERY ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, + sEntName, + embed::EntryInitModes::DEFAULT_INIT, + aMedDescr, + lObjArgs ); + + for ( beans::PropertyValue const & prop : lObjArgs ) + { + if ( prop.Name == "CloneFrom" ) + { + try + { + uno::Reference < embed::XEmbeddedObject > xObj; + uno::Reference < embed::XEmbeddedObject > xNew( xResult, uno::UNO_QUERY ); + prop.Value >>= xObj; + if ( xObj.is() ) + xNew->setVisualAreaSize( embed::Aspects::MSOLE_CONTENT, xObj->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ) ); + } + catch ( const uno::Exception& ) {} + break; + } + } + + return xResult; +} + + +uno::Reference< uno::XInterface > SAL_CALL OleEmbeddedObjectFactory::createInstanceInitFromMediaDescriptor( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& aMediaDescr, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + if ( officecfg::Office::Common::Security::Scripting::DisableActiveContent::get() ) + throw lang::NoSupportException("Active OLE content is disabled!"); + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + uno::Reference< uno::XInterface > xResult( + static_cast< ::cppu::OWeakObject* > ( new OleEmbeddedObject( m_xContext, false ) ), + uno::UNO_QUERY ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, + sEntName, + embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT, + aMediaDescr, + lObjArgs ); + + return xResult; +} + + +uno::Reference< uno::XInterface > SAL_CALL OleEmbeddedObjectFactory::createInstanceInitNew( + const uno::Sequence< sal_Int8 >& aClassID, + const OUString& aClassName, + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + if ( officecfg::Office::Common::Security::Scripting::DisableActiveContent::get() ) + throw lang::NoSupportException("Active OLE content is disabled!"); + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 3 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 4 ); + + uno::Reference< uno::XInterface > xResult( + static_cast< ::cppu::OWeakObject* > ( new OleEmbeddedObject( m_xContext, aClassID, aClassName ) ), + uno::UNO_QUERY ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, + sEntName, + embed::EntryInitModes::TRUNCATE_INIT, + uno::Sequence< beans::PropertyValue >(), + lObjArgs ); + + return xResult; +} + + +uno::Reference< uno::XInterface > SAL_CALL OleEmbeddedObjectFactory::createInstanceLink( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& aMediaDescr, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + if ( officecfg::Office::Common::Security::Scripting::DisableActiveContent::get() ) + throw lang::NoSupportException("Active OLE content is disabled!"); + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + uno::Reference< uno::XInterface > xResult( + static_cast< ::cppu::OWeakObject* > ( new OleEmbeddedObject( m_xContext, true ) ), + uno::UNO_QUERY ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, + sEntName, + embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT, + aMediaDescr, + lObjArgs ); + + return xResult; +} + + +uno::Reference< uno::XInterface > SAL_CALL OleEmbeddedObjectFactory::createInstanceUserInit( + const uno::Sequence< sal_Int8 >& aClassID, + const OUString& aClassName, + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + sal_Int32 /*nEntryConnectionMode*/, + const uno::Sequence< beans::PropertyValue >& /*lArguments*/, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + if (officecfg::Office::Common::Security::Scripting::DisableActiveContent::get()) + throw lang::NoSupportException("Active OLE content is disabled!"); + // the initialization is completely controlled by user + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + uno::Reference< uno::XInterface > xResult( + static_cast< ::cppu::OWeakObject* > ( new OleEmbeddedObject( m_xContext, aClassID, aClassName ) ), + uno::UNO_QUERY ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, + sEntName, + embed::EntryInitModes::DEFAULT_INIT, + uno::Sequence< beans::PropertyValue >(), + lObjArgs ); + + return xResult; +} + + +OUString SAL_CALL OleEmbeddedObjectFactory::getImplementationName() +{ + return "com.sun.star.comp.embed.OLEEmbeddedObjectFactory"; +} + +sal_Bool SAL_CALL OleEmbeddedObjectFactory::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + + +uno::Sequence< OUString > SAL_CALL OleEmbeddedObjectFactory::getSupportedServiceNames() +{ + return { "com.sun.star.embed.OLEEmbeddedObjectFactory", + "com.sun.star.comp.embed.OLEEmbeddedObjectFactory" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +embeddedobj_OleEmbeddedObjectFactory_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new OleEmbeddedObjectFactory(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/xolefactory.hxx b/embeddedobj/source/msole/xolefactory.hxx new file mode 100644 index 0000000000..56e632202a --- /dev/null +++ b/embeddedobj/source/msole/xolefactory.hxx @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/embed/XEmbeddedObjectCreator.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <cppuhelper/implbase.hxx> +#include <osl/diagnose.h> + + +class OleEmbeddedObjectFactory : public ::cppu::WeakImplHelper< + css::embed::XEmbeddedObjectCreator, + css::lang::XServiceInfo > +{ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + +public: + explicit OleEmbeddedObjectFactory( + const css::uno::Reference< css::uno::XComponentContext >& xContext ) + : m_xContext( xContext ) + { + OSL_ENSURE( xContext.is(), "No service manager is provided!" ); + } + + // XEmbedObjectCreator + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceInitNew( const css::uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName, const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceInitFromEntry( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& aMedDescr, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceInitFromMediaDescriptor( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& aMediaDescr, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XEmbedObjectFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceUserInit( const css::uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName, const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, sal_Int32 nEntryConnectionMode, const css::uno::Sequence< css::beans::PropertyValue >& lArguments, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XLinkCreator + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceLink( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& aMediaDescr, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |