Coverage Report

Created: 2026-08-14 10:22

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