Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/comphelper/source/container/embeddedobjectcontainer.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <com/sun/star/container/XChild.hpp>
21
#include <com/sun/star/container/XNameAccess.hpp>
22
#include <com/sun/star/embed/EmbeddedObjectCreator.hpp>
23
#include <com/sun/star/embed/WrongStateException.hpp>
24
#include <com/sun/star/embed/XEmbeddedObject.hpp>
25
#include <com/sun/star/embed/XEmbedPersist.hpp>
26
#include <com/sun/star/embed/XLinkageSupport.hpp>
27
#include <com/sun/star/embed/XTransactedObject.hpp>
28
#include <com/sun/star/embed/XOptimizedStorage.hpp>
29
#include <com/sun/star/embed/EntryInitModes.hpp>
30
#include <com/sun/star/io/IOException.hpp>
31
#include <com/sun/star/util/XModifiable.hpp>
32
#include <com/sun/star/embed/EmbedStates.hpp>
33
#include <com/sun/star/beans/XPropertySetInfo.hpp>
34
#include <com/sun/star/beans/XPropertySet.hpp>
35
#include <com/sun/star/embed/Aspects.hpp>
36
#include <com/sun/star/embed/EmbedMisc.hpp>
37
38
#include <comphelper/classids.hxx>
39
#include <comphelper/mimeconfighelper.hxx>
40
#include <comphelper/seqstream.hxx>
41
#include <comphelper/processfactory.hxx>
42
#include <comphelper/storagehelper.hxx>
43
#include <comphelper/embeddedobjectcontainer.hxx>
44
#include <comphelper/sequence.hxx>
45
#include <comphelper/propertysequence.hxx>
46
#include <comphelper/propertyvalue.hxx>
47
#include <cppuhelper/weakref.hxx>
48
#include <sal/log.hxx>
49
#include <rtl/ref.hxx>
50
51
#include <officecfg/Office/Common.hxx>
52
53
#include <algorithm>
54
#include <unordered_map>
55
56
57
using namespace ::com::sun::star;
58
59
namespace comphelper {
60
61
typedef std::unordered_map<OUString, uno::Reference<embed::XEmbeddedObject>> EmbeddedObjectContainerNameMap;
62
struct EmbedImpl
63
{
64
    // TODO/LATER: remove objects from temp. Container storage when object is disposed
65
    EmbeddedObjectContainerNameMap maNameToObjectMap;
66
    // to speed up lookup by Reference
67
    std::unordered_map<uno::Reference<embed::XEmbeddedObject>, OUString> maObjectToNameMap;
68
    uno::Reference < embed::XStorage > mxStorage;
69
    std::unique_ptr<EmbeddedObjectContainer> mpTempObjectContainer;
70
    uno::Reference < embed::XStorage > mxImageStorage;
71
    uno::WeakReference < uno::XInterface > m_xModel;
72
73
    bool mbOwnsStorage : 1;
74
    bool mbUserAllowsLinkUpdate : 1;
75
76
    const uno::Reference < embed::XStorage >& GetReplacements();
77
};
78
79
const uno::Reference < embed::XStorage >& EmbedImpl::GetReplacements()
80
0
{
81
0
    if ( !mxImageStorage.is() )
82
0
    {
83
0
        try
84
0
        {
85
0
            mxImageStorage = mxStorage->openStorageElement(
86
0
                u"ObjectReplacements"_ustr, embed::ElementModes::READWRITE );
87
0
        }
88
0
        catch (const uno::Exception&)
89
0
        {
90
0
            mxImageStorage = mxStorage->openStorageElement(
91
0
                u"ObjectReplacements"_ustr, embed::ElementModes::READ );
92
0
        }
93
0
    }
94
95
0
    if ( !mxImageStorage.is() )
96
0
        throw io::IOException(u"No ObjectReplacements"_ustr);
97
98
0
    return mxImageStorage;
99
0
}
100
101
EmbeddedObjectContainer::EmbeddedObjectContainer()
102
11.6k
    :     pImpl(new EmbedImpl)
103
11.6k
{
104
11.6k
    pImpl->mxStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
105
11.6k
    pImpl->mbOwnsStorage = true;
106
11.6k
    pImpl->mbUserAllowsLinkUpdate = true;
107
11.6k
    pImpl->mpTempObjectContainer = nullptr;
108
11.6k
}
109
110
EmbeddedObjectContainer::EmbeddedObjectContainer( const uno::Reference < embed::XStorage >& rStor )
111
93
    :     pImpl(new EmbedImpl)
112
93
{
113
93
    pImpl->mxStorage = rStor;
114
93
    pImpl->mbOwnsStorage = false;
115
93
    pImpl->mbUserAllowsLinkUpdate = true;
116
93
    pImpl->mpTempObjectContainer = nullptr;
117
93
}
118
119
EmbeddedObjectContainer::EmbeddedObjectContainer( const uno::Reference < embed::XStorage >& rStor, const uno::Reference < uno::XInterface >& xModel )
120
32.2k
    :     pImpl(new EmbedImpl)
121
32.2k
{
122
32.2k
    pImpl->mxStorage = rStor;
123
32.2k
    pImpl->mbOwnsStorage = false;
124
32.2k
    pImpl->mbUserAllowsLinkUpdate = true;
125
32.2k
    pImpl->mpTempObjectContainer = nullptr;
126
32.2k
    pImpl->m_xModel = xModel;
127
32.2k
}
128
129
void EmbeddedObjectContainer::SwitchPersistence( const uno::Reference < embed::XStorage >& rStor )
130
0
{
131
0
    ReleaseImageSubStorage();
132
133
0
    if ( pImpl->mbOwnsStorage )
134
0
        pImpl->mxStorage->dispose();
135
136
0
    pImpl->mxStorage = rStor;
137
0
    pImpl->mbOwnsStorage = false;
138
0
}
139
140
bool EmbeddedObjectContainer::CommitImageSubStorage()
141
43.9k
{
142
43.9k
    if ( !pImpl->mxImageStorage )
143
43.9k
        return true;
144
145
0
    try
146
0
    {
147
0
        bool bReadOnlyMode = true;
148
0
        uno::Reference < beans::XPropertySet > xSet(pImpl->mxImageStorage,uno::UNO_QUERY);
149
0
        if ( xSet.is() )
150
0
        {
151
            // get the open mode from the parent storage
152
0
            sal_Int32 nMode = 0;
153
0
            uno::Any aAny = xSet->getPropertyValue(u"OpenMode"_ustr);
154
0
            if ( aAny >>= nMode )
155
0
                bReadOnlyMode = !(nMode & embed::ElementModes::WRITE );
156
0
        } // if ( xSet.is() )
157
0
        if ( !bReadOnlyMode )
158
0
        {
159
0
            uno::Reference< embed::XTransactedObject > xTransact( pImpl->mxImageStorage, uno::UNO_QUERY );
160
0
            if (xTransact)
161
0
                xTransact->commit();
162
0
        }
163
0
    }
164
0
    catch (const uno::Exception&)
165
0
    {
166
0
        return false;
167
0
    }
168
169
0
    return true;
170
0
}
171
172
void EmbeddedObjectContainer::ReleaseImageSubStorage()
173
43.9k
{
174
43.9k
    CommitImageSubStorage();
175
176
43.9k
    if ( pImpl->mxImageStorage.is() )
177
0
    {
178
0
        try
179
0
        {
180
0
            pImpl->mxImageStorage->dispose();
181
0
            pImpl->mxImageStorage.clear();
182
0
        }
183
0
        catch (const uno::Exception&)
184
0
        {
185
0
            SAL_WARN( "comphelper.container", "Problems releasing image substorage!" );
186
0
        }
187
0
    }
188
43.9k
}
189
190
EmbeddedObjectContainer::~EmbeddedObjectContainer()
191
43.9k
{
192
43.9k
    ReleaseImageSubStorage();
193
194
43.9k
    if ( pImpl->mbOwnsStorage )
195
11.6k
        pImpl->mxStorage->dispose();
196
43.9k
}
197
198
void EmbeddedObjectContainer::CloseEmbeddedObjects()
199
32.1k
{
200
32.1k
    for( const auto& rObj : pImpl->maNameToObjectMap )
201
10.8k
    {
202
10.8k
        uno::Reference < embed::XEmbeddedObject > const & xClose = rObj.second;
203
10.8k
        if( xClose.is() )
204
0
        {
205
0
            try
206
0
            {
207
0
                xClose->close( true );
208
0
            }
209
0
            catch (const uno::Exception&)
210
0
            {
211
0
            }
212
0
        }
213
10.8k
    }
214
32.1k
}
215
216
OUString EmbeddedObjectContainer::CreateUniqueObjectName()
217
37.7k
{
218
37.7k
    OUString aStr;
219
37.7k
    sal_Int32 i=1;
220
37.7k
    do
221
1.41M
    {
222
1.41M
        aStr = "Object " + OUString::number( i++ );
223
1.41M
    }
224
1.41M
    while( HasEmbeddedObject( aStr ) );
225
    // TODO/LATER: should we consider deleted objects?
226
227
37.7k
    return aStr;
228
37.7k
}
229
230
uno::Sequence < OUString > EmbeddedObjectContainer::GetObjectNames() const
231
594
{
232
594
    return comphelper::mapKeysToSequence(pImpl->maNameToObjectMap);
233
594
}
234
235
bool EmbeddedObjectContainer::HasEmbeddedObjects() const
236
0
{
237
0
    return !pImpl->maNameToObjectMap.empty();
238
0
}
239
240
bool EmbeddedObjectContainer::HasEmbeddedObject( const OUString& rName )
241
1.41M
{
242
1.41M
    if (pImpl->maNameToObjectMap.contains(rName))
243
1.38M
        return true;
244
37.7k
    if (!pImpl->mxStorage.is())
245
1.26k
        return false;
246
36.5k
    return pImpl->mxStorage->hasByName(rName);
247
37.7k
}
248
249
bool EmbeddedObjectContainer::HasEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj ) const
250
0
{
251
0
    return pImpl->maObjectToNameMap.contains(xObj);
252
0
}
253
254
bool EmbeddedObjectContainer::HasInstantiatedEmbeddedObject( const OUString& rName )
255
197
{
256
    // allows to detect whether the object was already instantiated
257
    // currently the filter instantiate it on loading, so this method allows
258
    // to avoid objects pointing to the same persistence
259
197
    return pImpl->maNameToObjectMap.contains(rName);
260
197
}
261
262
OUString EmbeddedObjectContainer::GetEmbeddedObjectName( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj ) const
263
0
{
264
0
    auto it = pImpl->maObjectToNameMap.find(xObj);
265
0
    if (it == pImpl->maObjectToNameMap.end())
266
0
    {
267
0
        SAL_WARN( "comphelper.container", "Unknown object!" );
268
0
        return OUString();
269
0
    }
270
0
    return it->second;
271
0
}
272
273
uno::Reference< embed::XEmbeddedObject>
274
EmbeddedObjectContainer::GetEmbeddedObject(
275
        const OUString& rName, OUString const*const pBaseURL)
276
15.6k
{
277
15.6k
    SAL_WARN_IF( rName.isEmpty(), "comphelper.container", "Empty object name!");
278
279
15.6k
    uno::Reference < embed::XEmbeddedObject > xObj;
280
15.6k
    auto aIt = pImpl->maNameToObjectMap.find( rName );
281
282
#if OSL_DEBUG_LEVEL > 1
283
    uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
284
    uno::Sequence< OUString> aSeq = xAccess->getElementNames();
285
    const OUString* pIter = aSeq.getConstArray();
286
    const OUString* pEnd   = pIter + aSeq.getLength();
287
    for(;pIter != pEnd;++pIter)
288
    {
289
        (void)*pIter;
290
    }
291
    OSL_ENSURE( aIt != pImpl->maNameToObjectMap.end() || xAccess->hasByName(rName), "Could not return object!" );
292
#endif
293
294
    // check if object was already created
295
15.6k
    if ( aIt != pImpl->maNameToObjectMap.end() )
296
12.8k
        xObj = (*aIt).second;
297
2.84k
    else
298
2.84k
        xObj = Get_Impl(rName, uno::Reference<embed::XEmbeddedObject>(), pBaseURL);
299
300
15.6k
    return xObj;
301
15.6k
}
302
303
uno::Reference<embed::XEmbeddedObject> EmbeddedObjectContainer::Get_Impl(
304
        const OUString& rName,
305
        const uno::Reference<embed::XEmbeddedObject>& xCopy,
306
        OUString const*const pBaseURL)
307
2.84k
{
308
2.84k
    uno::Reference < embed::XEmbeddedObject > xObj;
309
2.84k
    try
310
2.84k
    {
311
        // create the object from the storage
312
2.84k
        uno::Reference < beans::XPropertySet > xSet( pImpl->mxStorage, uno::UNO_QUERY );
313
2.84k
        bool bReadOnlyMode = true;
314
2.84k
        if ( xSet.is() )
315
1.58k
        {
316
            // get the open mode from the parent storage
317
1.58k
            sal_Int32 nMode = 0;
318
1.58k
            uno::Any aAny = xSet->getPropertyValue(u"OpenMode"_ustr);
319
1.58k
            if ( aAny >>= nMode )
320
1.58k
                bReadOnlyMode = !(nMode & embed::ElementModes::WRITE );
321
1.58k
        }
322
323
        // object was not added until now - should happen only by calling this method from "inside"
324
        //TODO/LATER: it would be good to detect an error when an object should be created already, but isn't (not an "inside" call)
325
2.84k
        uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
326
2.84k
        uno::Sequence< beans::PropertyValue > aObjDescr(1 + (xCopy.is() ? 1 : 0) + (pBaseURL ? 1 : 0));
327
2.84k
        auto itObjDescr = aObjDescr.getArray();
328
2.84k
        itObjDescr->Name = "Parent";
329
2.84k
        itObjDescr->Value <<= pImpl->m_xModel.get();
330
2.84k
        if (pBaseURL)
331
1.44k
        {
332
1.44k
            ++itObjDescr;
333
1.44k
            itObjDescr->Name = "DefaultParentBaseURL";
334
1.44k
            itObjDescr->Value <<= *pBaseURL;
335
1.44k
        }
336
2.84k
        if ( xCopy.is() )
337
0
        {
338
0
            ++itObjDescr;
339
0
            itObjDescr->Name = "CloneFrom";
340
0
            itObjDescr->Value <<= xCopy;
341
0
        }
342
343
2.84k
        uno::Sequence< beans::PropertyValue > aMediaDescr{ comphelper::makePropertyValue(
344
2.84k
            u"ReadOnly"_ustr, bReadOnlyMode) };
345
2.84k
        xObj.set( xFactory->createInstanceInitFromEntry(
346
2.84k
                pImpl->mxStorage, rName,
347
2.84k
                aMediaDescr, aObjDescr ), uno::UNO_QUERY );
348
349
        // insert object into my list
350
2.84k
        AddEmbeddedObject( xObj, rName );
351
2.84k
    }
352
2.84k
    catch (uno::Exception const& e)
353
2.84k
    {
354
2.84k
        SAL_WARN("comphelper.container", "EmbeddedObjectContainer::Get_Impl: exception caught: " << e);
355
2.84k
    }
356
357
2.84k
    return xObj;
358
2.84k
}
359
360
uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CreateEmbeddedObject(
361
            const uno::Sequence < sal_Int8 >& rClassId,
362
            OUString& rNewName,
363
            std::optional<OUString> oDefaultParentBaseURL )
364
25.2k
{
365
25.2k
    if ( rNewName.isEmpty() )
366
25.2k
        rNewName = CreateUniqueObjectName();
367
368
25.2k
    SAL_WARN_IF( HasEmbeddedObject(rNewName), "comphelper.container", "Object to create already exists!");
369
370
    // create object from classid by inserting it into storage
371
25.2k
    uno::Reference < embed::XEmbeddedObject > xObj;
372
25.2k
    try
373
25.2k
    {
374
25.2k
        uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
375
376
25.2k
        const size_t nArgs = oDefaultParentBaseURL.has_value() ? 2 : 1;
377
25.2k
        uno::Sequence< beans::PropertyValue > aObjDescr( nArgs );
378
25.2k
        auto pObjDescr = aObjDescr.getArray();
379
25.2k
        pObjDescr[0].Name = "Parent";
380
25.2k
        pObjDescr[0].Value <<= pImpl->m_xModel.get();
381
25.2k
        if (oDefaultParentBaseURL.has_value())
382
1.26k
        {
383
1.26k
            pObjDescr[1].Name = "DefaultParentBaseURL";
384
1.26k
            pObjDescr[1].Value <<= *oDefaultParentBaseURL;
385
1.26k
        }
386
25.2k
        xObj.set( xFactory->createInstanceInitNew(
387
25.2k
                    rClassId, OUString(), pImpl->mxStorage, rNewName,
388
25.2k
                    aObjDescr ), uno::UNO_QUERY );
389
390
25.2k
        AddEmbeddedObject( xObj, rNewName );
391
392
25.2k
        OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
393
25.2k
                    "A freshly create object should be running always!" );
394
25.2k
    }
395
25.2k
    catch (uno::Exception const& e)
396
25.2k
    {
397
25.2k
        SAL_WARN("comphelper.container", "EmbeddedObjectContainer::CreateEmbeddedObject: exception caught: " << e);
398
25.2k
    }
399
400
25.2k
    return xObj;
401
25.2k
}
402
403
void EmbeddedObjectContainer::AddEmbeddedObject(
404
            const css::uno::Reference < css::embed::XEmbeddedObject >& xObj, const OUString& rName )
405
10.8k
{
406
#if OSL_DEBUG_LEVEL > 1
407
    SAL_WARN_IF( rName.isEmpty(), "comphelper.container", "Added object doesn't have a name!");
408
    uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
409
    uno::Reference < embed::XEmbedPersist > xEmb( xObj, uno::UNO_QUERY );
410
    uno::Reference < embed::XLinkageSupport > xLink( xEmb, uno::UNO_QUERY );
411
    // if the object has a persistence and the object is not a link than it must have persistence entry in the storage
412
    OSL_ENSURE( !( xEmb.is() && ( !xLink.is() || !xLink->isLink() ) ) || xAccess->hasByName(rName),
413
                    "Added element not in storage!" );
414
#endif
415
416
    // remember object - it needs to be in storage already
417
10.8k
    auto aIt = pImpl->maNameToObjectMap.find( rName );
418
10.8k
    OSL_ENSURE( aIt == pImpl->maNameToObjectMap.end(), "Element already inserted!" );
419
10.8k
    pImpl->maNameToObjectMap[ rName ] = xObj;
420
10.8k
    pImpl->maObjectToNameMap[ xObj ] = rName;
421
10.8k
    uno::Reference < container::XChild > xChild( xObj, uno::UNO_QUERY );
422
10.8k
    if ( xChild.is() && xChild->getParent() != pImpl->m_xModel.get() )
423
0
        xChild->setParent( pImpl->m_xModel.get() );
424
425
    // look for object in temporary container
426
10.8k
    if ( !pImpl->mpTempObjectContainer )
427
10.8k
        return;
428
429
0
    auto& rObjectContainer = pImpl->mpTempObjectContainer->pImpl->maNameToObjectMap;
430
0
    auto aIter = std::find_if(rObjectContainer.begin(), rObjectContainer.end(),
431
0
        [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
432
0
    if (aIter == rObjectContainer.end())
433
0
        return;
434
435
    // copy replacement image from temporary container (if there is any)
436
0
    OUString aTempName = aIter->first;
437
0
    OUString aMediaType;
438
0
    uno::Reference < io::XInputStream > xStream = pImpl->mpTempObjectContainer->GetGraphicStream( xObj, &aMediaType );
439
0
    if ( xStream.is() )
440
0
    {
441
0
        InsertGraphicStream( xStream, rName, aMediaType );
442
0
        xStream = nullptr;
443
0
        pImpl->mpTempObjectContainer->RemoveGraphicStream( aTempName );
444
0
    }
445
446
    // remove object from storage of temporary container
447
0
    uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
448
0
    if ( xPersist.is() )
449
0
    {
450
0
        try
451
0
        {
452
0
            pImpl->mpTempObjectContainer->pImpl->mxStorage->removeElement( aTempName );
453
0
        }
454
0
        catch (const uno::Exception&)
455
0
        {
456
0
        }
457
0
    }
458
459
    // temp. container needs to forget the object
460
0
    pImpl->mpTempObjectContainer->pImpl->maObjectToNameMap.erase( aIter->second );
461
0
    pImpl->mpTempObjectContainer->pImpl->maNameToObjectMap.erase( aIter );
462
0
}
463
464
bool EmbeddedObjectContainer::StoreEmbeddedObject(
465
    const uno::Reference < embed::XEmbeddedObject >& xObj, OUString& rName, bool bCopy,
466
    const OUString& rSrcShellID, const OUString& rDestShellID )
467
10.8k
{
468
10.8k
    uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
469
10.8k
    if ( rName.isEmpty() )
470
10.8k
        rName = CreateUniqueObjectName();
471
472
#if OSL_DEBUG_LEVEL > 1
473
    uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
474
    OSL_ENSURE( !xPersist.is() || !xAccess->hasByName(rName), "Inserting element already present in storage!" );
475
    OSL_ENSURE( xPersist.is() || xObj->getCurrentState() == embed::EmbedStates::RUNNING, "Non persistent object inserted!");
476
#endif
477
478
    // insert objects' storage into the container storage (if object has one)
479
10.8k
    try
480
10.8k
    {
481
10.8k
        if ( xPersist.is() )
482
0
        {
483
0
            uno::Sequence < beans::PropertyValue > aSeq;
484
0
            auto aObjArgs(::comphelper::InitPropertySequence({
485
0
                { "SourceShellID", uno::Any(rSrcShellID) },
486
0
                { "DestinationShellID", uno::Any(rDestShellID) }
487
0
            }));
488
0
            if ( bCopy )
489
0
            {
490
0
                xPersist->storeToEntry(pImpl->mxStorage, rName, aSeq, aObjArgs);
491
0
            }
492
0
            else
493
0
            {
494
                //TODO/LATER: possible optimization, don't store immediately
495
                //xPersist->setPersistentEntry( pImpl->mxStorage, rName, embed::EntryInitModes::ENTRY_NO_INIT, aSeq, aSeq );
496
0
                xPersist->storeAsEntry( pImpl->mxStorage, rName, aSeq, aObjArgs );
497
0
                xPersist->saveCompleted( true );
498
0
            }
499
0
        }
500
10.8k
    }
501
10.8k
    catch (uno::Exception const& e)
502
10.8k
    {
503
0
        SAL_WARN("comphelper.container", "EmbeddedObjectContainer::StoreEmbeddedObject: exception caught: " << e);
504
        // TODO/LATER: better error recovery should keep storage intact
505
0
        return false;
506
0
    }
507
508
10.8k
    return true;
509
10.8k
}
510
bool EmbeddedObjectContainer::InsertEmbeddedObject(
511
        const uno::Reference < embed::XEmbeddedObject >& xObj,
512
        OUString& rName,
513
        OUString const* pTargetShellID )
514
10.8k
{
515
    // store it into the container storage
516
10.8k
    OUString sTargetShellID;
517
10.8k
    if (pTargetShellID)
518
10.8k
        sTargetShellID = *pTargetShellID;
519
10.8k
    if (StoreEmbeddedObject(xObj, rName, false, OUString(), sTargetShellID))
520
10.8k
    {
521
        // remember object
522
10.8k
        AddEmbeddedObject( xObj, rName );
523
10.8k
        return true;
524
10.8k
    }
525
0
    else
526
0
        return false;
527
10.8k
}
528
529
uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedObject(
530
            const uno::Reference < io::XInputStream >& xStm, OUString& rNewName )
531
0
{
532
0
    if ( rNewName.isEmpty() )
533
0
        rNewName = CreateUniqueObjectName();
534
535
    // store it into the container storage
536
0
    bool bIsStorage = false;
537
0
    try
538
0
    {
539
        // first try storage persistence
540
0
        uno::Reference < embed::XStorage > xStore = ::comphelper::OStorageHelper::GetStorageFromInputStream( xStm );
541
542
        // storage was created from stream successfully
543
0
        bIsStorage = true;
544
545
0
        uno::Reference < embed::XStorage > xNewStore = pImpl->mxStorage->openStorageElement( rNewName, embed::ElementModes::READWRITE );
546
0
        xStore->copyToStorage( xNewStore );
547
0
    }
548
0
    catch (const uno::Exception&)
549
0
    {
550
0
        if ( bIsStorage )
551
            // it is storage persistence, but opening of new substorage or copying to it failed
552
0
            return uno::Reference < embed::XEmbeddedObject >();
553
554
        // stream didn't contain a storage, now try stream persistence
555
0
        try
556
0
        {
557
0
            uno::Reference < io::XStream > xNewStream = pImpl->mxStorage->openStreamElement( rNewName, embed::ElementModes::READWRITE );
558
0
            ::comphelper::OStorageHelper::CopyInputToOutput( xStm, xNewStream->getOutputStream() );
559
560
            // No mediatype is provided so the default for OLE objects value is used
561
            // it is correct so for now, but what if somebody introduces a new stream based embedded object?
562
            // Probably introducing of such an object must be restricted ( a storage must be used! ).
563
0
            uno::Reference< beans::XPropertySet > xProps( xNewStream, uno::UNO_QUERY_THROW );
564
0
            xProps->setPropertyValue(u"MediaType"_ustr,
565
0
                    uno::Any( u"application/vnd.sun.star.oleobject"_ustr ) );
566
0
        }
567
0
        catch (uno::Exception const& e)
568
0
        {
569
            // complete disaster!
570
0
            SAL_WARN("comphelper.container", "EmbeddedObjectContainer::InsertEmbeddedObject: exception caught: " << e);
571
0
            return uno::Reference < embed::XEmbeddedObject >();
572
0
        }
573
0
    }
574
575
    // stream was copied into the container storage in either way, now try to open something form it
576
0
    uno::Reference < embed::XEmbeddedObject > xRet = GetEmbeddedObject( rNewName );
577
0
    try
578
0
    {
579
0
        if ( !xRet.is() )
580
            // no object could be created, so withdraw insertion
581
0
            pImpl->mxStorage->removeElement( rNewName );
582
0
    }
583
0
    catch (const uno::Exception&)
584
0
    {
585
0
    }
586
587
0
    return xRet;
588
0
}
589
590
uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedObject(
591
        const css::uno::Sequence < css::beans::PropertyValue >& aMedium,
592
        OUString& rNewName, OUString const* pBaseURL )
593
144
{
594
144
    if ( rNewName.isEmpty() )
595
0
        rNewName = CreateUniqueObjectName();
596
597
144
    uno::Reference < embed::XEmbeddedObject > xObj;
598
144
    try
599
144
    {
600
144
        uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
601
144
        uno::Sequence< beans::PropertyValue > aObjDescr(pBaseURL ? 2 : 1);
602
144
        auto pObjDescr = aObjDescr.getArray();
603
144
        pObjDescr[0].Name = "Parent";
604
144
        pObjDescr[0].Value <<= pImpl->m_xModel.get();
605
144
        if (pBaseURL)
606
0
        {
607
0
            pObjDescr[1].Name = "DefaultParentBaseURL";
608
0
            pObjDescr[1].Value <<= *pBaseURL;
609
0
        }
610
144
        xObj.set( xFactory->createInstanceInitFromMediaDescriptor(
611
144
                pImpl->mxStorage, rNewName, aMedium, aObjDescr ), uno::UNO_QUERY );
612
144
        uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
613
614
144
        OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
615
144
                    "A freshly create object should be running always!" );
616
617
        // possible optimization: store later!
618
144
        if ( xPersist.is())
619
0
            xPersist->storeOwn();
620
621
144
        AddEmbeddedObject( xObj, rNewName );
622
144
    }
623
144
    catch (const uno::Exception&)
624
144
    {
625
144
    }
626
627
144
    return xObj;
628
144
}
629
630
uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedLink(
631
        const css::uno::Sequence < css::beans::PropertyValue >& aMedium,
632
        OUString& rNewName )
633
0
{
634
0
    if ( rNewName.isEmpty() )
635
0
        rNewName = CreateUniqueObjectName();
636
637
0
    uno::Reference < embed::XEmbeddedObject > xObj;
638
0
    try
639
0
    {
640
0
        uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create(::comphelper::getProcessComponentContext());
641
0
        uno::Sequence< beans::PropertyValue > aObjDescr{ comphelper::makePropertyValue(
642
0
            u"Parent"_ustr, pImpl->m_xModel.get()) };
643
0
        xObj.set( xFactory->createInstanceLink( pImpl->mxStorage, rNewName, aMedium, aObjDescr ), uno::UNO_QUERY );
644
645
0
        uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
646
647
0
        OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
648
0
                    "A freshly create object should be running always!" );
649
650
        // possible optimization: store later!
651
0
        if ( xPersist.is())
652
0
            xPersist->storeOwn();
653
654
0
        AddEmbeddedObject( xObj, rNewName );
655
0
    }
656
0
    catch (uno::Exception const& e)
657
0
    {
658
0
        SAL_WARN("comphelper.container", "EmbeddedObjectContainer::InsertEmbeddedLink: "
659
0
                "exception caught: " << e);
660
0
    }
661
662
0
    return xObj;
663
0
}
664
665
bool EmbeddedObjectContainer::TryToCopyGraphReplacement( EmbeddedObjectContainer& rSrc,
666
                                                            const OUString& aOrigName,
667
                                                            const OUString& aTargetName )
668
0
{
669
0
    bool bResult = false;
670
671
0
    if ( ( &rSrc != this || aOrigName != aTargetName ) && !aOrigName.isEmpty() && !aTargetName.isEmpty() )
672
0
    {
673
0
        OUString aMediaType;
674
0
        uno::Reference < io::XInputStream > xGrStream = rSrc.GetGraphicStream( aOrigName, &aMediaType );
675
0
        if ( xGrStream.is() )
676
0
            bResult = InsertGraphicStream( xGrStream, aTargetName, aMediaType );
677
0
    }
678
679
0
    return bResult;
680
0
}
681
682
uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CopyAndGetEmbeddedObject(
683
    EmbeddedObjectContainer& rSrc, const uno::Reference <embed::XEmbeddedObject>& xObj, OUString& rName,
684
    const OUString& rSrcShellID, const OUString& rDestShellID )
685
363
{
686
363
    uno::Reference< embed::XEmbeddedObject > xResult;
687
688
    // TODO/LATER: For now only objects that implement XEmbedPersist have a replacement image, it might change in future
689
    // do an incompatible change so that object name is provided in all the move and copy methods
690
363
    OUString aOrigName;
691
363
    try
692
363
    {
693
363
        uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
694
363
        if (xPersist)
695
0
            aOrigName = xPersist->getEntryName();
696
363
    }
697
363
    catch (const uno::Exception&)
698
363
    {
699
0
    }
700
701
363
    if ( rName.isEmpty() )
702
363
        rName = CreateUniqueObjectName();
703
704
    // objects without persistence are not really stored by the method
705
363
    if (xObj.is() && StoreEmbeddedObject(xObj, rName, true, rSrcShellID, rDestShellID))
706
0
    {
707
0
        SAL_INFO_IF(rDestShellID.isEmpty(), "comphelper.container",
708
0
            "SfxObjectShell with no base URL?"); // every shell has a base URL, except the clipboard SwDocShell
709
0
        xResult = Get_Impl(rName, xObj, &rDestShellID);
710
0
        if ( !xResult.is() )
711
0
        {
712
            // this is a case when object has no real persistence
713
            // in such cases a new object should be explicitly created and initialized with the data of the old one
714
0
            try
715
0
            {
716
0
                uno::Reference< embed::XLinkageSupport > xOrigLinkage( xObj, uno::UNO_QUERY );
717
0
                if ( xOrigLinkage.is() && xOrigLinkage->isLink() )
718
0
                {
719
                    // this is an OOo link, it has no persistence
720
0
                    OUString aURL = xOrigLinkage->getLinkURL();
721
0
                    if ( aURL.isEmpty() )
722
0
                        throw uno::RuntimeException(u"URL of the linked object is empty"_ustr);
723
724
                    // create new linked object from the URL the link is based on
725
0
                    uno::Reference < embed::XEmbeddedObjectCreator > xCreator =
726
0
                        embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
727
728
0
                    uno::Sequence< beans::PropertyValue > aMediaDescr{ comphelper::makePropertyValue(
729
0
                        u"URL"_ustr, aURL) };
730
0
                    uno::Sequence< beans::PropertyValue > aObjDescr{ comphelper::makePropertyValue(
731
0
                        u"Parent"_ustr, pImpl->m_xModel.get()) };
732
0
                    xResult.set(xCreator->createInstanceLink(
733
0
                                    pImpl->mxStorage,
734
0
                                    rName,
735
0
                                    aMediaDescr,
736
0
                                    aObjDescr ),
737
0
                                uno::UNO_QUERY_THROW );
738
0
                }
739
0
                else
740
0
                {
741
                    // the component is required for copying of this object
742
0
                    if ( xObj->getCurrentState() == embed::EmbedStates::LOADED )
743
0
                        xObj->changeState( embed::EmbedStates::RUNNING );
744
745
                    // this must be an object based on properties, otherwise we can not copy it currently
746
0
                    uno::Reference< beans::XPropertySet > xOrigProps( xObj->getComponent(), uno::UNO_QUERY_THROW );
747
748
                    // use object class ID to create a new one and transfer all the properties
749
0
                    uno::Reference < embed::XEmbeddedObjectCreator > xCreator =
750
0
                        embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
751
752
0
                    uno::Sequence< beans::PropertyValue > aObjDescr{ comphelper::makePropertyValue(
753
0
                        u"Parent"_ustr, pImpl->m_xModel.get()) };
754
0
                    xResult.set(xCreator->createInstanceInitNew(
755
0
                                    xObj->getClassID(),
756
0
                                    xObj->getClassName(),
757
0
                                    pImpl->mxStorage,
758
0
                                    rName,
759
0
                                    aObjDescr ),
760
0
                                uno::UNO_QUERY_THROW );
761
762
0
                    if ( xResult->getCurrentState() == embed::EmbedStates::LOADED )
763
0
                        xResult->changeState( embed::EmbedStates::RUNNING );
764
765
0
                    uno::Reference< beans::XPropertySet > xTargetProps( xResult->getComponent(), uno::UNO_QUERY_THROW );
766
767
                    // copy all the properties from xOrigProps to xTargetProps
768
0
                    uno::Reference< beans::XPropertySetInfo > xOrigInfo = xOrigProps->getPropertySetInfo();
769
0
                    if ( !xOrigInfo.is() )
770
0
                        throw uno::RuntimeException(u"Object has no properties"_ustr);
771
772
0
                    const uno::Sequence< beans::Property > aPropertiesList = xOrigInfo->getProperties();
773
0
                    for ( const auto & p : aPropertiesList )
774
0
                    {
775
0
                        try
776
0
                        {
777
0
                            xTargetProps->setPropertyValue(
778
0
                                p.Name,
779
0
                                xOrigProps->getPropertyValue( p.Name ) );
780
0
                        }
781
0
                        catch (const beans::PropertyVetoException&)
782
0
                        {
783
                            // impossibility to copy readonly property is not treated as an error for now
784
                            // but the assertion is helpful to detect such scenarios and review them
785
0
                            SAL_WARN( "comphelper.container", "Could not copy readonly property!" );
786
0
                        }
787
0
                    }
788
0
                }
789
790
0
                if ( xResult.is() )
791
0
                    AddEmbeddedObject( xResult, rName );
792
0
            }
793
0
            catch (const uno::Exception&)
794
0
            {
795
0
                if ( xResult.is() )
796
0
                {
797
0
                    try
798
0
                    {
799
0
                        xResult->close( true );
800
0
                    }
801
0
                    catch (const uno::Exception&)
802
0
                    {
803
0
                    }
804
0
                    xResult.clear();
805
0
                }
806
0
            }
807
0
        }
808
0
    }
809
810
363
    SAL_WARN_IF( !xResult.is(), "comphelper.container", "Can not copy embedded object that has no persistence!" );
811
812
363
    if ( xResult.is() )
813
0
    {
814
        // the object is successfully copied, try to copy graphical replacement
815
0
        if ( !aOrigName.isEmpty() )
816
0
            TryToCopyGraphReplacement( rSrc, aOrigName, rName );
817
818
        // the object might need the size to be set
819
0
        try
820
0
        {
821
0
            if ( xResult->getStatus( embed::Aspects::MSOLE_CONTENT ) & embed::EmbedMisc::EMBED_NEEDSSIZEONLOAD )
822
0
                xResult->setVisualAreaSize( embed::Aspects::MSOLE_CONTENT,
823
0
                                            xObj->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ) );
824
0
        }
825
0
        catch (const uno::Exception&)
826
0
        {
827
0
        }
828
0
    }
829
830
363
    return xResult;
831
363
}
832
833
// #i119941, bKeepToTempStorage: use to specify whether store the removed object to temporary storage+
834
void EmbeddedObjectContainer::RemoveEmbeddedObject( const OUString& rName, bool bKeepToTempStorage )
835
558
{
836
558
    uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( rName );
837
558
    if ( xObj.is() )
838
0
        RemoveEmbeddedObject( xObj, bKeepToTempStorage );
839
558
}
840
841
bool EmbeddedObjectContainer::MoveEmbeddedObject( const OUString& rName, EmbeddedObjectContainer& rCnt )
842
0
{
843
    // find object entry
844
0
    auto aIt2 = rCnt.pImpl->maNameToObjectMap.find( rName );
845
0
    OSL_ENSURE( aIt2 == rCnt.pImpl->maNameToObjectMap.end(), "Object does already exist in target container!" );
846
847
0
    if ( aIt2 != rCnt.pImpl->maNameToObjectMap.end() )
848
0
        return false;
849
850
0
    uno::Reference < embed::XEmbeddedObject > xObj;
851
0
    auto aIt = pImpl->maNameToObjectMap.find( rName );
852
0
    if ( aIt != pImpl->maNameToObjectMap.end() )
853
0
    {
854
0
        xObj = (*aIt).second;
855
0
        try
856
0
        {
857
0
            if ( xObj.is() )
858
0
            {
859
                // move object
860
0
                OUString aName( rName );
861
0
                rCnt.InsertEmbeddedObject( xObj, aName );
862
0
                pImpl->maObjectToNameMap.erase( aIt->second );
863
0
                pImpl->maNameToObjectMap.erase( aIt );
864
0
                uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
865
0
                if ( xPersist.is() )
866
0
                    pImpl->mxStorage->removeElement( rName );
867
0
            }
868
0
            else
869
0
            {
870
                // copy storages; object *must* have persistence!
871
0
                uno::Reference < embed::XStorage > xOld = pImpl->mxStorage->openStorageElement( rName, embed::ElementModes::READ );
872
0
                uno::Reference < embed::XStorage > xNew = rCnt.pImpl->mxStorage->openStorageElement( rName, embed::ElementModes::READWRITE );
873
0
                xOld->copyToStorage( xNew );
874
0
            }
875
876
0
            rCnt.TryToCopyGraphReplacement( *this, rName, rName );
877
            // RemoveGraphicStream( rName );
878
879
0
            return true;
880
0
        }
881
0
        catch (const uno::Exception&)
882
0
        {
883
0
            SAL_WARN( "comphelper.container", "Could not move object!");
884
0
            return false;
885
0
        }
886
887
0
    }
888
0
    else
889
0
        SAL_WARN( "comphelper.container", "Unknown object!");
890
0
    return false;
891
0
}
892
893
// #i119941, bKeepToTempStorage: use to specify whether store the removed object to temporary storage+
894
bool EmbeddedObjectContainer::RemoveEmbeddedObject(
895
        const uno::Reference < embed::XEmbeddedObject >& xObj, bool bKeepToTempStorage )
896
0
{
897
0
    uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
898
0
    OUString aName;
899
0
    if ( xPersist.is() )
900
0
        aName = xPersist->getEntryName();
901
902
#if OSL_DEBUG_LEVEL > 1
903
    uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
904
    uno::Reference < embed::XLinkageSupport > xLink( xPersist, uno::UNO_QUERY );
905
    sal_Bool bIsNotEmbedded = !xPersist.is() || ( xLink.is() && xLink->isLink() );
906
907
    // if the object has a persistence and the object is not a link than it must have persistence entry in the storage
908
    OSL_ENSURE( bIsNotEmbedded || xAccess->hasByName(aName), "Removing element not present in storage!" );
909
#endif
910
911
    // somebody still needs the object, so we must assign a temporary persistence
912
0
    try
913
0
    {
914
0
        if ( xPersist.is() && bKeepToTempStorage ) // #i119941
915
0
        {
916
917
0
            if ( !pImpl->mpTempObjectContainer )
918
0
            {
919
0
                pImpl->mpTempObjectContainer = std::make_unique<EmbeddedObjectContainer>();
920
0
                try
921
0
                {
922
                    // TODO/LATER: in future probably the temporary container will have two storages ( of two formats )
923
                    //             the media type will be provided with object insertion
924
0
                    OUString aOrigStorMediaType;
925
0
                    uno::Reference< beans::XPropertySet > xStorProps( pImpl->mxStorage, uno::UNO_QUERY_THROW );
926
0
                    static constexpr OUString s_sMediaType(u"MediaType"_ustr);
927
0
                    xStorProps->getPropertyValue( s_sMediaType ) >>= aOrigStorMediaType;
928
929
0
                    SAL_WARN_IF( aOrigStorMediaType.isEmpty(), "comphelper.container", "No valuable media type in the storage!" );
930
931
0
                    uno::Reference< beans::XPropertySet > xTargetStorProps(
932
0
                                                                pImpl->mpTempObjectContainer->pImpl->mxStorage,
933
0
                                                                uno::UNO_QUERY_THROW );
934
0
                    xTargetStorProps->setPropertyValue( s_sMediaType,uno::Any( aOrigStorMediaType ) );
935
0
                }
936
0
                catch (const uno::Exception&)
937
0
                {
938
0
                    SAL_WARN( "comphelper.container", "Can not set the new media type to a storage!" );
939
0
                }
940
0
            }
941
942
0
            OUString aTempName, aMediaType;
943
            /* Do not create a new name for a removed object, in the pImpl->mpTempObjectContainer,
944
            because the original m_aEntryName of xObj will be overwritten by InsertEmbeddedObject(),
945
            so uno::Reference < embed::XEmbeddedObject >& xObj will misbehave in
946
            EmbeddedObjectContainer::StoreAsChildren and SfxObjectShell::SaveCompletedChildren
947
            and will throw an exception because of objects with the same names! */
948
0
            if( !pImpl->mpTempObjectContainer->HasEmbeddedObject(aName) )
949
0
                aTempName = aName;
950
951
0
            pImpl->mpTempObjectContainer->InsertEmbeddedObject( xObj, aTempName );
952
953
0
            uno::Reference < io::XInputStream > xStream = GetGraphicStream( xObj, &aMediaType );
954
0
            if ( xStream.is() )
955
0
                pImpl->mpTempObjectContainer->InsertGraphicStream( xStream, aTempName, aMediaType );
956
957
            // object is stored, so at least it can be set to loaded state
958
0
            xObj->changeState( embed::EmbedStates::LOADED );
959
0
        }
960
0
        else
961
            // objects without persistence need to stay in running state if they shall not be closed
962
0
            xObj->changeState( embed::EmbedStates::RUNNING );
963
0
    }
964
0
    catch (const uno::Exception&)
965
0
    {
966
0
        return false;
967
0
    }
968
969
0
    auto aIter = std::find_if(pImpl->maNameToObjectMap.begin(), pImpl->maNameToObjectMap.end(),
970
0
        [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
971
0
    if (aIter != pImpl->maNameToObjectMap.end())
972
0
    {
973
0
        pImpl->maObjectToNameMap.erase( aIter->second );
974
0
        pImpl->maNameToObjectMap.erase( aIter );
975
0
        uno::Reference < container::XChild > xChild( xObj, uno::UNO_QUERY );
976
0
        if ( xChild.is() )
977
0
            xChild->setParent( uno::Reference < uno::XInterface >() );
978
0
    }
979
0
    else
980
0
        SAL_WARN( "comphelper.container", "Object not found for removal!" );
981
982
0
    if ( !xPersist || !bKeepToTempStorage )  // #i119941#
983
0
        return true;
984
985
    // remove replacement image (if there is one)
986
0
    RemoveGraphicStream( aName );
987
988
    // now it's time to remove the storage from the container storage
989
0
    try
990
0
    {
991
#if OSL_DEBUG_LEVEL > 1
992
        // if the object has a persistence and the object is not a link than it must have persistence entry in storage
993
        OSL_ENSURE( bIsNotEmbedded || pImpl->mxStorage->hasByName( aName ), "The object has no persistence entry in the storage!" );
994
#endif
995
0
        if ( xPersist.is() && pImpl->mxStorage->hasByName( aName ) )
996
0
            pImpl->mxStorage->removeElement( aName );
997
0
    }
998
0
    catch (const uno::Exception&)
999
0
    {
1000
0
        SAL_WARN( "comphelper.container", "Failed to remove object from storage!" );
1001
0
        return false;
1002
0
    }
1003
1004
0
    return true;
1005
0
}
1006
1007
void EmbeddedObjectContainer::CloseEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj )
1008
0
{
1009
    // disconnect the object from the container and close it if possible
1010
1011
0
    auto aIter = std::find_if(pImpl->maNameToObjectMap.begin(), pImpl->maNameToObjectMap.end(),
1012
0
        [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
1013
0
    if (aIter == pImpl->maNameToObjectMap.end())
1014
0
        return;
1015
1016
0
    pImpl->maObjectToNameMap.erase( aIter->second );
1017
0
    pImpl->maNameToObjectMap.erase( aIter );
1018
1019
0
    try
1020
0
    {
1021
0
        xObj->close( true );
1022
0
    }
1023
0
    catch (const uno::Exception&)
1024
0
    {
1025
        // it is no problem if the object is already closed
1026
        // TODO/LATER: what if the object can not be closed?
1027
0
    }
1028
0
}
1029
1030
uno::Reference < io::XInputStream > EmbeddedObjectContainer::GetGraphicStream( const OUString& aName, OUString* pMediaType )
1031
0
{
1032
0
    uno::Reference < io::XInputStream > xStream;
1033
1034
0
    SAL_WARN_IF( aName.isEmpty(), "comphelper.container", "Retrieving graphic for unknown object!" );
1035
0
    if ( !aName.isEmpty() )
1036
0
    {
1037
0
        try
1038
0
        {
1039
0
            uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
1040
0
            uno::Reference < io::XStream > xGraphicStream = xReplacements->openStreamElement( aName, embed::ElementModes::READ );
1041
0
            xStream = xGraphicStream->getInputStream();
1042
0
            if ( pMediaType )
1043
0
            {
1044
0
                uno::Reference < beans::XPropertySet > xSet( xStream, uno::UNO_QUERY );
1045
0
                if ( xSet.is() )
1046
0
                {
1047
0
                    uno::Any aAny = xSet->getPropertyValue(u"MediaType"_ustr);
1048
0
                    aAny >>= *pMediaType;
1049
0
                }
1050
0
            }
1051
0
        }
1052
0
        catch (uno::Exception const& e)
1053
0
        {
1054
0
            SAL_INFO("comphelper.container",
1055
0
                "EmbeddedObjectContainer::GetGraphicStream(): " << e);
1056
0
        }
1057
0
    }
1058
1059
0
    return xStream;
1060
0
}
1061
1062
uno::Reference < io::XInputStream > EmbeddedObjectContainer::GetGraphicStream( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj, OUString* pMediaType )
1063
0
{
1064
    // try to load it from the container storage
1065
0
    return GetGraphicStream( GetEmbeddedObjectName( xObj ), pMediaType );
1066
0
}
1067
1068
bool EmbeddedObjectContainer::InsertGraphicStream( const css::uno::Reference < css::io::XInputStream >& rStream, const OUString& rObjectName, const OUString& rMediaType )
1069
0
{
1070
0
    try
1071
0
    {
1072
0
        uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
1073
1074
        // store it into the subfolder
1075
0
        uno::Reference < io::XOutputStream > xOutStream;
1076
0
        uno::Reference < io::XStream > xGraphicStream = xReplacements->openStreamElement( rObjectName,
1077
0
                embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
1078
0
        xOutStream = xGraphicStream->getOutputStream();
1079
0
        ::comphelper::OStorageHelper::CopyInputToOutput( rStream, xOutStream );
1080
0
        xOutStream->flush();
1081
1082
0
        uno::Reference< beans::XPropertySet > xPropSet( xGraphicStream, uno::UNO_QUERY );
1083
0
        if (xPropSet)
1084
0
        {
1085
0
            xPropSet->setPropertyValue(u"UseCommonStoragePasswordEncryption"_ustr,
1086
0
                                        uno::Any( true ) );
1087
0
            xPropSet->setPropertyValue(u"MediaType"_ustr, uno::Any(rMediaType) );
1088
1089
0
            xPropSet->setPropertyValue(u"Compressed"_ustr,
1090
0
                                        uno::Any( true ) );
1091
0
        }
1092
0
    }
1093
0
    catch (const uno::Exception&)
1094
0
    {
1095
0
        return false;
1096
0
    }
1097
1098
0
    return true;
1099
0
}
1100
1101
bool EmbeddedObjectContainer::InsertGraphicStreamDirectly( const css::uno::Reference < css::io::XInputStream >& rStream, const OUString& rObjectName, const OUString& rMediaType )
1102
0
{
1103
0
    try
1104
0
    {
1105
0
        uno::Reference < embed::XStorage > xReplacement = pImpl->GetReplacements();
1106
0
        uno::Reference < embed::XOptimizedStorage > xOptRepl( xReplacement, uno::UNO_QUERY_THROW );
1107
1108
        // store it into the subfolder
1109
0
        uno::Sequence< beans::PropertyValue > aProps{
1110
0
            comphelper::makePropertyValue(u"MediaType"_ustr, rMediaType),
1111
0
            comphelper::makePropertyValue(u"UseCommonStoragePasswordEncryption"_ustr, true),
1112
0
            comphelper::makePropertyValue(u"Compressed"_ustr, true)
1113
0
        };
1114
1115
0
        if ( xReplacement->hasByName( rObjectName ) )
1116
0
            xReplacement->removeElement( rObjectName );
1117
1118
0
        xOptRepl->insertStreamElementDirect( rObjectName, rStream, aProps );
1119
0
    }
1120
0
    catch (const uno::Exception&)
1121
0
    {
1122
0
        return false;
1123
0
    }
1124
1125
0
    return true;
1126
0
}
1127
1128
1129
void EmbeddedObjectContainer::RemoveGraphicStream( const OUString& rObjectName )
1130
0
{
1131
0
    try
1132
0
    {
1133
0
        uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
1134
0
        xReplacements->removeElement( rObjectName );
1135
0
    }
1136
0
    catch (const uno::Exception&)
1137
0
    {
1138
0
    }
1139
0
}
1140
namespace {
1141
    void InsertStreamIntoPicturesStorage_Impl( const uno::Reference< embed::XStorage >& xDocStor,
1142
                                            const uno::Reference< io::XInputStream >& xInStream,
1143
                                            const OUString& aStreamName )
1144
0
    {
1145
0
        OSL_ENSURE( !aStreamName.isEmpty() && xInStream.is() && xDocStor.is(), "Misuse of the method!" );
1146
1147
0
        try
1148
0
        {
1149
0
            uno::Reference< embed::XStorage > xPictures = xDocStor->openStorageElement(
1150
0
                                        u"Pictures"_ustr,
1151
0
                                        embed::ElementModes::READWRITE );
1152
0
            uno::Reference< io::XStream > xObjReplStr = xPictures->openStreamElement(
1153
0
                                        aStreamName,
1154
0
                                        embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
1155
0
            uno::Reference< io::XOutputStream > xOutStream(
1156
0
                                        xObjReplStr->getInputStream(), uno::UNO_QUERY_THROW );
1157
1158
0
            ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xOutStream );
1159
0
            xOutStream->closeOutput();
1160
1161
0
            uno::Reference< embed::XTransactedObject > xTransact( xPictures, uno::UNO_QUERY );
1162
0
            if ( xTransact.is() )
1163
0
                xTransact->commit();
1164
0
        }
1165
0
        catch (const uno::Exception&)
1166
0
        {
1167
0
            SAL_WARN( "comphelper.container", "The images storage is not available!" );
1168
0
        }
1169
0
    }
1170
1171
}
1172
1173
static bool AlwaysStoreReplacementImages(const uno::Reference<embed::XEmbeddedObject>& xObj)
1174
0
{
1175
0
    const auto clsid = xObj->getClassID();
1176
0
    if (clsid == MimeConfigurationHelper::GetSequenceClassID(SO3_SCH_CLASSID)
1177
0
        || clsid == MimeConfigurationHelper::GetSequenceClassID(SO3_SM_CLASSID))
1178
0
        return false;
1179
0
    return true;
1180
0
}
1181
1182
bool EmbeddedObjectContainer::StoreAsChildren(bool _bOasisFormat,bool _bCreateEmbedded, bool _bAutoSaveEvent,
1183
                                              const uno::Reference < embed::XStorage >& _xStorage)
1184
0
{
1185
0
    bool bResult = false;
1186
0
    try
1187
0
    {
1188
0
        comphelper::EmbeddedObjectContainer aCnt( _xStorage );
1189
0
        for (auto& name : GetObjectNames())
1190
0
        {
1191
0
            uno::Reference<embed::XEmbeddedObject> xObj = GetEmbeddedObject(name);
1192
0
            SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
1193
0
            if ( xObj.is() )
1194
0
            {
1195
0
                bool bSwitchBackToLoaded = false;
1196
0
                uno::Reference< embed::XLinkageSupport > xLink( xObj, uno::UNO_QUERY );
1197
1198
0
                uno::Reference < io::XInputStream > xStream;
1199
0
                OUString aMediaType;
1200
0
                if (officecfg::Office::Common::Save::Graphic::AddReplacementImages::get()
1201
0
                    || AlwaysStoreReplacementImages(xObj))
1202
0
                {
1203
0
                    sal_Int32 nCurState = xObj->getCurrentState();
1204
0
                    if (nCurState == embed::EmbedStates::LOADED
1205
0
                        || nCurState == embed::EmbedStates::RUNNING)
1206
0
                    {
1207
                        // means that the object is not active
1208
                        // copy replacement image from old to new container
1209
0
                        xStream = GetGraphicStream(xObj, &aMediaType);
1210
0
                    }
1211
1212
0
                    if (!xStream.is() && getUserAllowsLinkUpdate())
1213
0
                    {
1214
                        // the image must be regenerated
1215
                        // TODO/LATER: another aspect could be used
1216
0
                        if (xObj->getCurrentState() == embed::EmbedStates::LOADED)
1217
0
                            bSwitchBackToLoaded = true;
1218
1219
0
                        xStream = GetGraphicReplacementStream(embed::Aspects::MSOLE_CONTENT, xObj,
1220
0
                                                              &aMediaType);
1221
0
                    }
1222
0
                }
1223
1224
0
                if ( _bOasisFormat || (xLink.is() && xLink->isLink()) )
1225
0
                {
1226
0
                    if ( xStream.is() )
1227
0
                    {
1228
0
                        if ( _bOasisFormat )
1229
0
                        {
1230
                            // if it is an embedded object or the optimized inserting fails the normal inserting should be done
1231
0
                            if ( _bCreateEmbedded
1232
0
                                || !aCnt.InsertGraphicStreamDirectly(xStream, name, aMediaType))
1233
0
                                aCnt.InsertGraphicStream(xStream, name, aMediaType);
1234
0
                        }
1235
0
                        else
1236
0
                        {
1237
                            // it is a linked object exported into SO7 format
1238
0
                            InsertStreamIntoPicturesStorage_Impl(_xStorage, xStream, name);
1239
0
                        }
1240
0
                    }
1241
0
                }
1242
1243
0
                uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
1244
0
                if ( xPersist.is() )
1245
0
                {
1246
0
                    uno::Sequence< beans::PropertyValue > aArgs( _bOasisFormat ? 3 : 4 );
1247
0
                    auto pArgs = aArgs.getArray();
1248
0
                    pArgs[0].Name = "StoreVisualReplacement";
1249
0
                    pArgs[0].Value <<= !_bOasisFormat;
1250
1251
                    // if it is an embedded object or the optimized inserting fails the normal inserting should be done
1252
0
                    pArgs[1].Name = "CanTryOptimization";
1253
0
                    pArgs[1].Value <<= !_bCreateEmbedded;
1254
1255
0
                    pArgs[2].Name = "AutoSaveEvent";
1256
0
                    pArgs[2].Value <<= _bAutoSaveEvent;
1257
1258
0
                    if ( !_bOasisFormat )
1259
0
                    {
1260
                        // if object has no cached replacement it will use this one
1261
0
                        pArgs[3].Name = "VisualReplacement";
1262
0
                        pArgs[3].Value <<= xStream;
1263
0
                    }
1264
1265
0
                    try
1266
0
                    {
1267
0
                        xPersist->storeAsEntry( _xStorage, xPersist->getEntryName(), uno::Sequence< beans::PropertyValue >(), aArgs );
1268
0
                    }
1269
0
                    catch (const embed::WrongStateException&)
1270
0
                    {
1271
0
                        SAL_WARN("comphelper.container", "failed to store '" << name << "'");
1272
0
                    }
1273
0
                }
1274
1275
0
                if ( bSwitchBackToLoaded )
1276
                    // switch back to loaded state; that way we have a minimum cache confusion
1277
0
                    xObj->changeState( embed::EmbedStates::LOADED );
1278
0
            }
1279
0
        }
1280
1281
0
        bResult = aCnt.CommitImageSubStorage();
1282
1283
0
    }
1284
0
    catch (const uno::Exception& e)
1285
0
    {
1286
        // TODO/LATER: error handling
1287
0
        bResult = false;
1288
0
        SAL_WARN("comphelper.container", "failed. Message: " << e);
1289
0
    }
1290
1291
    // the old SO6 format does not store graphical replacements
1292
0
    if ( !_bOasisFormat && bResult )
1293
0
    {
1294
0
        try
1295
0
        {
1296
            // the substorage still can not be locked by the embedded object container
1297
0
            OUString aObjReplElement( u"ObjectReplacements"_ustr );
1298
0
            if ( _xStorage->hasByName( aObjReplElement ) && _xStorage->isStorageElement( aObjReplElement ) )
1299
0
                _xStorage->removeElement( aObjReplElement );
1300
0
        }
1301
0
        catch (const uno::Exception&)
1302
0
        {
1303
            // TODO/LATER: error handling;
1304
0
            bResult = false;
1305
0
        }
1306
0
    }
1307
0
    return bResult;
1308
0
}
1309
1310
bool EmbeddedObjectContainer::StoreChildren(bool _bOasisFormat,bool _bObjectsOnly)
1311
0
{
1312
0
    bool bResult = true;
1313
0
    for (auto& name : GetObjectNames())
1314
0
    {
1315
0
        try
1316
0
        {
1317
0
            uno::Reference<embed::XEmbeddedObject> xObj = GetEmbeddedObject(name);
1318
0
            SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
1319
0
            if ( xObj.is() )
1320
0
            {
1321
0
                sal_Int32 nCurState = xObj->getCurrentState();
1322
0
                if ( _bOasisFormat && nCurState != embed::EmbedStates::LOADED && nCurState != embed::EmbedStates::RUNNING )
1323
0
                {
1324
                    // means that the object is active
1325
                    // the image must be regenerated
1326
0
                    OUString aMediaType;
1327
1328
                    // TODO/LATER: another aspect could be used
1329
0
                    uno::Reference < io::XInputStream > xStream =
1330
0
                                GetGraphicReplacementStream(
1331
0
                                                            embed::Aspects::MSOLE_CONTENT,
1332
0
                                                            xObj,
1333
0
                                                            &aMediaType );
1334
0
                    if ( xStream.is() )
1335
0
                    {
1336
0
                        if (!InsertGraphicStreamDirectly(xStream, name, aMediaType))
1337
0
                            InsertGraphicStream(xStream, name, aMediaType);
1338
0
                    }
1339
0
                }
1340
1341
                // TODO/LATER: currently the object by default does not cache replacement image
1342
                // that means that if somebody loads SO7 document and store its objects using
1343
                // this method the images might be lost.
1344
                // Currently this method is only used on storing to alien formats, that means
1345
                // that SO7 documents storing does not use it, and all other filters are
1346
                // based on OASIS format. But if it changes the method must be fixed. The fix
1347
                // must be done only on demand since it can affect performance.
1348
1349
0
                uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
1350
0
                if ( xPersist.is() )
1351
0
                {
1352
0
                    try
1353
0
                    {
1354
                        //TODO/LATER: only storing if changed!
1355
                        //xPersist->storeOwn(); //commented, i120168
1356
1357
                        // begin:all charts will be persisted as xml format on disk when saving, which is time consuming.
1358
                        // '_bObjectsOnly' mean we are storing to alien formats.
1359
                        //  'isStorageElement' mean current object is NOT a MS OLE format. (may also include in future), i120168
1360
0
                        if (_bObjectsOnly && (nCurState == embed::EmbedStates::LOADED || nCurState == embed::EmbedStates::RUNNING)
1361
0
                            && (pImpl->mxStorage->isStorageElement(name)))
1362
0
                        {
1363
0
                            uno::Reference< util::XModifiable > xModifiable( xObj->getComponent(), uno::UNO_QUERY );
1364
0
                            if ( xModifiable.is() && xModifiable->isModified())
1365
0
                            {
1366
0
                                xPersist->storeOwn();
1367
0
                            }
1368
0
                            else
1369
0
                            {
1370
                                //do nothing. Embedded model is not modified, no need to persist.
1371
0
                            }
1372
0
                        }
1373
0
                        else //the embedded object is in active status, always store back it.
1374
0
                        {
1375
0
                            xPersist->storeOwn();
1376
0
                        }
1377
                        //end i120168
1378
0
                    }
1379
0
                    catch (const uno::Exception&)
1380
0
                    {
1381
                        // TODO/LATER: error handling
1382
0
                        bResult = false;
1383
0
                        break;
1384
0
                    }
1385
0
                }
1386
1387
0
                if ( !_bOasisFormat && !_bObjectsOnly )
1388
0
                {
1389
                    // copy replacement images for linked objects
1390
0
                    try
1391
0
                    {
1392
0
                        uno::Reference< embed::XLinkageSupport > xLink( xObj, uno::UNO_QUERY );
1393
0
                        if ( xLink.is() && xLink->isLink() )
1394
0
                        {
1395
0
                            OUString aMediaType;
1396
0
                            uno::Reference < io::XInputStream > xInStream = GetGraphicStream( xObj, &aMediaType );
1397
0
                            if ( xInStream.is() )
1398
0
                                InsertStreamIntoPicturesStorage_Impl( pImpl->mxStorage, xInStream, name );
1399
0
                        }
1400
0
                    }
1401
0
                    catch (const uno::Exception&)
1402
0
                    {
1403
0
                    }
1404
0
                }
1405
0
            }
1406
0
        }
1407
0
        catch (const uno::Exception&)
1408
0
        {
1409
            // TODO/LATER: error handling
1410
0
        }
1411
0
    }
1412
1413
0
    if ( bResult && _bOasisFormat )
1414
0
        bResult = CommitImageSubStorage();
1415
1416
0
    if ( bResult && !_bObjectsOnly )
1417
0
    {
1418
0
        try
1419
0
        {
1420
0
            ReleaseImageSubStorage();
1421
0
            OUString aObjReplElement( u"ObjectReplacements"_ustr );
1422
0
            if ( !_bOasisFormat && pImpl->mxStorage->hasByName( aObjReplElement ) && pImpl->mxStorage->isStorageElement( aObjReplElement ) )
1423
0
                pImpl->mxStorage->removeElement( aObjReplElement );
1424
0
        }
1425
0
        catch (const uno::Exception&)
1426
0
        {
1427
            // TODO/LATER: error handling
1428
0
            bResult = false;
1429
0
        }
1430
0
    }
1431
0
    return bResult;
1432
0
}
1433
1434
uno::Reference< io::XInputStream > EmbeddedObjectContainer::GetGraphicReplacementStream(
1435
                                                                sal_Int64 nViewAspect,
1436
                                                                const uno::Reference< embed::XEmbeddedObject >& xObj,
1437
                                                                OUString* pMediaType )
1438
0
{
1439
0
    if ( !xObj.is() )
1440
0
        return nullptr;
1441
1442
0
    rtl::Reference< ::comphelper::SequenceInputStream > xInStream;
1443
0
    try
1444
0
    {
1445
        // retrieving of the visual representation can switch object to running state
1446
0
        embed::VisualRepresentation aRep = xObj->getPreferredVisualRepresentation( nViewAspect );
1447
0
        if ( pMediaType )
1448
0
            *pMediaType = aRep.Flavor.MimeType;
1449
1450
0
        uno::Sequence < sal_Int8 > aSeq;
1451
0
        aRep.Data >>= aSeq;
1452
0
        xInStream = new ::comphelper::SequenceInputStream( aSeq );
1453
0
    }
1454
0
    catch (const uno::Exception&)
1455
0
    {
1456
0
    }
1457
1458
0
    return xInStream;
1459
0
}
1460
1461
bool EmbeddedObjectContainer::SetPersistentEntries(const uno::Reference< embed::XStorage >& _xStorage,bool _bClearModifiedFlag)
1462
0
{
1463
0
    bool bError = false;
1464
0
    for (auto& name : GetObjectNames())
1465
0
    {
1466
0
        uno::Reference<embed::XEmbeddedObject> xObj = GetEmbeddedObject(name);
1467
0
        SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
1468
0
        if ( xObj.is() )
1469
0
        {
1470
0
            uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
1471
0
            if ( xPersist.is() )
1472
0
            {
1473
0
                try
1474
0
                {
1475
0
                    xPersist->setPersistentEntry( _xStorage,
1476
0
                                                name,
1477
0
                                                embed::EntryInitModes::NO_INIT,
1478
0
                                                uno::Sequence< beans::PropertyValue >(),
1479
0
                                                uno::Sequence< beans::PropertyValue >() );
1480
1481
0
                }
1482
0
                catch (const uno::Exception&)
1483
0
                {
1484
                    // TODO/LATER: error handling
1485
0
                    bError = true;
1486
0
                    break;
1487
0
                }
1488
0
            }
1489
0
            if ( _bClearModifiedFlag )
1490
0
            {
1491
                // if this method is used as part of SaveCompleted the object must stay unmodified after execution
1492
0
                try
1493
0
                {
1494
0
                    uno::Reference< util::XModifiable > xModif( xObj->getComponent(), uno::UNO_QUERY );
1495
0
                    if ( xModif && xModif->isModified() )
1496
0
                        xModif->setModified( false );
1497
0
                }
1498
0
                catch (const uno::Exception&)
1499
0
                {
1500
0
                }
1501
0
            }
1502
0
        }
1503
0
    }
1504
0
    return bError;
1505
0
}
1506
1507
bool EmbeddedObjectContainer::getUserAllowsLinkUpdate() const
1508
216k
{
1509
216k
    if (officecfg::Office::Common::Security::Scripting::DisableActiveContent::get())
1510
0
        return false;
1511
216k
    return pImpl->mbUserAllowsLinkUpdate;
1512
216k
}
1513
1514
void EmbeddedObjectContainer::setUserAllowsLinkUpdate(bool bNew)
1515
26.4k
{
1516
26.4k
    if(pImpl->mbUserAllowsLinkUpdate != bNew)
1517
26.4k
    {
1518
26.4k
        pImpl->mbUserAllowsLinkUpdate = bNew;
1519
26.4k
    }
1520
26.4k
}
1521
1522
}
1523
1524
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */