Coverage Report

Created: 2026-05-16 09:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/embeddedobj/source/commonembedding/embedobj.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 <config_features.h>
21
22
#include <com/sun/star/embed/EmbedStates.hpp>
23
#include <com/sun/star/embed/EmbedUpdateModes.hpp>
24
#include <com/sun/star/embed/ObjectSaveVetoException.hpp>
25
#include <com/sun/star/embed/StorageWrappedTargetException.hpp>
26
#include <com/sun/star/embed/UnreachableStateException.hpp>
27
#include <com/sun/star/embed/XEmbeddedClient.hpp>
28
#include <com/sun/star/embed/XInplaceClient.hpp>
29
#include <com/sun/star/embed/XWindowSupplier.hpp>
30
#include <com/sun/star/embed/StateChangeInProgressException.hpp>
31
#include <com/sun/star/embed/Aspects.hpp>
32
33
#include <com/sun/star/awt/XWindowPeer.hpp>
34
#include <com/sun/star/io/IOException.hpp>
35
#include <com/sun/star/util/XCloseable.hpp>
36
#include <com/sun/star/util/XModifiable.hpp>
37
#include <com/sun/star/frame/ModuleManager.hpp>
38
#include <com/sun/star/lang/DisposedException.hpp>
39
40
#include <com/sun/star/embed/EmbedMisc.hpp>
41
#include <cppuhelper/exc_hlp.hxx>
42
#include <comphelper/multicontainer2.hxx>
43
#include <comphelper/lok.hxx>
44
#include <osl/file.hxx>
45
#include <sal/log.hxx>
46
#include <officecfg/Office/Common.hxx>
47
#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
48
#include <svl/documentlockfile.hxx>
49
#include <svl/msodocumentlockfile.hxx>
50
#endif
51
#include <com/sun/star/awt/XTopWindow.hpp>
52
#include <com/sun/star/beans/XPropertySet.hpp>
53
#include <com/sun/star/container/XIndexAccess.hpp>
54
#include <com/sun/star/frame/Desktop.hpp>
55
#include <tools/urlobj.hxx>
56
#include <unotools/resmgr.hxx>
57
#include <unotools/ucbhelper.hxx>
58
59
#include <strings.hrc>
60
#include <vcl/svapp.hxx>
61
#include <vcl/vclenum.hxx>
62
#include <vcl/weld/MessageDialog.hxx>
63
#include <vcl/weld/weld.hxx>
64
65
#include <targetstatecontrol.hxx>
66
67
#include <commonembobj.hxx>
68
#include "embedobj.hxx"
69
#include <specialobject.hxx>
70
#include <array>
71
72
namespace {
73
74
// tdf#157943 / tdf#126742: locate the visible top-level frame loaded from
75
// sLinkURL, or null. Hidden frames are skipped (caller routes them to the
76
// warn path). Comparison mirrors LoadEnv::impl_searchAlreadyLoaded.
77
css::uno::Reference< css::frame::XFrame > findLinkSourceFrame(
78
    const css::uno::Reference< css::uno::XComponentContext >& xContext,
79
    const OUString& sLinkURL )
80
0
{
81
0
    if ( sLinkURL.isEmpty() || INetURLObject( sLinkURL ).IsExoticProtocol() )
82
0
        return nullptr;
83
84
0
    try
85
0
    {
86
0
        css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( xContext );
87
0
        css::uno::Reference< css::container::XIndexAccess > xFrames = xDesktop->getFrames();
88
0
        if ( !xFrames.is() )
89
0
            return nullptr;
90
91
0
        const sal_Int32 nCount = xFrames->getCount();
92
0
        for ( sal_Int32 i = 0; i < nCount; ++i )
93
0
        {
94
0
            try
95
0
            {
96
0
                css::uno::Reference< css::frame::XFrame > xFrame;
97
0
                xFrames->getByIndex( i ) >>= xFrame;
98
0
                if ( !xFrame.is() )
99
0
                    continue;
100
101
0
                OUString sFrameURL;
102
0
                css::uno::Reference< css::frame::XController > xController = xFrame->getController();
103
0
                if ( xController.is() )
104
0
                {
105
0
                    css::uno::Reference< css::frame::XModel > xModel = xController->getModel();
106
0
                    if ( xModel.is() )
107
0
                    {
108
                        // Skip hidden frames. Calling activate() / toFront()
109
                        // on one would either silently no-op or unexpectedly
110
                        // reveal a frame meant to stay invisible.
111
0
                        bool bHidden = false;
112
0
                        for ( const auto& rProp : xModel->getArgs() )
113
0
                        {
114
0
                            if ( rProp.Name == "Hidden" )
115
0
                            {
116
0
                                rProp.Value >>= bHidden;
117
0
                                break;
118
0
                            }
119
0
                        }
120
0
                        if ( bHidden )
121
0
                            continue;
122
123
0
                        sFrameURL = xModel->getURL();
124
0
                    }
125
0
                }
126
0
                else
127
0
                {
128
                    // load may be in progress - URL lives on the frame itself
129
0
                    css::uno::Reference< css::beans::XPropertySet > xFrameProps( xFrame, css::uno::UNO_QUERY );
130
0
                    if ( xFrameProps.is() )
131
0
                        xFrameProps->getPropertyValue( u"URL"_ustr ) >>= sFrameURL;
132
0
                }
133
134
0
                if ( sFrameURL.isEmpty() )
135
0
                    continue;
136
137
0
                if ( ::utl::UCBContentHelper::EqualURLs( sLinkURL, sFrameURL ) )
138
0
                    return xFrame;
139
0
            }
140
0
            catch ( const css::uno::Exception& )
141
0
            {
142
                // ignore individual frame errors and keep iterating
143
0
            }
144
0
        }
145
0
    }
146
0
    catch ( const css::uno::Exception& )
147
0
    {
148
0
    }
149
0
    return nullptr;
150
0
}
151
152
// Bring an already-loaded source document's frame to the front. Best-effort:
153
// failure here is harmless because the caller refuses the OLE activation
154
// regardless, which is the safe outcome.
155
void switchToExistingFrame( const css::uno::Reference< css::frame::XFrame >& xFrame )
156
0
{
157
0
    if ( !xFrame.is() )
158
0
        return;
159
160
0
    try
161
0
    {
162
0
        xFrame->activate();
163
0
        css::uno::Reference< css::awt::XTopWindow > xTopWindow( xFrame->getContainerWindow(), css::uno::UNO_QUERY );
164
0
        if ( xTopWindow.is() )
165
0
            xTopWindow->toFront();
166
0
    }
167
0
    catch ( const css::uno::Exception& )
168
0
    {
169
0
    }
170
0
}
171
172
// tdf#157943: any lock - foreign, hidden own frame, or stale own lock -
173
// would fail the OLE writeback at save time, so the caller refuses on
174
// any non-empty result. Visible-own-frame is filtered out earlier.
175
// On non-multiuser builds (Android/iOS) the svt::*DocumentLockFile
176
// symbols aren't linked, so the body short-circuits to "" - the caller
177
// just skips case 2 naturally without needing a separate gate.
178
OUString getSourceLockOwner( std::u16string_view sLinkURL )
179
0
{
180
0
    if ( sLinkURL.empty() || INetURLObject( sLinkURL ).IsExoticProtocol() )
181
0
        return u""_ustr;
182
183
#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
184
    auto formatOwner = []( const LockFileEntry& aData, bool bMSO )
185
    {
186
        OUString sOwner = aData[LockFileComponent::OOOUSERNAME];
187
        if ( sOwner.isEmpty() )
188
            sOwner = aData[LockFileComponent::SYSUSERNAME];
189
        if ( sOwner.isEmpty() )
190
            return u""_ustr; // empty/corrupt lock data - treat as no lock
191
        if ( bMSO )
192
            sOwner += " (MS Office)";
193
        return sOwner;
194
    };
195
196
    // LO format (.~lock.*#)
197
    try
198
    {
199
        svt::DocumentLockFile aLockFile( sLinkURL );
200
        if ( OUString sOwner = formatOwner( aLockFile.GetLockData(), false );
201
             !sOwner.isEmpty() )
202
            return sOwner;
203
    }
204
    catch ( const css::uno::Exception& )
205
    {
206
    }
207
208
    // MSO format (~$*) - LO understands these via tdf#34171
209
    if ( svt::MSODocumentLockFile::IsMSOSupportedFileFormat( sLinkURL ) )
210
    {
211
        try
212
        {
213
            svt::MSODocumentLockFile aMSOLockFile( sLinkURL );
214
            if ( OUString sOwner = formatOwner( aMSOLockFile.GetLockData(), true );
215
                 !sOwner.isEmpty() )
216
                return sOwner;
217
        }
218
        catch ( const css::uno::Exception& )
219
        {
220
        }
221
    }
222
#endif
223
224
0
    return u""_ustr;
225
0
}
226
227
void showLinkSourceLockedDialog( const css::uno::Reference< css::awt::XWindow >& xClientWindow,
228
                                 std::u16string_view sLinkURL,
229
                                 std::u16string_view sOwner )
230
0
{
231
0
    std::locale aResLocale = Translate::Create( "emo" );
232
0
    OUString aMsg = Translate::get( STR_LINK_SOURCE_LOCKED, aResLocale );
233
234
0
    OUString aLinkURL( sLinkURL );
235
0
    OUString aSysPath = aLinkURL;
236
0
    osl::FileBase::getSystemPathFromFileURL( aLinkURL, aSysPath );
237
0
    aMsg = aMsg.replaceFirst( "%{filename}", aSysPath );
238
0
    aMsg = aMsg.replaceFirst( "%{owner}", sOwner );
239
240
0
    weld::Window* pParent = Application::GetFrameWeld( xClientWindow );
241
0
    std::shared_ptr< weld::MessageDialog > xQueryBox(
242
0
        Application::CreateMessageDialog( pParent,
243
0
            VclMessageType::Warning, VclButtonsType::Ok, aMsg ) );
244
0
    xQueryBox->runAsync( xQueryBox, []( sal_Int32 ) {} );
245
0
}
246
247
} // anonymous namespace
248
249
using namespace ::com::sun::star;
250
251
awt::Rectangle GetRectangleInterception( const awt::Rectangle& aRect1, const awt::Rectangle& aRect2 )
252
0
{
253
0
    awt::Rectangle aResult;
254
255
0
    OSL_ENSURE( aRect1.Width >= 0 && aRect2.Width >= 0 && aRect1.Height >= 0 && aRect2.Height >= 0,
256
0
                "Offset must not be less than zero!" );
257
258
0
    aResult.X = std::max(aRect1.X, aRect2.X);
259
0
    aResult.Y = std::max(aRect1.Y, aRect2.Y);
260
261
0
    sal_Int32 nRight1 = aRect1.X + aRect1.Width;
262
0
    sal_Int32 nBottom1 = aRect1.Y + aRect1.Height;
263
0
    sal_Int32 nRight2 = aRect2.X + aRect2.Width;
264
0
    sal_Int32 nBottom2 = aRect2.Y + aRect2.Height;
265
0
    aResult.Width = std::min( nRight1, nRight2 ) - aResult.X;
266
0
    aResult.Height = std::min( nBottom1, nBottom2 ) - aResult.Y;
267
268
0
    return aResult;
269
0
}
270
271
namespace
272
{
273
    using IntermediateStatesMap = std::array<std::array<uno::Sequence< sal_Int32 >, NUM_SUPPORTED_STATES>, NUM_SUPPORTED_STATES>;
274
    const IntermediateStatesMap & getIntermediateStatesMap()
275
0
    {
276
0
        static const IntermediateStatesMap map = [] () {
277
0
            IntermediateStatesMap tmp;
278
279
            // intermediate states
280
            // In the following table the first index points to starting state,
281
            // the second one to the target state, and the sequence referenced by
282
            // first two indexes contains intermediate states, that should be
283
            // passed by object to reach the target state.
284
            // If the sequence is empty that means that indirect switch from start
285
            // state to the target state is forbidden, only if direct switch is possible
286
            // the state can be reached.
287
288
0
            tmp[0][2] = { embed::EmbedStates::RUNNING };
289
290
0
            tmp[0][3] = { embed::EmbedStates::RUNNING,
291
0
                                                embed::EmbedStates::INPLACE_ACTIVE };
292
293
0
            tmp[0][4] = {embed::EmbedStates::RUNNING};
294
295
0
            tmp[1][3] = { embed::EmbedStates::INPLACE_ACTIVE };
296
297
0
            tmp[2][0] = { embed::EmbedStates::RUNNING };
298
299
0
            tmp[3][0] = { embed::EmbedStates::INPLACE_ACTIVE,
300
0
                                                embed::EmbedStates::RUNNING };
301
302
0
            tmp[3][1] = { embed::EmbedStates::INPLACE_ACTIVE };
303
304
0
            tmp[4][0] = { embed::EmbedStates::RUNNING };
305
306
0
            return tmp;
307
0
        }();
308
0
        return map;
309
0
    }
310
311
    // accepted states
312
    const css::uno::Sequence< sal_Int32 > & getAcceptedStates()
313
0
    {
314
0
        static const css::uno::Sequence< sal_Int32 > states {
315
0
            /* [0] */ embed::EmbedStates::LOADED,
316
0
                          /* [1] */ embed::EmbedStates::RUNNING,
317
0
                          /* [2] */ embed::EmbedStates::INPLACE_ACTIVE,
318
0
                          /* [3] */ embed::EmbedStates::UI_ACTIVE,
319
0
                          /* [4] */ embed::EmbedStates::ACTIVE };
320
0
        assert(states.getLength() == NUM_SUPPORTED_STATES);
321
0
        return states;
322
0
    }
323
324
}
325
326
sal_Int32 OCommonEmbeddedObject::ConvertVerbToState_Impl( sal_Int32 nVerb )
327
0
{
328
0
    auto it = m_aVerbTable.find( nVerb );
329
0
    if (it != m_aVerbTable.end())
330
0
        return it->second;
331
332
0
    throw lang::IllegalArgumentException(); // TODO: unexpected verb provided
333
0
}
334
335
336
void OCommonEmbeddedObject::Deactivate()
337
0
{
338
0
    uno::Reference< util::XModifiable > xModif( m_xDocHolder->GetComponent(), uno::UNO_QUERY );
339
340
    // no need to lock for the initialization
341
0
    uno::Reference< embed::XEmbeddedClient > xClientSite = m_xClientSite;
342
0
    if ( !xClientSite.is() )
343
0
        throw embed::WrongStateException(); //TODO: client site is not set!
344
345
    // tdf#131146 close frame before saving of the document
346
    // (during CloseFrame() call some changes could be detected not registered in util::XModifiable)
347
0
    m_xDocHolder->CloseFrame();
348
349
    // store document if it is modified
350
0
    if ( xModif.is() && xModif->isModified() )
351
0
    {
352
0
        try {
353
0
            xClientSite->saveObject();
354
355
            // tdf#141529 take note that an eventually used linked file
356
            // got changed/saved/written and that we need to copy it back if the
357
            // hosting file/document gets saved
358
0
            if(m_aLinkTempFile.is())
359
0
                m_bLinkTempFileChanged = true;
360
0
        }
361
0
        catch( const embed::ObjectSaveVetoException& )
362
0
        {
363
0
        }
364
0
        catch( const uno::Exception& )
365
0
        {
366
0
            css::uno::Any anyEx = cppu::getCaughtException();
367
0
            throw embed::StorageWrappedTargetException(
368
0
                u"The client could not store the object!"_ustr,
369
0
                static_cast< ::cppu::OWeakObject* >( this ),
370
0
                anyEx );
371
0
        }
372
0
    }
373
374
0
    xClientSite->visibilityChanged( false );
375
0
}
376
377
378
void OCommonEmbeddedObject::StateChangeNotification_Impl( bool bBeforeChange, sal_Int32 nOldState, sal_Int32 nNewState ,::osl::ResettableMutexGuard& rGuard )
379
0
{
380
0
    if ( !m_pInterfaceContainer )
381
0
        return;
382
383
0
    comphelper::OInterfaceContainerHelper2* pContainer = m_pInterfaceContainer->getContainer(
384
0
                        cppu::UnoType<embed::XStateChangeListener>::get());
385
0
    if ( pContainer == nullptr )
386
0
        return;
387
388
0
    lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >( this ) );
389
0
    comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer);
390
391
    // should be locked after the method is finished successfully
392
0
    rGuard.clear();
393
394
0
    while (pIterator.hasMoreElements())
395
0
    {
396
0
        try
397
0
        {
398
0
            if ( bBeforeChange )
399
0
                static_cast<embed::XStateChangeListener*>(pIterator.next())->changingState( aSource, nOldState, nNewState );
400
0
            else
401
0
                static_cast<embed::XStateChangeListener*>(pIterator.next())->stateChanged( aSource, nOldState, nNewState );
402
0
        }
403
0
        catch( const uno::Exception& )
404
0
        {
405
            // even if the listener complains ignore it for now
406
0
           }
407
408
0
        if ( m_bDisposed )
409
0
            return;
410
0
    }
411
412
0
    rGuard.reset();
413
0
}
414
415
void OCommonEmbeddedObject::SetInplaceActiveState()
416
0
{
417
0
    if ( !m_xClientSite.is() )
418
0
        throw embed::WrongStateException( u"client site not set, yet"_ustr, *this );
419
420
0
    uno::Reference< embed::XInplaceClient > xInplaceClient( m_xClientSite, uno::UNO_QUERY );
421
0
    if ( !xInplaceClient.is() || !xInplaceClient->canInplaceActivate() )
422
0
        throw embed::WrongStateException(); //TODO: can't activate inplace
423
0
    xInplaceClient->activatingInplace();
424
425
0
    uno::Reference< embed::XWindowSupplier > xClientWindowSupplier( xInplaceClient, uno::UNO_QUERY_THROW );
426
427
0
    m_xClientWindow = xClientWindowSupplier->getWindow();
428
0
    m_aOwnRectangle = xInplaceClient->getPlacement();
429
0
    m_aClipRectangle = xInplaceClient->getClipRectangle();
430
0
    awt::Rectangle aRectangleToShow = GetRectangleInterception( m_aOwnRectangle, m_aClipRectangle );
431
432
    // create own window based on the client window
433
    // place and resize the window according to the rectangles
434
0
    uno::Reference< awt::XWindowPeer > xClientWindowPeer( m_xClientWindow, uno::UNO_QUERY_THROW );
435
436
    // dispatch provider may not be provided
437
0
    uno::Reference< frame::XDispatchProvider > xContainerDP = xInplaceClient->getInplaceDispatchProvider();
438
0
    bool bOk = m_xDocHolder->ShowInplace( xClientWindowPeer, aRectangleToShow, xContainerDP );
439
0
    m_nObjectState = embed::EmbedStates::INPLACE_ACTIVE;
440
0
    if ( !bOk )
441
0
    {
442
0
        SwitchStateTo_Impl( embed::EmbedStates::RUNNING );
443
0
        throw embed::WrongStateException(); //TODO: can't activate inplace
444
0
    }
445
0
}
446
447
void OCommonEmbeddedObject::SwitchStateTo_Impl( sal_Int32 nNextState )
448
0
{
449
    // TODO: may be needs interaction handler to detect whether the object state
450
    //         can be changed even after errors
451
452
0
    if ( m_nObjectState == embed::EmbedStates::LOADED )
453
0
    {
454
0
        if ( nNextState == embed::EmbedStates::RUNNING )
455
0
        {
456
            // after the object reaches the running state the cloned size is not necessary any more
457
0
            m_bHasClonedSize = false;
458
459
0
            if ( m_bIsLinkURL )
460
0
            {
461
0
                m_xDocHolder->SetComponent( LoadLink_Impl(), m_bReadOnly );
462
0
            }
463
0
            else
464
0
            {
465
0
                if ( !dynamic_cast<OSpecialEmbeddedObject*>(this) )
466
0
                {
467
                    // in case embedded object is in loaded state the contents must
468
                    // be stored in the related storage and the storage
469
                    // must be created already
470
0
                    if ( !m_xObjectStorage.is() )
471
0
                        throw io::IOException(); //TODO: access denied
472
473
0
                    m_xDocHolder->SetComponent( LoadDocumentFromStorage_Impl(), m_bReadOnly );
474
0
                }
475
0
                else
476
0
                {
477
                    // objects without persistence will be initialized internally
478
0
                    uno::Sequence < uno::Any > aArgs{ uno::Any(
479
0
                        uno::Reference < embed::XEmbeddedObject >( this )) };
480
0
                    uno::Reference< util::XCloseable > xDocument(
481
0
                            m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( GetDocumentServiceName(), aArgs, m_xContext),
482
0
                            uno::UNO_QUERY );
483
484
0
                    uno::Reference < container::XChild > xChild( xDocument, uno::UNO_QUERY );
485
0
                    if ( xChild.is() )
486
0
                        xChild->setParent( m_xParent );
487
488
0
                    m_xDocHolder->SetComponent( xDocument, m_bReadOnly );
489
0
                }
490
0
            }
491
492
0
            if ( !m_xDocHolder->GetComponent().is() )
493
0
                throw embed::UnreachableStateException(); //TODO: can't open document
494
495
0
            m_nObjectState = nNextState;
496
0
        }
497
0
        else
498
0
        {
499
0
            SAL_WARN( "embeddedobj.common", "Unacceptable state switch!" );
500
0
            throw uno::RuntimeException(u"invalid next state, only RUNNING state allowed"_ustr); // TODO
501
0
        }
502
0
    }
503
0
    else if ( m_nObjectState == embed::EmbedStates::RUNNING )
504
0
    {
505
0
        if ( nNextState == embed::EmbedStates::LOADED )
506
0
        {
507
0
            m_nClonedMapUnit = m_xDocHolder->GetMapUnit( embed::Aspects::MSOLE_CONTENT );
508
0
            m_bHasClonedSize = m_xDocHolder->GetExtent( embed::Aspects::MSOLE_CONTENT, &m_aClonedSize );
509
510
            // actually frame should not exist at this point
511
0
            m_xDocHolder->CloseDocument( false, false );
512
513
0
            m_nObjectState = nNextState;
514
0
        }
515
0
        else
516
0
        {
517
0
            if ( nNextState == embed::EmbedStates::INPLACE_ACTIVE )
518
0
            {
519
0
                SetInplaceActiveState();
520
0
            }
521
0
            else if ( nNextState == embed::EmbedStates::ACTIVE )
522
0
            {
523
0
                if ( !m_xClientSite.is() )
524
0
                    throw embed::WrongStateException(); //TODO: client site is not set!
525
526
                // create frame and load document in the frame
527
0
                m_xDocHolder->Show();
528
529
0
                m_xClientSite->visibilityChanged( true );
530
0
                m_nObjectState = nNextState;
531
0
            }
532
0
            else
533
0
            {
534
0
                SAL_WARN( "embeddedobj.common", "Unacceptable state switch!" );
535
0
                throw uno::RuntimeException(u"invalid next state,only LOADED/INPLACE_ACTIVE/ACTIVE allowed"_ustr); // TODO
536
0
            }
537
0
        }
538
0
    }
539
0
    else if ( m_nObjectState == embed::EmbedStates::INPLACE_ACTIVE )
540
0
    {
541
0
        if ( nNextState == embed::EmbedStates::RUNNING )
542
0
        {
543
0
            uno::Reference< embed::XInplaceClient > xInplaceClient( m_xClientSite, uno::UNO_QUERY_THROW );
544
545
0
            m_xClientSite->visibilityChanged( true );
546
547
0
            xInplaceClient->deactivatedInplace();
548
0
            Deactivate();
549
0
            m_nObjectState = nNextState;
550
0
        }
551
0
        else if ( nNextState == embed::EmbedStates::UI_ACTIVE )
552
0
        {
553
0
            if ( !(m_nMiscStatus & embed::EmbedMisc::MS_EMBED_NOUIACTIVATE) )
554
0
            {
555
0
                uno::Reference< embed::XInplaceClient > xInplaceClient( m_xClientSite, uno::UNO_QUERY_THROW );
556
                // TODO:
557
0
                uno::Reference< css::frame::XLayoutManager > xContainerLM =
558
0
                            xInplaceClient->getLayoutManager();
559
0
                if ( !xContainerLM.is() )
560
0
                    throw embed::WrongStateException(); //TODO: can't activate UI
561
                // dispatch provider may not be provided
562
0
                uno::Reference< frame::XDispatchProvider > xContainerDP = xInplaceClient->getInplaceDispatchProvider();
563
564
                // get the container module name
565
0
                OUString aModuleName;
566
0
                try
567
0
                {
568
0
                    uno::Reference< embed::XComponentSupplier > xCompSupl( m_xClientSite, uno::UNO_QUERY_THROW );
569
0
                    uno::Reference< uno::XInterface > xContDoc( xCompSupl->getComponent(), uno::UNO_QUERY_THROW );
570
571
0
                    uno::Reference< frame::XModuleManager2 > xManager( frame::ModuleManager::create( m_xContext ) );
572
573
0
                    aModuleName = xManager->identify( xContDoc );
574
0
                }
575
0
                catch( const uno::Exception& )
576
0
                {}
577
578
0
                if (!comphelper::LibreOfficeKit::isActive())
579
0
                {
580
                    // if currently another object is UIactive it will be deactivated; usually this will activate the LM of
581
                    // the container. Locking the LM will prevent flicker.
582
0
                    xContainerLM->lock();
583
0
                    xInplaceClient->activatingUI();
584
0
                    bool bOk = m_xDocHolder->ShowUI( xContainerLM, xContainerDP, aModuleName );
585
0
                    xContainerLM->unlock();
586
587
0
                    if ( bOk )
588
0
                    {
589
0
                        m_nObjectState = nNextState;
590
0
                        m_xDocHolder->ResizeHatchWindow();
591
0
                    }
592
0
                    else
593
0
                    {
594
0
                        xInplaceClient->deactivatedUI();
595
0
                        throw embed::WrongStateException(); //TODO: can't activate UI
596
0
                    }
597
0
                }
598
0
            }
599
0
        }
600
0
        else
601
0
        {
602
0
            SAL_WARN( "embeddedobj.common", "Unacceptable state switch!" );
603
0
            throw uno::RuntimeException(u"invalid next state,only RUNNING/UI_ACTIVE allowed"_ustr); // TODO
604
0
        }
605
0
    }
606
0
    else if ( m_nObjectState == embed::EmbedStates::ACTIVE )
607
0
    {
608
0
        if ( nNextState == embed::EmbedStates::RUNNING )
609
0
        {
610
0
            Deactivate();
611
0
            m_nObjectState = nNextState;
612
0
        }
613
0
        else
614
0
        {
615
0
            SAL_WARN( "embeddedobj.common", "Unacceptable state switch!" );
616
0
            throw uno::RuntimeException(u"invalid next state, only RUNNING state allowed"_ustr); // TODO
617
0
        }
618
0
    }
619
0
    else if ( m_nObjectState == embed::EmbedStates::UI_ACTIVE )
620
0
    {
621
0
        if ( nNextState == embed::EmbedStates::INPLACE_ACTIVE )
622
0
        {
623
0
            uno::Reference< embed::XInplaceClient > xInplaceClient( m_xClientSite, uno::UNO_QUERY_THROW );
624
0
            uno::Reference< css::frame::XLayoutManager > xContainerLM =
625
0
                        xInplaceClient->getLayoutManager();
626
627
0
            bool bOk = false;
628
0
            if ( xContainerLM.is() )
629
0
                bOk = m_xDocHolder->HideUI( xContainerLM );
630
631
0
            if ( !bOk )
632
0
                throw embed::WrongStateException(); //TODO: can't activate UI
633
0
            m_nObjectState = nNextState;
634
0
            m_xDocHolder->ResizeHatchWindow();
635
0
            xInplaceClient->deactivatedUI();
636
0
        }
637
0
    }
638
0
    else
639
0
        throw embed::WrongStateException( u"The object is in unacceptable state!"_ustr,
640
0
                                          static_cast< ::cppu::OWeakObject* >(this) );
641
0
}
642
643
644
uno::Sequence< sal_Int32 > const & OCommonEmbeddedObject::GetIntermediateStatesSequence_Impl( sal_Int32 nNewState )
645
0
{
646
0
    sal_Int32 nCurInd = 0;
647
0
    auto & rAcceptedStates = getAcceptedStates();
648
0
    for ( nCurInd = 0; nCurInd < rAcceptedStates.getLength(); nCurInd++ )
649
0
        if ( rAcceptedStates[nCurInd] == m_nObjectState )
650
0
            break;
651
652
0
    if ( nCurInd == rAcceptedStates.getLength() )
653
0
        throw embed::WrongStateException( u"The object is in unacceptable state!"_ustr,
654
0
                                          static_cast< ::cppu::OWeakObject* >(this) );
655
656
0
    sal_Int32 nDestInd = 0;
657
0
    for ( nDestInd = 0; nDestInd < rAcceptedStates.getLength(); nDestInd++ )
658
0
        if ( rAcceptedStates[nDestInd] == nNewState )
659
0
            break;
660
661
0
    if ( nDestInd == rAcceptedStates.getLength() )
662
0
        throw embed::UnreachableStateException(
663
0
            u"The state either not reachable, or the object allows the state only as an intermediate one!"_ustr,
664
0
            static_cast< ::cppu::OWeakObject* >(this),
665
0
            m_nObjectState,
666
0
            nNewState );
667
668
0
    return getIntermediateStatesMap()[nCurInd][nDestInd];
669
0
}
670
671
672
void SAL_CALL OCommonEmbeddedObject::changeState( sal_Int32 nNewState )
673
0
{
674
0
    if ( officecfg::Office::Common::Security::Scripting::DisableActiveContent::get()
675
0
        && nNewState != embed::EmbedStates::LOADED )
676
0
        throw embed::UnreachableStateException();
677
0
    ::osl::ResettableMutexGuard aGuard( m_aMutex );
678
0
    if ( m_bDisposed )
679
0
        throw lang::DisposedException(); // TODO
680
681
0
    if ( m_nObjectState == -1 )
682
0
        throw embed::WrongStateException( u"The object has no persistence!"_ustr,
683
0
                                          static_cast< ::cppu::OWeakObject* >(this) );
684
685
0
    sal_Int32 nOldState = m_nObjectState;
686
687
0
    if ( m_nTargetState != -1 )
688
0
    {
689
        // means that the object is currently trying to reach the target state
690
0
        throw embed::StateChangeInProgressException( OUString(),
691
0
                                                    uno::Reference< uno::XInterface >(),
692
0
                                                    m_nTargetState );
693
0
    }
694
0
    else
695
0
    {
696
0
        TargetStateControl_Impl aControl( m_nTargetState, nNewState );
697
698
        // in case the object is already in requested state
699
0
        if ( m_nObjectState == nNewState )
700
0
        {
701
            // if active object is activated again, bring its window to top
702
0
            if ( m_nObjectState == embed::EmbedStates::ACTIVE )
703
0
                m_xDocHolder->Show();
704
705
0
            return;
706
0
        }
707
708
        // retrieve sequence of states that should be passed to reach desired state
709
0
        uno::Sequence< sal_Int32 > aIntermediateStates = GetIntermediateStatesSequence_Impl( nNewState );
710
711
        // notify listeners that the object is going to change the state
712
0
        StateChangeNotification_Impl( true, nOldState, nNewState,aGuard );
713
714
0
        try {
715
0
            for (sal_Int32 state : aIntermediateStates)
716
0
                SwitchStateTo_Impl( state );
717
718
0
            SwitchStateTo_Impl( nNewState );
719
0
        }
720
0
        catch( const uno::Exception& )
721
0
        {
722
0
            if ( nOldState != m_nObjectState )
723
                // notify listeners that the object has changed the state
724
0
                StateChangeNotification_Impl( false, nOldState, m_nObjectState, aGuard );
725
726
0
            throw;
727
0
        }
728
0
    }
729
730
    // notify listeners that the object has changed the state
731
0
    StateChangeNotification_Impl( false, nOldState, nNewState, aGuard );
732
733
    // let the object window be shown
734
0
    if ( nNewState == embed::EmbedStates::UI_ACTIVE || nNewState == embed::EmbedStates::INPLACE_ACTIVE )
735
0
        PostEvent_Impl( u"OnVisAreaChanged"_ustr );
736
0
}
737
738
739
uno::Sequence< sal_Int32 > SAL_CALL OCommonEmbeddedObject::getReachableStates()
740
0
{
741
0
    if ( m_bDisposed )
742
0
        throw lang::DisposedException(); // TODO
743
744
0
    if ( m_nObjectState == -1 )
745
0
        throw embed::WrongStateException( u"The object has no persistence!"_ustr,
746
0
                                           static_cast< ::cppu::OWeakObject* >(this) );
747
748
0
    return getAcceptedStates();
749
0
}
750
751
752
sal_Int32 SAL_CALL OCommonEmbeddedObject::getCurrentState()
753
0
{
754
0
    if ( m_bDisposed )
755
0
        throw lang::DisposedException(); // TODO
756
757
0
    if ( m_nObjectState == -1 )
758
0
        throw embed::WrongStateException( u"The object has no persistence!"_ustr,
759
0
                                          static_cast< ::cppu::OWeakObject* >(this) );
760
761
0
    return m_nObjectState;
762
0
}
763
764
765
void SAL_CALL OCommonEmbeddedObject::doVerb( sal_Int32 nVerbID )
766
0
{
767
0
    SolarMutexGuard aSolarGuard;
768
        //TODO: a gross hack to avoid deadlocks when this is called from the
769
        // outside and OCommonEmbeddedObject::changeState, with m_aMutex locked,
770
        // calls into framework code that tries to lock the solar mutex, while
771
        // another thread (through Window::ImplCallPaint, say) calls
772
        // OCommonEmbeddedObject::getComponent with the solar mutex locked and
773
        // then tries to lock m_aMutex (see fdo#56818); the alternative would be
774
        // to get locking done right in this class, but that looks like a
775
        // daunting task
776
777
0
    osl::ClearableMutexGuard aGuard( m_aMutex );
778
0
    if ( m_bDisposed )
779
0
        throw lang::DisposedException(); // TODO
780
781
0
    if ( m_nObjectState == -1 )
782
0
        throw embed::WrongStateException( u"The object has no persistence!"_ustr,
783
0
                                          static_cast< ::cppu::OWeakObject* >(this) );
784
785
    // for internal documents this call is just a duplicate of changeState
786
0
    sal_Int32 nNewState = -1;
787
0
    try
788
0
    {
789
0
        nNewState = ConvertVerbToState_Impl( nVerbID );
790
0
    }
791
0
    catch( const uno::Exception& )
792
0
    {}
793
794
0
    if ( nNewState == -1 )
795
0
    {
796
        // TODO/LATER: Save Copy as... verb ( -8 ) is implemented by container
797
        // TODO/LATER: check if the verb is a supported one and if it is produce related operation
798
0
    }
799
0
    else
800
0
    {
801
        // tdf#157943 / tdf#126742: refuse user-initiated activation of an
802
        // OLE link whose source is in use - either navigate to the visible
803
        // frame (matches MSO) or warn on any lock (foreign, hidden own
804
        // frame, or stale). Gated to visible-state verbs so internal
805
        // RUNNING transitions aren't redirected. Skipped in headless.
806
0
        if ( m_bIsLinkURL
807
0
             && ( nNewState == embed::EmbedStates::INPLACE_ACTIVE
808
0
                  || nNewState == embed::EmbedStates::UI_ACTIVE
809
0
                  || nNewState == embed::EmbedStates::ACTIVE )
810
0
             && !Application::IsHeadlessModeEnabled() )
811
0
        {
812
            // snapshot under m_aMutex so the framework calls below run on
813
            // stable values even if breakLink/reload races on another thread
814
0
            const OUString aLinkURL = m_aLinkURL;
815
0
            const auto xClientWindow = m_xClientWindow;
816
0
            const auto xContext = m_xContext;
817
0
            aGuard.clear();
818
819
            // 1. Source loaded as a visible frame here: bring it to front.
820
            // Matches MSO's "navigate to existing editor" behaviour.
821
0
            if ( auto xExistingFrame = findLinkSourceFrame( xContext, aLinkURL );
822
0
                 xExistingFrame.is() )
823
0
            {
824
0
                switchToExistingFrame( xExistingFrame );
825
0
                return;
826
0
            }
827
828
            // 2. Lock file present (foreign, hidden own frame, or stale
829
            // own lock from a crash) - all fail writeback at save time.
830
            // On non-multiuser builds getSourceLockOwner returns "" and
831
            // this branch is naturally skipped - no #if needed here.
832
0
            if ( OUString sLockOwner = getSourceLockOwner( aLinkURL );
833
0
                 !sLockOwner.isEmpty() )
834
0
            {
835
0
                showLinkSourceLockedDialog( xClientWindow, aLinkURL, sLockOwner );
836
0
                return;
837
0
            }
838
0
        }
839
0
        else
840
0
        {
841
0
            aGuard.clear();
842
0
        }
843
844
0
        changeState( nNewState );
845
0
    }
846
0
}
847
848
849
uno::Sequence< embed::VerbDescriptor > SAL_CALL OCommonEmbeddedObject::getSupportedVerbs()
850
0
{
851
0
    if ( m_bDisposed )
852
0
        throw lang::DisposedException(); // TODO
853
854
0
    if ( m_nObjectState == -1 )
855
0
        throw embed::WrongStateException( u"The object has no persistence!"_ustr,
856
0
                                          static_cast< ::cppu::OWeakObject* >(this) );
857
858
0
    return m_aObjectVerbs;
859
0
}
860
861
862
void SAL_CALL OCommonEmbeddedObject::setClientSite(
863
                const uno::Reference< embed::XEmbeddedClient >& xClient )
864
0
{
865
0
    ::osl::MutexGuard aGuard( m_aMutex );
866
0
    if ( m_bDisposed )
867
0
        throw lang::DisposedException(); // TODO
868
869
0
    if ( m_xClientSite != xClient)
870
0
    {
871
0
        if ( m_nObjectState != embed::EmbedStates::LOADED && m_nObjectState != embed::EmbedStates::RUNNING )
872
0
            throw embed::WrongStateException(
873
0
                                    u"The client site can not be set currently!"_ustr,
874
0
                                     static_cast< ::cppu::OWeakObject* >(this) );
875
876
0
        m_xClientSite = xClient;
877
0
    }
878
0
}
879
880
881
uno::Reference< embed::XEmbeddedClient > SAL_CALL OCommonEmbeddedObject::getClientSite()
882
0
{
883
0
    if ( m_bDisposed )
884
0
        throw lang::DisposedException(); // TODO
885
886
0
    if ( m_nObjectState == -1 )
887
0
        throw embed::WrongStateException( u"The object has no persistence!"_ustr,
888
0
                                          static_cast< ::cppu::OWeakObject* >(this) );
889
890
0
    return m_xClientSite;
891
0
}
892
893
894
void SAL_CALL OCommonEmbeddedObject::update()
895
0
{
896
0
    ::osl::MutexGuard aGuard( m_aMutex );
897
0
    if ( m_bDisposed )
898
0
        throw lang::DisposedException(); // TODO
899
900
0
    if ( m_nObjectState == -1 )
901
0
        throw embed::WrongStateException( u"The object has no persistence!"_ustr,
902
0
                                          static_cast< ::cppu::OWeakObject* >(this) );
903
904
0
    PostEvent_Impl( u"OnVisAreaChanged"_ustr );
905
0
}
906
907
908
void SAL_CALL OCommonEmbeddedObject::setUpdateMode( sal_Int32 nMode )
909
0
{
910
0
    ::osl::MutexGuard aGuard( m_aMutex );
911
0
    if ( m_bDisposed )
912
0
        throw lang::DisposedException(); // TODO
913
914
0
    if ( m_nObjectState == -1 )
915
0
        throw embed::WrongStateException( u"The object has no persistence!"_ustr,
916
0
                                          static_cast< ::cppu::OWeakObject* >(this) );
917
918
0
    OSL_ENSURE( nMode == embed::EmbedUpdateModes::ALWAYS_UPDATE
919
0
                    || nMode == embed::EmbedUpdateModes::EXPLICIT_UPDATE,
920
0
                "Unknown update mode!" );
921
0
    m_nUpdateMode = nMode;
922
0
}
923
924
925
sal_Int64 SAL_CALL OCommonEmbeddedObject::getStatus( sal_Int64 )
926
0
{
927
0
    if ( m_bDisposed )
928
0
        throw lang::DisposedException(); // TODO
929
930
0
    return m_nMiscStatus;
931
0
}
932
933
934
void SAL_CALL OCommonEmbeddedObject::setContainerName( const OUString& sName )
935
0
{
936
0
    ::osl::MutexGuard aGuard( m_aMutex );
937
0
    if ( m_bDisposed )
938
0
        throw lang::DisposedException(); // TODO
939
940
0
    m_aContainerName = sName;
941
0
}
942
943
void OCommonEmbeddedObject::SetOleState(bool bIsOleUpdate)
944
0
{
945
0
    ::osl::MutexGuard aGuard( m_aMutex );
946
947
0
    m_bOleUpdate = bIsOleUpdate;
948
0
}
949
950
css::uno::Reference< css::uno::XInterface > SAL_CALL OCommonEmbeddedObject::getParent()
951
0
{
952
0
    return m_xParent;
953
0
}
954
955
void SAL_CALL OCommonEmbeddedObject::setParent( const css::uno::Reference< css::uno::XInterface >& xParent )
956
0
{
957
0
    m_xParent = xParent;
958
0
    if ( m_nObjectState != -1 && m_nObjectState != embed::EmbedStates::LOADED )
959
0
    {
960
0
        uno::Reference < container::XChild > xChild( m_xDocHolder->GetComponent(), uno::UNO_QUERY );
961
0
        if ( xChild.is() )
962
0
            xChild->setParent( xParent );
963
0
    }
964
0
}
965
966
// XDefaultSizeTransmitter
967
void SAL_CALL OCommonEmbeddedObject::setDefaultSize( const css::awt::Size& rSize_100TH_MM )
968
0
{
969
    //#i103460# charts do not necessarily have an own size within ODF files, in this case they need to use the size settings from the surrounding frame, which is made available with this method
970
0
    m_aDefaultSizeForChart_In_100TH_MM = rSize_100TH_MM;
971
0
}
972
973
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */