Coverage Report

Created: 2026-04-09 11:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sfx2/source/view/frmload.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_collab.h>
21
22
#include <sfx2/app.hxx>
23
#include <sfx2/bindings.hxx>
24
#include <sfx2/docfac.hxx>
25
#include <sfx2/docfile.hxx>
26
#include <sfx2/docfilt.hxx>
27
#include <sfx2/doctempl.hxx>
28
#include <sfx2/fcontnr.hxx>
29
#include <sfx2/frame.hxx>
30
#include <sfx2/objsh.hxx>
31
#include <sfx2/request.hxx>
32
#include <sfx2/sfxsids.hrc>
33
#include <sfx2/viewfac.hxx>
34
35
#include <com/sun/star/container/XContainerQuery.hpp>
36
#include <com/sun/star/document/XTypeDetection.hpp>
37
#include <com/sun/star/frame/XFrame.hpp>
38
#include <com/sun/star/frame/XLoadable.hpp>
39
#include <com/sun/star/frame/XModel3.hpp>
40
#include <com/sun/star/task/XInteractionHandler.hpp>
41
#include <com/sun/star/task/XInteractionHandler2.hpp>
42
#include <com/sun/star/document/XViewDataSupplier.hpp>
43
#include <com/sun/star/container/XIndexAccess.hpp>
44
#include <com/sun/star/frame/XSynchronousFrameLoader.hpp>
45
#include <com/sun/star/frame/XController2.hpp>
46
#include <com/sun/star/frame/XModel2.hpp>
47
#include <com/sun/star/lang/XServiceInfo.hpp>
48
#include <com/sun/star/lang/XInitialization.hpp>
49
#include <com/sun/star/uno/XComponentContext.hpp>
50
#include <com/sun/star/util/XCloseable.hpp>
51
#if ENABLE_YRS
52
#include <com/sun/star/io/SequenceInputStream.hpp>
53
#include <com/sun/star/connection/Connector.hpp>
54
#endif
55
56
#include <comphelper/getexpandeduri.hxx>
57
#include <comphelper/interaction.hxx>
58
#include <comphelper/namedvaluecollection.hxx>
59
#include <cppuhelper/exc_hlp.hxx>
60
#include <cppuhelper/implbase.hxx>
61
#include <cppuhelper/supportsservice.hxx>
62
#include <framework/interaction.hxx>
63
#include <officecfg/Office/Common.hxx>
64
#include <rtl/ref.hxx>
65
#include <sal/log.hxx>
66
#include <svl/eitem.hxx>
67
#include <svl/stritem.hxx>
68
#include <unotools/fcm.hxx>
69
#include <unotools/moduleoptions.hxx>
70
#include <comphelper/diagnose_ex.hxx>
71
#include <tools/stream.hxx>
72
#include <tools/urlobj.hxx>
73
#include <vcl/svapp.hxx>
74
#include <o3tl/string_view.hxx>
75
76
using namespace com::sun::star;
77
using ::com::sun::star::beans::PropertyValue;
78
using ::com::sun::star::container::XContainerQuery;
79
using ::com::sun::star::container::XEnumeration;
80
using ::com::sun::star::document::XTypeDetection;
81
using ::com::sun::star::frame::XFrame;
82
using ::com::sun::star::frame::XLoadable;
83
using ::com::sun::star::task::XInteractionHandler;
84
using ::com::sun::star::task::XInteractionHandler2;
85
using ::com::sun::star::uno::Any;
86
using ::com::sun::star::uno::Exception;
87
using ::com::sun::star::uno::Reference;
88
using ::com::sun::star::uno::RuntimeException;
89
using ::com::sun::star::uno::Sequence;
90
using ::com::sun::star::uno::UNO_QUERY;
91
using ::com::sun::star::uno::UNO_QUERY_THROW;
92
using ::com::sun::star::uno::UNO_SET_THROW;
93
using ::com::sun::star::util::XCloseable;
94
using ::com::sun::star::document::XViewDataSupplier;
95
using ::com::sun::star::container::XIndexAccess;
96
using ::com::sun::star::frame::XController2;
97
using ::com::sun::star::frame::XModel2;
98
99
namespace {
100
101
class SfxFrameLoader_Impl : public ::cppu::WeakImplHelper< css::frame::XSynchronousFrameLoader, css::lang::XServiceInfo >
102
{
103
    css::uno::Reference < css::uno::XComponentContext >  m_aContext;
104
105
public:
106
    explicit SfxFrameLoader_Impl( const css::uno::Reference < css::uno::XComponentContext >& _rxContext );
107
108
    virtual OUString SAL_CALL getImplementationName() override;
109
110
    virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override;
111
112
    virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
113
114
115
    // XSynchronousFrameLoader
116
117
    virtual sal_Bool SAL_CALL load( const css::uno::Sequence< css::beans::PropertyValue >& _rArgs, const css::uno::Reference< css::frame::XFrame >& _rxFrame ) override;
118
    virtual void SAL_CALL cancel() override;
119
120
protected:
121
    virtual                 ~SfxFrameLoader_Impl() override;
122
123
private:
124
    std::shared_ptr<const SfxFilter>    impl_getFilterFromServiceName_nothrow(
125
                            const OUString& i_rServiceName
126
                        ) const;
127
128
    static OUString     impl_askForFilter_nothrow(
129
                            const css::uno::Reference< css::task::XInteractionHandler >& i_rxHandler,
130
                            const OUString& i_rDocumentURL
131
                        );
132
133
    std::shared_ptr<const SfxFilter>    impl_detectFilterForURL(
134
                            const OUString& _rURL,
135
                            const ::comphelper::NamedValueCollection& i_rDescriptor,
136
                            const SfxFilterMatcher& rMatcher
137
                        ) const;
138
139
    static bool         impl_createNewDocWithSlotParam(
140
                            const sal_uInt16 _nSlotID,
141
                            const css::uno::Reference< css::frame::XFrame >& i_rxFrame,
142
                            const bool i_bHidden
143
                        );
144
145
    void                impl_determineFilter(
146
                                  ::comphelper::NamedValueCollection& io_rDescriptor
147
                        ) const;
148
149
    bool                impl_determineTemplateDocument(
150
                            ::comphelper::NamedValueCollection& io_rDescriptor
151
                        ) const;
152
153
    static sal_uInt16   impl_findSlotParam(
154
                            std::u16string_view i_rFactoryURL
155
                        );
156
157
    static SfxObjectShellRef   impl_findObjectShell(
158
                            const css::uno::Reference< css::frame::XModel2 >& i_rxDocument
159
                        );
160
161
    static void         impl_handleCaughtError_nothrow(
162
                            const css::uno::Any& i_rCaughtError,
163
                            const ::comphelper::NamedValueCollection& i_rDescriptor
164
                        );
165
166
    static void         impl_removeLoaderArguments(
167
                            ::comphelper::NamedValueCollection& io_rDescriptor
168
                        );
169
170
    static SfxInterfaceId impl_determineEffectiveViewId_nothrow(
171
                            const SfxObjectShell& i_rDocument,
172
                            const ::comphelper::NamedValueCollection& i_rDescriptor
173
                        );
174
175
    static ::comphelper::NamedValueCollection
176
                        impl_extractViewCreationArgs(
177
                                  ::comphelper::NamedValueCollection& io_rDescriptor
178
                        );
179
180
    static css::uno::Reference< css::frame::XController2 >
181
                        impl_createDocumentView(
182
                            const css::uno::Reference< css::frame::XModel2 >& i_rModel,
183
                            const css::uno::Reference< css::frame::XFrame >& i_rFrame,
184
                            const ::comphelper::NamedValueCollection& i_rViewFactoryArgs,
185
                            const OUString& i_rViewName
186
                        );
187
};
188
189
SfxFrameLoader_Impl::SfxFrameLoader_Impl( const Reference< css::uno::XComponentContext >& _rxContext )
190
0
    :m_aContext( _rxContext )
191
0
{
192
0
}
193
194
SfxFrameLoader_Impl::~SfxFrameLoader_Impl()
195
0
{
196
0
}
197
198
199
std::shared_ptr<const SfxFilter> SfxFrameLoader_Impl::impl_detectFilterForURL( const OUString& sURL,
200
        const ::comphelper::NamedValueCollection& i_rDescriptor, const SfxFilterMatcher& rMatcher ) const
201
0
{
202
0
    OUString sFilter;
203
0
    try
204
0
    {
205
0
        if ( sURL.isEmpty() )
206
0
            return nullptr;
207
208
0
        Reference< XTypeDetection > xDetect(
209
0
            m_aContext->getServiceManager()->createInstanceWithContext(u"com.sun.star.document.TypeDetection"_ustr, m_aContext),
210
0
            UNO_QUERY_THROW);
211
212
0
        ::comphelper::NamedValueCollection aNewArgs;
213
0
        aNewArgs.put( u"URL"_ustr, sURL );
214
215
0
        if ( i_rDescriptor.has( u"InteractionHandler"_ustr ) )
216
0
            aNewArgs.put( u"InteractionHandler"_ustr, i_rDescriptor.get( u"InteractionHandler"_ustr ) );
217
0
        if ( i_rDescriptor.has( u"StatusIndicator"_ustr ) )
218
0
            aNewArgs.put( u"StatusIndicator"_ustr, i_rDescriptor.get( u"StatusIndicator"_ustr ) );
219
220
0
        Sequence< PropertyValue > aQueryArgs( aNewArgs.getPropertyValues() );
221
0
        OUString sType = xDetect->queryTypeByDescriptor( aQueryArgs, true );
222
0
        if ( !sType.isEmpty() )
223
0
        {
224
0
            std::shared_ptr<const SfxFilter> pFilter = rMatcher.GetFilter4EA( sType );
225
0
            if ( pFilter )
226
0
                sFilter = pFilter->GetName();
227
0
        }
228
0
    }
229
0
    catch ( const RuntimeException& )
230
0
    {
231
0
        throw;
232
0
    }
233
0
    catch( const Exception& )
234
0
    {
235
0
        DBG_UNHANDLED_EXCEPTION("sfx.view");
236
0
        sFilter.clear();
237
0
    }
238
239
0
    std::shared_ptr<const SfxFilter> pFilter;
240
0
    if (!sFilter.isEmpty())
241
0
        pFilter = rMatcher.GetFilter4FilterName(sFilter);
242
0
    return pFilter;
243
0
}
244
245
246
std::shared_ptr<const SfxFilter> SfxFrameLoader_Impl::impl_getFilterFromServiceName_nothrow( const OUString& i_rServiceName ) const
247
0
{
248
0
    try
249
0
    {
250
0
        ::comphelper::NamedValueCollection aQuery;
251
0
        aQuery.put( u"DocumentService"_ustr, i_rServiceName );
252
253
0
        const Reference< XContainerQuery > xQuery(
254
0
            m_aContext->getServiceManager()->createInstanceWithContext(u"com.sun.star.document.FilterFactory"_ustr, m_aContext),
255
0
            UNO_QUERY_THROW );
256
257
0
        const SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher();
258
0
        const SfxFilterFlags nMust = SfxFilterFlags::IMPORT;
259
0
        const SfxFilterFlags nDont = SFX_FILTER_NOTINSTALLED;
260
261
0
        Reference < XEnumeration > xEnum( xQuery->createSubSetEnumerationByProperties(
262
0
            aQuery.getNamedValues() ), UNO_SET_THROW );
263
0
        while ( xEnum->hasMoreElements() )
264
0
        {
265
0
            ::comphelper::NamedValueCollection aType( xEnum->nextElement() );
266
0
            OUString sFilterName = aType.getOrDefault( u"Name"_ustr, OUString() );
267
0
            if ( sFilterName.isEmpty() )
268
0
                continue;
269
270
0
            std::shared_ptr<const SfxFilter> pFilter = rMatcher.GetFilter4FilterName( sFilterName );
271
0
            if ( !pFilter )
272
0
                continue;
273
274
0
            SfxFilterFlags nFlags = pFilter->GetFilterFlags();
275
0
            if  (   ( ( nFlags & nMust ) == nMust )
276
0
                &&  ( ( nFlags & nDont ) == SfxFilterFlags::NONE )
277
0
                )
278
0
            {
279
0
                return pFilter;
280
0
            }
281
0
        }
282
0
    }
283
0
    catch( const Exception& )
284
0
    {
285
0
        DBG_UNHANDLED_EXCEPTION("sfx.view");
286
0
    }
287
0
    return nullptr;
288
0
}
289
290
291
OUString SfxFrameLoader_Impl::impl_askForFilter_nothrow( const Reference< XInteractionHandler >& i_rxHandler,
292
                                                                 const OUString& i_rDocumentURL )
293
0
{
294
0
    ENSURE_OR_THROW( i_rxHandler.is(), "invalid interaction handler" );
295
296
0
    OUString sFilterName;
297
0
    try
298
0
    {
299
0
        ::framework::RequestFilterSelect aRequest( i_rDocumentURL );
300
0
        i_rxHandler->handle( aRequest.GetRequest() );
301
0
        if( !aRequest.isAbort() )
302
0
            sFilterName = aRequest.getFilter();
303
0
    }
304
0
    catch( const Exception& )
305
0
    {
306
0
        DBG_UNHANDLED_EXCEPTION("sfx.view");
307
0
    }
308
309
0
    return sFilterName;
310
0
}
311
312
bool lcl_getDispatchResult(const SfxPoolItemHolder& rResult)
313
0
{
314
0
    if (!rResult)
315
0
        return false;
316
317
    // default must be set to true, because some return values
318
    // can't be checked, but nonetheless indicate "success"!
319
0
    bool bSuccess = true;
320
321
    // On the other side some special slots return a boolean state,
322
    // which can be set to FALSE.
323
0
    const SfxBoolItem* pItem(dynamic_cast<const SfxBoolItem*>(rResult.getItem()));
324
0
    if ( pItem )
325
0
        bSuccess = pItem->GetValue();
326
327
0
    return bSuccess;
328
0
}
329
330
bool SfxFrameLoader_Impl::impl_createNewDocWithSlotParam( const sal_uInt16 _nSlotID, const Reference< XFrame >& i_rxFrame,
331
                                                              const bool i_bHidden )
332
0
{
333
0
    SfxRequest aRequest( _nSlotID, SfxCallMode::SYNCHRON, SfxGetpApp()->GetPool() );
334
0
    aRequest.AppendItem( SfxUnoFrameItem( SID_FILLFRAME, i_rxFrame ) );
335
0
    if ( i_bHidden )
336
0
        aRequest.AppendItem( SfxBoolItem( SID_HIDDEN, true ) );
337
0
    return lcl_getDispatchResult(SfxGetpApp()->ExecuteSlot(aRequest));
338
0
}
339
340
341
void SfxFrameLoader_Impl::impl_determineFilter( ::comphelper::NamedValueCollection& io_rDescriptor ) const
342
0
{
343
0
    const OUString     sURL         = io_rDescriptor.getOrDefault( u"URL"_ustr,                OUString() );
344
0
    const OUString     sTypeName    = io_rDescriptor.getOrDefault( u"TypeName"_ustr,           OUString() );
345
0
    const OUString     sFilterName  = io_rDescriptor.getOrDefault( u"FilterName"_ustr,         OUString() );
346
0
    const OUString     sServiceName = io_rDescriptor.getOrDefault( u"DocumentService"_ustr,    OUString() );
347
0
    const Reference< XInteractionHandler >
348
0
                              xInteraction = io_rDescriptor.getOrDefault( u"InteractionHandler"_ustr, Reference< XInteractionHandler >() );
349
350
0
    const SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher();
351
0
    std::shared_ptr<const SfxFilter> pFilter;
352
353
    // get filter by its name directly ...
354
0
    if ( !sFilterName.isEmpty() )
355
0
        pFilter = rMatcher.GetFilter4FilterName( sFilterName );
356
357
    // or search the preferred filter for the detected type ...
358
0
    if ( !pFilter && !sTypeName.isEmpty() )
359
0
        pFilter = rMatcher.GetFilter4EA( sTypeName );
360
361
    // or use given document service for detection, too
362
0
    if ( !pFilter && !sServiceName.isEmpty() )
363
0
        pFilter = impl_getFilterFromServiceName_nothrow( sServiceName );
364
365
    // or use interaction to ask user for right filter.
366
0
    if ( !pFilter && xInteraction.is() && !sURL.isEmpty() )
367
0
    {
368
0
        OUString sSelectedFilter = impl_askForFilter_nothrow( xInteraction, sURL );
369
0
        if ( !sSelectedFilter.isEmpty() )
370
0
            pFilter = rMatcher.GetFilter4FilterName( sSelectedFilter );
371
0
    }
372
373
0
    if ( !pFilter )
374
0
        return;
375
376
0
    io_rDescriptor.put( u"FilterName"_ustr, pFilter->GetFilterName() );
377
378
    // If detected filter indicates using of an own template format
379
    // add property "AsTemplate" to descriptor. But suppress this step
380
    // if such property already exists.
381
0
    if ( pFilter->IsOwnTemplateFormat() && !io_rDescriptor.has( u"AsTemplate"_ustr ) )
382
0
        io_rDescriptor.put( u"AsTemplate"_ustr, true );
383
384
    // The DocumentService property will finally be used to determine the document type to create, so
385
    // override it with the service name as indicated by the found filter.
386
0
    io_rDescriptor.put( u"DocumentService"_ustr, pFilter->GetServiceName() );
387
0
}
388
389
390
SfxObjectShellRef SfxFrameLoader_Impl::impl_findObjectShell( const Reference< XModel2 >& i_rxDocument )
391
0
{
392
0
    for ( SfxObjectShell* pDoc = SfxObjectShell::GetFirst( nullptr, false ); pDoc;
393
0
                                    pDoc = SfxObjectShell::GetNext( *pDoc, nullptr, false ) )
394
0
    {
395
0
        if ( i_rxDocument == pDoc->GetModel() )
396
0
        {
397
0
            return pDoc;
398
0
        }
399
0
    }
400
401
0
    SAL_WARN( "sfx.view", "SfxFrameLoader_Impl::impl_findObjectShell: model is not based on SfxObjectShell - wrong frame loader usage!" );
402
0
    return nullptr;
403
0
}
404
405
406
bool SfxFrameLoader_Impl::impl_determineTemplateDocument( ::comphelper::NamedValueCollection& io_rDescriptor ) const
407
0
{
408
0
    try
409
0
    {
410
0
        const OUString sTemplateRegioName = io_rDescriptor.getOrDefault( u"TemplateRegionName"_ustr, OUString() );
411
0
        const OUString sTemplateName      = io_rDescriptor.getOrDefault( u"TemplateName"_ustr,       OUString() );
412
0
        const OUString sServiceName       = io_rDescriptor.getOrDefault( u"DocumentService"_ustr,    OUString() );
413
0
        const OUString sURL               = io_rDescriptor.getOrDefault( u"URL"_ustr,                OUString() );
414
415
        // determine the full URL of the template to use, if any
416
0
        OUString sTemplateURL;
417
0
        if ( !sTemplateRegioName.isEmpty() && !sTemplateName.isEmpty() )
418
0
        {
419
0
            SfxDocumentTemplates aTmpFac;
420
0
            aTmpFac.GetFull( sTemplateRegioName, sTemplateName, sTemplateURL );
421
0
        }
422
0
        else
423
0
        {
424
0
            if ( !sServiceName.isEmpty() )
425
0
                sTemplateURL = SfxObjectFactory::GetStandardTemplate( sServiceName );
426
0
            else
427
0
                sTemplateURL = SfxObjectFactory::GetStandardTemplate( SfxObjectShell::GetServiceNameFromFactory( sURL ) );
428
            // tdf#165851 expand trusted urls from configuration here
429
0
            sTemplateURL = comphelper::getExpandedUri(m_aContext, sTemplateURL);
430
0
        }
431
432
0
        if ( !sTemplateURL.isEmpty() )
433
0
        {
434
            // detect the filter for the template. Might still be NULL (if the template is broken, or does not
435
            // exist, or some such), but this is handled by our caller the same way as if no template/URL was present.
436
0
            std::shared_ptr<const SfxFilter> pTemplateFilter = impl_detectFilterForURL( sTemplateURL, io_rDescriptor, SfxGetpApp()->GetFilterMatcher() );
437
0
            if ( pTemplateFilter )
438
0
            {
439
                // load the template document, but, well, "as template"
440
0
                io_rDescriptor.put( u"FilterName"_ustr, pTemplateFilter->GetName() );
441
0
                io_rDescriptor.put( u"FileName"_ustr, sTemplateURL );
442
0
                io_rDescriptor.put( u"AsTemplate"_ustr, true );
443
444
                // #i21583#
445
                // the DocumentService property will finally be used to create the document. Thus, override any possibly
446
                // present value with the document service of the template.
447
0
                io_rDescriptor.put( u"DocumentService"_ustr, pTemplateFilter->GetServiceName() );
448
0
                return true;
449
0
            }
450
0
        }
451
0
    }
452
0
    catch (...)
453
0
    {
454
0
    }
455
0
    return false;
456
0
}
457
458
459
sal_uInt16 SfxFrameLoader_Impl::impl_findSlotParam( std::u16string_view i_rFactoryURL )
460
0
{
461
0
    std::u16string_view sSlotParam;
462
0
    const size_t nParamPos = i_rFactoryURL.find( '?' );
463
0
    if ( nParamPos != std::u16string_view::npos )
464
0
    {
465
        // currently only the "slot" parameter is supported
466
0
        const size_t nSlotPos = i_rFactoryURL.find( u"slot=", nParamPos );
467
0
        if ( nSlotPos > 0 && nSlotPos != std::u16string_view::npos )
468
0
            sSlotParam = i_rFactoryURL.substr( nSlotPos + 5 );
469
0
    }
470
471
0
    if ( !sSlotParam.empty() )
472
0
        return sal_uInt16( o3tl::toInt32(sSlotParam) );
473
474
0
    return 0;
475
0
}
476
477
478
void SfxFrameLoader_Impl::impl_handleCaughtError_nothrow( const Any& i_rCaughtError, const ::comphelper::NamedValueCollection& i_rDescriptor )
479
0
{
480
0
    try
481
0
    {
482
0
        const Reference< XInteractionHandler > xInteraction =
483
0
            i_rDescriptor.getOrDefault( u"InteractionHandler"_ustr, Reference< XInteractionHandler >() );
484
0
        if ( !xInteraction.is() )
485
0
            return;
486
0
        ::rtl::Reference< ::comphelper::OInteractionRequest > pRequest( new ::comphelper::OInteractionRequest( i_rCaughtError ) );
487
0
        ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove( new ::comphelper::OInteractionApprove );
488
0
        pRequest->addContinuation( pApprove );
489
490
0
        const Reference< XInteractionHandler2 > xHandler( xInteraction, UNO_QUERY );
491
    #if OSL_DEBUG_LEVEL > 0
492
        const bool bHandled =
493
    #endif
494
0
        xHandler.is() && xHandler->handleInteractionRequest( pRequest );
495
496
    #if OSL_DEBUG_LEVEL > 0
497
        if ( !bHandled )
498
            // the interaction handler couldn't deal with this error
499
            // => report it as assertion, at least (done in the DBG_UNHANDLED_EXCEPTION below)
500
            ::cppu::throwException( i_rCaughtError );
501
    #endif
502
0
    }
503
0
    catch( const Exception& )
504
0
    {
505
0
        DBG_UNHANDLED_EXCEPTION("sfx.view");
506
0
    }
507
0
}
508
509
510
void SfxFrameLoader_Impl::impl_removeLoaderArguments( ::comphelper::NamedValueCollection& io_rDescriptor )
511
0
{
512
    // remove the arguments which are for the loader only, and not for a call to attachResource
513
0
    io_rDescriptor.remove( u"StatusIndicator"_ustr );
514
0
    io_rDescriptor.remove( u"Model"_ustr );
515
0
}
516
517
518
::comphelper::NamedValueCollection SfxFrameLoader_Impl::impl_extractViewCreationArgs( ::comphelper::NamedValueCollection& io_rDescriptor )
519
0
{
520
0
    static const std::u16string_view sKnownViewArgs[] = { u"JumpMark", u"PickListEntry" };
521
522
0
    ::comphelper::NamedValueCollection aViewArgs;
523
0
    for (const auto& rKnownViewArg : sKnownViewArgs)
524
0
    {
525
0
        const OUString sKnownViewArg(rKnownViewArg);
526
0
        if ( io_rDescriptor.has( sKnownViewArg ) )
527
0
        {
528
0
            aViewArgs.put( sKnownViewArg, io_rDescriptor.get( sKnownViewArg ) );
529
0
            io_rDescriptor.remove( sKnownViewArg );
530
0
        }
531
0
    }
532
0
    return aViewArgs;
533
0
}
534
535
536
SfxInterfaceId SfxFrameLoader_Impl::impl_determineEffectiveViewId_nothrow( const SfxObjectShell& i_rDocument, const ::comphelper::NamedValueCollection& i_rDescriptor )
537
0
{
538
0
    SfxInterfaceId nViewId(i_rDescriptor.getOrDefault( u"ViewId"_ustr, sal_Int16( 0 ) ));
539
0
    try
540
0
    {
541
0
        if ( nViewId == SFX_INTERFACE_NONE )
542
0
            do
543
0
            {
544
0
                Reference< XViewDataSupplier > xViewDataSupplier( i_rDocument.GetModel(), UNO_QUERY );
545
0
                Reference< XIndexAccess > xViewData;
546
0
                if ( xViewDataSupplier.is() )
547
0
                    xViewData.set( xViewDataSupplier->getViewData() );
548
549
0
                if ( !xViewData.is() || ( xViewData->getCount() == 0 ) )
550
                    // no view data stored together with the model
551
0
                    break;
552
553
                // obtain the ViewID from the view data
554
0
                Sequence< PropertyValue > aViewData;
555
0
                if ( !( xViewData->getByIndex( 0 ) >>= aViewData ) )
556
0
                    break;
557
558
0
                OUString sViewId = ::comphelper::NamedValueCollection::getOrDefault( aViewData, u"ViewId", OUString() );
559
0
                if ( sViewId.isEmpty() )
560
0
                    break;
561
562
                // somewhat weird convention here ... in the view data, the ViewId is a string, effectively describing
563
                // a view name. In the document load descriptor, the ViewId is in fact the numeric ID.
564
565
0
                SfxViewFactory* pViewFactory = i_rDocument.GetFactory().GetViewFactoryByViewName( sViewId );
566
0
                if ( pViewFactory )
567
0
                    nViewId = pViewFactory->GetOrdinal();
568
0
            }
569
0
            while ( false );
570
0
    }
571
0
    catch( const Exception& )
572
0
    {
573
0
        DBG_UNHANDLED_EXCEPTION("sfx.view");
574
0
    }
575
576
0
    if ( nViewId == SFX_INTERFACE_NONE )
577
0
        nViewId = i_rDocument.GetFactory().GetViewFactory().GetOrdinal();
578
0
    return nViewId;
579
0
}
580
581
582
Reference< XController2 > SfxFrameLoader_Impl::impl_createDocumentView( const Reference< XModel2 >& i_rModel,
583
        const Reference< XFrame >& i_rFrame, const ::comphelper::NamedValueCollection& i_rViewFactoryArgs,
584
        const OUString& i_rViewName )
585
0
{
586
    // let the model create a new controller
587
0
    const Reference< XController2 > xController( i_rModel->createViewController(
588
0
        i_rViewName,
589
0
        i_rViewFactoryArgs.getPropertyValues(),
590
0
        i_rFrame
591
0
    ), UNO_SET_THROW );
592
593
    // introduce model/view/controller to each other
594
0
    utl::ConnectFrameControllerModel(i_rFrame, xController, i_rModel);
595
596
0
    return xController;
597
0
}
598
599
std::shared_ptr<const SfxFilter> getEmptyURLFilter(std::u16string_view sURL)
600
0
{
601
0
    INetURLObject aParser(sURL);
602
0
    const OUString aExt = aParser.getExtension(INetURLObject::LAST_SEGMENT, true,
603
0
                                               INetURLObject::DecodeMechanism::WithCharset);
604
0
    const SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher();
605
606
    // Requiring the export+preferred flags helps to find the relevant filter, e.g. .doc -> WW8 (and
607
    // not WW6 or Mac_Word).
608
0
    std::shared_ptr<const SfxFilter> pFilter = rMatcher.GetFilter4Extension(
609
0
        aExt, SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT | SfxFilterFlags::PREFERED);
610
0
    if (!pFilter)
611
0
    {
612
        // retry without PREFERED so we can find at least something for 0-byte *.ods
613
0
        pFilter
614
0
            = rMatcher.GetFilter4Extension(aExt, SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT);
615
0
    }
616
0
    return pFilter;
617
0
}
618
619
sal_Bool SAL_CALL SfxFrameLoader_Impl::load( const Sequence< PropertyValue >& rArgs,
620
                                             const Reference< XFrame >& _rTargetFrame )
621
0
{
622
0
    ENSURE_OR_THROW( _rTargetFrame.is(), "illegal NULL frame" );
623
624
0
    SAL_INFO( "sfx.view", "SfxFrameLoader::load" );
625
626
0
    ::comphelper::NamedValueCollection aDescriptor( rArgs );
627
628
    // ensure the descriptor contains a referrer
629
0
    if ( !aDescriptor.has( u"Referer"_ustr ) )
630
0
        aDescriptor.put( u"Referer"_ustr, OUString() );
631
632
    // did the caller already pass a model?
633
0
    Reference< XModel2 > xModel = aDescriptor.getOrDefault( u"Model"_ustr, Reference< XModel2 >() );
634
0
    const bool bExternalModel = xModel.is();
635
636
#if ENABLE_YRS
637
    uno::Reference<connection::XConnection> xConnection;
638
    if (!xModel.is()
639
        && aDescriptor.getOrDefault(u"URL"_ustr, OUString()) == "private:factory/swriter"
640
        && getenv("YRSCONNECT"))
641
    {
642
        SAL_INFO("sfx.yrs", "YRS connect sfx2");
643
644
        // must read this SYNC
645
        auto const conn = u"pipe,name=ytest"_ustr;
646
        auto const xConnector = css::connection::Connector::create(m_aContext);
647
        xConnection = xConnector->connect(conn);
648
        uno::Sequence<sal_Int8> buf;
649
        if (xConnection->read(buf, 4) != 4)
650
        {
651
            abort();
652
        }
653
        sal_Int32 const size{static_cast<sal_uInt8>(buf[0])
654
                | static_cast<sal_uInt8>(buf[1]) << 8
655
                | static_cast<sal_uInt8>(buf[2]) << 16
656
                | static_cast<sal_uInt8>(buf[3]) << 24};
657
        if (size != 0)
658
        {
659
            SAL_INFO("sfx.yrs", "YRS connect reading file of size " << size);
660
            uno::Sequence<sal_Int8> buff(size);
661
            if (xConnection->read(buff, size) != size)
662
            {
663
                abort();
664
            }
665
            uno::Reference<io::XInputStream> const xInStream{
666
                io::SequenceInputStream::createStreamFromSequence(m_aContext, buff)};
667
            assert(xInStream.is());
668
669
            aDescriptor.put(u"URL"_ustr, u"private:stream"_ustr);
670
            aDescriptor.put(u"InputStream"_ustr, uno::Any(xInStream));
671
        }
672
        aDescriptor.put(u"ReadOnly"_ustr, uno::Any(true));
673
        aDescriptor.put(u"YrsConnect"_ustr, uno::Any(xConnection));
674
    }
675
#endif
676
677
    // check for factory URLs to create a new doc, instead of loading one
678
0
    const OUString sURL = aDescriptor.getOrDefault( u"URL"_ustr, OUString() );
679
0
    const bool bIsFactoryURL = sURL.startsWith( "private:factory/" );
680
681
0
    if (bIsFactoryURL && officecfg::Office::Common::Misc::ViewerAppMode::get())
682
0
        return false;
683
684
0
    std::shared_ptr<const SfxFilter> pEmptyURLFilter;
685
0
    bool bInitNewModel = bIsFactoryURL;
686
0
    const bool bIsDefault = bIsFactoryURL && !bExternalModel;
687
0
    if (!aDescriptor.has(u"Replaceable"_ustr))
688
0
        aDescriptor.put(u"Replaceable"_ustr, bIsDefault);
689
0
    if (bIsDefault)
690
0
    {
691
0
        const OUString sFactory = sURL.copy( sizeof( "private:factory/" ) -1 );
692
        // special handling for some weird factory URLs a la private:factory/swriter?slot=21053
693
0
        const sal_uInt16 nSlotParam = impl_findSlotParam( sFactory );
694
0
        if ( nSlotParam != 0 )
695
0
        {
696
0
            return impl_createNewDocWithSlotParam( nSlotParam, _rTargetFrame, aDescriptor.getOrDefault( u"Hidden"_ustr, false ) );
697
0
        }
698
699
0
        const bool bDescribesValidTemplate = impl_determineTemplateDocument( aDescriptor );
700
0
        if ( bDescribesValidTemplate )
701
0
        {
702
            // if the media descriptor allowed us to determine a template document to create the new document
703
            // from, then do not init a new document model from scratch (below), but instead load the
704
            // template document
705
0
            bInitNewModel = false;
706
0
        }
707
0
        else
708
0
        {
709
0
            const OUString sServiceName = SfxObjectShell::GetServiceNameFromFactory( sFactory );
710
0
            aDescriptor.put( u"DocumentService"_ustr, sServiceName );
711
0
        }
712
0
    }
713
0
    else
714
0
    {
715
        // compatibility
716
0
        aDescriptor.put( u"FileName"_ustr, aDescriptor.get( u"URL"_ustr ) );
717
718
0
        if (!bIsFactoryURL && !bExternalModel && tools::isEmptyFileUrl(sURL))
719
0
        {
720
0
            pEmptyURLFilter = getEmptyURLFilter(sURL);
721
0
            if (pEmptyURLFilter)
722
0
            {
723
0
                aDescriptor.put(u"DocumentService"_ustr, pEmptyURLFilter->GetServiceName());
724
0
                if (impl_determineTemplateDocument(aDescriptor))
725
0
                {
726
                    // if the media descriptor allowed us to determine a template document
727
                    // to create the new document from, then do not init a new document model
728
                    // from scratch (below), but instead load the template document
729
0
                    bInitNewModel = false;
730
                    // Do not try to load from empty UCB content
731
0
                    aDescriptor.remove(u"UCBContent"_ustr);
732
0
                }
733
0
                else
734
0
                {
735
0
                    bInitNewModel = true;
736
0
                }
737
0
            }
738
0
        }
739
0
    }
740
741
0
    bool bLoadSuccess = false;
742
0
    try
743
0
    {
744
        // extract view relevant arguments from the loader args
745
0
        ::comphelper::NamedValueCollection aViewCreationArgs( impl_extractViewCreationArgs( aDescriptor ) );
746
747
        // no model passed from outside? => create one from scratch
748
0
        if ( !bExternalModel )
749
0
        {
750
0
            bool bInternalFilter = aDescriptor.getOrDefault<OUString>(u"FilterProvider"_ustr, OUString()).isEmpty();
751
752
0
            if (bInternalFilter && !bInitNewModel)
753
0
            {
754
                // Ensure that the current SfxFilter instance is loaded before
755
                // going further.  We don't need to do this for external
756
                // filter providers.
757
0
                impl_determineFilter(aDescriptor);
758
0
            }
759
760
            // create the new doc
761
0
            const OUString sServiceName = aDescriptor.getOrDefault( u"DocumentService"_ustr, OUString() );
762
0
            xModel.set( m_aContext->getServiceManager()->createInstanceWithContext(sServiceName, m_aContext), UNO_QUERY_THROW );
763
764
            // load resp. init it
765
0
            const Reference< XLoadable > xLoadable( xModel, UNO_QUERY_THROW );
766
0
            if ( bInitNewModel )
767
0
            {
768
0
                xLoadable->initNew();
769
770
0
                impl_removeLoaderArguments( aDescriptor );
771
0
                xModel->attachResource( OUString(), aDescriptor.getPropertyValues() );
772
0
            }
773
0
            else
774
0
            {
775
0
                xLoadable->load( aDescriptor.getPropertyValues() );
776
0
            }
777
0
        }
778
0
        else
779
0
        {
780
            // tell the doc its (current) load args.
781
0
            impl_removeLoaderArguments( aDescriptor );
782
0
            xModel->attachResource( xModel->getURL(), aDescriptor.getPropertyValues() );
783
0
        }
784
785
0
        SolarMutexGuard aGuard;
786
787
        // get the SfxObjectShell (still needed at the moment)
788
        // SfxObjectShellRef is used here ( instead of ...Lock ) since the model is closed below if necessary
789
        // SfxObjectShellLock would be even dangerous here, since the lifetime control should be done outside in case of success
790
0
        const SfxObjectShellRef xDoc = impl_findObjectShell( xModel );
791
0
        ENSURE_OR_THROW( xDoc.is(), "no SfxObjectShell for the given model" );
792
793
0
        if (pEmptyURLFilter)
794
0
        {
795
            // Detach the medium from the template, and set proper document name and filter
796
0
            auto pMedium = xDoc->GetMedium();
797
0
            auto& rItemSet = pMedium->GetItemSet();
798
0
            rItemSet.ClearItem(SID_TEMPLATE);
799
0
            rItemSet.Put(SfxStringItem(SID_FILTER_NAME, pEmptyURLFilter->GetFilterName()));
800
0
            pMedium->SetName(sURL, true);
801
0
            pMedium->SetFilter(pEmptyURLFilter);
802
0
            pMedium->GetInitFileDate(true);
803
0
            xDoc->SetLoading(SfxLoadedFlags::NONE);
804
0
            xDoc->FinishedLoading();
805
0
        }
806
807
        // ensure the ID of the to-be-created view is in the descriptor, if possible
808
0
        const SfxInterfaceId nViewId = impl_determineEffectiveViewId_nothrow( *xDoc, aDescriptor );
809
0
        const sal_Int16 nViewNo = xDoc->GetFactory().GetViewNo_Impl( nViewId, 0 );
810
0
        const OUString sViewName( xDoc->GetFactory().GetViewFactory( nViewNo ).GetAPIViewName() );
811
812
        // plug the document into the frame
813
0
        Reference<XController2> xController =
814
0
            impl_createDocumentView( xModel, _rTargetFrame, aViewCreationArgs, sViewName );
815
816
0
        Reference<lang::XInitialization> xInit(xController, UNO_QUERY);
817
0
        if (xInit.is())
818
0
        {
819
0
            uno::Sequence<uno::Any> aArgs; // empty for now.
820
0
            xInit->initialize(aArgs);
821
0
        }
822
823
0
        bLoadSuccess = true;
824
0
    }
825
0
    catch ( Exception& )
826
0
    {
827
0
        const Any aError( ::cppu::getCaughtException() );
828
0
        if ( !aDescriptor.getOrDefault( u"Silent"_ustr, false ) )
829
0
            impl_handleCaughtError_nothrow( aError, aDescriptor );
830
0
    }
831
832
    // if loading was not successful, close the document
833
0
    if ( !bLoadSuccess && !bExternalModel )
834
0
    {
835
0
        try
836
0
        {
837
0
            const Reference< XCloseable > xCloseable( xModel, UNO_QUERY_THROW );
838
0
            xCloseable->close( true );
839
0
        }
840
0
        catch ( Exception& )
841
0
        {
842
0
            DBG_UNHANDLED_EXCEPTION("sfx.view");
843
0
        }
844
0
    }
845
846
0
    return bLoadSuccess;
847
0
}
848
849
void SfxFrameLoader_Impl::cancel()
850
0
{
851
0
}
852
853
/* XServiceInfo */
854
OUString SAL_CALL SfxFrameLoader_Impl::getImplementationName()
855
0
{
856
0
    return u"com.sun.star.comp.office.FrameLoader"_ustr;
857
0
}
858
859
/* XServiceInfo */
860
sal_Bool SAL_CALL SfxFrameLoader_Impl::supportsService( const OUString& sServiceName )
861
0
{
862
0
    return cppu::supportsService(this, sServiceName);
863
0
}
864
865
/* XServiceInfo */
866
Sequence< OUString > SAL_CALL SfxFrameLoader_Impl::getSupportedServiceNames()
867
0
{
868
0
    return { u"com.sun.star.frame.SynchronousFrameLoader"_ustr, u"com.sun.star.frame.OfficeFrameLoader"_ustr };
869
0
}
870
871
}
872
873
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
874
com_sun_star_comp_office_FrameLoader_get_implementation(
875
    css::uno::XComponentContext *context,
876
    css::uno::Sequence<css::uno::Any> const &)
877
0
{
878
0
    return cppu::acquire(new SfxFrameLoader_Impl(context));
879
0
}
880
881
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */