Coverage Report

Created: 2026-09-28 10:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/framework/source/loadenv/loadenv.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 <loadenv/loadenv.hxx>
21
22
#include <loadenv/loadenvexception.hxx>
23
#include <loadenv/targethelper.hxx>
24
#include <framework/framelistanalyzer.hxx>
25
26
#include <interaction/quietinteraction.hxx>
27
#include <properties.h>
28
#include <protocols.h>
29
#include <services.h>
30
#include <targets.h>
31
#include <comphelper/interaction.hxx>
32
#include <comphelper/lok.hxx>
33
#include <comphelper/namedvaluecollection.hxx>
34
#include <comphelper/propertysequence.hxx>
35
#include <framework/interaction.hxx>
36
#include <comphelper/processfactory.hxx>
37
#include <officecfg/Office/Common.hxx>
38
#include <officecfg/Setup.hxx>
39
40
#include <com/sun/star/awt/XWindow2.hpp>
41
#include <com/sun/star/beans/XPropertySet.hpp>
42
#include <com/sun/star/container/XNameAccess.hpp>
43
#include <com/sun/star/container/XEnumeration.hpp>
44
#include <com/sun/star/document/MacroExecMode.hpp>
45
#include <com/sun/star/document/XTypeDetection.hpp>
46
#include <com/sun/star/document/XActionLockable.hpp>
47
#include <com/sun/star/document/UpdateDocMode.hpp>
48
#include <com/sun/star/frame/Desktop.hpp>
49
#include <com/sun/star/frame/OfficeFrameLoader.hpp>
50
#include <com/sun/star/frame/XModel.hpp>
51
#include <com/sun/star/frame/XFrameLoader.hpp>
52
#include <com/sun/star/frame/XSynchronousFrameLoader.hpp>
53
#include <com/sun/star/frame/XNotifyingDispatch.hpp>
54
#include <com/sun/star/frame/FrameLoaderFactory.hpp>
55
#include <com/sun/star/frame/ContentHandlerFactory.hpp>
56
#include <com/sun/star/frame/DispatchResultState.hpp>
57
#include <com/sun/star/frame/FrameSearchFlag.hpp>
58
#include <com/sun/star/frame/XDispatchProvider.hpp>
59
#include <com/sun/star/lang/IllegalArgumentException.hpp>
60
#include <com/sun/star/lang/XInitialization.hpp>
61
#include <com/sun/star/lang/DisposedException.hpp>
62
#include <com/sun/star/io/XInputStream.hpp>
63
#include <com/sun/star/task/XInteractionHandler.hpp>
64
#include <com/sun/star/task/ErrorCodeRequest.hpp>
65
#include <com/sun/star/task/InteractionHandler.hpp>
66
#include <com/sun/star/task/XStatusIndicatorFactory.hpp>
67
#include <com/sun/star/task/XStatusIndicator.hpp>
68
#include <com/sun/star/uno/RuntimeException.hpp>
69
#include <com/sun/star/ucb/UniversalContentBroker.hpp>
70
#include <com/sun/star/util/CloseVetoException.hpp>
71
#include <com/sun/star/util/URLTransformer.hpp>
72
#include <com/sun/star/util/XURLTransformer.hpp>
73
#include <com/sun/star/util/XCloseable.hpp>
74
#include <com/sun/star/util/XModifiable.hpp>
75
76
#include <utility>
77
#include <vcl/window.hxx>
78
#include <vcl/wrkwin.hxx>
79
#include <vcl/syswin.hxx>
80
81
#include <toolkit/helper/vclunohelper.hxx>
82
#include <unotools/moduleoptions.hxx>
83
#include <svtools/sfxecode.hxx>
84
#include <unotools/ucbhelper.hxx>
85
#include <comphelper/configurationhelper.hxx>
86
#include <rtl/bootstrap.hxx>
87
#include <sal/log.hxx>
88
#include <comphelper/errcode.hxx>
89
#include <vcl/svapp.hxx>
90
#include <cppuhelper/implbase.hxx>
91
#include <comphelper/profilezone.hxx>
92
#include <classes/taskcreator.hxx>
93
#include <tools/fileutil.hxx>
94
#include <unotools/mediadescriptor.hxx>
95
96
constexpr OUString PROP_TYPES = u"Types"_ustr;
97
constexpr OUString PROP_NAME = u"Name"_ustr;
98
99
namespace framework {
100
101
using namespace com::sun::star;
102
103
namespace {
104
105
class LoadEnvListener : public ::cppu::WeakImplHelper< css::frame::XLoadEventListener      ,
106
                                                        css::frame::XDispatchResultListener >
107
{
108
    private:
109
        std::mutex m_mutex;
110
        bool m_bWaitingResult;
111
        LoadEnv* m_pLoadEnv;
112
113
    public:
114
115
        explicit LoadEnvListener(LoadEnv* pLoadEnv)
116
0
            : m_bWaitingResult(true)
117
0
            , m_pLoadEnv(pLoadEnv)
118
0
        {
119
0
        }
120
121
        // frame.XLoadEventListener
122
        virtual void SAL_CALL loadFinished(const css::uno::Reference< css::frame::XFrameLoader >& xLoader) override;
123
124
        virtual void SAL_CALL loadCancelled(const css::uno::Reference< css::frame::XFrameLoader >& xLoader) override;
125
126
        // frame.XDispatchResultListener
127
        virtual void SAL_CALL dispatchFinished(const css::frame::DispatchResultEvent& aEvent) override;
128
129
        // lang.XEventListener
130
        virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override;
131
};
132
133
}
134
135
LoadEnv::LoadEnv(css::uno::Reference< css::uno::XComponentContext >  xContext)
136
0
    : m_xContext(std::move(xContext))
137
0
    , m_nSearchFlags(0)
138
0
    , m_eFeature(LoadEnvFeatures::NONE)
139
0
    , m_eContentType(E_UNSUPPORTED_CONTENT)
140
0
    , m_bCloseFrameOnError(false)
141
0
    , m_bReactivateControllerOnError(false)
142
0
    , m_bLoaded( false )
143
0
{
144
0
}
145
146
LoadEnv::~LoadEnv()
147
0
{
148
0
}
149
150
css::uno::Reference< css::lang::XComponent > LoadEnv::loadComponentFromURL(const css::uno::Reference< css::frame::XComponentLoader >&    xLoader,
151
                                                                           const css::uno::Reference< css::uno::XComponentContext >&     xContext  ,
152
                                                                           const OUString&                                        sURL   ,
153
                                                                           const OUString&                                        sTarget,
154
                                                                                 sal_Int32                                               nSearchFlags ,
155
                                                                           const css::uno::Sequence< css::beans::PropertyValue >&        lArgs  )
156
0
{
157
0
    css::uno::Reference< css::lang::XComponent > xComponent;
158
0
    comphelper::ProfileZone aZone("loadComponentFromURL");
159
160
0
    try
161
0
    {
162
0
        LoadEnv aEnv(xContext);
163
0
        LoadEnvFeatures loadEnvFeatures = LoadEnvFeatures::WorkWithUI;
164
        // tdf#118238 Only disable UI interaction when loading as hidden
165
0
        if (comphelper::NamedValueCollection::get(lArgs, u"Hidden") == uno::Any(true) || Application::IsHeadlessModeEnabled())
166
0
            loadEnvFeatures = LoadEnvFeatures::NONE;
167
168
0
        aEnv.startLoading(sURL,
169
0
                               lArgs,
170
0
                               css::uno::Reference< css::frame::XFrame >(xLoader, css::uno::UNO_QUERY),
171
0
                               sTarget,
172
0
                               nSearchFlags,
173
0
                               loadEnvFeatures);
174
0
        aEnv.waitWhileLoading(); // wait for ever!
175
176
0
        xComponent = aEnv.getTargetComponent();
177
0
    }
178
0
    catch(const LoadEnvException& ex)
179
0
    {
180
0
        switch(ex.m_nID)
181
0
        {
182
0
            case LoadEnvException::ID_INVALID_MEDIADESCRIPTOR:
183
0
                throw css::lang::IllegalArgumentException(
184
0
                    u"Optional list of arguments seem to be corrupted."_ustr, xLoader, 4);
185
186
0
            case LoadEnvException::ID_UNSUPPORTED_CONTENT:
187
0
                throw css::lang::IllegalArgumentException(
188
0
                    "Unsupported URL <" + sURL + ">: \"" + ex.m_sMessage + "\"",
189
0
                    xLoader, 1);
190
191
0
            default:
192
0
                SAL_WARN(
193
0
                    "fwk.loadenv",
194
0
                    "caught LoadEnvException " << +ex.m_nID << " \""
195
0
                        << ex.m_sMessage << "\""
196
0
                        << (ex.m_exOriginal.has<css::uno::Exception>()
197
0
                            ? (", " + ex.m_exOriginal.getValueTypeName() + " \""
198
0
                               + (ex.m_exOriginal.get<css::uno::Exception>().
199
0
                                  Message)
200
0
                               + "\"")
201
0
                            : OUString())
202
0
                        << " while loading <" << sURL << ">");
203
0
                xComponent.clear();
204
0
                break;
205
0
        }
206
0
    }
207
208
    // if AbortOnLoadFailure is set and we couldn't load the document, assert, intended for use with crashtesting to
209
    // detect when we export something we can't import
210
0
    assert(xComponent.is() || comphelper::NamedValueCollection::get(lArgs, u"AbortOnLoadFailure") != uno::Any(true));
211
212
0
    return xComponent;
213
0
}
214
215
namespace {
216
217
comphelper::SequenceAsHashMap addModelArgs(const uno::Sequence<beans::PropertyValue>& rDescriptor)
218
0
{
219
0
    comphelper::SequenceAsHashMap rResult(rDescriptor);
220
0
    uno::Reference<frame::XModel> xModel(rResult.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_MODEL, uno::Reference<frame::XModel>()));
221
222
0
    if (xModel.is())
223
0
    {
224
0
        comphelper::SequenceAsHashMap aModelArgs(xModel->getArgs());
225
0
        auto pIt = aModelArgs.find(utl::MediaDescriptor::PROP_MACROEXECUTIONMODE);
226
0
        if (pIt != aModelArgs.end())
227
0
            rResult[utl::MediaDescriptor::PROP_MACROEXECUTIONMODE] = pIt->second;
228
0
    }
229
230
0
    return rResult;
231
0
}
232
233
}
234
235
void LoadEnv::startLoading(const OUString& sURL, const uno::Sequence<beans::PropertyValue>& lMediaDescriptor,
236
        const uno::Reference<frame::XFrame>& xBaseFrame, const OUString& sTarget,
237
        sal_Int32 nSearchFlags, LoadEnvFeatures eFeature)
238
0
{
239
0
    osl::MutexGuard g(m_mutex);
240
241
    // Handle still running processes!
242
0
    if (m_xAsynchronousJob.is())
243
0
        throw LoadEnvException(LoadEnvException::ID_STILL_RUNNING);
244
245
    // take over all new parameters.
246
0
    m_xTargetFrame.clear();
247
0
    m_xBaseFrame = xBaseFrame;
248
0
    m_lMediaDescriptor = addModelArgs(lMediaDescriptor);
249
0
    m_sTarget = sTarget;
250
0
    m_nSearchFlags = nSearchFlags;
251
0
    m_eFeature = eFeature;
252
0
    m_eContentType = E_UNSUPPORTED_CONTENT;
253
0
    m_bCloseFrameOnError = false;
254
0
    m_bReactivateControllerOnError = false;
255
0
    m_bLoaded = false;
256
257
0
    OUString aRealURL;
258
0
    if (!officecfg::Office::Common::Load::DetectWebDAVRedirection::get()
259
0
        || !tools::IsMappedWebDAVPath(sURL, &aRealURL))
260
0
        aRealURL = sURL;
261
262
    // try to find out, if it's really a content, which can be loaded or must be "handled"
263
    // We use a default value for this in-parameter. Then we have to start a complex check method
264
    // internally. But if this check was already done outside it can be suppressed to perform
265
    // the load request. We take over the result then!
266
0
    m_eContentType = LoadEnv::classifyContent(aRealURL, lMediaDescriptor);
267
0
    if (m_eContentType == E_UNSUPPORTED_CONTENT)
268
0
        throw LoadEnvException(LoadEnvException::ID_UNSUPPORTED_CONTENT, u"from LoadEnv::startLoading"_ustr);
269
270
    // make URL part of the MediaDescriptor
271
    // It doesn't matter if it is already an item of it.
272
    // It must be the same value... so we can overwrite it :-)
273
0
    m_lMediaDescriptor[utl::MediaDescriptor::PROP_URL] <<= aRealURL;
274
275
    // parse it - because some following code require that
276
0
    m_aURL.Complete = aRealURL;
277
0
    uno::Reference<util::XURLTransformer> xParser(util::URLTransformer::create(m_xContext));
278
0
    xParser->parseStrict(m_aURL);
279
280
    // BTW: Split URL and JumpMark ...
281
    // Because such mark is an explicit value of the media descriptor!
282
0
    if (!m_aURL.Mark.isEmpty())
283
0
        m_lMediaDescriptor[utl::MediaDescriptor::PROP_JUMPMARK] <<= m_aURL.Mark;
284
285
    // By the way: remove the old and deprecated value "FileName" from the descriptor!
286
0
    auto pIt = m_lMediaDescriptor.find(utl::MediaDescriptor::PROP_FILENAME);
287
0
    if (pIt != m_lMediaDescriptor.end())
288
0
        m_lMediaDescriptor.erase(pIt);
289
290
    // patch the MediaDescriptor, so it fulfil the outside requirements
291
    // Means especially items like e.g. UI InteractionHandler, Status Indicator,
292
    // MacroExecutionMode, etc.
293
294
    /*TODO progress is bound to a frame ... How can we set it here? */
295
296
    // UI mode
297
0
    const bool bUIMode =
298
0
        (m_eFeature & LoadEnvFeatures::WorkWithUI) &&
299
0
        !m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN, false) &&
300
0
        !m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_PREVIEW, false);
301
302
0
    if( comphelper::LibreOfficeKit::isActive() &&
303
0
        m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_SILENT, false))
304
0
    {
305
0
        rtl::Reference<QuietInteraction> pQuietInteraction = new QuietInteraction();
306
0
        m_lMediaDescriptor[utl::MediaDescriptor::PROP_INTERACTIONHANDLER] <<=
307
0
                uno::Reference<task::XInteractionHandler>(pQuietInteraction);
308
0
    }
309
310
0
    initializeUIDefaults(m_xContext, m_lMediaDescriptor, bUIMode, &m_pQuietInteraction);
311
312
0
    start();
313
0
}
314
315
void LoadEnv::initializeUIDefaults( const css::uno::Reference< css::uno::XComponentContext >& i_rxContext,
316
                                    comphelper::SequenceAsHashMap& io_lMediaDescriptor, const bool i_bUIMode,
317
                                    rtl::Reference<QuietInteraction>* o_ppQuietInteraction )
318
0
{
319
0
    css::uno::Reference< css::task::XInteractionHandler > xInteractionHandler;
320
0
    sal_Int16                                             nMacroMode;
321
0
    sal_Int16                                             nUpdateMode;
322
323
0
    if ( i_bUIMode )
324
0
    {
325
0
        nMacroMode  = css::document::MacroExecMode::USE_CONFIG;
326
0
        nUpdateMode = css::document::UpdateDocMode::ACCORDING_TO_CONFIG;
327
0
        try
328
0
        {
329
            // tdf#154308 At least for the case the document is launched from the StartCenter, put that StartCenter as the
330
            // parent for any dialogs that may appear during typedetection (once load starts a permanent frame will be set
331
            // anyway and used as dialog parent, which will be this one if the startcenter was running)
332
0
            css::uno::Reference<css::frame::XFramesSupplier> xSupplier = css::frame::Desktop::create(i_rxContext);
333
0
            FrameListAnalyzer aTasksAnalyzer(xSupplier, css::uno::Reference<css::frame::XFrame>(), FrameAnalyzerFlags::BackingComponent);
334
0
            css::uno::Reference<css::awt::XWindow> xDialogParent(aTasksAnalyzer.m_xBackingComponent ?
335
0
                                                                 aTasksAnalyzer.m_xBackingComponent->getContainerWindow() :
336
0
                                                                 nullptr);
337
338
0
            xInteractionHandler.set( css::task::InteractionHandler::createWithParent(i_rxContext, xDialogParent), css::uno::UNO_QUERY_THROW );
339
0
        }
340
0
        catch(const css::uno::RuntimeException&) {throw;}
341
0
        catch(const css::uno::Exception&       ) {      }
342
0
    }
343
    // hidden mode
344
0
    else
345
0
    {
346
0
        nMacroMode  = css::document::MacroExecMode::NEVER_EXECUTE;
347
0
        nUpdateMode = css::document::UpdateDocMode::NO_UPDATE;
348
0
        rtl::Reference<QuietInteraction> pQuietInteraction = new QuietInteraction();
349
0
        xInteractionHandler = pQuietInteraction.get();
350
0
        if ( o_ppQuietInteraction != nullptr )
351
0
        {
352
0
            *o_ppQuietInteraction = std::move(pQuietInteraction);
353
0
        }
354
0
    }
355
356
0
    if ( xInteractionHandler.is() )
357
0
    {
358
0
        if( io_lMediaDescriptor.find(utl::MediaDescriptor::PROP_INTERACTIONHANDLER) == io_lMediaDescriptor.end() )
359
0
        {
360
0
            io_lMediaDescriptor[utl::MediaDescriptor::PROP_INTERACTIONHANDLER] <<= xInteractionHandler;
361
0
        }
362
0
        if( io_lMediaDescriptor.find(utl::MediaDescriptor::PROP_AUTHENTICATIONHANDLER) == io_lMediaDescriptor.end() )
363
0
        {
364
0
            io_lMediaDescriptor[utl::MediaDescriptor::PROP_AUTHENTICATIONHANDLER] <<= xInteractionHandler;
365
0
        }
366
0
    }
367
368
0
    if (io_lMediaDescriptor.find(utl::MediaDescriptor::PROP_MACROEXECUTIONMODE) == io_lMediaDescriptor.end())
369
0
        io_lMediaDescriptor[utl::MediaDescriptor::PROP_MACROEXECUTIONMODE] <<= nMacroMode;
370
371
0
    if (io_lMediaDescriptor.find(utl::MediaDescriptor::PROP_UPDATEDOCMODE) == io_lMediaDescriptor.end())
372
0
        io_lMediaDescriptor[utl::MediaDescriptor::PROP_UPDATEDOCMODE] <<= nUpdateMode;
373
0
}
374
375
void LoadEnv::start()
376
0
{
377
    // SAFE ->
378
0
    {
379
0
        osl::MutexGuard aReadLock(m_mutex);
380
381
        // Handle still running processes!
382
0
        if (m_xAsynchronousJob.is())
383
0
            throw LoadEnvException(LoadEnvException::ID_STILL_RUNNING);
384
385
        // content can not be loaded or handled
386
        // check "classifyContent()" failed before ...
387
0
        if (m_eContentType == E_UNSUPPORTED_CONTENT)
388
0
            throw LoadEnvException(LoadEnvException::ID_UNSUPPORTED_CONTENT,
389
0
                                   u"from LoadEnv::start"_ustr);
390
0
    }
391
    // <- SAFE
392
393
    // detect its type/filter etc.
394
    // This information will be available by the
395
    // used descriptor member afterwards and is needed
396
    // for all following operations!
397
    // Note: An exception will be thrown, in case operation was not successfully ...
398
0
    if (m_eContentType != E_CAN_BE_SET)/* Attention: special feature to set existing component on a frame must ignore type detection! */
399
0
        impl_detectTypeAndFilter();
400
401
    // start loading the content...
402
    // Attention: Don't check m_eContentType deeper then UNSUPPORTED/SUPPORTED!
403
    // Because it was made in the easiest way... may a flat detection was made only.
404
    // And such simple detection can fail sometimes .-)
405
    // Use another strategy here. Try it and let it run into the case "loading not possible".
406
0
    bool bStarted = false;
407
0
    if (
408
0
        (m_eFeature & LoadEnvFeatures::AllowContentHandler) &&
409
0
        (m_eContentType                        != E_CAN_BE_SET          )   /* Attention: special feature to set existing component on a frame must ignore type detection! */
410
0
       )
411
0
    {
412
0
        bStarted = impl_handleContent();
413
0
    }
414
415
0
    if (!bStarted)
416
0
        bStarted = impl_loadContent();
417
418
    // not started => general error
419
    // We can't say - what was the reason for.
420
0
    if (!bStarted)
421
0
        throw LoadEnvException(
422
0
            LoadEnvException::ID_GENERAL_ERROR, u"not started"_ustr);
423
0
}
424
425
/*-----------------------------------------------
426
    TODO
427
        First draft does not implement timeout using [ms].
428
        Current implementation counts yield calls only ...
429
-----------------------------------------------*/
430
bool LoadEnv::waitWhileLoading(sal_uInt32 nTimeout)
431
0
{
432
    // Because it's not a good idea to block the main thread
433
    // (and we can't be sure that we are currently not used inside the
434
    // main thread!), we can't use conditions here really. We must yield
435
    // in an intelligent manner :-)
436
437
0
    sal_Int32 nTime = nTimeout;
438
0
    while(!Application::IsQuit())
439
0
    {
440
        // SAFE -> ------------------------------
441
0
        {
442
0
            osl::MutexGuard aReadLock1(m_mutex);
443
0
            if (!m_xAsynchronousJob.is())
444
0
                break;
445
0
        }
446
        // <- SAFE ------------------------------
447
448
0
        Application::Yield();
449
450
        // forever!
451
0
        if (nTimeout==0)
452
0
            continue;
453
454
        // timed out?
455
0
        --nTime;
456
0
        if (nTime<1)
457
0
            break;
458
0
    }
459
460
0
    osl::MutexGuard g(m_mutex);
461
0
    return !m_xAsynchronousJob.is();
462
0
}
463
464
css::uno::Reference< css::lang::XComponent > LoadEnv::getTargetComponent() const
465
0
{
466
0
    osl::MutexGuard g(m_mutex);
467
468
0
    if (!m_xTargetFrame.is())
469
0
        return css::uno::Reference< css::lang::XComponent >();
470
471
0
    css::uno::Reference< css::frame::XController > xController = m_xTargetFrame->getController();
472
0
    if (!xController.is())
473
0
        return m_xTargetFrame->getComponentWindow();
474
475
0
    css::uno::Reference< css::frame::XModel > xModel = xController->getModel();
476
0
    if (!xModel.is())
477
0
        return xController;
478
479
0
    return xModel;
480
0
}
481
482
void SAL_CALL LoadEnvListener::loadFinished(const css::uno::Reference< css::frame::XFrameLoader >&)
483
0
{
484
0
    std::unique_lock g(m_mutex);
485
0
    if (m_bWaitingResult)
486
0
        m_pLoadEnv->impl_setResult(true);
487
0
    m_bWaitingResult = false;
488
0
}
489
490
void SAL_CALL LoadEnvListener::loadCancelled(const css::uno::Reference< css::frame::XFrameLoader >&)
491
0
{
492
0
    std::unique_lock g(m_mutex);
493
0
    if (m_bWaitingResult)
494
0
        m_pLoadEnv->impl_setResult(false);
495
0
    m_bWaitingResult = false;
496
0
}
497
498
void SAL_CALL LoadEnvListener::dispatchFinished(const css::frame::DispatchResultEvent& aEvent)
499
0
{
500
0
    std::unique_lock g(m_mutex);
501
502
0
    if (!m_bWaitingResult)
503
0
        return;
504
505
0
    switch(aEvent.State)
506
0
    {
507
0
        case css::frame::DispatchResultState::FAILURE :
508
0
            m_pLoadEnv->impl_setResult(false);
509
0
            break;
510
511
0
        case css::frame::DispatchResultState::SUCCESS :
512
0
            m_pLoadEnv->impl_setResult(false);
513
0
            break;
514
515
0
        case css::frame::DispatchResultState::DONTKNOW :
516
0
            m_pLoadEnv->impl_setResult(false);
517
0
            break;
518
0
    }
519
0
    m_bWaitingResult = false;
520
0
}
521
522
void SAL_CALL LoadEnvListener::disposing(const css::lang::EventObject&)
523
0
{
524
0
    std::unique_lock g(m_mutex);
525
0
    if (m_bWaitingResult)
526
0
        m_pLoadEnv->impl_setResult(false);
527
0
    m_bWaitingResult = false;
528
0
}
529
530
void LoadEnv::impl_setResult(bool bResult)
531
0
{
532
0
    osl::MutexGuard g(m_mutex);
533
534
0
    m_bLoaded = bResult;
535
536
0
    impl_reactForLoadingState();
537
538
    // clearing of this reference will unblock waitWhileLoading()!
539
    // So we must be sure, that loading process was really finished.
540
    // => do it as last operation of this method ...
541
0
    m_xAsynchronousJob.clear();
542
0
}
543
544
/*-----------------------------------------------
545
    TODO: Is it a good idea to change Sequence<>
546
          parameter to stl-adapter?
547
-----------------------------------------------*/
548
LoadEnv::EContentType LoadEnv::classifyContent(const OUString&                                 sURL            ,
549
                                               const css::uno::Sequence< css::beans::PropertyValue >& lMediaDescriptor)
550
0
{
551
552
    // (i) Filter some special well known URL protocols,
553
    //     which can not be handled or loaded in general.
554
    //     Of course an empty URL must be ignored here too.
555
    //     Note: These URL schemata are fix and well known ...
556
    //     But there can be some additional ones, which was not
557
    //     defined at implementation time of this class :-(
558
    //     So we have to make sure, that the following code
559
    //     can detect such protocol schemata too :-)
560
561
0
    if(
562
0
        (sURL.isEmpty()                                          ) ||
563
0
        (ProtocolCheck::isProtocol(sURL,EProtocol::Uno    )) ||
564
0
        (ProtocolCheck::isProtocol(sURL,EProtocol::Slot   )) ||
565
0
        (ProtocolCheck::isProtocol(sURL,EProtocol::Macro  )) ||
566
0
        (ProtocolCheck::isProtocol(sURL,EProtocol::Service)) ||
567
0
        (ProtocolCheck::isProtocol(sURL,EProtocol::MailTo )) ||
568
0
        (ProtocolCheck::isProtocol(sURL,EProtocol::News   ))
569
0
      )
570
0
    {
571
0
        return E_UNSUPPORTED_CONTENT;
572
0
    }
573
574
    // (ii) Some special URLs indicates a given input stream,
575
    //      a full featured document model directly or
576
    //      specify a request for opening an empty document.
577
    //      Such contents are loadable in general.
578
    //      But we have to check, if the media descriptor contains
579
    //      all needed resources. If they are missing - the following
580
    //      load request will fail.
581
582
    /* Attention: The following code can't work on such special URLs!
583
                  It should not break the office... but it makes no sense
584
                  to start expensive object creations and complex search
585
                  algorithm if it's clear, that such URLs must be handled
586
                  in a special way .-)
587
    */
588
589
    // creation of new documents
590
0
    if (ProtocolCheck::isProtocol(sURL,EProtocol::PrivateFactory))
591
0
        return E_CAN_BE_LOADED;
592
593
    // using of an existing input stream
594
0
    comphelper::SequenceAsHashMap stlMediaDescriptor(lMediaDescriptor);
595
0
    if (ProtocolCheck::isProtocol(sURL,EProtocol::PrivateStream))
596
0
    {
597
0
        auto pIt = stlMediaDescriptor.find(utl::MediaDescriptor::PROP_INPUTSTREAM);
598
0
        css::uno::Reference< css::io::XInputStream > xStream;
599
0
        if (pIt != stlMediaDescriptor.end())
600
0
            pIt->second >>= xStream;
601
0
        if (xStream.is())
602
0
            return E_CAN_BE_LOADED;
603
0
        SAL_INFO("fwk.loadenv", "LoadEnv::classifyContent(): loading from stream with right URL but invalid stream detected");
604
0
        return E_UNSUPPORTED_CONTENT;
605
0
    }
606
607
    // using of a full featured document
608
0
    if (ProtocolCheck::isProtocol(sURL,EProtocol::PrivateObject))
609
0
    {
610
0
        auto pIt = stlMediaDescriptor.find(utl::MediaDescriptor::PROP_MODEL);
611
0
        css::uno::Reference< css::frame::XModel > xModel;
612
0
        if (pIt != stlMediaDescriptor.end())
613
0
            pIt->second >>= xModel;
614
0
        if (xModel.is())
615
0
            return E_CAN_BE_SET;
616
0
        SAL_INFO("fwk.loadenv", "LoadEnv::classifyContent(): loading with object with right URL but invalid object detected");
617
0
        return E_UNSUPPORTED_CONTENT;
618
0
    }
619
620
    // following operations can work on an internal type name only :-(
621
0
    const css::uno::Reference< css::uno::XComponentContext >& xContext = ::comphelper::getProcessComponentContext();
622
0
    css::uno::Reference< css::document::XTypeDetection > xDetect(
623
0
         xContext->getServiceManager()->createInstanceWithContext(
624
0
             u"com.sun.star.document.TypeDetection"_ustr, xContext),
625
0
         css::uno::UNO_QUERY_THROW);
626
627
0
    OUString sType = xDetect->queryTypeByURL(sURL);
628
629
0
    css::uno::Reference< css::frame::XLoaderFactory >      xLoaderFactory;
630
0
    css::uno::Reference< css::container::XEnumeration >    xSet;
631
632
    // (iii) If a FrameLoader service (or at least
633
    //      a Filter) can be found, which supports
634
    //      this URL - it must be a loadable content.
635
    //      Because both items are registered for types
636
    //      it's enough to check for frame loaders only.
637
    //      Most of our filters are handled by our global
638
    //      default loader. But there exist some specialized
639
    //      loader, which does not work on top of filters!
640
    //      So it's not enough to search on the filter configuration.
641
    //      Further it's not enough to search for types!
642
    //      Because there exist some types, which are referenced by
643
    //      other objects... but neither by filters nor frame loaders!
644
0
    css::uno::Sequence< OUString > lTypesReg { sType };
645
0
    css::uno::Sequence< css::beans::NamedValue >           lQuery
646
0
    {
647
0
        css::beans::NamedValue(PROP_TYPES, css::uno::Any(lTypesReg))
648
0
    };
649
650
0
    xLoaderFactory = css::frame::FrameLoaderFactory::create(xContext);
651
0
    xSet       = xLoaderFactory->createSubSetEnumerationByProperties(lQuery);
652
    // at least one registered frame loader is enough!
653
0
    if (xSet->hasMoreElements())
654
0
        return E_CAN_BE_LOADED;
655
656
    // (iv) Some URL protocols are supported by special services.
657
    //      E.g. ContentHandler.
658
    //      Such contents can be handled ... but not loaded.
659
660
0
    xLoaderFactory = css::frame::ContentHandlerFactory::create(xContext);
661
0
    xSet       = xLoaderFactory->createSubSetEnumerationByProperties(lQuery);
662
    // at least one registered content handler is enough!
663
0
    if (xSet->hasMoreElements())
664
0
        return E_CAN_BE_HANDLED;
665
666
    // (v) Last but not least the UCB is used inside office to
667
    //     load contents. He has a special configuration to know
668
    //     which URL schemata can be used inside office.
669
0
    css::uno::Reference< css::ucb::XUniversalContentBroker > xUCB(css::ucb::UniversalContentBroker::create(xContext));
670
0
    if (xUCB->queryContentProvider(sURL).is())
671
0
        return E_CAN_BE_LOADED;
672
673
    // (TODO) At this point, we have no idea .-)
674
    //        But it seems to be better, to break all
675
    //        further requests for this URL. Otherwise
676
    //        we can run into some trouble.
677
0
    return E_UNSUPPORTED_CONTENT;
678
0
}
679
680
namespace {
681
682
bool queryOrcusTypeAndFilter(const uno::Sequence<beans::PropertyValue>& rDescriptor, OUString& rType, OUString& rFilter)
683
0
{
684
0
    OUString aURL;
685
0
    sal_Int32 nSize = rDescriptor.getLength();
686
0
    for (sal_Int32 i = 0; i < nSize; ++i)
687
0
    {
688
0
        const beans::PropertyValue& rProp = rDescriptor[i];
689
0
        if (rProp.Name == "URL")
690
0
        {
691
0
            rProp.Value >>= aURL;
692
0
            break;
693
0
        }
694
0
    }
695
696
0
    if (aURL.isEmpty() || o3tl::equalsIgnoreAsciiCase(aURL.subView(0,8), u"private:"))
697
0
        return false;
698
699
    // TODO : Type must be set to be generic_Text (or any other type that
700
    // exists) in order to find a usable loader. Exploit it as a temporary
701
    // hack.
702
703
    // depending on the experimental mode
704
0
    if (!officecfg::Office::Common::Misc::ExperimentalMode::get())
705
0
    {
706
0
        return false;
707
0
    }
708
709
0
    OUString aUseOrcus;
710
0
    rtl::Bootstrap::get(u"LIBO_USE_ORCUS"_ustr, aUseOrcus);
711
0
    bool bUseOrcus = (aUseOrcus == "YES");
712
713
0
    if (!bUseOrcus)
714
0
        return false;
715
716
0
    if (aURL.endsWith(".xlsx"))
717
0
    {
718
0
        rType = "generic_Text";
719
0
        rFilter = "xlsx";
720
0
        return true;
721
0
    }
722
0
    else if (aURL.endsWith(".ods"))
723
0
    {
724
0
        rType = "generic_Text";
725
0
        rFilter = "ods";
726
0
        return true;
727
0
    }
728
0
    else if (aURL.endsWith(".csv"))
729
0
    {
730
0
        rType = "generic_Text";
731
0
        rFilter = "csv";
732
0
        return true;
733
0
    }
734
735
0
    return false;
736
0
}
737
738
}
739
740
void LoadEnv::impl_detectTypeAndFilter()
741
0
{
742
0
    static const sal_Int32 FILTERFLAG_TEMPLATEPATH  = 16;
743
744
    // SAFE ->
745
0
    osl::ClearableMutexGuard aReadLock(m_mutex);
746
747
    // Attention: Because our stl media descriptor is a copy of a uno sequence
748
    // we can't use as an in/out parameter here. Copy it before and don't forget to
749
    // update structure afterwards again!
750
0
    css::uno::Sequence< css::beans::PropertyValue >        lDescriptor = m_lMediaDescriptor.getAsConstPropertyValueList();
751
0
    css::uno::Reference< css::uno::XComponentContext >     xContext = m_xContext;
752
753
0
    aReadLock.clear();
754
    // <- SAFE
755
756
0
    OUString sType, sFilter;
757
758
0
    if (queryOrcusTypeAndFilter(lDescriptor, sType, sFilter) && !sType.isEmpty() && !sFilter.isEmpty())
759
0
    {
760
        // SAFE ->
761
0
        osl::MutexGuard aWriteLock(m_mutex);
762
763
        // Orcus type detected.  Skip the normal type detection process.
764
0
        m_lMediaDescriptor << lDescriptor;
765
0
        m_lMediaDescriptor[utl::MediaDescriptor::PROP_TYPENAME] <<= sType;
766
0
        m_lMediaDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] <<= sFilter;
767
0
        m_lMediaDescriptor[utl::MediaDescriptor::PROP_FILTERPROVIDER] <<= u"orcus"_ustr;
768
0
        m_lMediaDescriptor[utl::MediaDescriptor::PROP_DOCUMENTSERVICE] <<= u"com.sun.star.sheet.SpreadsheetDocument"_ustr;
769
0
        return;
770
        // <- SAFE
771
0
    }
772
773
0
    css::uno::Reference< css::document::XTypeDetection > xDetect(
774
0
        xContext->getServiceManager()->createInstanceWithContext(
775
0
            u"com.sun.star.document.TypeDetection"_ustr, xContext),
776
0
        css::uno::UNO_QUERY_THROW);
777
0
    sType = xDetect->queryTypeByDescriptor(lDescriptor, true); /*TODO should deep detection be able for enable/disable it from outside? */
778
779
    // no valid content -> loading not possible
780
0
    if (sType.isEmpty())
781
0
        throw LoadEnvException(
782
0
            LoadEnvException::ID_UNSUPPORTED_CONTENT, u"type detection failed"_ustr);
783
784
    // SAFE ->
785
0
    osl::ResettableMutexGuard aWriteLock(m_mutex);
786
787
    // detection was successful => update the descriptor member of this class
788
0
    m_lMediaDescriptor << lDescriptor;
789
0
    m_lMediaDescriptor[utl::MediaDescriptor::PROP_TYPENAME] <<= sType;
790
    // Is there an already detected (may be preselected) filter?
791
    // see below ...
792
0
    sFilter = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_FILTERNAME, OUString());
793
794
0
    aWriteLock.clear();
795
    // <- SAFE
796
797
    // We do have potentially correct type, but the detection process was aborted.
798
0
    if (m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_ABORTED, false))
799
0
        throw LoadEnvException(
800
0
            LoadEnvException::ID_UNSUPPORTED_CONTENT, u"type detection aborted"_ustr);
801
802
    // But the type isn't enough. For loading sometimes we need more information.
803
    // E.g. for our "_default" feature, where we recycle any frame which contains
804
    // and "Untitled" document, we must know if the new document is based on a template!
805
    // But this information is available as a filter property only.
806
    // => We must try(!) to detect the right filter for this load request.
807
    // On the other side ... if no filter is available .. ignore it.
808
    // Then the type information must be enough.
809
0
    if (sFilter.isEmpty())
810
0
    {
811
        // no -> try to find a preferred filter for the detected type.
812
        // Don't forget to update the media descriptor.
813
0
        css::uno::Reference< css::container::XNameAccess > xTypeCont(xDetect, css::uno::UNO_QUERY_THROW);
814
0
        try
815
0
        {
816
0
            ::comphelper::SequenceAsHashMap lTypeProps(xTypeCont->getByName(sType));
817
0
            sFilter = lTypeProps.getUnpackedValueOrDefault(u"PreferredFilter"_ustr, OUString());
818
0
            if (!sFilter.isEmpty())
819
0
            {
820
                // SAFE ->
821
0
                aWriteLock.reset();
822
0
                m_lMediaDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] <<= sFilter;
823
0
                aWriteLock.clear();
824
                // <- SAFE
825
0
            }
826
0
        }
827
0
        catch(const css::container::NoSuchElementException&)
828
0
            {}
829
0
    }
830
831
    // check if the filter (if one exists) points to a template format filter.
832
    // Then we have to add the property "AsTemplate".
833
    // We need this information to decide afterwards if we can use a "recycle frame"
834
    // for target "_default" or has to create a new one every time.
835
    // On the other side we have to suppress that, if this property already exists
836
    // and should trigger a special handling. Then the outside call of this method here,
837
    // has to know, what he is doing .-)
838
839
0
    bool bIsOwnTemplate = false;
840
0
    if (!sFilter.isEmpty())
841
0
    {
842
0
        css::uno::Reference< css::container::XNameAccess > xFilterCont(xContext->getServiceManager()->createInstanceWithContext(SERVICENAME_FILTERFACTORY, xContext), css::uno::UNO_QUERY_THROW);
843
0
        try
844
0
        {
845
0
            ::comphelper::SequenceAsHashMap lFilterProps(xFilterCont->getByName(sFilter));
846
0
            sal_Int32 nFlags         = lFilterProps.getUnpackedValueOrDefault(u"Flags"_ustr, sal_Int32(0));
847
0
            bIsOwnTemplate = ((nFlags & FILTERFLAG_TEMPLATEPATH) == FILTERFLAG_TEMPLATEPATH);
848
0
        }
849
0
        catch(const css::container::NoSuchElementException&)
850
0
            {}
851
0
    }
852
0
    if (bIsOwnTemplate)
853
0
    {
854
        // SAFE ->
855
0
        aWriteLock.reset();
856
        // Don't overwrite external decisions! See comments before ...
857
0
        auto pAsTemplateItem = m_lMediaDescriptor.find(utl::MediaDescriptor::PROP_ASTEMPLATE);
858
0
        if (pAsTemplateItem == m_lMediaDescriptor.end())
859
0
            m_lMediaDescriptor[utl::MediaDescriptor::PROP_ASTEMPLATE] <<= true;
860
0
        aWriteLock.clear();
861
        // <- SAFE
862
0
    }
863
0
}
864
865
bool LoadEnv::impl_handleContent()
866
0
{
867
    // SAFE -> -----------------------------------
868
0
    osl::ClearableMutexGuard aReadLock(m_mutex);
869
870
    // the type must exist inside the descriptor ... otherwise this class is implemented wrong :-)
871
0
    OUString sType = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_TYPENAME, OUString());
872
0
    if (sType.isEmpty())
873
0
        throw LoadEnvException(LoadEnvException::ID_INVALID_MEDIADESCRIPTOR);
874
875
    // convert media descriptor and URL to right format for later interface call!
876
0
    css::uno::Sequence< css::beans::PropertyValue > lDescriptor;
877
0
    m_lMediaDescriptor >> lDescriptor;
878
0
    css::util::URL aURL = m_aURL;
879
880
    // get necessary container to query for a handler object
881
0
    css::uno::Reference< css::frame::XLoaderFactory > xLoaderFactory = css::frame::ContentHandlerFactory::create(m_xContext);
882
883
0
    aReadLock.clear();
884
    // <- SAFE -----------------------------------
885
886
    // query
887
0
    css::uno::Sequence< OUString > lTypeReg { sType };
888
889
0
    css::uno::Sequence< css::beans::NamedValue > lQuery { { PROP_TYPES, css::uno::Any(lTypeReg) } };
890
891
0
    css::uno::Reference< css::container::XEnumeration > xSet = xLoaderFactory->createSubSetEnumerationByProperties(lQuery);
892
0
    while(xSet->hasMoreElements())
893
0
    {
894
0
        ::comphelper::SequenceAsHashMap lProps   (xSet->nextElement());
895
0
        OUString                 sHandler = lProps.getUnpackedValueOrDefault(PROP_NAME, OUString());
896
897
0
        css::uno::Reference< css::frame::XNotifyingDispatch > xHandler;
898
0
        try
899
0
        {
900
0
            xHandler.set(xLoaderFactory->createInstance(sHandler), css::uno::UNO_QUERY);
901
0
            if (!xHandler.is())
902
0
                continue;
903
0
        }
904
0
        catch(const css::uno::RuntimeException&)
905
0
            { throw; }
906
0
        catch(const css::uno::Exception&)
907
0
            { continue; }
908
909
        // SAFE -> -----------------------------------
910
0
        osl::ClearableMutexGuard aWriteLock(m_mutex);
911
0
        m_xAsynchronousJob = xHandler;
912
0
        rtl::Reference<LoadEnvListener> xListener = new LoadEnvListener(this);
913
0
        aWriteLock.clear();
914
        // <- SAFE -----------------------------------
915
916
0
        xHandler->dispatchWithNotification(aURL, lDescriptor, xListener);
917
918
0
        return true;
919
0
    }
920
921
0
    return false;
922
0
}
923
924
bool LoadEnv::impl_furtherDocsAllowed()
925
0
{
926
    // SAFE ->
927
0
    osl::ResettableMutexGuard aReadLock(m_mutex);
928
0
    css::uno::Reference< css::uno::XComponentContext > xContext = m_xContext;
929
0
    aReadLock.clear();
930
    // <- SAFE
931
932
0
    bool bAllowed = true;
933
934
0
    try
935
0
    {
936
0
        std::optional<sal_Int32> x(officecfg::Office::Common::Misc::MaxOpenDocuments::get());
937
938
        // NIL means: count of allowed documents = infinite !
939
        //     => return true
940
0
        if ( !x)
941
0
            bAllowed = true;
942
0
        else
943
0
        {
944
0
            sal_Int32 nMaxOpenDocuments(*x);
945
946
0
            css::uno::Reference< css::frame::XFramesSupplier > xDesktop(
947
0
                css::frame::Desktop::create(xContext),
948
0
                css::uno::UNO_QUERY_THROW);
949
950
0
            FrameListAnalyzer aAnalyzer(xDesktop,
951
0
                                        css::uno::Reference< css::frame::XFrame >(),
952
0
                                        FrameAnalyzerFlags::Help |
953
0
                                        FrameAnalyzerFlags::BackingComponent |
954
0
                                        FrameAnalyzerFlags::Hidden);
955
956
0
            sal_Int32 nOpenDocuments = aAnalyzer.m_lOtherVisibleFrames.size();
957
0
            bAllowed       = (nOpenDocuments < nMaxOpenDocuments);
958
0
        }
959
0
    }
960
0
    catch(const css::uno::Exception&)
961
0
        { bAllowed = true; } // !! internal errors are no reason to disturb the office from opening documents .-)
962
963
0
    if ( ! bAllowed )
964
0
    {
965
        // SAFE ->
966
0
        aReadLock.reset();
967
0
        css::uno::Reference< css::task::XInteractionHandler > xInteraction = m_lMediaDescriptor.getUnpackedValueOrDefault(
968
0
                                                                                utl::MediaDescriptor::PROP_INTERACTIONHANDLER,
969
0
                                                                                css::uno::Reference< css::task::XInteractionHandler >());
970
0
        aReadLock.clear();
971
        // <- SAFE
972
973
0
        if (xInteraction.is())
974
0
        {
975
0
            css::uno::Any                                                                    aInteraction;
976
977
0
            rtl::Reference<comphelper::OInteractionAbort>   pAbort   = new comphelper::OInteractionAbort();
978
0
            rtl::Reference<comphelper::OInteractionApprove> pApprove = new comphelper::OInteractionApprove();
979
980
0
            css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > lContinuations{
981
0
                pAbort, pApprove
982
0
            };
983
984
0
            css::task::ErrorCodeRequest aErrorCode;
985
0
            aErrorCode.ErrCode = sal_uInt32(ERRCODE_SFX_NOMOREDOCUMENTSALLOWED);
986
0
            aInteraction <<= aErrorCode;
987
0
            xInteraction->handle( InteractionRequest::CreateRequest(aInteraction, lContinuations) );
988
0
        }
989
0
    }
990
991
0
    return bAllowed;
992
0
}
993
994
bool LoadEnv::impl_filterHasInteractiveDialog() const
995
0
{
996
    //show the frame now so it can be the parent for any message dialogs shown during import
997
998
    //unless (tdf#114648) an Interactive case such as the new database wizard
999
0
    if (m_aURL.Arguments == "Interactive")
1000
0
       return true;
1001
1002
    // unless (tdf#116277) it's the labels/business cards slave frame
1003
0
    if (m_aURL.Arguments.indexOf("slot=") != -1)
1004
0
        return true;
1005
1006
0
    OUString sFilter = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_FILTERNAME, OUString());
1007
0
    if (sFilter.isEmpty())
1008
0
        return false;
1009
1010
    // unless (tdf#115683) the filter has a UIComponent
1011
0
    OUString sUIComponent;
1012
0
    css::uno::Reference<css::container::XNameAccess> xFilterCont(m_xContext->getServiceManager()->createInstanceWithContext(SERVICENAME_FILTERFACTORY, m_xContext),
1013
0
                                                                 css::uno::UNO_QUERY_THROW);
1014
0
    try
1015
0
    {
1016
0
        ::comphelper::SequenceAsHashMap lFilterProps(xFilterCont->getByName(sFilter));
1017
0
        sUIComponent = lFilterProps.getUnpackedValueOrDefault(u"UIComponent"_ustr, OUString());
1018
0
    }
1019
0
    catch(const css::container::NoSuchElementException&)
1020
0
    {
1021
0
    }
1022
1023
0
    return !sUIComponent.isEmpty();
1024
0
}
1025
1026
bool LoadEnv::impl_loadContent()
1027
0
{
1028
    // SAFE -> -----------------------------------
1029
0
    osl::ClearableMutexGuard aWriteLock(m_mutex);
1030
1031
    // search or create right target frame
1032
0
    OUString sTarget = m_sTarget;
1033
0
    if (TargetHelper::matchSpecialTarget(sTarget, TargetHelper::ESpecialTarget::Default))
1034
0
    {
1035
0
        m_xTargetFrame = impl_searchAlreadyLoaded();
1036
0
        if (m_xTargetFrame.is())
1037
0
        {
1038
0
            impl_setResult(true);
1039
0
            return true;
1040
0
        }
1041
0
        m_xTargetFrame = impl_searchRecycleTarget();
1042
0
    }
1043
1044
0
    if (! m_xTargetFrame.is())
1045
0
    {
1046
0
        if (
1047
0
            (TargetHelper::matchSpecialTarget(sTarget, TargetHelper::ESpecialTarget::Blank  )) ||
1048
0
            (TargetHelper::matchSpecialTarget(sTarget, TargetHelper::ESpecialTarget::Default))
1049
0
           )
1050
0
        {
1051
0
            if (! impl_furtherDocsAllowed())
1052
0
                return false;
1053
0
            TaskCreator aCreator(m_xContext);
1054
0
            m_xTargetFrame = aCreator.createTask(SPECIALTARGET_BLANK, m_lMediaDescriptor);
1055
0
            m_bCloseFrameOnError = m_xTargetFrame.is();
1056
0
        }
1057
0
        else
1058
0
        {
1059
0
            sal_Int32 nSearchFlags = m_nSearchFlags & ~css::frame::FrameSearchFlag::CREATE;
1060
0
            m_xTargetFrame   = m_xBaseFrame->findFrame(sTarget, nSearchFlags);
1061
0
            if (! m_xTargetFrame.is())
1062
0
            {
1063
0
                if (! impl_furtherDocsAllowed())
1064
0
                    return false;
1065
0
                m_xTargetFrame       = m_xBaseFrame->findFrame(SPECIALTARGET_BLANK, 0);
1066
0
                m_bCloseFrameOnError = m_xTargetFrame.is();
1067
0
            }
1068
0
        }
1069
0
    }
1070
1071
    // If we couldn't find a valid frame or the frame has no container window
1072
    // we have to throw an exception.
1073
0
    if (
1074
0
        ( ! m_xTargetFrame.is()                       ) ||
1075
0
        ( ! m_xTargetFrame->getContainerWindow().is() )
1076
0
       )
1077
0
        throw LoadEnvException(LoadEnvException::ID_NO_TARGET_FOUND);
1078
1079
0
    css::uno::Reference< css::frame::XFrame > xTargetFrame = m_xTargetFrame;
1080
1081
    // Now we have a valid frame ... and type detection was already done.
1082
    // We should apply the module dependent window position and size to the
1083
    // frame window.
1084
0
    impl_applyPersistentWindowState(xTargetFrame->getContainerWindow());
1085
1086
    // Don't forget to lock task for following load process. Otherwise it could die
1087
    // during this operation runs by terminating the office or closing this task via api.
1088
    // If we set this lock "close()" will return false and closing will be broken.
1089
    // Attention: Don't forget to reset this lock again after finishing operation.
1090
    // Otherwise task AND office couldn't die!!!
1091
    // This includes gracefully handling of Exceptions (Runtime!) too ...
1092
    // That's why we use a specialized guard, which will reset the lock
1093
    // if it will be run out of scope.
1094
1095
    // Note further: ignore if this internal guard already contains a resource.
1096
    // Might impl_searchRecycleTarget() set it before. But in case this impl-method wasn't used
1097
    // and the target frame was new created ... this lock here must be set!
1098
0
    css::uno::Reference< css::document::XActionLockable > xTargetLock(xTargetFrame, css::uno::UNO_QUERY);
1099
0
    m_aTargetLock.setResource(xTargetLock);
1100
1101
    // Add status indicator to descriptor. Loader can show a progress then.
1102
    // But don't do it, if loading should be hidden or preview is used...!
1103
    // So we prevent our code against wrong using. Why?
1104
    // It could be, that using of this progress could make trouble. e.g. He makes window visible...
1105
    // but shouldn't do that. But if no indicator is available... nobody has a chance to do that!
1106
0
    bool bHidden    = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN, false);
1107
0
    bool bMinimized = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_MINIMIZED, false);
1108
0
    bool bPreview   = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_PREVIEW, false);
1109
0
    bool bStartPres = m_lMediaDescriptor.contains("StartPresentation");
1110
1111
0
    if (!bHidden && !bMinimized && !bPreview && !bStartPres)
1112
0
    {
1113
0
        css::uno::Reference<css::task::XStatusIndicator> xProgress = m_lMediaDescriptor.getUnpackedValueOrDefault(
1114
0
            utl::MediaDescriptor::PROP_STATUSINDICATOR, css::uno::Reference<css::task::XStatusIndicator>());
1115
0
        if (!xProgress.is())
1116
0
        {
1117
            // Note: it's an optional interface!
1118
0
            css::uno::Reference< css::task::XStatusIndicatorFactory > xProgressFactory(xTargetFrame, css::uno::UNO_QUERY);
1119
0
            if (xProgressFactory.is())
1120
0
            {
1121
0
                xProgress = xProgressFactory->createStatusIndicator();
1122
0
                if (xProgress.is())
1123
0
                    m_lMediaDescriptor[utl::MediaDescriptor::PROP_STATUSINDICATOR] <<= xProgress;
1124
0
            }
1125
0
        }
1126
1127
        // Now that we have a target window into which we can load, reinit the interaction handler to have this
1128
        // window as its parent for modal dialogs and ensure the window is visible
1129
0
        css::uno::Reference< css::task::XInteractionHandler > xInteraction = m_lMediaDescriptor.getUnpackedValueOrDefault(
1130
0
                                                                                utl::MediaDescriptor::PROP_INTERACTIONHANDLER,
1131
0
                                                                                css::uno::Reference< css::task::XInteractionHandler >());
1132
0
        css::uno::Reference<css::lang::XInitialization> xHandler(xInteraction, css::uno::UNO_QUERY);
1133
0
        if (xHandler.is())
1134
0
        {
1135
0
            css::uno::Reference<css::awt::XWindow> xWindow = xTargetFrame->getContainerWindow();
1136
0
            uno::Sequence<uno::Any> aArguments(comphelper::InitAnyPropertySequence(
1137
0
            {
1138
0
                {"Parent", uno::Any(xWindow)}
1139
0
            }));
1140
0
            xHandler->initialize(aArguments);
1141
            //show the frame as early as possible to make it the parent of any message dialogs
1142
0
            if (!impl_filterHasInteractiveDialog())
1143
0
            {
1144
0
                impl_makeFrameWindowVisible(xWindow, shouldFocusAndToFront());
1145
0
                m_bFocusedAndToFront = true; // no need to ask shouldFocusAndToFront second time
1146
0
            }
1147
0
        }
1148
0
    }
1149
1150
    // convert media descriptor and URL to right format for later interface call!
1151
0
    css::uno::Sequence< css::beans::PropertyValue > lDescriptor;
1152
0
    m_lMediaDescriptor >> lDescriptor;
1153
0
    OUString sURL = m_aURL.Complete;
1154
1155
    // try to locate any interested frame loader
1156
0
    css::uno::Reference< css::uno::XInterface >                xLoader     = impl_searchLoader();
1157
0
    css::uno::Reference< css::frame::XFrameLoader >            xAsyncLoader(xLoader, css::uno::UNO_QUERY);
1158
0
    css::uno::Reference< css::frame::XSynchronousFrameLoader > xSyncLoader (xLoader, css::uno::UNO_QUERY);
1159
1160
0
    if (xAsyncLoader.is())
1161
0
    {
1162
0
        m_xAsynchronousJob = xAsyncLoader;
1163
0
        rtl::Reference<LoadEnvListener> xListener = new LoadEnvListener(this);
1164
0
        aWriteLock.clear();
1165
        // <- SAFE -----------------------------------
1166
1167
0
        xAsyncLoader->load(xTargetFrame, sURL, lDescriptor, xListener);
1168
1169
0
        return true;
1170
0
    }
1171
0
    else if (xSyncLoader.is())
1172
0
    {
1173
0
        uno::Reference<beans::XPropertySet> xTargetFrameProps(xTargetFrame, uno::UNO_QUERY);
1174
0
        if (xTargetFrameProps.is())
1175
0
        {
1176
            // Set the URL on the frame itself, for the duration of the load, when it has no
1177
            // controller.
1178
0
            xTargetFrameProps->setPropertyValue(u"URL"_ustr, uno::Any(sURL));
1179
0
        }
1180
0
        bool bResult = xSyncLoader->load(lDescriptor, xTargetFrame);
1181
        // react for the result here, so the outside waiting
1182
        // code can ask for it later.
1183
0
        impl_setResult(bResult);
1184
        // But the return value indicates a valid started(!) operation.
1185
        // And that's true every time we reach this line :-)
1186
0
        return true;
1187
0
    }
1188
1189
0
    aWriteLock.clear();
1190
    // <- SAFE
1191
1192
0
    return false;
1193
0
}
1194
1195
css::uno::Reference< css::uno::XInterface > LoadEnv::impl_searchLoader()
1196
0
{
1197
    // SAFE -> -----------------------------------
1198
0
    osl::ClearableMutexGuard aReadLock(m_mutex);
1199
1200
    // special mode to set an existing component on this frame
1201
    // In such case the loader is fix. It must be the SFX based implementation,
1202
    // which can create a view on top of such xModel components :-)
1203
0
    if (m_eContentType == E_CAN_BE_SET)
1204
0
    {
1205
0
        try
1206
0
        {
1207
0
            return css::frame::OfficeFrameLoader::create(m_xContext);
1208
0
        }
1209
0
        catch(const css::uno::RuntimeException&)
1210
0
            { throw; }
1211
0
        catch(const css::uno::Exception&)
1212
0
            {}
1213
0
        throw LoadEnvException(LoadEnvException::ID_INVALID_ENVIRONMENT);
1214
0
    }
1215
1216
    // Otherwise...
1217
    // We need this type information to locate a registered frame loader
1218
    // Without such information we can't work!
1219
0
    OUString sType = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_TYPENAME, OUString());
1220
0
    if (sType.isEmpty())
1221
0
        throw LoadEnvException(LoadEnvException::ID_INVALID_MEDIADESCRIPTOR);
1222
1223
    // try to locate any interested frame loader
1224
0
    css::uno::Reference< css::frame::XLoaderFactory > xLoaderFactory = css::frame::FrameLoaderFactory::create(m_xContext);
1225
1226
0
    aReadLock.clear();
1227
    // <- SAFE -----------------------------------
1228
1229
0
    css::uno::Sequence< OUString > lTypesReg { sType };
1230
1231
0
    css::uno::Sequence< css::beans::NamedValue > lQuery { { PROP_TYPES, css::uno::Any(lTypesReg) } };
1232
1233
0
    css::uno::Reference< css::container::XEnumeration > xSet = xLoaderFactory->createSubSetEnumerationByProperties(lQuery);
1234
0
    while(xSet->hasMoreElements())
1235
0
    {
1236
0
        try
1237
0
        {
1238
            // try everyone ...
1239
            // Ignore any loader, which makes trouble :-)
1240
0
            ::comphelper::SequenceAsHashMap             lLoaderProps(xSet->nextElement());
1241
0
            OUString                             sLoader     = lLoaderProps.getUnpackedValueOrDefault(PROP_NAME, OUString());
1242
0
            css::uno::Reference< css::uno::XInterface > xLoader = xLoaderFactory->createInstance(sLoader);
1243
0
            if (xLoader.is())
1244
0
                return xLoader;
1245
0
        }
1246
0
        catch(const css::uno::RuntimeException&)
1247
0
            { throw; }
1248
0
        catch(const css::uno::Exception&)
1249
0
            { continue; }
1250
0
    }
1251
1252
0
    return css::uno::Reference< css::uno::XInterface >();
1253
0
}
1254
1255
void LoadEnv::impl_jumpToMark(const css::uno::Reference< css::frame::XFrame >& xFrame,
1256
                              const css::util::URL&                            aURL  )
1257
0
{
1258
0
    if (aURL.Mark.isEmpty())
1259
0
        return;
1260
1261
0
    css::uno::Reference< css::frame::XDispatchProvider > xProvider(xFrame, css::uno::UNO_QUERY);
1262
0
    if (! xProvider.is())
1263
0
        return;
1264
1265
    // SAFE ->
1266
0
    osl::ClearableMutexGuard aReadLock(m_mutex);
1267
0
    css::uno::Reference< css::uno::XComponentContext > xContext = m_xContext;
1268
0
    aReadLock.clear();
1269
    // <- SAFE
1270
1271
0
    css::util::URL aCmd;
1272
0
    aCmd.Complete = ".uno:JumpToMark";
1273
1274
0
    css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(xContext));
1275
0
    xParser->parseStrict(aCmd);
1276
1277
0
    css::uno::Reference< css::frame::XDispatch > xDispatcher = xProvider->queryDispatch(aCmd, SPECIALTARGET_SELF, 0);
1278
0
    if (! xDispatcher.is())
1279
0
        return;
1280
1281
0
    ::comphelper::SequenceAsHashMap lArgs;
1282
0
    lArgs[u"Bookmark"_ustr] <<= aURL.Mark;
1283
0
    xDispatcher->dispatch(aCmd, lArgs.getAsConstPropertyValueList());
1284
0
}
1285
1286
css::uno::Reference< css::frame::XFrame > LoadEnv::impl_searchAlreadyLoaded()
1287
0
{
1288
0
    osl::MutexGuard g(m_mutex);
1289
1290
    // such search is allowed for special requests only ...
1291
    // or better it's not allowed for some requests in general :-)
1292
0
    if (
1293
0
        ( ! TargetHelper::matchSpecialTarget(m_sTarget, TargetHelper::ESpecialTarget::Default)                                               ) ||
1294
0
        m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_ASTEMPLATE , false) ||
1295
//      (m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN()     , false) == sal_True) ||
1296
0
        m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_OPENNEWVIEW, false)
1297
0
       )
1298
0
    {
1299
0
        return css::uno::Reference< css::frame::XFrame >();
1300
0
    }
1301
1302
    // check URL
1303
    // May it's not useful to start expensive document search, if it
1304
    // can fail only .. because we load from a stream or model directly!
1305
0
    if (
1306
0
        (ProtocolCheck::isProtocol(m_aURL.Complete, EProtocol::PrivateStream )) ||
1307
0
        (ProtocolCheck::isProtocol(m_aURL.Complete, EProtocol::PrivateObject ))
1308
        /*TODO should be private:factory here tested too? */
1309
0
       )
1310
0
    {
1311
0
        return css::uno::Reference< css::frame::XFrame >();
1312
0
    }
1313
1314
    // otherwise - iterate through the tasks of the desktop container
1315
    // to find out, which of them might contains the requested document
1316
0
    css::uno::Reference< css::frame::XDesktop2 >  xSupplier = css::frame::Desktop::create( m_xContext );
1317
0
    css::uno::Reference< css::container::XIndexAccess > xTaskList = xSupplier->getFrames();
1318
1319
0
    if (!xTaskList.is())
1320
0
        return css::uno::Reference< css::frame::XFrame >(); // task list can be empty!
1321
1322
    // Note: To detect if a document was already loaded before
1323
    // we check URLs here only. But might the existing and the required
1324
    // document has different versions! Then its URLs are the same...
1325
0
    sal_Int16 nNewVersion = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_VERSION, sal_Int16(-1));
1326
1327
    // will be used to save the first hidden frame referring the searched model
1328
    // Normally we are interested on visible frames... but if there is no such visible
1329
    // frame we refer to any hidden frame also (but as fallback only).
1330
0
    css::uno::Reference< css::frame::XFrame > xHiddenTask;
1331
0
    css::uno::Reference< css::frame::XFrame > xTask;
1332
1333
0
    sal_Int32 count = xTaskList->getCount();
1334
0
    for (sal_Int32 i=0; i<count; ++i)
1335
0
    {
1336
0
        try
1337
0
        {
1338
            // locate model of task
1339
            // Note: Without a model there is no chance to decide if
1340
            // this task contains the searched document or not!
1341
0
            xTaskList->getByIndex(i) >>= xTask;
1342
0
            if (!xTask.is())
1343
0
                continue;
1344
1345
0
            OUString sURL;
1346
0
            css::uno::Reference< css::frame::XController > xController = xTask->getController();
1347
0
            if (!xController.is())
1348
0
            {
1349
                // If we have no controller, then perhaps there is a load in progress. The frame
1350
                // itself has the URL in this case.
1351
0
                uno::Reference<beans::XPropertySet> xTaskProps(xTask, uno::UNO_QUERY);
1352
0
                if (xTaskProps.is())
1353
0
                {
1354
0
                    xTaskProps->getPropertyValue(u"URL"_ustr) >>= sURL;
1355
0
                }
1356
0
                if (sURL.isEmpty())
1357
0
                {
1358
0
                    xTask.clear();
1359
0
                    continue;
1360
0
                }
1361
0
            }
1362
1363
0
            uno::Reference<frame::XModel> xModel;
1364
0
            if (sURL.isEmpty())
1365
0
            {
1366
0
                xModel = xController->getModel();
1367
0
                if (!xModel.is())
1368
0
                {
1369
0
                    xTask.clear();
1370
0
                    continue;
1371
0
                }
1372
1373
                // don't check the complete URL here.
1374
                // use its main part - ignore optional jumpmarks!
1375
0
                sURL = xModel->getURL();
1376
0
            }
1377
0
            if (!::utl::UCBContentHelper::EqualURLs( m_aURL.Main, sURL ))
1378
0
            {
1379
0
                xTask.clear ();
1380
0
                continue;
1381
0
            }
1382
1383
            // get the original load arguments from the current document
1384
            // and decide if it's really the same then the one will be.
1385
            // It must be visible and must use the same file revision ...
1386
            // or must not have any file revision set (-1 == -1!)
1387
0
            comphelper::SequenceAsHashMap lOldDocDescriptor;
1388
0
            if (xModel.is())
1389
0
            {
1390
0
                lOldDocDescriptor = xModel->getArgs();
1391
1392
0
                if (lOldDocDescriptor.getUnpackedValueOrDefault(
1393
0
                        utl::MediaDescriptor::PROP_VERSION, sal_Int32(-1))
1394
0
                    != nNewVersion)
1395
0
                {
1396
0
                    xTask.clear();
1397
0
                    continue;
1398
0
                }
1399
0
            }
1400
1401
            // Hidden frames are special.
1402
            // They will be used as "last chance" if there is no visible frame pointing to the same model.
1403
            // Safe the result but continue with current loop might be looking for other visible frames.
1404
0
            bool bIsHidden = lOldDocDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN, false);
1405
0
            if ( bIsHidden && ! xHiddenTask.is() )
1406
0
            {
1407
0
                xHiddenTask = xTask;
1408
0
                xTask.clear ();
1409
0
                continue;
1410
0
            }
1411
1412
            // We found a visible task pointing to the right model ...
1413
            // Break search.
1414
0
            break;
1415
0
        }
1416
0
        catch(const css::uno::RuntimeException&)
1417
0
            { throw; }
1418
0
        catch(const css::uno::Exception&)
1419
0
            { continue; }
1420
0
    }
1421
1422
0
    css::uno::Reference< css::frame::XFrame > xResult;
1423
0
    if (xTask.is())
1424
0
        xResult = std::move(xTask);
1425
0
    else if (xHiddenTask.is())
1426
0
        xResult = std::move(xHiddenTask);
1427
1428
0
    if (xResult.is())
1429
0
    {
1430
        // Now we are sure, that this task includes the searched document.
1431
        // It's time to activate it. As special feature we try to jump internally
1432
        // if an optional jumpmark is given too.
1433
0
        if (!m_aURL.Mark.isEmpty())
1434
0
            impl_jumpToMark(xResult, m_aURL);
1435
0
    }
1436
1437
0
    return xResult;
1438
0
}
1439
1440
// static
1441
bool LoadEnv::impl_isFrameAlreadyUsedForLoading(const css::uno::Reference< css::frame::XFrame >& xFrame)
1442
0
{
1443
0
    css::uno::Reference< css::document::XActionLockable > xLock(xFrame, css::uno::UNO_QUERY);
1444
1445
    // ? no lock interface ?
1446
    // Maybe it's an external written frame implementation :-(
1447
    // Allowing using of it... but it can fail if it's not synchronized with our processes!
1448
0
    if (!xLock.is())
1449
0
        return false;
1450
1451
    // Otherwise we have to look for any other existing lock.
1452
0
    return xLock->isActionLocked();
1453
0
}
1454
1455
css::uno::Reference< css::frame::XFrame > LoadEnv::impl_searchRecycleTarget()
1456
0
{
1457
    // SAFE -> ..................................
1458
0
    osl::ClearableMutexGuard aReadLock(m_mutex);
1459
1460
    // The special backing mode frame will be recycled by definition!
1461
    // It doesn't matter if somewhere wants to create a new view
1462
    // or open a new untitled document...
1463
    // The only exception from that - hidden frames!
1464
0
    if (m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN, false))
1465
0
        return css::uno::Reference< css::frame::XFrame >();
1466
1467
0
    css::uno::Reference< css::frame::XFramesSupplier > xSupplier = css::frame::Desktop::create( m_xContext );
1468
0
    FrameListAnalyzer aTasksAnalyzer(xSupplier, css::uno::Reference< css::frame::XFrame >(), FrameAnalyzerFlags::BackingComponent);
1469
0
    if (aTasksAnalyzer.m_xBackingComponent.is())
1470
0
    {
1471
0
        if (!impl_isFrameAlreadyUsedForLoading(aTasksAnalyzer.m_xBackingComponent))
1472
0
        {
1473
0
            m_bReactivateControllerOnError = true;
1474
0
            return aTasksAnalyzer.m_xBackingComponent;
1475
0
        }
1476
0
    }
1477
1478
    // These states indicates a wish for creation of a new view in general.
1479
0
    if (
1480
0
        m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_ASTEMPLATE , false) ||
1481
0
        m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_OPENNEWVIEW, false)
1482
0
       )
1483
0
    {
1484
0
        return css::uno::Reference< css::frame::XFrame >();
1485
0
    }
1486
1487
    // On the other side some special URLs will open a new frame every time (expecting
1488
    // they can use the backing-mode frame!)
1489
0
    if (
1490
0
        (ProtocolCheck::isProtocol(m_aURL.Complete, EProtocol::PrivateFactory )) ||
1491
0
        (ProtocolCheck::isProtocol(m_aURL.Complete, EProtocol::PrivateStream  )) ||
1492
0
        (ProtocolCheck::isProtocol(m_aURL.Complete, EProtocol::PrivateObject  ))
1493
0
       )
1494
0
    {
1495
0
        return css::uno::Reference< css::frame::XFrame >();
1496
0
    }
1497
1498
    // No backing frame! No special URL => recycle active task - if possible.
1499
    // Means - if it does not already contains a modified document, or
1500
    // use another office module.
1501
0
    css::uno::Reference< css::frame::XFrame > xTask = xSupplier->getActiveFrame();
1502
1503
    // not a real error - but might a focus problem!
1504
0
    if (!xTask.is())
1505
0
        return css::uno::Reference< css::frame::XFrame >();
1506
1507
    // not a real error - may it's a view only
1508
0
    css::uno::Reference< css::frame::XController > xController = xTask->getController();
1509
0
    if (!xController.is())
1510
0
        return css::uno::Reference< css::frame::XFrame >();
1511
1512
    // not a real error - may it's a db component instead of a full featured office document
1513
0
    css::uno::Reference< css::frame::XModel > xModel = xController->getModel();
1514
0
    if (!xModel.is())
1515
0
        return css::uno::Reference< css::frame::XFrame >();
1516
1517
    // get some more information ...
1518
1519
    // A valid set URL means: there is already a location for this document.
1520
    // => it was saved there or opened from there. Such Documents can not be used here.
1521
    // We search for empty document ... created by a private:factory/ URL!
1522
0
    if (xModel->getURL().getLength()>0)
1523
0
        return css::uno::Reference< css::frame::XFrame >();
1524
1525
    // The old document must be unmodified ...
1526
0
    css::uno::Reference< css::util::XModifiable > xModified(xModel, css::uno::UNO_QUERY);
1527
0
    if (xModified->isModified())
1528
0
        return css::uno::Reference< css::frame::XFrame >();
1529
1530
0
    VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xTask->getContainerWindow());
1531
0
    if (pWindow && pWindow->IsInModalMode())
1532
0
        return css::uno::Reference< css::frame::XFrame >();
1533
1534
    // find out the application type of this document
1535
    // We can recycle only documents, which uses the same application
1536
    // then the new one.
1537
0
    SvtModuleOptions::EFactory eOldApp = SvtModuleOptions::ClassifyFactoryByModel(xModel);
1538
0
    SvtModuleOptions::EFactory eNewApp = SvtModuleOptions::ClassifyFactoryByURL  (m_aURL.Complete, m_lMediaDescriptor.getAsConstPropertyValueList());
1539
1540
0
    aReadLock.clear();
1541
    // <- SAFE ..................................
1542
1543
0
    if (eOldApp != eNewApp)
1544
0
        return css::uno::Reference< css::frame::XFrame >();
1545
1546
    // OK this task seems to be usable for recycling
1547
    // But we should mark it as such - means set an action lock.
1548
    // Otherwise it would be used more than ones or will be destroyed
1549
    // by a close() or terminate() request.
1550
    // But if such lock already exist ... it means this task is used for
1551
    // any other operation already. Don't use it then.
1552
0
    if (impl_isFrameAlreadyUsedForLoading(xTask))
1553
0
        return css::uno::Reference< css::frame::XFrame >();
1554
1555
    // OK - there is a valid target frame.
1556
    // But may be it contains already a document.
1557
    // Then we have to ask it, if it allows recycling of this frame .-)
1558
0
    bool bReactivateOldControllerOnError = false;
1559
0
    css::uno::Reference< css::frame::XController > xOldDoc = xTask->getController();
1560
0
    if (xOldDoc.is())
1561
0
    {
1562
0
        comphelper::SequenceAsHashMap lOldDocDescriptor(xModel->getArgs());
1563
1564
        // replaceable document
1565
0
        if (!lOldDocDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_REPLACEABLE, false))
1566
0
            return css::uno::Reference< css::frame::XFrame >();
1567
1568
0
        bReactivateOldControllerOnError = xOldDoc->suspend(true);
1569
0
        if (! bReactivateOldControllerOnError)
1570
0
            return css::uno::Reference< css::frame::XFrame >();
1571
0
    }
1572
1573
    // SAFE -> ..................................
1574
0
    {
1575
0
        osl::MutexGuard aWriteLock(m_mutex);
1576
1577
0
        css::uno::Reference< css::document::XActionLockable > xLock(xTask, css::uno::UNO_QUERY);
1578
0
        if (!m_aTargetLock.setResource(xLock))
1579
0
            return css::uno::Reference< css::frame::XFrame >();
1580
1581
0
        m_bReactivateControllerOnError = bReactivateOldControllerOnError;
1582
0
    }
1583
    // <- SAFE ..................................
1584
1585
0
    return xTask;
1586
0
}
1587
1588
void LoadEnv::impl_reactForLoadingState()
1589
0
{
1590
    /*TODO reset action locks */
1591
1592
    // SAFE -> ----------------------------------
1593
0
    osl::ClearableMutexGuard aReadLock(m_mutex);
1594
1595
0
    if (m_bLoaded)
1596
0
    {
1597
        // Bring the new loaded document to front (if allowed!).
1598
        // Note: We show new created frames here only.
1599
        // We don't hide already visible frames here ...
1600
0
        css::uno::Reference< css::awt::XWindow > xWindow      = m_xTargetFrame->getContainerWindow();
1601
0
        bool                                 bHidden      = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN, false);
1602
0
        bool                                 bMinimized = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_MINIMIZED, false);
1603
0
        bool                                 bStartPres = m_lMediaDescriptor.contains("StartPresentation");
1604
1605
0
        VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow);
1606
1607
0
        if (bMinimized)
1608
0
        {
1609
0
            SolarMutexGuard aSolarGuard;
1610
            // check for system window is necessary to guarantee correct pointer cast!
1611
0
            if (pWindow && pWindow->IsSystemWindow())
1612
0
                static_cast<WorkWindow*>(pWindow.get())->Minimize();
1613
0
        }
1614
0
        else if (!bHidden && !bStartPres)
1615
0
        {
1616
            // show frame ... if it's not still visible ...
1617
            // But do nothing if it's already visible!
1618
0
            impl_makeFrameWindowVisible(xWindow, !m_bFocusedAndToFront && shouldFocusAndToFront());
1619
0
        }
1620
1621
0
        if (pWindow)
1622
0
            pWindow->FlashWindow();
1623
1624
        // Note: Only if an existing property "FrameName" is given by this media descriptor,
1625
        // it should be used. Otherwise we should do nothing. May be the outside code has already
1626
        // set a frame name on the target!
1627
0
        auto pFrameName = m_lMediaDescriptor.find(utl::MediaDescriptor::PROP_FRAMENAME);
1628
0
        if (pFrameName != m_lMediaDescriptor.end())
1629
0
        {
1630
0
            OUString sFrameName;
1631
0
            pFrameName->second >>= sFrameName;
1632
            // Check the name again. e.g. "_default" isn't allowed.
1633
            // On the other side "_beamer" is a valid name :-)
1634
0
            if (TargetHelper::isValidNameForFrame(sFrameName))
1635
0
                m_xTargetFrame->setName(sFrameName);
1636
0
        }
1637
0
    }
1638
0
    else if (m_bReactivateControllerOnError)
1639
0
    {
1640
        // Try to reactivate the old document (if any exists!)
1641
0
        css::uno::Reference< css::frame::XController > xOldDoc = m_xTargetFrame->getController();
1642
        // clear does not depend from reactivation state of a might existing old document!
1643
        // We must make sure, that a might following getTargetComponent() call does not return
1644
        // the old document!
1645
0
        m_xTargetFrame.clear();
1646
0
        if (xOldDoc.is())
1647
0
        {
1648
0
            bool bReactivated = xOldDoc->suspend(false);
1649
0
            if (!bReactivated)
1650
0
                throw LoadEnvException(LoadEnvException::ID_COULD_NOT_REACTIVATE_CONTROLLER);
1651
0
            m_bReactivateControllerOnError = false;
1652
0
        }
1653
0
    }
1654
0
    else if (m_bCloseFrameOnError)
1655
0
    {
1656
        // close empty frames
1657
0
        css::uno::Reference< css::util::XCloseable > xCloseable (m_xTargetFrame, css::uno::UNO_QUERY);
1658
1659
0
        try
1660
0
        {
1661
0
            if (xCloseable.is())
1662
0
                xCloseable->close(true);
1663
0
            else if (m_xTargetFrame.is())
1664
0
                m_xTargetFrame->dispose();
1665
0
        }
1666
0
        catch(const css::util::CloseVetoException&)
1667
0
        {}
1668
0
        catch(const css::lang::DisposedException&)
1669
0
        {}
1670
0
        m_xTargetFrame.clear();
1671
0
    }
1672
1673
    // This max force an implicit closing of our target frame ...
1674
    // e.g. in case close(sal_True) was called before and the frame
1675
    // kill itself if our external use-lock is released here!
1676
    // That's why we release this lock AFTER ALL OPERATIONS on this frame
1677
    // are finished. The frame itself must handle then
1678
    // this situation gracefully.
1679
0
    m_aTargetLock.freeResource();
1680
1681
    // Last but not least :-)
1682
    // We have to clear the current media descriptor.
1683
    // Otherwise it hold a might existing stream open!
1684
0
    m_lMediaDescriptor.clear();
1685
1686
0
    css::uno::Any aRequest;
1687
0
    bool bThrow = false;
1688
0
    if ( !m_bLoaded && m_pQuietInteraction.is() && m_pQuietInteraction->wasUsed() )
1689
0
    {
1690
0
        aRequest = m_pQuietInteraction->getRequest();
1691
0
        m_pQuietInteraction.clear();
1692
0
        bThrow = true;
1693
0
    }
1694
1695
0
    aReadLock.clear();
1696
1697
0
    if (bThrow)
1698
0
    {
1699
0
        if  ( aRequest.isExtractableTo( ::cppu::UnoType< css::uno::Exception >::get() ) )
1700
0
            throw LoadEnvException(
1701
0
                LoadEnvException::ID_GENERAL_ERROR, u"interaction request"_ustr,
1702
0
                aRequest);
1703
0
    }
1704
1705
    // <- SAFE ----------------------------------
1706
0
}
1707
1708
bool LoadEnv::shouldFocusAndToFront() const
1709
0
{
1710
0
    bool const preview(
1711
0
        m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_PREVIEW, false));
1712
0
    return !preview;
1713
0
}
1714
1715
// static
1716
void LoadEnv::impl_makeFrameWindowVisible(const css::uno::Reference< css::awt::XWindow >& xWindow      ,
1717
                                                bool bForceToFront)
1718
0
{
1719
0
    SolarMutexGuard aSolarGuard;
1720
0
    VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow);
1721
0
    if ( !pWindow )
1722
0
        return;
1723
1724
0
    if (pWindow->IsVisible() && bForceToFront)
1725
0
        pWindow->ToTop( ToTopFlags::RestoreWhenMin | ToTopFlags::ForegroundTask );
1726
0
    else
1727
0
        pWindow->Show(true, bForceToFront ? ShowFlags::ForegroundTask : ShowFlags::NONE);
1728
0
}
1729
1730
void LoadEnv::impl_applyPersistentWindowState(const css::uno::Reference< css::awt::XWindow >& xWindow)
1731
0
{
1732
    // no window -> action not possible
1733
0
    if (!xWindow.is())
1734
0
        return;
1735
1736
    // window already visible -> do nothing! If we use a "recycle frame" for loading ...
1737
    // the current position and size must be used.
1738
0
    css::uno::Reference< css::awt::XWindow2 > xVisibleCheck(xWindow, css::uno::UNO_QUERY);
1739
0
    if (
1740
0
        (xVisibleCheck.is()        ) &&
1741
0
        (xVisibleCheck->isVisible())
1742
0
       )
1743
0
       return;
1744
1745
    // SOLAR SAFE ->
1746
0
    {
1747
0
        SolarMutexGuard aSolarGuard1;
1748
1749
0
        VclPtr<vcl::Window>  pWindow = VCLUnoHelper::GetWindow(xWindow);
1750
0
        if (!pWindow)
1751
0
            return;
1752
1753
0
        bool bSystemWindow = pWindow->IsSystemWindow();
1754
0
        bool bWorkWindow = (pWindow->GetType() == WindowType::WORKWINDOW);
1755
1756
0
        if (!bSystemWindow && !bWorkWindow)
1757
0
            return;
1758
1759
        // don't overwrite this special state!
1760
0
        WorkWindow* pWorkWindow = static_cast<WorkWindow*>(pWindow.get());
1761
0
        if (pWorkWindow->IsMinimized())
1762
0
            return;
1763
0
    }
1764
    // <- SOLAR SAFE
1765
1766
    // SAFE ->
1767
0
    osl::ClearableMutexGuard aReadLock(m_mutex);
1768
1769
    // no filter -> no module -> no persistent window state
1770
0
    OUString sFilter = m_lMediaDescriptor.getUnpackedValueOrDefault(
1771
0
                                    utl::MediaDescriptor::PROP_FILTERNAME,
1772
0
                                    OUString());
1773
0
    if (sFilter.isEmpty())
1774
0
        return;
1775
1776
0
    css::uno::Reference< css::uno::XComponentContext > xContext = m_xContext;
1777
1778
0
    aReadLock.clear();
1779
    // <- SAFE
1780
1781
0
    try
1782
0
    {
1783
        // retrieve the module name from the filter configuration
1784
0
        css::uno::Reference< css::container::XNameAccess > xFilterCfg(
1785
0
            xContext->getServiceManager()->createInstanceWithContext(SERVICENAME_FILTERFACTORY, xContext),
1786
0
            css::uno::UNO_QUERY_THROW);
1787
0
        ::comphelper::SequenceAsHashMap lProps (xFilterCfg->getByName(sFilter));
1788
0
        OUString                 sModule = lProps.getUnpackedValueOrDefault(FILTER_PROPNAME_ASCII_DOCUMENTSERVICE, OUString());
1789
1790
        // get access to the configuration of this office module
1791
0
        css::uno::Reference< css::container::XNameAccess > xModuleCfg(officecfg::Setup::Office::Factories::get());
1792
1793
        // read window state from the configuration
1794
        // and apply it on the window.
1795
        // Do nothing, if no configuration entry exists!
1796
0
        OUString sWindowState;
1797
1798
        // Don't look for persistent window attributes when used through LibreOfficeKit
1799
0
        if( !comphelper::LibreOfficeKit::isActive() )
1800
0
            comphelper::ConfigurationHelper::readRelativeKey(xModuleCfg, sModule, u"ooSetupFactoryWindowAttributes"_ustr) >>= sWindowState;
1801
1802
0
        if (!sWindowState.isEmpty())
1803
0
        {
1804
            // SOLAR SAFE ->
1805
0
            SolarMutexGuard aSolarGuard;
1806
1807
            // We have to retrieve the window pointer again. Because nobody can guarantee
1808
            // that the XWindow was not disposed in between .-)
1809
            // But if we get a valid pointer we can be sure, that it's the system window pointer
1810
            // we already checked and used before. Because nobody recycle the same uno reference for
1811
            // a new internal c++ implementation ... hopefully .-))
1812
0
            VclPtr<vcl::Window> pWindowCheck = VCLUnoHelper::GetWindow(xWindow);
1813
0
            if (! pWindowCheck)
1814
0
                return;
1815
1816
0
            SystemWindow* pSystemWindow = static_cast<SystemWindow*>(pWindowCheck.get());
1817
0
            pSystemWindow->SetWindowState(vcl::WindowData(sWindowState));
1818
            // <- SOLAR SAFE
1819
0
        }
1820
0
    }
1821
0
    catch(const css::uno::RuntimeException&)
1822
0
        { throw; }
1823
0
    catch(const css::uno::Exception&)
1824
0
        {}
1825
0
}
1826
1827
} // namespace framework
1828
1829
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */